Commit 0c49b699 authored by John W. Linville's avatar John W. Linville

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: default avatarJohn W. Linville <linville@tuxdriver.com>
parents 90e6274d 4c0ba9ac
......@@ -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 @@ static int __pn533_send_cmd_frame_async(struct pn533 *dev,
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);
return 0;
error:
mutex_unlock(&dev->cmd_lock);
return rc;
return 0;
}
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 @@ static void pn533_wq_mi_recv(struct work_struct *work)
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 @@ static int pn533_probe(struct usb_interface *interface,
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);
......
This diff is collapsed.
......@@ -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 @@ static int pn544_hci_i2c_read(struct i2c_client *client, struct sk_buff **skb)
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 @@ static int pn544_hci_open(struct nfc_shdlc *shdlc)
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 @@ static void pn544_hci_close(struct nfc_shdlc *shdlc)
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,
......
/*
* 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;
......
/*
* 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)
{
......
This diff is collapsed.
......@@ -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;
......
/*
* 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;
}
/*
* 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_ */
/*
* 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);
}
......@@ -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;
local->gb_len = gb_len;
out:
kfree(version_tlv);
kfree(lto_tlv);
kfree(wks_tlv);
kfree(miux_tlv);
local->gb_len = gb_len;
return 0;
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, &param.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)&param,
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 @@ static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev,
/* 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 @@ static void nci_core_init_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment