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:
John W. Linville 2012-10-29 14:52:37 -04:00
Родитель d1f1030256 52feb444a9
Коммит e298c79efc
25 изменённых файлов: 1473 добавлений и 545 удалений

Просмотреть файл

@ -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

500
drivers/nfc/pn544/i2c.c Normal file
Просмотреть файл

@ -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)
{ {
return nfc_hci_send_cmd(hdev, target->hci_reader_gate, pr_debug("supported protocol %d", target->supported_protocols);
PN544_RF_READER_CMD_PRESENCE_CHECK, if (target->supported_protocols & (NFC_PROTO_ISO14443_MASK |
NULL, 0, NULL); NFC_PROTO_ISO14443_B_MASK)) {
return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
PN544_RF_READER_CMD_PRESENCE_CHECK,
NULL, 0, NULL);
} else if (target->supported_protocols & NFC_PROTO_MIFARE_MASK) {
if (target->nfcid1_len != 4 && target->nfcid1_len != 7 &&
target->nfcid1_len != 10)
return -EOPNOTSUPP;
return nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE,
PN544_RF_READER_CMD_ACTIVATE_NEXT,
target->nfcid1, target->nfcid1_len, NULL);
} else if (target->supported_protocols & NFC_PROTO_JEWEL_MASK) {
return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
PN544_JEWEL_RAW_CMD, NULL, 0, NULL);
} else if (target->supported_protocols & NFC_PROTO_FELICA_MASK) {
return nfc_hci_send_cmd(hdev, PN544_RF_READER_F_GATE,
PN544_FELICA_RAW, NULL, 0, NULL);
} else if (target->supported_protocols & NFC_PROTO_NFC_DEP_MASK) {
return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
PN544_HCI_CMD_ATTREQUEST,
NULL, 0, NULL);
}
return 0;
}
static void pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate,
u8 event, struct sk_buff *skb)
{
struct sk_buff *rgb_skb = NULL;
int r = 0;
pr_debug("hci event %d", event);
switch (event) {
case PN544_HCI_EVT_ACTIVATED:
if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE)
nfc_hci_target_discovered(hdev, gate);
else if (gate == PN544_RF_READER_NFCIP1_TARGET_GATE) {
r = nfc_hci_get_param(hdev, gate, PN544_DEP_ATR_REQ,
&rgb_skb);
if (r < 0)
goto exit;
nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK,
NFC_COMM_PASSIVE, rgb_skb->data,
rgb_skb->len);
kfree_skb(rgb_skb);
}
break;
case PN544_HCI_EVT_DEACTIVATED:
nfc_hci_send_event(hdev, gate,
NFC_HCI_EVT_END_OPERATION, NULL, 0);
break;
case PN544_HCI_EVT_RCV_DATA:
if (skb->len < 2) {
r = -EPROTO;
goto exit;
}
if (skb->data[0] != 0) {
pr_debug("data0 %d", skb->data[0]);
r = -EPROTO;
goto exit;
}
skb_pull(skb, 2);
nfc_tm_data_received(hdev->ndev, skb);
return;
default:
break;
}
exit:
kfree_skb(skb);
} }
static struct nfc_hci_ops pn544_hci_ops = { 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);

32
drivers/nfc/pn544/pn544.h Normal file
Просмотреть файл

@ -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,16 +257,16 @@ static u8 nfc_hci_create_pipe(struct nfc_hci_dev *hdev, u8 dest_host,
*result = nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE, *result = nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE,
NFC_HCI_ADM_CREATE_PIPE, NFC_HCI_ADM_CREATE_PIPE,
(u8 *) &params, sizeof(params), &skb); (u8 *) &params, sizeof(params), &skb);
if (*result == 0) { if (*result < 0)
resp = (struct hci_create_pipe_resp *)skb->data;
pipe = resp->pipe;
kfree_skb(skb);
pr_debug("pipe created=%d\n", pipe);
return pipe;
} else
return NFC_HCI_INVALID_PIPE; return NFC_HCI_INVALID_PIPE;
resp = (struct hci_create_pipe_resp *)skb->data;
pipe = resp->pipe;
kfree_skb(skb);
pr_debug("pipe created=%d\n", pipe);
return 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,8 +65,9 @@ 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:
@ -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,7 +264,9 @@ static int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate)
break; break;
} }
targets->hci_reader_gate = gate; /* if driver set the new gate, we will skip the old one */
if (targets->hci_reader_gate == 0x00)
targets->hci_reader_gate = gate;
r = nfc_targets_found(hdev->ndev, targets, 1); 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,10 +185,12 @@ 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, &lto_length);
lto = 150;
lto_tlv = nfc_llcp_build_tlv(LLCP_TLV_LTO, &lto, 1, &lto_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);
}
return; static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
struct sk_buff *skb)
{
struct nfc_llcp_sock *llcp_sock;
u8 dsap, ssap, *tlv, type, length, tid, sap;
u16 tlv_len, offset;
char *service_name;
size_t service_name_len;
dsap = nfc_llcp_dsap(skb);
ssap = nfc_llcp_ssap(skb);
pr_debug("%d %d\n", dsap, ssap);
if (dsap != LLCP_SAP_SDP || ssap != LLCP_SAP_SDP) {
pr_err("Wrong SNL SAP\n");
return;
}
tlv = &skb->data[LLCP_HEADER_SIZE];
tlv_len = skb->len - LLCP_HEADER_SIZE;
offset = 0;
while (offset < tlv_len) {
type = tlv[0];
length = tlv[1];
switch (type) {
case LLCP_TLV_SDREQ:
tid = tlv[2];
service_name = (char *) &tlv[3];
service_name_len = length - 1;
pr_debug("Looking for %.16s\n", service_name);
if (service_name_len == strlen("urn:nfc:sn:sdp") &&
!strncmp(service_name, "urn:nfc:sn:sdp",
service_name_len)) {
sap = 1;
goto send_snl;
}
llcp_sock = nfc_llcp_sock_from_sn(local, service_name,
service_name_len);
if (!llcp_sock) {
sap = 0;
goto send_snl;
}
/*
* We found a socket but its ssap has not been reserved
* yet. We need to assign it for good and send a reply.
* The ssap will be freed when the socket is closed.
*/
if (llcp_sock->ssap == LLCP_SDP_UNBOUND) {
atomic_t *client_count;
sap = nfc_llcp_reserve_sdp_ssap(local);
pr_debug("Reserving %d\n", sap);
if (sap == LLCP_SAP_MAX) {
sap = 0;
goto send_snl;
}
client_count =
&local->local_sdp_cnt[sap -
LLCP_WKS_NUM_SAP];
atomic_inc(client_count);
llcp_sock->ssap = sap;
llcp_sock->reserved_ssap = sap;
} else {
sap = llcp_sock->ssap;
}
pr_debug("%p %d\n", llcp_sock, sap);
send_snl:
nfc_llcp_send_snl(local, tid, sap);
break;
default:
pr_err("Invalid SNL tlv value 0x%x\n", type);
break;
}
offset += length + 2;
tlv += length + 2;
}
} }
static void nfc_llcp_rx_work(struct work_struct *work) 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, &param.len); param.val = nfc_get_local_general_bytes(nfc_dev, &param.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)&param, return nci_request(ndev, nci_set_config_req, (unsigned long)&param,
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);
@ -590,7 +593,7 @@ static int nfc_genl_start_poll(struct sk_buff *skb, struct genl_info *info)
if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
((!info->attrs[NFC_ATTR_IM_PROTOCOLS] && ((!info->attrs[NFC_ATTR_IM_PROTOCOLS] &&
!info->attrs[NFC_ATTR_PROTOCOLS]) && !info->attrs[NFC_ATTR_PROTOCOLS]) &&
!info->attrs[NFC_ATTR_TM_PROTOCOLS])) !info->attrs[NFC_ATTR_TM_PROTOCOLS]))
return -EINVAL; return -EINVAL;
idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
@ -715,6 +718,146 @@ static int nfc_genl_dep_link_down(struct sk_buff *skb, struct genl_info *info)
return rc; 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,