2011-06-02 21:47:01

by Lauro Ramos Venancio

[permalink] [raw]
Subject: [PATCH 0/6] NFC subsystem

This series of patches implements the first part of the Near Field
Communication (NFC) subsystem.

A NFC subsystem is required to standardize the NFC device drivers
development and to create an unified userspace interface.

This implementation focus in one of the three NFC use cases: the tag
read/write. However the architecture was designed with the card
emulation and peer-to-peer use cases in mind.

The subsystem is divided in some parts. The 'core' is responsible for
providing the device driver interface. On the other side, it is also
responsible for providing an interface to the control operations and
low-level data exchange.

The control operations are available to userspace via generic netlink.
The most important operations are NFC_CMD_START_POLL and
NFC_CMD_STOP_POLL that control the polling for targets. The event
NFC_EVENT_TARGETS_FOUND is emitted to return polling results.

The low-level data exchange interface to userspace is provided by a
new socket family: PF_NFC. The NFC_SOCKPROTO_RAW uses SOCK_SEQPACKET
to perform raw communication with NFC targets. The address family
(AF_NFC) has the local device id, the NFC target id and the NFC
protocol as fields.

Further, the low-level data exchange interface provided by the core
can also be used to implement the LLCP protocol.

To test the implementation, we developed the NXP pn533 driver and an
user application example. The driver implementation is included in
this patch series. The user application example can be found at
http://code.openbossa.org.

Aloisio Almeida Jr (3):
NFC: add NFC socket family
NFC: pn533: add NXP pn533 nfc device driver
NFC: add Documentation/networking/nfc.txt

Lauro Ramos Venancio (3):
NFC: add nfc subsystem core
NFC: add nfc generic netlink interface
NFC: add the NFC socket raw protocol

Documentation/networking/nfc.txt | 127 +++
drivers/Kconfig | 2 -
drivers/Makefile | 1 +
drivers/nfc/Kconfig | 24 +-
drivers/nfc/Makefile | 3 +
drivers/nfc/pn533.c | 1629 ++++++++++++++++++++++++++++++++++++++
include/linux/nfc.h | 136 ++++
include/linux/socket.h | 4 +-
include/net/nfc.h | 149 ++++
net/Kconfig | 1 +
net/Makefile | 1 +
net/core/sock.c | 6 +-
net/nfc/Kconfig | 24 +
net/nfc/Makefile | 9 +
net/nfc/af_nfc.c | 98 +++
net/nfc/core.c | 403 ++++++++++
net/nfc/netlink.c | 446 +++++++++++
net/nfc/nfc.h | 110 +++
net/nfc/rawsock.c | 351 ++++++++
19 files changed, 3506 insertions(+), 18 deletions(-)
create mode 100644 Documentation/networking/nfc.txt
create mode 100644 drivers/nfc/pn533.c
create mode 100644 include/linux/nfc.h
create mode 100644 include/net/nfc.h
create mode 100644 net/nfc/Kconfig
create mode 100644 net/nfc/Makefile
create mode 100644 net/nfc/af_nfc.c
create mode 100644 net/nfc/core.c
create mode 100644 net/nfc/netlink.c
create mode 100644 net/nfc/nfc.h
create mode 100644 net/nfc/rawsock.c



2011-06-17 17:01:52

by John W. Linville

[permalink] [raw]
Subject: Re: [PATCH 0/6] NFC subsystem

On Thu, Jun 02, 2011 at 06:46:04PM -0300, Lauro Ramos Venancio wrote:
> This series of patches implements the first part of the Near Field
> Communication (NFC) subsystem.

Will you be submitting a new series to account for the comments from
Johannes and others?

John
--
John W. Linville Someday the world will need a hero, and you
[email protected] might be all we have. Be ready.

2011-06-03 21:39:12

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH 2/6] NFC: add nfc generic netlink interface

On Fri, 2011-06-03 at 18:35 -0300, Aloisio Almeida wrote:
> On Fri, Jun 3, 2011 at 5:38 PM, Johannes Berg <[email protected]> wrote:
> > On Fri, 2011-06-03 at 17:18 -0300, Lauro Ramos Venancio wrote:
> >> We don't need a generation counter here because we have the events
> >> NFC_EVENT_DEVICE_ADDED and NFC_EVENT_DEVICE_REMOVED. So, it is
> >> possible to keep the device list consistency listening for these
> >> events.
> >
> > That may be true, but it complicates coding in userspace. Much easier to
> > check a generation counter (in both cases) to see if data is consistent
> > than to listen to multiple events concurrently with receiving a dump.
>
> The userspace must listen to these events anyway. In addition, the
> application needs to get the list of devices only once (on its
> initialization).
>
> In the target list case, the userspace application would have to
> orderly call start_poll before receive the dump in order to create the
> "invalid list" situation.

I think you're considering only the current applications, and new ones
might be developed, but whatever, I'll let it rest since I don't
particularly care anyway.

johannes


2011-06-02 21:47:32

by Lauro Ramos Venancio

[permalink] [raw]
Subject: [PATCH 6/6] NFC: add Documentation/networking/nfc.txt

From: Aloisio Almeida Jr <[email protected]>

Signed-off-by: Aloisio Almeida Jr <[email protected]>
Signed-off-by: Lauro Ramos Venancio <[email protected]>
---
Documentation/networking/nfc.txt | 127 ++++++++++++++++++++++++++++++++++++++
1 files changed, 127 insertions(+), 0 deletions(-)
create mode 100644 Documentation/networking/nfc.txt

diff --git a/Documentation/networking/nfc.txt b/Documentation/networking/nfc.txt
new file mode 100644
index 0000000..e17ac19
--- /dev/null
+++ b/Documentation/networking/nfc.txt
@@ -0,0 +1,127 @@
+Linux NFC subsystem
+===================
+
+The Near Field Communication (NFC) subsystem is required to standardize the
+NFC device drivers development and to create an unified userspace interface.p
+
+This document covers the architecture overview, the device driver interface
+description and the userspace interface description.
+
+Architecture overview
+---------------------
+
+The NFC subsystem is responsible for:
+ - NFC adapters management;
+ - Polling for targets;
+ - Low-level data exchange;
+
+The subsystem is divided in some parts. The 'core' is responsible for
+providing the device driver interface. On the other side, it is also
+responsible for providing an interface to control operations and low-level
+data exchange.
+
+The control operations are available to userspace via generic netlink.
+
+The low-level data exchage interface is provided by the new socket family
+PF_NFC. The NFC_SOCKPROTO_RAW performs raw communication with NFC targets.
+
+
+ +--------------------------------------+
+ | USER SPACE |
+ +--------------------------------------+
+ ^ ^
+ | low-level | control
+ | data exchange | operations
+ | |
+ | v
+ | +-----------+
+ | AF_NFC | netlink |
+ | socket +-----------+
+ | raw ^
+ | |
+ v v
+ +---------+ +-----------+
+ | rawsock | <--------> | core |
+ +---------+ +-----------+
+ ^
+ |
+ v
+ +-----------+
+ | driver |
+ +-----------+
+
+Device Driver Interface
+-----------------------
+
+When registering on the NFC subsystem, the device driver must inform the set
+of supported NFC protocols and the set of ops callbacks. The ops callbacks
+that must be implemented are the following:
+
+* start_poll - setup the device to poll for targets
+* stop_poll - stop on progress polling operation
+* activate_target - select and initialize one of the targets found
+* deactivate_target - deselect and deinitialize the selected target
+* data_exchange - send data and receive the response (transceive operation)
+
+Userspace interface
+--------------------
+
+The userspace interface is divided in control operations and low-level data
+exchange operation.
+
+CONTROL OPERATIONS:
+
+Generic netlink was used to implement the interface to the control operations.
+The operations are composed by commands and events, all listed below:
+
+* NFC_CMD_GET_DEVICE - get an specific device info or dump the device list
+* NFC_CMD_START_POLL - setup a specific device to polling for targets
+* NFC_CMD_STOP_POLL - stop the polling operation in a specifc device
+
+* NFC_EVENT_DEVICE_ADDED - reports a NFC device addition
+* NFC_EVENT_DEVICE_REMOVED - reports a NFC device removal
+* NFC_EVENT_TARGETS_FOUND - reports START_POLL results when 1 or more targets
+are found
+
+The user must call START_POLL to poll for NFC targets, passing the desired NFC
+protocols through NFC_ATTR_PROTOCOLS attribute. The device remains in polling
+state until it finds any target. However, the user can stop the polling
+operation by calling STOP_POLL command. In this case, it will be checked if
+the requester of STOP_POLL is the same of START_POLL.
+
+If the polling operation finds one or more targets, the event TARGETS_FOUND is
+sent. The event attributes have relevant information about the target, such as
+the supported NFC protocols. The TARGETS_FOUND event has the list of all
+targets found by a specific device.
+
+All polling operations requested through one netlink socket are stopped when
+it's closed.
+
+LOW-LEVEL DATA EXCHANGE:
+
+The userspace must use PF_NFC sockets to perform any data communication with
+targets. All NFC sockets use AF_NFC:
+
+struct sockaddr_nfc {
+ sa_family_t sa_family;
+ __u32 dev_idx;
+ __u32 target_idx;
+ __u32 nfc_protocol;
+};
+
+To establish a connection with one target, the user must create a
+NFC_SOCKPROTO_RAW socket and call the 'connect' syscall with the sockaddr_nfc
+struct correctly filled. All information comes from NFC_EVENT_TARGETS_FOUND
+netlink event. As a target can support more than one NFC protocol, the user
+must inform which protocol it wants to use.
+
+Internally, 'connect' will result in a activate_target call to the driver.
+When the socket is closed, the target is deactivated.
+
+The data format exchanged through the sockets is NFC protocol dependent. For
+instance, when communicating with MIFARE tags, the data exchanged are MIFARE
+commands and their responses.
+
+The first received package is the response to the first sent package and so
+on. In order to allow valid "empty" responses, every data received has a NULL
+header of 1 byte.
--
1.7.1


2011-06-02 21:47:19

by Lauro Ramos Venancio

[permalink] [raw]
Subject: [PATCH 2/6] NFC: add nfc generic netlink interface

The NFC generic netlink interface exports the NFC control operations
to the user space.

Signed-off-by: Lauro Ramos Venancio <[email protected]>
Signed-off-by: Aloisio Almeida Jr <[email protected]>
Signed-off-by: Samuel Ortiz <[email protected]>
---
include/linux/nfc.h | 122 ++++++++++++++
include/net/nfc.h | 18 ++
net/nfc/Makefile | 2 +-
net/nfc/core.c | 63 +++++++-
net/nfc/netlink.c | 446 +++++++++++++++++++++++++++++++++++++++++++++++++++
net/nfc/nfc.h | 14 ++
6 files changed, 662 insertions(+), 3 deletions(-)
create mode 100644 include/linux/nfc.h
create mode 100644 net/nfc/netlink.c

diff --git a/include/linux/nfc.h b/include/linux/nfc.h
new file mode 100644
index 0000000..be8fc88
--- /dev/null
+++ b/include/linux/nfc.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ *
+ * Authors:
+ * Lauro Ramos Venancio <[email protected]>
+ * Aloisio Almeida Jr <[email protected]>
+ *
+ * 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 __LINUX_NFC_H
+#define __LINUX_NFC_H
+
+#define NFC_GENL_NAME "nfc"
+#define NFC_GENL_VERSION 1
+
+#define NFC_GENL_MCAST_EVENT_NAME "events"
+
+/**
+ * enum nfc_commands - supported nfc commands
+ *
+ * @NFC_CMD_UNSPEC: unspecified command
+ *
+ * @NFC_CMD_GET_DEVICE: request information about a device (requires
+ * %NFC_ATTR_DEVICE_INDEX) or dump request to get a list of all nfc devices
+ * @NFC_CMD_START_POLL: start polling for targets using the given protocols
+ * (requires %NFC_ATTR_DEVICE_INDEX and %NFC_ATTR_PROTOCOLS)
+ * @NFC_CMD_STOP_POLL: stop polling for targets (requires
+ * %NFC_ATTR_DEVICE_INDEX)
+ * @NFC_EVENT_TARGETS_FOUND: event emitted when a new target is found
+ * (it sends %NFC_ATTR_DEVICE_INDEX and %NFC_ATTR_TARGETS)
+ * @NFC_EVENT_DEVICE_ADDED: event emitted when a new device is registred
+ * @NFC_EVENT_DEVICE_REMOVED: event emitted when a device is removed
+ */
+enum nfc_commands {
+ NFC_CMD_UNSPEC,
+ NFC_CMD_GET_DEVICE,
+ NFC_CMD_START_POLL,
+ NFC_CMD_STOP_POLL,
+ NFC_EVENT_TARGETS_FOUND,
+ NFC_EVENT_DEVICE_ADDED,
+ NFC_EVENT_DEVICE_REMOVED,
+/* private: internal use only */
+ __NFC_CMD_AFTER_LAST
+};
+#define NFC_CMD_MAX (__NFC_CMD_AFTER_LAST - 1)
+
+/**
+ * enum nfc_attrs - supported nfc attributes
+ *
+ * @NFC_ATTR_UNSPEC: unspecified attribute
+ *
+ * @NFC_ATTR_DEVICE_INDEX: index of nfc device
+ * @NFC_ATTR_DEVICE_NAME: device name, max 8 chars
+ * @NFC_ATTR_PROTOCOLS: nfc protocols - bitwise or-ed combination from
+ * NFC_PROTO_*_MASK constants
+ * @NFC_ATTR_TARGETS: array of targets (see enum nfc_target_attr)
+ */
+enum nfc_attrs {
+ NFC_ATTR_UNSPEC,
+ NFC_ATTR_DEVICE_INDEX,
+ NFC_ATTR_DEVICE_NAME,
+ NFC_ATTR_PROTOCOLS,
+ NFC_ATTR_TARGETS,
+/* private: internal use only */
+ __NFC_ATTR_AFTER_LAST
+};
+#define NFC_ATTR_MAX (__NFC_ATTR_AFTER_LAST - 1)
+
+#define NFC_DEVICE_NAME_MAXSIZE 8
+
+/* NFC protocols */
+#define NFC_PROTO_JEWEL 0
+#define NFC_PROTO_MIFARE 1
+#define NFC_PROTO_FELICA 2
+#define NFC_PROTO_ISO14443 3
+#define NFC_PROTO_NFC_DEP 4
+
+#define NFC_PROTO_MAX 5
+
+/* NFC protocols masks used in bitsets */
+#define NFC_PROTO_JEWEL_MASK (1 << NFC_PROTO_JEWEL)
+#define NFC_PROTO_MIFARE_MASK (1 << NFC_PROTO_MIFARE)
+#define NFC_PROTO_FELICA_MASK (1 << NFC_PROTO_FELICA)
+#define NFC_PROTO_ISO14443_MASK (1 << NFC_PROTO_ISO14443)
+#define NFC_PROTO_NFC_DEP_MASK (1 << NFC_PROTO_NFC_DEP)
+
+/**
+ * enum nfc_target_attr - attributes for nfc targets
+ *
+ * @NFC_TARGET_ATTR_UNSPEC: unspecified attribute
+ * @NFC_TARGET_ATTR_TARGET_INDEX: target index
+ * @NFC_TARGET_ATTR_SUPPORTED_PROTOCOLS: protocols supported by the target
+ * (bitwise or-ed combination from NFC_PROTO_* constants)
+ * @NFC_TARGET_ATTR_SENS_RES: extra information for NFC-A targets
+ * @NFC_TARGET_ATTR_SEL_RES: extra information for NFC-A targets
+ */
+enum nfc_target_attr {
+ NFC_TARGET_ATTR_UNSPEC,
+ NFC_TARGET_ATTR_TARGET_INDEX,
+ NFC_TARGET_ATTR_SUPPORTED_PROTOCOLS,
+ NFC_TARGET_ATTR_SENS_RES,
+ NFC_TARGET_ATTR_SEL_RES,
+/* private: internal use only */
+ __NFC_TARGET_ATTR_AFTER_LAST
+};
+#define NFC_TARGET_ATTR_MAX (__NFC_TARGET_ATTR_AFTER_LAST - 1)
+
+#endif /*__LINUX_NFC_H */
diff --git a/include/net/nfc.h b/include/net/nfc.h
index 11d63dc..72cc54d 100644
--- a/include/net/nfc.h
+++ b/include/net/nfc.h
@@ -54,16 +54,31 @@ struct nfc_ops {
void *cb_context);
};

+struct nfc_genl_data {
+ u32 poll_req_pid;
+ struct mutex genl_data_mutex;
+};
+
struct nfc_dev {
unsigned idx;
+ unsigned target_idx;
+ int n_targets;
struct device dev;
bool polling;
+ struct nfc_genl_data genl_data;
u32 supported_protocols;

struct nfc_ops *ops;
};
#define to_nfc_dev(_dev) container_of(_dev, struct nfc_dev, dev)

+struct nfc_target {
+ u32 idx;
+ u32 supported_protocols;
+ u16 sens_res;
+ u8 sel_res;
+};
+
extern struct class nfc_class;

struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
@@ -128,4 +143,7 @@ static inline const char *nfc_device_name(struct nfc_dev *dev)

struct sk_buff *nfc_alloc_skb(unsigned int size, gfp_t gfp);

+int nfc_targets_found(struct nfc_dev *dev, struct nfc_target *targets,
+ int ntargets, gfp_t gfp);
+
#endif /* __NET_NFC_H */
diff --git a/net/nfc/Makefile b/net/nfc/Makefile
index d837743..8aeaddc 100644
--- a/net/nfc/Makefile
+++ b/net/nfc/Makefile
@@ -4,6 +4,6 @@

obj-$(CONFIG_NFC) += nfc.o

-nfc-objs := core.o
+nfc-objs := core.o netlink.o

ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
diff --git a/net/nfc/core.c b/net/nfc/core.c
index a6b8c95..b73c917 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -210,6 +210,38 @@ struct sk_buff *nfc_alloc_skb(unsigned int size, gfp_t gfp)
}
EXPORT_SYMBOL(nfc_alloc_skb);

+/**
+ * nfc_targets_found - inform that targets were found
+ *
+ * @dev: The nfc device that found the targets
+ * @targets: array of nfc targets found
+ * @ntargets: targets array size
+ * @gfp: gfp flags
+ *
+ * The device driver must call this function when one or many nfc targets
+ * are found. After calling this function, the device driver must stop
+ * polling for targets.
+ */
+int nfc_targets_found(struct nfc_dev *dev, struct nfc_target *targets,
+ int ntargets, gfp_t gfp)
+{
+ int i;
+
+ pr_debug("%s: dev_name:%s", __func__, dev_name(&dev->dev));
+
+ dev->polling = false;
+
+ for (i = 0; i < ntargets; i++)
+ targets[i].idx = dev->target_idx++;
+
+ dev->n_targets = ntargets;
+
+ nfc_genl_targets_found(dev, targets, ntargets);
+
+ return 0;
+}
+EXPORT_SYMBOL(nfc_targets_found);
+
static void nfc_release(struct device *d)
{
struct nfc_dev *dev = to_nfc_dev(d);
@@ -286,9 +318,17 @@ EXPORT_SYMBOL(nfc_allocate_device);
*/
int nfc_register_device(struct nfc_dev *dev)
{
+ int rc;
+
pr_debug("%s: dev_name:%s", __func__, dev_name(&dev->dev));

- return device_add(&dev->dev);
+ nfc_genl_data_init(&dev->genl_data);
+
+ rc = device_add(&dev->dev);
+ if (rc < 0)
+ return rc;
+
+ return nfc_genl_device_added(dev);
}
EXPORT_SYMBOL(nfc_register_device);

@@ -301,9 +341,12 @@ void nfc_unregister_device(struct nfc_dev *dev)
{
pr_debug("%s: dev_name:%s", __func__, dev_name(&dev->dev));

+ nfc_genl_data_exit(&dev->genl_data);
+
/* lock to avoid unregistering a device while an operation
is in progress */
device_lock(&dev->dev);
+ nfc_genl_device_removed(dev);
device_del(&dev->dev);
device_unlock(&dev->dev);
}
@@ -311,13 +354,29 @@ EXPORT_SYMBOL(nfc_unregister_device);

static int __init nfc_init(void)
{
+ int rc;
+
printk(KERN_INFO "NFC Core ver %s\n", VERSION);

- return class_register(&nfc_class);
+ rc = class_register(&nfc_class);
+ if (rc)
+ goto err;
+
+ rc = nfc_genl_init();
+ if (rc)
+ goto err_genl;
+
+ return 0;
+
+err_genl:
+ class_unregister(&nfc_class);
+err:
+ return rc;
}

static void __exit nfc_exit(void)
{
+ nfc_genl_exit();
class_unregister(&nfc_class);
}

diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
new file mode 100644
index 0000000..76ec1ff
--- /dev/null
+++ b/net/nfc/netlink.c
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ *
+ * Authors:
+ * Lauro Ramos Venancio <[email protected]>
+ * Aloisio Almeida Jr <[email protected]>
+ *
+ * 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.
+ */
+
+#include <net/genetlink.h>
+#include <linux/nfc.h>
+#include <linux/slab.h>
+
+#include "nfc.h"
+
+static struct genl_multicast_group nfc_genl_event_mcgrp = {
+ .name = NFC_GENL_MCAST_EVENT_NAME,
+};
+
+struct genl_family nfc_genl_family = {
+ .id = GENL_ID_GENERATE,
+ .hdrsize = 0,
+ .name = NFC_GENL_NAME,
+ .version = NFC_GENL_VERSION,
+ .maxattr = NFC_ATTR_MAX,
+};
+
+static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = {
+ [NFC_ATTR_DEVICE_INDEX] = { .type = NLA_U32 },
+ [NFC_ATTR_DEVICE_NAME] = { .type = NLA_STRING,
+ .len = NFC_DEVICE_NAME_MAXSIZE },
+ [NFC_ATTR_PROTOCOLS] = { .type = NLA_U32 },
+ [NFC_ATTR_TARGETS] = { .type = NLA_NESTED },
+};
+
+static int nfc_genl_msg_put_target(struct sk_buff *msg,
+ struct nfc_target *target)
+{
+ NLA_PUT_U32(msg, NFC_TARGET_ATTR_TARGET_INDEX, target->idx);
+ NLA_PUT_U32(msg, NFC_TARGET_ATTR_SUPPORTED_PROTOCOLS,
+ target->supported_protocols);
+ NLA_PUT_U8(msg, NFC_TARGET_ATTR_SENS_RES, target->sens_res);
+ NLA_PUT_U8(msg, NFC_TARGET_ATTR_SEL_RES, target->sel_res);
+
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+
+int nfc_genl_targets_found(struct nfc_dev *dev, struct nfc_target *targets,
+ int ntargets)
+{
+ struct sk_buff *msg;
+ void *hdr;
+ struct nlattr *targets_attr;
+ int i;
+
+ pr_debug("%s\n", __func__);
+
+ dev->genl_data.poll_req_pid = 0;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+ NFC_EVENT_TARGETS_FOUND);
+ if (!hdr)
+ goto free_msg;
+
+ NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx);
+
+ targets_attr = nla_nest_start(msg, NFC_ATTR_TARGETS);
+
+ for (i = 0; i < ntargets; i++) {
+ struct nlattr *target = nla_nest_start(msg, i);
+
+ if (nfc_genl_msg_put_target(msg, &targets[i]))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, target);
+ }
+
+ nla_nest_end(msg, targets_attr);
+ genlmsg_end(msg, hdr);
+
+ return genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_ATOMIC);
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+free_msg:
+ nlmsg_free(msg);
+ return -EMSGSIZE;
+}
+
+int nfc_genl_device_added(struct nfc_dev *dev)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ pr_debug("%s\n", __func__);
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+ NFC_EVENT_DEVICE_ADDED);
+ if (!hdr)
+ goto free_msg;
+
+ NLA_PUT_STRING(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev));
+ NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx);
+ NLA_PUT_U32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols);
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+
+ return 0;
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+free_msg:
+ nlmsg_free(msg);
+ return -EMSGSIZE;
+}
+
+int nfc_genl_device_removed(struct nfc_dev *dev)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ pr_debug("%s\n", __func__);
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+ NFC_EVENT_DEVICE_REMOVED);
+ if (!hdr)
+ goto free_msg;
+
+ NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx);
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+
+ return 0;
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+free_msg:
+ nlmsg_free(msg);
+ return -EMSGSIZE;
+}
+
+static int nfc_genl_send_device(struct sk_buff *msg, u32 pid, u32 seq,
+ int flags, struct nfc_dev *dev)
+{
+ void *hdr;
+
+ pr_debug("%s\n", __func__);
+
+ hdr = genlmsg_put(msg, pid, seq, &nfc_genl_family, flags,
+ NFC_CMD_GET_DEVICE);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ NLA_PUT_STRING(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev));
+ NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx);
+ NLA_PUT_U32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols);
+
+ return genlmsg_end(msg, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ return -EMSGSIZE;
+}
+
+static int nfc_genl_dump_devices(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0];
+ struct nfc_dev *dev = (struct nfc_dev *) cb->args[1];
+ u32 pid = NETLINK_CB(cb->skb).pid;
+ u32 seq = cb->nlh->nlmsg_seq;
+
+ pr_debug("%s\n", __func__);
+
+ if (!iter) {
+ iter = kmalloc(sizeof(struct class_dev_iter), GFP_KERNEL);
+ if (!iter)
+ return -ENOMEM;
+
+ nfc_device_iter_init(iter);
+ cb->args[0] = (long) iter;
+
+ dev = nfc_device_iter_next(iter);
+ }
+
+ while (dev) {
+ int rc;
+
+ rc = nfc_genl_send_device(skb, pid, seq, NLM_F_MULTI, dev);
+ if (rc < 0)
+ break;
+
+ dev = nfc_device_iter_next(iter);
+ }
+
+ cb->args[1] = (long) dev;
+
+ return skb->len;
+}
+
+static int nfc_genl_dump_devices_done(struct netlink_callback *cb)
+{
+ struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0];
+
+ pr_debug("%s\n", __func__);
+
+ nfc_device_iter_exit(iter);
+ kfree(iter);
+
+ return 0;
+}
+
+static int nfc_genl_get_device(struct sk_buff *skb, struct genl_info *info)
+{
+ struct sk_buff *msg;
+ struct nfc_dev *dev;
+ u32 idx;
+ int rc = -ENOBUFS;
+
+ pr_debug("%s\n", __func__);
+
+ if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
+ return -EINVAL;
+
+ idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+
+ dev = nfc_get_device(idx);
+ if (!dev)
+ return -ENODEV;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg) {
+ rc = -ENOMEM;
+ goto out_putdev;
+ }
+
+ rc = nfc_genl_send_device(msg, info->snd_pid, info->snd_seq, 0, dev);
+ if (rc < 0)
+ goto out_free;
+
+ nfc_put_device(dev);
+
+ return genlmsg_reply(msg, info);
+
+out_free:
+ nlmsg_free(msg);
+out_putdev:
+ nfc_put_device(dev);
+ return rc;
+}
+
+static int nfc_genl_start_poll(struct sk_buff *skb, struct genl_info *info)
+{
+ struct nfc_dev *dev;
+ int rc;
+ u32 idx;
+ u32 protocols;
+
+ pr_debug("%s\n", __func__);
+
+ if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
+ !info->attrs[NFC_ATTR_PROTOCOLS])
+ return -EINVAL;
+
+ idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+ protocols = nla_get_u32(info->attrs[NFC_ATTR_PROTOCOLS]);
+
+ dev = nfc_get_device(idx);
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->genl_data.genl_data_mutex);
+
+ rc = nfc_start_poll(dev, protocols);
+ if (!rc)
+ dev->genl_data.poll_req_pid = info->snd_pid;
+
+ mutex_unlock(&dev->genl_data.genl_data_mutex);
+
+ nfc_put_device(dev);
+ return rc;
+}
+
+static int nfc_genl_stop_poll(struct sk_buff *skb, struct genl_info *info)
+{
+ struct nfc_dev *dev;
+ int rc;
+ u32 idx;
+
+ pr_debug("%s\n", __func__);
+
+ if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
+ return -EINVAL;
+
+ idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+
+ dev = nfc_get_device(idx);
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->genl_data.genl_data_mutex);
+
+ if (dev->genl_data.poll_req_pid != info->snd_pid) {
+ rc = -EBUSY;
+ goto out;
+ }
+
+ rc = nfc_stop_poll(dev);
+ dev->genl_data.poll_req_pid = 0;
+
+out:
+ mutex_unlock(&dev->genl_data.genl_data_mutex);
+ nfc_put_device(dev);
+ return rc;
+}
+
+static struct genl_ops nfc_genl_ops[] = {
+ {
+ .cmd = NFC_CMD_GET_DEVICE,
+ .doit = nfc_genl_get_device,
+ .dumpit = nfc_genl_dump_devices,
+ .done = nfc_genl_dump_devices_done,
+ .policy = nfc_genl_policy,
+ },
+ {
+ .cmd = NFC_CMD_START_POLL,
+ .doit = nfc_genl_start_poll,
+ .policy = nfc_genl_policy,
+ },
+ {
+ .cmd = NFC_CMD_STOP_POLL,
+ .doit = nfc_genl_stop_poll,
+ .policy = nfc_genl_policy,
+ },
+};
+
+static int nfc_genl_rcv_nl_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct netlink_notify *n = ptr;
+ struct class_dev_iter iter;
+ struct nfc_dev *dev;
+
+ if (event != NETLINK_URELEASE || n->protocol != NETLINK_GENERIC)
+ goto out;
+
+ pr_debug("%s: NETLINK_URELEASE event from id %d\n", __func__, n->pid);
+
+ nfc_device_iter_init(&iter);
+ dev = nfc_device_iter_next(&iter);
+
+ while (dev) {
+ mutex_lock(&dev->genl_data.genl_data_mutex);
+ if (dev->genl_data.poll_req_pid == n->pid) {
+ nfc_stop_poll(dev);
+ dev->genl_data.poll_req_pid = 0;
+ }
+ mutex_unlock(&dev->genl_data.genl_data_mutex);
+ dev = nfc_device_iter_next(&iter);
+ }
+
+ nfc_device_iter_exit(&iter);
+
+out:
+ return NOTIFY_DONE;
+}
+
+void nfc_genl_data_init(struct nfc_genl_data *genl_data)
+{
+ genl_data->poll_req_pid = 0;
+ mutex_init(&genl_data->genl_data_mutex);
+}
+
+void nfc_genl_data_exit(struct nfc_genl_data *genl_data)
+{
+ mutex_destroy(&genl_data->genl_data_mutex);
+}
+
+static struct notifier_block nl_notifier = {
+ .notifier_call = nfc_genl_rcv_nl_event,
+};
+
+/**
+ * nfc_genl_init() - Initialize netlink interface
+ *
+ * This initialization function registers the nfc netlink family.
+ */
+int __init nfc_genl_init(void)
+{
+ int rc;
+
+ rc = genl_register_family_with_ops(&nfc_genl_family, nfc_genl_ops,
+ ARRAY_SIZE(nfc_genl_ops));
+ if (rc)
+ return rc;
+
+ rc = genl_register_mc_group(&nfc_genl_family, &nfc_genl_event_mcgrp);
+
+ netlink_register_notifier(&nl_notifier);
+
+ return rc;
+}
+
+/**
+ * nfc_genl_exit() - Deinitialize netlink interface
+ *
+ * This exit function unregisters the nfc netlink family.
+ */
+void nfc_genl_exit(void)
+{
+ netlink_unregister_notifier(&nl_notifier);
+ genl_unregister_family(&nfc_genl_family);
+}
diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h
index 8a50fd1..33d19eb 100644
--- a/net/nfc/nfc.h
+++ b/net/nfc/nfc.h
@@ -26,6 +26,20 @@

#include <net/nfc.h>

+int __init nfc_genl_init(void);
+
+void nfc_genl_exit(void);
+
+void nfc_genl_data_init(struct nfc_genl_data *genl_data);
+
+void nfc_genl_data_exit(struct nfc_genl_data *genl_data);
+
+int nfc_genl_targets_found(struct nfc_dev *dev, struct nfc_target *targets,
+ int ntargets);
+
+int nfc_genl_device_added(struct nfc_dev *dev);
+int nfc_genl_device_removed(struct nfc_dev *dev);
+
struct nfc_dev *nfc_get_device(unsigned idx);

static inline void nfc_put_device(struct nfc_dev *dev)
--
1.7.1


2011-06-03 13:45:26

by Aloisio Almeida

[permalink] [raw]
Subject: Re: [PATCH 1/6] NFC: add nfc subsystem core

On Fri, Jun 3, 2011 at 10:35 AM, Johannes Berg
<[email protected]> wrote:
> On Thu, 2011-06-02 at 18:46 -0300, Lauro Ramos Venancio wrote:
>
>> +struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? u32 supported_protocols);
>
> Where do the protocols come from? I don't see any defines for them?

The defines are on the patch 2/6 (generic netlink): NFC_PROTO_*

Aloisio

>
> johannes
>
>

2011-06-02 21:47:30

by Lauro Ramos Venancio

[permalink] [raw]
Subject: [PATCH 5/6] NFC: pn533: add NXP pn533 nfc device driver

From: Aloisio Almeida Jr <[email protected]>

Signed-off-by: Lauro Ramos Venancio <[email protected]>
Signed-off-by: Aloisio Almeida Jr <[email protected]>
Signed-off-by: Samuel Ortiz <[email protected]>
---
drivers/nfc/Kconfig | 10 +
drivers/nfc/Makefile | 1 +
drivers/nfc/pn533.c | 1629 ++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1640 insertions(+), 0 deletions(-)
create mode 100644 drivers/nfc/pn533.c

diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig
index 7809289..2acff43 100644
--- a/drivers/nfc/Kconfig
+++ b/drivers/nfc/Kconfig
@@ -17,4 +17,14 @@ config PN544_NFC
To compile this driver as a module, choose m here. The module will
be called pn544.

+config NFC_PN533
+ tristate "NXP PN533 USB driver"
+ depends on USB
+ help
+ NXP PN533 USB driver.
+ This driver provides support for NFC NXP PN533 devices.
+
+ Say Y here to compile support for PN533 devices into the
+ kernel or say M to compile it as module (pn533).
+
endmenu
diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile
index 25296f0..8ef446d 100644
--- a/drivers/nfc/Makefile
+++ b/drivers/nfc/Makefile
@@ -3,5 +3,6 @@
#

obj-$(CONFIG_PN544_NFC) += pn544.o
+obj-$(CONFIG_NFC_PN533) += pn533.o

ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c
new file mode 100644
index 0000000..231e7c4
--- /dev/null
+++ b/drivers/nfc/pn533.c
@@ -0,0 +1,1629 @@
+/*
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ *
+ * Authors:
+ * Lauro Ramos Venancio <[email protected]>
+ * Aloisio Almeida Jr <[email protected]>
+ *
+ * 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.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/nfc.h>
+#include <linux/netdevice.h>
+#include <net/nfc.h>
+
+#define VERSION "0.1"
+
+#define PN533_VENDOR_ID 0x4CC
+#define PN533_PRODUCT_ID 0x2533
+
+#define SCM_VENDOR_ID 0x4E6
+#define SCL3711_PRODUCT_ID 0x5591
+
+static const struct usb_device_id pn533_table[] = {
+ { USB_DEVICE(PN533_VENDOR_ID, PN533_PRODUCT_ID) },
+ { USB_DEVICE(SCM_VENDOR_ID, SCL3711_PRODUCT_ID) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, pn533_table);
+
+/* frame definitions */
+#define PN533_FRAME_TAIL_SIZE 2
+#define PN533_FRAME_SIZE(f) (sizeof(struct pn533_frame) + f->datalen + \
+ PN533_FRAME_TAIL_SIZE)
+#define PN533_FRAME_ACK_SIZE (sizeof(struct pn533_frame) + 1)
+#define PN533_FRAME_CHECKSUM(f) (f->data[f->datalen])
+#define PN533_FRAME_POSTAMBLE(f) (f->data[f->datalen + 1])
+
+/* start of frame */
+#define PN533_SOF 0x00FF
+
+/* frame identifier: in/out/error */
+#define PN533_FRAME_IDENTIFIER(f) (f->data[0])
+#define PN533_DIR_OUT 0xD4
+#define PN533_DIR_IN 0xD5
+
+/* PN533 Commands */
+#define PN533_FRAME_CMD(f) (f->data[1])
+#define PN533_FRAME_CMD_PARAMS_PTR(f) (&f->data[2])
+#define PN533_FRAME_CMD_PARAMS_LEN(f) (f->datalen - 2)
+
+#define PN533_CMD_GET_FIRMWARE_VERSION 0x02
+#define PN533_CMD_RF_CONFIGURATION 0x32
+#define PN533_CMD_IN_DATA_EXCHANGE 0x40
+#define PN533_CMD_IN_LIST_PASSIVE_TARGET 0x4A
+#define PN533_CMD_IN_ATR 0x50
+#define PN533_CMD_IN_RELEASE 0x52
+
+#define PN533_CMD_RESPONSE(cmd) (cmd + 1)
+
+/* PN533 Return codes */
+#define PN533_CMD_RET_MASK 0x3F
+#define PN533_CMD_MI_MASK 0x40
+#define PN533_CMD_RET_SUCCESS 0x00
+
+struct pn533;
+
+typedef int (*pn533_cmd_complete_t) (struct pn533 *dev, void *arg,
+ u8 *params, int params_len);
+
+/* structs for pn533 commands */
+
+/* PN533_CMD_GET_FIRMWARE_VERSION */
+struct pn533_fw_version {
+ u8 ic;
+ u8 ver;
+ u8 rev;
+ u8 support;
+};
+
+/* PN533_CMD_RF_CONFIGURATION */
+#define PN533_CFGITEM_MAX_RETRIES 0x05
+
+#define PN533_CONFIG_MAX_RETRIES_NO_RETRY 0x00
+#define PN533_CONFIG_MAX_RETRIES_ENDLESS 0xFF
+
+struct pn533_config_max_retries {
+ u8 mx_rty_atr;
+ u8 mx_rty_psl;
+ u8 mx_rty_passive_act;
+} __packed;
+
+/* PN533_CMD_IN_LIST_PASSIVE_TARGET */
+
+/* felica commands opcode */
+#define PN533_FELICA_OPC_SENSF_REQ 0
+#define PN533_FELICA_OPC_SENSF_RES 1
+/* felica SENSF_REQ parameters */
+#define PN533_FELICA_SENSF_SC_ALL 0xFFFF
+#define PN533_FELICA_SENSF_RC_NO_SYSTEM_CODE 0
+#define PN533_FELICA_SENSF_RC_SYSTEM_CODE 1
+#define PN533_FELICA_SENSF_RC_ADVANCED_PROTOCOL 2
+
+/* type B initiator_data values */
+#define PN533_TYPE_B_AFI_ALL_FAMILIES 0
+#define PN533_TYPE_B_POLL_METHOD_TIMESLOT 0
+#define PN533_TYPE_B_POLL_METHOD_PROBABILISTIC 1
+
+union pn533_cmd_poll_initdata {
+ struct {
+ u8 afi;
+ u8 polling_method;
+ } type_b;
+ struct {
+ u8 opcode;
+ __be16 sc;
+ u8 rc;
+ u8 tsn;
+ } felica;
+};
+
+/* Poll modulations */
+enum {
+ PN533_POLL_MOD_106KBPS_A,
+ PN533_POLL_MOD_212KBPS_FELICA,
+ PN533_POLL_MOD_424KBPS_FELICA,
+ PN533_POLL_MOD_106KBPS_JEWEL,
+ PN533_POLL_MOD_847KBPS_B,
+
+ __PN533_POLL_MOD_AFTER_LAST,
+};
+#define PN533_POLL_MOD_MAX (__PN533_POLL_MOD_AFTER_LAST - 1)
+
+struct pn533_poll_modulations {
+ struct {
+ u8 maxtg;
+ u8 brty;
+ union pn533_cmd_poll_initdata initiator_data;
+ } data;
+ u8 len;
+};
+
+const struct pn533_poll_modulations poll_mod[] = {
+ [PN533_POLL_MOD_106KBPS_A] = {
+ .data = {
+ .maxtg = 1,
+ .brty = 0,
+ },
+ .len = 2,
+ },
+ [PN533_POLL_MOD_212KBPS_FELICA] = {
+ .data = {
+ .maxtg = 1,
+ .brty = 1,
+ .initiator_data.felica = {
+ .opcode = PN533_FELICA_OPC_SENSF_REQ,
+ .sc = PN533_FELICA_SENSF_SC_ALL,
+ .rc = PN533_FELICA_SENSF_RC_NO_SYSTEM_CODE,
+ .tsn = 0,
+ },
+ },
+ .len = 7,
+ },
+ [PN533_POLL_MOD_424KBPS_FELICA] = {
+ .data = {
+ .maxtg = 1,
+ .brty = 2,
+ .initiator_data.felica = {
+ .opcode = PN533_FELICA_OPC_SENSF_REQ,
+ .sc = PN533_FELICA_SENSF_SC_ALL,
+ .rc = PN533_FELICA_SENSF_RC_NO_SYSTEM_CODE,
+ .tsn = 0,
+ },
+ },
+ .len = 7,
+ },
+ [PN533_POLL_MOD_106KBPS_JEWEL] = {
+ .data = {
+ .maxtg = 1,
+ .brty = 4,
+ },
+ .len = 2,
+ },
+ [PN533_POLL_MOD_847KBPS_B] = {
+ .data = {
+ .maxtg = 1,
+ .brty = 8,
+ .initiator_data.type_b = {
+ .afi = PN533_TYPE_B_AFI_ALL_FAMILIES,
+ .polling_method =
+ PN533_TYPE_B_POLL_METHOD_TIMESLOT,
+ },
+ },
+ .len = 3,
+ },
+};
+
+/* PN533_CMD_IN_ATR */
+
+struct pn533_cmd_activate_param {
+ u8 tg;
+ u8 next;
+} __packed;
+
+struct pn533_cmd_activate_response {
+ u8 status;
+ u8 nfcid3t[10];
+ u8 didt;
+ u8 bst;
+ u8 brt;
+ u8 to;
+ u8 ppt;
+ /* optional */
+ u8 gt[];
+} __packed;
+
+
+struct pn533 {
+ struct usb_device *udev;
+ struct usb_interface *interface;
+ struct nfc_dev *nfc_dev;
+
+ struct urb *out_urb;
+ int out_maxlen;
+ struct pn533_frame *out_frame;
+
+ struct urb *in_urb;
+ int in_maxlen;
+ struct pn533_frame *in_frame;
+
+ struct tasklet_struct tasklet;
+ struct pn533_frame *tklt_in_frame;
+ int tklt_in_error;
+
+ pn533_cmd_complete_t cmd_complete;
+ void *cmd_complete_arg;
+ struct semaphore cmd_lock;
+ u8 cmd;
+
+ struct pn533_poll_modulations *poll_mod_active[PN533_POLL_MOD_MAX + 1];
+ u8 poll_mod_count;
+ u8 poll_mod_curr;
+ u32 poll_protocols;
+
+ u8 tgt_available_prots;
+ u8 tgt_active_prot;
+};
+
+struct pn533_frame {
+ u8 preamble;
+ __be16 start_frame;
+ u8 datalen;
+ u8 datalen_checksum;
+ u8 data[];
+} __packed;
+
+/* The rule: value + checksum = 0 */
+static inline u8 pn533_checksum(u8 value)
+{
+ return ~value + 1;
+}
+
+/* The rule: sum(data elements) + checksum = 0 */
+static u8 pn533_data_checksum(u8 *data, int datalen)
+{
+ u8 sum = 0;
+ int i;
+
+ for (i = 0; i < datalen; i++)
+ sum += data[i];
+
+ return pn533_checksum(sum);
+}
+
+/**
+ * pn533_tx_frame_ack - create a ack frame
+ * @frame: The frame to be set as ack
+ *
+ * Ack is different type of standard frame. As a standard frame, it has
+ * preamble and start_frame. However the checksum of this frame must fail,
+ * i.e. datalen + datalen_checksum must NOT be zero. When the checksum test
+ * fails and datalen = 0 and datalen_checksum = 0xFF, the frame is a ack.
+ * After datalen_checksum field, the postamble is placed.
+ */
+static void pn533_tx_frame_ack(struct pn533_frame *frame)
+{
+ frame->preamble = 0;
+ frame->start_frame = cpu_to_be16(PN533_SOF);
+ frame->datalen = 0;
+ frame->datalen_checksum = 0xFF;
+ /* data[0] is used as postamble */
+ frame->data[0] = 0;
+}
+
+static void pn533_tx_frame_init(struct pn533_frame *frame, u8 cmd)
+{
+ frame->preamble = 0;
+ frame->start_frame = cpu_to_be16(PN533_SOF);
+ PN533_FRAME_IDENTIFIER(frame) = PN533_DIR_OUT;
+ PN533_FRAME_CMD(frame) = cmd;
+ frame->datalen = 2;
+}
+
+static void pn533_tx_frame_finish(struct pn533_frame *frame)
+{
+ frame->datalen_checksum = pn533_checksum(frame->datalen);
+
+ PN533_FRAME_CHECKSUM(frame) =
+ pn533_data_checksum(frame->data, frame->datalen);
+
+ PN533_FRAME_POSTAMBLE(frame) = 0;
+}
+
+static bool pn533_rx_frame_is_valid(struct pn533_frame *frame)
+{
+ u8 checksum;
+
+ if (frame->start_frame != cpu_to_be16(PN533_SOF))
+ return false;
+
+ checksum = pn533_checksum(frame->datalen);
+ if (checksum != frame->datalen_checksum)
+ return false;
+
+ checksum = pn533_data_checksum(frame->data, frame->datalen);
+ if (checksum != PN533_FRAME_CHECKSUM(frame))
+ return false;
+
+ return true;
+}
+
+static bool pn533_rx_frame_is_ack(struct pn533_frame *frame)
+{
+ if (frame->start_frame != cpu_to_be16(PN533_SOF))
+ return false;
+
+ if (frame->datalen != 0 || frame->datalen_checksum != 0xFF)
+ return false;
+
+ return true;
+}
+
+static bool pn533_rx_frame_is_cmd_response(struct pn533_frame *frame, u8 cmd)
+{
+ return (PN533_FRAME_CMD(frame) == PN533_CMD_RESPONSE(cmd));
+}
+
+static void pn533_tasklet_cmd_complete(unsigned long arg)
+{
+ struct pn533 *dev = (struct pn533 *) arg;
+ struct pn533_frame *in_frame = dev->tklt_in_frame;
+ int rc;
+
+ if (dev->tklt_in_error)
+ rc = dev->cmd_complete(dev, dev->cmd_complete_arg, NULL,
+ dev->tklt_in_error);
+ else
+ rc = dev->cmd_complete(dev, dev->cmd_complete_arg,
+ PN533_FRAME_CMD_PARAMS_PTR(in_frame),
+ PN533_FRAME_CMD_PARAMS_LEN(in_frame));
+
+ if (rc != -EINPROGRESS)
+ up(&dev->cmd_lock);
+}
+
+static void pn533_recv_response(struct urb *urb)
+{
+ struct pn533 *dev = urb->context;
+ struct pn533_frame *in_frame;
+
+ dev->tklt_in_frame = NULL;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ dev_dbg(&dev->interface->dev, "%s - urb shutting down with "
+ "status: %d", __func__, urb->status);
+ dev->tklt_in_error = urb->status;
+ goto sched_tasklet;
+ default:
+ dev_err(&dev->interface->dev, "%s - nonzero urb status "
+ "received: %d", __func__, urb->status);
+ dev->tklt_in_error = urb->status;
+ goto sched_tasklet;
+ }
+
+ in_frame = dev->in_urb->transfer_buffer;
+
+ if (!pn533_rx_frame_is_valid(in_frame)) {
+ dev_err(&dev->interface->dev, "Received an invalid frame");
+ dev->tklt_in_error = -EIO;
+ goto sched_tasklet;
+ }
+
+ if (!pn533_rx_frame_is_cmd_response(in_frame, dev->cmd)) {
+ dev_err(&dev->interface->dev, "The received frame is not "
+ "response to the last command");
+ dev->tklt_in_error = -EIO;
+ goto sched_tasklet;
+ }
+
+ dev_dbg(&dev->interface->dev, "Received a valid frame");
+ dev->tklt_in_error = 0;
+ dev->tklt_in_frame = in_frame;
+
+sched_tasklet:
+ tasklet_schedule(&dev->tasklet);
+}
+
+static int pn533_submit_urb_for_response(struct pn533 *dev, gfp_t flags)
+{
+ dev->in_urb->complete = pn533_recv_response;
+
+ return usb_submit_urb(dev->in_urb, flags);
+}
+
+static void pn533_recv_ack(struct urb *urb)
+{
+ struct pn533 *dev = urb->context;
+ struct pn533_frame *in_frame;
+ int rc;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ dev_dbg(&dev->interface->dev, "%s - urb shutting down with "
+ "status: %d", __func__, urb->status);
+ dev->tklt_in_error = urb->status;
+ goto sched_tasklet;
+ default:
+ dev_err(&dev->interface->dev, "%s - nonzero urb status "
+ "received: %d", __func__, urb->status);
+ dev->tklt_in_error = urb->status;
+ goto sched_tasklet;
+ }
+
+ in_frame = dev->in_urb->transfer_buffer;
+
+ if (!pn533_rx_frame_is_ack(in_frame)) {
+ dev_err(&dev->interface->dev, "%s - received an invalid ack",
+ __func__);
+ dev->tklt_in_error = -EIO;
+ goto sched_tasklet;
+ }
+
+ dev_dbg(&dev->interface->dev, "Received a valid ack");
+
+ rc = pn533_submit_urb_for_response(dev, GFP_ATOMIC);
+ if (rc) {
+ dev_err(&dev->interface->dev, "%s - usb_submit_urb failed "
+ "with result %d", __func__, rc);
+ dev->tklt_in_error = rc;
+ goto sched_tasklet;
+ }
+
+ return;
+
+sched_tasklet:
+ dev->tklt_in_frame = NULL;
+ tasklet_schedule(&dev->tasklet);
+}
+
+static int pn533_submit_urb_for_ack(struct pn533 *dev, gfp_t flags)
+{
+ dev->in_urb->complete = pn533_recv_ack;
+
+ return usb_submit_urb(dev->in_urb, flags);
+}
+
+static int pn533_send_ack(struct pn533 *dev, gfp_t flags)
+{
+ int rc;
+
+ dev_dbg(&dev->interface->dev, "Sending ack");
+
+ pn533_tx_frame_ack(dev->out_frame);
+
+ dev->out_urb->transfer_buffer = dev->out_frame;
+ dev->out_urb->transfer_buffer_length = PN533_FRAME_ACK_SIZE;
+ rc = usb_submit_urb(dev->out_urb, flags);
+
+ return rc;
+}
+
+static int __pn533_send_cmd_frame_async(struct pn533 *dev,
+ 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)
+{
+ int rc;
+
+ dev_dbg(&dev->interface->dev, "Sending command 0x%x",
+ PN533_FRAME_CMD(out_frame));
+
+ dev->cmd = PN533_FRAME_CMD(out_frame);
+ dev->cmd_complete = cmd_complete;
+ dev->cmd_complete_arg = arg;
+
+ dev->out_urb->transfer_buffer = out_frame;
+ dev->out_urb->transfer_buffer_length =
+ PN533_FRAME_SIZE(out_frame);
+
+ dev->in_urb->transfer_buffer = in_frame;
+ dev->in_urb->transfer_buffer_length = in_frame_len;
+
+ rc = usb_submit_urb(dev->out_urb, flags);
+ if (rc)
+ return rc;
+
+ rc = pn533_submit_urb_for_ack(dev, flags);
+ if (rc)
+ goto error;
+
+ return 0;
+
+error:
+ usb_unlink_urb(dev->out_urb);
+ return rc;
+}
+
+static int pn533_send_cmd_frame_async(struct pn533 *dev,
+ 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)
+{
+ int rc;
+
+ if (down_trylock(&dev->cmd_lock))
+ return -EBUSY;
+
+ rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame,
+ in_frame_len, cmd_complete, arg, flags);
+ if (rc)
+ goto error;
+
+ return 0;
+error:
+ up(&dev->cmd_lock);
+ return rc;
+}
+
+struct pn533_sync_cmd_response {
+ int rc;
+ struct completion done;
+};
+
+static int pn533_sync_cmd_complete(struct pn533 *dev, void *_arg,
+ u8 *params, int params_len)
+{
+ struct pn533_sync_cmd_response *arg = _arg;
+
+ arg->rc = 0;
+
+ if (params_len < 0) /* error */
+ arg->rc = params_len;
+
+ complete(&arg->done);
+
+ return 0;
+}
+
+static int pn533_send_cmd_frame_sync(struct pn533 *dev,
+ struct pn533_frame *out_frame,
+ struct pn533_frame *in_frame,
+ int in_frame_len)
+{
+ int rc;
+ struct pn533_sync_cmd_response arg;
+
+ init_completion(&arg.done);
+
+ rc = pn533_send_cmd_frame_async(dev, out_frame, in_frame, in_frame_len,
+ pn533_sync_cmd_complete, &arg, GFP_KERNEL);
+ if (rc)
+ return rc;
+
+ wait_for_completion(&arg.done);
+
+ return arg.rc;
+}
+
+static void pn533_send_complete(struct urb *urb)
+{
+ struct pn533 *dev = urb->context;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ dev_dbg(&dev->interface->dev, "%s - urb shutting down with "
+ "status: %d", __func__, urb->status);
+ break;
+ default:
+ dev_dbg(&dev->interface->dev, "%s - nonzero urb status "
+ "received: %d", __func__, urb->status);
+ }
+}
+
+struct pn533_target_type_a {
+ __be16 sens_res;
+ u8 sel_res;
+ u8 nfcid_len;
+ u8 nfcid_data[];
+} __packed;
+
+
+#define PN533_TYPE_A_SENS_RES_NFCID1(x) ((u8)((be16_to_cpu(x) & 0x00C0) >> 6))
+#define PN533_TYPE_A_SENS_RES_SSD(x) ((u8)((be16_to_cpu(x) & 0x001F) >> 0))
+#define PN533_TYPE_A_SENS_RES_PLATCONF(x) ((u8)((be16_to_cpu(x) & 0x0F00) >> 8))
+
+#define PN533_TYPE_A_SENS_RES_SSD_JEWEL 0x00
+#define PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL 0x0C
+
+#define PN533_TYPE_A_SEL_PROT(x) (((x) & 0x60) >> 5)
+#define PN533_TYPE_A_SEL_CASCADE(x) (((x) & 0x04) >> 2)
+
+#define PN533_TYPE_A_SEL_PROT_MIFARE 0
+#define PN533_TYPE_A_SEL_PROT_ISO14443 1
+#define PN533_TYPE_A_SEL_PROT_DEP 2
+#define PN533_TYPE_A_SEL_PROT_ISO14443_DEP 3
+
+static bool pn533_target_type_a_is_valid(struct pn533_target_type_a *type_a,
+ int target_data_len)
+{
+ u8 ssd;
+ u8 platconf;
+
+ if (target_data_len < sizeof(struct pn533_target_type_a))
+ return false;
+
+ /* The lenght check of nfcid[] and ats[] are not being performed because
+ the values are not being used */
+
+ /* Requirement 4.6.3.3 from NFC Forum Digital Spec */
+ ssd = PN533_TYPE_A_SENS_RES_SSD(type_a->sens_res);
+ platconf = PN533_TYPE_A_SENS_RES_PLATCONF(type_a->sens_res);
+
+ if ((ssd == PN533_TYPE_A_SENS_RES_SSD_JEWEL &&
+ platconf != PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL) ||
+ (ssd != PN533_TYPE_A_SENS_RES_SSD_JEWEL &&
+ platconf == PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL))
+ return false;
+
+ /* Requirements 4.8.2.1, 4.8.2.3, 4.8.2.5 and 4.8.2.7 from NFC Forum */
+ if (PN533_TYPE_A_SEL_CASCADE(type_a->sel_res) != 0)
+ return false;
+
+ return true;
+}
+
+static int pn533_target_found_type_a(struct nfc_target *nfc_tgt, u8 *tgt_data,
+ int tgt_data_len)
+{
+ struct pn533_target_type_a *tgt_type_a;
+
+ tgt_type_a = (struct pn533_target_type_a *) tgt_data;
+
+ if (!pn533_target_type_a_is_valid(tgt_type_a, tgt_data_len))
+ return -EPROTO;
+
+ switch (PN533_TYPE_A_SEL_PROT(tgt_type_a->sel_res)) {
+ case PN533_TYPE_A_SEL_PROT_MIFARE:
+ nfc_tgt->supported_protocols = NFC_PROTO_MIFARE_MASK;
+ break;
+ case PN533_TYPE_A_SEL_PROT_ISO14443:
+ nfc_tgt->supported_protocols = NFC_PROTO_ISO14443_MASK;
+ break;
+ case PN533_TYPE_A_SEL_PROT_DEP:
+ nfc_tgt->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+ break;
+ case PN533_TYPE_A_SEL_PROT_ISO14443_DEP:
+ nfc_tgt->supported_protocols = NFC_PROTO_ISO14443_MASK |
+ NFC_PROTO_NFC_DEP_MASK;
+ break;
+ }
+
+ nfc_tgt->sens_res = be16_to_cpu(tgt_type_a->sens_res);
+ nfc_tgt->sel_res = tgt_type_a->sel_res;
+
+ return 0;
+}
+
+struct pn533_target_felica {
+ u8 pol_res;
+ u8 opcode;
+ u8 nfcid2[8];
+ u8 pad[8];
+ /* optional */
+ u8 syst_code[];
+} __packed;
+
+#define PN533_FELICA_SENSF_NFCID2_DEP_B1 0x01
+#define PN533_FELICA_SENSF_NFCID2_DEP_B2 0xFE
+
+static bool pn533_target_felica_is_valid(struct pn533_target_felica *felica,
+ int target_data_len)
+{
+ if (target_data_len < sizeof(struct pn533_target_felica))
+ return false;
+
+ if (felica->opcode != PN533_FELICA_OPC_SENSF_RES)
+ return false;
+
+ return true;
+}
+
+static int pn533_target_found_felica(struct nfc_target *nfc_tgt, u8 *tgt_data,
+ int tgt_data_len)
+{
+ struct pn533_target_felica *tgt_felica;
+
+ tgt_felica = (struct pn533_target_felica *) tgt_data;
+
+ if (!pn533_target_felica_is_valid(tgt_felica, tgt_data_len))
+ return -EPROTO;
+
+ if (tgt_felica->nfcid2[0] == PN533_FELICA_SENSF_NFCID2_DEP_B1 &&
+ tgt_felica->nfcid2[1] ==
+ PN533_FELICA_SENSF_NFCID2_DEP_B2)
+ nfc_tgt->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+ else
+ nfc_tgt->supported_protocols = NFC_PROTO_FELICA_MASK;
+
+ return 0;
+}
+
+struct pn533_target_jewel {
+ __be16 sens_res;
+ u8 jewelid[4];
+} __packed;
+
+static bool pn533_target_jewel_is_valid(struct pn533_target_jewel *jewel,
+ int target_data_len)
+{
+ u8 ssd;
+ u8 platconf;
+
+ if (target_data_len < sizeof(struct pn533_target_jewel))
+ return false;
+
+ /* Requirement 4.6.3.3 from NFC Forum Digital Spec */
+ ssd = PN533_TYPE_A_SENS_RES_SSD(jewel->sens_res);
+ platconf = PN533_TYPE_A_SENS_RES_PLATCONF(jewel->sens_res);
+
+ if ((ssd == PN533_TYPE_A_SENS_RES_SSD_JEWEL &&
+ platconf != PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL) ||
+ (ssd != PN533_TYPE_A_SENS_RES_SSD_JEWEL &&
+ platconf == PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL))
+ return false;
+
+ return true;
+}
+
+static int pn533_target_found_jewel(struct nfc_target *nfc_tgt, u8 *tgt_data,
+ int tgt_data_len)
+{
+ struct pn533_target_jewel *tgt_jewel;
+
+ tgt_jewel = (struct pn533_target_jewel *) tgt_data;
+
+ if (!pn533_target_jewel_is_valid(tgt_jewel, tgt_data_len))
+ return -EPROTO;
+
+ nfc_tgt->supported_protocols = NFC_PROTO_JEWEL_MASK;
+
+ return 0;
+}
+
+struct pn533_type_b_prot_info {
+ u8 bitrate;
+ u8 fsci_type;
+ u8 fwi_adc_fo;
+} __packed;
+
+#define PN533_TYPE_B_PROT_FCSI(x) (((x) & 0xF0) >> 4)
+#define PN533_TYPE_B_PROT_TYPE(x) (((x) & 0x0F) >> 0)
+#define PN533_TYPE_B_PROT_TYPE_RFU_MASK 0x8
+
+struct pn533_type_b_sens_res {
+ u8 opcode;
+ u8 nfcid[4];
+ u8 appdata[4];
+ struct pn533_type_b_prot_info prot_info;
+} __packed;
+
+#define PN533_TYPE_B_OPC_SENSB_RES 0x50
+
+struct pn533_target_type_b {
+ struct pn533_type_b_sens_res sensb_res;
+ u8 attrib_res_len;
+ u8 attrib_res[];
+} __packed;
+
+static bool pn533_target_type_b_is_valid(struct pn533_target_type_b *type_b,
+ int target_data_len)
+{
+ if (target_data_len < sizeof(struct pn533_target_type_b))
+ return false;
+
+ if (type_b->sensb_res.opcode != PN533_TYPE_B_OPC_SENSB_RES)
+ return false;
+
+ if (PN533_TYPE_B_PROT_TYPE(type_b->sensb_res.prot_info.fsci_type) &
+ PN533_TYPE_B_PROT_TYPE_RFU_MASK)
+ return false;
+
+ return true;
+}
+
+static int pn533_target_found_type_b(struct nfc_target *nfc_tgt, u8 *tgt_data,
+ int tgt_data_len)
+{
+ struct pn533_target_type_b *tgt_type_b;
+
+ tgt_type_b = (struct pn533_target_type_b *) tgt_data;
+
+ if (!pn533_target_type_b_is_valid(tgt_type_b, tgt_data_len))
+ return -EPROTO;
+
+ nfc_tgt->supported_protocols = NFC_PROTO_ISO14443_MASK;
+
+ return 0;
+}
+
+struct pn533_poll_response {
+ u8 nbtg;
+ u8 tg;
+ u8 target_data[];
+} __packed;
+
+static int pn533_target_found(struct pn533 *dev,
+ struct pn533_poll_response *resp, int resp_len)
+{
+ int target_data_len;
+ struct nfc_target nfc_tgt;
+ int rc;
+
+ dev_dbg(&dev->interface->dev, "%s - Target found - modulation=%d",
+ __func__, dev->poll_mod_curr);
+
+ if (resp->tg != 1)
+ return -EPROTO;
+
+ target_data_len = resp_len - sizeof(struct pn533_poll_response);
+
+ switch (dev->poll_mod_curr) {
+ case PN533_POLL_MOD_106KBPS_A:
+ rc = pn533_target_found_type_a(&nfc_tgt, resp->target_data,
+ target_data_len);
+ break;
+ case PN533_POLL_MOD_212KBPS_FELICA:
+ case PN533_POLL_MOD_424KBPS_FELICA:
+ rc = pn533_target_found_felica(&nfc_tgt, resp->target_data,
+ target_data_len);
+ break;
+ case PN533_POLL_MOD_106KBPS_JEWEL:
+ rc = pn533_target_found_jewel(&nfc_tgt, resp->target_data,
+ target_data_len);
+ break;
+ case PN533_POLL_MOD_847KBPS_B:
+ rc = pn533_target_found_type_b(&nfc_tgt, resp->target_data,
+ target_data_len);
+ break;
+ default:
+ dev_err(&dev->interface->dev, "%s - Unknown current poll"
+ " modulation", __func__);
+ return -EPROTO;
+ }
+
+ if (rc)
+ return rc;
+
+ if (!(nfc_tgt.supported_protocols & dev->poll_protocols)) {
+ dev_dbg(&dev->interface->dev, "%s - The target found does not"
+ " have the desired protocol", __func__);
+ return -EAGAIN;
+ }
+
+ dev_dbg(&dev->interface->dev, "%s - Target found - protocols=0x%x",
+ __func__, nfc_tgt.supported_protocols);
+
+ dev->tgt_available_prots = nfc_tgt.supported_protocols;
+
+ nfc_targets_found(dev->nfc_dev, &nfc_tgt, 1, GFP_ATOMIC);
+
+ return 0;
+}
+
+static void pn533_poll_reset_mod_list(struct pn533 *dev)
+{
+ dev->poll_mod_count = 0;
+}
+
+static void pn533_poll_add_mod(struct pn533 *dev, u8 mod_index)
+{
+ dev->poll_mod_active[dev->poll_mod_count] =
+ (struct pn533_poll_modulations *) &poll_mod[mod_index];
+ dev->poll_mod_count++;
+}
+
+static void pn533_poll_create_mod_list(struct pn533 *dev, u32 protocols)
+{
+ pn533_poll_reset_mod_list(dev);
+
+ if (protocols & NFC_PROTO_MIFARE_MASK
+ || protocols & NFC_PROTO_ISO14443_MASK
+ || protocols & NFC_PROTO_NFC_DEP_MASK)
+ pn533_poll_add_mod(dev, PN533_POLL_MOD_106KBPS_A);
+
+ if (protocols & NFC_PROTO_FELICA_MASK
+ || protocols & NFC_PROTO_NFC_DEP_MASK) {
+ pn533_poll_add_mod(dev, PN533_POLL_MOD_212KBPS_FELICA);
+ pn533_poll_add_mod(dev, PN533_POLL_MOD_424KBPS_FELICA);
+ }
+
+ if (protocols & NFC_PROTO_JEWEL_MASK)
+ pn533_poll_add_mod(dev, PN533_POLL_MOD_106KBPS_JEWEL);
+
+ if (protocols & NFC_PROTO_ISO14443_MASK)
+ pn533_poll_add_mod(dev, PN533_POLL_MOD_847KBPS_B);
+}
+
+static void pn533_start_poll_frame(struct pn533_frame *frame,
+ struct pn533_poll_modulations *mod)
+{
+
+ pn533_tx_frame_init(frame, PN533_CMD_IN_LIST_PASSIVE_TARGET);
+
+ memcpy(PN533_FRAME_CMD_PARAMS_PTR(frame), &mod->data, mod->len);
+ frame->datalen += mod->len;
+
+ pn533_tx_frame_finish(frame);
+}
+
+static int pn533_start_poll_complete(struct pn533 *dev, void *arg,
+ u8 *params, int params_len)
+{
+ struct pn533_poll_response *resp;
+ struct pn533_poll_modulations *next_mod;
+ int rc;
+
+ if (params_len == -ENOENT) {
+ dev_dbg(&dev->interface->dev, "%s - poll has been stopped",
+ __func__);
+ goto stop_poll;
+ }
+
+ if (params_len < 0) {
+ dev_err(&dev->interface->dev, "%s - error %d when running poll",
+ __func__, params_len);
+ goto stop_poll;
+ }
+
+ resp = (struct pn533_poll_response *) params;
+ if (resp->nbtg) {
+ rc = pn533_target_found(dev, resp, params_len);
+
+ /* We must stop the poll after a valid target found */
+ if (rc == 0)
+ goto stop_poll;
+
+ if (rc != -EAGAIN)
+ dev_err(&dev->interface->dev, "%s - The target found is"
+ " not valid, continue to poll",
+ __func__);
+ }
+
+ dev->poll_mod_curr = (dev->poll_mod_curr + 1) % dev->poll_mod_count;
+
+ next_mod = dev->poll_mod_active[dev->poll_mod_curr];
+
+ dev_dbg(&dev->interface->dev, "%s - poll next modulation (0x%x)",
+ __func__, dev->poll_mod_curr);
+
+ pn533_start_poll_frame(dev->out_frame, next_mod);
+
+ /* Don't need to down the semaphore again */
+ rc = __pn533_send_cmd_frame_async(dev, dev->out_frame, dev->in_frame,
+ dev->in_maxlen, pn533_start_poll_complete,
+ NULL, GFP_ATOMIC);
+
+ if (rc == -EPERM) {
+ dev_dbg(&dev->interface->dev, "%s - cannot poll next modulation"
+ " because poll has been stopped", __func__);
+ goto stop_poll;
+ }
+
+ if (rc) {
+ dev_err(&dev->interface->dev, "%s - error %d when trying to"
+ " poll next modulation", __func__, rc);
+ goto stop_poll;
+ }
+
+ /* Inform caller function to do not up the semaphore */
+ return -EINPROGRESS;
+
+stop_poll:
+ pn533_poll_reset_mod_list(dev);
+ dev->poll_protocols = 0;
+ return 0;
+}
+
+static int pn533_start_poll(struct nfc_dev *nfc_dev, u32 protocols)
+{
+ struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+ struct pn533_poll_modulations *start_mod;
+ int rc;
+
+ dev_dbg(&dev->interface->dev, "%s - protocols=0x%x", __func__,
+ protocols);
+
+ if (dev->poll_mod_count) {
+ dev_err(&dev->interface->dev, "%s - "
+ "poll already active", __func__);
+ return -EBUSY;
+ }
+
+ if (dev->tgt_active_prot) {
+ dev_err(&dev->interface->dev, "%s - cannot poll with a target"
+ " already activated", __func__);
+ return -EBUSY;
+ }
+
+ pn533_poll_create_mod_list(dev, protocols);
+
+ if (!dev->poll_mod_count) {
+ dev_err(&dev->interface->dev, "%s - "
+ "no valid protocols specified", __func__);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ dev_dbg(&dev->interface->dev, "%s - poll %d modulations types",
+ __func__, dev->poll_mod_count);
+
+ dev->poll_mod_curr = 0;
+ start_mod = dev->poll_mod_active[dev->poll_mod_curr];
+
+ pn533_start_poll_frame(dev->out_frame, start_mod);
+
+ rc = pn533_send_cmd_frame_async(dev, dev->out_frame, dev->in_frame,
+ dev->in_maxlen, pn533_start_poll_complete,
+ NULL, GFP_KERNEL);
+
+ if (rc) {
+ dev_err(&dev->interface->dev, "%s - error %d when trying to"
+ " start poll", __func__, rc);
+ goto error;
+ }
+
+ dev->poll_protocols = protocols;
+
+ return 0;
+
+error:
+ pn533_poll_reset_mod_list(dev);
+ return rc;
+}
+
+static void pn533_stop_poll(struct nfc_dev *nfc_dev)
+{
+ struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+
+ dev_dbg(&dev->interface->dev, "%s", __func__);
+
+ if (!dev->poll_mod_count) {
+ dev_dbg(&dev->interface->dev, "%s - polling was not running",
+ __func__);
+ return;
+ }
+
+ /* An ack will cancel the last issued command (poll) */
+ pn533_send_ack(dev, GFP_KERNEL);
+
+ /* prevent pn533_start_poll_complete to issue a new poll meanwhile */
+ usb_kill_urb(dev->in_urb);
+}
+
+static int pn533_activate_target_nfcdep(struct pn533 *dev)
+{
+ struct pn533_cmd_activate_param param;
+ struct pn533_cmd_activate_response *resp;
+ int rc;
+
+ dev_dbg(&dev->interface->dev, "%s", __func__);
+
+ pn533_tx_frame_init(dev->out_frame, PN533_CMD_IN_ATR);
+
+ param.tg = 1;
+ param.next = 0;
+ memcpy(PN533_FRAME_CMD_PARAMS_PTR(dev->out_frame), &param,
+ sizeof(struct pn533_cmd_activate_param));
+ dev->out_frame->datalen += sizeof(struct pn533_cmd_activate_param);
+
+ pn533_tx_frame_finish(dev->out_frame);
+
+ rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame,
+ dev->in_maxlen);
+ if (rc)
+ return rc;
+
+ resp = (struct pn533_cmd_activate_response *)
+ PN533_FRAME_CMD_PARAMS_PTR(dev->in_frame);
+ rc = resp->status & PN533_CMD_RET_MASK;
+ if (rc != PN533_CMD_RET_SUCCESS)
+ return -EIO;
+
+ return 0;
+}
+
+static int pn533_activate_target(struct nfc_dev *nfc_dev, u32 target_idx,
+ u32 protocol)
+{
+ struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+ int rc;
+
+ dev_dbg(&dev->interface->dev, "%s", __func__);
+
+ if (dev->poll_mod_count) {
+ dev_err(&dev->interface->dev, "%s - Cannot activate while"
+ " polling", __func__);
+ return -EBUSY;
+ }
+
+ if (dev->tgt_active_prot) {
+ dev_err(&dev->interface->dev, "%s - There is a target already"
+ " activated", __func__);
+ return -EBUSY;
+ }
+
+ if (!dev->tgt_available_prots) {
+ dev_err(&dev->interface->dev, "%s - There is no target"
+ " available to be activated", __func__);
+ return -EINVAL;
+ }
+
+ if (!(dev->tgt_available_prots & (1 << protocol))) {
+ dev_err(&dev->interface->dev, "%s - The found target does not"
+ " support the requested protocol %u",
+ __func__, protocol);
+ return -EINVAL;
+ }
+
+ dev_dbg(&dev->interface->dev, "%s - Activating target with protocol"
+ " %u", __func__, protocol);
+
+ if (protocol == NFC_PROTO_NFC_DEP) {
+ rc = pn533_activate_target_nfcdep(dev);
+ if (rc) {
+ dev_err(&dev->interface->dev, "%s - Error %d when "
+ " activating target with NFC_DEP"
+ " protocol", __func__, rc);
+ return rc;
+ }
+ }
+
+ dev->tgt_active_prot = protocol;
+ dev->tgt_available_prots = 0;
+
+ return 0;
+}
+
+static void pn533_deactivate_target(struct nfc_dev *nfc_dev, u32 target_idx)
+{
+ struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+ u8 tg;
+ u8 status;
+ int rc;
+
+ dev_dbg(&dev->interface->dev, "%s", __func__);
+
+ if (!dev->tgt_active_prot) {
+ dev_err(&dev->interface->dev, "%s - There is no activated"
+ " target", __func__);
+ return;
+ }
+
+ dev_dbg(&dev->interface->dev, "%s - Deactivating target with protocol"
+ " %u", __func__, dev->tgt_active_prot);
+
+ dev->tgt_active_prot = 0;
+
+ pn533_tx_frame_init(dev->out_frame, PN533_CMD_IN_RELEASE);
+
+ tg = 1;
+ memcpy(PN533_FRAME_CMD_PARAMS_PTR(dev->out_frame), &tg, sizeof(u8));
+ dev->out_frame->datalen += sizeof(u8);
+
+ pn533_tx_frame_finish(dev->out_frame);
+
+ rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame,
+ dev->in_maxlen);
+ if (rc) {
+ dev_err(&dev->interface->dev, "%s - Error when sending release"
+ " command to the controller", __func__);
+ return;
+ }
+
+ status = PN533_FRAME_CMD_PARAMS_PTR(dev->in_frame)[0];
+ rc = status & PN533_CMD_RET_MASK;
+ if (rc != PN533_CMD_RET_SUCCESS)
+ dev_err(&dev->interface->dev, "%s - Error 0x%x when releasing"
+ " the target", __func__, rc);
+
+ return;
+}
+
+#define PN533_CMD_DATAEXCH_HEAD_LEN (sizeof(struct pn533_frame) + 3)
+#define PN533_CMD_DATAEXCH_DATA_MAXLEN 262
+
+static int pn533_data_exchange_tx_frame(struct pn533 *dev, struct sk_buff *skb)
+{
+ int payload_len = skb->len;
+ struct pn533_frame *out_frame;
+ struct sk_buff *discarded;
+ u8 tg;
+
+ dev_dbg(&dev->interface->dev, "%s - Sending %d bytes", __func__,
+ payload_len);
+
+ if (payload_len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
+ /* TODO: Implement support to multi-part data exchange */
+ dev_err(&dev->interface->dev, "%s - Data length greater than"
+ " the max allowed: %d", __func__,
+ PN533_CMD_DATAEXCH_DATA_MAXLEN);
+ return -ENOSYS;
+ }
+
+ /* Reserving header space */
+ if (skb_cow_head(skb, PN533_CMD_DATAEXCH_HEAD_LEN)) {
+ dev_err(&dev->interface->dev, "%s - Error to add header data",
+ __func__);
+ return -ENOMEM;
+ }
+
+ /* Reserving tail space, see pn533_tx_frame_finish */
+ if (skb_cow_data(skb, PN533_FRAME_TAIL_SIZE, &discarded) < 0) {
+ dev_err(&dev->interface->dev, "%s - Error to add tail data",
+ __func__);
+ return -ENOMEM;
+ }
+
+ skb_push(skb, PN533_CMD_DATAEXCH_HEAD_LEN);
+ out_frame = (struct pn533_frame *) skb->data;
+
+ pn533_tx_frame_init(out_frame, PN533_CMD_IN_DATA_EXCHANGE);
+
+ tg = 1;
+ memcpy(PN533_FRAME_CMD_PARAMS_PTR(out_frame), &tg, sizeof(u8));
+ out_frame->datalen += sizeof(u8);
+
+ /* The data is already in the out_frame, just update the datalen */
+ out_frame->datalen += payload_len;
+
+ pn533_tx_frame_finish(out_frame);
+ skb_put(skb, PN533_FRAME_TAIL_SIZE);
+
+ return 0;
+}
+
+struct pn533_data_exchange_arg {
+ struct sk_buff *skb_resp;
+ struct sk_buff *skb_out;
+ data_exchange_cb_t cb;
+ void *cb_context;
+};
+
+static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
+ u8 *params, int params_len)
+{
+ struct pn533_data_exchange_arg *arg = _arg;
+ struct sk_buff *skb_resp = arg->skb_resp;
+ struct pn533_frame *in_frame = (struct pn533_frame *) skb_resp->data;
+ int err = 0;
+ u8 status;
+ u8 cmd_ret;
+
+ dev_dbg(&dev->interface->dev, "%s", __func__);
+
+ dev_kfree_skb_irq(arg->skb_out);
+
+ if (params_len < 0) { /* error */
+ err = params_len;
+ goto error;
+ }
+
+ skb_put(skb_resp, PN533_FRAME_SIZE(in_frame));
+
+ status = params[0];
+
+ cmd_ret = status & PN533_CMD_RET_MASK;
+ if (cmd_ret != PN533_CMD_RET_SUCCESS) {
+ dev_err(&dev->interface->dev, "%s - PN533 reported error %d"
+ " when exchanging data", __func__, cmd_ret);
+ err = -EIO;
+ goto error;
+ }
+
+ if (status & PN533_CMD_MI_MASK) {
+ /* TODO: Implement support to multi-part data exchange */
+ dev_err(&dev->interface->dev, "%s - Multi-part message not"
+ " supported yet", __func__);
+ /* Prevent the other messages from controller */
+ pn533_send_ack(dev, GFP_ATOMIC);
+ err = -ENOSYS;
+ goto error;
+ }
+
+ skb_pull(skb_resp, PN533_CMD_DATAEXCH_HEAD_LEN);
+ skb_trim(skb_resp, skb_resp->len - PN533_FRAME_TAIL_SIZE);
+
+ arg->cb(arg->cb_context, skb_resp, 0);
+ kfree(arg);
+ return 0;
+
+error:
+ dev_kfree_skb_irq(skb_resp);
+ arg->cb(arg->cb_context, NULL, err);
+ kfree(arg);
+ return 0;
+}
+
+int pn533_data_exchange(struct nfc_dev *nfc_dev, u32 target_idx,
+ struct sk_buff *skb,
+ data_exchange_cb_t cb,
+ void *cb_context)
+{
+ struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+ struct pn533_frame *out_frame, *in_frame;
+ struct pn533_data_exchange_arg *arg;
+ struct sk_buff *skb_resp;
+ int skb_resp_len;
+ int rc;
+
+ dev_dbg(&dev->interface->dev, "%s", __func__);
+
+ if (!dev->tgt_active_prot) {
+ dev_err(&dev->interface->dev, "%s - Cannot exchange data with"
+ " no activated target", __func__);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ rc = pn533_data_exchange_tx_frame(dev, skb);
+ if (rc)
+ goto error;
+
+ skb_resp_len = PN533_CMD_DATAEXCH_HEAD_LEN +
+ PN533_CMD_DATAEXCH_DATA_MAXLEN +
+ PN533_FRAME_TAIL_SIZE;
+
+ skb_resp = nfc_alloc_skb(skb_resp_len, GFP_KERNEL);
+ if (!skb_resp) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ in_frame = (struct pn533_frame *) skb_resp->data;
+ out_frame = (struct pn533_frame *) skb->data;
+
+ arg = kmalloc(sizeof(struct pn533_data_exchange_arg), GFP_KERNEL);
+ if (!arg) {
+ rc = -ENOMEM;
+ goto free_skb_resp;
+ }
+
+ arg->skb_resp = skb_resp;
+ arg->skb_out = skb;
+ arg->cb = cb;
+ arg->cb_context = cb_context;
+
+ rc = pn533_send_cmd_frame_async(dev, out_frame, in_frame, skb_resp_len,
+ pn533_data_exchange_complete, arg,
+ GFP_KERNEL);
+ if (rc) {
+ dev_err(&dev->interface->dev, "%s - error %d when trying to"
+ " perform data_exchange", __func__, rc);
+ goto free_arg;
+ }
+
+ return 0;
+
+free_arg:
+ kfree(arg);
+free_skb_resp:
+ kfree_skb(skb_resp);
+error:
+ kfree_skb(skb);
+ return rc;
+}
+
+static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata,
+ u8 cfgdata_len)
+{
+ int rc;
+ u8 *params;
+
+ pn533_tx_frame_init(dev->out_frame, PN533_CMD_RF_CONFIGURATION);
+
+ params = PN533_FRAME_CMD_PARAMS_PTR(dev->out_frame);
+ params[0] = cfgitem;
+ memcpy(&params[1], cfgdata, cfgdata_len);
+ dev->out_frame->datalen += (1 + cfgdata_len);
+
+ pn533_tx_frame_finish(dev->out_frame);
+
+ rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame,
+ dev->in_maxlen);
+
+ return rc;
+}
+
+struct nfc_ops pn533_nfc_ops = {
+ .start_poll = pn533_start_poll,
+ .stop_poll = pn533_stop_poll,
+ .activate_target = pn533_activate_target,
+ .deactivate_target = pn533_deactivate_target,
+ .data_exchange = pn533_data_exchange,
+};
+
+static int pn533_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct pn533_fw_version *fw_ver;
+ struct pn533 *dev;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ struct pn533_config_max_retries max_retries;
+ int in_endpoint = 0;
+ int out_endpoint = 0;
+ int rc = -ENOMEM;
+ int i;
+ u32 protocols;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->udev = usb_get_dev(interface_to_usbdev(interface));
+ dev->interface = interface;
+ sema_init(&dev->cmd_lock, 1);
+
+ iface_desc = interface->cur_altsetting;
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ if (!in_endpoint && usb_endpoint_is_bulk_in(endpoint)) {
+ dev->in_maxlen = le16_to_cpu(endpoint->wMaxPacketSize);
+ in_endpoint = endpoint->bEndpointAddress;
+ }
+
+ if (!out_endpoint && usb_endpoint_is_bulk_out(endpoint)) {
+ dev->out_maxlen =
+ le16_to_cpu(endpoint->wMaxPacketSize);
+ out_endpoint = endpoint->bEndpointAddress;
+ }
+ }
+
+ if (!in_endpoint || !out_endpoint) {
+ dev_err(&interface->dev, "Could not find bulk-in or "
+ "bulk-out endpoint");
+ rc = -ENODEV;
+ goto error;
+ }
+
+ dev->in_frame = kmalloc(dev->in_maxlen, GFP_KERNEL);
+ dev->in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ dev->out_frame = kmalloc(dev->out_maxlen, GFP_KERNEL);
+ dev->out_urb = usb_alloc_urb(0, GFP_KERNEL);
+
+ if (!dev->in_frame || !dev->out_frame ||
+ !dev->in_urb || !dev->out_urb)
+ goto error;
+
+ usb_fill_bulk_urb(dev->in_urb, dev->udev,
+ usb_rcvbulkpipe(dev->udev, in_endpoint),
+ NULL, 0, NULL, dev);
+ usb_fill_bulk_urb(dev->out_urb, dev->udev,
+ usb_sndbulkpipe(dev->udev, out_endpoint),
+ NULL, 0,
+ pn533_send_complete, dev);
+
+ tasklet_init(&dev->tasklet, pn533_tasklet_cmd_complete, (ulong)dev);
+
+ usb_set_intfdata(interface, dev);
+
+ pn533_tx_frame_init(dev->out_frame, PN533_CMD_GET_FIRMWARE_VERSION);
+ pn533_tx_frame_finish(dev->out_frame);
+
+ rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame,
+ dev->in_maxlen);
+ if (rc)
+ goto kill_tasklet;
+
+ fw_ver = (struct pn533_fw_version *)
+ PN533_FRAME_CMD_PARAMS_PTR(dev->in_frame);
+ dev_info(&interface->dev, "NXP PN533 firmware ver %d.%d now attached",
+ fw_ver->ver, fw_ver->rev);
+
+ protocols = NFC_PROTO_JEWEL_MASK
+ | NFC_PROTO_MIFARE_MASK | NFC_PROTO_FELICA_MASK
+ | NFC_PROTO_ISO14443_MASK
+ | NFC_PROTO_NFC_DEP_MASK;
+
+ dev->nfc_dev = nfc_allocate_device(&pn533_nfc_ops, protocols);
+ if (!dev->nfc_dev)
+ goto kill_tasklet;
+
+ nfc_set_parent_dev(dev->nfc_dev, &interface->dev);
+ nfc_set_drvdata(dev->nfc_dev, dev);
+
+ rc = nfc_register_device(dev->nfc_dev);
+ if (rc)
+ goto free_nfc_dev;
+
+ max_retries.mx_rty_atr = PN533_CONFIG_MAX_RETRIES_ENDLESS;
+ max_retries.mx_rty_psl = 2;
+ max_retries.mx_rty_passive_act = PN533_CONFIG_MAX_RETRIES_NO_RETRY;
+
+ rc = pn533_set_configuration(dev, PN533_CFGITEM_MAX_RETRIES,
+ (u8 *) &max_retries, sizeof(max_retries));
+
+ if (rc) {
+ dev_err(&interface->dev, "Error on setting MAX_RETRIES "
+ "config");
+ goto free_nfc_dev;
+ }
+
+ return 0;
+
+free_nfc_dev:
+ nfc_free_device(dev->nfc_dev);
+kill_tasklet:
+ tasklet_kill(&dev->tasklet);
+error:
+ kfree(dev->in_frame);
+ usb_free_urb(dev->in_urb);
+ kfree(dev->out_frame);
+ usb_free_urb(dev->out_urb);
+ kfree(dev);
+ return rc;
+}
+
+static void pn533_disconnect(struct usb_interface *interface)
+{
+ struct pn533 *dev;
+
+ dev = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ nfc_unregister_device(dev->nfc_dev);
+ nfc_free_device(dev->nfc_dev);
+
+ usb_kill_urb(dev->in_urb);
+ usb_kill_urb(dev->out_urb);
+
+ tasklet_kill(&dev->tasklet);
+
+ kfree(dev->in_frame);
+ usb_free_urb(dev->in_urb);
+ kfree(dev->out_frame);
+ usb_free_urb(dev->out_urb);
+ kfree(dev);
+
+ dev_info(&interface->dev, "NXP PN533 NFC device disconnected");
+}
+
+static struct usb_driver pn533_driver = {
+ .name = "pn533",
+ .probe = pn533_probe,
+ .disconnect = pn533_disconnect,
+ .id_table = pn533_table,
+};
+
+static int __init pn533_init(void)
+{
+ int rc;
+
+ rc = usb_register(&pn533_driver);
+ if (rc)
+ err("usb_register failed. Error number %d", rc);
+
+ return rc;
+}
+
+static void __exit pn533_exit(void)
+{
+ usb_deregister(&pn533_driver);
+}
+
+module_init(pn533_init);
+module_exit(pn533_exit);
+
+MODULE_AUTHOR("Lauro Ramos Venancio <[email protected]>");
+MODULE_DESCRIPTION("PN533 usb driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
--
1.7.1


2011-06-03 13:44:24

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH 2/6] NFC: add nfc generic netlink interface

On Thu, 2011-06-02 at 18:46 -0300, Lauro Ramos Venancio wrote:

> + * @NFC_ATTR_TARGETS: array of targets (see enum nfc_target_attr)


> +static int nfc_genl_msg_put_target(struct sk_buff *msg,
> + struct nfc_target *target)
> +{
> + NLA_PUT_U32(msg, NFC_TARGET_ATTR_TARGET_INDEX, target->idx);
> + NLA_PUT_U32(msg, NFC_TARGET_ATTR_SUPPORTED_PROTOCOLS,
> + target->supported_protocols);
> + NLA_PUT_U8(msg, NFC_TARGET_ATTR_SENS_RES, target->sens_res);
> + NLA_PUT_U8(msg, NFC_TARGET_ATTR_SEL_RES, target->sel_res);
> +
> + return 0;
> +
> +nla_put_failure:
> + return -EMSGSIZE;
> +}
> +
> +int nfc_genl_targets_found(struct nfc_dev *dev, struct nfc_target *targets,
> + int ntargets)
> +{
> + struct sk_buff *msg;
> + void *hdr;
> + struct nlattr *targets_attr;
> + int i;
> +
> + pr_debug("%s\n", __func__);
> +
> + dev->genl_data.poll_req_pid = 0;
> +
> + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
> + if (!msg)
> + return -ENOMEM;
> +
> + hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
> + NFC_EVENT_TARGETS_FOUND);
> + if (!hdr)
> + goto free_msg;
> +
> + NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx);
> +
> + targets_attr = nla_nest_start(msg, NFC_ATTR_TARGETS);
> +
> + for (i = 0; i < ntargets; i++) {
> + struct nlattr *target = nla_nest_start(msg, i);
> +
> + if (nfc_genl_msg_put_target(msg, &targets[i]))
> + goto nla_put_failure;
> +
> + nla_nest_end(msg, target);
> + }
> +
> + nla_nest_end(msg, targets_attr);
> + genlmsg_end(msg, hdr);
> +
> + return genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_ATOMIC);

This is almost certainly not a good idea.

Eventually, you will want to add more information about a target. I know
NFC is a pretty limited protocol, but I wouldn't want to make this
harder in the future than it must be. The way you're doing it, you're
currently limiting yourself to about 100 targets. Each new target
attribute that you might add in the future will reduce that number
significantly.

IMHO, the better way to structure this would be to create an event that
contains no information, and then allow a dump of the targets (with a
generation counter). That way, there are no such artificial limits due
to message sizes.


> +static int nfc_genl_dump_devices(struct sk_buff *skb,
> + struct netlink_callback *cb)

You should have a generation counter here so that applications getting a
dump can know whether their dump was a complete and consistent snapshot.
Otherwise, if devices are added or removed during the dump applications
will not be able to know that their dump wasn't right.

johannes


2011-06-03 21:36:06

by Aloisio Almeida

[permalink] [raw]
Subject: Re: [PATCH 2/6] NFC: add nfc generic netlink interface

On Fri, Jun 3, 2011 at 5:38 PM, Johannes Berg <[email protected]> wrote:
> On Fri, 2011-06-03 at 17:18 -0300, Lauro Ramos Venancio wrote:
>> We don't need a generation counter here because we have the events
>> NFC_EVENT_DEVICE_ADDED and NFC_EVENT_DEVICE_REMOVED. So, it is
>> possible to keep the device list consistency listening for these
>> events.
>
> That may be true, but it complicates coding in userspace. Much easier to
> check a generation counter (in both cases) to see if data is consistent
> than to listen to multiple events concurrently with receiving a dump.

The userspace must listen to these events anyway. In addition, the
application needs to get the list of devices only once (on its
initialization).

In the target list case, the userspace application would have to
orderly call start_poll before receive the dump in order to create the
"invalid list" situation.

I understand the benefits of the generation counters, but IMO they are
'too much' in these cases.

Aloisio

2011-06-06 16:20:50

by Samuel Ortiz

[permalink] [raw]
Subject: Re: [PATCH 2/6] NFC: add nfc generic netlink interface

Hi Lauro,

On Fri, Jun 03, 2011 at 05:18:26PM -0300, Lauro Ramos Venancio wrote:
> > IMHO, the better way to structure this would be to create an event that
> > contains no information, and then allow a dump of the targets (with a
> > generation counter). That way, there are no such artificial limits due
> > to message sizes.
>
> I agree that this is a better solution. But I think we don't need a
> generation counter because only a new polling operation (start_poll
> call) can change the targets list (i.e. there is no passive polling).
I think we agree on the fact that going through a target list dump would be a
better solution. Then if we do so we need to provide some sort of consistency
check, even though NFC doesn't define any sort of passive polling. One example
would be an NFC daemon running and getting the target list while a command
line tool could initiate a target poll.


> >> +static int nfc_genl_dump_devices(struct sk_buff *skb,
> >> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct netlink_callback *cb)
> >
> > You should have a generation counter here so that applications getting a
> > dump can know whether their dump was a complete and consistent snapshot.
> > Otherwise, if devices are added or removed during the dump applications
> > will not be able to know that their dump wasn't right.
> >
>
> We don't need a generation counter here because we have the events
> NFC_EVENT_DEVICE_ADDED and NFC_EVENT_DEVICE_REMOVED. So, it is
> possible to keep the device list consistency listening for these
> events.
I agree with you here we can keep it consistent by listening to these
events. But if we provide the dump hook, we need to add an additional
consistency check even though this kind of race is much less likely to happen
than in the target case (devices don't just show up on your NFC enabled phone).

Cheers,
Samuel.

--
Intel Open Source Technology Centre
http://oss.intel.com/

2011-06-03 20:38:32

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH 2/6] NFC: add nfc generic netlink interface

On Fri, 2011-06-03 at 17:18 -0300, Lauro Ramos Venancio wrote:

> Actually, we don't expect more than a couple of targets because of NFC
> short range.

I know.

> I agree that this can be a problem if we start supporting vicinity cards.

I have no idea what that is, but I think that you'll start adding
attributes at some point, and somebody will also build "scanners" with
better RX sensitivity ... I'd rather not design something that _right
now_ limits you to around 100 targets.

> I agree that this is a better solution. But I think we don't need a
> generation counter because only a new polling operation (start_poll
> call) can change the targets list (i.e. there is no passive polling).


> We don't need a generation counter here because we have the events
> NFC_EVENT_DEVICE_ADDED and NFC_EVENT_DEVICE_REMOVED. So, it is
> possible to keep the device list consistency listening for these
> events.

That may be true, but it complicates coding in userspace. Much easier to
check a generation counter (in both cases) to see if data is consistent
than to listen to multiple events concurrently with receiving a dump.

johannes


2011-06-03 13:48:40

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH 1/6] NFC: add nfc subsystem core

On Fri, 2011-06-03 at 10:45 -0300, Aloisio Almeida wrote:
> On Fri, Jun 3, 2011 at 10:35 AM, Johannes Berg
> <[email protected]> wrote:
> > On Thu, 2011-06-02 at 18:46 -0300, Lauro Ramos Venancio wrote:
> >
> >> +struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
> >> + u32 supported_protocols);
> >
> > Where do the protocols come from? I don't see any defines for them?
>
> The defines are on the patch 2/6 (generic netlink): NFC_PROTO_*

Yeah, just found them too, thanks.

johannes


2011-06-02 21:47:26

by Lauro Ramos Venancio

[permalink] [raw]
Subject: [PATCH 4/6] NFC: add the NFC socket raw protocol

This socket protocol is used to perform data exchange with NFC
targets.

Signed-off-by: Lauro Ramos Venancio <[email protected]>
Signed-off-by: Aloisio Almeida Jr <[email protected]>
Signed-off-by: Samuel Ortiz <[email protected]>
---
include/linux/nfc.h | 13 ++-
net/nfc/Makefile | 2 +-
net/nfc/core.c | 7 +
net/nfc/nfc.h | 14 ++
net/nfc/rawsock.c | 351 +++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 385 insertions(+), 2 deletions(-)
create mode 100644 net/nfc/rawsock.c

diff --git a/include/linux/nfc.h b/include/linux/nfc.h
index 7361f80..a62e232 100644
--- a/include/linux/nfc.h
+++ b/include/linux/nfc.h
@@ -24,6 +24,9 @@
#ifndef __LINUX_NFC_H
#define __LINUX_NFC_H

+#include <linux/types.h>
+#include <linux/socket.h>
+
#define NFC_GENL_NAME "nfc"
#define NFC_GENL_VERSION 1

@@ -119,7 +122,15 @@ enum nfc_target_attr {
};
#define NFC_TARGET_ATTR_MAX (__NFC_TARGET_ATTR_AFTER_LAST - 1)

+struct sockaddr_nfc {
+ sa_family_t sa_family;
+ __u32 dev_idx;
+ __u32 target_idx;
+ __u32 nfc_protocol;
+};
+
/* NFC socket protocols */
-#define NFC_SOCKPROTO_MAX 0
+#define NFC_SOCKPROTO_RAW 0
+#define NFC_SOCKPROTO_MAX 1

#endif /*__LINUX_NFC_H */
diff --git a/net/nfc/Makefile b/net/nfc/Makefile
index c65add7..26996e0 100644
--- a/net/nfc/Makefile
+++ b/net/nfc/Makefile
@@ -4,6 +4,6 @@

obj-$(CONFIG_NFC) += nfc.o

-nfc-objs := core.o netlink.o af_nfc.o
+nfc-objs := core.o netlink.o af_nfc.o rawsock.o

ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
diff --git a/net/nfc/core.c b/net/nfc/core.c
index d4d2ecc..f045e0c 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -366,6 +366,10 @@ static int __init nfc_init(void)
if (rc)
goto err_genl;

+ rc = rawsock_init();
+ if (rc)
+ goto err_rawsock;
+
rc = af_nfc_init();
if (rc)
goto err_af_nfc;
@@ -373,6 +377,8 @@ static int __init nfc_init(void)
return 0;

err_af_nfc:
+ rawsock_exit();
+err_rawsock:
nfc_genl_exit();
err_genl:
class_unregister(&nfc_class);
@@ -383,6 +389,7 @@ err:
static void __exit nfc_exit(void)
{
af_nfc_exit();
+ rawsock_exit();
nfc_genl_exit();
class_unregister(&nfc_class);
}
diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h
index 76b891f..1a86641 100644
--- a/net/nfc/nfc.h
+++ b/net/nfc/nfc.h
@@ -35,6 +35,20 @@ struct nfc_protocol {
const struct nfc_protocol *nfc_proto);
};

+struct nfc_rawsock {
+ struct sock sk;
+ struct nfc_dev *dev;
+ u32 target_idx;
+ struct work_struct tx_work;
+ bool tx_work_scheduled;
+};
+#define nfc_rawsock(sk) ((struct nfc_rawsock *) sk)
+#define to_rawsock_sk(_tx_work) \
+ ((struct sock *) container_of(_tx_work, struct nfc_rawsock, tx_work))
+
+int __init rawsock_init(void);
+void rawsock_exit(void);
+
int __init af_nfc_init(void);
void af_nfc_exit(void);
int nfc_proto_register(const struct nfc_protocol *nfc_proto);
diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c
new file mode 100644
index 0000000..87ec5e2
--- /dev/null
+++ b/net/nfc/rawsock.c
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ *
+ * Authors:
+ * Aloisio Almeida Jr <[email protected]>
+ * Lauro Ramos Venancio <[email protected]>
+ *
+ * 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.
+ */
+
+#include <net/tcp_states.h>
+#include <linux/nfc.h>
+
+#include "nfc.h"
+
+static void rawsock_write_queue_purge(struct sock *sk)
+{
+ pr_debug("%s\n", __func__);
+
+ spin_lock_bh(&sk->sk_write_queue.lock);
+ __skb_queue_purge(&sk->sk_write_queue);
+ nfc_rawsock(sk)->tx_work_scheduled = false;
+ spin_unlock_bh(&sk->sk_write_queue.lock);
+}
+
+static void rawsock_report_error(struct sock *sk, int err)
+{
+ pr_debug("%s\n", __func__);
+
+ sk->sk_shutdown = SHUTDOWN_MASK;
+ sk->sk_err = -err;
+ sk->sk_error_report(sk);
+
+ rawsock_write_queue_purge(sk);
+}
+
+static int rawsock_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ pr_debug("%s\n", __func__);
+
+ sock_orphan(sk);
+ sock_put(sk);
+
+ return 0;
+}
+
+static int rawsock_connect(struct socket *sock, struct sockaddr *_addr,
+ int len, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct sockaddr_nfc *addr = (struct sockaddr_nfc *)_addr;
+ struct nfc_dev *dev;
+ int rc = 0;
+
+ pr_debug("%s\n", __func__);
+
+ if (!addr || len < sizeof(struct sockaddr_nfc) ||
+ addr->sa_family != AF_NFC)
+ return -EINVAL;
+
+ lock_sock(sk);
+
+ if (sock->state == SS_CONNECTED) {
+ rc = -EISCONN;
+ goto error;
+ }
+
+ dev = nfc_get_device(addr->dev_idx);
+ if (!dev) {
+ rc = -ENODEV;
+ goto error;
+ }
+
+ if (addr->target_idx > dev->target_idx - 1 ||
+ addr->target_idx < dev->target_idx - dev->n_targets) {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ if (addr->target_idx > dev->target_idx - 1 ||
+ addr->target_idx < dev->target_idx - dev->n_targets) {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ rc = nfc_activate_target(dev, addr->target_idx, addr->nfc_protocol);
+ if (rc)
+ goto put_dev;
+
+ nfc_rawsock(sk)->dev = dev;
+ nfc_rawsock(sk)->target_idx = addr->target_idx;
+ sock->state = SS_CONNECTED;
+ sk->sk_state = TCP_ESTABLISHED;
+ sk->sk_state_change(sk);
+
+ release_sock(sk);
+ return 0;
+
+put_dev:
+ nfc_put_device(dev);
+error:
+ release_sock(sk);
+ return rc;
+}
+
+static int rawsock_add_header(struct sk_buff *skb)
+{
+
+ if (skb_cow_head(skb, 1))
+ return -ENOMEM;
+
+ *skb_push(skb, 1) = 0;
+
+ return 0;
+}
+
+static void rawsock_data_exchange_complete(void *context, struct sk_buff *skb,
+ int err)
+{
+ struct sock *sk = (struct sock *) context;
+
+ BUG_ON(in_irq());
+
+ if (err)
+ goto error;
+
+ err = rawsock_add_header(skb);
+ if (err)
+ goto error;
+
+ err = sock_queue_rcv_skb(sk, skb);
+ if (err)
+ goto error;
+
+ spin_lock_bh(&sk->sk_write_queue.lock);
+ if (!skb_queue_empty(&sk->sk_write_queue))
+ schedule_work(&nfc_rawsock(sk)->tx_work);
+ else
+ nfc_rawsock(sk)->tx_work_scheduled = false;
+ spin_unlock_bh(&sk->sk_write_queue.lock);
+
+ sock_put(sk);
+ return;
+
+error:
+ rawsock_report_error(sk, err);
+ sock_put(sk);
+}
+
+static void rawsock_tx_work(struct work_struct *work)
+{
+ struct sock *sk = to_rawsock_sk(work);
+ struct nfc_dev *dev = nfc_rawsock(sk)->dev;
+ u32 target_idx = nfc_rawsock(sk)->target_idx;
+ struct sk_buff *skb;
+ int rc;
+
+ if (sk->sk_shutdown & SEND_SHUTDOWN) {
+ rawsock_write_queue_purge(sk);
+ return;
+ }
+
+ skb = skb_dequeue(&sk->sk_write_queue);
+
+ sock_hold(sk);
+ rc = nfc_data_exchange(dev, target_idx, skb,
+ rawsock_data_exchange_complete, sk);
+ if (rc) {
+ rawsock_report_error(sk, rc);
+ sock_put(sk);
+ }
+}
+
+static int rawsock_sendmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t len)
+{
+ struct sock *sk = sock->sk;
+ struct sk_buff *skb;
+ int rc;
+
+ pr_debug("%s\n", __func__);
+
+ if (msg->msg_namelen)
+ return -EOPNOTSUPP;
+
+ if (sock->state != SS_CONNECTED)
+ return -ENOTCONN;
+
+ skb = sock_alloc_send_skb(sk, len, msg->msg_flags & MSG_DONTWAIT,
+ &rc);
+ if (!skb)
+ return rc;
+
+ rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
+ if (rc < 0) {
+ kfree_skb(skb);
+ return rc;
+ }
+
+ spin_lock_bh(&sk->sk_write_queue.lock);
+ __skb_queue_tail(&sk->sk_write_queue, skb);
+ if (!nfc_rawsock(sk)->tx_work_scheduled) {
+ schedule_work(&nfc_rawsock(sk)->tx_work);
+ nfc_rawsock(sk)->tx_work_scheduled = true;
+ }
+ spin_unlock_bh(&sk->sk_write_queue.lock);
+
+ return len;
+}
+
+static int rawsock_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t len, int flags)
+{
+ int noblock = flags & MSG_DONTWAIT;
+ struct sock *sk = sock->sk;
+ struct sk_buff *skb;
+ int copied;
+ int rc;
+
+ pr_debug("%s\n", __func__);
+
+ skb = skb_recv_datagram(sk, flags, noblock, &rc);
+ if (!skb)
+ return rc;
+
+ msg->msg_namelen = 0;
+
+ copied = skb->len;
+ if (len < copied) {
+ msg->msg_flags |= MSG_TRUNC;
+ copied = len;
+ }
+
+ rc = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+
+ skb_free_datagram(sk, skb);
+
+ return rc ? : copied;
+}
+
+
+static const struct proto_ops rawsock_ops = {
+ .family = PF_NFC,
+ .owner = THIS_MODULE,
+ .release = rawsock_release,
+ .bind = sock_no_bind,
+ .connect = rawsock_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = sock_no_getname,
+ .poll = datagram_poll,
+ .ioctl = sock_no_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_no_setsockopt,
+ .getsockopt = sock_no_getsockopt,
+ .sendmsg = rawsock_sendmsg,
+ .recvmsg = rawsock_recvmsg,
+ .mmap = sock_no_mmap,
+};
+
+static void rawsock_destruct(struct sock *sk)
+{
+ pr_debug("%s\n", __func__);
+
+ if (sk->sk_state == TCP_ESTABLISHED) {
+ nfc_deactivate_target(nfc_rawsock(sk)->dev,
+ nfc_rawsock(sk)->target_idx);
+ nfc_put_device(nfc_rawsock(sk)->dev);
+ }
+
+ skb_queue_purge(&sk->sk_receive_queue);
+
+ if (!sock_flag(sk, SOCK_DEAD)) {
+ printk(KERN_ERR "Freeing alive NFC raw socket %p\n", sk);
+ return;
+ }
+}
+
+static int rawsock_create(struct net *net, struct socket *sock,
+ const struct nfc_protocol *nfc_proto)
+{
+ struct sock *sk;
+
+ pr_debug("%s\n", __func__);
+
+ if (sock->type != SOCK_SEQPACKET)
+ return -ESOCKTNOSUPPORT;
+
+ sock->ops = &rawsock_ops;
+
+ sk = sk_alloc(net, PF_NFC, GFP_KERNEL, nfc_proto->proto);
+ if (!sk)
+ return -ENOMEM;
+
+ sock_init_data(sock, sk);
+ sk->sk_protocol = nfc_proto->id;
+ sk->sk_destruct = rawsock_destruct;
+ sock->state = SS_UNCONNECTED;
+
+ INIT_WORK(&nfc_rawsock(sk)->tx_work, rawsock_tx_work);
+ nfc_rawsock(sk)->tx_work_scheduled = false;
+
+ return 0;
+}
+
+static struct proto rawsock_proto = {
+ .name = "NFC_RAW",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct nfc_rawsock),
+};
+
+static const struct nfc_protocol rawsock_nfc_proto = {
+ .id = NFC_SOCKPROTO_RAW,
+ .proto = &rawsock_proto,
+ .owner = THIS_MODULE,
+ .create = rawsock_create
+};
+
+int __init rawsock_init(void)
+{
+ int rc;
+
+ pr_debug("%s\n", __func__);
+
+ rc = nfc_proto_register(&rawsock_nfc_proto);
+
+ return rc;
+}
+
+void rawsock_exit(void)
+{
+ pr_debug("%s\n", __func__);
+
+ nfc_proto_unregister(&rawsock_nfc_proto);
+}
--
1.7.1


2011-06-17 17:07:46

by Samuel Ortiz

[permalink] [raw]
Subject: Re: [PATCH 0/6] NFC subsystem

Hi John,

On Fri, Jun 17, 2011 at 12:45:11PM -0400, John W. Linville wrote:
> On Thu, Jun 02, 2011 at 06:46:04PM -0300, Lauro Ramos Venancio wrote:
> > This series of patches implements the first part of the Near Field
> > Communication (NFC) subsystem.
>
> Will you be submitting a new series to account for the comments from
> Johannes and others?
Yes, v2 should hit your inbox quite soon.

Cheers,
Samuel.


> John
> --
> John W. Linville Someday the world will need a hero, and you
> [email protected] might be all we have. Be ready.

2011-06-06 08:01:30

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH 2/6] NFC: add nfc generic netlink interface

Lauro Ramos Venancio <[email protected]> writes:

>> This is almost certainly not a good idea.
>>
>> Eventually, you will want to add more information about a target. I know
>> NFC is a pretty limited protocol, but I wouldn't want to make this
>> harder in the future than it must be. The way you're doing it, you're
>> currently limiting yourself to about 100 targets. Each new target
>> attribute that you might add in the future will reduce that number
>> significantly.
>
> Actually, we don't expect more than a couple of targets because of
> NFC short range.

This reminds me of Bill Gate's "640K ought to be enough for anybody"
quote (or misattribution, based on wikipedia). Better not to make any
assumptions based on the current state, instead make it extensible
enough so that the interface doesn't need to be redesigned in the next
few years.

--
Kalle Valo

2011-06-03 20:18:29

by Lauro Ramos Venancio

[permalink] [raw]
Subject: Re: [PATCH 2/6] NFC: add nfc generic netlink interface

Hi Johannes,

2011/6/3 Johannes Berg <[email protected]>:
> On Thu, 2011-06-02 at 18:46 -0300, Lauro Ramos Venancio wrote:
>
>> + * @NFC_ATTR_TARGETS: array of targets (see enum nfc_target_attr)
>
>
>> +static int nfc_genl_msg_put_target(struct sk_buff *msg,
>> +                             struct nfc_target *target)
>> +{
>> +     NLA_PUT_U32(msg, NFC_TARGET_ATTR_TARGET_INDEX, target->idx);
>> +     NLA_PUT_U32(msg, NFC_TARGET_ATTR_SUPPORTED_PROTOCOLS,
>> +                             target->supported_protocols);
>> +     NLA_PUT_U8(msg, NFC_TARGET_ATTR_SENS_RES, target->sens_res);
>> +     NLA_PUT_U8(msg, NFC_TARGET_ATTR_SEL_RES, target->sel_res);
>> +
>> +     return 0;
>> +
>> +nla_put_failure:
>> +     return -EMSGSIZE;
>> +}
>> +
>> +int nfc_genl_targets_found(struct nfc_dev *dev, struct nfc_target *targets,
>> +                                                             int ntargets)
>> +{
>> +     struct sk_buff *msg;
>> +     void *hdr;
>> +     struct nlattr *targets_attr;
>> +     int i;
>> +
>> +     pr_debug("%s\n", __func__);
>> +
>> +     dev->genl_data.poll_req_pid = 0;
>> +
>> +     msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
>> +     if (!msg)
>> +             return -ENOMEM;
>> +
>> +     hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
>> +                             NFC_EVENT_TARGETS_FOUND);
>> +     if (!hdr)
>> +             goto free_msg;
>> +
>> +     NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx);
>> +
>> +     targets_attr = nla_nest_start(msg, NFC_ATTR_TARGETS);
>> +
>> +     for (i = 0; i < ntargets; i++) {
>> +             struct nlattr *target = nla_nest_start(msg, i);
>> +
>> +             if (nfc_genl_msg_put_target(msg, &targets[i]))
>> +                     goto nla_put_failure;
>> +
>> +             nla_nest_end(msg, target);
>> +     }
>> +
>> +     nla_nest_end(msg, targets_attr);
>> +     genlmsg_end(msg, hdr);
>> +
>> +     return genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_ATOMIC);
>
> This is almost certainly not a good idea.
>
> Eventually, you will want to add more information about a target. I know
> NFC is a pretty limited protocol, but I wouldn't want to make this
> harder in the future than it must be. The way you're doing it, you're
> currently limiting yourself to about 100 targets. Each new target
> attribute that you might add in the future will reduce that number
> significantly.

Actually, we don't expect more than a couple of targets because of NFC
short range.
I agree that this can be a problem if we start supporting vicinity cards.

> IMHO, the better way to structure this would be to create an event that
> contains no information, and then allow a dump of the targets (with a
> generation counter). That way, there are no such artificial limits due
> to message sizes.

I agree that this is a better solution. But I think we don't need a
generation counter because only a new polling operation (start_poll
call) can change the targets list (i.e. there is no passive polling).

>> +static int nfc_genl_dump_devices(struct sk_buff *skb,
>> +                               struct netlink_callback *cb)
>
> You should have a generation counter here so that applications getting a
> dump can know whether their dump was a complete and consistent snapshot.
> Otherwise, if devices are added or removed during the dump applications
> will not be able to know that their dump wasn't right.
>

We don't need a generation counter here because we have the events
NFC_EVENT_DEVICE_ADDED and NFC_EVENT_DEVICE_REMOVED. So, it is
possible to keep the device list consistency listening for these
events.


Lauro

2011-06-02 21:47:22

by Lauro Ramos Venancio

[permalink] [raw]
Subject: [PATCH 3/6] NFC: add NFC socket family

From: Aloisio Almeida Jr <[email protected]>

Signed-off-by: Lauro Ramos Venancio <[email protected]>
Signed-off-by: Aloisio Almeida Jr <[email protected]>
---
include/linux/nfc.h | 3 +
include/linux/socket.h | 4 +-
net/core/sock.c | 6 +-
net/nfc/Makefile | 2 +-
net/nfc/af_nfc.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++
net/nfc/core.c | 7 +++
net/nfc/nfc.h | 14 +++++++
7 files changed, 129 insertions(+), 5 deletions(-)
create mode 100644 net/nfc/af_nfc.c

diff --git a/include/linux/nfc.h b/include/linux/nfc.h
index be8fc88..7361f80 100644
--- a/include/linux/nfc.h
+++ b/include/linux/nfc.h
@@ -119,4 +119,7 @@ enum nfc_target_attr {
};
#define NFC_TARGET_ATTR_MAX (__NFC_TARGET_ATTR_AFTER_LAST - 1)

+/* NFC socket protocols */
+#define NFC_SOCKPROTO_MAX 0
+
#endif /*__LINUX_NFC_H */
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 4ef98e4..e17f822 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -192,7 +192,8 @@ struct ucred {
#define AF_IEEE802154 36 /* IEEE802154 sockets */
#define AF_CAIF 37 /* CAIF sockets */
#define AF_ALG 38 /* Algorithm sockets */
-#define AF_MAX 39 /* For now.. */
+#define AF_NFC 39 /* NFC sockets */
+#define AF_MAX 40 /* For now.. */

/* Protocol families, same as address families. */
#define PF_UNSPEC AF_UNSPEC
@@ -234,6 +235,7 @@ struct ucred {
#define PF_IEEE802154 AF_IEEE802154
#define PF_CAIF AF_CAIF
#define PF_ALG AF_ALG
+#define PF_NFC AF_NFC
#define PF_MAX AF_MAX

/* Maximum queue length specifiable by listen. */
diff --git a/net/core/sock.c b/net/core/sock.c
index 6e81978..84d6de8 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -158,7 +158,7 @@ static const char *const af_family_key_strings[AF_MAX+1] = {
"sk_lock-AF_TIPC" , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV" ,
"sk_lock-AF_RXRPC" , "sk_lock-AF_ISDN" , "sk_lock-AF_PHONET" ,
"sk_lock-AF_IEEE802154", "sk_lock-AF_CAIF" , "sk_lock-AF_ALG" ,
- "sk_lock-AF_MAX"
+ "sk_lock-AF_NFC" , "sk_lock-AF_MAX"
};
static const char *const af_family_slock_key_strings[AF_MAX+1] = {
"slock-AF_UNSPEC", "slock-AF_UNIX" , "slock-AF_INET" ,
@@ -174,7 +174,7 @@ static const char *const af_family_slock_key_strings[AF_MAX+1] = {
"slock-AF_TIPC" , "slock-AF_BLUETOOTH", "slock-AF_IUCV" ,
"slock-AF_RXRPC" , "slock-AF_ISDN" , "slock-AF_PHONET" ,
"slock-AF_IEEE802154", "slock-AF_CAIF" , "slock-AF_ALG" ,
- "slock-AF_MAX"
+ "slock-AF_NFC" , "slock-AF_MAX"
};
static const char *const af_family_clock_key_strings[AF_MAX+1] = {
"clock-AF_UNSPEC", "clock-AF_UNIX" , "clock-AF_INET" ,
@@ -190,7 +190,7 @@ static const char *const af_family_clock_key_strings[AF_MAX+1] = {
"clock-AF_TIPC" , "clock-AF_BLUETOOTH", "clock-AF_IUCV" ,
"clock-AF_RXRPC" , "clock-AF_ISDN" , "clock-AF_PHONET" ,
"clock-AF_IEEE802154", "clock-AF_CAIF" , "clock-AF_ALG" ,
- "clock-AF_MAX"
+ "clock-AF_NFC" , "clock-AF_MAX"
};

/*
diff --git a/net/nfc/Makefile b/net/nfc/Makefile
index 8aeaddc..c65add7 100644
--- a/net/nfc/Makefile
+++ b/net/nfc/Makefile
@@ -4,6 +4,6 @@

obj-$(CONFIG_NFC) += nfc.o

-nfc-objs := core.o netlink.o
+nfc-objs := core.o netlink.o af_nfc.o

ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
diff --git a/net/nfc/af_nfc.c b/net/nfc/af_nfc.c
new file mode 100644
index 0000000..e982cef
--- /dev/null
+++ b/net/nfc/af_nfc.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ *
+ * Authors:
+ * Aloisio Almeida Jr <[email protected]>
+ * Lauro Ramos Venancio <[email protected]>
+ *
+ * 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.
+ */
+
+#include <linux/nfc.h>
+
+#include "nfc.h"
+
+static DEFINE_RWLOCK(proto_tab_lock);
+static const struct nfc_protocol *proto_tab[NFC_SOCKPROTO_MAX];
+
+static int nfc_sock_create(struct net *net, struct socket *sock, int proto,
+ int kern)
+{
+ int rc = -EPROTONOSUPPORT;
+
+ if (net != &init_net)
+ return -EAFNOSUPPORT;
+
+ if (proto < 0 || proto >= NFC_SOCKPROTO_MAX)
+ return -EINVAL;
+
+ read_lock(&proto_tab_lock);
+ if (proto_tab[proto] && try_module_get(proto_tab[proto]->owner)) {
+ rc = proto_tab[proto]->create(net, sock, proto_tab[proto]);
+ module_put(proto_tab[proto]->owner);
+ }
+ read_unlock(&proto_tab_lock);
+
+ return rc;
+}
+
+static struct net_proto_family nfc_sock_family_ops = {
+ .owner = THIS_MODULE,
+ .family = PF_NFC,
+ .create = nfc_sock_create,
+};
+
+int nfc_proto_register(const struct nfc_protocol *nfc_proto)
+{
+ int rc;
+
+ if (nfc_proto->id < 0 || nfc_proto->id >= NFC_SOCKPROTO_MAX)
+ return -EINVAL;
+
+ rc = proto_register(nfc_proto->proto, 0);
+ if (rc)
+ return rc;
+
+ write_lock(&proto_tab_lock);
+ if (proto_tab[nfc_proto->id])
+ rc = -EBUSY;
+ else
+ proto_tab[nfc_proto->id] = nfc_proto;
+ write_unlock(&proto_tab_lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(nfc_proto_register);
+
+void nfc_proto_unregister(const struct nfc_protocol *nfc_proto)
+{
+ write_lock(&proto_tab_lock);
+ proto_tab[nfc_proto->id] = NULL;
+ write_unlock(&proto_tab_lock);
+
+ proto_unregister(nfc_proto->proto);
+}
+EXPORT_SYMBOL(nfc_proto_unregister);
+
+int __init af_nfc_init(void)
+{
+ return sock_register(&nfc_sock_family_ops);
+}
+
+void af_nfc_exit(void)
+{
+ sock_unregister(PF_NFC);
+}
diff --git a/net/nfc/core.c b/net/nfc/core.c
index b73c917..d4d2ecc 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -366,8 +366,14 @@ static int __init nfc_init(void)
if (rc)
goto err_genl;

+ rc = af_nfc_init();
+ if (rc)
+ goto err_af_nfc;
+
return 0;

+err_af_nfc:
+ nfc_genl_exit();
err_genl:
class_unregister(&nfc_class);
err:
@@ -376,6 +382,7 @@ err:

static void __exit nfc_exit(void)
{
+ af_nfc_exit();
nfc_genl_exit();
class_unregister(&nfc_class);
}
diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h
index 33d19eb..76b891f 100644
--- a/net/nfc/nfc.h
+++ b/net/nfc/nfc.h
@@ -25,6 +25,20 @@
#define __LOCAL_NFC_H

#include <net/nfc.h>
+#include <net/sock.h>
+
+struct nfc_protocol {
+ int id;
+ struct proto *proto;
+ struct module *owner;
+ int (*create)(struct net *net, struct socket *sock,
+ const struct nfc_protocol *nfc_proto);
+};
+
+int __init af_nfc_init(void);
+void af_nfc_exit(void);
+int nfc_proto_register(const struct nfc_protocol *nfc_proto);
+void nfc_proto_unregister(const struct nfc_protocol *nfc_proto);

int __init nfc_genl_init(void);

--
1.7.1


2011-06-03 13:35:38

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH 1/6] NFC: add nfc subsystem core

On Thu, 2011-06-02 at 18:46 -0300, Lauro Ramos Venancio wrote:

> +struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
> + u32 supported_protocols);

Where do the protocols come from? I don't see any defines for them?

johannes


2011-06-18 08:03:12

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH 0/6] NFC subsystem

On Fri, 17 Jun 2011 12:45:11 -0400, John W. Linville wrote:
> On Thu, Jun 02, 2011 at 06:46:04PM -0300, Lauro Ramos Venancio wrote:
>> This series of patches implements the first part of the Near Field
>> Communication (NFC) subsystem.
>
> Will you be submitting a new series to account for the comments from
> Johannes and others?

Yeah, but they found some issues with the dump consistency stuff that
tgr and I would like to solve generically (he was aware of them before);
I'm hoping I can do that quickly next week and then this can rely on it
instead of re-implementing it first.

johannes

2011-06-02 21:48:01

by Lauro Ramos Venancio

[permalink] [raw]
Subject: [PATCH 1/6] NFC: add nfc subsystem core

The NFC subsystem core is responsible for providing the device driver
interface. It is also responsible for providing an interface to the control
operations and data exchange.

Signed-off-by: Lauro Ramos Venancio <[email protected]>
Signed-off-by: Aloisio Almeida Jr <[email protected]>
Signed-off-by: Samuel Ortiz <[email protected]>
---
drivers/Kconfig | 2 -
drivers/Makefile | 1 +
drivers/nfc/Kconfig | 16 +--
drivers/nfc/Makefile | 2 +
include/net/nfc.h | 131 ++++++++++++++++++++
net/Kconfig | 1 +
net/Makefile | 1 +
net/nfc/Kconfig | 24 ++++
net/nfc/Makefile | 9 ++
net/nfc/core.c | 330 ++++++++++++++++++++++++++++++++++++++++++++++++++
net/nfc/nfc.h | 68 ++++++++++
11 files changed, 570 insertions(+), 15 deletions(-)
create mode 100644 include/net/nfc.h
create mode 100644 net/nfc/Kconfig
create mode 100644 net/nfc/Makefile
create mode 100644 net/nfc/core.c
create mode 100644 net/nfc/nfc.h

diff --git a/drivers/Kconfig b/drivers/Kconfig
index 61631ed..a56b0b8 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -92,8 +92,6 @@ source "drivers/memstick/Kconfig"

source "drivers/leds/Kconfig"

-source "drivers/nfc/Kconfig"
-
source "drivers/accessibility/Kconfig"

source "drivers/infiniband/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index a29527f..843cd31 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -120,3 +120,4 @@ obj-y += ieee802154/
obj-y += clk/

obj-$(CONFIG_HWSPINLOCK) += hwspinlock/
+obj-$(CONFIG_NFC) += nfc/
diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig
index ea15800..7809289 100644
--- a/drivers/nfc/Kconfig
+++ b/drivers/nfc/Kconfig
@@ -2,17 +2,8 @@
# Near Field Communication (NFC) devices
#

-menuconfig NFC_DEVICES
- bool "Near Field Communication (NFC) devices"
- default n
- ---help---
- You'll have to say Y if your computer contains an NFC device that
- you want to use under Linux.
-
- You can say N here if you don't have any Near Field Communication
- devices connected to your computer.
-
-if NFC_DEVICES
+menu "Near Field Communication (NFC) devices"
+ depends on NFC

config PN544_NFC
tristate "PN544 NFC driver"
@@ -26,5 +17,4 @@ config PN544_NFC
To compile this driver as a module, choose m here. The module will
be called pn544.

-
-endif # NFC_DEVICES
+endmenu
diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile
index a4efb16..25296f0 100644
--- a/drivers/nfc/Makefile
+++ b/drivers/nfc/Makefile
@@ -3,3 +3,5 @@
#

obj-$(CONFIG_PN544_NFC) += pn544.o
+
+ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
diff --git a/include/net/nfc.h b/include/net/nfc.h
new file mode 100644
index 0000000..11d63dc
--- /dev/null
+++ b/include/net/nfc.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ *
+ * Authors:
+ * Lauro Ramos Venancio <[email protected]>
+ * Aloisio Almeida Jr <[email protected]>
+ *
+ * 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 __NET_NFC_H
+#define __NET_NFC_H
+
+#include <linux/device.h>
+#include <linux/skbuff.h>
+
+struct nfc_dev;
+
+/**
+ * data_exchange_cb_t - Definition of nfc_data_exchange callback
+ *
+ * @context: nfc_data_exchange cb_context parameter
+ * @skb: response data
+ * @err: If an error has occurred during data exchange, it is the
+ * error number. Zero means no error.
+ *
+ * When a rx or tx package is lost or corrupted or the target gets out
+ * of the operating field, err is -EIO.
+ */
+typedef void (*data_exchange_cb_t)(void *context, struct sk_buff *skb,
+ int err);
+
+struct nfc_ops {
+ int (*start_poll)(struct nfc_dev *dev, u32 protocols);
+ void (*stop_poll)(struct nfc_dev *dev);
+ int (*activate_target)(struct nfc_dev *dev, u32 target_idx,
+ u32 protocol);
+ void (*deactivate_target)(struct nfc_dev *dev, u32 target_idx);
+ int (*data_exchange)(struct nfc_dev *dev, u32 target_idx,
+ struct sk_buff *skb, data_exchange_cb_t cb,
+ void *cb_context);
+};
+
+struct nfc_dev {
+ unsigned idx;
+ struct device dev;
+ bool polling;
+ u32 supported_protocols;
+
+ struct nfc_ops *ops;
+};
+#define to_nfc_dev(_dev) container_of(_dev, struct nfc_dev, dev)
+
+extern struct class nfc_class;
+
+struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
+ u32 supported_protocols);
+
+/**
+ * nfc_free_device - free nfc device
+ *
+ * @dev: The nfc device to free
+ */
+static inline void nfc_free_device(struct nfc_dev *dev)
+{
+ put_device(&dev->dev);
+}
+
+int nfc_register_device(struct nfc_dev *dev);
+
+void nfc_unregister_device(struct nfc_dev *dev);
+
+/**
+ * nfc_set_parent_dev - set the parent device
+ *
+ * @nfc_dev: The nfc device whose parent is being set
+ * @dev: The parent device
+ */
+static inline void nfc_set_parent_dev(struct nfc_dev *nfc_dev,
+ struct device *dev)
+{
+ nfc_dev->dev.parent = dev;
+}
+
+/**
+ * nfc_set_drvdata - set driver specifc data
+ *
+ * @dev: The nfc device
+ * @data: Pointer to driver specifc data
+ */
+static inline void nfc_set_drvdata(struct nfc_dev *dev, void *data)
+{
+ dev_set_drvdata(&dev->dev, data);
+}
+
+/**
+ * nfc_get_drvdata - get driver specifc data
+ *
+ * @dev: The nfc device
+ */
+static inline void *nfc_get_drvdata(struct nfc_dev *dev)
+{
+ return dev_get_drvdata(&dev->dev);
+}
+
+/**
+ * nfc_device_name - get the nfc device name
+ *
+ * @dev: The nfc device whose name to return
+ */
+static inline const char *nfc_device_name(struct nfc_dev *dev)
+{
+ return dev_name(&dev->dev);
+}
+
+struct sk_buff *nfc_alloc_skb(unsigned int size, gfp_t gfp);
+
+#endif /* __NET_NFC_H */
diff --git a/net/Kconfig b/net/Kconfig
index 878151c..a073148 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -322,6 +322,7 @@ source "net/rfkill/Kconfig"
source "net/9p/Kconfig"
source "net/caif/Kconfig"
source "net/ceph/Kconfig"
+source "net/nfc/Kconfig"


endif # if NET
diff --git a/net/Makefile b/net/Makefile
index a51d946..acdde49 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -68,3 +68,4 @@ obj-$(CONFIG_WIMAX) += wimax/
obj-$(CONFIG_DNS_RESOLVER) += dns_resolver/
obj-$(CONFIG_CEPH_LIB) += ceph/
obj-$(CONFIG_BATMAN_ADV) += batman-adv/
+obj-$(CONFIG_NFC) += nfc/
diff --git a/net/nfc/Kconfig b/net/nfc/Kconfig
new file mode 100644
index 0000000..4d5609c
--- /dev/null
+++ b/net/nfc/Kconfig
@@ -0,0 +1,24 @@
+#
+# NFC sybsystem configuration
+#
+
+menuconfig NFC
+ depends on NET && EXPERIMENTAL
+ tristate "NFC subsystem support (EXPERIMENTAL)"
+ default n
+ help
+ Say Y here if you want to build support for NFC (Near field
+ communication) devices.
+
+ To compile this support as a module, choose M here: the module will
+ be called nfc.
+
+config NFC_DEBUG
+ bool "NFC verbose debug messages"
+ depends on NFC
+ help
+ Say Y here if you want the NFC core and drivers to produce a bunch
+ of debug messages to the system log. Select this if you are having a
+ problem with NFC support and want to see more of what is going on.
+
+source "drivers/nfc/Kconfig"
diff --git a/net/nfc/Makefile b/net/nfc/Makefile
new file mode 100644
index 0000000..d837743
--- /dev/null
+++ b/net/nfc/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the Linux NFC subsystem.
+#
+
+obj-$(CONFIG_NFC) += nfc.o
+
+nfc-objs := core.o
+
+ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
diff --git a/net/nfc/core.c b/net/nfc/core.c
new file mode 100644
index 0000000..a6b8c95
--- /dev/null
+++ b/net/nfc/core.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ *
+ * Authors:
+ * Lauro Ramos Venancio <[email protected]>
+ * Aloisio Almeida Jr <[email protected]>
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "nfc.h"
+
+#define VERSION "0.1"
+
+/**
+ * nfc_start_poll - start polling for nfc targets
+ *
+ * @dev: The nfc device that must start polling
+ * @protocols: bitset of nfc protocols that must be used for polling
+ *
+ * The device remains polling for targets until a target is found or
+ * the nfc_stop_poll function is called.
+ */
+int nfc_start_poll(struct nfc_dev *dev, u32 protocols)
+{
+ int rc;
+
+ pr_debug("%s: dev_name:%s protocols=0x%x", __func__,
+ dev_name(&dev->dev), protocols);
+
+ if (!protocols)
+ return -EINVAL;
+
+ device_lock(&dev->dev);
+
+ if (!device_is_registered(&dev->dev)) {
+ rc = -ENODEV;
+ goto error;
+ }
+
+ if (dev->polling) {
+ rc = -EBUSY;
+ goto error;
+ }
+
+ rc = dev->ops->start_poll(dev, protocols);
+ if (!rc)
+ dev->polling = true;
+
+error:
+ device_unlock(&dev->dev);
+ return rc;
+}
+
+/**
+ * nfc_stop_poll - stop polling for nfc targets
+ *
+ * @dev: The nfc device that must stop polling
+ */
+int nfc_stop_poll(struct nfc_dev *dev)
+{
+ int rc = 0;
+
+ pr_debug("%s: dev_name:%s", __func__, dev_name(&dev->dev));
+
+ device_lock(&dev->dev);
+
+ if (!device_is_registered(&dev->dev)) {
+ rc = -ENODEV;
+ goto error;
+ }
+
+ if (!dev->polling) {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ dev->ops->stop_poll(dev);
+ dev->polling = false;
+
+error:
+ device_unlock(&dev->dev);
+ return rc;
+}
+
+/**
+ * nfc_activate_target - prepare the target for data exchange
+ *
+ * @dev: The nfc device that found the target
+ * @target_idx: index of the target that must be activated
+ * @protocol: nfc protocol that will be used for data exchange
+ */
+int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol)
+{
+ int rc;
+
+ pr_debug("%s: dev_name:%s", __func__, dev_name(&dev->dev));
+
+ device_lock(&dev->dev);
+
+ if (!device_is_registered(&dev->dev)) {
+ rc = -ENODEV;
+ goto error;
+ }
+
+ rc = dev->ops->activate_target(dev, target_idx, protocol);
+
+error:
+ device_unlock(&dev->dev);
+ return rc;
+}
+
+/**
+ * nfc_deactivate_target - deactivate a nfc target
+ *
+ * @dev: The nfc device that found the target
+ * @target_idx: index of the target that must be deactivated
+ */
+int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx)
+{
+ int rc = 0;
+
+ pr_debug("%s: dev_name:%s", __func__, dev_name(&dev->dev));
+
+ device_lock(&dev->dev);
+
+ if (!device_is_registered(&dev->dev)) {
+ rc = -ENODEV;
+ goto error;
+ }
+
+ dev->ops->deactivate_target(dev, target_idx);
+
+error:
+ device_unlock(&dev->dev);
+ return rc;
+}
+
+/**
+ * nfc_data_exchange - transceive data
+ *
+ * @dev: The nfc device that found the target
+ * @target_idx: index of the target
+ * @skb: data to be sent
+ * @cb: callback called when the response is received
+ * @cb_context: parameter for the callback function
+ *
+ * The user must wait for the callback before calling this function again.
+ */
+int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx,
+ struct sk_buff *skb,
+ data_exchange_cb_t cb,
+ void *cb_context)
+{
+ int rc;
+
+ pr_debug("%s: dev_name:%s", __func__, dev_name(&dev->dev));
+
+ device_lock(&dev->dev);
+
+ if (!device_is_registered(&dev->dev)) {
+ rc = -ENODEV;
+ kfree_skb(skb);
+ goto error;
+ }
+
+ rc = dev->ops->data_exchange(dev, target_idx, skb, cb, cb_context);
+
+error:
+ device_unlock(&dev->dev);
+ return rc;
+}
+
+/**
+ * nfc_alloc_skb - allocate a skb for data exchange responses
+ *
+ * @size: size to allocate
+ * @gfp: gfp flags
+ */
+struct sk_buff *nfc_alloc_skb(unsigned int size, gfp_t gfp)
+{
+ struct sk_buff *skb;
+ unsigned int total_size;
+
+ total_size = size + 1;
+ skb = alloc_skb(total_size, gfp);
+
+ if (skb)
+ skb_reserve(skb, 1);
+
+ return skb;
+}
+EXPORT_SYMBOL(nfc_alloc_skb);
+
+static void nfc_release(struct device *d)
+{
+ struct nfc_dev *dev = to_nfc_dev(d);
+
+ pr_debug("%s: dev_name:%s", __func__, dev_name(&dev->dev));
+
+ kfree(dev);
+}
+
+struct class nfc_class = {
+ .name = "nfc",
+ .dev_release = nfc_release,
+};
+EXPORT_SYMBOL(nfc_class);
+
+static int match_idx(struct device *d, void *data)
+{
+ struct nfc_dev *dev = to_nfc_dev(d);
+ unsigned *idx = data;
+
+ return dev->idx == *idx;
+}
+
+struct nfc_dev *nfc_get_device(unsigned idx)
+{
+ struct device *d;
+
+ d = class_find_device(&nfc_class, NULL, &idx, match_idx);
+ if (!d)
+ return NULL;
+
+ return to_nfc_dev(d);
+}
+
+/**
+ * nfc_allocate_device - allocate a new nfc device
+ *
+ * @ops: device operations
+ * @supported_protocols: NFC protocols supported by the device
+ */
+struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
+ u32 supported_protocols)
+{
+ static atomic_t dev_no = ATOMIC_INIT(0);
+ struct nfc_dev *dev;
+
+ if (!ops->start_poll || !ops->stop_poll || !ops->activate_target ||
+ !ops->deactivate_target || !ops->data_exchange)
+ return NULL;
+
+ if (!supported_protocols)
+ return NULL;
+
+ dev = kzalloc(sizeof(struct nfc_dev), GFP_KERNEL);
+ if (!dev)
+ return NULL;
+
+ dev->dev.class = &nfc_class;
+ dev->idx = atomic_inc_return(&dev_no) - 1;
+ dev_set_name(&dev->dev, "nfc%d", dev->idx);
+ device_initialize(&dev->dev);
+
+ dev->ops = ops;
+ dev->supported_protocols = supported_protocols;
+
+ return dev;
+}
+EXPORT_SYMBOL(nfc_allocate_device);
+
+/**
+ * nfc_register_device - register a nfc device in the nfc subsystem
+ *
+ * @dev: The nfc device to register
+ */
+int nfc_register_device(struct nfc_dev *dev)
+{
+ pr_debug("%s: dev_name:%s", __func__, dev_name(&dev->dev));
+
+ return device_add(&dev->dev);
+}
+EXPORT_SYMBOL(nfc_register_device);
+
+/**
+ * nfc_unregister_device - unregister a nfc device in the nfc subsystem
+ *
+ * @dev: The nfc device to unregister
+ */
+void nfc_unregister_device(struct nfc_dev *dev)
+{
+ pr_debug("%s: dev_name:%s", __func__, dev_name(&dev->dev));
+
+ /* lock to avoid unregistering a device while an operation
+ is in progress */
+ device_lock(&dev->dev);
+ device_del(&dev->dev);
+ device_unlock(&dev->dev);
+}
+EXPORT_SYMBOL(nfc_unregister_device);
+
+static int __init nfc_init(void)
+{
+ printk(KERN_INFO "NFC Core ver %s\n", VERSION);
+
+ return class_register(&nfc_class);
+}
+
+static void __exit nfc_exit(void)
+{
+ class_unregister(&nfc_class);
+}
+
+subsys_initcall(nfc_init);
+module_exit(nfc_exit);
+
+MODULE_AUTHOR("Lauro Ramos Venancio <[email protected]>");
+MODULE_DESCRIPTION("NFC Core ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h
new file mode 100644
index 0000000..8a50fd1
--- /dev/null
+++ b/net/nfc/nfc.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ *
+ * Authors:
+ * Lauro Ramos Venancio <[email protected]>
+ * Aloisio Almeida Jr <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __LOCAL_NFC_H
+#define __LOCAL_NFC_H
+
+#include <net/nfc.h>
+
+struct nfc_dev *nfc_get_device(unsigned idx);
+
+static inline void nfc_put_device(struct nfc_dev *dev)
+{
+ put_device(&dev->dev);
+}
+
+static inline void nfc_device_iter_init(struct class_dev_iter *iter)
+{
+ class_dev_iter_init(iter, &nfc_class, NULL, NULL);
+}
+
+static inline struct nfc_dev *nfc_device_iter_next(struct class_dev_iter *iter)
+{
+ struct device *d = class_dev_iter_next(iter);
+ if (!d)
+ return NULL;
+
+ return to_nfc_dev(d);
+}
+
+static inline void nfc_device_iter_exit(struct class_dev_iter *iter)
+{
+ class_dev_iter_exit(iter);
+}
+
+int nfc_start_poll(struct nfc_dev *dev, u32 protocols);
+
+int nfc_stop_poll(struct nfc_dev *dev);
+
+int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol);
+
+int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx);
+
+int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx,
+ struct sk_buff *skb,
+ data_exchange_cb_t cb,
+ void *cb_context);
+
+#endif /* __LOCAL_NFC_H */
--
1.7.1