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. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAABAgAGBQJQjcPOAAoJEIqAPN1PVmxKLQUP/3rkHa4enQTyBMU326z+OE6B MtY0Xg6C5U5mksNiqAelkKPMOYy3nuoLcx2sZ9ltgJfG6qCNSV273Hr109JFlhI+ qYAybb+VOb4MqcbkUcMFzO+CBGfcDaGhJ4ibjPAQCgBxIOjKqFzbLVZ4K/xf+GUj eTEeEryJYb+jFt/RSMXzoHipEM30ROLa9VRvoyxL6m0/3uRrxK/NPVbOxPRHaJqa /oNJxC2BLPb7t4V2iBOzqChFWP2tknmVWzCuI+X0484hH2BGhm4dUzlKXEu286U9 iMueUv0vx68IpKXgzTStgfeIzxA84ufVWV5d+rPo4iu4HbCth+Fsk6Lds2nqmGAn QoEYhds0r2CrPnuWN7zP+cc/mXTDHDrPj8pf9iC/KdMSe2bUg0kHOyF08A9zLNh0 nOr2DSzYQ9IqgvWtrAUXdfR2QaFHFsb/+pXWKZsE8+PHxXR/sv7mGPD2OCR1TAqx G1OmOtHHvnwTlxHpNhIOuH35gfi5HPvPCZ9in5fnN8n0eaX3JyZw96fk5QUqYJ3w UQBAsagMnWRJUTUYYKyG+IjxkGrdNjmLRQQScYuKktZ07/PL4T5t3RpSjxzlMFwc ImdOdPntHXI2d+m874yCRAhwdp6vTfpgJl4V4XuON13DPbVO6qBqtNGxhYMqJFWF cxqrWdaPydmFKy/KLNwF =OC0R -----END PGP SIGNATURE----- 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.
This commit is contained in:
Коммит
e298c79efc
|
@ -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
|
||||
|
||||
|
|
|
@ -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 ||
|
||||
|
|
|
@ -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
|
|
@ -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);
|
|
@ -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)
|
||||
{
|
||||
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);
|
|
@ -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_ */
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
|
|
@ -257,7 +257,9 @@ 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) {
|
||||
if (*result < 0)
|
||||
return NFC_HCI_INVALID_PIPE;
|
||||
|
||||
resp = (struct hci_create_pipe_resp *)skb->data;
|
||||
pipe = resp->pipe;
|
||||
kfree_skb(skb);
|
||||
|
@ -265,8 +267,6 @@ static u8 nfc_hci_create_pipe(struct nfc_hci_dev *hdev, u8 dest_host,
|
|||
pr_debug("pipe created=%d\n", pipe);
|
||||
|
||||
return pipe;
|
||||
} else
|
||||
return NFC_HCI_INVALID_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,
|
||||
return nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE,
|
||||
NFC_HCI_ADM_CLEAR_ALL_PIPE, param, 2, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nfc_hci_disconnect_gate(struct nfc_hci_dev *hdev, u8 gate)
|
||||
|
|
|
@ -65,9 +65,10 @@ 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:
|
||||
if (list_empty(&hdev->msg_tx_queue))
|
||||
|
@ -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,6 +264,8 @@ static int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate)
|
|||
break;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,11 +185,13 @@ 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,10 +1114,102 @@ 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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local,
|
||||
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
return nci_request(ndev, nci_set_config_req, (unsigned long)¶m,
|
||||
msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
Загрузка…
Ссылка в новой задаче