This is the first NFC pull request for the 3.7 merge window.
With this one we get: - HCI and LLC layers separation. We now can support various LLC protocols for HCI drivers, SHDLC being one of them. This will be needed as we're planning to support raw HCI chipsets that do the SHDLC encapsulation in firmware. So for now we have an SHDLC and a NOP LLC layers. - pn533 command queueing implementation. This simplifies the pn533 locking logic and fixes a kernel warning. - NCI p2p initiator mode implementation. - Replace custom workqueues with system ones, for HCI and LLCP. - Raw pn544 driver removal, as scheduled on the features-removal.txt file. - A few HCI, SHDLC and LLCP fixes. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAABAgAGBQJQYNzhAAoJEIqAPN1PVmxKv/QP/1potGZKprGwLWMICOIOKpVz MXTuUKTnNVW3Uosa0dpxy7Z9/DIUE2eHvgDPEvM+VCd3JB0+B+w1oCH1Ash5D9Cd N+SSz/iFWEc7YQQkE4fqCJJ7ox+3Bq0c7la1xMqTorPQDmXpIBBbOOiiKfihTlN/ pI7GJ/8MSgWKoC9EIlkgv6uUlnco8tnUxrHQ5NwoW9O0PL4W1NKkr1LbA3K9f9Gv 77pD32+rMUv4Xk7Uup4/DC8+LiAyJO12kccXIcZj+0omGQ16cqxGr6sa4mFxHQuw +mroei6D/t9wwzzPnDgUUSVbNHsEuCKk7wZyjjZQG+6HGk7zlmS/yQcs+ltH5Mc+ aQJi3FSYy0muzgmGw7dWvL1KfowndOYOOv6IJLUiUkp26nltCwekcqmOlGScRTWt JEjkbU9t6EhhHPX6CmnMgM7VRNmHkkKMSy4Sz2SdM4wJxTWezDNZ+Q3aT0f1278f P4EdhvfriBxDqo1HYtBWVgG6emfE9A+2lpsKljkUr3D4qfjOQLonK7RsMRySplE1 wg0j+ou1BVbdSfkD4CAQWmfn9t0idlWnUUzmle4LwyrJdiItqBKJh86zsoMKpjsI WgtvlsyEOcfqiazGpUB0RSNUdprjasXFT9gC+k78q1ayTI0ysJomZQ2LtnAHoaM1 Ss/03CgKYjh6G1nFH1rk =yFjv -----END PGP SIGNATURE----- Merge tag 'nfc-next-3.7-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/nfc-3.0 So says Samuel Ortiz <sameo@linux.intel.com>: This is the first NFC pull request for the 3.7 merge window. With this one we get: - HCI and LLC layers separation. We now can support various LLC protocols for HCI drivers, SHDLC being one of them. This will be needed as we're planning to support raw HCI chipsets that do the SHDLC encapsulation in firmware. So for now we have an SHDLC and a NOP LLC layers. - pn533 command queueing implementation. This simplifies the pn533 locking logic and fixes a kernel warning. - NCI p2p initiator mode implementation. - Replace custom workqueues with system ones, for HCI and LLCP. - Raw pn544 driver removal, as scheduled on the features-removal.txt file. - A few HCI, SHDLC and LLCP fixes. Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
commit
0c49b69931
|
@ -516,18 +516,6 @@ Who: Kees Cook <keescook@chromium.org>
|
|||
|
||||
----------------------------
|
||||
|
||||
What: Removing the pn544 raw driver.
|
||||
When: 3.6
|
||||
Why: With the introduction of the NFC HCI and SHDL kernel layers, pn544.c
|
||||
is being replaced by pn544_hci.c which is accessible through the netlink
|
||||
and socket NFC APIs. Moreover, pn544.c is outdated and does not seem to
|
||||
work properly with the latest Android stacks.
|
||||
Having 2 drivers for the same hardware is confusing and as such we
|
||||
should only keep the one following the kernel NFC APIs.
|
||||
Who: Samuel Ortiz <sameo@linux.intel.com>
|
||||
|
||||
----------------------------
|
||||
|
||||
What: setitimer accepts user NULL pointer (value)
|
||||
When: 3.6
|
||||
Why: setitimer is not returning -EFAULT if user pointer is NULL. This
|
||||
|
|
|
@ -4793,6 +4793,7 @@ M: Lauro Ramos Venancio <lauro.venancio@openbossa.org>
|
|||
M: Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
|
||||
M: Samuel Ortiz <sameo@linux.intel.com>
|
||||
L: linux-wireless@vger.kernel.org
|
||||
L: linux-nfc@lists.01.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
F: net/nfc/
|
||||
F: include/linux/nfc.h
|
||||
|
|
|
@ -5,21 +5,9 @@
|
|||
menu "Near Field Communication (NFC) devices"
|
||||
depends on NFC
|
||||
|
||||
config PN544_NFC
|
||||
tristate "PN544 NFC driver"
|
||||
depends on I2C
|
||||
select CRC_CCITT
|
||||
default n
|
||||
---help---
|
||||
Say yes if you want PN544 Near Field Communication driver.
|
||||
This is for i2c connected version. If unsure, say N here.
|
||||
|
||||
To compile this driver as a module, choose m here. The module will
|
||||
be called pn544.
|
||||
|
||||
config PN544_HCI_NFC
|
||||
tristate "HCI PN544 NFC driver"
|
||||
depends on I2C && NFC_SHDLC
|
||||
depends on I2C && NFC_HCI && NFC_SHDLC
|
||||
select CRC_CCITT
|
||||
default n
|
||||
---help---
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
# Makefile for nfc devices
|
||||
#
|
||||
|
||||
obj-$(CONFIG_PN544_NFC) += pn544.o
|
||||
obj-$(CONFIG_PN544_HCI_NFC) += pn544_hci.o
|
||||
obj-$(CONFIG_NFC_PN533) += pn533.o
|
||||
obj-$(CONFIG_NFC_WILINK) += nfcwilink.o
|
||||
|
|
|
@ -352,8 +352,6 @@ static long nfcwilink_receive(void *priv_data, struct sk_buff *skb)
|
|||
struct nfcwilink *drv = priv_data;
|
||||
int rc;
|
||||
|
||||
nfc_dev_dbg(&drv->pdev->dev, "receive entry, len %d", skb->len);
|
||||
|
||||
if (!skb)
|
||||
return -EFAULT;
|
||||
|
||||
|
@ -362,6 +360,8 @@ static long nfcwilink_receive(void *priv_data, struct sk_buff *skb)
|
|||
return -EFAULT;
|
||||
}
|
||||
|
||||
nfc_dev_dbg(&drv->pdev->dev, "receive entry, len %d", skb->len);
|
||||
|
||||
/* strip the ST header
|
||||
(apart for the chnl byte, which is not received in the hdr) */
|
||||
skb_pull(skb, (NFCWILINK_HDR_LEN-1));
|
||||
|
@ -604,21 +604,7 @@ static struct platform_driver nfcwilink_driver = {
|
|||
},
|
||||
};
|
||||
|
||||
/* ------- Module Init/Exit interfaces ------ */
|
||||
static int __init nfcwilink_init(void)
|
||||
{
|
||||
printk(KERN_INFO "NFC Driver for TI WiLink");
|
||||
|
||||
return platform_driver_register(&nfcwilink_driver);
|
||||
}
|
||||
|
||||
static void __exit nfcwilink_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&nfcwilink_driver);
|
||||
}
|
||||
|
||||
module_init(nfcwilink_init);
|
||||
module_exit(nfcwilink_exit);
|
||||
module_platform_driver(nfcwilink_driver);
|
||||
|
||||
/* ------ Module Info ------ */
|
||||
|
||||
|
|
|
@ -356,6 +356,7 @@ struct pn533 {
|
|||
|
||||
struct workqueue_struct *wq;
|
||||
struct work_struct cmd_work;
|
||||
struct work_struct cmd_complete_work;
|
||||
struct work_struct poll_work;
|
||||
struct work_struct mi_work;
|
||||
struct work_struct tg_work;
|
||||
|
@ -383,6 +384,19 @@ struct pn533 {
|
|||
u8 tgt_mode;
|
||||
|
||||
u32 device_type;
|
||||
|
||||
struct list_head cmd_queue;
|
||||
u8 cmd_pending;
|
||||
};
|
||||
|
||||
struct pn533_cmd {
|
||||
struct list_head queue;
|
||||
struct pn533_frame *out_frame;
|
||||
struct pn533_frame *in_frame;
|
||||
int in_frame_len;
|
||||
pn533_cmd_complete_t cmd_complete;
|
||||
void *arg;
|
||||
gfp_t flags;
|
||||
};
|
||||
|
||||
struct pn533_frame {
|
||||
|
@ -487,7 +501,7 @@ static bool pn533_rx_frame_is_cmd_response(struct pn533_frame *frame, u8 cmd)
|
|||
|
||||
static void pn533_wq_cmd_complete(struct work_struct *work)
|
||||
{
|
||||
struct pn533 *dev = container_of(work, struct pn533, cmd_work);
|
||||
struct pn533 *dev = container_of(work, struct pn533, cmd_complete_work);
|
||||
struct pn533_frame *in_frame;
|
||||
int rc;
|
||||
|
||||
|
@ -502,7 +516,7 @@ static void pn533_wq_cmd_complete(struct work_struct *work)
|
|||
PN533_FRAME_CMD_PARAMS_LEN(in_frame));
|
||||
|
||||
if (rc != -EINPROGRESS)
|
||||
mutex_unlock(&dev->cmd_lock);
|
||||
queue_work(dev->wq, &dev->cmd_work);
|
||||
}
|
||||
|
||||
static void pn533_recv_response(struct urb *urb)
|
||||
|
@ -550,7 +564,7 @@ static void pn533_recv_response(struct urb *urb)
|
|||
dev->wq_in_frame = in_frame;
|
||||
|
||||
sched_wq:
|
||||
queue_work(dev->wq, &dev->cmd_work);
|
||||
queue_work(dev->wq, &dev->cmd_complete_work);
|
||||
}
|
||||
|
||||
static int pn533_submit_urb_for_response(struct pn533 *dev, gfp_t flags)
|
||||
|
@ -606,7 +620,7 @@ static void pn533_recv_ack(struct urb *urb)
|
|||
|
||||
sched_wq:
|
||||
dev->wq_in_frame = NULL;
|
||||
queue_work(dev->wq, &dev->cmd_work);
|
||||
queue_work(dev->wq, &dev->cmd_complete_work);
|
||||
}
|
||||
|
||||
static int pn533_submit_urb_for_ack(struct pn533 *dev, gfp_t flags)
|
||||
|
@ -669,6 +683,31 @@ error:
|
|||
return rc;
|
||||
}
|
||||
|
||||
static void pn533_wq_cmd(struct work_struct *work)
|
||||
{
|
||||
struct pn533 *dev = container_of(work, struct pn533, cmd_work);
|
||||
struct pn533_cmd *cmd;
|
||||
|
||||
mutex_lock(&dev->cmd_lock);
|
||||
|
||||
if (list_empty(&dev->cmd_queue)) {
|
||||
dev->cmd_pending = 0;
|
||||
mutex_unlock(&dev->cmd_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
cmd = list_first_entry(&dev->cmd_queue, struct pn533_cmd, queue);
|
||||
|
||||
mutex_unlock(&dev->cmd_lock);
|
||||
|
||||
__pn533_send_cmd_frame_async(dev, cmd->out_frame, cmd->in_frame,
|
||||
cmd->in_frame_len, cmd->cmd_complete,
|
||||
cmd->arg, cmd->flags);
|
||||
|
||||
list_del(&cmd->queue);
|
||||
kfree(cmd);
|
||||
}
|
||||
|
||||
static int pn533_send_cmd_frame_async(struct pn533 *dev,
|
||||
struct pn533_frame *out_frame,
|
||||
struct pn533_frame *in_frame,
|
||||
|
@ -676,22 +715,44 @@ static int pn533_send_cmd_frame_async(struct pn533 *dev,
|
|||
pn533_cmd_complete_t cmd_complete,
|
||||
void *arg, gfp_t flags)
|
||||
{
|
||||
struct pn533_cmd *cmd;
|
||||
int rc;
|
||||
|
||||
nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
|
||||
|
||||
if (!mutex_trylock(&dev->cmd_lock))
|
||||
return -EBUSY;
|
||||
mutex_lock(&dev->cmd_lock);
|
||||
|
||||
rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame,
|
||||
in_frame_len, cmd_complete, arg, flags);
|
||||
if (rc)
|
||||
goto error;
|
||||
if (!dev->cmd_pending) {
|
||||
rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame,
|
||||
in_frame_len, cmd_complete,
|
||||
arg, flags);
|
||||
if (!rc)
|
||||
dev->cmd_pending = 1;
|
||||
|
||||
mutex_unlock(&dev->cmd_lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
nfc_dev_dbg(&dev->interface->dev, "%s Queueing command", __func__);
|
||||
|
||||
cmd = kzalloc(sizeof(struct pn533_cmd), flags);
|
||||
if (!cmd)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&cmd->queue);
|
||||
cmd->out_frame = out_frame;
|
||||
cmd->in_frame = in_frame;
|
||||
cmd->in_frame_len = in_frame_len;
|
||||
cmd->cmd_complete = cmd_complete;
|
||||
cmd->arg = arg;
|
||||
cmd->flags = flags;
|
||||
|
||||
list_add_tail(&cmd->queue, &dev->cmd_queue);
|
||||
|
||||
mutex_unlock(&dev->cmd_lock);
|
||||
|
||||
return 0;
|
||||
error:
|
||||
mutex_unlock(&dev->cmd_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct pn533_sync_cmd_response {
|
||||
|
@ -1305,8 +1366,6 @@ static void pn533_listen_mode_timer(unsigned long data)
|
|||
|
||||
dev->cancel_listen = 1;
|
||||
|
||||
mutex_unlock(&dev->cmd_lock);
|
||||
|
||||
pn533_poll_next_mod(dev);
|
||||
|
||||
queue_work(dev->wq, &dev->poll_work);
|
||||
|
@ -2131,7 +2190,7 @@ error_cmd:
|
|||
|
||||
kfree(arg);
|
||||
|
||||
mutex_unlock(&dev->cmd_lock);
|
||||
queue_work(dev->wq, &dev->cmd_work);
|
||||
}
|
||||
|
||||
static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata,
|
||||
|
@ -2330,13 +2389,12 @@ static int pn533_probe(struct usb_interface *interface,
|
|||
NULL, 0,
|
||||
pn533_send_complete, dev);
|
||||
|
||||
INIT_WORK(&dev->cmd_work, pn533_wq_cmd_complete);
|
||||
INIT_WORK(&dev->cmd_work, pn533_wq_cmd);
|
||||
INIT_WORK(&dev->cmd_complete_work, pn533_wq_cmd_complete);
|
||||
INIT_WORK(&dev->mi_work, pn533_wq_mi_recv);
|
||||
INIT_WORK(&dev->tg_work, pn533_wq_tg_get_data);
|
||||
INIT_WORK(&dev->poll_work, pn533_wq_poll);
|
||||
dev->wq = alloc_workqueue("pn533",
|
||||
WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM,
|
||||
1);
|
||||
dev->wq = alloc_ordered_workqueue("pn533", 0);
|
||||
if (dev->wq == NULL)
|
||||
goto error;
|
||||
|
||||
|
@ -2346,6 +2404,8 @@ static int pn533_probe(struct usb_interface *interface,
|
|||
|
||||
skb_queue_head_init(&dev->resp_q);
|
||||
|
||||
INIT_LIST_HEAD(&dev->cmd_queue);
|
||||
|
||||
usb_set_intfdata(interface, dev);
|
||||
|
||||
pn533_tx_frame_init(dev->out_frame, PN533_CMD_GET_FIRMWARE_VERSION);
|
||||
|
@ -2417,6 +2477,7 @@ error:
|
|||
static void pn533_disconnect(struct usb_interface *interface)
|
||||
{
|
||||
struct pn533 *dev;
|
||||
struct pn533_cmd *cmd, *n;
|
||||
|
||||
dev = usb_get_intfdata(interface);
|
||||
usb_set_intfdata(interface, NULL);
|
||||
|
@ -2433,6 +2494,11 @@ static void pn533_disconnect(struct usb_interface *interface)
|
|||
|
||||
del_timer(&dev->listen_timer);
|
||||
|
||||
list_for_each_entry_safe(cmd, n, &dev->cmd_queue, queue) {
|
||||
list_del(&cmd->queue);
|
||||
kfree(cmd);
|
||||
}
|
||||
|
||||
kfree(dev->in_frame);
|
||||
usb_free_urb(dev->in_urb);
|
||||
kfree(dev->out_frame);
|
||||
|
|
|
@ -1,893 +0,0 @@
|
|||
/*
|
||||
* Driver for the PN544 NFC chip.
|
||||
*
|
||||
* Copyright (C) Nokia Corporation
|
||||
*
|
||||
* Author: Jari Vanhala <ext-jari.vanhala@nokia.com>
|
||||
* Contact: Matti Aaltonen <matti.j.aaltonen@nokia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms 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/completion.h>
|
||||
#include <linux/crc-ccitt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/nfc/pn544.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/serial_core.h> /* for TCGETS */
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define DRIVER_CARD "PN544 NFC"
|
||||
#define DRIVER_DESC "NFC driver for PN544"
|
||||
|
||||
static struct i2c_device_id pn544_id_table[] = {
|
||||
{ PN544_DRIVER_NAME, 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pn544_id_table);
|
||||
|
||||
#define HCI_MODE 0
|
||||
#define FW_MODE 1
|
||||
|
||||
enum pn544_state {
|
||||
PN544_ST_COLD,
|
||||
PN544_ST_FW_READY,
|
||||
PN544_ST_READY,
|
||||
};
|
||||
|
||||
enum pn544_irq {
|
||||
PN544_NONE,
|
||||
PN544_INT,
|
||||
};
|
||||
|
||||
struct pn544_info {
|
||||
struct miscdevice miscdev;
|
||||
struct i2c_client *i2c_dev;
|
||||
struct regulator_bulk_data regs[3];
|
||||
|
||||
enum pn544_state state;
|
||||
wait_queue_head_t read_wait;
|
||||
loff_t read_offset;
|
||||
enum pn544_irq read_irq;
|
||||
struct mutex read_mutex; /* Serialize read_irq access */
|
||||
struct mutex mutex; /* Serialize info struct access */
|
||||
u8 *buf;
|
||||
size_t buflen;
|
||||
};
|
||||
|
||||
static const char reg_vdd_io[] = "Vdd_IO";
|
||||
static const char reg_vbat[] = "VBat";
|
||||
static const char reg_vsim[] = "VSim";
|
||||
|
||||
/* sysfs interface */
|
||||
static ssize_t pn544_test(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct pn544_info *info = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = info->i2c_dev;
|
||||
struct pn544_nfc_platform_data *pdata = client->dev.platform_data;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", pdata->test());
|
||||
}
|
||||
|
||||
static int pn544_enable(struct pn544_info *info, int mode)
|
||||
{
|
||||
struct pn544_nfc_platform_data *pdata;
|
||||
struct i2c_client *client = info->i2c_dev;
|
||||
|
||||
int r;
|
||||
|
||||
r = regulator_bulk_enable(ARRAY_SIZE(info->regs), info->regs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
pdata = client->dev.platform_data;
|
||||
info->read_irq = PN544_NONE;
|
||||
if (pdata->enable)
|
||||
pdata->enable(mode);
|
||||
|
||||
if (mode) {
|
||||
info->state = PN544_ST_FW_READY;
|
||||
dev_dbg(&client->dev, "now in FW-mode\n");
|
||||
} else {
|
||||
info->state = PN544_ST_READY;
|
||||
dev_dbg(&client->dev, "now in HCI-mode\n");
|
||||
}
|
||||
|
||||
usleep_range(10000, 15000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pn544_disable(struct pn544_info *info)
|
||||
{
|
||||
struct pn544_nfc_platform_data *pdata;
|
||||
struct i2c_client *client = info->i2c_dev;
|
||||
|
||||
pdata = client->dev.platform_data;
|
||||
if (pdata->disable)
|
||||
pdata->disable();
|
||||
|
||||
info->state = PN544_ST_COLD;
|
||||
|
||||
dev_dbg(&client->dev, "Now in OFF-mode\n");
|
||||
|
||||
msleep(PN544_RESETVEN_TIME);
|
||||
|
||||
info->read_irq = PN544_NONE;
|
||||
regulator_bulk_disable(ARRAY_SIZE(info->regs), info->regs);
|
||||
}
|
||||
|
||||
static int check_crc(u8 *buf, int buflen)
|
||||
{
|
||||
u8 len;
|
||||
u16 crc;
|
||||
|
||||
len = buf[0] + 1;
|
||||
if (len < 4 || len != buflen || len > PN544_MSG_MAX_SIZE) {
|
||||
pr_err(PN544_DRIVER_NAME
|
||||
": CRC; corrupt packet len %u (%d)\n", len, buflen);
|
||||
print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE,
|
||||
16, 2, buf, buflen, false);
|
||||
return -EPERM;
|
||||
}
|
||||
crc = crc_ccitt(0xffff, buf, len - 2);
|
||||
crc = ~crc;
|
||||
|
||||
if (buf[len-2] != (crc & 0xff) || buf[len-1] != (crc >> 8)) {
|
||||
pr_err(PN544_DRIVER_NAME ": CRC error 0x%x != 0x%x 0x%x\n",
|
||||
crc, buf[len-1], buf[len-2]);
|
||||
|
||||
print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE,
|
||||
16, 2, buf, buflen, false);
|
||||
return -EPERM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pn544_i2c_write(struct i2c_client *client, u8 *buf, int len)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (len < 4 || len != (buf[0] + 1)) {
|
||||
dev_err(&client->dev, "%s: Illegal message length: %d\n",
|
||||
__func__, len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (check_crc(buf, len))
|
||||
return -EINVAL;
|
||||
|
||||
usleep_range(3000, 6000);
|
||||
|
||||
r = i2c_master_send(client, buf, len);
|
||||
dev_dbg(&client->dev, "send: %d\n", r);
|
||||
|
||||
if (r == -EREMOTEIO) { /* Retry, chip was in standby */
|
||||
usleep_range(6000, 10000);
|
||||
r = i2c_master_send(client, buf, len);
|
||||
dev_dbg(&client->dev, "send2: %d\n", r);
|
||||
}
|
||||
|
||||
if (r != len)
|
||||
return -EREMOTEIO;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int pn544_i2c_read(struct i2c_client *client, u8 *buf, int buflen)
|
||||
{
|
||||
int r;
|
||||
u8 len;
|
||||
|
||||
/*
|
||||
* You could read a packet in one go, but then you'd need to read
|
||||
* max size and rest would be 0xff fill, so we do split reads.
|
||||
*/
|
||||
r = i2c_master_recv(client, &len, 1);
|
||||
dev_dbg(&client->dev, "recv1: %d\n", r);
|
||||
|
||||
if (r != 1)
|
||||
return -EREMOTEIO;
|
||||
|
||||
if (len < PN544_LLC_HCI_OVERHEAD)
|
||||
len = PN544_LLC_HCI_OVERHEAD;
|
||||
else if (len > (PN544_MSG_MAX_SIZE - 1))
|
||||
len = PN544_MSG_MAX_SIZE - 1;
|
||||
|
||||
if (1 + len > buflen) /* len+(data+crc16) */
|
||||
return -EMSGSIZE;
|
||||
|
||||
buf[0] = len;
|
||||
|
||||
r = i2c_master_recv(client, buf + 1, len);
|
||||
dev_dbg(&client->dev, "recv2: %d\n", r);
|
||||
|
||||
if (r != len)
|
||||
return -EREMOTEIO;
|
||||
|
||||
usleep_range(3000, 6000);
|
||||
|
||||
return r + 1;
|
||||
}
|
||||
|
||||
static int pn544_fw_write(struct i2c_client *client, u8 *buf, int len)
|
||||
{
|
||||
int r;
|
||||
|
||||
dev_dbg(&client->dev, "%s\n", __func__);
|
||||
|
||||
if (len < PN544_FW_HEADER_SIZE ||
|
||||
(PN544_FW_HEADER_SIZE + (buf[1] << 8) + buf[2]) != len)
|
||||
return -EINVAL;
|
||||
|
||||
r = i2c_master_send(client, buf, len);
|
||||
dev_dbg(&client->dev, "fw send: %d\n", r);
|
||||
|
||||
if (r == -EREMOTEIO) { /* Retry, chip was in standby */
|
||||
usleep_range(6000, 10000);
|
||||
r = i2c_master_send(client, buf, len);
|
||||
dev_dbg(&client->dev, "fw send2: %d\n", r);
|
||||
}
|
||||
|
||||
if (r != len)
|
||||
return -EREMOTEIO;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int pn544_fw_read(struct i2c_client *client, u8 *buf, int buflen)
|
||||
{
|
||||
int r, len;
|
||||
|
||||
if (buflen < PN544_FW_HEADER_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
r = i2c_master_recv(client, buf, PN544_FW_HEADER_SIZE);
|
||||
dev_dbg(&client->dev, "FW recv1: %d\n", r);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (r < PN544_FW_HEADER_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
len = (buf[1] << 8) + buf[2];
|
||||
if (len == 0) /* just header, no additional data */
|
||||
return r;
|
||||
|
||||
if (len > buflen - PN544_FW_HEADER_SIZE)
|
||||
return -EMSGSIZE;
|
||||
|
||||
r = i2c_master_recv(client, buf + PN544_FW_HEADER_SIZE, len);
|
||||
dev_dbg(&client->dev, "fw recv2: %d\n", r);
|
||||
|
||||
if (r != len)
|
||||
return -EINVAL;
|
||||
|
||||
return r + PN544_FW_HEADER_SIZE;
|
||||
}
|
||||
|
||||
static irqreturn_t pn544_irq_thread_fn(int irq, void *dev_id)
|
||||
{
|
||||
struct pn544_info *info = dev_id;
|
||||
struct i2c_client *client = info->i2c_dev;
|
||||
|
||||
BUG_ON(!info);
|
||||
BUG_ON(irq != info->i2c_dev->irq);
|
||||
|
||||
dev_dbg(&client->dev, "IRQ\n");
|
||||
|
||||
mutex_lock(&info->read_mutex);
|
||||
info->read_irq = PN544_INT;
|
||||
mutex_unlock(&info->read_mutex);
|
||||
|
||||
wake_up_interruptible(&info->read_wait);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static enum pn544_irq pn544_irq_state(struct pn544_info *info)
|
||||
{
|
||||
enum pn544_irq irq;
|
||||
|
||||
mutex_lock(&info->read_mutex);
|
||||
irq = info->read_irq;
|
||||
mutex_unlock(&info->read_mutex);
|
||||
/*
|
||||
* XXX: should we check GPIO-line status directly?
|
||||
* return pdata->irq_status() ? PN544_INT : PN544_NONE;
|
||||
*/
|
||||
|
||||
return irq;
|
||||
}
|
||||
|
||||
static ssize_t pn544_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *offset)
|
||||
{
|
||||
struct pn544_info *info = container_of(file->private_data,
|
||||
struct pn544_info, miscdev);
|
||||
struct i2c_client *client = info->i2c_dev;
|
||||
enum pn544_irq irq;
|
||||
size_t len;
|
||||
int r = 0;
|
||||
|
||||
dev_dbg(&client->dev, "%s: info: %p, count: %zu\n", __func__,
|
||||
info, count);
|
||||
|
||||
mutex_lock(&info->mutex);
|
||||
|
||||
if (info->state == PN544_ST_COLD) {
|
||||
r = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
irq = pn544_irq_state(info);
|
||||
if (irq == PN544_NONE) {
|
||||
if (file->f_flags & O_NONBLOCK) {
|
||||
r = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (wait_event_interruptible(info->read_wait,
|
||||
(info->read_irq == PN544_INT))) {
|
||||
r = -ERESTARTSYS;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (info->state == PN544_ST_FW_READY) {
|
||||
len = min(count, info->buflen);
|
||||
|
||||
mutex_lock(&info->read_mutex);
|
||||
r = pn544_fw_read(info->i2c_dev, info->buf, len);
|
||||
info->read_irq = PN544_NONE;
|
||||
mutex_unlock(&info->read_mutex);
|
||||
|
||||
if (r < 0) {
|
||||
dev_err(&info->i2c_dev->dev, "FW read failed: %d\n", r);
|
||||
goto out;
|
||||
}
|
||||
|
||||
print_hex_dump(KERN_DEBUG, "FW read: ", DUMP_PREFIX_NONE,
|
||||
16, 2, info->buf, r, false);
|
||||
|
||||
*offset += r;
|
||||
if (copy_to_user(buf, info->buf, r)) {
|
||||
r = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
len = min(count, info->buflen);
|
||||
|
||||
mutex_lock(&info->read_mutex);
|
||||
r = pn544_i2c_read(info->i2c_dev, info->buf, len);
|
||||
info->read_irq = PN544_NONE;
|
||||
mutex_unlock(&info->read_mutex);
|
||||
|
||||
if (r < 0) {
|
||||
dev_err(&info->i2c_dev->dev, "read failed (%d)\n", r);
|
||||
goto out;
|
||||
}
|
||||
print_hex_dump(KERN_DEBUG, "read: ", DUMP_PREFIX_NONE,
|
||||
16, 2, info->buf, r, false);
|
||||
|
||||
*offset += r;
|
||||
if (copy_to_user(buf, info->buf, r)) {
|
||||
r = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&info->mutex);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static unsigned int pn544_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct pn544_info *info = container_of(file->private_data,
|
||||
struct pn544_info, miscdev);
|
||||
struct i2c_client *client = info->i2c_dev;
|
||||
int r = 0;
|
||||
|
||||
dev_dbg(&client->dev, "%s: info: %p\n", __func__, info);
|
||||
|
||||
mutex_lock(&info->mutex);
|
||||
|
||||
if (info->state == PN544_ST_COLD) {
|
||||
r = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
poll_wait(file, &info->read_wait, wait);
|
||||
|
||||
if (pn544_irq_state(info) == PN544_INT) {
|
||||
r = POLLIN | POLLRDNORM;
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&info->mutex);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static ssize_t pn544_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct pn544_info *info = container_of(file->private_data,
|
||||
struct pn544_info, miscdev);
|
||||
struct i2c_client *client = info->i2c_dev;
|
||||
ssize_t len;
|
||||
int r;
|
||||
|
||||
dev_dbg(&client->dev, "%s: info: %p, count %zu\n", __func__,
|
||||
info, count);
|
||||
|
||||
mutex_lock(&info->mutex);
|
||||
|
||||
if (info->state == PN544_ST_COLD) {
|
||||
r = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX: should we detect rset-writes and clean possible
|
||||
* read_irq state
|
||||
*/
|
||||
if (info->state == PN544_ST_FW_READY) {
|
||||
size_t fw_len;
|
||||
|
||||
if (count < PN544_FW_HEADER_SIZE) {
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
len = min(count, info->buflen);
|
||||
if (copy_from_user(info->buf, buf, len)) {
|
||||
r = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
print_hex_dump(KERN_DEBUG, "FW write: ", DUMP_PREFIX_NONE,
|
||||
16, 2, info->buf, len, false);
|
||||
|
||||
fw_len = PN544_FW_HEADER_SIZE + (info->buf[1] << 8) +
|
||||
info->buf[2];
|
||||
|
||||
if (len > fw_len) /* 1 msg at a time */
|
||||
len = fw_len;
|
||||
|
||||
r = pn544_fw_write(info->i2c_dev, info->buf, len);
|
||||
} else {
|
||||
if (count < PN544_LLC_MIN_SIZE) {
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
len = min(count, info->buflen);
|
||||
if (copy_from_user(info->buf, buf, len)) {
|
||||
r = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
print_hex_dump(KERN_DEBUG, "write: ", DUMP_PREFIX_NONE,
|
||||
16, 2, info->buf, len, false);
|
||||
|
||||
if (len > (info->buf[0] + 1)) /* 1 msg at a time */
|
||||
len = info->buf[0] + 1;
|
||||
|
||||
r = pn544_i2c_write(info->i2c_dev, info->buf, len);
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&info->mutex);
|
||||
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
static long pn544_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct pn544_info *info = container_of(file->private_data,
|
||||
struct pn544_info, miscdev);
|
||||
struct i2c_client *client = info->i2c_dev;
|
||||
struct pn544_nfc_platform_data *pdata;
|
||||
unsigned int val;
|
||||
int r = 0;
|
||||
|
||||
dev_dbg(&client->dev, "%s: info: %p, cmd: 0x%x\n", __func__, info, cmd);
|
||||
|
||||
mutex_lock(&info->mutex);
|
||||
|
||||
if (info->state == PN544_ST_COLD) {
|
||||
r = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pdata = info->i2c_dev->dev.platform_data;
|
||||
switch (cmd) {
|
||||
case PN544_GET_FW_MODE:
|
||||
dev_dbg(&client->dev, "%s: PN544_GET_FW_MODE\n", __func__);
|
||||
|
||||
val = (info->state == PN544_ST_FW_READY);
|
||||
if (copy_to_user((void __user *)arg, &val, sizeof(val))) {
|
||||
r = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PN544_SET_FW_MODE:
|
||||
dev_dbg(&client->dev, "%s: PN544_SET_FW_MODE\n", __func__);
|
||||
|
||||
if (copy_from_user(&val, (void __user *)arg, sizeof(val))) {
|
||||
r = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (val) {
|
||||
if (info->state == PN544_ST_FW_READY)
|
||||
break;
|
||||
|
||||
pn544_disable(info);
|
||||
r = pn544_enable(info, FW_MODE);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
} else {
|
||||
if (info->state == PN544_ST_READY)
|
||||
break;
|
||||
pn544_disable(info);
|
||||
r = pn544_enable(info, HCI_MODE);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
}
|
||||
file->f_pos = info->read_offset;
|
||||
break;
|
||||
|
||||
case TCGETS:
|
||||
dev_dbg(&client->dev, "%s: TCGETS\n", __func__);
|
||||
|
||||
r = -ENOIOCTLCMD;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(&client->dev, "Unknown ioctl 0x%x\n", cmd);
|
||||
r = -ENOIOCTLCMD;
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&info->mutex);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int pn544_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct pn544_info *info = container_of(file->private_data,
|
||||
struct pn544_info, miscdev);
|
||||
struct i2c_client *client = info->i2c_dev;
|
||||
int r = 0;
|
||||
|
||||
dev_dbg(&client->dev, "%s: info: %p, client %p\n", __func__,
|
||||
info, info->i2c_dev);
|
||||
|
||||
mutex_lock(&info->mutex);
|
||||
|
||||
/*
|
||||
* Only 1 at a time.
|
||||
* XXX: maybe user (counter) would work better
|
||||
*/
|
||||
if (info->state != PN544_ST_COLD) {
|
||||
r = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
file->f_pos = info->read_offset;
|
||||
r = pn544_enable(info, HCI_MODE);
|
||||
|
||||
out:
|
||||
mutex_unlock(&info->mutex);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int pn544_close(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct pn544_info *info = container_of(file->private_data,
|
||||
struct pn544_info, miscdev);
|
||||
struct i2c_client *client = info->i2c_dev;
|
||||
|
||||
dev_dbg(&client->dev, "%s: info: %p, client %p\n",
|
||||
__func__, info, info->i2c_dev);
|
||||
|
||||
mutex_lock(&info->mutex);
|
||||
pn544_disable(info);
|
||||
mutex_unlock(&info->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations pn544_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.read = pn544_read,
|
||||
.write = pn544_write,
|
||||
.poll = pn544_poll,
|
||||
.open = pn544_open,
|
||||
.release = pn544_close,
|
||||
.unlocked_ioctl = pn544_ioctl,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int pn544_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct pn544_info *info;
|
||||
int r = 0;
|
||||
|
||||
dev_info(&client->dev, "***\n%s: client %p\n***\n", __func__, client);
|
||||
|
||||
info = i2c_get_clientdata(client);
|
||||
dev_info(&client->dev, "%s: info: %p, client %p\n", __func__,
|
||||
info, client);
|
||||
|
||||
mutex_lock(&info->mutex);
|
||||
|
||||
switch (info->state) {
|
||||
case PN544_ST_FW_READY:
|
||||
/* Do not suspend while upgrading FW, please! */
|
||||
r = -EPERM;
|
||||
break;
|
||||
|
||||
case PN544_ST_READY:
|
||||
/*
|
||||
* CHECK: Device should be in standby-mode. No way to check?
|
||||
* Allowing low power mode for the regulator is potentially
|
||||
* dangerous if pn544 does not go to suspension.
|
||||
*/
|
||||
break;
|
||||
|
||||
case PN544_ST_COLD:
|
||||
break;
|
||||
};
|
||||
|
||||
mutex_unlock(&info->mutex);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int pn544_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct pn544_info *info = i2c_get_clientdata(client);
|
||||
int r = 0;
|
||||
|
||||
dev_dbg(&client->dev, "%s: info: %p, client %p\n", __func__,
|
||||
info, client);
|
||||
|
||||
mutex_lock(&info->mutex);
|
||||
|
||||
switch (info->state) {
|
||||
case PN544_ST_READY:
|
||||
/*
|
||||
* CHECK: If regulator low power mode is allowed in
|
||||
* pn544_suspend, we should go back to normal mode
|
||||
* here.
|
||||
*/
|
||||
break;
|
||||
|
||||
case PN544_ST_COLD:
|
||||
break;
|
||||
|
||||
case PN544_ST_FW_READY:
|
||||
break;
|
||||
};
|
||||
|
||||
mutex_unlock(&info->mutex);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(pn544_pm_ops, pn544_suspend, pn544_resume);
|
||||
#endif
|
||||
|
||||
static struct device_attribute pn544_attr =
|
||||
__ATTR(nfc_test, S_IRUGO, pn544_test, NULL);
|
||||
|
||||
static int __devinit pn544_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct pn544_info *info;
|
||||
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);
|
||||
|
||||
/* private data allocation */
|
||||
info = kzalloc(sizeof(struct pn544_info), GFP_KERNEL);
|
||||
if (!info) {
|
||||
dev_err(&client->dev,
|
||||
"Cannot allocate memory for pn544_info.\n");
|
||||
r = -ENOMEM;
|
||||
goto err_info_alloc;
|
||||
}
|
||||
|
||||
info->buflen = max(PN544_MSG_MAX_SIZE, PN544_MAX_I2C_TRANSFER);
|
||||
info->buf = kzalloc(info->buflen, GFP_KERNEL);
|
||||
if (!info->buf) {
|
||||
dev_err(&client->dev,
|
||||
"Cannot allocate memory for pn544_info->buf.\n");
|
||||
r = -ENOMEM;
|
||||
goto err_buf_alloc;
|
||||
}
|
||||
|
||||
info->regs[0].supply = reg_vdd_io;
|
||||
info->regs[1].supply = reg_vbat;
|
||||
info->regs[2].supply = reg_vsim;
|
||||
r = regulator_bulk_get(&client->dev, ARRAY_SIZE(info->regs),
|
||||
info->regs);
|
||||
if (r < 0)
|
||||
goto err_kmalloc;
|
||||
|
||||
info->i2c_dev = client;
|
||||
info->state = PN544_ST_COLD;
|
||||
info->read_irq = PN544_NONE;
|
||||
mutex_init(&info->read_mutex);
|
||||
mutex_init(&info->mutex);
|
||||
init_waitqueue_head(&info->read_wait);
|
||||
i2c_set_clientdata(client, info);
|
||||
pdata = client->dev.platform_data;
|
||||
if (!pdata) {
|
||||
dev_err(&client->dev, "No platform data\n");
|
||||
r = -EINVAL;
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
if (!pdata->request_resources) {
|
||||
dev_err(&client->dev, "request_resources() missing\n");
|
||||
r = -EINVAL;
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
r = pdata->request_resources(client);
|
||||
if (r) {
|
||||
dev_err(&client->dev, "Cannot get platform resources\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
r = request_threaded_irq(client->irq, NULL, pn544_irq_thread_fn,
|
||||
IRQF_TRIGGER_RISING, PN544_DRIVER_NAME,
|
||||
info);
|
||||
if (r < 0) {
|
||||
dev_err(&client->dev, "Unable to register IRQ handler\n");
|
||||
goto err_res;
|
||||
}
|
||||
|
||||
/* If we don't have the test we don't need the sysfs file */
|
||||
if (pdata->test) {
|
||||
r = device_create_file(&client->dev, &pn544_attr);
|
||||
if (r) {
|
||||
dev_err(&client->dev,
|
||||
"sysfs registration failed, error %d\n", r);
|
||||
goto err_irq;
|
||||
}
|
||||
}
|
||||
|
||||
info->miscdev.minor = MISC_DYNAMIC_MINOR;
|
||||
info->miscdev.name = PN544_DRIVER_NAME;
|
||||
info->miscdev.fops = &pn544_fops;
|
||||
info->miscdev.parent = &client->dev;
|
||||
r = misc_register(&info->miscdev);
|
||||
if (r < 0) {
|
||||
dev_err(&client->dev, "Device registration failed\n");
|
||||
goto err_sysfs;
|
||||
}
|
||||
|
||||
dev_dbg(&client->dev, "%s: info: %p, pdata %p, client %p\n",
|
||||
__func__, info, pdata, client);
|
||||
|
||||
return 0;
|
||||
|
||||
err_sysfs:
|
||||
if (pdata->test)
|
||||
device_remove_file(&client->dev, &pn544_attr);
|
||||
err_irq:
|
||||
free_irq(client->irq, info);
|
||||
err_res:
|
||||
if (pdata->free_resources)
|
||||
pdata->free_resources();
|
||||
err_reg:
|
||||
regulator_bulk_free(ARRAY_SIZE(info->regs), info->regs);
|
||||
err_kmalloc:
|
||||
kfree(info->buf);
|
||||
err_buf_alloc:
|
||||
kfree(info);
|
||||
err_info_alloc:
|
||||
return r;
|
||||
}
|
||||
|
||||
static __devexit int pn544_remove(struct i2c_client *client)
|
||||
{
|
||||
struct pn544_info *info = i2c_get_clientdata(client);
|
||||
struct pn544_nfc_platform_data *pdata = client->dev.platform_data;
|
||||
|
||||
dev_dbg(&client->dev, "%s\n", __func__);
|
||||
|
||||
misc_deregister(&info->miscdev);
|
||||
if (pdata->test)
|
||||
device_remove_file(&client->dev, &pn544_attr);
|
||||
|
||||
if (info->state != PN544_ST_COLD) {
|
||||
if (pdata->disable)
|
||||
pdata->disable();
|
||||
|
||||
info->read_irq = PN544_NONE;
|
||||
}
|
||||
|
||||
free_irq(client->irq, info);
|
||||
if (pdata->free_resources)
|
||||
pdata->free_resources();
|
||||
|
||||
regulator_bulk_free(ARRAY_SIZE(info->regs), info->regs);
|
||||
kfree(info->buf);
|
||||
kfree(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_driver pn544_driver = {
|
||||
.driver = {
|
||||
.name = PN544_DRIVER_NAME,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &pn544_pm_ops,
|
||||
#endif
|
||||
},
|
||||
.probe = pn544_probe,
|
||||
.id_table = pn544_id_table,
|
||||
.remove = __devexit_p(pn544_remove),
|
||||
};
|
||||
|
||||
static int __init pn544_init(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
pr_debug(DRIVER_DESC ": %s\n", __func__);
|
||||
|
||||
r = i2c_add_driver(&pn544_driver);
|
||||
if (r) {
|
||||
pr_err(PN544_DRIVER_NAME ": driver registration failed\n");
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit pn544_exit(void)
|
||||
{
|
||||
i2c_del_driver(&pn544_driver);
|
||||
pr_info(DRIVER_DESC ", Exiting.\n");
|
||||
}
|
||||
|
||||
module_init(pn544_init);
|
||||
module_exit(pn544_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
#include <linux/nfc.h>
|
||||
#include <net/nfc/hci.h>
|
||||
#include <net/nfc/shdlc.h>
|
||||
#include <net/nfc/llc.h>
|
||||
|
||||
#include <linux/nfc/pn544.h>
|
||||
|
||||
|
@ -128,10 +128,12 @@ static struct nfc_hci_gate pn544_gates[] = {
|
|||
|
||||
/* Largest headroom needed for outgoing custom commands */
|
||||
#define PN544_CMDS_HEADROOM 2
|
||||
#define PN544_FRAME_HEADROOM 1
|
||||
#define PN544_FRAME_TAILROOM 2
|
||||
|
||||
struct pn544_hci_info {
|
||||
struct i2c_client *i2c_dev;
|
||||
struct nfc_shdlc *shdlc;
|
||||
struct nfc_hci_dev *hdev;
|
||||
|
||||
enum pn544_state state;
|
||||
|
||||
|
@ -146,6 +148,9 @@ struct pn544_hci_info {
|
|||
* < 0 if hardware error occured (e.g. i2c err)
|
||||
* and prevents normal operation.
|
||||
*/
|
||||
int async_cb_type;
|
||||
data_exchange_cb_t async_cb;
|
||||
void *async_cb_context;
|
||||
};
|
||||
|
||||
static void pn544_hci_platform_init(struct pn544_hci_info *info)
|
||||
|
@ -230,8 +235,12 @@ static int pn544_hci_i2c_write(struct i2c_client *client, u8 *buf, int len)
|
|||
r = i2c_master_send(client, buf, len);
|
||||
}
|
||||
|
||||
if (r >= 0 && r != len)
|
||||
r = -EREMOTEIO;
|
||||
if (r >= 0) {
|
||||
if (r != len)
|
||||
return -EREMOTEIO;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
@ -341,13 +350,16 @@ flush:
|
|||
static irqreturn_t pn544_hci_irq_thread_fn(int irq, void *dev_id)
|
||||
{
|
||||
struct pn544_hci_info *info = dev_id;
|
||||
struct i2c_client *client = info->i2c_dev;
|
||||
struct i2c_client *client;
|
||||
struct sk_buff *skb = NULL;
|
||||
int r;
|
||||
|
||||
BUG_ON(!info);
|
||||
BUG_ON(irq != info->i2c_dev->irq);
|
||||
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)
|
||||
|
@ -357,21 +369,21 @@ static irqreturn_t pn544_hci_irq_thread_fn(int irq, void *dev_id)
|
|||
if (r == -EREMOTEIO) {
|
||||
info->hard_fault = r;
|
||||
|
||||
nfc_shdlc_recv_frame(info->shdlc, NULL);
|
||||
nfc_hci_recv_frame(info->hdev, NULL);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
} else if ((r == -ENOMEM) || (r == -EBADMSG)) {
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
nfc_shdlc_recv_frame(info->shdlc, skb);
|
||||
nfc_hci_recv_frame(info->hdev, skb);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int pn544_hci_open(struct nfc_shdlc *shdlc)
|
||||
static int pn544_hci_open(struct nfc_hci_dev *hdev)
|
||||
{
|
||||
struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc);
|
||||
struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
|
||||
int r = 0;
|
||||
|
||||
mutex_lock(&info->info_lock);
|
||||
|
@ -391,9 +403,9 @@ out:
|
|||
return r;
|
||||
}
|
||||
|
||||
static void pn544_hci_close(struct nfc_shdlc *shdlc)
|
||||
static void pn544_hci_close(struct nfc_hci_dev *hdev)
|
||||
{
|
||||
struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc);
|
||||
struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
|
||||
|
||||
mutex_lock(&info->info_lock);
|
||||
|
||||
|
@ -408,9 +420,8 @@ out:
|
|||
mutex_unlock(&info->info_lock);
|
||||
}
|
||||
|
||||
static int pn544_hci_ready(struct nfc_shdlc *shdlc)
|
||||
static int pn544_hci_ready(struct nfc_hci_dev *hdev)
|
||||
{
|
||||
struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);
|
||||
struct sk_buff *skb;
|
||||
static struct hw_config {
|
||||
u8 adr[2];
|
||||
|
@ -576,21 +587,45 @@ static int pn544_hci_ready(struct nfc_shdlc *shdlc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pn544_hci_xmit(struct nfc_shdlc *shdlc, struct sk_buff *skb)
|
||||
static void pn544_hci_add_len_crc(struct sk_buff *skb)
|
||||
{
|
||||
struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc);
|
||||
u16 crc;
|
||||
int len;
|
||||
|
||||
len = skb->len + 2;
|
||||
*skb_push(skb, 1) = len;
|
||||
|
||||
crc = crc_ccitt(0xffff, skb->data, skb->len);
|
||||
crc = ~crc;
|
||||
*skb_put(skb, 1) = crc & 0xff;
|
||||
*skb_put(skb, 1) = crc >> 8;
|
||||
}
|
||||
|
||||
static void pn544_hci_remove_len_crc(struct sk_buff *skb)
|
||||
{
|
||||
skb_pull(skb, PN544_FRAME_HEADROOM);
|
||||
skb_trim(skb, PN544_FRAME_TAILROOM);
|
||||
}
|
||||
|
||||
static int pn544_hci_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
|
||||
struct i2c_client *client = info->i2c_dev;
|
||||
int r;
|
||||
|
||||
if (info->hard_fault != 0)
|
||||
return info->hard_fault;
|
||||
|
||||
return pn544_hci_i2c_write(client, skb->data, skb->len);
|
||||
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_shdlc *shdlc,
|
||||
static int pn544_hci_start_poll(struct nfc_hci_dev *hdev,
|
||||
u32 im_protocols, u32 tm_protocols)
|
||||
{
|
||||
struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);
|
||||
u8 phases = 0;
|
||||
int r;
|
||||
u8 duration[2];
|
||||
|
@ -641,7 +676,7 @@ static int pn544_hci_start_poll(struct nfc_shdlc *shdlc,
|
|||
return r;
|
||||
}
|
||||
|
||||
static int pn544_hci_target_from_gate(struct nfc_shdlc *shdlc, u8 gate,
|
||||
static int pn544_hci_target_from_gate(struct nfc_hci_dev *hdev, u8 gate,
|
||||
struct nfc_target *target)
|
||||
{
|
||||
switch (gate) {
|
||||
|
@ -659,11 +694,10 @@ static int pn544_hci_target_from_gate(struct nfc_shdlc *shdlc, u8 gate,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pn544_hci_complete_target_discovered(struct nfc_shdlc *shdlc,
|
||||
static int pn544_hci_complete_target_discovered(struct nfc_hci_dev *hdev,
|
||||
u8 gate,
|
||||
struct nfc_target *target)
|
||||
{
|
||||
struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);
|
||||
struct sk_buff *uid_skb;
|
||||
int r = 0;
|
||||
|
||||
|
@ -704,6 +738,26 @@ static int pn544_hci_complete_target_discovered(struct nfc_shdlc *shdlc,
|
|||
return r;
|
||||
}
|
||||
|
||||
#define PN544_CB_TYPE_READER_F 1
|
||||
|
||||
static void pn544_hci_data_exchange_cb(void *context, struct sk_buff *skb,
|
||||
int err)
|
||||
{
|
||||
struct pn544_hci_info *info = context;
|
||||
|
||||
switch (info->async_cb_type) {
|
||||
case PN544_CB_TYPE_READER_F:
|
||||
if (err == 0)
|
||||
skb_pull(skb, 1);
|
||||
info->async_cb(info->async_cb_context, skb, err);
|
||||
break;
|
||||
default:
|
||||
if (err == 0)
|
||||
kfree_skb(skb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define MIFARE_CMD_AUTH_KEY_A 0x60
|
||||
#define MIFARE_CMD_AUTH_KEY_B 0x61
|
||||
#define MIFARE_CMD_HEADER 2
|
||||
|
@ -715,13 +769,12 @@ static int pn544_hci_complete_target_discovered(struct nfc_shdlc *shdlc,
|
|||
* <= 0: driver handled the data exchange
|
||||
* 1: driver doesn't especially handle, please do standard processing
|
||||
*/
|
||||
static int pn544_hci_data_exchange(struct nfc_shdlc *shdlc,
|
||||
static int pn544_hci_data_exchange(struct nfc_hci_dev *hdev,
|
||||
struct nfc_target *target,
|
||||
struct sk_buff *skb,
|
||||
struct sk_buff **res_skb)
|
||||
struct sk_buff *skb, data_exchange_cb_t cb,
|
||||
void *cb_context)
|
||||
{
|
||||
struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);
|
||||
int r;
|
||||
struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
|
||||
|
||||
pr_info(DRIVER_DESC ": %s for gate=%d\n", __func__,
|
||||
target->hci_reader_gate);
|
||||
|
@ -746,41 +799,43 @@ static int pn544_hci_data_exchange(struct nfc_shdlc *shdlc,
|
|||
memcpy(data, uid, MIFARE_UID_LEN);
|
||||
}
|
||||
|
||||
return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
|
||||
PN544_MIFARE_CMD,
|
||||
skb->data, skb->len, res_skb);
|
||||
return nfc_hci_send_cmd_async(hdev,
|
||||
target->hci_reader_gate,
|
||||
PN544_MIFARE_CMD,
|
||||
skb->data, skb->len,
|
||||
cb, cb_context);
|
||||
} else
|
||||
return 1;
|
||||
case PN544_RF_READER_F_GATE:
|
||||
*skb_push(skb, 1) = 0;
|
||||
*skb_push(skb, 1) = 0;
|
||||
|
||||
r = nfc_hci_send_cmd(hdev, target->hci_reader_gate,
|
||||
PN544_FELICA_RAW,
|
||||
skb->data, skb->len, res_skb);
|
||||
if (r == 0)
|
||||
skb_pull(*res_skb, 1);
|
||||
return r;
|
||||
info->async_cb_type = PN544_CB_TYPE_READER_F;
|
||||
info->async_cb = cb;
|
||||
info->async_cb_context = cb_context;
|
||||
|
||||
return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
|
||||
PN544_FELICA_RAW, skb->data,
|
||||
skb->len,
|
||||
pn544_hci_data_exchange_cb, info);
|
||||
case PN544_RF_READER_JEWEL_GATE:
|
||||
return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
|
||||
PN544_JEWEL_RAW_CMD,
|
||||
skb->data, skb->len, res_skb);
|
||||
return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
|
||||
PN544_JEWEL_RAW_CMD, skb->data,
|
||||
skb->len, cb, cb_context);
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int pn544_hci_check_presence(struct nfc_shdlc *shdlc,
|
||||
static int pn544_hci_check_presence(struct nfc_hci_dev *hdev,
|
||||
struct nfc_target *target)
|
||||
{
|
||||
struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);
|
||||
|
||||
return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
|
||||
PN544_RF_READER_CMD_PRESENCE_CHECK,
|
||||
NULL, 0, NULL);
|
||||
}
|
||||
|
||||
static struct nfc_shdlc_ops pn544_shdlc_ops = {
|
||||
static struct nfc_hci_ops pn544_hci_ops = {
|
||||
.open = pn544_hci_open,
|
||||
.close = pn544_hci_close,
|
||||
.hci_ready = pn544_hci_ready,
|
||||
|
@ -848,8 +903,8 @@ static int __devinit pn544_hci_probe(struct i2c_client *client,
|
|||
pn544_hci_platform_init(info);
|
||||
|
||||
r = request_threaded_irq(client->irq, NULL, pn544_hci_irq_thread_fn,
|
||||
IRQF_TRIGGER_RISING, PN544_HCI_DRIVER_NAME,
|
||||
info);
|
||||
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;
|
||||
|
@ -872,22 +927,30 @@ static int __devinit pn544_hci_probe(struct i2c_client *client,
|
|||
NFC_PROTO_ISO14443_B_MASK |
|
||||
NFC_PROTO_NFC_DEP_MASK;
|
||||
|
||||
info->shdlc = nfc_shdlc_allocate(&pn544_shdlc_ops,
|
||||
&init_data, protocols,
|
||||
PN544_CMDS_HEADROOM, 0,
|
||||
PN544_HCI_LLC_MAX_PAYLOAD,
|
||||
dev_name(&client->dev));
|
||||
if (!info->shdlc) {
|
||||
dev_err(&client->dev, "Cannot allocate nfc shdlc.\n");
|
||||
info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data,
|
||||
protocols, LLC_SHDLC_NAME,
|
||||
PN544_FRAME_HEADROOM +
|
||||
PN544_CMDS_HEADROOM,
|
||||
PN544_FRAME_TAILROOM,
|
||||
PN544_HCI_LLC_MAX_PAYLOAD);
|
||||
if (!info->hdev) {
|
||||
dev_err(&client->dev, "Cannot allocate nfc hdev.\n");
|
||||
r = -ENOMEM;
|
||||
goto err_allocshdlc;
|
||||
goto err_alloc_hdev;
|
||||
}
|
||||
|
||||
nfc_shdlc_set_clientdata(info->shdlc, info);
|
||||
nfc_hci_set_clientdata(info->hdev, info);
|
||||
|
||||
r = nfc_hci_register_device(info->hdev);
|
||||
if (r)
|
||||
goto err_regdev;
|
||||
|
||||
return 0;
|
||||
|
||||
err_allocshdlc:
|
||||
err_regdev:
|
||||
nfc_hci_free_device(info->hdev);
|
||||
|
||||
err_alloc_hdev:
|
||||
free_irq(client->irq, info);
|
||||
|
||||
err_rti:
|
||||
|
@ -908,7 +971,7 @@ static __devexit int pn544_hci_remove(struct i2c_client *client)
|
|||
|
||||
dev_dbg(&client->dev, "%s\n", __func__);
|
||||
|
||||
nfc_shdlc_free(info->shdlc);
|
||||
nfc_hci_free_device(info->hdev);
|
||||
|
||||
if (info->state != PN544_ST_COLD) {
|
||||
if (pdata->disable)
|
||||
|
|
|
@ -30,6 +30,11 @@ struct nfc_hci_ops {
|
|||
int (*open) (struct nfc_hci_dev *hdev);
|
||||
void (*close) (struct nfc_hci_dev *hdev);
|
||||
int (*hci_ready) (struct nfc_hci_dev *hdev);
|
||||
/*
|
||||
* xmit must always send the complete buffer before
|
||||
* returning. Returned result must be 0 for success
|
||||
* or negative for failure.
|
||||
*/
|
||||
int (*xmit) (struct nfc_hci_dev *hdev, struct sk_buff *skb);
|
||||
int (*start_poll) (struct nfc_hci_dev *hdev,
|
||||
u32 im_protocols, u32 tm_protocols);
|
||||
|
@ -38,8 +43,8 @@ struct nfc_hci_ops {
|
|||
int (*complete_target_discovered) (struct nfc_hci_dev *hdev, u8 gate,
|
||||
struct nfc_target *target);
|
||||
int (*data_exchange) (struct nfc_hci_dev *hdev,
|
||||
struct nfc_target *target,
|
||||
struct sk_buff *skb, struct sk_buff **res_skb);
|
||||
struct nfc_target *target, struct sk_buff *skb,
|
||||
data_exchange_cb_t cb, void *cb_context);
|
||||
int (*check_presence)(struct nfc_hci_dev *hdev,
|
||||
struct nfc_target *target);
|
||||
};
|
||||
|
@ -74,7 +79,6 @@ struct nfc_hci_dev {
|
|||
|
||||
struct list_head msg_tx_queue;
|
||||
|
||||
struct workqueue_struct *msg_tx_wq;
|
||||
struct work_struct msg_tx_work;
|
||||
|
||||
struct timer_list cmd_timer;
|
||||
|
@ -82,13 +86,14 @@ struct nfc_hci_dev {
|
|||
|
||||
struct sk_buff_head rx_hcp_frags;
|
||||
|
||||
struct workqueue_struct *msg_rx_wq;
|
||||
struct work_struct msg_rx_work;
|
||||
|
||||
struct sk_buff_head msg_rx_queue;
|
||||
|
||||
struct nfc_hci_ops *ops;
|
||||
|
||||
struct nfc_llc *llc;
|
||||
|
||||
struct nfc_hci_init_data init_data;
|
||||
|
||||
void *clientdata;
|
||||
|
@ -105,12 +110,17 @@ struct nfc_hci_dev {
|
|||
u8 hw_mpw;
|
||||
u8 hw_software;
|
||||
u8 hw_bsid;
|
||||
|
||||
int async_cb_type;
|
||||
data_exchange_cb_t async_cb;
|
||||
void *async_cb_context;
|
||||
};
|
||||
|
||||
/* hci device allocation */
|
||||
struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
|
||||
struct nfc_hci_init_data *init_data,
|
||||
u32 protocols,
|
||||
const char *llc_name,
|
||||
int tx_headroom,
|
||||
int tx_tailroom,
|
||||
int max_link_payload);
|
||||
|
@ -202,6 +212,9 @@ int nfc_hci_set_param(struct nfc_hci_dev *hdev, u8 gate, u8 idx,
|
|||
const u8 *param, size_t param_len);
|
||||
int nfc_hci_send_cmd(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
|
||||
const u8 *param, size_t param_len, struct sk_buff **skb);
|
||||
int nfc_hci_send_cmd_async(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
|
||||
const u8 *param, size_t param_len,
|
||||
data_exchange_cb_t cb, void *cb_context);
|
||||
int nfc_hci_send_response(struct nfc_hci_dev *hdev, u8 gate, u8 response,
|
||||
const u8 *param, size_t param_len);
|
||||
int nfc_hci_send_event(struct nfc_hci_dev *hdev, u8 gate, u8 event,
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Link Layer Control manager public interface
|
||||
*
|
||||
* Copyright (C) 2012 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __NFC_LLC_H_
|
||||
#define __NFC_LLC_H_
|
||||
|
||||
#include <net/nfc/hci.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#define LLC_NOP_NAME "nop"
|
||||
#define LLC_SHDLC_NAME "shdlc"
|
||||
|
||||
typedef void (*rcv_to_hci_t) (struct nfc_hci_dev *hdev, struct sk_buff *skb);
|
||||
typedef int (*xmit_to_drv_t) (struct nfc_hci_dev *hdev, struct sk_buff *skb);
|
||||
typedef void (*llc_failure_t) (struct nfc_hci_dev *hdev, int err);
|
||||
|
||||
struct nfc_llc;
|
||||
|
||||
struct nfc_llc *nfc_llc_allocate(const char *name, struct nfc_hci_dev *hdev,
|
||||
xmit_to_drv_t xmit_to_drv,
|
||||
rcv_to_hci_t rcv_to_hci, int tx_headroom,
|
||||
int tx_tailroom, llc_failure_t llc_failure);
|
||||
void nfc_llc_free(struct nfc_llc *llc);
|
||||
|
||||
void nfc_llc_get_rx_head_tail_room(struct nfc_llc *llc, int *rx_headroom,
|
||||
int *rx_tailroom);
|
||||
|
||||
|
||||
int nfc_llc_start(struct nfc_llc *llc);
|
||||
int nfc_llc_stop(struct nfc_llc *llc);
|
||||
void nfc_llc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb);
|
||||
int nfc_llc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb);
|
||||
|
||||
int nfc_llc_init(void);
|
||||
void nfc_llc_exit(void);
|
||||
|
||||
#endif /* __NFC_LLC_H_ */
|
|
@ -32,6 +32,7 @@
|
|||
#define NCI_MAX_NUM_MAPPING_CONFIGS 10
|
||||
#define NCI_MAX_NUM_RF_CONFIGS 10
|
||||
#define NCI_MAX_NUM_CONN 10
|
||||
#define NCI_MAX_PARAM_LEN 251
|
||||
|
||||
/* NCI Status Codes */
|
||||
#define NCI_STATUS_OK 0x00
|
||||
|
@ -102,6 +103,9 @@
|
|||
#define NCI_RF_INTERFACE_ISO_DEP 0x02
|
||||
#define NCI_RF_INTERFACE_NFC_DEP 0x03
|
||||
|
||||
/* NCI Configuration Parameter Tags */
|
||||
#define NCI_PN_ATR_REQ_GEN_BYTES 0x29
|
||||
|
||||
/* NCI Reset types */
|
||||
#define NCI_RESET_TYPE_KEEP_CONFIG 0x00
|
||||
#define NCI_RESET_TYPE_RESET_CONFIG 0x01
|
||||
|
@ -188,6 +192,18 @@ struct nci_core_reset_cmd {
|
|||
|
||||
#define NCI_OP_CORE_INIT_CMD nci_opcode_pack(NCI_GID_CORE, 0x01)
|
||||
|
||||
#define NCI_OP_CORE_SET_CONFIG_CMD nci_opcode_pack(NCI_GID_CORE, 0x02)
|
||||
struct set_config_param {
|
||||
__u8 id;
|
||||
__u8 len;
|
||||
__u8 val[NCI_MAX_PARAM_LEN];
|
||||
} __packed;
|
||||
|
||||
struct nci_core_set_config_cmd {
|
||||
__u8 num_params;
|
||||
struct set_config_param param; /* support 1 param per cmd is enough */
|
||||
} __packed;
|
||||
|
||||
#define NCI_OP_RF_DISCOVER_MAP_CMD nci_opcode_pack(NCI_GID_RF_MGMT, 0x00)
|
||||
struct disc_map_config {
|
||||
__u8 rf_protocol;
|
||||
|
@ -252,6 +268,13 @@ struct nci_core_init_rsp_2 {
|
|||
__le32 manufact_specific_info;
|
||||
} __packed;
|
||||
|
||||
#define NCI_OP_CORE_SET_CONFIG_RSP nci_opcode_pack(NCI_GID_CORE, 0x02)
|
||||
struct nci_core_set_config_rsp {
|
||||
__u8 status;
|
||||
__u8 num_params;
|
||||
__u8 params_id[0]; /* variable size array */
|
||||
} __packed;
|
||||
|
||||
#define NCI_OP_RF_DISCOVER_MAP_RSP nci_opcode_pack(NCI_GID_RF_MGMT, 0x00)
|
||||
|
||||
#define NCI_OP_RF_DISCOVER_RSP nci_opcode_pack(NCI_GID_RF_MGMT, 0x03)
|
||||
|
@ -328,6 +351,11 @@ struct activation_params_nfcb_poll_iso_dep {
|
|||
__u8 attrib_res[50];
|
||||
};
|
||||
|
||||
struct activation_params_poll_nfc_dep {
|
||||
__u8 atr_res_len;
|
||||
__u8 atr_res[63];
|
||||
};
|
||||
|
||||
struct nci_rf_intf_activated_ntf {
|
||||
__u8 rf_discovery_id;
|
||||
__u8 rf_interface;
|
||||
|
@ -351,6 +379,7 @@ struct nci_rf_intf_activated_ntf {
|
|||
union {
|
||||
struct activation_params_nfca_poll_iso_dep nfca_poll_iso_dep;
|
||||
struct activation_params_nfcb_poll_iso_dep nfcb_poll_iso_dep;
|
||||
struct activation_params_poll_nfc_dep poll_nfc_dep;
|
||||
} activation_params;
|
||||
|
||||
} __packed;
|
||||
|
|
|
@ -54,6 +54,7 @@ enum nci_state {
|
|||
/* NCI timeouts */
|
||||
#define NCI_RESET_TIMEOUT 5000
|
||||
#define NCI_INIT_TIMEOUT 5000
|
||||
#define NCI_SET_CONFIG_TIMEOUT 5000
|
||||
#define NCI_RF_DISC_TIMEOUT 5000
|
||||
#define NCI_RF_DISC_SELECT_TIMEOUT 5000
|
||||
#define NCI_RF_DEACTIVATE_TIMEOUT 30000
|
||||
|
@ -137,6 +138,10 @@ struct nci_dev {
|
|||
data_exchange_cb_t data_exchange_cb;
|
||||
void *data_exchange_cb_context;
|
||||
struct sk_buff *rx_data_reassembly;
|
||||
|
||||
/* stored during intf_activated_ntf */
|
||||
__u8 remote_gb[NFC_MAX_GT_LEN];
|
||||
__u8 remote_gb_len;
|
||||
};
|
||||
|
||||
/* ----- NCI Devices ----- */
|
||||
|
|
|
@ -72,6 +72,7 @@ struct nfc_ops {
|
|||
|
||||
#define NFC_TARGET_IDX_ANY -1
|
||||
#define NFC_MAX_GT_LEN 48
|
||||
#define NFC_ATR_RES_GT_OFFSET 15
|
||||
|
||||
struct nfc_target {
|
||||
u32 idx;
|
||||
|
@ -112,7 +113,6 @@ struct nfc_dev {
|
|||
int tx_tailroom;
|
||||
|
||||
struct timer_list check_pres_timer;
|
||||
struct workqueue_struct *check_pres_wq;
|
||||
struct work_struct check_pres_work;
|
||||
|
||||
struct nfc_ops *ops;
|
||||
|
|
|
@ -1,107 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 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 __NFC_SHDLC_H
|
||||
#define __NFC_SHDLC_H
|
||||
|
||||
struct nfc_shdlc;
|
||||
|
||||
struct nfc_shdlc_ops {
|
||||
int (*open) (struct nfc_shdlc *shdlc);
|
||||
void (*close) (struct nfc_shdlc *shdlc);
|
||||
int (*hci_ready) (struct nfc_shdlc *shdlc);
|
||||
int (*xmit) (struct nfc_shdlc *shdlc, struct sk_buff *skb);
|
||||
int (*start_poll) (struct nfc_shdlc *shdlc,
|
||||
u32 im_protocols, u32 tm_protocols);
|
||||
int (*target_from_gate) (struct nfc_shdlc *shdlc, u8 gate,
|
||||
struct nfc_target *target);
|
||||
int (*complete_target_discovered) (struct nfc_shdlc *shdlc, u8 gate,
|
||||
struct nfc_target *target);
|
||||
int (*data_exchange) (struct nfc_shdlc *shdlc,
|
||||
struct nfc_target *target,
|
||||
struct sk_buff *skb, struct sk_buff **res_skb);
|
||||
int (*check_presence)(struct nfc_shdlc *shdlc,
|
||||
struct nfc_target *target);
|
||||
};
|
||||
|
||||
enum shdlc_state {
|
||||
SHDLC_DISCONNECTED = 0,
|
||||
SHDLC_CONNECTING = 1,
|
||||
SHDLC_NEGOCIATING = 2,
|
||||
SHDLC_CONNECTED = 3
|
||||
};
|
||||
|
||||
struct nfc_shdlc {
|
||||
struct mutex state_mutex;
|
||||
enum shdlc_state state;
|
||||
int hard_fault;
|
||||
|
||||
struct nfc_hci_dev *hdev;
|
||||
|
||||
wait_queue_head_t *connect_wq;
|
||||
int connect_tries;
|
||||
int connect_result;
|
||||
struct timer_list connect_timer;/* aka T3 in spec 10.6.1 */
|
||||
|
||||
u8 w; /* window size */
|
||||
bool srej_support;
|
||||
|
||||
struct timer_list t1_timer; /* send ack timeout */
|
||||
bool t1_active;
|
||||
|
||||
struct timer_list t2_timer; /* guard/retransmit timeout */
|
||||
bool t2_active;
|
||||
|
||||
int ns; /* next seq num for send */
|
||||
int nr; /* next expected seq num for receive */
|
||||
int dnr; /* oldest sent unacked seq num */
|
||||
|
||||
struct sk_buff_head rcv_q;
|
||||
|
||||
struct sk_buff_head send_q;
|
||||
bool rnr; /* other side is not ready to receive */
|
||||
|
||||
struct sk_buff_head ack_pending_q;
|
||||
|
||||
struct workqueue_struct *sm_wq;
|
||||
struct work_struct sm_work;
|
||||
|
||||
struct nfc_shdlc_ops *ops;
|
||||
|
||||
int client_headroom;
|
||||
int client_tailroom;
|
||||
|
||||
void *clientdata;
|
||||
};
|
||||
|
||||
void nfc_shdlc_recv_frame(struct nfc_shdlc *shdlc, struct sk_buff *skb);
|
||||
|
||||
struct nfc_shdlc *nfc_shdlc_allocate(struct nfc_shdlc_ops *ops,
|
||||
struct nfc_hci_init_data *init_data,
|
||||
u32 protocols,
|
||||
int tx_headroom, int tx_tailroom,
|
||||
int max_link_payload, const char *devname);
|
||||
|
||||
void nfc_shdlc_free(struct nfc_shdlc *shdlc);
|
||||
|
||||
void nfc_shdlc_set_clientdata(struct nfc_shdlc *shdlc, void *clientdata);
|
||||
void *nfc_shdlc_get_clientdata(struct nfc_shdlc *shdlc);
|
||||
struct nfc_hci_dev *nfc_shdlc_get_hci_dev(struct nfc_shdlc *shdlc);
|
||||
|
||||
#endif /* __NFC_SHDLC_H */
|
|
@ -679,7 +679,7 @@ static void nfc_release(struct device *d)
|
|||
|
||||
if (dev->ops->check_presence) {
|
||||
del_timer_sync(&dev->check_pres_timer);
|
||||
destroy_workqueue(dev->check_pres_wq);
|
||||
cancel_work_sync(&dev->check_pres_work);
|
||||
}
|
||||
|
||||
nfc_genl_data_exit(&dev->genl_data);
|
||||
|
@ -715,7 +715,7 @@ static void nfc_check_pres_timeout(unsigned long data)
|
|||
{
|
||||
struct nfc_dev *dev = (struct nfc_dev *)data;
|
||||
|
||||
queue_work(dev->check_pres_wq, &dev->check_pres_work);
|
||||
queue_work(system_nrt_wq, &dev->check_pres_work);
|
||||
}
|
||||
|
||||
struct class nfc_class = {
|
||||
|
@ -784,20 +784,11 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
|
|||
dev->targets_generation = 1;
|
||||
|
||||
if (ops->check_presence) {
|
||||
char name[32];
|
||||
init_timer(&dev->check_pres_timer);
|
||||
dev->check_pres_timer.data = (unsigned long)dev;
|
||||
dev->check_pres_timer.function = nfc_check_pres_timeout;
|
||||
|
||||
INIT_WORK(&dev->check_pres_work, nfc_check_pres_work);
|
||||
snprintf(name, sizeof(name), "nfc%d_check_pres_wq", dev->idx);
|
||||
dev->check_pres_wq = alloc_workqueue(name, WQ_NON_REENTRANT |
|
||||
WQ_UNBOUND |
|
||||
WQ_MEM_RECLAIM, 1);
|
||||
if (dev->check_pres_wq == NULL) {
|
||||
kfree(dev);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return dev;
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
|
||||
obj-$(CONFIG_NFC_HCI) += hci.o
|
||||
|
||||
hci-y := core.o hcp.o command.o
|
||||
hci-$(CONFIG_NFC_SHDLC) += shdlc.o
|
||||
hci-y := core.o hcp.o command.o llc.o llc_nop.o
|
||||
hci-$(CONFIG_NFC_SHDLC) += llc_shdlc.o
|
||||
|
|
|
@ -28,10 +28,29 @@
|
|||
|
||||
#include "hci.h"
|
||||
|
||||
static void nfc_hci_execute_cb(struct nfc_hci_dev *hdev, int err,
|
||||
struct sk_buff *skb, void *cb_data)
|
||||
static int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
|
||||
const u8 *param, size_t param_len,
|
||||
data_exchange_cb_t cb, void *cb_context)
|
||||
{
|
||||
struct hcp_exec_waiter *hcp_ew = (struct hcp_exec_waiter *)cb_data;
|
||||
pr_debug("exec cmd async through pipe=%d, cmd=%d, plen=%zd\n", pipe,
|
||||
cmd, param_len);
|
||||
|
||||
/* TODO: Define hci cmd execution delay. Should it be the same
|
||||
* for all commands?
|
||||
*/
|
||||
return nfc_hci_hcp_message_tx(hdev, pipe, NFC_HCI_HCP_COMMAND, cmd,
|
||||
param, param_len, cb, cb_context, 3000);
|
||||
}
|
||||
|
||||
/*
|
||||
* HCI command execution completion callback.
|
||||
* err will be a standard linux error (may be converted from HCI response)
|
||||
* skb contains the response data and must be disposed, or may be NULL if
|
||||
* an error occured
|
||||
*/
|
||||
static void nfc_hci_execute_cb(void *context, struct sk_buff *skb, int err)
|
||||
{
|
||||
struct hcp_exec_waiter *hcp_ew = (struct hcp_exec_waiter *)context;
|
||||
|
||||
pr_debug("HCI Cmd completed with result=%d\n", err);
|
||||
|
||||
|
@ -55,7 +74,8 @@ static int nfc_hci_execute_cmd(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
|
|||
hcp_ew.exec_complete = false;
|
||||
hcp_ew.result_skb = NULL;
|
||||
|
||||
pr_debug("through pipe=%d, cmd=%d, plen=%zd\n", pipe, cmd, param_len);
|
||||
pr_debug("exec cmd sync through pipe=%d, cmd=%d, plen=%zd\n", pipe,
|
||||
cmd, param_len);
|
||||
|
||||
/* TODO: Define hci cmd execution delay. Should it be the same
|
||||
* for all commands?
|
||||
|
@ -133,6 +153,23 @@ int nfc_hci_send_cmd(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
|
|||
}
|
||||
EXPORT_SYMBOL(nfc_hci_send_cmd);
|
||||
|
||||
int nfc_hci_send_cmd_async(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
|
||||
const u8 *param, size_t param_len,
|
||||
data_exchange_cb_t cb, void *cb_context)
|
||||
{
|
||||
u8 pipe;
|
||||
|
||||
pr_debug("\n");
|
||||
|
||||
pipe = hdev->gate2pipe[gate];
|
||||
if (pipe == NFC_HCI_INVALID_PIPE)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
return nfc_hci_execute_cmd_async(hdev, pipe, cmd, param, param_len,
|
||||
cb, cb_context);
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_hci_send_cmd_async);
|
||||
|
||||
int nfc_hci_set_param(struct nfc_hci_dev *hdev, u8 gate, u8 idx,
|
||||
const u8 *param, size_t param_len)
|
||||
{
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include <net/nfc/nfc.h>
|
||||
#include <net/nfc/hci.h>
|
||||
#include <net/nfc/llc.h>
|
||||
|
||||
#include "hci.h"
|
||||
|
||||
|
@ -57,12 +58,11 @@ static void nfc_hci_msg_tx_work(struct work_struct *work)
|
|||
if (hdev->cmd_pending_msg) {
|
||||
if (timer_pending(&hdev->cmd_timer) == 0) {
|
||||
if (hdev->cmd_pending_msg->cb)
|
||||
hdev->cmd_pending_msg->cb(hdev,
|
||||
-ETIME,
|
||||
NULL,
|
||||
hdev->
|
||||
hdev->cmd_pending_msg->cb(hdev->
|
||||
cmd_pending_msg->
|
||||
cb_context);
|
||||
cb_context,
|
||||
NULL,
|
||||
-ETIME);
|
||||
kfree(hdev->cmd_pending_msg);
|
||||
hdev->cmd_pending_msg = NULL;
|
||||
} else
|
||||
|
@ -78,12 +78,12 @@ next_msg:
|
|||
|
||||
pr_debug("msg_tx_queue has a cmd to send\n");
|
||||
while ((skb = skb_dequeue(&msg->msg_frags)) != NULL) {
|
||||
r = hdev->ops->xmit(hdev, skb);
|
||||
r = nfc_llc_xmit_from_hci(hdev->llc, skb);
|
||||
if (r < 0) {
|
||||
kfree_skb(skb);
|
||||
skb_queue_purge(&msg->msg_frags);
|
||||
if (msg->cb)
|
||||
msg->cb(hdev, r, NULL, msg->cb_context);
|
||||
msg->cb(msg->cb_context, NULL, r);
|
||||
kfree(msg);
|
||||
break;
|
||||
}
|
||||
|
@ -133,15 +133,15 @@ static void __nfc_hci_cmd_completion(struct nfc_hci_dev *hdev, int err,
|
|||
del_timer_sync(&hdev->cmd_timer);
|
||||
|
||||
if (hdev->cmd_pending_msg->cb)
|
||||
hdev->cmd_pending_msg->cb(hdev, err, skb,
|
||||
hdev->cmd_pending_msg->cb_context);
|
||||
hdev->cmd_pending_msg->cb(hdev->cmd_pending_msg->cb_context,
|
||||
skb, err);
|
||||
else
|
||||
kfree_skb(skb);
|
||||
|
||||
kfree(hdev->cmd_pending_msg);
|
||||
hdev->cmd_pending_msg = NULL;
|
||||
|
||||
queue_work(hdev->msg_tx_wq, &hdev->msg_tx_work);
|
||||
queue_work(system_nrt_wq, &hdev->msg_tx_work);
|
||||
}
|
||||
|
||||
void nfc_hci_resp_received(struct nfc_hci_dev *hdev, u8 result,
|
||||
|
@ -326,7 +326,7 @@ static void nfc_hci_cmd_timeout(unsigned long data)
|
|||
{
|
||||
struct nfc_hci_dev *hdev = (struct nfc_hci_dev *)data;
|
||||
|
||||
queue_work(hdev->msg_tx_wq, &hdev->msg_tx_work);
|
||||
queue_work(system_nrt_wq, &hdev->msg_tx_work);
|
||||
}
|
||||
|
||||
static int hci_dev_connect_gates(struct nfc_hci_dev *hdev, u8 gate_count,
|
||||
|
@ -398,8 +398,7 @@ disconnect_all:
|
|||
nfc_hci_disconnect_all_gates(hdev);
|
||||
|
||||
exit:
|
||||
if (skb)
|
||||
kfree_skb(skb);
|
||||
kfree_skb(skb);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
@ -470,29 +469,38 @@ static int hci_dev_up(struct nfc_dev *nfc_dev)
|
|||
return r;
|
||||
}
|
||||
|
||||
r = nfc_llc_start(hdev->llc);
|
||||
if (r < 0)
|
||||
goto exit_close;
|
||||
|
||||
r = hci_dev_session_init(hdev);
|
||||
if (r < 0)
|
||||
goto exit;
|
||||
goto exit_llc;
|
||||
|
||||
r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
|
||||
NFC_HCI_EVT_END_OPERATION, NULL, 0);
|
||||
if (r < 0)
|
||||
goto exit;
|
||||
goto exit_llc;
|
||||
|
||||
if (hdev->ops->hci_ready) {
|
||||
r = hdev->ops->hci_ready(hdev);
|
||||
if (r < 0)
|
||||
goto exit;
|
||||
goto exit_llc;
|
||||
}
|
||||
|
||||
r = hci_dev_version(hdev);
|
||||
if (r < 0)
|
||||
goto exit;
|
||||
goto exit_llc;
|
||||
|
||||
return 0;
|
||||
|
||||
exit_llc:
|
||||
nfc_llc_stop(hdev->llc);
|
||||
|
||||
exit_close:
|
||||
if (hdev->ops->close)
|
||||
hdev->ops->close(hdev);
|
||||
|
||||
exit:
|
||||
if (r < 0)
|
||||
if (hdev->ops->close)
|
||||
hdev->ops->close(hdev);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -500,6 +508,8 @@ static int hci_dev_down(struct nfc_dev *nfc_dev)
|
|||
{
|
||||
struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
|
||||
|
||||
nfc_llc_stop(hdev->llc);
|
||||
|
||||
if (hdev->ops->close)
|
||||
hdev->ops->close(hdev);
|
||||
|
||||
|
@ -539,13 +549,37 @@ static void hci_deactivate_target(struct nfc_dev *nfc_dev,
|
|||
{
|
||||
}
|
||||
|
||||
#define HCI_CB_TYPE_TRANSCEIVE 1
|
||||
|
||||
static void hci_transceive_cb(void *context, struct sk_buff *skb, int err)
|
||||
{
|
||||
struct nfc_hci_dev *hdev = context;
|
||||
|
||||
switch (hdev->async_cb_type) {
|
||||
case HCI_CB_TYPE_TRANSCEIVE:
|
||||
/*
|
||||
* TODO: Check RF Error indicator to make sure data is valid.
|
||||
* It seems that HCI cmd can complete without error, but data
|
||||
* can be invalid if an RF error occured? Ignore for now.
|
||||
*/
|
||||
if (err == 0)
|
||||
skb_trim(skb, skb->len - 1); /* RF Err ind */
|
||||
|
||||
hdev->async_cb(hdev->async_cb_context, skb, err);
|
||||
break;
|
||||
default:
|
||||
if (err == 0)
|
||||
kfree_skb(skb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
|
||||
struct sk_buff *skb, data_exchange_cb_t cb,
|
||||
void *cb_context)
|
||||
{
|
||||
struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
|
||||
int r;
|
||||
struct sk_buff *res_skb = NULL;
|
||||
|
||||
pr_debug("target_idx=%d\n", target->idx);
|
||||
|
||||
|
@ -553,40 +587,37 @@ static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
|
|||
case NFC_HCI_RF_READER_A_GATE:
|
||||
case NFC_HCI_RF_READER_B_GATE:
|
||||
if (hdev->ops->data_exchange) {
|
||||
r = hdev->ops->data_exchange(hdev, target, skb,
|
||||
&res_skb);
|
||||
r = hdev->ops->data_exchange(hdev, target, skb, cb,
|
||||
cb_context);
|
||||
if (r <= 0) /* handled */
|
||||
break;
|
||||
}
|
||||
|
||||
*skb_push(skb, 1) = 0; /* CTR, see spec:10.2.2.1 */
|
||||
r = nfc_hci_send_cmd(hdev, target->hci_reader_gate,
|
||||
NFC_HCI_WR_XCHG_DATA,
|
||||
skb->data, skb->len, &res_skb);
|
||||
/*
|
||||
* TODO: Check RF Error indicator to make sure data is valid.
|
||||
* It seems that HCI cmd can complete without error, but data
|
||||
* can be invalid if an RF error occured? Ignore for now.
|
||||
*/
|
||||
if (r == 0)
|
||||
skb_trim(res_skb, res_skb->len - 1); /* RF Err ind */
|
||||
|
||||
hdev->async_cb_type = HCI_CB_TYPE_TRANSCEIVE;
|
||||
hdev->async_cb = cb;
|
||||
hdev->async_cb_context = cb_context;
|
||||
|
||||
r = nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
|
||||
NFC_HCI_WR_XCHG_DATA, skb->data,
|
||||
skb->len, hci_transceive_cb, hdev);
|
||||
break;
|
||||
default:
|
||||
if (hdev->ops->data_exchange) {
|
||||
r = hdev->ops->data_exchange(hdev, target, skb,
|
||||
&res_skb);
|
||||
r = hdev->ops->data_exchange(hdev, target, skb, cb,
|
||||
cb_context);
|
||||
if (r == 1)
|
||||
r = -ENOTSUPP;
|
||||
}
|
||||
else
|
||||
r = -ENOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
cb(cb_context, res_skb, r);
|
||||
|
||||
return 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
static int hci_check_presence(struct nfc_dev *nfc_dev,
|
||||
|
@ -600,149 +631,6 @@ static int hci_check_presence(struct nfc_dev *nfc_dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct nfc_ops hci_nfc_ops = {
|
||||
.dev_up = hci_dev_up,
|
||||
.dev_down = hci_dev_down,
|
||||
.start_poll = hci_start_poll,
|
||||
.stop_poll = hci_stop_poll,
|
||||
.activate_target = hci_activate_target,
|
||||
.deactivate_target = hci_deactivate_target,
|
||||
.im_transceive = hci_transceive,
|
||||
.check_presence = hci_check_presence,
|
||||
};
|
||||
|
||||
struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
|
||||
struct nfc_hci_init_data *init_data,
|
||||
u32 protocols,
|
||||
int tx_headroom,
|
||||
int tx_tailroom,
|
||||
int max_link_payload)
|
||||
{
|
||||
struct nfc_hci_dev *hdev;
|
||||
|
||||
if (ops->xmit == NULL)
|
||||
return NULL;
|
||||
|
||||
if (protocols == 0)
|
||||
return NULL;
|
||||
|
||||
hdev = kzalloc(sizeof(struct nfc_hci_dev), GFP_KERNEL);
|
||||
if (hdev == NULL)
|
||||
return NULL;
|
||||
|
||||
hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols,
|
||||
tx_headroom + HCI_CMDS_HEADROOM,
|
||||
tx_tailroom);
|
||||
if (!hdev->ndev) {
|
||||
kfree(hdev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hdev->ops = ops;
|
||||
hdev->max_data_link_payload = max_link_payload;
|
||||
hdev->init_data = *init_data;
|
||||
|
||||
nfc_set_drvdata(hdev->ndev, hdev);
|
||||
|
||||
memset(hdev->gate2pipe, NFC_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe));
|
||||
|
||||
return hdev;
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_hci_allocate_device);
|
||||
|
||||
void nfc_hci_free_device(struct nfc_hci_dev *hdev)
|
||||
{
|
||||
nfc_free_device(hdev->ndev);
|
||||
kfree(hdev);
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_hci_free_device);
|
||||
|
||||
int nfc_hci_register_device(struct nfc_hci_dev *hdev)
|
||||
{
|
||||
struct device *dev = &hdev->ndev->dev;
|
||||
const char *devname = dev_name(dev);
|
||||
char name[32];
|
||||
int r = 0;
|
||||
|
||||
mutex_init(&hdev->msg_tx_mutex);
|
||||
|
||||
INIT_LIST_HEAD(&hdev->msg_tx_queue);
|
||||
|
||||
INIT_WORK(&hdev->msg_tx_work, nfc_hci_msg_tx_work);
|
||||
snprintf(name, sizeof(name), "%s_hci_msg_tx_wq", devname);
|
||||
hdev->msg_tx_wq = alloc_workqueue(name, WQ_NON_REENTRANT | WQ_UNBOUND |
|
||||
WQ_MEM_RECLAIM, 1);
|
||||
if (hdev->msg_tx_wq == NULL) {
|
||||
r = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
init_timer(&hdev->cmd_timer);
|
||||
hdev->cmd_timer.data = (unsigned long)hdev;
|
||||
hdev->cmd_timer.function = nfc_hci_cmd_timeout;
|
||||
|
||||
skb_queue_head_init(&hdev->rx_hcp_frags);
|
||||
|
||||
INIT_WORK(&hdev->msg_rx_work, nfc_hci_msg_rx_work);
|
||||
snprintf(name, sizeof(name), "%s_hci_msg_rx_wq", devname);
|
||||
hdev->msg_rx_wq = alloc_workqueue(name, WQ_NON_REENTRANT | WQ_UNBOUND |
|
||||
WQ_MEM_RECLAIM, 1);
|
||||
if (hdev->msg_rx_wq == NULL) {
|
||||
r = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
skb_queue_head_init(&hdev->msg_rx_queue);
|
||||
|
||||
r = nfc_register_device(hdev->ndev);
|
||||
|
||||
exit:
|
||||
if (r < 0) {
|
||||
if (hdev->msg_tx_wq)
|
||||
destroy_workqueue(hdev->msg_tx_wq);
|
||||
if (hdev->msg_rx_wq)
|
||||
destroy_workqueue(hdev->msg_rx_wq);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_hci_register_device);
|
||||
|
||||
void nfc_hci_unregister_device(struct nfc_hci_dev *hdev)
|
||||
{
|
||||
struct hci_msg *msg, *n;
|
||||
|
||||
skb_queue_purge(&hdev->rx_hcp_frags);
|
||||
skb_queue_purge(&hdev->msg_rx_queue);
|
||||
|
||||
list_for_each_entry_safe(msg, n, &hdev->msg_tx_queue, msg_l) {
|
||||
list_del(&msg->msg_l);
|
||||
skb_queue_purge(&msg->msg_frags);
|
||||
kfree(msg);
|
||||
}
|
||||
|
||||
del_timer_sync(&hdev->cmd_timer);
|
||||
|
||||
nfc_unregister_device(hdev->ndev);
|
||||
|
||||
destroy_workqueue(hdev->msg_tx_wq);
|
||||
|
||||
destroy_workqueue(hdev->msg_rx_wq);
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_hci_unregister_device);
|
||||
|
||||
void nfc_hci_set_clientdata(struct nfc_hci_dev *hdev, void *clientdata)
|
||||
{
|
||||
hdev->clientdata = clientdata;
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_hci_set_clientdata);
|
||||
|
||||
void *nfc_hci_get_clientdata(struct nfc_hci_dev *hdev)
|
||||
{
|
||||
return hdev->clientdata;
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_hci_get_clientdata);
|
||||
|
||||
static void nfc_hci_failure(struct nfc_hci_dev *hdev, int err)
|
||||
{
|
||||
mutex_lock(&hdev->msg_tx_mutex);
|
||||
|
@ -758,13 +646,12 @@ exit:
|
|||
mutex_unlock(&hdev->msg_tx_mutex);
|
||||
}
|
||||
|
||||
void nfc_hci_driver_failure(struct nfc_hci_dev *hdev, int err)
|
||||
static void nfc_hci_llc_failure(struct nfc_hci_dev *hdev, int err)
|
||||
{
|
||||
nfc_hci_failure(hdev, err);
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_hci_driver_failure);
|
||||
|
||||
void nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb)
|
||||
static void nfc_hci_recv_from_llc(struct nfc_hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hcp_packet *packet;
|
||||
u8 type;
|
||||
|
@ -827,9 +714,158 @@ void nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb)
|
|||
nfc_hci_hcp_message_rx(hdev, pipe, type, instruction, hcp_skb);
|
||||
} else {
|
||||
skb_queue_tail(&hdev->msg_rx_queue, hcp_skb);
|
||||
queue_work(hdev->msg_rx_wq, &hdev->msg_rx_work);
|
||||
queue_work(system_nrt_wq, &hdev->msg_rx_work);
|
||||
}
|
||||
}
|
||||
|
||||
static struct nfc_ops hci_nfc_ops = {
|
||||
.dev_up = hci_dev_up,
|
||||
.dev_down = hci_dev_down,
|
||||
.start_poll = hci_start_poll,
|
||||
.stop_poll = hci_stop_poll,
|
||||
.activate_target = hci_activate_target,
|
||||
.deactivate_target = hci_deactivate_target,
|
||||
.im_transceive = hci_transceive,
|
||||
.check_presence = hci_check_presence,
|
||||
};
|
||||
|
||||
struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
|
||||
struct nfc_hci_init_data *init_data,
|
||||
u32 protocols,
|
||||
const char *llc_name,
|
||||
int tx_headroom,
|
||||
int tx_tailroom,
|
||||
int max_link_payload)
|
||||
{
|
||||
struct nfc_hci_dev *hdev;
|
||||
|
||||
if (ops->xmit == NULL)
|
||||
return NULL;
|
||||
|
||||
if (protocols == 0)
|
||||
return NULL;
|
||||
|
||||
hdev = kzalloc(sizeof(struct nfc_hci_dev), GFP_KERNEL);
|
||||
if (hdev == NULL)
|
||||
return NULL;
|
||||
|
||||
hdev->llc = nfc_llc_allocate(llc_name, hdev, ops->xmit,
|
||||
nfc_hci_recv_from_llc, tx_headroom,
|
||||
tx_tailroom, nfc_hci_llc_failure);
|
||||
if (hdev->llc == NULL) {
|
||||
kfree(hdev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols,
|
||||
tx_headroom + HCI_CMDS_HEADROOM,
|
||||
tx_tailroom);
|
||||
if (!hdev->ndev) {
|
||||
nfc_llc_free(hdev->llc);
|
||||
kfree(hdev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hdev->ops = ops;
|
||||
hdev->max_data_link_payload = max_link_payload;
|
||||
hdev->init_data = *init_data;
|
||||
|
||||
nfc_set_drvdata(hdev->ndev, hdev);
|
||||
|
||||
memset(hdev->gate2pipe, NFC_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe));
|
||||
|
||||
return hdev;
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_hci_allocate_device);
|
||||
|
||||
void nfc_hci_free_device(struct nfc_hci_dev *hdev)
|
||||
{
|
||||
nfc_free_device(hdev->ndev);
|
||||
nfc_llc_free(hdev->llc);
|
||||
kfree(hdev);
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_hci_free_device);
|
||||
|
||||
int nfc_hci_register_device(struct nfc_hci_dev *hdev)
|
||||
{
|
||||
mutex_init(&hdev->msg_tx_mutex);
|
||||
|
||||
INIT_LIST_HEAD(&hdev->msg_tx_queue);
|
||||
|
||||
INIT_WORK(&hdev->msg_tx_work, nfc_hci_msg_tx_work);
|
||||
|
||||
init_timer(&hdev->cmd_timer);
|
||||
hdev->cmd_timer.data = (unsigned long)hdev;
|
||||
hdev->cmd_timer.function = nfc_hci_cmd_timeout;
|
||||
|
||||
skb_queue_head_init(&hdev->rx_hcp_frags);
|
||||
|
||||
INIT_WORK(&hdev->msg_rx_work, nfc_hci_msg_rx_work);
|
||||
|
||||
skb_queue_head_init(&hdev->msg_rx_queue);
|
||||
|
||||
return nfc_register_device(hdev->ndev);
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_hci_register_device);
|
||||
|
||||
void nfc_hci_unregister_device(struct nfc_hci_dev *hdev)
|
||||
{
|
||||
struct hci_msg *msg, *n;
|
||||
|
||||
skb_queue_purge(&hdev->rx_hcp_frags);
|
||||
skb_queue_purge(&hdev->msg_rx_queue);
|
||||
|
||||
list_for_each_entry_safe(msg, n, &hdev->msg_tx_queue, msg_l) {
|
||||
list_del(&msg->msg_l);
|
||||
skb_queue_purge(&msg->msg_frags);
|
||||
kfree(msg);
|
||||
}
|
||||
|
||||
del_timer_sync(&hdev->cmd_timer);
|
||||
|
||||
nfc_unregister_device(hdev->ndev);
|
||||
|
||||
cancel_work_sync(&hdev->msg_tx_work);
|
||||
cancel_work_sync(&hdev->msg_rx_work);
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_hci_unregister_device);
|
||||
|
||||
void nfc_hci_set_clientdata(struct nfc_hci_dev *hdev, void *clientdata)
|
||||
{
|
||||
hdev->clientdata = clientdata;
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_hci_set_clientdata);
|
||||
|
||||
void *nfc_hci_get_clientdata(struct nfc_hci_dev *hdev)
|
||||
{
|
||||
return hdev->clientdata;
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_hci_get_clientdata);
|
||||
|
||||
void nfc_hci_driver_failure(struct nfc_hci_dev *hdev, int err)
|
||||
{
|
||||
nfc_hci_failure(hdev, err);
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_hci_driver_failure);
|
||||
|
||||
void inline nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
nfc_llc_rcv_from_drv(hdev->llc, skb);
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_hci_recv_frame);
|
||||
|
||||
static int __init nfc_hci_init(void)
|
||||
{
|
||||
return nfc_llc_init();
|
||||
}
|
||||
|
||||
static void __exit nfc_hci_exit(void)
|
||||
{
|
||||
nfc_llc_exit();
|
||||
}
|
||||
|
||||
subsys_initcall(nfc_hci_init);
|
||||
module_exit(nfc_hci_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("NFC HCI Core");
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#ifndef __LOCAL_HCI_H
|
||||
#define __LOCAL_HCI_H
|
||||
|
||||
#include <net/nfc/hci.h>
|
||||
|
||||
struct gate_pipe_map {
|
||||
u8 gate;
|
||||
u8 pipe;
|
||||
|
@ -35,15 +37,6 @@ struct hcp_packet {
|
|||
struct hcp_message message;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* HCI command execution completion callback.
|
||||
* result will be a standard linux error (may be converted from HCI response)
|
||||
* skb contains the response data and must be disposed, or may be NULL if
|
||||
* an error occured
|
||||
*/
|
||||
typedef void (*hci_cmd_cb_t) (struct nfc_hci_dev *hdev, int result,
|
||||
struct sk_buff *skb, void *cb_data);
|
||||
|
||||
struct hcp_exec_waiter {
|
||||
wait_queue_head_t *wq;
|
||||
bool exec_complete;
|
||||
|
@ -55,7 +48,7 @@ struct hci_msg {
|
|||
struct list_head msg_l;
|
||||
struct sk_buff_head msg_frags;
|
||||
bool wait_response;
|
||||
hci_cmd_cb_t cb;
|
||||
data_exchange_cb_t cb;
|
||||
void *cb_context;
|
||||
unsigned long completion_delay;
|
||||
};
|
||||
|
@ -83,7 +76,7 @@ struct hci_create_pipe_resp {
|
|||
int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
|
||||
u8 type, u8 instruction,
|
||||
const u8 *payload, size_t payload_len,
|
||||
hci_cmd_cb_t cb, void *cb_data,
|
||||
data_exchange_cb_t cb, void *cb_context,
|
||||
unsigned long completion_delay);
|
||||
|
||||
u8 nfc_hci_pipe2gate(struct nfc_hci_dev *hdev, u8 pipe);
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
|
||||
u8 type, u8 instruction,
|
||||
const u8 *payload, size_t payload_len,
|
||||
hci_cmd_cb_t cb, void *cb_data,
|
||||
data_exchange_cb_t cb, void *cb_context,
|
||||
unsigned long completion_delay)
|
||||
{
|
||||
struct nfc_dev *ndev = hdev->ndev;
|
||||
|
@ -52,7 +52,7 @@ int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
|
|||
skb_queue_head_init(&cmd->msg_frags);
|
||||
cmd->wait_response = (type == NFC_HCI_HCP_COMMAND) ? true : false;
|
||||
cmd->cb = cb;
|
||||
cmd->cb_context = cb_data;
|
||||
cmd->cb_context = cb_context;
|
||||
cmd->completion_delay = completion_delay;
|
||||
|
||||
hci_len = payload_len + 1;
|
||||
|
@ -108,7 +108,7 @@ int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
|
|||
list_add_tail(&cmd->msg_l, &hdev->msg_tx_queue);
|
||||
mutex_unlock(&hdev->msg_tx_mutex);
|
||||
|
||||
queue_work(hdev->msg_tx_wq, &hdev->msg_tx_work);
|
||||
queue_work(system_nrt_wq, &hdev->msg_tx_work);
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* Link Layer Control manager
|
||||
*
|
||||
* Copyright (C) 2012 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <net/nfc/llc.h>
|
||||
|
||||
#include "llc.h"
|
||||
|
||||
static struct list_head llc_engines;
|
||||
|
||||
int nfc_llc_init(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
INIT_LIST_HEAD(&llc_engines);
|
||||
|
||||
r = nfc_llc_nop_register();
|
||||
if (r)
|
||||
goto exit;
|
||||
|
||||
r = nfc_llc_shdlc_register();
|
||||
if (r)
|
||||
goto exit;
|
||||
|
||||
return 0;
|
||||
|
||||
exit:
|
||||
nfc_llc_exit();
|
||||
return r;
|
||||
}
|
||||
|
||||
void nfc_llc_exit(void)
|
||||
{
|
||||
struct nfc_llc_engine *llc_engine, *n;
|
||||
|
||||
list_for_each_entry_safe(llc_engine, n, &llc_engines, entry) {
|
||||
list_del(&llc_engine->entry);
|
||||
kfree(llc_engine->name);
|
||||
kfree(llc_engine);
|
||||
}
|
||||
}
|
||||
|
||||
int nfc_llc_register(const char *name, struct nfc_llc_ops *ops)
|
||||
{
|
||||
struct nfc_llc_engine *llc_engine;
|
||||
|
||||
llc_engine = kzalloc(sizeof(struct nfc_llc_engine), GFP_KERNEL);
|
||||
if (llc_engine == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
llc_engine->name = kstrdup(name, GFP_KERNEL);
|
||||
if (llc_engine->name == NULL) {
|
||||
kfree(llc_engine);
|
||||
return -ENOMEM;
|
||||
}
|
||||
llc_engine->ops = ops;
|
||||
|
||||
INIT_LIST_HEAD(&llc_engine->entry);
|
||||
list_add_tail (&llc_engine->entry, &llc_engines);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct nfc_llc_engine *nfc_llc_name_to_engine(const char *name)
|
||||
{
|
||||
struct nfc_llc_engine *llc_engine;
|
||||
|
||||
list_for_each_entry(llc_engine, &llc_engines, entry) {
|
||||
if (strcmp(llc_engine->name, name) == 0)
|
||||
return llc_engine;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void nfc_llc_unregister(const char *name)
|
||||
{
|
||||
struct nfc_llc_engine *llc_engine;
|
||||
|
||||
llc_engine = nfc_llc_name_to_engine(name);
|
||||
if (llc_engine == NULL)
|
||||
return;
|
||||
|
||||
list_del(&llc_engine->entry);
|
||||
kfree(llc_engine->name);
|
||||
kfree(llc_engine);
|
||||
}
|
||||
|
||||
struct nfc_llc *nfc_llc_allocate(const char *name, struct nfc_hci_dev *hdev,
|
||||
xmit_to_drv_t xmit_to_drv,
|
||||
rcv_to_hci_t rcv_to_hci, int tx_headroom,
|
||||
int tx_tailroom, llc_failure_t llc_failure)
|
||||
{
|
||||
struct nfc_llc_engine *llc_engine;
|
||||
struct nfc_llc *llc;
|
||||
|
||||
llc_engine = nfc_llc_name_to_engine(name);
|
||||
if (llc_engine == NULL)
|
||||
return NULL;
|
||||
|
||||
llc = kzalloc(sizeof(struct nfc_llc), GFP_KERNEL);
|
||||
if (llc == NULL)
|
||||
return NULL;
|
||||
|
||||
llc->data = llc_engine->ops->init(hdev, xmit_to_drv, rcv_to_hci,
|
||||
tx_headroom, tx_tailroom,
|
||||
&llc->rx_headroom, &llc->rx_tailroom,
|
||||
llc_failure);
|
||||
if (llc->data == NULL) {
|
||||
kfree(llc);
|
||||
return NULL;
|
||||
}
|
||||
llc->ops = llc_engine->ops;
|
||||
|
||||
return llc;
|
||||
}
|
||||
|
||||
void nfc_llc_free(struct nfc_llc *llc)
|
||||
{
|
||||
llc->ops->deinit(llc);
|
||||
kfree(llc);
|
||||
}
|
||||
|
||||
inline void nfc_llc_get_rx_head_tail_room(struct nfc_llc *llc, int *rx_headroom,
|
||||
int *rx_tailroom)
|
||||
{
|
||||
*rx_headroom = llc->rx_headroom;
|
||||
*rx_tailroom = llc->rx_tailroom;
|
||||
}
|
||||
|
||||
inline int nfc_llc_start(struct nfc_llc *llc)
|
||||
{
|
||||
return llc->ops->start(llc);
|
||||
}
|
||||
|
||||
inline int nfc_llc_stop(struct nfc_llc *llc)
|
||||
{
|
||||
return llc->ops->stop(llc);
|
||||
}
|
||||
|
||||
inline void nfc_llc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb)
|
||||
{
|
||||
llc->ops->rcv_from_drv(llc, skb);
|
||||
}
|
||||
|
||||
inline int nfc_llc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb)
|
||||
{
|
||||
return llc->ops->xmit_from_hci(llc, skb);
|
||||
}
|
||||
|
||||
inline void *nfc_llc_get_data(struct nfc_llc *llc)
|
||||
{
|
||||
return llc->data;
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Link Layer Control manager
|
||||
*
|
||||
* Copyright (C) 2012 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __LOCAL_LLC_H_
|
||||
#define __LOCAL_LLC_H_
|
||||
|
||||
#include <net/nfc/hci.h>
|
||||
#include <net/nfc/llc.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
struct nfc_llc_ops {
|
||||
void *(*init) (struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv,
|
||||
rcv_to_hci_t rcv_to_hci, int tx_headroom,
|
||||
int tx_tailroom, int *rx_headroom, int *rx_tailroom,
|
||||
llc_failure_t llc_failure);
|
||||
void (*deinit) (struct nfc_llc *llc);
|
||||
int (*start) (struct nfc_llc *llc);
|
||||
int (*stop) (struct nfc_llc *llc);
|
||||
void (*rcv_from_drv) (struct nfc_llc *llc, struct sk_buff *skb);
|
||||
int (*xmit_from_hci) (struct nfc_llc *llc, struct sk_buff *skb);
|
||||
};
|
||||
|
||||
struct nfc_llc_engine {
|
||||
const char *name;
|
||||
struct nfc_llc_ops *ops;
|
||||
struct list_head entry;
|
||||
};
|
||||
|
||||
struct nfc_llc {
|
||||
void *data;
|
||||
struct nfc_llc_ops *ops;
|
||||
int rx_headroom;
|
||||
int rx_tailroom;
|
||||
};
|
||||
|
||||
void *nfc_llc_get_data(struct nfc_llc *llc);
|
||||
|
||||
int nfc_llc_register(const char *name, struct nfc_llc_ops *ops);
|
||||
void nfc_llc_unregister(const char *name);
|
||||
|
||||
int nfc_llc_nop_register(void);
|
||||
int nfc_llc_shdlc_register(void);
|
||||
|
||||
#endif /* __LOCAL_LLC_H_ */
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* nop (passthrough) Link Layer Control
|
||||
*
|
||||
* Copyright (C) 2012 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "llc.h"
|
||||
|
||||
struct llc_nop {
|
||||
struct nfc_hci_dev *hdev;
|
||||
xmit_to_drv_t xmit_to_drv;
|
||||
rcv_to_hci_t rcv_to_hci;
|
||||
int tx_headroom;
|
||||
int tx_tailroom;
|
||||
llc_failure_t llc_failure;
|
||||
};
|
||||
|
||||
static void *llc_nop_init(struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv,
|
||||
rcv_to_hci_t rcv_to_hci, int tx_headroom,
|
||||
int tx_tailroom, int *rx_headroom, int *rx_tailroom,
|
||||
llc_failure_t llc_failure)
|
||||
{
|
||||
struct llc_nop *llc_nop;
|
||||
|
||||
*rx_headroom = 0;
|
||||
*rx_tailroom = 0;
|
||||
|
||||
llc_nop = kzalloc(sizeof(struct llc_nop), GFP_KERNEL);
|
||||
if (llc_nop == NULL)
|
||||
return NULL;
|
||||
|
||||
llc_nop->hdev = hdev;
|
||||
llc_nop->xmit_to_drv = xmit_to_drv;
|
||||
llc_nop->rcv_to_hci = rcv_to_hci;
|
||||
llc_nop->tx_headroom = tx_headroom;
|
||||
llc_nop->tx_tailroom = tx_tailroom;
|
||||
llc_nop->llc_failure = llc_failure;
|
||||
|
||||
return llc_nop;
|
||||
}
|
||||
|
||||
static void llc_nop_deinit(struct nfc_llc *llc)
|
||||
{
|
||||
kfree(nfc_llc_get_data(llc));
|
||||
}
|
||||
|
||||
static int llc_nop_start(struct nfc_llc *llc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int llc_nop_stop(struct nfc_llc *llc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void llc_nop_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb)
|
||||
{
|
||||
struct llc_nop *llc_nop = nfc_llc_get_data(llc);
|
||||
|
||||
llc_nop->rcv_to_hci(llc_nop->hdev, skb);
|
||||
}
|
||||
|
||||
static int llc_nop_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb)
|
||||
{
|
||||
struct llc_nop *llc_nop = nfc_llc_get_data(llc);
|
||||
|
||||
return llc_nop->xmit_to_drv(llc_nop->hdev, skb);
|
||||
}
|
||||
|
||||
static struct nfc_llc_ops llc_nop_ops = {
|
||||
.init = llc_nop_init,
|
||||
.deinit = llc_nop_deinit,
|
||||
.start = llc_nop_start,
|
||||
.stop = llc_nop_stop,
|
||||
.rcv_from_drv = llc_nop_rcv_from_drv,
|
||||
.xmit_from_hci = llc_nop_xmit_from_hci,
|
||||
};
|
||||
|
||||
int nfc_llc_nop_register(void)
|
||||
{
|
||||
return nfc_llc_register(LLC_NOP_NAME, &llc_nop_ops);
|
||||
}
|
|
@ -1,10 +1,11 @@
|
|||
/*
|
||||
* shdlc Link Layer Control
|
||||
*
|
||||
* Copyright (C) 2012 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms 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 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
|
||||
|
@ -19,18 +20,65 @@
|
|||
|
||||
#define pr_fmt(fmt) "shdlc: %s: " fmt, __func__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/crc-ccitt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include <net/nfc/hci.h>
|
||||
#include <net/nfc/shdlc.h>
|
||||
#include "llc.h"
|
||||
|
||||
enum shdlc_state {
|
||||
SHDLC_DISCONNECTED = 0,
|
||||
SHDLC_CONNECTING = 1,
|
||||
SHDLC_NEGOTIATING = 2,
|
||||
SHDLC_HALF_CONNECTED = 3,
|
||||
SHDLC_CONNECTED = 4
|
||||
};
|
||||
|
||||
struct llc_shdlc {
|
||||
struct nfc_hci_dev *hdev;
|
||||
xmit_to_drv_t xmit_to_drv;
|
||||
rcv_to_hci_t rcv_to_hci;
|
||||
|
||||
struct mutex state_mutex;
|
||||
enum shdlc_state state;
|
||||
int hard_fault;
|
||||
|
||||
wait_queue_head_t *connect_wq;
|
||||
int connect_tries;
|
||||
int connect_result;
|
||||
struct timer_list connect_timer;/* aka T3 in spec 10.6.1 */
|
||||
|
||||
u8 w; /* window size */
|
||||
bool srej_support;
|
||||
|
||||
struct timer_list t1_timer; /* send ack timeout */
|
||||
bool t1_active;
|
||||
|
||||
struct timer_list t2_timer; /* guard/retransmit timeout */
|
||||
bool t2_active;
|
||||
|
||||
int ns; /* next seq num for send */
|
||||
int nr; /* next expected seq num for receive */
|
||||
int dnr; /* oldest sent unacked seq num */
|
||||
|
||||
struct sk_buff_head rcv_q;
|
||||
|
||||
struct sk_buff_head send_q;
|
||||
bool rnr; /* other side is not ready to receive */
|
||||
|
||||
struct sk_buff_head ack_pending_q;
|
||||
|
||||
struct work_struct sm_work;
|
||||
|
||||
int tx_headroom;
|
||||
int tx_tailroom;
|
||||
|
||||
llc_failure_t llc_failure;
|
||||
};
|
||||
|
||||
#define SHDLC_LLC_HEAD_ROOM 2
|
||||
#define SHDLC_LLC_TAIL_ROOM 2
|
||||
|
||||
#define SHDLC_MAX_WINDOW 4
|
||||
#define SHDLC_SREJ_SUPPORT false
|
||||
|
@ -71,7 +119,7 @@ do { \
|
|||
} while (0)
|
||||
|
||||
/* checks x < y <= z modulo 8 */
|
||||
static bool nfc_shdlc_x_lt_y_lteq_z(int x, int y, int z)
|
||||
static bool llc_shdlc_x_lt_y_lteq_z(int x, int y, int z)
|
||||
{
|
||||
if (x < z)
|
||||
return ((x < y) && (y <= z)) ? true : false;
|
||||
|
@ -80,7 +128,7 @@ static bool nfc_shdlc_x_lt_y_lteq_z(int x, int y, int z)
|
|||
}
|
||||
|
||||
/* checks x <= y < z modulo 8 */
|
||||
static bool nfc_shdlc_x_lteq_y_lt_z(int x, int y, int z)
|
||||
static bool llc_shdlc_x_lteq_y_lt_z(int x, int y, int z)
|
||||
{
|
||||
if (x <= z)
|
||||
return ((x <= y) && (y < z)) ? true : false;
|
||||
|
@ -88,36 +136,21 @@ static bool nfc_shdlc_x_lteq_y_lt_z(int x, int y, int z)
|
|||
return ((y >= x) || (y < z)) ? true : false;
|
||||
}
|
||||
|
||||
static struct sk_buff *nfc_shdlc_alloc_skb(struct nfc_shdlc *shdlc,
|
||||
static struct sk_buff *llc_shdlc_alloc_skb(struct llc_shdlc *shdlc,
|
||||
int payload_len)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = alloc_skb(shdlc->client_headroom + SHDLC_LLC_HEAD_ROOM +
|
||||
shdlc->client_tailroom + SHDLC_LLC_TAIL_ROOM +
|
||||
payload_len, GFP_KERNEL);
|
||||
skb = alloc_skb(shdlc->tx_headroom + SHDLC_LLC_HEAD_ROOM +
|
||||
shdlc->tx_tailroom + payload_len, GFP_KERNEL);
|
||||
if (skb)
|
||||
skb_reserve(skb, shdlc->client_headroom + SHDLC_LLC_HEAD_ROOM);
|
||||
skb_reserve(skb, shdlc->tx_headroom + SHDLC_LLC_HEAD_ROOM);
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static void nfc_shdlc_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;
|
||||
}
|
||||
|
||||
/* immediately sends an S frame. */
|
||||
static int nfc_shdlc_send_s_frame(struct nfc_shdlc *shdlc,
|
||||
static int llc_shdlc_send_s_frame(struct llc_shdlc *shdlc,
|
||||
enum sframe_type sframe_type, int nr)
|
||||
{
|
||||
int r;
|
||||
|
@ -125,15 +158,13 @@ static int nfc_shdlc_send_s_frame(struct nfc_shdlc *shdlc,
|
|||
|
||||
pr_debug("sframe_type=%d nr=%d\n", sframe_type, nr);
|
||||
|
||||
skb = nfc_shdlc_alloc_skb(shdlc, 0);
|
||||
skb = llc_shdlc_alloc_skb(shdlc, 0);
|
||||
if (skb == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
*skb_push(skb, 1) = SHDLC_CONTROL_HEAD_S | (sframe_type << 3) | nr;
|
||||
|
||||
nfc_shdlc_add_len_crc(skb);
|
||||
|
||||
r = shdlc->ops->xmit(shdlc, skb);
|
||||
r = shdlc->xmit_to_drv(shdlc->hdev, skb);
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
|
@ -141,7 +172,7 @@ static int nfc_shdlc_send_s_frame(struct nfc_shdlc *shdlc,
|
|||
}
|
||||
|
||||
/* immediately sends an U frame. skb may contain optional payload */
|
||||
static int nfc_shdlc_send_u_frame(struct nfc_shdlc *shdlc,
|
||||
static int llc_shdlc_send_u_frame(struct llc_shdlc *shdlc,
|
||||
struct sk_buff *skb,
|
||||
enum uframe_modifier uframe_modifier)
|
||||
{
|
||||
|
@ -151,9 +182,7 @@ static int nfc_shdlc_send_u_frame(struct nfc_shdlc *shdlc,
|
|||
|
||||
*skb_push(skb, 1) = SHDLC_CONTROL_HEAD_U | uframe_modifier;
|
||||
|
||||
nfc_shdlc_add_len_crc(skb);
|
||||
|
||||
r = shdlc->ops->xmit(shdlc, skb);
|
||||
r = shdlc->xmit_to_drv(shdlc->hdev, skb);
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
|
@ -164,7 +193,7 @@ static int nfc_shdlc_send_u_frame(struct nfc_shdlc *shdlc,
|
|||
* Free ack_pending frames until y_nr - 1, and reset t2 according to
|
||||
* the remaining oldest ack_pending frame sent time
|
||||
*/
|
||||
static void nfc_shdlc_reset_t2(struct nfc_shdlc *shdlc, int y_nr)
|
||||
static void llc_shdlc_reset_t2(struct llc_shdlc *shdlc, int y_nr)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
int dnr = shdlc->dnr; /* MUST initially be < y_nr */
|
||||
|
@ -204,7 +233,7 @@ static void nfc_shdlc_reset_t2(struct nfc_shdlc *shdlc, int y_nr)
|
|||
* Receive validated frames from lower layer. skb contains HCI payload only.
|
||||
* Handle according to algorithm at spec:10.8.2
|
||||
*/
|
||||
static void nfc_shdlc_rcv_i_frame(struct nfc_shdlc *shdlc,
|
||||
static void llc_shdlc_rcv_i_frame(struct llc_shdlc *shdlc,
|
||||
struct sk_buff *skb, int ns, int nr)
|
||||
{
|
||||
int x_ns = ns;
|
||||
|
@ -216,66 +245,64 @@ static void nfc_shdlc_rcv_i_frame(struct nfc_shdlc *shdlc,
|
|||
goto exit;
|
||||
|
||||
if (x_ns != shdlc->nr) {
|
||||
nfc_shdlc_send_s_frame(shdlc, S_FRAME_REJ, shdlc->nr);
|
||||
llc_shdlc_send_s_frame(shdlc, S_FRAME_REJ, shdlc->nr);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (shdlc->t1_active == false) {
|
||||
shdlc->t1_active = true;
|
||||
mod_timer(&shdlc->t1_timer,
|
||||
mod_timer(&shdlc->t1_timer, jiffies +
|
||||
msecs_to_jiffies(SHDLC_T1_VALUE_MS(shdlc->w)));
|
||||
pr_debug("(re)Start T1(send ack)\n");
|
||||
}
|
||||
|
||||
if (skb->len) {
|
||||
nfc_hci_recv_frame(shdlc->hdev, skb);
|
||||
shdlc->rcv_to_hci(shdlc->hdev, skb);
|
||||
skb = NULL;
|
||||
}
|
||||
|
||||
shdlc->nr = (shdlc->nr + 1) % 8;
|
||||
|
||||
if (nfc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) {
|
||||
nfc_shdlc_reset_t2(shdlc, y_nr);
|
||||
if (llc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) {
|
||||
llc_shdlc_reset_t2(shdlc, y_nr);
|
||||
|
||||
shdlc->dnr = y_nr;
|
||||
}
|
||||
|
||||
exit:
|
||||
if (skb)
|
||||
kfree_skb(skb);
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
static void nfc_shdlc_rcv_ack(struct nfc_shdlc *shdlc, int y_nr)
|
||||
static void llc_shdlc_rcv_ack(struct llc_shdlc *shdlc, int y_nr)
|
||||
{
|
||||
pr_debug("remote acked up to frame %d excluded\n", y_nr);
|
||||
|
||||
if (nfc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) {
|
||||
nfc_shdlc_reset_t2(shdlc, y_nr);
|
||||
if (llc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) {
|
||||
llc_shdlc_reset_t2(shdlc, y_nr);
|
||||
shdlc->dnr = y_nr;
|
||||
}
|
||||
}
|
||||
|
||||
static void nfc_shdlc_requeue_ack_pending(struct nfc_shdlc *shdlc)
|
||||
static void llc_shdlc_requeue_ack_pending(struct llc_shdlc *shdlc)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
pr_debug("ns reset to %d\n", shdlc->dnr);
|
||||
|
||||
while ((skb = skb_dequeue_tail(&shdlc->ack_pending_q))) {
|
||||
skb_pull(skb, 2); /* remove len+control */
|
||||
skb_trim(skb, skb->len - 2); /* remove crc */
|
||||
skb_pull(skb, 1); /* remove control field */
|
||||
skb_queue_head(&shdlc->send_q, skb);
|
||||
}
|
||||
shdlc->ns = shdlc->dnr;
|
||||
}
|
||||
|
||||
static void nfc_shdlc_rcv_rej(struct nfc_shdlc *shdlc, int y_nr)
|
||||
static void llc_shdlc_rcv_rej(struct llc_shdlc *shdlc, int y_nr)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
pr_debug("remote asks retransmition from frame %d\n", y_nr);
|
||||
|
||||
if (nfc_shdlc_x_lteq_y_lt_z(shdlc->dnr, y_nr, shdlc->ns)) {
|
||||
if (llc_shdlc_x_lteq_y_lt_z(shdlc->dnr, y_nr, shdlc->ns)) {
|
||||
if (shdlc->t2_active) {
|
||||
del_timer_sync(&shdlc->t2_timer);
|
||||
shdlc->t2_active = false;
|
||||
|
@ -289,12 +316,12 @@ static void nfc_shdlc_rcv_rej(struct nfc_shdlc *shdlc, int y_nr)
|
|||
}
|
||||
}
|
||||
|
||||
nfc_shdlc_requeue_ack_pending(shdlc);
|
||||
llc_shdlc_requeue_ack_pending(shdlc);
|
||||
}
|
||||
}
|
||||
|
||||
/* See spec RR:10.8.3 REJ:10.8.4 */
|
||||
static void nfc_shdlc_rcv_s_frame(struct nfc_shdlc *shdlc,
|
||||
static void llc_shdlc_rcv_s_frame(struct llc_shdlc *shdlc,
|
||||
enum sframe_type s_frame_type, int nr)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
@ -304,21 +331,21 @@ static void nfc_shdlc_rcv_s_frame(struct nfc_shdlc *shdlc,
|
|||
|
||||
switch (s_frame_type) {
|
||||
case S_FRAME_RR:
|
||||
nfc_shdlc_rcv_ack(shdlc, nr);
|
||||
llc_shdlc_rcv_ack(shdlc, nr);
|
||||
if (shdlc->rnr == true) { /* see SHDLC 10.7.7 */
|
||||
shdlc->rnr = false;
|
||||
if (shdlc->send_q.qlen == 0) {
|
||||
skb = nfc_shdlc_alloc_skb(shdlc, 0);
|
||||
skb = llc_shdlc_alloc_skb(shdlc, 0);
|
||||
if (skb)
|
||||
skb_queue_tail(&shdlc->send_q, skb);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case S_FRAME_REJ:
|
||||
nfc_shdlc_rcv_rej(shdlc, nr);
|
||||
llc_shdlc_rcv_rej(shdlc, nr);
|
||||
break;
|
||||
case S_FRAME_RNR:
|
||||
nfc_shdlc_rcv_ack(shdlc, nr);
|
||||
llc_shdlc_rcv_ack(shdlc, nr);
|
||||
shdlc->rnr = true;
|
||||
break;
|
||||
default:
|
||||
|
@ -326,7 +353,7 @@ static void nfc_shdlc_rcv_s_frame(struct nfc_shdlc *shdlc,
|
|||
}
|
||||
}
|
||||
|
||||
static void nfc_shdlc_connect_complete(struct nfc_shdlc *shdlc, int r)
|
||||
static void llc_shdlc_connect_complete(struct llc_shdlc *shdlc, int r)
|
||||
{
|
||||
pr_debug("result=%d\n", r);
|
||||
|
||||
|
@ -337,7 +364,7 @@ static void nfc_shdlc_connect_complete(struct nfc_shdlc *shdlc, int r)
|
|||
shdlc->nr = 0;
|
||||
shdlc->dnr = 0;
|
||||
|
||||
shdlc->state = SHDLC_CONNECTED;
|
||||
shdlc->state = SHDLC_HALF_CONNECTED;
|
||||
} else {
|
||||
shdlc->state = SHDLC_DISCONNECTED;
|
||||
}
|
||||
|
@ -347,36 +374,36 @@ static void nfc_shdlc_connect_complete(struct nfc_shdlc *shdlc, int r)
|
|||
wake_up(shdlc->connect_wq);
|
||||
}
|
||||
|
||||
static int nfc_shdlc_connect_initiate(struct nfc_shdlc *shdlc)
|
||||
static int llc_shdlc_connect_initiate(struct llc_shdlc *shdlc)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
pr_debug("\n");
|
||||
|
||||
skb = nfc_shdlc_alloc_skb(shdlc, 2);
|
||||
skb = llc_shdlc_alloc_skb(shdlc, 2);
|
||||
if (skb == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
*skb_put(skb, 1) = SHDLC_MAX_WINDOW;
|
||||
*skb_put(skb, 1) = SHDLC_SREJ_SUPPORT ? 1 : 0;
|
||||
|
||||
return nfc_shdlc_send_u_frame(shdlc, skb, U_FRAME_RSET);
|
||||
return llc_shdlc_send_u_frame(shdlc, skb, U_FRAME_RSET);
|
||||
}
|
||||
|
||||
static int nfc_shdlc_connect_send_ua(struct nfc_shdlc *shdlc)
|
||||
static int llc_shdlc_connect_send_ua(struct llc_shdlc *shdlc)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
pr_debug("\n");
|
||||
|
||||
skb = nfc_shdlc_alloc_skb(shdlc, 0);
|
||||
skb = llc_shdlc_alloc_skb(shdlc, 0);
|
||||
if (skb == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
return nfc_shdlc_send_u_frame(shdlc, skb, U_FRAME_UA);
|
||||
return llc_shdlc_send_u_frame(shdlc, skb, U_FRAME_UA);
|
||||
}
|
||||
|
||||
static void nfc_shdlc_rcv_u_frame(struct nfc_shdlc *shdlc,
|
||||
static void llc_shdlc_rcv_u_frame(struct llc_shdlc *shdlc,
|
||||
struct sk_buff *skb,
|
||||
enum uframe_modifier u_frame_modifier)
|
||||
{
|
||||
|
@ -388,8 +415,13 @@ static void nfc_shdlc_rcv_u_frame(struct nfc_shdlc *shdlc,
|
|||
|
||||
switch (u_frame_modifier) {
|
||||
case U_FRAME_RSET:
|
||||
if (shdlc->state == SHDLC_NEGOCIATING) {
|
||||
/* we sent RSET, but chip wants to negociate */
|
||||
switch (shdlc->state) {
|
||||
case SHDLC_NEGOTIATING:
|
||||
case SHDLC_CONNECTING:
|
||||
/*
|
||||
* We sent RSET, but chip wants to negociate or we
|
||||
* got RSET before we managed to send out our.
|
||||
*/
|
||||
if (skb->len > 0)
|
||||
w = skb->data[0];
|
||||
|
||||
|
@ -401,22 +433,34 @@ static void nfc_shdlc_rcv_u_frame(struct nfc_shdlc *shdlc,
|
|||
(SHDLC_SREJ_SUPPORT || (srej_support == false))) {
|
||||
shdlc->w = w;
|
||||
shdlc->srej_support = srej_support;
|
||||
r = nfc_shdlc_connect_send_ua(shdlc);
|
||||
nfc_shdlc_connect_complete(shdlc, r);
|
||||
r = llc_shdlc_connect_send_ua(shdlc);
|
||||
llc_shdlc_connect_complete(shdlc, r);
|
||||
}
|
||||
} else if (shdlc->state == SHDLC_CONNECTED) {
|
||||
break;
|
||||
case SHDLC_HALF_CONNECTED:
|
||||
/*
|
||||
* Chip resent RSET due to its timeout - Ignote it
|
||||
* as we already sent UA.
|
||||
*/
|
||||
break;
|
||||
case SHDLC_CONNECTED:
|
||||
/*
|
||||
* Chip wants to reset link. This is unexpected and
|
||||
* unsupported.
|
||||
*/
|
||||
shdlc->hard_fault = -ECONNRESET;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case U_FRAME_UA:
|
||||
if ((shdlc->state == SHDLC_CONNECTING &&
|
||||
shdlc->connect_tries > 0) ||
|
||||
(shdlc->state == SHDLC_NEGOCIATING))
|
||||
nfc_shdlc_connect_complete(shdlc, 0);
|
||||
(shdlc->state == SHDLC_NEGOTIATING)) {
|
||||
llc_shdlc_connect_complete(shdlc, 0);
|
||||
shdlc->state = SHDLC_CONNECTED;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -425,7 +469,7 @@ static void nfc_shdlc_rcv_u_frame(struct nfc_shdlc *shdlc,
|
|||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
static void nfc_shdlc_handle_rcv_queue(struct nfc_shdlc *shdlc)
|
||||
static void llc_shdlc_handle_rcv_queue(struct llc_shdlc *shdlc)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
u8 control;
|
||||
|
@ -443,19 +487,25 @@ static void nfc_shdlc_handle_rcv_queue(struct nfc_shdlc *shdlc)
|
|||
switch (control & SHDLC_CONTROL_HEAD_MASK) {
|
||||
case SHDLC_CONTROL_HEAD_I:
|
||||
case SHDLC_CONTROL_HEAD_I2:
|
||||
if (shdlc->state == SHDLC_HALF_CONNECTED)
|
||||
shdlc->state = SHDLC_CONNECTED;
|
||||
|
||||
ns = (control & SHDLC_CONTROL_NS_MASK) >> 3;
|
||||
nr = control & SHDLC_CONTROL_NR_MASK;
|
||||
nfc_shdlc_rcv_i_frame(shdlc, skb, ns, nr);
|
||||
llc_shdlc_rcv_i_frame(shdlc, skb, ns, nr);
|
||||
break;
|
||||
case SHDLC_CONTROL_HEAD_S:
|
||||
if (shdlc->state == SHDLC_HALF_CONNECTED)
|
||||
shdlc->state = SHDLC_CONNECTED;
|
||||
|
||||
s_frame_type = (control & SHDLC_CONTROL_TYPE_MASK) >> 3;
|
||||
nr = control & SHDLC_CONTROL_NR_MASK;
|
||||
nfc_shdlc_rcv_s_frame(shdlc, s_frame_type, nr);
|
||||
llc_shdlc_rcv_s_frame(shdlc, s_frame_type, nr);
|
||||
kfree_skb(skb);
|
||||
break;
|
||||
case SHDLC_CONTROL_HEAD_U:
|
||||
u_frame_modifier = control & SHDLC_CONTROL_M_MASK;
|
||||
nfc_shdlc_rcv_u_frame(shdlc, skb, u_frame_modifier);
|
||||
llc_shdlc_rcv_u_frame(shdlc, skb, u_frame_modifier);
|
||||
break;
|
||||
default:
|
||||
pr_err("UNKNOWN Control=%d\n", control);
|
||||
|
@ -465,7 +515,7 @@ static void nfc_shdlc_handle_rcv_queue(struct nfc_shdlc *shdlc)
|
|||
}
|
||||
}
|
||||
|
||||
static int nfc_shdlc_w_used(int ns, int dnr)
|
||||
static int llc_shdlc_w_used(int ns, int dnr)
|
||||
{
|
||||
int unack_count;
|
||||
|
||||
|
@ -478,7 +528,7 @@ static int nfc_shdlc_w_used(int ns, int dnr)
|
|||
}
|
||||
|
||||
/* Send frames according to algorithm at spec:10.8.1 */
|
||||
static void nfc_shdlc_handle_send_queue(struct nfc_shdlc *shdlc)
|
||||
static void llc_shdlc_handle_send_queue(struct llc_shdlc *shdlc)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
int r;
|
||||
|
@ -489,7 +539,7 @@ static void nfc_shdlc_handle_send_queue(struct nfc_shdlc *shdlc)
|
|||
("sendQlen=%d ns=%d dnr=%d rnr=%s w_room=%d unackQlen=%d\n",
|
||||
shdlc->send_q.qlen, shdlc->ns, shdlc->dnr,
|
||||
shdlc->rnr == false ? "false" : "true",
|
||||
shdlc->w - nfc_shdlc_w_used(shdlc->ns, shdlc->dnr),
|
||||
shdlc->w - llc_shdlc_w_used(shdlc->ns, shdlc->dnr),
|
||||
shdlc->ack_pending_q.qlen);
|
||||
|
||||
while (shdlc->send_q.qlen && shdlc->ack_pending_q.qlen < shdlc->w &&
|
||||
|
@ -508,11 +558,9 @@ static void nfc_shdlc_handle_send_queue(struct nfc_shdlc *shdlc)
|
|||
|
||||
pr_debug("Sending I-Frame %d, waiting to rcv %d\n", shdlc->ns,
|
||||
shdlc->nr);
|
||||
/* SHDLC_DUMP_SKB("shdlc frame written", skb); */
|
||||
SHDLC_DUMP_SKB("shdlc frame written", skb);
|
||||
|
||||
nfc_shdlc_add_len_crc(skb);
|
||||
|
||||
r = shdlc->ops->xmit(shdlc, skb);
|
||||
r = shdlc->xmit_to_drv(shdlc->hdev, skb);
|
||||
if (r < 0) {
|
||||
shdlc->hard_fault = r;
|
||||
break;
|
||||
|
@ -534,36 +582,36 @@ static void nfc_shdlc_handle_send_queue(struct nfc_shdlc *shdlc)
|
|||
}
|
||||
}
|
||||
|
||||
static void nfc_shdlc_connect_timeout(unsigned long data)
|
||||
static void llc_shdlc_connect_timeout(unsigned long data)
|
||||
{
|
||||
struct nfc_shdlc *shdlc = (struct nfc_shdlc *)data;
|
||||
struct llc_shdlc *shdlc = (struct llc_shdlc *)data;
|
||||
|
||||
pr_debug("\n");
|
||||
|
||||
queue_work(shdlc->sm_wq, &shdlc->sm_work);
|
||||
queue_work(system_nrt_wq, &shdlc->sm_work);
|
||||
}
|
||||
|
||||
static void nfc_shdlc_t1_timeout(unsigned long data)
|
||||
static void llc_shdlc_t1_timeout(unsigned long data)
|
||||
{
|
||||
struct nfc_shdlc *shdlc = (struct nfc_shdlc *)data;
|
||||
struct llc_shdlc *shdlc = (struct llc_shdlc *)data;
|
||||
|
||||
pr_debug("SoftIRQ: need to send ack\n");
|
||||
|
||||
queue_work(shdlc->sm_wq, &shdlc->sm_work);
|
||||
queue_work(system_nrt_wq, &shdlc->sm_work);
|
||||
}
|
||||
|
||||
static void nfc_shdlc_t2_timeout(unsigned long data)
|
||||
static void llc_shdlc_t2_timeout(unsigned long data)
|
||||
{
|
||||
struct nfc_shdlc *shdlc = (struct nfc_shdlc *)data;
|
||||
struct llc_shdlc *shdlc = (struct llc_shdlc *)data;
|
||||
|
||||
pr_debug("SoftIRQ: need to retransmit\n");
|
||||
|
||||
queue_work(shdlc->sm_wq, &shdlc->sm_work);
|
||||
queue_work(system_nrt_wq, &shdlc->sm_work);
|
||||
}
|
||||
|
||||
static void nfc_shdlc_sm_work(struct work_struct *work)
|
||||
static void llc_shdlc_sm_work(struct work_struct *work)
|
||||
{
|
||||
struct nfc_shdlc *shdlc = container_of(work, struct nfc_shdlc, sm_work);
|
||||
struct llc_shdlc *shdlc = container_of(work, struct llc_shdlc, sm_work);
|
||||
int r;
|
||||
|
||||
pr_debug("\n");
|
||||
|
@ -578,46 +626,47 @@ static void nfc_shdlc_sm_work(struct work_struct *work)
|
|||
break;
|
||||
case SHDLC_CONNECTING:
|
||||
if (shdlc->hard_fault) {
|
||||
nfc_shdlc_connect_complete(shdlc, shdlc->hard_fault);
|
||||
llc_shdlc_connect_complete(shdlc, shdlc->hard_fault);
|
||||
break;
|
||||
}
|
||||
|
||||
if (shdlc->connect_tries++ < 5)
|
||||
r = nfc_shdlc_connect_initiate(shdlc);
|
||||
r = llc_shdlc_connect_initiate(shdlc);
|
||||
else
|
||||
r = -ETIME;
|
||||
if (r < 0)
|
||||
nfc_shdlc_connect_complete(shdlc, r);
|
||||
llc_shdlc_connect_complete(shdlc, r);
|
||||
else {
|
||||
mod_timer(&shdlc->connect_timer, jiffies +
|
||||
msecs_to_jiffies(SHDLC_CONNECT_VALUE_MS));
|
||||
|
||||
shdlc->state = SHDLC_NEGOCIATING;
|
||||
shdlc->state = SHDLC_NEGOTIATING;
|
||||
}
|
||||
break;
|
||||
case SHDLC_NEGOCIATING:
|
||||
case SHDLC_NEGOTIATING:
|
||||
if (timer_pending(&shdlc->connect_timer) == 0) {
|
||||
shdlc->state = SHDLC_CONNECTING;
|
||||
queue_work(shdlc->sm_wq, &shdlc->sm_work);
|
||||
queue_work(system_nrt_wq, &shdlc->sm_work);
|
||||
}
|
||||
|
||||
nfc_shdlc_handle_rcv_queue(shdlc);
|
||||
llc_shdlc_handle_rcv_queue(shdlc);
|
||||
|
||||
if (shdlc->hard_fault) {
|
||||
nfc_shdlc_connect_complete(shdlc, shdlc->hard_fault);
|
||||
llc_shdlc_connect_complete(shdlc, shdlc->hard_fault);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SHDLC_HALF_CONNECTED:
|
||||
case SHDLC_CONNECTED:
|
||||
nfc_shdlc_handle_rcv_queue(shdlc);
|
||||
nfc_shdlc_handle_send_queue(shdlc);
|
||||
llc_shdlc_handle_rcv_queue(shdlc);
|
||||
llc_shdlc_handle_send_queue(shdlc);
|
||||
|
||||
if (shdlc->t1_active && timer_pending(&shdlc->t1_timer) == 0) {
|
||||
pr_debug
|
||||
("Handle T1(send ack) elapsed (T1 now inactive)\n");
|
||||
|
||||
shdlc->t1_active = false;
|
||||
r = nfc_shdlc_send_s_frame(shdlc, S_FRAME_RR,
|
||||
r = llc_shdlc_send_s_frame(shdlc, S_FRAME_RR,
|
||||
shdlc->nr);
|
||||
if (r < 0)
|
||||
shdlc->hard_fault = r;
|
||||
|
@ -629,12 +678,12 @@ static void nfc_shdlc_sm_work(struct work_struct *work)
|
|||
|
||||
shdlc->t2_active = false;
|
||||
|
||||
nfc_shdlc_requeue_ack_pending(shdlc);
|
||||
nfc_shdlc_handle_send_queue(shdlc);
|
||||
llc_shdlc_requeue_ack_pending(shdlc);
|
||||
llc_shdlc_handle_send_queue(shdlc);
|
||||
}
|
||||
|
||||
if (shdlc->hard_fault) {
|
||||
nfc_hci_driver_failure(shdlc->hdev, shdlc->hard_fault);
|
||||
shdlc->llc_failure(shdlc->hdev, shdlc->hard_fault);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -647,7 +696,7 @@ static void nfc_shdlc_sm_work(struct work_struct *work)
|
|||
* Called from syscall context to establish shdlc link. Sleeps until
|
||||
* link is ready or failure.
|
||||
*/
|
||||
static int nfc_shdlc_connect(struct nfc_shdlc *shdlc)
|
||||
static int llc_shdlc_connect(struct llc_shdlc *shdlc)
|
||||
{
|
||||
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(connect_wq);
|
||||
|
||||
|
@ -662,14 +711,14 @@ static int nfc_shdlc_connect(struct nfc_shdlc *shdlc)
|
|||
|
||||
mutex_unlock(&shdlc->state_mutex);
|
||||
|
||||
queue_work(shdlc->sm_wq, &shdlc->sm_work);
|
||||
queue_work(system_nrt_wq, &shdlc->sm_work);
|
||||
|
||||
wait_event(connect_wq, shdlc->connect_result != 1);
|
||||
|
||||
return shdlc->connect_result;
|
||||
}
|
||||
|
||||
static void nfc_shdlc_disconnect(struct nfc_shdlc *shdlc)
|
||||
static void llc_shdlc_disconnect(struct llc_shdlc *shdlc)
|
||||
{
|
||||
pr_debug("\n");
|
||||
|
||||
|
@ -679,7 +728,7 @@ static void nfc_shdlc_disconnect(struct nfc_shdlc *shdlc)
|
|||
|
||||
mutex_unlock(&shdlc->state_mutex);
|
||||
|
||||
queue_work(shdlc->sm_wq, &shdlc->sm_work);
|
||||
queue_work(system_nrt_wq, &shdlc->sm_work);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -687,7 +736,7 @@ static void nfc_shdlc_disconnect(struct nfc_shdlc *shdlc)
|
|||
* skb contains only LLC header and payload.
|
||||
* If skb == NULL, it is a notification that the link below is dead.
|
||||
*/
|
||||
void nfc_shdlc_recv_frame(struct nfc_shdlc *shdlc, struct sk_buff *skb)
|
||||
static void llc_shdlc_recv_frame(struct llc_shdlc *shdlc, struct sk_buff *skb)
|
||||
{
|
||||
if (skb == NULL) {
|
||||
pr_err("NULL Frame -> link is dead\n");
|
||||
|
@ -697,176 +746,37 @@ void nfc_shdlc_recv_frame(struct nfc_shdlc *shdlc, struct sk_buff *skb)
|
|||
skb_queue_tail(&shdlc->rcv_q, skb);
|
||||
}
|
||||
|
||||
queue_work(shdlc->sm_wq, &shdlc->sm_work);
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_shdlc_recv_frame);
|
||||
|
||||
static int nfc_shdlc_open(struct nfc_hci_dev *hdev)
|
||||
{
|
||||
struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
|
||||
int r;
|
||||
|
||||
pr_debug("\n");
|
||||
|
||||
if (shdlc->ops->open) {
|
||||
r = shdlc->ops->open(shdlc);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = nfc_shdlc_connect(shdlc);
|
||||
if (r < 0 && shdlc->ops->close)
|
||||
shdlc->ops->close(shdlc);
|
||||
|
||||
return r;
|
||||
queue_work(system_nrt_wq, &shdlc->sm_work);
|
||||
}
|
||||
|
||||
static void nfc_shdlc_close(struct nfc_hci_dev *hdev)
|
||||
static void *llc_shdlc_init(struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv,
|
||||
rcv_to_hci_t rcv_to_hci, int tx_headroom,
|
||||
int tx_tailroom, int *rx_headroom, int *rx_tailroom,
|
||||
llc_failure_t llc_failure)
|
||||
{
|
||||
struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
|
||||
struct llc_shdlc *shdlc;
|
||||
|
||||
pr_debug("\n");
|
||||
*rx_headroom = SHDLC_LLC_HEAD_ROOM;
|
||||
*rx_tailroom = 0;
|
||||
|
||||
nfc_shdlc_disconnect(shdlc);
|
||||
|
||||
if (shdlc->ops->close)
|
||||
shdlc->ops->close(shdlc);
|
||||
}
|
||||
|
||||
static int nfc_shdlc_hci_ready(struct nfc_hci_dev *hdev)
|
||||
{
|
||||
struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
|
||||
int r = 0;
|
||||
|
||||
pr_debug("\n");
|
||||
|
||||
if (shdlc->ops->hci_ready)
|
||||
r = shdlc->ops->hci_ready(shdlc);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int nfc_shdlc_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
|
||||
|
||||
SHDLC_DUMP_SKB("queuing HCP packet to shdlc", skb);
|
||||
|
||||
skb_queue_tail(&shdlc->send_q, skb);
|
||||
|
||||
queue_work(shdlc->sm_wq, &shdlc->sm_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfc_shdlc_start_poll(struct nfc_hci_dev *hdev,
|
||||
u32 im_protocols, u32 tm_protocols)
|
||||
{
|
||||
struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
|
||||
|
||||
pr_debug("\n");
|
||||
|
||||
if (shdlc->ops->start_poll)
|
||||
return shdlc->ops->start_poll(shdlc,
|
||||
im_protocols, tm_protocols);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfc_shdlc_target_from_gate(struct nfc_hci_dev *hdev, u8 gate,
|
||||
struct nfc_target *target)
|
||||
{
|
||||
struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
|
||||
|
||||
if (shdlc->ops->target_from_gate)
|
||||
return shdlc->ops->target_from_gate(shdlc, gate, target);
|
||||
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
static int nfc_shdlc_complete_target_discovered(struct nfc_hci_dev *hdev,
|
||||
u8 gate,
|
||||
struct nfc_target *target)
|
||||
{
|
||||
struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
|
||||
|
||||
pr_debug("\n");
|
||||
|
||||
if (shdlc->ops->complete_target_discovered)
|
||||
return shdlc->ops->complete_target_discovered(shdlc, gate,
|
||||
target);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfc_shdlc_data_exchange(struct nfc_hci_dev *hdev,
|
||||
struct nfc_target *target,
|
||||
struct sk_buff *skb,
|
||||
struct sk_buff **res_skb)
|
||||
{
|
||||
struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
|
||||
|
||||
if (shdlc->ops->data_exchange)
|
||||
return shdlc->ops->data_exchange(shdlc, target, skb, res_skb);
|
||||
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
static int nfc_shdlc_check_presence(struct nfc_hci_dev *hdev,
|
||||
struct nfc_target *target)
|
||||
{
|
||||
struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
|
||||
|
||||
if (shdlc->ops->check_presence)
|
||||
return shdlc->ops->check_presence(shdlc, target);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct nfc_hci_ops shdlc_ops = {
|
||||
.open = nfc_shdlc_open,
|
||||
.close = nfc_shdlc_close,
|
||||
.hci_ready = nfc_shdlc_hci_ready,
|
||||
.xmit = nfc_shdlc_xmit,
|
||||
.start_poll = nfc_shdlc_start_poll,
|
||||
.target_from_gate = nfc_shdlc_target_from_gate,
|
||||
.complete_target_discovered = nfc_shdlc_complete_target_discovered,
|
||||
.data_exchange = nfc_shdlc_data_exchange,
|
||||
.check_presence = nfc_shdlc_check_presence,
|
||||
};
|
||||
|
||||
struct nfc_shdlc *nfc_shdlc_allocate(struct nfc_shdlc_ops *ops,
|
||||
struct nfc_hci_init_data *init_data,
|
||||
u32 protocols,
|
||||
int tx_headroom, int tx_tailroom,
|
||||
int max_link_payload, const char *devname)
|
||||
{
|
||||
struct nfc_shdlc *shdlc;
|
||||
int r;
|
||||
char name[32];
|
||||
|
||||
if (ops->xmit == NULL)
|
||||
return NULL;
|
||||
|
||||
shdlc = kzalloc(sizeof(struct nfc_shdlc), GFP_KERNEL);
|
||||
shdlc = kzalloc(sizeof(struct llc_shdlc), GFP_KERNEL);
|
||||
if (shdlc == NULL)
|
||||
return NULL;
|
||||
|
||||
mutex_init(&shdlc->state_mutex);
|
||||
shdlc->ops = ops;
|
||||
shdlc->state = SHDLC_DISCONNECTED;
|
||||
|
||||
init_timer(&shdlc->connect_timer);
|
||||
shdlc->connect_timer.data = (unsigned long)shdlc;
|
||||
shdlc->connect_timer.function = nfc_shdlc_connect_timeout;
|
||||
shdlc->connect_timer.function = llc_shdlc_connect_timeout;
|
||||
|
||||
init_timer(&shdlc->t1_timer);
|
||||
shdlc->t1_timer.data = (unsigned long)shdlc;
|
||||
shdlc->t1_timer.function = nfc_shdlc_t1_timeout;
|
||||
shdlc->t1_timer.function = llc_shdlc_t1_timeout;
|
||||
|
||||
init_timer(&shdlc->t2_timer);
|
||||
shdlc->t2_timer.data = (unsigned long)shdlc;
|
||||
shdlc->t2_timer.function = nfc_shdlc_t2_timeout;
|
||||
shdlc->t2_timer.function = llc_shdlc_t2_timeout;
|
||||
|
||||
shdlc->w = SHDLC_MAX_WINDOW;
|
||||
shdlc->srej_support = SHDLC_SREJ_SUPPORT;
|
||||
|
@ -875,52 +785,21 @@ struct nfc_shdlc *nfc_shdlc_allocate(struct nfc_shdlc_ops *ops,
|
|||
skb_queue_head_init(&shdlc->send_q);
|
||||
skb_queue_head_init(&shdlc->ack_pending_q);
|
||||
|
||||
INIT_WORK(&shdlc->sm_work, nfc_shdlc_sm_work);
|
||||
snprintf(name, sizeof(name), "%s_shdlc_sm_wq", devname);
|
||||
shdlc->sm_wq = alloc_workqueue(name, WQ_NON_REENTRANT | WQ_UNBOUND |
|
||||
WQ_MEM_RECLAIM, 1);
|
||||
if (shdlc->sm_wq == NULL)
|
||||
goto err_allocwq;
|
||||
INIT_WORK(&shdlc->sm_work, llc_shdlc_sm_work);
|
||||
|
||||
shdlc->client_headroom = tx_headroom;
|
||||
shdlc->client_tailroom = tx_tailroom;
|
||||
|
||||
shdlc->hdev = nfc_hci_allocate_device(&shdlc_ops, init_data, protocols,
|
||||
tx_headroom + SHDLC_LLC_HEAD_ROOM,
|
||||
tx_tailroom + SHDLC_LLC_TAIL_ROOM,
|
||||
max_link_payload);
|
||||
if (shdlc->hdev == NULL)
|
||||
goto err_allocdev;
|
||||
|
||||
nfc_hci_set_clientdata(shdlc->hdev, shdlc);
|
||||
|
||||
r = nfc_hci_register_device(shdlc->hdev);
|
||||
if (r < 0)
|
||||
goto err_regdev;
|
||||
shdlc->hdev = hdev;
|
||||
shdlc->xmit_to_drv = xmit_to_drv;
|
||||
shdlc->rcv_to_hci = rcv_to_hci;
|
||||
shdlc->tx_headroom = tx_headroom;
|
||||
shdlc->tx_tailroom = tx_tailroom;
|
||||
shdlc->llc_failure = llc_failure;
|
||||
|
||||
return shdlc;
|
||||
|
||||
err_regdev:
|
||||
nfc_hci_free_device(shdlc->hdev);
|
||||
|
||||
err_allocdev:
|
||||
destroy_workqueue(shdlc->sm_wq);
|
||||
|
||||
err_allocwq:
|
||||
kfree(shdlc);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_shdlc_allocate);
|
||||
|
||||
void nfc_shdlc_free(struct nfc_shdlc *shdlc)
|
||||
static void llc_shdlc_deinit(struct nfc_llc *llc)
|
||||
{
|
||||
pr_debug("\n");
|
||||
|
||||
nfc_hci_unregister_device(shdlc->hdev);
|
||||
nfc_hci_free_device(shdlc->hdev);
|
||||
|
||||
destroy_workqueue(shdlc->sm_wq);
|
||||
struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
|
||||
|
||||
skb_queue_purge(&shdlc->rcv_q);
|
||||
skb_queue_purge(&shdlc->send_q);
|
||||
|
@ -928,24 +807,51 @@ void nfc_shdlc_free(struct nfc_shdlc *shdlc)
|
|||
|
||||
kfree(shdlc);
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_shdlc_free);
|
||||
|
||||
void nfc_shdlc_set_clientdata(struct nfc_shdlc *shdlc, void *clientdata)
|
||||
static int llc_shdlc_start(struct nfc_llc *llc)
|
||||
{
|
||||
pr_debug("\n");
|
||||
struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
|
||||
|
||||
shdlc->clientdata = clientdata;
|
||||
return llc_shdlc_connect(shdlc);
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_shdlc_set_clientdata);
|
||||
|
||||
void *nfc_shdlc_get_clientdata(struct nfc_shdlc *shdlc)
|
||||
static int llc_shdlc_stop(struct nfc_llc *llc)
|
||||
{
|
||||
return shdlc->clientdata;
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_shdlc_get_clientdata);
|
||||
struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
|
||||
|
||||
struct nfc_hci_dev *nfc_shdlc_get_hci_dev(struct nfc_shdlc *shdlc)
|
||||
{
|
||||
return shdlc->hdev;
|
||||
llc_shdlc_disconnect(shdlc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void llc_shdlc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb)
|
||||
{
|
||||
struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
|
||||
|
||||
llc_shdlc_recv_frame(shdlc, skb);
|
||||
}
|
||||
|
||||
static int llc_shdlc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb)
|
||||
{
|
||||
struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
|
||||
|
||||
skb_queue_tail(&shdlc->send_q, skb);
|
||||
|
||||
queue_work(system_nrt_wq, &shdlc->sm_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct nfc_llc_ops llc_shdlc_ops = {
|
||||
.init = llc_shdlc_init,
|
||||
.deinit = llc_shdlc_deinit,
|
||||
.start = llc_shdlc_start,
|
||||
.stop = llc_shdlc_stop,
|
||||
.rcv_from_drv = llc_shdlc_rcv_from_drv,
|
||||
.xmit_from_hci = llc_shdlc_xmit_from_hci,
|
||||
};
|
||||
|
||||
int nfc_llc_shdlc_register(void)
|
||||
{
|
||||
return nfc_llc_register(LLC_SHDLC_NAME, &llc_shdlc_ops);
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_shdlc_get_hci_dev);
|
|
@ -114,9 +114,9 @@ static void local_release(struct kref *ref)
|
|||
nfc_llcp_socket_release(local, false);
|
||||
del_timer_sync(&local->link_timer);
|
||||
skb_queue_purge(&local->tx_queue);
|
||||
destroy_workqueue(local->tx_wq);
|
||||
destroy_workqueue(local->rx_wq);
|
||||
destroy_workqueue(local->timeout_wq);
|
||||
cancel_work_sync(&local->tx_work);
|
||||
cancel_work_sync(&local->rx_work);
|
||||
cancel_work_sync(&local->timeout_work);
|
||||
kfree_skb(local->rx_pending);
|
||||
kfree(local);
|
||||
}
|
||||
|
@ -181,7 +181,7 @@ static void nfc_llcp_symm_timer(unsigned long data)
|
|||
|
||||
pr_err("SYMM timeout\n");
|
||||
|
||||
queue_work(local->timeout_wq, &local->timeout_work);
|
||||
queue_work(system_nrt_wq, &local->timeout_work);
|
||||
}
|
||||
|
||||
struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev)
|
||||
|
@ -426,6 +426,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
|
|||
u8 *miux_tlv, miux_length;
|
||||
__be16 miux;
|
||||
u8 gb_len = 0;
|
||||
int ret = 0;
|
||||
|
||||
version = LLCP_VERSION_11;
|
||||
version_tlv = nfc_llcp_build_tlv(LLCP_TLV_VERSION, &version,
|
||||
|
@ -450,8 +451,8 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
|
|||
gb_len += ARRAY_SIZE(llcp_magic);
|
||||
|
||||
if (gb_len > NFC_MAX_GT_LEN) {
|
||||
kfree(version_tlv);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
gb_cur = local->gb;
|
||||
|
@ -471,12 +472,15 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
|
|||
memcpy(gb_cur, miux_tlv, miux_length);
|
||||
gb_cur += miux_length;
|
||||
|
||||
kfree(version_tlv);
|
||||
kfree(lto_tlv);
|
||||
|
||||
local->gb_len = gb_len;
|
||||
|
||||
return 0;
|
||||
out:
|
||||
kfree(version_tlv);
|
||||
kfree(lto_tlv);
|
||||
kfree(wks_tlv);
|
||||
kfree(miux_tlv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len)
|
||||
|
@ -1052,7 +1056,7 @@ static void nfc_llcp_rx_work(struct work_struct *work)
|
|||
|
||||
}
|
||||
|
||||
queue_work(local->tx_wq, &local->tx_work);
|
||||
queue_work(system_nrt_wq, &local->tx_work);
|
||||
kfree_skb(local->rx_pending);
|
||||
local->rx_pending = NULL;
|
||||
|
||||
|
@ -1071,7 +1075,7 @@ void nfc_llcp_recv(void *data, struct sk_buff *skb, int err)
|
|||
|
||||
local->rx_pending = skb_get(skb);
|
||||
del_timer(&local->link_timer);
|
||||
queue_work(local->rx_wq, &local->rx_work);
|
||||
queue_work(system_nrt_wq, &local->rx_work);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -1086,7 +1090,7 @@ int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb)
|
|||
|
||||
local->rx_pending = skb_get(skb);
|
||||
del_timer(&local->link_timer);
|
||||
queue_work(local->rx_wq, &local->rx_work);
|
||||
queue_work(system_nrt_wq, &local->rx_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1121,7 +1125,7 @@ void nfc_llcp_mac_is_up(struct nfc_dev *dev, u32 target_idx,
|
|||
if (rf_mode == NFC_RF_INITIATOR) {
|
||||
pr_debug("Queueing Tx work\n");
|
||||
|
||||
queue_work(local->tx_wq, &local->tx_work);
|
||||
queue_work(system_nrt_wq, &local->tx_work);
|
||||
} else {
|
||||
mod_timer(&local->link_timer,
|
||||
jiffies + msecs_to_jiffies(local->remote_lto));
|
||||
|
@ -1130,10 +1134,7 @@ void nfc_llcp_mac_is_up(struct nfc_dev *dev, u32 target_idx,
|
|||
|
||||
int nfc_llcp_register_device(struct nfc_dev *ndev)
|
||||
{
|
||||
struct device *dev = &ndev->dev;
|
||||
struct nfc_llcp_local *local;
|
||||
char name[32];
|
||||
int err;
|
||||
|
||||
local = kzalloc(sizeof(struct nfc_llcp_local), GFP_KERNEL);
|
||||
if (local == NULL)
|
||||
|
@ -1149,38 +1150,11 @@ int nfc_llcp_register_device(struct nfc_dev *ndev)
|
|||
|
||||
skb_queue_head_init(&local->tx_queue);
|
||||
INIT_WORK(&local->tx_work, nfc_llcp_tx_work);
|
||||
snprintf(name, sizeof(name), "%s_llcp_tx_wq", dev_name(dev));
|
||||
local->tx_wq =
|
||||
alloc_workqueue(name,
|
||||
WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM,
|
||||
1);
|
||||
if (local->tx_wq == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto err_local;
|
||||
}
|
||||
|
||||
local->rx_pending = NULL;
|
||||
INIT_WORK(&local->rx_work, nfc_llcp_rx_work);
|
||||
snprintf(name, sizeof(name), "%s_llcp_rx_wq", dev_name(dev));
|
||||
local->rx_wq =
|
||||
alloc_workqueue(name,
|
||||
WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM,
|
||||
1);
|
||||
if (local->rx_wq == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto err_tx_wq;
|
||||
}
|
||||
|
||||
INIT_WORK(&local->timeout_work, nfc_llcp_timeout_work);
|
||||
snprintf(name, sizeof(name), "%s_llcp_timeout_wq", dev_name(dev));
|
||||
local->timeout_wq =
|
||||
alloc_workqueue(name,
|
||||
WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM,
|
||||
1);
|
||||
if (local->timeout_wq == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto err_rx_wq;
|
||||
}
|
||||
|
||||
local->sockets.lock = __RW_LOCK_UNLOCKED(local->sockets.lock);
|
||||
local->connecting_sockets.lock = __RW_LOCK_UNLOCKED(local->connecting_sockets.lock);
|
||||
|
@ -1192,17 +1166,6 @@ int nfc_llcp_register_device(struct nfc_dev *ndev)
|
|||
|
||||
list_add(&llcp_devices, &local->list);
|
||||
|
||||
return 0;
|
||||
|
||||
err_rx_wq:
|
||||
destroy_workqueue(local->rx_wq);
|
||||
|
||||
err_tx_wq:
|
||||
destroy_workqueue(local->tx_wq);
|
||||
|
||||
err_local:
|
||||
kfree(local);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -56,12 +56,9 @@ struct nfc_llcp_local {
|
|||
|
||||
struct timer_list link_timer;
|
||||
struct sk_buff_head tx_queue;
|
||||
struct workqueue_struct *tx_wq;
|
||||
struct work_struct tx_work;
|
||||
struct workqueue_struct *rx_wq;
|
||||
struct work_struct rx_work;
|
||||
struct sk_buff *rx_pending;
|
||||
struct workqueue_struct *timeout_wq;
|
||||
struct work_struct timeout_work;
|
||||
|
||||
u32 target_idx;
|
||||
|
|
|
@ -300,9 +300,6 @@ static int llcp_sock_getname(struct socket *sock, struct sockaddr *uaddr,
|
|||
pr_debug("%p %d %d %d\n", sk, llcp_sock->target_idx,
|
||||
llcp_sock->dsap, llcp_sock->ssap);
|
||||
|
||||
if (llcp_sock == NULL || llcp_sock->dev == NULL)
|
||||
return -EBADFD;
|
||||
|
||||
uaddr->sa_family = AF_NFC;
|
||||
|
||||
*len = sizeof(struct sockaddr_nfc_llcp);
|
||||
|
|
|
@ -176,6 +176,27 @@ static void nci_init_complete_req(struct nci_dev *ndev, unsigned long opt)
|
|||
(1 + ((*num) * sizeof(struct disc_map_config))), &cmd);
|
||||
}
|
||||
|
||||
struct nci_set_config_param {
|
||||
__u8 id;
|
||||
size_t len;
|
||||
__u8 *val;
|
||||
};
|
||||
|
||||
static void nci_set_config_req(struct nci_dev *ndev, unsigned long opt)
|
||||
{
|
||||
struct nci_set_config_param *param = (struct nci_set_config_param *)opt;
|
||||
struct nci_core_set_config_cmd cmd;
|
||||
|
||||
BUG_ON(param->len > NCI_MAX_PARAM_LEN);
|
||||
|
||||
cmd.num_params = 1;
|
||||
cmd.param.id = param->id;
|
||||
cmd.param.len = param->len;
|
||||
memcpy(cmd.param.val, param->val, param->len);
|
||||
|
||||
nci_send_cmd(ndev, NCI_OP_CORE_SET_CONFIG_CMD, (3 + param->len), &cmd);
|
||||
}
|
||||
|
||||
static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt)
|
||||
{
|
||||
struct nci_rf_disc_cmd cmd;
|
||||
|
@ -388,6 +409,32 @@ static int nci_dev_down(struct nfc_dev *nfc_dev)
|
|||
return nci_close_device(ndev);
|
||||
}
|
||||
|
||||
static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev)
|
||||
{
|
||||
struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
|
||||
struct nci_set_config_param param;
|
||||
__u8 local_gb[NFC_MAX_GT_LEN];
|
||||
int i, rc = 0;
|
||||
|
||||
param.val = nfc_get_local_general_bytes(nfc_dev, ¶m.len);
|
||||
if ((param.val == NULL) || (param.len == 0))
|
||||
return rc;
|
||||
|
||||
if (param.len > NCI_MAX_PARAM_LEN)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < param.len; i++)
|
||||
local_gb[param.len-1-i] = param.val[i];
|
||||
|
||||
param.id = NCI_PN_ATR_REQ_GEN_BYTES;
|
||||
param.val = local_gb;
|
||||
|
||||
rc = nci_request(ndev, nci_set_config_req, (unsigned long)¶m,
|
||||
msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int nci_start_poll(struct nfc_dev *nfc_dev,
|
||||
__u32 im_protocols, __u32 tm_protocols)
|
||||
{
|
||||
|
@ -415,6 +462,14 @@ static int nci_start_poll(struct nfc_dev *nfc_dev,
|
|||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (im_protocols & NFC_PROTO_NFC_DEP_MASK) {
|
||||
rc = nci_set_local_general_bytes(nfc_dev);
|
||||
if (rc) {
|
||||
pr_err("failed to set local general bytes\n");
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
rc = nci_request(ndev, nci_rf_discover_req, im_protocols,
|
||||
msecs_to_jiffies(NCI_RF_DISC_TIMEOUT));
|
||||
|
||||
|
@ -509,7 +564,7 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev,
|
|||
{
|
||||
struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
|
||||
|
||||
pr_debug("target_idx %d\n", target->idx);
|
||||
pr_debug("entry\n");
|
||||
|
||||
if (!ndev->target_active_prot) {
|
||||
pr_err("unable to deactivate target, no active target\n");
|
||||
|
@ -524,6 +579,38 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev,
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
static int nci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
|
||||
__u8 comm_mode, __u8 *gb, size_t gb_len)
|
||||
{
|
||||
struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
|
||||
int rc;
|
||||
|
||||
pr_debug("target_idx %d, comm_mode %d\n", target->idx, comm_mode);
|
||||
|
||||
rc = nci_activate_target(nfc_dev, target, NFC_PROTO_NFC_DEP);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = nfc_set_remote_general_bytes(nfc_dev, ndev->remote_gb,
|
||||
ndev->remote_gb_len);
|
||||
if (!rc)
|
||||
rc = nfc_dep_link_is_up(nfc_dev, target->idx, NFC_COMM_PASSIVE,
|
||||
NFC_RF_INITIATOR);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int nci_dep_link_down(struct nfc_dev *nfc_dev)
|
||||
{
|
||||
pr_debug("entry\n");
|
||||
|
||||
nci_deactivate_target(nfc_dev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
|
||||
struct sk_buff *skb,
|
||||
data_exchange_cb_t cb, void *cb_context)
|
||||
|
@ -557,6 +644,8 @@ static struct nfc_ops nci_nfc_ops = {
|
|||
.dev_down = nci_dev_down,
|
||||
.start_poll = nci_start_poll,
|
||||
.stop_poll = nci_stop_poll,
|
||||
.dep_link_up = nci_dep_link_up,
|
||||
.dep_link_down = nci_dep_link_down,
|
||||
.activate_target = nci_activate_target,
|
||||
.deactivate_target = nci_deactivate_target,
|
||||
.im_transceive = nci_transceive,
|
||||
|
|
|
@ -176,6 +176,8 @@ static int nci_add_new_protocol(struct nci_dev *ndev,
|
|||
protocol = NFC_PROTO_ISO14443_B_MASK;
|
||||
else if (rf_protocol == NCI_RF_PROTOCOL_T3T)
|
||||
protocol = NFC_PROTO_FELICA_MASK;
|
||||
else if (rf_protocol == NCI_RF_PROTOCOL_NFC_DEP)
|
||||
protocol = NFC_PROTO_NFC_DEP_MASK;
|
||||
else
|
||||
protocol = 0;
|
||||
|
||||
|
@ -361,6 +363,33 @@ static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev,
|
|||
return NCI_STATUS_OK;
|
||||
}
|
||||
|
||||
static int nci_extract_activation_params_nfc_dep(struct nci_dev *ndev,
|
||||
struct nci_rf_intf_activated_ntf *ntf, __u8 *data)
|
||||
{
|
||||
struct activation_params_poll_nfc_dep *poll;
|
||||
int i;
|
||||
|
||||
switch (ntf->activation_rf_tech_and_mode) {
|
||||
case NCI_NFC_A_PASSIVE_POLL_MODE:
|
||||
case NCI_NFC_F_PASSIVE_POLL_MODE:
|
||||
poll = &ntf->activation_params.poll_nfc_dep;
|
||||
poll->atr_res_len = min_t(__u8, *data++, 63);
|
||||
pr_debug("atr_res_len %d\n", poll->atr_res_len);
|
||||
if (poll->atr_res_len > 0) {
|
||||
for (i = 0; i < poll->atr_res_len; i++)
|
||||
poll->atr_res[poll->atr_res_len-1-i] = data[i];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_err("unsupported activation_rf_tech_and_mode 0x%x\n",
|
||||
ntf->activation_rf_tech_and_mode);
|
||||
return NCI_STATUS_RF_PROTOCOL_ERROR;
|
||||
}
|
||||
|
||||
return NCI_STATUS_OK;
|
||||
}
|
||||
|
||||
static void nci_target_auto_activated(struct nci_dev *ndev,
|
||||
struct nci_rf_intf_activated_ntf *ntf)
|
||||
{
|
||||
|
@ -454,6 +483,11 @@ static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev,
|
|||
&ntf, data);
|
||||
break;
|
||||
|
||||
case NCI_RF_INTERFACE_NFC_DEP:
|
||||
err = nci_extract_activation_params_nfc_dep(ndev,
|
||||
&ntf, data);
|
||||
break;
|
||||
|
||||
case NCI_RF_INTERFACE_FRAME:
|
||||
/* no activation params */
|
||||
break;
|
||||
|
@ -473,6 +507,24 @@ exit:
|
|||
|
||||
/* set the available credits to initial value */
|
||||
atomic_set(&ndev->credits_cnt, ndev->initial_num_credits);
|
||||
|
||||
/* store general bytes to be reported later in dep_link_up */
|
||||
if (ntf.rf_interface == NCI_RF_INTERFACE_NFC_DEP) {
|
||||
ndev->remote_gb_len = 0;
|
||||
|
||||
if (ntf.activation_params_len > 0) {
|
||||
/* ATR_RES general bytes at offset 15 */
|
||||
ndev->remote_gb_len = min_t(__u8,
|
||||
(ntf.activation_params
|
||||
.poll_nfc_dep.atr_res_len
|
||||
- NFC_ATR_RES_GT_OFFSET),
|
||||
NFC_MAX_GT_LEN);
|
||||
memcpy(ndev->remote_gb,
|
||||
(ntf.activation_params.poll_nfc_dep
|
||||
.atr_res + NFC_ATR_RES_GT_OFFSET),
|
||||
ndev->remote_gb_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (atomic_read(&ndev->state) == NCI_DISCOVERY) {
|
||||
|
|
|
@ -119,6 +119,16 @@ exit:
|
|||
nci_req_complete(ndev, rsp_1->status);
|
||||
}
|
||||
|
||||
static void nci_core_set_config_rsp_packet(struct nci_dev *ndev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct nci_core_set_config_rsp *rsp = (void *) skb->data;
|
||||
|
||||
pr_debug("status 0x%x\n", rsp->status);
|
||||
|
||||
nci_req_complete(ndev, rsp->status);
|
||||
}
|
||||
|
||||
static void nci_rf_disc_map_rsp_packet(struct nci_dev *ndev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
|
@ -194,6 +204,10 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
|
|||
nci_core_init_rsp_packet(ndev, skb);
|
||||
break;
|
||||
|
||||
case NCI_OP_CORE_SET_CONFIG_RSP:
|
||||
nci_core_set_config_rsp_packet(ndev, skb);
|
||||
break;
|
||||
|
||||
case NCI_OP_RF_DISCOVER_MAP_RSP:
|
||||
nci_rf_disc_map_rsp_packet(ndev, skb);
|
||||
break;
|
||||
|
|
Loading…
Reference in New Issue