2011-06-20 17:51:50

by Aloisio Almeida

[permalink] [raw]
Subject: [RFC][PATCH v2 0/7] NFC subsystem

This v2 includes (1) changes on the 'targets_found' report and (2) addition of
support to report dump consistency.

Due to the size limit of netlink messages, now NFC_EVENT_TARGETS_FOUND only
informs that new targets were found in a specific device. The userspace must
call NFC_CMD_GET_TARGET netlink command to get such list.

In order to inform an eventual dump inconsistency to userspace, generation
counters are being used internally. For each message to be sent in a dump,
genl_dump_check_consistent() is called to check if the current message comes
from a newer generation than previous messages. In that case, the message is
flagged with NLM_F_DUMP_INTR.

The function genl_dump_check_consistent() and the flag NLM_F_DUMP_INTR were
added by Johannes Berg's patch, which was just sent to netdev and wireless
kernel lists. As a reference, I added the Johannes's patch at the beggining of
this patch series. That's the reason to flag this patch series as RFC.

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

Johannes Berg (1):
netlink: advertise incomplete dumps

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 | 1630 ++++++++++++++++++++++++++++++++++++++
include/linux/netlink.h | 2 +
include/linux/nfc.h | 125 +++
include/linux/socket.h | 4 +-
include/net/genetlink.h | 32 +
include/net/netlink.h | 24 +
include/net/nfc.h | 152 ++++
net/Kconfig | 1 +
net/Makefile | 1 +
net/core/sock.c | 6 +-
net/netlink/af_netlink.c | 2 +
net/nfc/Kconfig | 24 +
net/nfc/Makefile | 9 +
net/nfc/af_nfc.c | 98 +++
net/nfc/core.c | 438 ++++++++++
net/nfc/netlink.c | 537 +++++++++++++
net/nfc/nfc.h | 110 +++
net/nfc/rawsock.c | 351 ++++++++
23 files changed, 3685 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

--
1.7.4.1



2011-06-20 17:52:10

by Aloisio Almeida

[permalink] [raw]
Subject: [RFC][PATCH v2 7/7] NFC: add Documentation/networking/nfc.txt

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..23cf411
--- /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.
+
+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.4.1


2011-06-22 14:18:40

by Aloisio Almeida

[permalink] [raw]
Subject: Re: [RFC][PATCH v2 2/7] NFC: add nfc subsystem core

Hi Marcel and Gustavo,

On Tue, Jun 21, 2011 at 11:24 PM, Marcel Holtmann <[email protected]> wrote:
>> > +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.
>>
>> I think that use only dynamic debug is a lot nicer. I don't see a point to
>> have a NFC debug option in Kconfig. We've been using dynamic debug in the
>> Bluetooth subsystem for a while and it works fine. If we have dynamic debug
>> there is no need to recompile a module to add debug support to it.
>>
>> Also it's a good idea use macros for pr_debug, instead of calling it every
>> time with the same paramenters (__func__, for example). In the Bluetooth
>> subsystem we do like this:
>>
>> #define BT_INFO(fmt, arg...) printk(KERN_INFO "Bluetooth: " fmt "\n" , ## arg)
>> #define BT_ERR(fmt, arg...) ?printk(KERN_ERR "%s: " fmt "\n" , __func__ , ##
>> arg)
>> #define BT_DBG(fmt, arg...) ?pr_debug("%s: " fmt "\n" , __func__ , ## arg)
>
> I would clearly second this. Switching the Bluetooth subsystem to
> dynamic debug made so many things so much easier. And using a macro like
> NFC_DBG makes it pretty nice and clean.

That's definitively a good idea. I'll change that.

Aloisio

2011-06-20 17:51:59

by Aloisio Almeida

[permalink] [raw]
Subject: [RFC][PATCH v2 3/7] NFC: add nfc generic netlink interface

From: Lauro Ramos Venancio <[email protected]>

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 | 111 +++++++++++
include/net/nfc.h | 21 ++
net/nfc/Makefile | 2 +-
net/nfc/core.c | 83 ++++++++-
net/nfc/netlink.c | 537 +++++++++++++++++++++++++++++++++++++++++++++++++++
net/nfc/nfc.h | 11 +
6 files changed, 762 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..59b3c79
--- /dev/null
+++ b/include/linux/nfc.h
@@ -0,0 +1,111 @@
+/*
+ * 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_CMD_GET_TARGET: dump all targets found by the previous poll (requires
+ * %NFC_ATTR_DEVICE_INDEX)
+ * @NFC_EVENT_TARGETS_FOUND: event emitted when a new target is found
+ * (it sends %NFC_ATTR_DEVICE_INDEX)
+ * @NFC_EVENT_DEVICE_ADDED: event emitted when a new device is registred
+ * (it sends %NFC_ATTR_DEVICE_NAME, %NFC_ATTR_DEVICE_INDEX and
+ * %NFC_ATTR_PROTOCOLS)
+ * @NFC_EVENT_DEVICE_REMOVED: event emitted when a device is removed
+ * (it sends %NFC_ATTR_DEVICE_INDEX)
+ */
+enum nfc_commands {
+ NFC_CMD_UNSPEC,
+ NFC_CMD_GET_DEVICE,
+ NFC_CMD_START_POLL,
+ NFC_CMD_STOP_POLL,
+ NFC_CMD_GET_TARGET,
+ 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_TARGET_INDEX: index of the nfc target
+ * @NFC_ATTR_TARGET_SENS_RES: extra information for NFC-A targets
+ * @NFC_ATTR_TARGET_SEL_RES: extra information for NFC-A targets
+ */
+enum nfc_attrs {
+ NFC_ATTR_UNSPEC,
+ NFC_ATTR_DEVICE_INDEX,
+ NFC_ATTR_DEVICE_NAME,
+ NFC_ATTR_PROTOCOLS,
+ NFC_ATTR_TARGET_INDEX,
+ NFC_ATTR_TARGET_SENS_RES,
+ NFC_ATTR_TARGET_SEL_RES,
+/* 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)
+
+#endif /*__LINUX_NFC_H */
diff --git a/include/net/nfc.h b/include/net/nfc.h
index 11d63dc..01a30b3 100644
--- a/include/net/nfc.h
+++ b/include/net/nfc.h
@@ -54,10 +54,28 @@ struct nfc_ops {
void *cb_context);
};

+struct nfc_target {
+ u32 idx;
+ u32 supported_protocols;
+ u16 sens_res;
+ u8 sel_res;
+};
+
+struct nfc_genl_data {
+ u32 poll_req_pid;
+ struct mutex genl_data_mutex;
+};
+
struct nfc_dev {
unsigned idx;
+ unsigned target_idx;
+ struct nfc_target *targets;
+ int n_targets;
+ int targets_generation;
+ spinlock_t targets_lock;
struct device dev;
bool polling;
+ struct nfc_genl_data genl_data;
u32 supported_protocols;

struct nfc_ops *ops;
@@ -128,4 +146,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);
+
#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 f4710fe..5e09d50 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -213,12 +213,61 @@ 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
+ *
+ * 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 n_targets)
+{
+ int i;
+
+ pr_debug("%s: dev_name:%s", __func__, dev_name(&dev->dev));
+
+ dev->polling = false;
+
+ for (i = 0; i < n_targets; i++)
+ targets[i].idx = dev->target_idx++;
+
+ spin_lock_bh(&dev->targets_lock);
+
+ dev->targets_generation++;
+
+ kfree(dev->targets);
+ dev->targets = kzalloc(n_targets * sizeof(struct nfc_target),
+ GFP_ATOMIC);
+ if (!dev->targets) {
+ dev->n_targets = 0;
+ spin_unlock_bh(&dev->targets_lock);
+ return -ENOMEM;
+ }
+
+ memcpy(dev->targets, targets, n_targets * sizeof(struct nfc_target));
+ dev->n_targets = n_targets;
+
+ spin_unlock_bh(&dev->targets_lock);
+
+ nfc_genl_targets_found(dev);
+
+ return 0;
+}
+EXPORT_SYMBOL(nfc_targets_found);
+
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));

+ nfc_genl_data_exit(&dev->genl_data);
+ kfree(dev->targets);
kfree(dev);
}

@@ -278,6 +327,12 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
dev->ops = ops;
dev->supported_protocols = supported_protocols;

+ spin_lock_init(&dev->targets_lock);
+ nfc_genl_data_init(&dev->genl_data);
+
+ /* first generation must not be 0 */
+ dev->targets_generation = 1;
+
return dev;
}
EXPORT_SYMBOL(nfc_allocate_device);
@@ -298,7 +353,10 @@ int nfc_register_device(struct nfc_dev *dev)
rc = device_add(&dev->dev);
mutex_unlock(&nfc_devlist_mutex);

- return rc;
+ if (rc < 0)
+ return rc;
+
+ return nfc_genl_device_added(dev);
}
EXPORT_SYMBOL(nfc_register_device);

@@ -321,18 +379,39 @@ void nfc_unregister_device(struct nfc_dev *dev)
device_unlock(&dev->dev);

mutex_unlock(&nfc_devlist_mutex);
+
+ nfc_genl_device_removed(dev);
}
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;
+
+ /* the first generation must not be 0 */
+ nfc_devlist_generation = 1;
+
+ 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..5f2ddb2
--- /dev/null
+++ b/net/nfc/netlink.c
@@ -0,0 +1,537 @@
+/*
+ * 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 },
+};
+
+static int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target,
+ struct netlink_callback *cb, int flags)
+{
+ void *hdr;
+
+ pr_debug("%s\n", __func__);
+
+ hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
+ &nfc_genl_family, flags, NFC_CMD_GET_TARGET);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ genl_dump_check_consistent(cb, hdr, &nfc_genl_family);
+
+ NLA_PUT_U32(msg, NFC_ATTR_TARGET_INDEX, target->idx);
+ NLA_PUT_U32(msg, NFC_ATTR_PROTOCOLS,
+ target->supported_protocols);
+ NLA_PUT_U16(msg, NFC_ATTR_TARGET_SENS_RES, target->sens_res);
+ NLA_PUT_U8(msg, NFC_ATTR_TARGET_SEL_RES, target->sel_res);
+
+ return genlmsg_end(msg, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ return -EMSGSIZE;
+}
+
+static struct nfc_dev *__get_device_from_cb(struct netlink_callback *cb)
+{
+ struct nfc_dev *dev;
+ int rc;
+ u32 idx;
+
+ rc = nlmsg_parse(cb->nlh, GENL_HDRLEN + nfc_genl_family.hdrsize,
+ nfc_genl_family.attrbuf,
+ nfc_genl_family.maxattr,
+ nfc_genl_policy);
+ if (rc < 0)
+ return ERR_PTR(rc);
+
+ if (!nfc_genl_family.attrbuf[NFC_ATTR_DEVICE_INDEX])
+ return ERR_PTR(-EINVAL);
+
+ idx = nla_get_u32(nfc_genl_family.attrbuf[NFC_ATTR_DEVICE_INDEX]);
+
+ dev = nfc_get_device(idx);
+ if (!dev)
+ return ERR_PTR(-ENODEV);
+
+ return dev;
+}
+
+static int nfc_genl_dump_targets(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ int i = cb->args[0];
+ struct nfc_dev *dev = (struct nfc_dev *) cb->args[1];
+ int rc;
+
+ pr_debug("%s\n", __func__);
+
+ if (!dev) {
+ dev = __get_device_from_cb(cb);
+ if (IS_ERR(dev))
+ return PTR_ERR(dev);
+
+ cb->args[1] = (long) dev;
+ }
+
+ spin_lock_bh(&dev->targets_lock);
+
+ cb->seq = dev->targets_generation;
+
+ while (i < dev->n_targets) {
+ rc = nfc_genl_send_target(skb, &dev->targets[i], cb,
+ NLM_F_MULTI);
+ if (rc < 0)
+ break;
+
+ i++;
+ }
+
+ spin_unlock_bh(&dev->targets_lock);
+
+ cb->args[0] = i;
+
+ return skb->len;
+}
+
+static int nfc_genl_dump_targets_done(struct netlink_callback *cb)
+{
+ struct nfc_dev *dev = (struct nfc_dev *) cb->args[1];
+
+ pr_debug("%s\n", __func__);
+
+ if (dev)
+ nfc_put_device(dev);
+
+ return 0;
+}
+
+int nfc_genl_targets_found(struct nfc_dev *dev)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ 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);
+
+ 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, struct nfc_dev *dev,
+ u32 pid, u32 seq,
+ struct netlink_callback *cb,
+ int flags)
+{
+ 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;
+
+ if (cb)
+ genl_dump_check_consistent(cb, hdr, &nfc_genl_family);
+
+ 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];
+ bool first_call = false;
+
+ pr_debug("%s\n", __func__);
+
+ if (!iter) {
+ first_call = true;
+ iter = kmalloc(sizeof(struct class_dev_iter), GFP_KERNEL);
+ if (!iter)
+ return -ENOMEM;
+ cb->args[0] = (long) iter;
+ }
+
+ mutex_lock(&nfc_devlist_mutex);
+
+ cb->seq = nfc_devlist_generation;
+
+ if (first_call) {
+ nfc_device_iter_init(iter);
+ dev = nfc_device_iter_next(iter);
+ }
+
+ while (dev) {
+ int rc;
+
+ rc = nfc_genl_send_device(skb, dev, NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq,
+ cb, NLM_F_MULTI);
+ if (rc < 0)
+ break;
+
+ dev = nfc_device_iter_next(iter);
+ }
+
+ mutex_unlock(&nfc_devlist_mutex);
+
+ 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, dev, info->snd_pid, info->snd_seq,
+ NULL, 0);
+ 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,
+ },
+ {
+ .cmd = NFC_CMD_GET_TARGET,
+ .dumpit = nfc_genl_dump_targets,
+ .done = nfc_genl_dump_targets_done,
+ .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 ad19e4a..878a333 100644
--- a/net/nfc/nfc.h
+++ b/net/nfc/nfc.h
@@ -29,6 +29,17 @@
extern int nfc_devlist_generation;
extern struct mutex nfc_devlist_mutex;

+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);
+
+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.4.1


2011-06-22 14:07:40

by Aloisio Almeida

[permalink] [raw]
Subject: Re: [RFC][PATCH v2 3/7] NFC: add nfc generic netlink interface

Hi Gustavo,

On Tue, Jun 21, 2011 at 7:05 PM, Gustavo F. Padovan
<[email protected]> wrote:
>> ?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;
>
> Just return rc here and get rid of the label.
>
ok

>> +/**
>> + * nfc_genl_exit() - Deinitialize netlink interface
>> + *
>> + * This exit function unregisters the nfc netlink family.
>> + */
>> +void nfc_genl_exit(void)
>
> You may want __exit here.

The nfc_genl_exit() is called in '__init nfc_init(void)' if any error
occurs. In that case we will have an __exit code inside an __init
code, resulting in a section mismatch.

Aloisio

2011-06-20 17:51:52

by Aloisio Almeida

[permalink] [raw]
Subject: [RFC][PATCH v2 1/7] netlink: advertise incomplete dumps

From: Johannes Berg <[email protected]>

Consider the following situation:
* a dump that would show 8 entries, four in the first
round, and four in the second
* between the first and second rounds, 6 entries are
removed
* now the second round will not show any entry, and
even if there is a sequence/generation counter the
application will not know

To solve this problem, add a new flag NLM_F_DUMP_INTR
to the netlink header that indicates the dump wasn't
consistent, this flag can also be set on the MSG_DONE
message that terminates the dump, and as such above
situation can be detected.

To achieve this, add a sequence counter to the netlink
callback struct. Of course, netlink code still needs
to use this new functionality. The correct way to do
that is to always set cb->seq when a dumpit callback
is invoked and call nl_dump_check_consistent() for
each new message. The core code will also call this
function for the final MSG_DONE message.

To make it usable with generic netlink, a new function
genlmsg_nlhdr() is needed to obtain the netlink header
from the genetlink user header.

Signed-off-by: Johannes Berg <[email protected]>
---
include/linux/netlink.h | 2 ++
include/net/genetlink.h | 32 ++++++++++++++++++++++++++++++++
include/net/netlink.h | 24 ++++++++++++++++++++++++
net/netlink/af_netlink.c | 2 ++
4 files changed, 60 insertions(+), 0 deletions(-)

diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index 4c4ac3f..8d1bcec 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -48,6 +48,7 @@ struct nlmsghdr {
#define NLM_F_MULTI 2 /* Multipart message, terminated by NLMSG_DONE */
#define NLM_F_ACK 4 /* Reply with ack, with zero or error code */
#define NLM_F_ECHO 8 /* Echo this request */
+#define NLM_F_DUMP_INTR 16 /* Dump was inconsistent due to sequence change */

/* Modifiers to GET request */
#define NLM_F_ROOT 0x100 /* specify tree root */
@@ -221,6 +222,7 @@ struct netlink_callback {
struct netlink_callback *cb);
int (*done)(struct netlink_callback *cb);
int family;
+ unsigned int prev_seq, seq;
long args[6];
};

diff --git a/include/net/genetlink.h b/include/net/genetlink.h
index d420f28..82d8d09 100644
--- a/include/net/genetlink.h
+++ b/include/net/genetlink.h
@@ -160,6 +160,38 @@ static inline void *genlmsg_put(struct sk_buff *skb, u32 pid, u32 seq,
}

/**
+ * genlmsg_nlhdr - Obtain netlink header from user specified header
+ * @user_hdr: user header as returned from genlmsg_put()
+ * @family: generic netlink family
+ *
+ * Returns pointer to netlink header.
+ */
+static inline struct nlmsghdr *genlmsg_nlhdr(void *user_hdr,
+ struct genl_family *family)
+{
+ return (struct nlmsghdr *)((char *)user_hdr -
+ family->hdrsize -
+ GENL_HDRLEN -
+ NLMSG_HDRLEN);
+}
+
+/**
+ * genl_dump_check_consistent - check if sequence is consistent and advertise if not
+ * @cb: netlink callback structure that stores the sequence number
+ * @user_hdr: user header as returned from genlmsg_put()
+ * @family: generic netlink family
+ *
+ * Cf. nl_dump_check_consistent(), this just provides a wrapper to make it
+ * simpler to use with generic netlink.
+ */
+static inline void genl_dump_check_consistent(struct netlink_callback *cb,
+ void *user_hdr,
+ struct genl_family *family)
+{
+ nl_dump_check_consistent(cb, genlmsg_nlhdr(user_hdr, family));
+}
+
+/**
* genlmsg_put_reply - Add generic netlink header to a reply message
* @skb: socket buffer holding the message
* @info: receiver info
diff --git a/include/net/netlink.h b/include/net/netlink.h
index 02740a9..98c1854 100644
--- a/include/net/netlink.h
+++ b/include/net/netlink.h
@@ -638,6 +638,30 @@ static inline int nlmsg_unicast(struct sock *sk, struct sk_buff *skb, u32 pid)
nlmsg_ok(pos, rem); \
pos = nlmsg_next(pos, &(rem)))

+/**
+ * nl_dump_check_consistent - check if sequence is consistent and advertise if not
+ * @cb: netlink callback structure that stores the sequence number
+ * @nlh: netlink message header to write the flag to
+ *
+ * This function checks if the sequence (generation) number changed during dump
+ * and if it did, advertises it in the netlink message header.
+ *
+ * The correct way to use it is to set cb->seq to the generation counter when
+ * all locks for dumping have been acquired, and then call this function for
+ * each message that is generated.
+ *
+ * Note that due to initialisation concerns, 0 is an invalid sequence number
+ * and must not be used by code that uses this functionality.
+ */
+static inline void
+nl_dump_check_consistent(struct netlink_callback *cb,
+ struct nlmsghdr *nlh)
+{
+ if (cb->prev_seq && cb->seq != cb->prev_seq)
+ nlh->nlmsg_flags |= NLM_F_DUMP_INTR;
+ cb->prev_seq = cb->seq;
+}
+
/**************************************************************************
* Netlink Attributes
**************************************************************************/
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 6ef64ad..a7ec851 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1693,6 +1693,8 @@ static int netlink_dump(struct sock *sk)
if (!nlh)
goto errout_skb;

+ nl_dump_check_consistent(cb, nlh);
+
memcpy(nlmsg_data(nlh), &len, sizeof(len));

if (sk_filter(sk, skb))
--
1.7.4.1


2011-06-22 14:13:28

by Aloisio Almeida

[permalink] [raw]
Subject: Re: [RFC][PATCH v3 7/7] NFC: add Documentation/networking/nfc.txt

Hi Randy,

Thanks for your comments, I'll apply all.

Aloisio

2011-06-20 17:52:08

by Aloisio Almeida

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

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 | 1630 ++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1641 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..3e805fa
--- /dev/null
+++ b/drivers/nfc/pn533.c
@@ -0,0 +1,1630 @@
+/*
+ * 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;
+ nfc_tgt->sens_res = be16_to_cpu(tgt_jewel->sens_res);
+
+ 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);
+
+ 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.4.1


2011-06-21 22:04:54

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [RFC][PATCH v2 3/7] NFC: add nfc generic netlink interface

* Aloisio Almeida Jr <[email protected]> [2011-06-20 14:50:08 -0300]:

> From: Lauro Ramos Venancio <[email protected]>
>
> 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 | 111 +++++++++++
> include/net/nfc.h | 21 ++
> net/nfc/Makefile | 2 +-
> net/nfc/core.c | 83 ++++++++-
> net/nfc/netlink.c | 537 +++++++++++++++++++++++++++++++++++++++++++++++++++
> net/nfc/nfc.h | 11 +
> 6 files changed, 762 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..59b3c79
> --- /dev/null
> +++ b/include/linux/nfc.h
> @@ -0,0 +1,111 @@
> +/*
> + * 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_CMD_GET_TARGET: dump all targets found by the previous poll (requires
> + * %NFC_ATTR_DEVICE_INDEX)
> + * @NFC_EVENT_TARGETS_FOUND: event emitted when a new target is found
> + * (it sends %NFC_ATTR_DEVICE_INDEX)
> + * @NFC_EVENT_DEVICE_ADDED: event emitted when a new device is registred
> + * (it sends %NFC_ATTR_DEVICE_NAME, %NFC_ATTR_DEVICE_INDEX and
> + * %NFC_ATTR_PROTOCOLS)
> + * @NFC_EVENT_DEVICE_REMOVED: event emitted when a device is removed
> + * (it sends %NFC_ATTR_DEVICE_INDEX)
> + */
> +enum nfc_commands {
> + NFC_CMD_UNSPEC,
> + NFC_CMD_GET_DEVICE,
> + NFC_CMD_START_POLL,
> + NFC_CMD_STOP_POLL,
> + NFC_CMD_GET_TARGET,
> + 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_TARGET_INDEX: index of the nfc target
> + * @NFC_ATTR_TARGET_SENS_RES: extra information for NFC-A targets
> + * @NFC_ATTR_TARGET_SEL_RES: extra information for NFC-A targets
> + */
> +enum nfc_attrs {
> + NFC_ATTR_UNSPEC,
> + NFC_ATTR_DEVICE_INDEX,
> + NFC_ATTR_DEVICE_NAME,
> + NFC_ATTR_PROTOCOLS,
> + NFC_ATTR_TARGET_INDEX,
> + NFC_ATTR_TARGET_SENS_RES,
> + NFC_ATTR_TARGET_SEL_RES,
> +/* 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)
> +
> +#endif /*__LINUX_NFC_H */
> diff --git a/include/net/nfc.h b/include/net/nfc.h
> index 11d63dc..01a30b3 100644
> --- a/include/net/nfc.h
> +++ b/include/net/nfc.h
> @@ -54,10 +54,28 @@ struct nfc_ops {
> void *cb_context);
> };
>
> +struct nfc_target {
> + u32 idx;
> + u32 supported_protocols;
> + u16 sens_res;
> + u8 sel_res;
> +};
> +
> +struct nfc_genl_data {
> + u32 poll_req_pid;
> + struct mutex genl_data_mutex;
> +};
> +
> struct nfc_dev {
> unsigned idx;
> + unsigned target_idx;
> + struct nfc_target *targets;
> + int n_targets;
> + int targets_generation;
> + spinlock_t targets_lock;
> struct device dev;
> bool polling;
> + struct nfc_genl_data genl_data;
> u32 supported_protocols;
>
> struct nfc_ops *ops;
> @@ -128,4 +146,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);
> +
> #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 f4710fe..5e09d50 100644
> --- a/net/nfc/core.c
> +++ b/net/nfc/core.c
> @@ -213,12 +213,61 @@ 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
> + *
> + * 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 n_targets)
> +{
> + int i;
> +
> + pr_debug("%s: dev_name:%s", __func__, dev_name(&dev->dev));
> +
> + dev->polling = false;
> +
> + for (i = 0; i < n_targets; i++)
> + targets[i].idx = dev->target_idx++;
> +
> + spin_lock_bh(&dev->targets_lock);
> +
> + dev->targets_generation++;
> +
> + kfree(dev->targets);
> + dev->targets = kzalloc(n_targets * sizeof(struct nfc_target),
> + GFP_ATOMIC);
> + if (!dev->targets) {
> + dev->n_targets = 0;
> + spin_unlock_bh(&dev->targets_lock);
> + return -ENOMEM;
> + }
> +
> + memcpy(dev->targets, targets, n_targets * sizeof(struct nfc_target));
> + dev->n_targets = n_targets;
> +
> + spin_unlock_bh(&dev->targets_lock);
> +
> + nfc_genl_targets_found(dev);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(nfc_targets_found);
> +
> 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));
>
> + nfc_genl_data_exit(&dev->genl_data);
> + kfree(dev->targets);
> kfree(dev);
> }
>
> @@ -278,6 +327,12 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
> dev->ops = ops;
> dev->supported_protocols = supported_protocols;
>
> + spin_lock_init(&dev->targets_lock);
> + nfc_genl_data_init(&dev->genl_data);
> +
> + /* first generation must not be 0 */
> + dev->targets_generation = 1;
> +
> return dev;
> }
> EXPORT_SYMBOL(nfc_allocate_device);
> @@ -298,7 +353,10 @@ int nfc_register_device(struct nfc_dev *dev)
> rc = device_add(&dev->dev);
> mutex_unlock(&nfc_devlist_mutex);
>
> - return rc;
> + if (rc < 0)
> + return rc;
> +
> + return nfc_genl_device_added(dev);
> }
> EXPORT_SYMBOL(nfc_register_device);
>
> @@ -321,18 +379,39 @@ void nfc_unregister_device(struct nfc_dev *dev)
> device_unlock(&dev->dev);
>
> mutex_unlock(&nfc_devlist_mutex);
> +
> + nfc_genl_device_removed(dev);
> }
> 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;

Just return rc here and get rid of the label.

> +
> + rc = nfc_genl_init();
> + if (rc)
> + goto err_genl;
> +
> + /* the first generation must not be 0 */
> + nfc_devlist_generation = 1;
> +
> + 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..5f2ddb2
> --- /dev/null
> +++ b/net/nfc/netlink.c
> @@ -0,0 +1,537 @@
> +/*
> + * 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 },
> +};
> +
> +static int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target,
> + struct netlink_callback *cb, int flags)
> +{
> + void *hdr;
> +
> + pr_debug("%s\n", __func__);
> +
> + hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
> + &nfc_genl_family, flags, NFC_CMD_GET_TARGET);
> + if (!hdr)
> + return -EMSGSIZE;
> +
> + genl_dump_check_consistent(cb, hdr, &nfc_genl_family);
> +
> + NLA_PUT_U32(msg, NFC_ATTR_TARGET_INDEX, target->idx);
> + NLA_PUT_U32(msg, NFC_ATTR_PROTOCOLS,
> + target->supported_protocols);
> + NLA_PUT_U16(msg, NFC_ATTR_TARGET_SENS_RES, target->sens_res);
> + NLA_PUT_U8(msg, NFC_ATTR_TARGET_SEL_RES, target->sel_res);
> +
> + return genlmsg_end(msg, hdr);
> +
> +nla_put_failure:

There is no use for this macro in all function that have a label with this
name.

> + genlmsg_cancel(msg, hdr);
> + return -EMSGSIZE;
> +}
> +
> +static struct nfc_dev *__get_device_from_cb(struct netlink_callback *cb)
> +{
> + struct nfc_dev *dev;
> + int rc;
> + u32 idx;
> +
> + rc = nlmsg_parse(cb->nlh, GENL_HDRLEN + nfc_genl_family.hdrsize,
> + nfc_genl_family.attrbuf,
> + nfc_genl_family.maxattr,
> + nfc_genl_policy);
> + if (rc < 0)
> + return ERR_PTR(rc);
> +
> + if (!nfc_genl_family.attrbuf[NFC_ATTR_DEVICE_INDEX])
> + return ERR_PTR(-EINVAL);
> +
> + idx = nla_get_u32(nfc_genl_family.attrbuf[NFC_ATTR_DEVICE_INDEX]);
> +
> + dev = nfc_get_device(idx);
> + if (!dev)
> + return ERR_PTR(-ENODEV);
> +
> + return dev;
> +}
> +
> +static int nfc_genl_dump_targets(struct sk_buff *skb,
> + struct netlink_callback *cb)
> +{
> + int i = cb->args[0];
> + struct nfc_dev *dev = (struct nfc_dev *) cb->args[1];
> + int rc;
> +
> + pr_debug("%s\n", __func__);
> +
> + if (!dev) {
> + dev = __get_device_from_cb(cb);
> + if (IS_ERR(dev))
> + return PTR_ERR(dev);
> +
> + cb->args[1] = (long) dev;
> + }
> +
> + spin_lock_bh(&dev->targets_lock);
> +
> + cb->seq = dev->targets_generation;
> +
> + while (i < dev->n_targets) {
> + rc = nfc_genl_send_target(skb, &dev->targets[i], cb,
> + NLM_F_MULTI);
> + if (rc < 0)
> + break;
> +
> + i++;
> + }
> +
> + spin_unlock_bh(&dev->targets_lock);
> +
> + cb->args[0] = i;
> +
> + return skb->len;
> +}
> +
> +static int nfc_genl_dump_targets_done(struct netlink_callback *cb)
> +{
> + struct nfc_dev *dev = (struct nfc_dev *) cb->args[1];
> +
> + pr_debug("%s\n", __func__);
> +
> + if (dev)
> + nfc_put_device(dev);
> +
> + return 0;
> +}
> +
> +int nfc_genl_targets_found(struct nfc_dev *dev)
> +{
> + struct sk_buff *msg;
> + void *hdr;
> +
> + 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);
> +
> + 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, struct nfc_dev *dev,
> + u32 pid, u32 seq,
> + struct netlink_callback *cb,
> + int flags)
> +{
> + 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;
> +
> + if (cb)
> + genl_dump_check_consistent(cb, hdr, &nfc_genl_family);
> +
> + 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];
> + bool first_call = false;
> +
> + pr_debug("%s\n", __func__);
> +
> + if (!iter) {
> + first_call = true;
> + iter = kmalloc(sizeof(struct class_dev_iter), GFP_KERNEL);
> + if (!iter)
> + return -ENOMEM;
> + cb->args[0] = (long) iter;
> + }
> +
> + mutex_lock(&nfc_devlist_mutex);
> +
> + cb->seq = nfc_devlist_generation;
> +
> + if (first_call) {
> + nfc_device_iter_init(iter);
> + dev = nfc_device_iter_next(iter);
> + }
> +
> + while (dev) {
> + int rc;
> +
> + rc = nfc_genl_send_device(skb, dev, NETLINK_CB(cb->skb).pid,
> + cb->nlh->nlmsg_seq,
> + cb, NLM_F_MULTI);
> + if (rc < 0)
> + break;
> +
> + dev = nfc_device_iter_next(iter);
> + }
> +
> + mutex_unlock(&nfc_devlist_mutex);
> +
> + 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, dev, info->snd_pid, info->snd_seq,
> + NULL, 0);
> + 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,
> + },
> + {
> + .cmd = NFC_CMD_GET_TARGET,
> + .dumpit = nfc_genl_dump_targets,
> + .done = nfc_genl_dump_targets_done,
> + .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)

You may want __exit here.

Gustavo

2011-06-22 20:03:08

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [RFC][PATCH v2 3/7] NFC: add nfc generic netlink interface

Hi Eliad,

* Eliad Peller <[email protected]> [2011-06-22 01:15:42 +0300]:

> On Wed, Jun 22, 2011 at 1:05 AM, Gustavo F. Padovan
> <[email protected]> wrote:
> >> +static int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target,
> >> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct netlink_callback *cb, int flags)
> >> +{
> >> + ? ? void *hdr;
> >> +
> >> + ? ? pr_debug("%s\n", __func__);
> >> +
> >> + ? ? hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
> >> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? &nfc_genl_family, flags, NFC_CMD_GET_TARGET);
> >> + ? ? if (!hdr)
> >> + ? ? ? ? ? ? return -EMSGSIZE;
> >> +
> >> + ? ? genl_dump_check_consistent(cb, hdr, &nfc_genl_family);
> >> +
> >> + ? ? NLA_PUT_U32(msg, NFC_ATTR_TARGET_INDEX, target->idx);
> >> + ? ? NLA_PUT_U32(msg, NFC_ATTR_PROTOCOLS,
> >> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? target->supported_protocols);
> >> + ? ? NLA_PUT_U16(msg, NFC_ATTR_TARGET_SENS_RES, target->sens_res);
> >> + ? ? NLA_PUT_U8(msg, NFC_ATTR_TARGET_SEL_RES, target->sel_res);
> >> +
> >> + ? ? return genlmsg_end(msg, hdr);
> >> +
> >> +nla_put_failure:
> >
> > There is no use for this macro in all function that have a label with this
> > name.
>
> the NLA_PUT_* macros use this label:
>
> #define NLA_PUT(skb, attrtype, attrlen, data) \
> do { \
> if (unlikely(nla_put(skb, attrtype, attrlen, data) < 0)) \
> goto nla_put_failure; \
> } while(0)

Nice to know, I'm not a netlink expert. Thanks.

Gustavo

2011-06-22 13:26:57

by Samuel Ortiz

[permalink] [raw]
Subject: Re: [RFC][PATCH v2 3/7] NFC: add nfc generic netlink interface

On Wed, Jun 22, 2011 at 03:08:06PM +0200, Johannes Berg wrote:
> On Wed, 2011-06-22 at 14:57 +0200, Samuel Ortiz wrote:
>
> > > And this looks like an array. Do you really want to do that? That means
> > > lots of reallocations.
>
> > So NFC polling is a bit weird. Whenever you start polling for passive targets,
> > the polling results are minimal: You only know that there is _a_ target on a
> > particular frequency/modulation. It could be the same as the one you got 5
> > minutes ago, or not. To verify it, you'd need to select the target (and put
> > all other ones on standby) and start sending specific commands to it (Of
> > course, you have a different set of commands per target family...). Only then
> > you _may_ read some sort of UID that could help you matching targets from your
> > previous poll cycle.
> > My point is, when we start polling, we will invalidate all existing targets
> > anyway. So a linked list or an array won't make a big difference in that area.
>
> So basically you're saying that you basically get everything at the same
> time so you really throw away the old list/array and re-create it.
Basically, yes. And trying to check which parts of the old list is still there
is quite expensive.


> But from a driver POV, does it really know the number of targets?
It does. From the NFC HW I got access to, it's either one single target, or
at most one per rf band. And in that case the driver can know if there is or
not a target on a specific band.

> Anyway, all this doesn't matter since it's purely internal, and the
> userspace APIs no longer have this limitation, so if this turns out an
> issue at some point internal refactoring can easily fix it :-)
That's certainly true.

Cheers,
Samuel.

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

2011-06-20 17:52:01

by Aloisio Almeida

[permalink] [raw]
Subject: [RFC][PATCH v2 4/7] NFC: add NFC socket family

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 59b3c79..f6f2d62 100644
--- a/include/linux/nfc.h
+++ b/include/linux/nfc.h
@@ -108,4 +108,7 @@ enum nfc_attrs {
#define NFC_PROTO_ISO14443_MASK (1 << NFC_PROTO_ISO14443)
#define NFC_PROTO_NFC_DEP_MASK (1 << NFC_PROTO_NFC_DEP)

+/* 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 5e09d50..1cad6a7 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -401,8 +401,14 @@ static int __init nfc_init(void)
/* the first generation must not be 0 */
nfc_devlist_generation = 1;

+ 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:
@@ -411,6 +417,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 878a333..d3a1fa5 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);

extern int nfc_devlist_generation;
extern struct mutex nfc_devlist_mutex;
--
1.7.4.1


2011-06-21 21:23:45

by Randy Dunlap

[permalink] [raw]
Subject: Re: [RFC][PATCH v3 7/7] NFC: add Documentation/networking/nfc.txt

On Mon, 20 Jun 2011 17:24:41 -0300 Aloisio Almeida Jr wrote:

> Signed-off-by: Aloisio Almeida Jr <[email protected]>
> Signed-off-by: Lauro Ramos Venancio <[email protected]>
> ---
> Documentation/networking/nfc.txt | 128 ++++++++++++++++++++++++++++++++++++++
> 1 files changed, 128 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..cb23b48
> --- /dev/null
> +++ b/Documentation/networking/nfc.txt
> @@ -0,0 +1,128 @@
> +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.
> +
> +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 exchange 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

must inform the core of 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.

is used

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

get specific device info or

> +* NFC_CMD_START_POLL - setup a specific device to polling for targets
> +* NFC_CMD_STOP_POLL - stop the polling operation in a specific device
> +* NFC_CMD_GET_TARGET - dump the list of targets found by a specific device
> +
> +* NFC_EVENT_DEVICE_ADDED - reports a NFC device addition

an NFC

> +* NFC_EVENT_DEVICE_REMOVED - reports a NFC device removal

an NFC

> +* 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 (including the device id). The user must call GET_TARGET to get the list of
> +all targets found by such device. Each reply message has target attributes with
> +relevant information such as the supported NFC protocols.
> +
> +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

create an

> +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.

in an

> +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.
> --


---
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***

2011-06-22 12:57:03

by Samuel Ortiz

[permalink] [raw]
Subject: Re: [RFC][PATCH v2 3/7] NFC: add nfc generic netlink interface

Hi Johannes,

On Wed, Jun 22, 2011 at 09:34:44AM +0200, Johannes Berg wrote:
>
> > + * @NFC_ATTR_TARGET_SENS_RES: extra information for NFC-A targets
> > + * @NFC_ATTR_TARGET_SEL_RES: extra information for NFC-A targets
>
> Those descriptions aren't very useful to me, but hopefully if I knew
> anything about NFC I'd understand :)
You might, yes :)


> > +struct nfc_target {
> > + u32 idx;
> > + u32 supported_protocols;
> > + u16 sens_res;
> > + u8 sel_res;
> > +};
>
> There's no list_head here.
>
> > struct nfc_dev {
> > unsigned idx;
> > + unsigned target_idx;
> > + struct nfc_target *targets;
>
> And this looks like an array. Do you really want to do that? That means
> lots of reallocations.
So NFC polling is a bit weird. Whenever you start polling for passive targets,
the polling results are minimal: You only know that there is _a_ target on a
particular frequency/modulation. It could be the same as the one you got 5
minutes ago, or not. To verify it, you'd need to select the target (and put
all other ones on standby) and start sending specific commands to it (Of
course, you have a different set of commands per target family...). Only then
you _may_ read some sort of UID that could help you matching targets from your
previous poll cycle.
My point is, when we start polling, we will invalidate all existing targets
anyway. So a linked list or an array won't make a big difference in that area.


> > +/**
> > + * 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
> > + *
> > + * 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 n_targets)
>
> I haven't looked at the API users, but do you really expect them to
> build an array? That seems rather odd since I'd typically expect targets
> to be discovered one by one, not en bloc.
That's a typical use case, yes. But the HCI specs clearly leaves some room for
detecting one target per frequency/modulation. So for example if you have a
Felica card and a Mifare one next to your NFC dongle, the dongle itself will
send an event to the host saying "I found several targets". You then read some
sort of pseudo registry for each rf family to check if there is a target for
it or not. So, the driver gets one single event for several targets found.

> > @@ -298,7 +353,10 @@ int nfc_register_device(struct nfc_dev *dev)
> > rc = device_add(&dev->dev);
> > mutex_unlock(&nfc_devlist_mutex);
> >
> > - return rc;
> > + if (rc < 0)
> > + return rc;
> > +
> > + return nfc_genl_device_added(dev);
>
> No rollback if nfc_genl_device_added() fails? Either you need to ignore
> the error or roll back I think.
True.

Cheers,
Samuel.

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

2011-06-23 07:55:15

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC][PATCH v2 3/7] NFC: add nfc generic netlink interface

On Mon, 2011-06-20 at 14:50 -0300, Aloisio Almeida Jr wrote:
> From: Lauro Ramos Venancio <[email protected]>
>
> The NFC generic netlink interface exports the NFC control operations
> to the user space.

I guess I should say

Reviewed-by: Johannes Berg <[email protected]>

johannes


2011-06-22 13:08:11

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC][PATCH v2 3/7] NFC: add nfc generic netlink interface

On Wed, 2011-06-22 at 14:57 +0200, Samuel Ortiz wrote:

> > And this looks like an array. Do you really want to do that? That means
> > lots of reallocations.

> So NFC polling is a bit weird. Whenever you start polling for passive targets,
> the polling results are minimal: You only know that there is _a_ target on a
> particular frequency/modulation. It could be the same as the one you got 5
> minutes ago, or not. To verify it, you'd need to select the target (and put
> all other ones on standby) and start sending specific commands to it (Of
> course, you have a different set of commands per target family...). Only then
> you _may_ read some sort of UID that could help you matching targets from your
> previous poll cycle.
> My point is, when we start polling, we will invalidate all existing targets
> anyway. So a linked list or an array won't make a big difference in that area.

So basically you're saying that you basically get everything at the same
time so you really throw away the old list/array and re-create it.

But from a driver POV, does it really know the number of targets?
Otherwise it'll have to reallocate.

Anyway, all this doesn't matter since it's purely internal, and the
userspace APIs no longer have this limitation, so if this turns out an
issue at some point internal refactoring can easily fix it :-)

johannes


2011-06-20 20:25:39

by Aloisio Almeida

[permalink] [raw]
Subject: [RFC][PATCH v3 7/7] NFC: add Documentation/networking/nfc.txt

Signed-off-by: Aloisio Almeida Jr <[email protected]>
Signed-off-by: Lauro Ramos Venancio <[email protected]>
---
Documentation/networking/nfc.txt | 128 ++++++++++++++++++++++++++++++++++++++
1 files changed, 128 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..cb23b48
--- /dev/null
+++ b/Documentation/networking/nfc.txt
@@ -0,0 +1,128 @@
+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.
+
+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 exchange 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 specific device
+* NFC_CMD_GET_TARGET - dump the list of targets found by a specific 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 (including the device id). The user must call GET_TARGET to get the list of
+all targets found by such device. Each reply message has target attributes with
+relevant information such as the supported NFC protocols.
+
+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.4.1


2011-06-22 06:56:18

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC][PATCH v2 3/7] NFC: add nfc generic netlink interface

On Tue, 2011-06-21 at 19:05 -0300, Gustavo F. Padovan wrote:

> > 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;
>
> Just return rc here and get rid of the label.


Seriously, please trim your quotes. If you're commenting on a specific
piece of code, you can well remove all the code you're not commenting on
like I did above.

johannes


2011-06-20 17:52:04

by Aloisio Almeida

[permalink] [raw]
Subject: [RFC][PATCH v2 5/7] NFC: add the NFC socket raw protocol

From: Lauro Ramos Venancio <[email protected]>

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 f6f2d62..b8fdfe1 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

@@ -108,7 +111,15 @@ enum nfc_attrs {
#define NFC_PROTO_ISO14443_MASK (1 << NFC_PROTO_ISO14443)
#define NFC_PROTO_NFC_DEP_MASK (1 << NFC_PROTO_NFC_DEP)

+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 1cad6a7..f0bc1a7 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -401,6 +401,10 @@ static int __init nfc_init(void)
/* the first generation must not be 0 */
nfc_devlist_generation = 1;

+ rc = rawsock_init();
+ if (rc)
+ goto err_rawsock;
+
rc = af_nfc_init();
if (rc)
goto err_af_nfc;
@@ -408,6 +412,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);
@@ -418,6 +424,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 d3a1fa5..2ef2b56 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.4.1


2011-06-20 17:51:56

by Aloisio Almeida

[permalink] [raw]
Subject: [RFC][PATCH v2 2/7] NFC: add nfc subsystem core

From: Lauro Ramos Venancio <[email protected]>

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 | 345 ++++++++++++++++++++++++++++++++++++++++++++++++++
net/nfc/nfc.h | 71 ++++++++++
11 files changed, 588 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..f4710fe
--- /dev/null
+++ b/net/nfc/core.c
@@ -0,0 +1,345 @@
+/*
+ * 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"
+
+int nfc_devlist_generation;
+DEFINE_MUTEX(nfc_devlist_mutex);
+
+/**
+ * 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)
+{
+ int rc;
+
+ pr_debug("%s: dev_name:%s", __func__, dev_name(&dev->dev));
+
+ mutex_lock(&nfc_devlist_mutex);
+ nfc_devlist_generation++;
+ rc = device_add(&dev->dev);
+ mutex_unlock(&nfc_devlist_mutex);
+
+ return rc;
+}
+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));
+
+ mutex_lock(&nfc_devlist_mutex);
+ nfc_devlist_generation++;
+
+ /* lock to avoid unregistering a device while an operation
+ is in progress */
+ device_lock(&dev->dev);
+ device_del(&dev->dev);
+ device_unlock(&dev->dev);
+
+ mutex_unlock(&nfc_devlist_mutex);
+}
+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..ad19e4a
--- /dev/null
+++ b/net/nfc/nfc.h
@@ -0,0 +1,71 @@
+/*
+ * 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>
+
+extern int nfc_devlist_generation;
+extern struct mutex nfc_devlist_mutex;
+
+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.4.1


2011-06-22 16:49:39

by Aloisio Almeida

[permalink] [raw]
Subject: Re: [RFC][PATCH v2 3/7] NFC: add nfc generic netlink interface

Hi Johannes,

On Wed, Jun 22, 2011 at 4:34 AM, Johannes Berg
<[email protected]> wrote:
>
>> + * @NFC_ATTR_TARGET_SENS_RES: extra information for NFC-A targets
>> + * @NFC_ATTR_TARGET_SEL_RES: extra information for NFC-A targets
>
> Those descriptions aren't very useful to me, but hopefully if I knew
> anything about NFC I'd understand :)

Yes. But I can change a little bit:

@NFC_ATTR_TARGET_SENS_RES: NFC-A targets extra information such as NFCID
@NFC_ATTR_TARGET_SEL_RES: NFC-A targets extra information (useful if
the target is not NFC-Forum compliant)

>> + ? ? dev->targets_generation++;
>> +
>> + ? ? kfree(dev->targets);
>> + ? ? dev->targets = kzalloc(n_targets * sizeof(struct nfc_target),
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? GFP_ATOMIC);
>> + ? ? if (!dev->targets) {
>> + ? ? ? ? ? ? dev->n_targets = 0;
>> + ? ? ? ? ? ? spin_unlock_bh(&dev->targets_lock);
>> + ? ? ? ? ? ? return -ENOMEM;
>> + ? ? }
>> +
>> + ? ? memcpy(dev->targets, targets, n_targets * sizeof(struct nfc_target));
>> + ? ? dev->n_targets = n_targets;
>
> If you really want to keep the array thing at least you should use
> kmemdup(). I'd argue that the overhead won't be huge even if you don't,
> since as you said yourself you don't expect tons of targets (and if you
> do have tons of targets, this approach breaks down anyway).

Ok, I will change kmalloc()/memcpy() by kmemdup().

>> @@ -298,7 +353,10 @@ int nfc_register_device(struct nfc_dev *dev)
>> ? ? ? rc = device_add(&dev->dev);
>> ? ? ? mutex_unlock(&nfc_devlist_mutex);
>>
>> - ? ? return rc;
>> + ? ? if (rc < 0)
>> + ? ? ? ? ? ? return rc;
>> +
>> + ? ? return nfc_genl_device_added(dev);
>
> No rollback if nfc_genl_device_added() fails? Either you need to ignore
> the error or roll back I think.
>
>> +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;
>
> I'd probably ignore the errors since any error just means userspace
> wasn't notified, but it can still later look up the devices.

Yes, you're right. I'll fix that.

Aloisio

2011-06-22 07:34:49

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC][PATCH v2 3/7] NFC: add nfc generic netlink interface


> + * @NFC_ATTR_TARGET_SENS_RES: extra information for NFC-A targets
> + * @NFC_ATTR_TARGET_SEL_RES: extra information for NFC-A targets

Those descriptions aren't very useful to me, but hopefully if I knew
anything about NFC I'd understand :)

> +struct nfc_target {
> + u32 idx;
> + u32 supported_protocols;
> + u16 sens_res;
> + u8 sel_res;
> +};

There's no list_head here.

> struct nfc_dev {
> unsigned idx;
> + unsigned target_idx;
> + struct nfc_target *targets;

And this looks like an array. Do you really want to do that? That means
lots of reallocations.

> +/**
> + * 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
> + *
> + * 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 n_targets)

I haven't looked at the API users, but do you really expect them to
build an array? That seems rather odd since I'd typically expect targets
to be discovered one by one, not en bloc.

> + dev->targets_generation++;
> +
> + kfree(dev->targets);
> + dev->targets = kzalloc(n_targets * sizeof(struct nfc_target),
> + GFP_ATOMIC);
> + if (!dev->targets) {
> + dev->n_targets = 0;
> + spin_unlock_bh(&dev->targets_lock);
> + return -ENOMEM;
> + }
> +
> + memcpy(dev->targets, targets, n_targets * sizeof(struct nfc_target));
> + dev->n_targets = n_targets;

If you really want to keep the array thing at least you should use
kmemdup(). I'd argue that the overhead won't be huge even if you don't,
since as you said yourself you don't expect tons of targets (and if you
do have tons of targets, this approach breaks down anyway).

I'd really consider going with a linked list.

> @@ -298,7 +353,10 @@ int nfc_register_device(struct nfc_dev *dev)
> rc = device_add(&dev->dev);
> mutex_unlock(&nfc_devlist_mutex);
>
> - return rc;
> + if (rc < 0)
> + return rc;
> +
> + return nfc_genl_device_added(dev);

No rollback if nfc_genl_device_added() fails? Either you need to ignore
the error or roll back I think.

> +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;

I'd probably ignore the errors since any error just means userspace
wasn't notified, but it can still later look up the devices.

johannes


2011-06-21 22:15:44

by Eliad Peller

[permalink] [raw]
Subject: Re: [RFC][PATCH v2 3/7] NFC: add nfc generic netlink interface

On Wed, Jun 22, 2011 at 1:05 AM, Gustavo F. Padovan
<[email protected]> wrote:
>> +static int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct netlink_callback *cb, int flags)
>> +{
>> + ? ? void *hdr;
>> +
>> + ? ? pr_debug("%s\n", __func__);
>> +
>> + ? ? hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? &nfc_genl_family, flags, NFC_CMD_GET_TARGET);
>> + ? ? if (!hdr)
>> + ? ? ? ? ? ? return -EMSGSIZE;
>> +
>> + ? ? genl_dump_check_consistent(cb, hdr, &nfc_genl_family);
>> +
>> + ? ? NLA_PUT_U32(msg, NFC_ATTR_TARGET_INDEX, target->idx);
>> + ? ? NLA_PUT_U32(msg, NFC_ATTR_PROTOCOLS,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? target->supported_protocols);
>> + ? ? NLA_PUT_U16(msg, NFC_ATTR_TARGET_SENS_RES, target->sens_res);
>> + ? ? NLA_PUT_U8(msg, NFC_ATTR_TARGET_SEL_RES, target->sel_res);
>> +
>> + ? ? return genlmsg_end(msg, hdr);
>> +
>> +nla_put_failure:
>
> There is no use for this macro in all function that have a label with this
> name.

the NLA_PUT_* macros use this label:

#define NLA_PUT(skb, attrtype, attrlen, data) \
do { \
if (unlikely(nla_put(skb, attrtype, attrlen, data) < 0)) \
goto nla_put_failure; \
} while(0)

Eliad.

2011-06-22 19:55:31

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [RFC][PATCH v2 3/7] NFC: add nfc generic netlink interface

Hi Johannes,

* Johannes Berg <[email protected]> [2011-06-22 08:56:11 +0200]:

> On Tue, 2011-06-21 at 19:05 -0300, Gustavo F. Padovan wrote:
>
> > > 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;
> >
> > Just return rc here and get rid of the label.
>
>
> Seriously, please trim your quotes. If you're commenting on a specific
> piece of code, you can well remove all the code you're not commenting on
> like I did above.

Sorry, I didn't realize that it was so big. Also I usually forget to trim the
beginning of the patch. Have to get used to that.

Gustavo

2011-06-22 02:24:45

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [RFC][PATCH v2 2/7] NFC: add nfc subsystem core

Hi Gustavo,

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

<snip>

> > +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.
>
> I think that use only dynamic debug is a lot nicer. I don't see a point to
> have a NFC debug option in Kconfig. We've been using dynamic debug in the
> Bluetooth subsystem for a while and it works fine. If we have dynamic debug
> there is no need to recompile a module to add debug support to it.
>
> Also it's a good idea use macros for pr_debug, instead of calling it every
> time with the same paramenters (__func__, for example). In the Bluetooth
> subsystem we do like this:
>
> #define BT_INFO(fmt, arg...) printk(KERN_INFO "Bluetooth: " fmt "\n" , ## arg)
> #define BT_ERR(fmt, arg...) printk(KERN_ERR "%s: " fmt "\n" , __func__ , ##
> arg)
> #define BT_DBG(fmt, arg...) pr_debug("%s: " fmt "\n" , __func__ , ## arg)

I would clearly second this. Switching the Bluetooth subsystem to
dynamic debug made so many things so much easier. And using a macro like
NFC_DBG makes it pretty nice and clean.

Regards

Marcel



2011-06-21 21:55:36

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [RFC][PATCH v2 2/7] NFC: add nfc subsystem core

* Aloisio Almeida Jr <[email protected]> [2011-06-20 14:50:07 -0300]:

> From: Lauro Ramos Venancio <[email protected]>
>
> 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 | 345 ++++++++++++++++++++++++++++++++++++++++++++++++++
> net/nfc/nfc.h | 71 ++++++++++
> 11 files changed, 588 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.

I think that use only dynamic debug is a lot nicer. I don't see a point to
have a NFC debug option in Kconfig. We've been using dynamic debug in the
Bluetooth subsystem for a while and it works fine. If we have dynamic debug
there is no need to recompile a module to add debug support to it.

Also it's a good idea use macros for pr_debug, instead of calling it every
time with the same paramenters (__func__, for example). In the Bluetooth
subsystem we do like this:

#define BT_INFO(fmt, arg...) printk(KERN_INFO "Bluetooth: " fmt "\n" , ## arg)
#define BT_ERR(fmt, arg...) printk(KERN_ERR "%s: " fmt "\n" , __func__ , ##
arg)
#define BT_DBG(fmt, arg...) pr_debug("%s: " fmt "\n" , __func__ , ## arg)

Gustavo