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
|
# 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_PN533) += pn533.o
|
||||||
obj-$(CONFIG_NFC_WILINK) += nfcwilink.o
|
obj-$(CONFIG_NFC_WILINK) += nfcwilink.o
|
||||||
|
|
||||||
|
|
|
@ -84,6 +84,10 @@ MODULE_DEVICE_TABLE(usb, pn533_table);
|
||||||
#define PN533_LISTEN_TIME 2
|
#define PN533_LISTEN_TIME 2
|
||||||
|
|
||||||
/* frame definitions */
|
/* 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_TAIL_SIZE 2
|
||||||
#define PN533_FRAME_SIZE(f) (sizeof(struct pn533_frame) + f->datalen + \
|
#define PN533_FRAME_SIZE(f) (sizeof(struct pn533_frame) + f->datalen + \
|
||||||
PN533_FRAME_TAIL_SIZE)
|
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);
|
pn533_poll_add_mod(dev, PN533_LISTEN_MOD);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pn533_start_poll_complete(struct pn533 *dev, void *arg,
|
static int pn533_start_poll_complete(struct pn533 *dev, u8 *params, int params_len)
|
||||||
u8 *params, int params_len)
|
|
||||||
{
|
{
|
||||||
struct pn533_poll_response *resp;
|
struct pn533_poll_response *resp;
|
||||||
int rc;
|
int rc;
|
||||||
|
@ -1304,8 +1307,7 @@ static void pn533_wq_tg_get_data(struct work_struct *work)
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ATR_REQ_GB_OFFSET 17
|
#define ATR_REQ_GB_OFFSET 17
|
||||||
static int pn533_init_target_complete(struct pn533 *dev, void *arg,
|
static int pn533_init_target_complete(struct pn533 *dev, u8 *params, int params_len)
|
||||||
u8 *params, int params_len)
|
|
||||||
{
|
{
|
||||||
struct pn533_cmd_init_target_response *resp;
|
struct pn533_cmd_init_target_response *resp;
|
||||||
u8 frame, comm_mode = NFC_COMM_PASSIVE, *gb;
|
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) {
|
if (cur_mod->len == 0) {
|
||||||
del_timer(&dev->listen_timer);
|
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 {
|
} else {
|
||||||
rc = pn533_start_poll_complete(dev, arg, params, params_len);
|
rc = pn533_start_poll_complete(dev, params, params_len);
|
||||||
if (!rc)
|
if (!rc)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -2373,9 +2375,9 @@ static int pn533_probe(struct usb_interface *interface,
|
||||||
goto error;
|
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->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);
|
dev->out_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||||
|
|
||||||
if (!dev->in_frame || !dev->out_frame ||
|
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.
|
* 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/delay.h>
|
||||||
#include <linux/slab.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 <linux/nfc.h>
|
||||||
#include <net/nfc/hci.h>
|
#include <net/nfc/hci.h>
|
||||||
#include <net/nfc/llc.h>
|
#include <net/nfc/llc.h>
|
||||||
|
|
||||||
#include <linux/nfc/pn544.h>
|
#include "pn544.h"
|
||||||
|
|
||||||
#define DRIVER_DESC "HCI NFC driver for PN544"
|
|
||||||
|
|
||||||
#define PN544_HCI_DRIVER_NAME "pn544_hci"
|
|
||||||
|
|
||||||
/* Timing restrictions (ms) */
|
/* Timing restrictions (ms) */
|
||||||
#define PN544_HCI_RESETVEN_TIME 30
|
#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 HCI_MODE 0
|
||||||
#define FW_MODE 1
|
#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 {
|
enum pn544_state {
|
||||||
PN544_ST_COLD,
|
PN544_ST_COLD,
|
||||||
PN544_ST_FW_READY,
|
PN544_ST_FW_READY,
|
||||||
|
@ -100,6 +74,10 @@ enum pn544_state {
|
||||||
#define PN544_SYS_MGMT_INFO_NOTIFICATION 0x02
|
#define PN544_SYS_MGMT_INFO_NOTIFICATION 0x02
|
||||||
|
|
||||||
#define PN544_POLLING_LOOP_MGMT_GATE 0x94
|
#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_RDPHASES 0x06
|
||||||
#define PN544_PL_EMULATION 0x07
|
#define PN544_PL_EMULATION 0x07
|
||||||
#define PN544_PL_NFCT_DEACTIVATED 0x09
|
#define PN544_PL_NFCT_DEACTIVATED 0x09
|
||||||
|
@ -108,6 +86,15 @@ enum pn544_state {
|
||||||
|
|
||||||
#define PN544_NFC_WI_MGMT_GATE 0xA1
|
#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[] = {
|
static struct nfc_hci_gate pn544_gates[] = {
|
||||||
{NFC_HCI_ADMIN_GATE, NFC_HCI_INVALID_PIPE},
|
{NFC_HCI_ADMIN_GATE, NFC_HCI_INVALID_PIPE},
|
||||||
{NFC_HCI_LOOPBACK_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 */
|
/* Largest headroom needed for outgoing custom commands */
|
||||||
#define PN544_CMDS_HEADROOM 2
|
#define PN544_CMDS_HEADROOM 2
|
||||||
#define PN544_FRAME_HEADROOM 1
|
|
||||||
#define PN544_FRAME_TAILROOM 2
|
|
||||||
|
|
||||||
struct pn544_hci_info {
|
struct pn544_hci_info {
|
||||||
struct i2c_client *i2c_dev;
|
struct nfc_phy_ops *phy_ops;
|
||||||
|
void *phy_id;
|
||||||
|
|
||||||
struct nfc_hci_dev *hdev;
|
struct nfc_hci_dev *hdev;
|
||||||
|
|
||||||
enum pn544_state state;
|
enum pn544_state state;
|
||||||
|
|
||||||
struct mutex info_lock;
|
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;
|
int async_cb_type;
|
||||||
data_exchange_cb_t async_cb;
|
data_exchange_cb_t async_cb;
|
||||||
void *async_cb_context;
|
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)
|
static int pn544_hci_open(struct nfc_hci_dev *hdev)
|
||||||
{
|
{
|
||||||
struct pn544_hci_info *info = nfc_hci_get_clientdata(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;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = pn544_hci_enable(info, HCI_MODE);
|
r = info->phy_ops->enable(info->phy_id);
|
||||||
|
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
info->state = PN544_ST_READY;
|
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)
|
if (info->state == PN544_ST_COLD)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
pn544_hci_disable(info);
|
info->phy_ops->disable(info->phy_id);
|
||||||
|
|
||||||
info->state = PN544_ST_COLD;
|
info->state = PN544_ST_COLD;
|
||||||
|
|
||||||
|
@ -587,40 +337,11 @@ static int pn544_hci_ready(struct nfc_hci_dev *hdev)
|
||||||
return 0;
|
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)
|
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 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->phy_ops->write(info->phy_id, skb);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pn544_hci_start_poll(struct nfc_hci_dev *hdev,
|
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;
|
int r;
|
||||||
u8 duration[2];
|
u8 duration[2];
|
||||||
u8 activated;
|
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",
|
pr_info(DRIVER_DESC ": %s protocols 0x%x 0x%x\n",
|
||||||
__func__, im_protocols, tm_protocols);
|
__func__, im_protocols, tm_protocols);
|
||||||
|
@ -667,6 +391,61 @@ static int pn544_hci_start_poll(struct nfc_hci_dev *hdev,
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
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,
|
r = 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);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
@ -676,6 +455,43 @@ static int pn544_hci_start_poll(struct nfc_hci_dev *hdev,
|
||||||
return r;
|
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,
|
static int pn544_hci_target_from_gate(struct nfc_hci_dev *hdev, u8 gate,
|
||||||
struct nfc_target *target)
|
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->supported_protocols = NFC_PROTO_JEWEL_MASK;
|
||||||
target->sens_res = 0x0c00;
|
target->sens_res = 0x0c00;
|
||||||
break;
|
break;
|
||||||
|
case PN544_RF_READER_NFCIP1_INITIATOR_GATE:
|
||||||
|
target->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return -EPROTO;
|
return -EPROTO;
|
||||||
}
|
}
|
||||||
|
@ -701,7 +520,18 @@ static int pn544_hci_complete_target_discovered(struct nfc_hci_dev *hdev,
|
||||||
struct sk_buff *uid_skb;
|
struct sk_buff *uid_skb;
|
||||||
int r = 0;
|
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 &&
|
if (target->nfcid1_len != 4 && target->nfcid1_len != 7 &&
|
||||||
target->nfcid1_len != 10)
|
target->nfcid1_len != 10)
|
||||||
return -EPROTO;
|
return -EPROTO;
|
||||||
|
@ -724,6 +554,16 @@ static int pn544_hci_complete_target_discovered(struct nfc_hci_dev *hdev,
|
||||||
PN544_RF_READER_CMD_ACTIVATE_NEXT,
|
PN544_RF_READER_CMD_ACTIVATE_NEXT,
|
||||||
uid_skb->data, uid_skb->len, NULL);
|
uid_skb->data, uid_skb->len, NULL);
|
||||||
kfree_skb(uid_skb);
|
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) {
|
} else if (target->supported_protocols & NFC_PROTO_ISO14443_MASK) {
|
||||||
/*
|
/*
|
||||||
* TODO: maybe other ISO 14443 require some kind of continue
|
* 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
|
* <= 0: driver handled the data exchange
|
||||||
* 1: driver doesn't especially handle, please do standard processing
|
* 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 nfc_target *target,
|
||||||
struct sk_buff *skb, data_exchange_cb_t cb,
|
struct sk_buff *skb, data_exchange_cb_t cb,
|
||||||
void *cb_context)
|
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,
|
return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
|
||||||
PN544_JEWEL_RAW_CMD, skb->data,
|
PN544_JEWEL_RAW_CMD, skb->data,
|
||||||
skb->len, cb, cb_context);
|
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:
|
default:
|
||||||
return 1;
|
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,
|
static int pn544_hci_check_presence(struct nfc_hci_dev *hdev,
|
||||||
struct nfc_target *target)
|
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,
|
return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
|
||||||
PN544_RF_READER_CMD_PRESENCE_CHECK,
|
PN544_RF_READER_CMD_PRESENCE_CHECK,
|
||||||
NULL, 0, NULL);
|
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 = {
|
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,
|
.hci_ready = pn544_hci_ready,
|
||||||
.xmit = pn544_hci_xmit,
|
.xmit = pn544_hci_xmit,
|
||||||
.start_poll = pn544_hci_start_poll,
|
.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,
|
.target_from_gate = pn544_hci_target_from_gate,
|
||||||
.complete_target_discovered = pn544_hci_complete_target_discovered,
|
.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,
|
.check_presence = pn544_hci_check_presence,
|
||||||
|
.event_received = pn544_hci_event_received,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __devinit pn544_hci_probe(struct i2c_client *client,
|
int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
|
||||||
const struct i2c_device_id *id)
|
int phy_headroom, int phy_tailroom, int phy_payload,
|
||||||
|
struct nfc_hci_dev **hdev)
|
||||||
{
|
{
|
||||||
struct pn544_hci_info *info;
|
struct pn544_hci_info *info;
|
||||||
struct pn544_nfc_platform_data *pdata;
|
|
||||||
int r = 0;
|
|
||||||
u32 protocols;
|
u32 protocols;
|
||||||
struct nfc_hci_init_data init_data;
|
struct nfc_hci_init_data init_data;
|
||||||
|
int r;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
info = kzalloc(sizeof(struct pn544_hci_info), GFP_KERNEL);
|
info = kzalloc(sizeof(struct pn544_hci_info), GFP_KERNEL);
|
||||||
if (!info) {
|
if (!info) {
|
||||||
dev_err(&client->dev,
|
pr_err("Cannot allocate memory for pn544_hci_info.\n");
|
||||||
"Cannot allocate memory for pn544_hci_info.\n");
|
|
||||||
r = -ENOMEM;
|
r = -ENOMEM;
|
||||||
goto err_info_alloc;
|
goto err_info_alloc;
|
||||||
}
|
}
|
||||||
|
|
||||||
info->i2c_dev = client;
|
info->phy_ops = phy_ops;
|
||||||
|
info->phy_id = phy_id;
|
||||||
info->state = PN544_ST_COLD;
|
info->state = PN544_ST_COLD;
|
||||||
mutex_init(&info->info_lock);
|
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);
|
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;
|
NFC_PROTO_NFC_DEP_MASK;
|
||||||
|
|
||||||
info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data,
|
info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data,
|
||||||
protocols, LLC_SHDLC_NAME,
|
protocols, llc_name,
|
||||||
PN544_FRAME_HEADROOM +
|
phy_headroom + PN544_CMDS_HEADROOM,
|
||||||
PN544_CMDS_HEADROOM,
|
phy_tailroom, phy_payload);
|
||||||
PN544_FRAME_TAILROOM,
|
|
||||||
PN544_HCI_LLC_MAX_PAYLOAD);
|
|
||||||
if (!info->hdev) {
|
if (!info->hdev) {
|
||||||
dev_err(&client->dev, "Cannot allocate nfc hdev.\n");
|
pr_err("Cannot allocate nfc hdev.\n");
|
||||||
r = -ENOMEM;
|
r = -ENOMEM;
|
||||||
goto err_alloc_hdev;
|
goto err_alloc_hdev;
|
||||||
}
|
}
|
||||||
|
@ -945,79 +838,25 @@ static int __devinit pn544_hci_probe(struct i2c_client *client,
|
||||||
if (r)
|
if (r)
|
||||||
goto err_regdev;
|
goto err_regdev;
|
||||||
|
|
||||||
|
*hdev = info->hdev;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_regdev:
|
err_regdev:
|
||||||
nfc_hci_free_device(info->hdev);
|
nfc_hci_free_device(info->hdev);
|
||||||
|
|
||||||
err_alloc_hdev:
|
err_alloc_hdev:
|
||||||
free_irq(client->irq, info);
|
|
||||||
|
|
||||||
err_rti:
|
|
||||||
if (pdata->free_resources != NULL)
|
|
||||||
pdata->free_resources();
|
|
||||||
|
|
||||||
err_pdata:
|
|
||||||
kfree(info);
|
kfree(info);
|
||||||
|
|
||||||
err_info_alloc:
|
err_info_alloc:
|
||||||
return r;
|
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_hci_info *info = nfc_hci_get_clientdata(hdev);
|
||||||
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();
|
|
||||||
|
|
||||||
|
nfc_hci_unregister_device(hdev);
|
||||||
|
nfc_hci_free_device(hdev);
|
||||||
kfree(info);
|
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>
|
#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_dev;
|
||||||
|
|
||||||
struct nfc_hci_ops {
|
struct nfc_hci_ops {
|
||||||
|
@ -38,15 +44,21 @@ struct nfc_hci_ops {
|
||||||
int (*xmit) (struct nfc_hci_dev *hdev, struct sk_buff *skb);
|
int (*xmit) (struct nfc_hci_dev *hdev, struct sk_buff *skb);
|
||||||
int (*start_poll) (struct nfc_hci_dev *hdev,
|
int (*start_poll) (struct nfc_hci_dev *hdev,
|
||||||
u32 im_protocols, u32 tm_protocols);
|
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,
|
int (*target_from_gate) (struct nfc_hci_dev *hdev, u8 gate,
|
||||||
struct nfc_target *target);
|
struct nfc_target *target);
|
||||||
int (*complete_target_discovered) (struct nfc_hci_dev *hdev, u8 gate,
|
int (*complete_target_discovered) (struct nfc_hci_dev *hdev, u8 gate,
|
||||||
struct nfc_target *target);
|
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,
|
struct nfc_target *target, struct sk_buff *skb,
|
||||||
data_exchange_cb_t cb, void *cb_context);
|
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,
|
int (*check_presence)(struct nfc_hci_dev *hdev,
|
||||||
struct nfc_target *target);
|
struct nfc_target *target);
|
||||||
|
void (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event,
|
||||||
|
struct sk_buff *skb);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Pipes */
|
/* Pipes */
|
||||||
|
@ -114,6 +126,9 @@ struct nfc_hci_dev {
|
||||||
int async_cb_type;
|
int async_cb_type;
|
||||||
data_exchange_cb_t async_cb;
|
data_exchange_cb_t async_cb;
|
||||||
void *async_cb_context;
|
void *async_cb_context;
|
||||||
|
|
||||||
|
u8 *gb;
|
||||||
|
size_t gb_len;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* hci device allocation */
|
/* 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);
|
const u8 *param, size_t param_len);
|
||||||
int nfc_hci_send_event(struct nfc_hci_dev *hdev, u8 gate, u8 event,
|
int nfc_hci_send_event(struct nfc_hci_dev *hdev, u8 gate, u8 event,
|
||||||
const u8 *param, size_t param_len);
|
const u8 *param, size_t param_len);
|
||||||
|
int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate);
|
||||||
|
|
||||||
#endif /* __NET_HCI_H */
|
#endif /* __NET_HCI_H */
|
||||||
|
|
|
@ -95,7 +95,7 @@ struct nfc_genl_data {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct nfc_dev {
|
struct nfc_dev {
|
||||||
unsigned int idx;
|
int idx;
|
||||||
u32 target_next_idx;
|
u32 target_next_idx;
|
||||||
struct nfc_target *targets;
|
struct nfc_target *targets;
|
||||||
int n_targets;
|
int n_targets;
|
||||||
|
|
|
@ -60,6 +60,13 @@
|
||||||
* target mode.
|
* target mode.
|
||||||
* @NFC_EVENT_DEVICE_DEACTIVATED: event emitted when the adapter is deactivated
|
* @NFC_EVENT_DEVICE_DEACTIVATED: event emitted when the adapter is deactivated
|
||||||
* from target mode.
|
* 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 {
|
enum nfc_commands {
|
||||||
NFC_CMD_UNSPEC,
|
NFC_CMD_UNSPEC,
|
||||||
|
@ -77,6 +84,8 @@ enum nfc_commands {
|
||||||
NFC_EVENT_TARGET_LOST,
|
NFC_EVENT_TARGET_LOST,
|
||||||
NFC_EVENT_TM_ACTIVATED,
|
NFC_EVENT_TM_ACTIVATED,
|
||||||
NFC_EVENT_TM_DEACTIVATED,
|
NFC_EVENT_TM_DEACTIVATED,
|
||||||
|
NFC_CMD_LLC_GET_PARAMS,
|
||||||
|
NFC_CMD_LLC_SET_PARAMS,
|
||||||
/* private: internal use only */
|
/* private: internal use only */
|
||||||
__NFC_CMD_AFTER_LAST
|
__NFC_CMD_AFTER_LAST
|
||||||
};
|
};
|
||||||
|
@ -102,6 +111,9 @@ enum nfc_commands {
|
||||||
* @NFC_ATTR_RF_MODE: Initiator or target
|
* @NFC_ATTR_RF_MODE: Initiator or target
|
||||||
* @NFC_ATTR_IM_PROTOCOLS: Initiator mode protocols to poll for
|
* @NFC_ATTR_IM_PROTOCOLS: Initiator mode protocols to poll for
|
||||||
* @NFC_ATTR_TM_PROTOCOLS: Target mode protocols to listen 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 {
|
enum nfc_attrs {
|
||||||
NFC_ATTR_UNSPEC,
|
NFC_ATTR_UNSPEC,
|
||||||
|
@ -119,6 +131,9 @@ enum nfc_attrs {
|
||||||
NFC_ATTR_DEVICE_POWERED,
|
NFC_ATTR_DEVICE_POWERED,
|
||||||
NFC_ATTR_IM_PROTOCOLS,
|
NFC_ATTR_IM_PROTOCOLS,
|
||||||
NFC_ATTR_TM_PROTOCOLS,
|
NFC_ATTR_TM_PROTOCOLS,
|
||||||
|
NFC_ATTR_LLC_PARAM_LTO,
|
||||||
|
NFC_ATTR_LLC_PARAM_RW,
|
||||||
|
NFC_ATTR_LLC_PARAM_MIUX,
|
||||||
/* private: internal use only */
|
/* private: internal use only */
|
||||||
__NFC_ATTR_AFTER_LAST
|
__NFC_ATTR_AFTER_LAST
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
menuconfig NFC
|
menuconfig NFC
|
||||||
depends on NET && EXPERIMENTAL
|
depends on NET
|
||||||
tristate "NFC subsystem support (EXPERIMENTAL)"
|
tristate "NFC subsystem support"
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
Say Y here if you want to build support for NFC (Near field
|
Say Y here if you want to build support for NFC (Near field
|
||||||
|
|
|
@ -40,6 +40,9 @@
|
||||||
int nfc_devlist_generation;
|
int nfc_devlist_generation;
|
||||||
DEFINE_MUTEX(nfc_devlist_mutex);
|
DEFINE_MUTEX(nfc_devlist_mutex);
|
||||||
|
|
||||||
|
/* NFC device ID bitmap */
|
||||||
|
static DEFINE_IDA(nfc_index_ida);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nfc_dev_up - turn on the NFC device
|
* 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->ops->stop_poll(dev);
|
||||||
dev->polling = false;
|
dev->polling = false;
|
||||||
|
dev->rf_mode = NFC_RF_NONE;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
device_unlock(&dev->dev);
|
device_unlock(&dev->dev);
|
||||||
|
@ -194,7 +198,7 @@ static struct nfc_target *nfc_find_target(struct nfc_dev *dev, u32 target_idx)
|
||||||
if (dev->n_targets == 0)
|
if (dev->n_targets == 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
for (i = 0; i < dev->n_targets ; i++) {
|
for (i = 0; i < dev->n_targets; i++) {
|
||||||
if (dev->targets[i].idx == target_idx)
|
if (dev->targets[i].idx == target_idx)
|
||||||
return &dev->targets[i];
|
return &dev->targets[i];
|
||||||
}
|
}
|
||||||
|
@ -274,12 +278,14 @@ int nfc_dep_link_down(struct nfc_dev *dev)
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
dev->dep_link_up = false;
|
dev->dep_link_up = false;
|
||||||
dev->active_target = NULL;
|
dev->active_target = NULL;
|
||||||
|
dev->rf_mode = NFC_RF_NONE;
|
||||||
nfc_llcp_mac_is_down(dev);
|
nfc_llcp_mac_is_down(dev);
|
||||||
nfc_genl_dep_link_down_event(dev);
|
nfc_genl_dep_link_down_event(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
error:
|
error:
|
||||||
device_unlock(&dev->dev);
|
device_unlock(&dev->dev);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -503,6 +509,7 @@ EXPORT_SYMBOL(nfc_tm_activated);
|
||||||
int nfc_tm_deactivated(struct nfc_dev *dev)
|
int nfc_tm_deactivated(struct nfc_dev *dev)
|
||||||
{
|
{
|
||||||
dev->dep_link_up = false;
|
dev->dep_link_up = false;
|
||||||
|
dev->rf_mode = NFC_RF_NONE;
|
||||||
|
|
||||||
return nfc_genl_tm_deactivated(dev);
|
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) {
|
if (dev->active_target && timer_pending(&dev->check_pres_timer) == 0) {
|
||||||
rc = dev->ops->check_presence(dev, dev->active_target);
|
rc = dev->ops->check_presence(dev, dev->active_target);
|
||||||
|
if (rc == -EOPNOTSUPP)
|
||||||
|
goto exit;
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
mod_timer(&dev->check_pres_timer, jiffies +
|
mod_timer(&dev->check_pres_timer, jiffies +
|
||||||
msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
|
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);
|
device_unlock(&dev->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -753,7 +763,6 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
|
||||||
u32 supported_protocols,
|
u32 supported_protocols,
|
||||||
int tx_headroom, int tx_tailroom)
|
int tx_headroom, int tx_tailroom)
|
||||||
{
|
{
|
||||||
static atomic_t dev_no = ATOMIC_INIT(0);
|
|
||||||
struct nfc_dev *dev;
|
struct nfc_dev *dev;
|
||||||
|
|
||||||
if (!ops->start_poll || !ops->stop_poll || !ops->activate_target ||
|
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)
|
if (!dev)
|
||||||
return NULL;
|
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->ops = ops;
|
||||||
dev->supported_protocols = supported_protocols;
|
dev->supported_protocols = supported_protocols;
|
||||||
dev->tx_headroom = tx_headroom;
|
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);
|
nfc_genl_data_init(&dev->genl_data);
|
||||||
|
|
||||||
|
dev->rf_mode = NFC_RF_NONE;
|
||||||
|
|
||||||
/* first generation must not be 0 */
|
/* first generation must not be 0 */
|
||||||
dev->targets_generation = 1;
|
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));
|
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);
|
mutex_lock(&nfc_devlist_mutex);
|
||||||
nfc_devlist_generation++;
|
nfc_devlist_generation++;
|
||||||
rc = device_add(&dev->dev);
|
rc = device_add(&dev->dev);
|
||||||
|
@ -834,10 +847,12 @@ EXPORT_SYMBOL(nfc_register_device);
|
||||||
*/
|
*/
|
||||||
void nfc_unregister_device(struct nfc_dev *dev)
|
void nfc_unregister_device(struct nfc_dev *dev)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc, id;
|
||||||
|
|
||||||
pr_debug("dev_name=%s\n", dev_name(&dev->dev));
|
pr_debug("dev_name=%s\n", dev_name(&dev->dev));
|
||||||
|
|
||||||
|
id = dev->idx;
|
||||||
|
|
||||||
mutex_lock(&nfc_devlist_mutex);
|
mutex_lock(&nfc_devlist_mutex);
|
||||||
nfc_devlist_generation++;
|
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",
|
pr_debug("The userspace won't be notified that the device %s was removed\n",
|
||||||
dev_name(&dev->dev));
|
dev_name(&dev->dev));
|
||||||
|
|
||||||
|
ida_simple_remove(&nfc_index_ida, id);
|
||||||
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(nfc_unregister_device);
|
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,
|
*result = nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE,
|
||||||
NFC_HCI_ADM_CREATE_PIPE,
|
NFC_HCI_ADM_CREATE_PIPE,
|
||||||
(u8 *) ¶ms, sizeof(params), &skb);
|
(u8 *) ¶ms, sizeof(params), &skb);
|
||||||
if (*result == 0) {
|
if (*result < 0)
|
||||||
|
return NFC_HCI_INVALID_PIPE;
|
||||||
|
|
||||||
resp = (struct hci_create_pipe_resp *)skb->data;
|
resp = (struct hci_create_pipe_resp *)skb->data;
|
||||||
pipe = resp->pipe;
|
pipe = resp->pipe;
|
||||||
kfree_skb(skb);
|
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);
|
pr_debug("pipe created=%d\n", pipe);
|
||||||
|
|
||||||
return pipe;
|
return pipe;
|
||||||
} else
|
|
||||||
return NFC_HCI_INVALID_PIPE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nfc_hci_delete_pipe(struct nfc_hci_dev *hdev, u8 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)
|
static int nfc_hci_clear_all_pipes(struct nfc_hci_dev *hdev)
|
||||||
{
|
{
|
||||||
int r;
|
|
||||||
|
|
||||||
u8 param[2];
|
u8 param[2];
|
||||||
|
|
||||||
/* TODO: Find out what the identity reference data is
|
/* 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");
|
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);
|
NFC_HCI_ADM_CLEAR_ALL_PIPE, param, 2, NULL);
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int nfc_hci_disconnect_gate(struct nfc_hci_dev *hdev, u8 gate)
|
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);
|
-ETIME);
|
||||||
kfree(hdev->cmd_pending_msg);
|
kfree(hdev->cmd_pending_msg);
|
||||||
hdev->cmd_pending_msg = NULL;
|
hdev->cmd_pending_msg = NULL;
|
||||||
} else
|
} else {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
next_msg:
|
next_msg:
|
||||||
if (list_empty(&hdev->msg_tx_queue))
|
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 nfc_target *targets;
|
||||||
struct sk_buff *atqa_skb = NULL;
|
struct sk_buff *atqa_skb = NULL;
|
||||||
|
@ -263,6 +264,8 @@ static int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* if driver set the new gate, we will skip the old one */
|
||||||
|
if (targets->hci_reader_gate == 0x00)
|
||||||
targets->hci_reader_gate = gate;
|
targets->hci_reader_gate = gate;
|
||||||
|
|
||||||
r = nfc_targets_found(hdev->ndev, targets, 1);
|
r = nfc_targets_found(hdev->ndev, targets, 1);
|
||||||
|
@ -275,6 +278,7 @@ exit:
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(nfc_hci_target_discovered);
|
||||||
|
|
||||||
void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event,
|
void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event,
|
||||||
struct sk_buff *skb)
|
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));
|
nfc_hci_pipe2gate(hdev, pipe));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* TODO: Unknown events are hardware specific
|
if (hdev->ops->event_received) {
|
||||||
* pass them to the driver (needs a new hci_ops) */
|
hdev->ops->event_received(hdev,
|
||||||
|
nfc_hci_pipe2gate(hdev, pipe),
|
||||||
|
event, skb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
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);
|
return hdev->ops->start_poll(hdev, im_protocols, tm_protocols);
|
||||||
else
|
else
|
||||||
return nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
|
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)
|
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);
|
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,
|
static int hci_activate_target(struct nfc_dev *nfc_dev,
|
||||||
struct nfc_target *target, u32 protocol)
|
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) {
|
switch (target->hci_reader_gate) {
|
||||||
case NFC_HCI_RF_READER_A_GATE:
|
case NFC_HCI_RF_READER_A_GATE:
|
||||||
case NFC_HCI_RF_READER_B_GATE:
|
case NFC_HCI_RF_READER_B_GATE:
|
||||||
if (hdev->ops->data_exchange) {
|
if (hdev->ops->im_transceive) {
|
||||||
r = hdev->ops->data_exchange(hdev, target, skb, cb,
|
r = hdev->ops->im_transceive(hdev, target, skb, cb,
|
||||||
cb_context);
|
cb_context);
|
||||||
if (r <= 0) /* handled */
|
if (r <= 0) /* handled */
|
||||||
break;
|
break;
|
||||||
|
@ -604,14 +636,14 @@ static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
|
||||||
skb->len, hci_transceive_cb, hdev);
|
skb->len, hci_transceive_cb, hdev);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (hdev->ops->data_exchange) {
|
if (hdev->ops->im_transceive) {
|
||||||
r = hdev->ops->data_exchange(hdev, target, skb, cb,
|
r = hdev->ops->im_transceive(hdev, target, skb, cb,
|
||||||
cb_context);
|
cb_context);
|
||||||
if (r == 1)
|
if (r == 1)
|
||||||
r = -ENOTSUPP;
|
r = -ENOTSUPP;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
r = -ENOTSUPP;
|
r = -ENOTSUPP;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -620,6 +652,16 @@ static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
|
||||||
return r;
|
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,
|
static int hci_check_presence(struct nfc_dev *nfc_dev,
|
||||||
struct nfc_target *target)
|
struct nfc_target *target)
|
||||||
{
|
{
|
||||||
|
@ -723,9 +765,12 @@ static struct nfc_ops hci_nfc_ops = {
|
||||||
.dev_down = hci_dev_down,
|
.dev_down = hci_dev_down,
|
||||||
.start_poll = hci_start_poll,
|
.start_poll = hci_start_poll,
|
||||||
.stop_poll = hci_stop_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,
|
.activate_target = hci_activate_target,
|
||||||
.deactivate_target = hci_deactivate_target,
|
.deactivate_target = hci_deactivate_target,
|
||||||
.im_transceive = hci_transceive,
|
.im_transceive = hci_transceive,
|
||||||
|
.tm_send = hci_tm_send,
|
||||||
.check_presence = hci_check_presence,
|
.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);
|
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);
|
nfc_llc_rcv_from_drv(hdev->llc, skb);
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ int nfc_llc_register(const char *name, struct nfc_llc_ops *ops)
|
||||||
llc_engine->ops = ops;
|
llc_engine->ops = ops;
|
||||||
|
|
||||||
INIT_LIST_HEAD(&llc_engine->entry);
|
INIT_LIST_HEAD(&llc_engine->entry);
|
||||||
list_add_tail (&llc_engine->entry, &llc_engines);
|
list_add_tail(&llc_engine->entry, &llc_engines);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -634,9 +634,9 @@ static void llc_shdlc_sm_work(struct work_struct *work)
|
||||||
r = llc_shdlc_connect_initiate(shdlc);
|
r = llc_shdlc_connect_initiate(shdlc);
|
||||||
else
|
else
|
||||||
r = -ETIME;
|
r = -ETIME;
|
||||||
if (r < 0)
|
if (r < 0) {
|
||||||
llc_shdlc_connect_complete(shdlc, r);
|
llc_shdlc_connect_complete(shdlc, r);
|
||||||
else {
|
} else {
|
||||||
mod_timer(&shdlc->connect_timer, jiffies +
|
mod_timer(&shdlc->connect_timer, jiffies +
|
||||||
msecs_to_jiffies(SHDLC_CONNECT_VALUE_MS));
|
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);
|
llc_shdlc_handle_send_queue(shdlc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shdlc->hard_fault) {
|
if (shdlc->hard_fault)
|
||||||
shdlc->llc_failure(shdlc->hdev, shdlc->hard_fault);
|
shdlc->llc_failure(shdlc->hdev, shdlc->hard_fault);
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
config NFC_LLCP
|
config NFC_LLCP
|
||||||
depends on NFC && EXPERIMENTAL
|
depends on NFC
|
||||||
bool "NFC LLCP support (EXPERIMENTAL)"
|
bool "NFC LLCP support"
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
Say Y here if you want to build support for a kernel NFC LLCP
|
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 sk_buff *skb;
|
||||||
struct nfc_dev *dev;
|
struct nfc_dev *dev;
|
||||||
struct nfc_llcp_local *local;
|
struct nfc_llcp_local *local;
|
||||||
u16 size = 0;
|
|
||||||
|
|
||||||
pr_debug("Sending DISC\n");
|
pr_debug("Sending DISC\n");
|
||||||
|
|
||||||
|
@ -273,17 +272,10 @@ int nfc_llcp_disconnect(struct nfc_llcp_sock *sock)
|
||||||
if (dev == NULL)
|
if (dev == NULL)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
size += LLCP_HEADER_SIZE;
|
skb = llcp_allocate_pdu(sock, LLCP_PDU_DISC, 0);
|
||||||
size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE;
|
|
||||||
|
|
||||||
skb = alloc_skb(size, GFP_ATOMIC);
|
|
||||||
if (skb == NULL)
|
if (skb == NULL)
|
||||||
return -ENOMEM;
|
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);
|
skb_queue_tail(&local->tx_queue, skb);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -324,8 +316,7 @@ int nfc_llcp_send_connect(struct nfc_llcp_sock *sock)
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
u8 *service_name_tlv = NULL, service_name_tlv_length;
|
u8 *service_name_tlv = NULL, service_name_tlv_length;
|
||||||
u8 *miux_tlv = NULL, miux_tlv_length;
|
u8 *miux_tlv = NULL, miux_tlv_length;
|
||||||
u8 *rw_tlv = NULL, rw_tlv_length, rw;
|
u8 *rw_tlv = NULL, rw_tlv_length;
|
||||||
__be16 miux;
|
|
||||||
int err;
|
int err;
|
||||||
u16 size = 0;
|
u16 size = 0;
|
||||||
|
|
||||||
|
@ -343,13 +334,11 @@ int nfc_llcp_send_connect(struct nfc_llcp_sock *sock)
|
||||||
size += service_name_tlv_length;
|
size += service_name_tlv_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
miux = cpu_to_be16(LLCP_MAX_MIUX);
|
miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0,
|
||||||
miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0,
|
|
||||||
&miux_tlv_length);
|
&miux_tlv_length);
|
||||||
size += miux_tlv_length;
|
size += miux_tlv_length;
|
||||||
|
|
||||||
rw = LLCP_MAX_RW;
|
rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &local->rw, 0, &rw_tlv_length);
|
||||||
rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length);
|
|
||||||
size += rw_tlv_length;
|
size += rw_tlv_length;
|
||||||
|
|
||||||
pr_debug("SKB size %d SN length %zu\n", size, sock->service_name_len);
|
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 nfc_llcp_local *local;
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
u8 *miux_tlv = NULL, miux_tlv_length;
|
u8 *miux_tlv = NULL, miux_tlv_length;
|
||||||
u8 *rw_tlv = NULL, rw_tlv_length, rw;
|
u8 *rw_tlv = NULL, rw_tlv_length;
|
||||||
__be16 miux;
|
|
||||||
int err;
|
int err;
|
||||||
u16 size = 0;
|
u16 size = 0;
|
||||||
|
|
||||||
|
@ -397,13 +385,11 @@ int nfc_llcp_send_cc(struct nfc_llcp_sock *sock)
|
||||||
if (local == NULL)
|
if (local == NULL)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
miux = cpu_to_be16(LLCP_MAX_MIUX);
|
miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0,
|
||||||
miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0,
|
|
||||||
&miux_tlv_length);
|
&miux_tlv_length);
|
||||||
size += miux_tlv_length;
|
size += miux_tlv_length;
|
||||||
|
|
||||||
rw = LLCP_MAX_RW;
|
rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &local->rw, 0, &rw_tlv_length);
|
||||||
rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length);
|
|
||||||
size += rw_tlv_length;
|
size += rw_tlv_length;
|
||||||
|
|
||||||
skb = llcp_allocate_pdu(sock, LLCP_PDU_CC, size);
|
skb = llcp_allocate_pdu(sock, LLCP_PDU_CC, size);
|
||||||
|
@ -428,6 +414,52 @@ error_tlv:
|
||||||
return err;
|
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)
|
int nfc_llcp_send_dm(struct nfc_llcp_local *local, u8 ssap, u8 dsap, u8 reason)
|
||||||
{
|
{
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
|
@ -541,6 +573,52 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
|
||||||
return len;
|
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)
|
int nfc_llcp_send_rr(struct nfc_llcp_sock *sock)
|
||||||
{
|
{
|
||||||
struct sk_buff *skb;
|
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);
|
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)
|
static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool listen)
|
||||||
{
|
{
|
||||||
struct sock *sk;
|
struct sock *sk;
|
||||||
struct hlist_node *node, *tmp;
|
struct hlist_node *node, *tmp;
|
||||||
struct nfc_llcp_sock *llcp_sock;
|
struct nfc_llcp_sock *llcp_sock;
|
||||||
|
|
||||||
|
skb_queue_purge(&local->tx_queue);
|
||||||
|
|
||||||
write_lock(&local->sockets.lock);
|
write_lock(&local->sockets.lock);
|
||||||
|
|
||||||
sk_for_each_safe(sk, node, tmp, &local->sockets.head) {
|
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);
|
bh_lock_sock(sk);
|
||||||
|
|
||||||
|
nfc_llcp_socket_purge(llcp_sock);
|
||||||
|
|
||||||
if (sk->sk_state == LLCP_CONNECTED)
|
if (sk->sk_state == LLCP_CONNECTED)
|
||||||
nfc_put_device(llcp_sock->dev);
|
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 nfc_llcp_sock *lsk, *n;
|
||||||
struct sock *accept_sk;
|
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_queue) {
|
||||||
accept_sk = &lsk->sk;
|
accept_sk = &lsk->sk;
|
||||||
bh_lock_sock(accept_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;
|
sk->sk_state = LLCP_CLOSED;
|
||||||
|
|
||||||
bh_unlock_sock(sk);
|
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 sock *sk;
|
||||||
struct hlist_node *node;
|
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);
|
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;
|
llcp_sock = NULL;
|
||||||
|
|
||||||
sk_for_each(sk, node, &local->sockets.head) {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
read_unlock(&local->sockets.lock);
|
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);
|
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;
|
continue;
|
||||||
|
|
||||||
if (tmp_sock->service_name == NULL ||
|
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)
|
static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
|
||||||
{
|
{
|
||||||
u8 *gb_cur, *version_tlv, version, version_length;
|
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 *wks_tlv, wks_length;
|
||||||
u8 *miux_tlv, miux_length;
|
u8 *miux_tlv, miux_length;
|
||||||
__be16 miux;
|
|
||||||
u8 gb_len = 0;
|
u8 gb_len = 0;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
@ -433,9 +478,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
|
||||||
1, &version_length);
|
1, &version_length);
|
||||||
gb_len += version_length;
|
gb_len += version_length;
|
||||||
|
|
||||||
/* 1500 ms */
|
lto_tlv = nfc_llcp_build_tlv(LLCP_TLV_LTO, &local->lto, 1, <o_length);
|
||||||
lto = 150;
|
|
||||||
lto_tlv = nfc_llcp_build_tlv(LLCP_TLV_LTO, <o, 1, <o_length);
|
|
||||||
gb_len += lto_length;
|
gb_len += lto_length;
|
||||||
|
|
||||||
pr_debug("Local wks 0x%lx\n", local->local_wks);
|
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);
|
&wks_length);
|
||||||
gb_len += wks_length;
|
gb_len += wks_length;
|
||||||
|
|
||||||
miux = cpu_to_be16(LLCP_MAX_MIUX);
|
miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0,
|
||||||
miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0,
|
|
||||||
&miux_length);
|
&miux_length);
|
||||||
gb_len += miux_length;
|
gb_len += miux_length;
|
||||||
|
|
||||||
|
@ -610,7 +652,10 @@ static void nfc_llcp_tx_work(struct work_struct *work)
|
||||||
if (skb != NULL) {
|
if (skb != NULL) {
|
||||||
sk = skb->sk;
|
sk = skb->sk;
|
||||||
llcp_sock = nfc_llcp_sock(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;
|
int ret;
|
||||||
|
|
||||||
pr_debug("Sending pending skb\n");
|
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_queue_tail(&llcp_sock->tx_pending_queue,
|
||||||
skb);
|
skb);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
nfc_llcp_send_symm(local->dev);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
nfc_llcp_send_symm(local->dev);
|
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;
|
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,
|
static void nfc_llcp_recv_connect(struct nfc_llcp_local *local,
|
||||||
struct sk_buff *skb)
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
|
@ -823,9 +899,6 @@ static void nfc_llcp_recv_connect(struct nfc_llcp_local *local,
|
||||||
fail:
|
fail:
|
||||||
/* Send DM */
|
/* Send DM */
|
||||||
nfc_llcp_send_dm(local, dsap, ssap, reason);
|
nfc_llcp_send_dm(local, dsap, ssap, reason);
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int nfc_llcp_queue_i_frames(struct nfc_llcp_sock *sock)
|
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;
|
sk = &llcp_sock->sk;
|
||||||
lock_sock(sk);
|
lock_sock(sk);
|
||||||
|
|
||||||
|
nfc_llcp_socket_purge(llcp_sock);
|
||||||
|
|
||||||
if (sk->sk_state == LLCP_CLOSED) {
|
if (sk->sk_state == LLCP_CLOSED) {
|
||||||
release_sock(sk);
|
release_sock(sk);
|
||||||
nfc_llcp_sock_put(llcp_sock);
|
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) {
|
if (llcp_sock == NULL) {
|
||||||
pr_err("Invalid DM\n");
|
pr_debug("Already closed\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1038,8 +1114,100 @@ static void nfc_llcp_recv_dm(struct nfc_llcp_local *local, struct sk_buff *skb)
|
||||||
sk->sk_state_change(sk);
|
sk->sk_state_change(sk);
|
||||||
|
|
||||||
nfc_llcp_sock_put(llcp_sock);
|
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;
|
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)
|
static void nfc_llcp_rx_work(struct work_struct *work)
|
||||||
|
@ -1072,6 +1240,11 @@ static void nfc_llcp_rx_work(struct work_struct *work)
|
||||||
pr_debug("SYMM\n");
|
pr_debug("SYMM\n");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case LLCP_PDU_UI:
|
||||||
|
pr_debug("UI\n");
|
||||||
|
nfc_llcp_recv_ui(local, skb);
|
||||||
|
break;
|
||||||
|
|
||||||
case LLCP_PDU_CONNECT:
|
case LLCP_PDU_CONNECT:
|
||||||
pr_debug("CONNECT\n");
|
pr_debug("CONNECT\n");
|
||||||
nfc_llcp_recv_connect(local, skb);
|
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);
|
nfc_llcp_recv_dm(local, skb);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case LLCP_PDU_SNL:
|
||||||
|
pr_debug("SNL\n");
|
||||||
|
nfc_llcp_recv_snl(local, skb);
|
||||||
|
break;
|
||||||
|
|
||||||
case LLCP_PDU_I:
|
case LLCP_PDU_I:
|
||||||
case LLCP_PDU_RR:
|
case LLCP_PDU_RR:
|
||||||
case LLCP_PDU_RNR:
|
case LLCP_PDU_RNR:
|
||||||
|
@ -1104,8 +1282,6 @@ static void nfc_llcp_rx_work(struct work_struct *work)
|
||||||
schedule_work(&local->tx_work);
|
schedule_work(&local->tx_work);
|
||||||
kfree_skb(local->rx_pending);
|
kfree_skb(local->rx_pending);
|
||||||
local->rx_pending = NULL;
|
local->rx_pending = NULL;
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nfc_llcp_recv(void *data, struct sk_buff *skb, int err)
|
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);
|
local->rx_pending = skb_get(skb);
|
||||||
del_timer(&local->link_timer);
|
del_timer(&local->link_timer);
|
||||||
schedule_work(&local->rx_work);
|
schedule_work(&local->rx_work);
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb)
|
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->connecting_sockets.lock);
|
||||||
rwlock_init(&local->raw_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);
|
nfc_llcp_build_gb(local);
|
||||||
|
|
||||||
local->remote_miu = LLCP_DEFAULT_MIU;
|
local->remote_miu = LLCP_DEFAULT_MIU;
|
||||||
|
|
|
@ -64,6 +64,9 @@ struct nfc_llcp_local {
|
||||||
u32 target_idx;
|
u32 target_idx;
|
||||||
u8 rf_mode;
|
u8 rf_mode;
|
||||||
u8 comm_mode;
|
u8 comm_mode;
|
||||||
|
u8 lto;
|
||||||
|
u8 rw;
|
||||||
|
__be16 miux;
|
||||||
unsigned long local_wks; /* Well known services */
|
unsigned long local_wks; /* Well known services */
|
||||||
unsigned long local_sdp; /* Local services */
|
unsigned long local_sdp; /* Local services */
|
||||||
unsigned long local_sap; /* Local SAPs, not available for discovery */
|
unsigned long local_sap; /* Local SAPs, not available for discovery */
|
||||||
|
@ -124,6 +127,13 @@ struct nfc_llcp_sock {
|
||||||
struct sock *parent;
|
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_sock(sk) ((struct nfc_llcp_sock *) (sk))
|
||||||
#define nfc_llcp_dev(sk) (nfc_llcp_sock((sk))->dev)
|
#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_symm(struct nfc_dev *dev);
|
||||||
int nfc_llcp_send_connect(struct nfc_llcp_sock *sock);
|
int nfc_llcp_send_connect(struct nfc_llcp_sock *sock);
|
||||||
int nfc_llcp_send_cc(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_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_disconnect(struct nfc_llcp_sock *sock);
|
||||||
int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
|
int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
|
||||||
struct msghdr *msg, size_t len);
|
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);
|
int nfc_llcp_send_rr(struct nfc_llcp_sock *sock);
|
||||||
|
|
||||||
/* Socket API */
|
/* Socket API */
|
||||||
|
|
|
@ -205,8 +205,8 @@ static int llcp_sock_listen(struct socket *sock, int backlog)
|
||||||
|
|
||||||
lock_sock(sk);
|
lock_sock(sk);
|
||||||
|
|
||||||
if ((sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM)
|
if ((sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM) ||
|
||||||
|| sk->sk_state != LLCP_BOUND) {
|
sk->sk_state != LLCP_BOUND) {
|
||||||
ret = -EBADFD;
|
ret = -EBADFD;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
@ -608,6 +608,25 @@ static int llcp_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
|
||||||
|
|
||||||
lock_sock(sk);
|
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) {
|
if (sk->sk_state != LLCP_CONNECTED) {
|
||||||
release_sock(sk);
|
release_sock(sk);
|
||||||
return -ENOTCONN;
|
return -ENOTCONN;
|
||||||
|
@ -663,11 +682,28 @@ static int llcp_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
|
||||||
return -EFAULT;
|
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 */
|
/* Mark read part of skb as used */
|
||||||
if (!(flags & MSG_PEEK)) {
|
if (!(flags & MSG_PEEK)) {
|
||||||
|
|
||||||
/* SOCK_STREAM: re-queue skb if it contains unreceived data */
|
/* 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);
|
skb_pull(skb, copied);
|
||||||
if (skb->len) {
|
if (skb->len) {
|
||||||
skb_queue_head(&sk->sk_receive_queue, skb);
|
skb_queue_head(&sk->sk_receive_queue, skb);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
config NFC_NCI
|
config NFC_NCI
|
||||||
depends on NFC && EXPERIMENTAL
|
depends on NFC
|
||||||
tristate "NCI protocol support (EXPERIMENTAL)"
|
tristate "NCI protocol support"
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
NCI (NFC Controller Interface) is a communication protocol between
|
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;
|
cmd.num_disc_configs = 0;
|
||||||
|
|
||||||
if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
|
if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
|
||||||
(protocols & NFC_PROTO_JEWEL_MASK
|
(protocols & NFC_PROTO_JEWEL_MASK ||
|
||||||
|| protocols & NFC_PROTO_MIFARE_MASK
|
protocols & NFC_PROTO_MIFARE_MASK ||
|
||||||
|| protocols & NFC_PROTO_ISO14443_MASK
|
protocols & NFC_PROTO_ISO14443_MASK ||
|
||||||
|| protocols & NFC_PROTO_NFC_DEP_MASK)) {
|
protocols & NFC_PROTO_NFC_DEP_MASK)) {
|
||||||
cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
|
cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
|
||||||
NCI_NFC_A_PASSIVE_POLL_MODE;
|
NCI_NFC_A_PASSIVE_POLL_MODE;
|
||||||
cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
|
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) &&
|
if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
|
||||||
(protocols & NFC_PROTO_FELICA_MASK
|
(protocols & NFC_PROTO_FELICA_MASK ||
|
||||||
|| protocols & NFC_PROTO_NFC_DEP_MASK)) {
|
protocols & NFC_PROTO_NFC_DEP_MASK)) {
|
||||||
cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
|
cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
|
||||||
NCI_NFC_F_PASSIVE_POLL_MODE;
|
NCI_NFC_F_PASSIVE_POLL_MODE;
|
||||||
cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
|
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_dev *ndev = nfc_get_drvdata(nfc_dev);
|
||||||
struct nci_set_config_param param;
|
struct nci_set_config_param param;
|
||||||
__u8 local_gb[NFC_MAX_GT_LEN];
|
__u8 local_gb[NFC_MAX_GT_LEN];
|
||||||
int i, rc = 0;
|
int i;
|
||||||
|
|
||||||
param.val = nfc_get_local_general_bytes(nfc_dev, ¶m.len);
|
param.val = nfc_get_local_general_bytes(nfc_dev, ¶m.len);
|
||||||
if ((param.val == NULL) || (param.len == 0))
|
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;
|
return -EINVAL;
|
||||||
|
|
||||||
for (i = 0; i < param.len; i++)
|
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.id = NCI_PN_ATR_REQ_GEN_BYTES;
|
||||||
param.val = local_gb;
|
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));
|
msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nci_start_poll(struct nfc_dev *nfc_dev,
|
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,
|
static int nci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
|
||||||
__u8 comm_mode, __u8 *gb, size_t gb_len)
|
__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);
|
pr_debug("len %d\n", skb->len);
|
||||||
|
|
||||||
if (!ndev || (!test_bit(NCI_UP, &ndev->flags)
|
if (!ndev || (!test_bit(NCI_UP, &ndev->flags) &&
|
||||||
&& !test_bit(NCI_INIT, &ndev->flags))) {
|
!test_bit(NCI_INIT, &ndev->flags))) {
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
|
|
||||||
#include "nfc.h"
|
#include "nfc.h"
|
||||||
|
|
||||||
|
#include "llcp/llcp.h"
|
||||||
|
|
||||||
static struct genl_multicast_group nfc_genl_event_mcgrp = {
|
static struct genl_multicast_group nfc_genl_event_mcgrp = {
|
||||||
.name = NFC_GENL_MCAST_EVENT_NAME,
|
.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)) ||
|
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_DEVICE_INDEX, dev->idx) ||
|
||||||
nla_put_u32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols) ||
|
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;
|
goto nla_put_failure;
|
||||||
|
|
||||||
return genlmsg_end(msg, hdr);
|
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;
|
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[] = {
|
static struct genl_ops nfc_genl_ops[] = {
|
||||||
{
|
{
|
||||||
.cmd = NFC_CMD_GET_DEVICE,
|
.cmd = NFC_CMD_GET_DEVICE,
|
||||||
|
@ -759,6 +902,16 @@ static struct genl_ops nfc_genl_ops[] = {
|
||||||
.done = nfc_genl_dump_targets_done,
|
.done = nfc_genl_dump_targets_done,
|
||||||
.policy = nfc_genl_policy,
|
.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);
|
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);
|
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);
|
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);
|
int __init nfc_llcp_init(void);
|
||||||
void nfc_llcp_exit(void);
|
void nfc_llcp_exit(void);
|
||||||
|
|
||||||
|
@ -97,6 +98,11 @@ static inline int nfc_llcp_data_received(struct nfc_dev *dev,
|
||||||
return 0;
|
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)
|
static inline int nfc_llcp_init(void)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -256,7 +256,6 @@ static int rawsock_recvmsg(struct kiocb *iocb, struct socket *sock,
|
||||||
return rc ? : copied;
|
return rc ? : copied;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static const struct proto_ops rawsock_ops = {
|
static const struct proto_ops rawsock_ops = {
|
||||||
.family = PF_NFC,
|
.family = PF_NFC,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
|
|
Загрузка…
Ссылка в новой задаче