NFC: Upgrade to latest nfc-next tree
authorWaldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Thu, 11 Oct 2012 12:03:58 +0000 (14:03 +0200)
committerArron Wang <arron.wang@intel.com>
Wed, 24 Apr 2013 07:06:13 +0000 (15:06 +0800)
NFC: pn533: Fix in/out frame buffer allocation

Max frame size should be 264 bytes as per spec and not limited to
endpoint MaxPacketSize which is 64 in my case (acr122 reader).

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Remove unused arg parameter

Get rid of unused arg param in pn533_init_target_complete and
in pn533_start_poll_complete.

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Use IDR library to assing NFC devices IDs

As a consequence the NFC device IDs won't be increasing all the time,
as IDR provides the first available ID.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Fix some code style and whitespace issues

Signed-off-by: Szymon Janc <szymon.janc@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Remove CONFIG_EXPERIMENTAL

This config item has not carried much meaning for a while now and is
almost always enabled by default. As agreed during the Linux kernel
summit, remove it.

Acked-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Remove CONFIG_EXPERIMENTAL from the LLCP Makefile

This config item has not carried much meaning for a while now and is
almost always enabled by default. As agreed during the Linux kernel
summit, remove it.

Acked-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Remove CONFIG_EXPERIMENTAL from the NCI Makefile

This config item has not carried much meaning for a while now and is
almost always enabled by default. As agreed during the Linux kernel
summit, remove it.

Acked-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Purge LLCP socket Tx queues when being disconnected

The Tx queues are no longer valid when we receive a disconnection or when
the LLCP link goes down. In the later case we also purge the entire local
Tx queue.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Extend netlink interface for LTO, RW, and MIUX parameters support

NFC_CMD_LLC_GET_PARAMS: request LTO, RW, and MIUX parameters for a device

NFC_CMD_LLC_SET_PARAMS: set one or more of LTO, RW, and MIUX parameters for
a device. LTO must be set before the link is up otherwise -EINPROGRESS is
returned. RW and MIUX can be set at anytime and will be passed in subsequent
CONNECT and CC messages. If one of the passed parameters is wrong none is
set and -EINVAL is returned.

Signed-off-by: Thierry Escande <thierry.escande@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Fix hci_connect_gate() when a pre-opened pipe is passed

In some cases, pre-opened pipes don't stay open when a clear all pipes
command is sent. They stay created however. Therefore, one can never
assume that such a pipe is already open. As re-opening a pipe seems not
to be a problem, we do that now.

Signed-off-by: Eric Lapuyade <eric.lapuyade@intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Ignore err when chip doesn't implement HW/SW info registers

NFC_HCI_ID_MGMT_VERSION_SW and NFC_HCI_ID_MGMT_VERSION_HW are optional
registers for gate NFC_HCI_ID_MGMT_GATE in standard HCI. When chip
doesn't implement, just leave all the information as zeros.

Signed-off-by: Eric Lapuyade <eric.lapuyade@intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Dot not dispatch HCI event received on unopened pipe

A chip with pre-opened gates may send events on a gate that nobody
has opened in the handset host. Discard those events.

Signed-off-by: Eric Lapuyade <eric.lapuyade@intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Export nfc_hci_result_to_errno as it can be needed by HCI drivers

Signed-off-by: Eric Lapuyade <eric.lapuyade@intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Export nfc_hci_sak_to_protocol()

Some HCI drivers will need it.

Signed-off-by: Eric Lapuyade <eric.lapuyade@intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Remove unused details from pn544.h header file

The majority of the defines and structures from pn544.h are no
longer in use. So just remove them.

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Move pn544.h to linux/platform_data/

The pn544.h just provides the platform data struct and defines and
nothing else. So move it to to linux/platform_data/ now.

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Copy user space buffer when sending UI frames

Using the userspace IO vector directly is wrong, we should copy it from
user space first.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Stop sending LLCP frames when tx queues are getting too deep

When the tx pending queues and/or the socket tx queue is getting too deep,
we have to let userspace know. We won't be queueing any more frames until
the congestion is fixed.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Queue a copy of the transmitted LLCP skb

Drivers are allowed to modify the sent skb and thus we need to make a copy
of it before passing it to the driver. Without this fix, LLCP Tx skbs were
not queued properly as the ptype check was failing due to e.g. the pn533
driver skb_pushing the Tx skb.

Reported-by: Thierry Escande <thierry.escande@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Fix nfc_llcp_local chained list insertion

list_add was called with swapped parameters

Signed-off-by: Thierry Escande <thierry.escande@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Fix missing lock while operating on commands list

In pn533_wq_cmd command was removed from list without cmd_lock held
(race with pn533_send_cmd_frame_async) which could lead to list
corruption. Delete command from list before releasing lock.

Signed-off-by: Szymon Janc <szymon.janc@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Fix use after free

cmd was freed in pn533_dep_link_up regardless of
pn533_send_cmd_frame_async return code. Cmd is passed as argument to
pn533_in_dep_link_up_complete callback and should be freed there.

Signed-off-by: Szymon Janc <szymon.janc@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Fix mem leak in pn533_in_dep_link_up

cmd is allocated in pn533_dep_link_up and passed as an arg to
pn533_send_cmd_frame_async together with a complete cb.

arg is passed to the cb and must be kfreed there.

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Fix pn533 target mode memory leak

In target mode, sent sk_buff were not freed in pn533_tm_send_complete

Signed-off-by: Thierry Escande <thierry.escande@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Fix incorrect llcp pointer dereference

nfc_llcp_ns(s) dereferences the s pointer which is freed a line
above. In a result, it can produce a crash or you will read
incorrect value.

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Add support for SO_TIMESTAMP LLCP socket option

Set timestamp in sent and received sk_buffs. timestamp is then put in
msghdr structure in llcp_sock_recvmsg().

Signed-off-by: Thierry Escande <thierry.escande@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Remove in/out_maxlen as it is not used

in_maxlen and out_maxlen was replaced with PN533_NORMAL_FRAME_MAX_LEN

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Remove unused definitions

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Add frame header length define

PN533_CMD_DATAEXCH_HEAD_LEN includes a frame header length which is not
seen at a glance. It can be missleading, so split it and define the
frame header length explicitly.

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Remove pointless flags param

__pn533_send_cmd_frame_async() is called when lock is held so GFP_KERNEL
flag will be always used. Thus, having extra param does not optimise the
code.

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Add a new pn533_send_cmd_async iface

This is intendend to replace pn533_send_cmd_frame_async() which
requires from the caller to create a complete frame.

The new function constructs a frame and sends it out which hides the
frame logic and avoid code duplication.

The caller has to allocate skb and put its payload there, and finally
provide the skb together with a complete cb to pn533_send_cmd_async().

Response skb is allocated by the core part and pass to the caller cb.
Next, the caller has to free it when is not needed anymore or pass it
up to the stack.

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Add a new pn533_send_data_async iface

This iface is intended to be used with DEP transfers. It differs
from pn533_send_cmd_async() in the way the response skb is allocated.

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Add a new pn533_send_sync iface

It is intended to replace pn533_send_cmd_frame_sync() iface which
requires from the caller to create complete frame.

The new function constructs a complete frame itself and sends it out
in sync manner. This way frame logic is hidden from the caller.

pn533_send_cmd_sync() returns ERR_PTR in case of an error or a pointer
to valid response sk_buff otherwise. The pointer must be freed by the
caller when it's been consumed.

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Add pn533_alloc_skb for req allocation

Allocate sk_buff for the request.

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Remove pn533_send_cmd_frame_sync

Remove obsolate pn533_send_cmd_frame_sync() and use
previously added new iface for sync send.

The new interface require the use of individual skb for each
cmd which removes some memcpy calls and hides frame logic.

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Remove the tx backlog queue

Not only it was improperly use to queue backlogged RX skbuffs, but it was
also not processed at all.
If the socket receive queue is full we simply drop the incoming packets.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
workqueue: avoid using deprecated functions

The network merge brought in a few users of functions that got
deprecated by the workqueue cleanups: the 'system_nrt_wq' is now the
same as the regular system_wq, since all workqueues are now non-
reentrant.

Similarly, remove one use of flush_work_sync() - the regular
flush_work() has become synchronous, and the "_sync()" version is thus
deprecated as being superfluous.

Change-Id: I6189414648e447f81053b03669ce23e528b441a9
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
NFC: llcp: Fix Rx memory leak

The reference count bump on the llcp Rx path is leading to a memory leak
whenever we're not receiving an I frame.
We fix that by removing the refcount bump (drivers must not free their
received skb) and using it only in the I frame path, when the frame is
actually queued. In that case, the skb will only be freed when someone
fetches it from userspace. in all other cases, LLCP received frames will
be freed when leaving the Rx work queue.

Reported-by: Eric Lapuyade <eric.lapuyade@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Del frame logic from TgGet/SetData cmd

Remove frame logic from TgSetData and TgGetData commands
using the new iface for async send.

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Del frame logic from InJumForDep cmd

Remove frame logic from InJmumpForDEP command using
the new iface for async send.

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Del frame logic from Data Exchange cmd

Remove frame logic from transceive cb using new iface
for async send.

For pn533_wq_mi_recv() use pn533_send_cmd_direct_async which
sends the cmd directly to the hardware, skipping cmd queue.

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Dump tx and rx data in debug mode

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Remove frame logic from poll cmds

Remove frame logic from start_pool cb using the new iface
for async send.

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Fix quoted strings split across lines

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Fix spacing issues

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Fix open parenthesis alignment

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Fix minor style issues

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Remove unused pn533_send_cmd_frame_asy

Remove obsolete send async api as it's no longer used. Remove
global dev->in_frame as well, as each packet is kept is a
seperate skb struct now, so that's not used anymore.

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Fix urb->status handling

Fix text message to be more suitable for the error code and treat
ESHUTDOWN as an error not debug msg.

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Cleanup debug messages

Remove debug messages which do not include valueable informations
in debug mode. Add some new ones for better tracking or reword when
if necessary.

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Don't use out_frame in pn533_send_ack

dev->out_frame buffer is much bigger for ACK frame needs. Use
local buffer instead.

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Use skb in __pn533_send_cmd_frame_asyn

__pn533_send_cmd_frame_async() should be frame type independent. So, don't
use pn533_frame type params and instead use skb for req and resp pointers.

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Remove deprecated dev->out_frame buff

As it's not used anymore get rid of that buffer.

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Cleanup pn533_cmd_complete_t

'params' arg in pn533_cmd_complete_t definition has been deprecated and
currently is not in use (resp skb is pass in arg ptr), so remove it.

Also 'params_len' arg is used as a transfer status indicator, so simply
reword it appropriately.

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Remove unused dev->wq_in_frame

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Introduce ops for frame logic

Encapsulate whole frame logic (tx/rx frame structure and size) inside
the ops structure to make the core driver generic for devices which
handle frames in non standard menner (different then pn533 spec say).

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: nfcwilink: Use devm_kzalloc

devm_kzalloc allocates memory that is released when a driver detaches.
This patch uses devm_kzalloc for data that is allocated in the probe
function of a platform device and is only freed in the remove function.

Signed-off-by: Julia Lawall <Julia.Lawall@lip6.fr>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Fixed nfc core and hci unregistration and cleanup

When an adapter is removed, it will unregister itself from hci and/or
nfc core. In order to do that safely, work tasks must first be canceled
and prevented to be scheduled again, before the hci or nfc device can be
destroyed.

Signed-off-by: Eric Lapuyade <eric.lapuyade@intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Added error handling in event_received hci ops

There is no use to return an error if the caller doesn't get it.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Changed event_received hci ops result semantic

Some chips use a standard HCI event code, destined to a proprietary
gate, with a different meaning. Therefore, the HCI driver must always
have a chance to intercept the event before standard processing is
attempted.
The new semantic specifies that the result value "1" means that the
driver doesn't especially handle the event. result <= 0 means it was
handled.

Signed-off-by: Eric Lapuyade <eric.lapuyade@intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Fixed skb leak in tm_send() nfc and hci ops implementations

Signed-off-by: Eric Lapuyade <eric.lapuyade@intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Add HCI quirks to support driver (non)standard implementations

Some chips diverge from the HCI spec in their implementation of standard
features. This adds a new quirks parameter to
nfc_hci_allocate_device() to let the driver indicate its divergence.

Signed-off-by: Eric Lapuyade <eric.lapuyade@intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn544: Separate the core code and the i2c one into different modules

As we may need to support other physical layers, we can avoid linking the
core part into each and every pn544 module.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn544: Use module_i2c_driver

The pn544 init routine does nothing but adding the driver to the i2c bus.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn544: Use devm_kzalloc API

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: update HCI documentation

Signed-off-by: Eric Lapuyade <eric.lapuyade@intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Initial Secure Element API

Each NFC adapter can have several links to different secure elements and
that property needs to be exported by the drivers.
A secure element link can be enabled and disabled, and card emulation will
be handled by the currently active one. Otherwise card emulation will be
host implemented.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Fix missing parenthesis

This is a quite critical patch as it fixes potential reference to
undefined general_bytes which were never set correctly on target
activation due to missing parenthesis.

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Update pn544 documentation

The pn544 driver no longer has a /dev/pn544 interface nor a sysfs one.

Reported-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Avoid memcpy on LLCP connection less Rx path

We can cast msg_name to a sockaddr_nfc_llcp pointer directly.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Use skb_copy_datagram_iovec

Safer and more robust than than memcpy_toiovec.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Fix bad allocation size

Use dereferenced pointer in sizeof instead of pointer itself.

Signed-off-by: Thierry Escande <thierry.escande@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Change nfc.h license

nfc.h being GPL makes it quite controversial for non GPL applications to
include it.
Moreover, nfc.h only includes structures and API definitions that are hardly
copyrightable.

Signed-off-by: Lauro Ramos Venancio <lauro.venancio@openbossa.org>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Initial support for Inside Secure microread

Inside Secure microread is an HCI based NFC chipset.
This initial support includes reader and p2p (Target and initiator) modes.

Signed-off-by: Eric Lapuyade <eric.lapuyade@intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: microread: Add i2c physical layer

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: microread: Add MEI physical layer

On some peculiar worlds, microreads are found hidden behind MEIs and needs
to be accessed through the ME bus.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Fix target polling mode

Remove unneeded bitwise OR operator on uninitialized sk_buff data

Signed-off-by: Thierry Escande <thierry.escande@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: integer underflow in nfc_llcp_set_remote_gb()

If gb_len is less than 3 it would cause an integer underflow and
possibly memory corruption in nfc_llcp_parse_gb_tlv().

I removed the old test for gb_len == 0.  I also removed the test for
->remote_gb == NULL.  It's not possible for ->remote_gb to be NULL and
we have already dereferenced ->remote_gb_len so it's too late to test.

The old test return -ENODEV but my test returns -EINVAL.

Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
NFC: microread: Fix mei physical layer

The MEI bus API changed according to the latest comments from the char-misc
maintainers, and this patch fixes the microread mei physical layer code
according to those changes:
We pass the MEI id back to the probe routine, and the mei_driver takes a
table of MEI ids instead of one static id.
Also, mei_bus_driver got renamed to mei_driver, mei_bus_client to
mei_device, and mei_bus_set/get_clientdata to mei_set/get_clientdata.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Decrease socket ack log when accepting a connection

This is really difficult to test with real NFC devices, but without
this fix an LLCP server will eventually refuse new connections.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Clean local timers and works when removing a device

Whenever an adapter is removed we must clean all the local structures,
especially the timers and scheduled work. Otherwise those asynchronous
threads will eventually try to access the freed nfc_dev pointer if an LLCP
link is up.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Clean raw sockets from nfc_llcp_socket_release

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Report error to pending sockets when a device is removed

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: microread: Fix MEI build failure

The mei_device field should be called device, not mei_device.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Rename socket rw and miu fields

They really are remote peer parameters, and we need to distinguish them
from the local ones as we'll modify the latter with socket options.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Implement socket options

Some LLCP services (e.g. the validation ones) require some control over
the LLCP link parameters like the receive window (RW) or the MIU extension
(MIUX). This can only be done through socket options.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Use socket specific link parameters before the local ones

If the socket link options are set, use them before the local one.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Remove redundant printk

We already have a pr_debug for that.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Add missing type policies for netlink attributes

Signed-off-by: Thierry Escande <thierry.escande@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Service Name Lookup SDRES aggregation

This modifies the way SDRES PDUs are sent back. If multiple SDREQs are
received within a single SNL PDU, all SDRES replies are sent packed in
one SNL PDU too.

Signed-off-by: Thierry Escande <thierry.escande@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Service Name Lookup netlink interface

This adds a netlink interface for service name lookup support.
Multiple URIs can be passed nested into the NFC_ATTR_LLC_SDP attribute
using the NFC_CMD_LLC_SDREQ netlink command.
When the SNL reply is received, a NFC_EVENT_LLC_SDRES event is sent to
the user space. URI and SAP tuples are passed back, nested into
NFC_ATTR_LLC_SDP attribute.

Change-Id: If36fbcb0660f246fcd7f9696e7ce6d1e790f99cb
Signed-off-by: Thierry Escande <thierry.escande@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Add cleanup support for unreplied SNL requests

If the remote LLC doesn't reply in time to our SNL requests we remove
them from the list of pending requests. The timeout is fixed to an
arbitrary value of 3 times remote_lto.

When not replied, the local LLC broadcasts NFC_EVENT_LLC_SDRES nl events for
the concerned uris with sap values set to LLCP_SDP_UNBOUND (which is 65).

Change-Id: If6c89a1d3b18942d9a9f45b95c6486d214561c84
Signed-off-by: Thierry Escande <thierry.escande@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Detach socket from process context only when releasing the socket

Calling sock_orphan when e.g. the NFC adapter is removed can lead to
kernel crashes when e.g. a connection less client is sleeping on the
Rx workqueue, waiting for data to show up.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Remove possible double call to kfree_skb

kfree_skb was called twice when the socket receive queue is full

Signed-off-by: Thierry Escande <thierry.escande@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Revert "NFC: microread: Fix MEI build failure"

This reverts commit 63cd353c34a08af2d1935f8d0c2b6b091714ff79.

We no longer need this fix as the MEI bus API are now merged into
char-misc-next.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: microread: Fix build failure due to a new MEI bus API

uuid device_id field is removed and mei_device is renamed mei_cl_device.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: pn533: Use dynamic debug for pn533 hex dumps

Those can be very verbose and we only want them when debugging pn533.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Socket miux is a big endian field

The MIUX must be transmitted in big endian and as such we have to convert
it properly.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Fall back to local values when getting socket options

If a socket option has not been set by the user, fall back to the LLCP
local ones.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Fix zero octets length SDU handling

LLCP Validation test #2 (Connection-less information transfer) send a
service data unit of zero octets length. This is now handled correctly.

Signed-off-by: Olivier Guiter <olivier.guiter@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Aggregated frames support

This adds support for AGF PDUs. For each PDU contained in the AGF, a new sk_buff
is allocated and dispatched to its corresponding handler.

Signed-off-by: Thierry Escande <thierry.escande@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Use localy stored remote_miu value if not set at socket level

If remote_miu value is not set in the socket (i.e. connection-less socket) the
value stored in the local is used.

Signed-off-by: Thierry Escande <thierry.escande@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Reset RW, LTO, and MIU remote parameters when link goes down

This resets remote parameters in both local and socket llcp structures when the
link goes down. That way, nfc_llcp_getsockopt won't return values corresponding
to the previous link parameters.

Signed-off-by: Thierry Escande <thierry.escande@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Add support in getsockopt for RW, LTO, and MIU remote parameters

Useful for LLCP validation tests.

Signed-off-by: Thierry Escande <thierry.escande@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Keep the connected socket parent pointer alive

And avoid decreasing the ack log twice when dequeueing connected LLCP
sockets.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Only keep raw sockets alive when the LLCP local leaves

When the MAC goes down, connected and connection less sockets should be
notified, but raw sockets should be kept alive.
They will get notified only when the physical devices goes away.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Remove local_cleanup last argument

local_cleanup is always called with device set to false as it means the
local LLCP is going away. So no need to pass this switch as an argument.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: llcp: Terminate connection when receiving a DISC on (0,0)

According to the LLCP specs, we must terminate the LLCP link when receiving
a DISC with both ssap and dsap set to 0.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
NFC: Prevent polling when device is down

Some devices turn radio on whenever they're asked to start a poll.
To prevent that from happening, we just don't call into the driver
start_poll hook when the NFC device is down.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
ida: simplified functions for id allocation

The current hyper-optimized functions are overkill if you simply want to
allocate an id for a device.  Create versions which use an internal
lock.

In followup patches, numerous drivers are converted to use this
interface.

Thanks to Tejun for feedback.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Acked-by: Tejun Heo <tj@kernel.org>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
ida: make ida_simple_get/put() IRQ safe

It's often convenient to be able to release resource from IRQ context.
Make ida_simple_*() use irqsave/restore spin ops so that they are IRQ
safe.

Signed-off-by: Tejun Heo <tj@kernel.org>
Acked-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
drivercore: Generalize module_platform_driver

This patch generalizes the module_platform_driver macro and introduces a new
module_driver macro. The module_driver macro takes a driver name, a register
and a unregister function for this driver type. Using these it construct the
module init and exit sections which register and unregister the driver. Since
such init/exit sections are commonly found in drivers this macro can be used
to eliminate a lot of boilerplate code.

The macro is not intended to be used by driver modules directly, instead it
should be used to generate bus specific macros for registering drivers like
the module_platform_driver macro.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Acked-by: Grant Likely <grant.likely@secretlab.ca>
Acked-by: Jonathan Cameron <jic23@kernel.org>
Acked-by: Wolfram Sang <w.sang@pengutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
I2C: Add helper macro for i2c_driver boilerplate

This patch introduces the module_i2c_driver macro which is a convenience macro
for I2C driver modules similar to module_platform_driver. It is intended to be
used by drivers which init/exit section does nothing but register/unregister
the I2C driver. By using this macro it is possible to eliminate a few lines of
boilerplate code per I2C driver.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Acked-by: Grant Likely <grant.likely@secretlab.ca>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Acked-by: Wolfram Sang <w.sang@pengutronix.de>
Acked-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Remove deprecated file

Change-Id: I59c1dc402c556eebc7b5161057258f50f67a25ee

Signed-off-by: Arron Wang <arron.wang@intel.com>
47 files changed:
Documentation/nfc/nfc-hci.txt
Documentation/nfc/nfc-pn544.txt
drivers/nfc/Kconfig
drivers/nfc/Makefile
drivers/nfc/microread/Kconfig [new file with mode: 0644]
drivers/nfc/microread/Makefile [new file with mode: 0644]
drivers/nfc/microread/i2c.c [new file with mode: 0644]
drivers/nfc/microread/mei.c [new file with mode: 0644]
drivers/nfc/microread/microread.c [new file with mode: 0644]
drivers/nfc/microread/microread.h [new file with mode: 0644]
drivers/nfc/nfcwilink.c
drivers/nfc/pn533.c
drivers/nfc/pn544.c [deleted file]
drivers/nfc/pn544/Kconfig [new file with mode: 0644]
drivers/nfc/pn544/Makefile
drivers/nfc/pn544/i2c.c
drivers/nfc/pn544/pn544.c
include/linux/device.h
include/linux/i2c.h
include/linux/idr.h
include/linux/nfc.h
include/linux/nfc/pn544.h [deleted file]
include/linux/platform_data/microread.h [new file with mode: 0644]
include/linux/platform_data/pn544.h [new file with mode: 0644]
include/linux/socket.h
include/net/nfc/hci.h
include/net/nfc/nci_core.h
include/net/nfc/nfc.h
lib/idr.c
net/nfc/Kconfig
net/nfc/core.c
net/nfc/hci/command.c
net/nfc/hci/core.c
net/nfc/hci/hcp.c
net/nfc/hci/llc.c
net/nfc/hci/llc_shdlc.c
net/nfc/hci/shdlc.c [deleted file]
net/nfc/llcp/Kconfig
net/nfc/llcp/commands.c
net/nfc/llcp/llcp.c
net/nfc/llcp/llcp.h
net/nfc/llcp/sock.c
net/nfc/nci/Kconfig
net/nfc/nci/core.c
net/nfc/netlink.c
net/nfc/nfc.h
net/nfc/rawsock.c

index 89a339c..0686c9e 100644 (file)
@@ -17,10 +17,12 @@ HCI
 HCI registers as an nfc device with NFC Core. Requests coming from userspace are
 routed through netlink sockets to NFC Core and then to HCI. From this point,
 they are translated in a sequence of HCI commands sent to the HCI layer in the
-host controller (the chip). The sending context blocks while waiting for the
-response to arrive.
+host controller (the chip). Commands can be executed synchronously (the sending
+context blocks waiting for response) or asynchronously (the response is returned
+from HCI Rx context).
 HCI events can also be received from the host controller. They will be handled
-and a translation will be forwarded to NFC Core as needed.
+and a translation will be forwarded to NFC Core as needed. There are hooks to
+let the HCI driver handle proprietary events or override standard behavior.
 HCI uses 2 execution contexts:
 - one for executing commands : nfc_hci_msg_tx_work(). Only one command
 can be executing at any given moment.
@@ -33,6 +35,8 @@ The Session initialization is an HCI standard which must unfortunately
 support proprietary gates. This is the reason why the driver will pass a list
 of proprietary gates that must be part of the session. HCI will ensure all
 those gates have pipes connected when the hci device is set up.
+In case the chip supports pre-opened gates and pseudo-static pipes, the driver
+can pass that information to HCI core.
 
 HCI Gates and Pipes
 -------------------
@@ -46,6 +50,13 @@ without knowing the pipe connected to it.
 Driver interface
 ----------------
 
+A driver is generally written in two parts : the physical link management and
+the HCI management. This makes it easier to maintain a driver for a chip that
+can be connected using various phy (i2c, spi, ...)
+
+HCI Management
+--------------
+
 A driver would normally register itself with HCI and provide the following
 entry points:
 
@@ -53,58 +64,113 @@ struct nfc_hci_ops {
        int (*open)(struct nfc_hci_dev *hdev);
        void (*close)(struct nfc_hci_dev *hdev);
        int (*hci_ready) (struct nfc_hci_dev *hdev);
-       int (*xmit)(struct nfc_hci_dev *hdev, struct sk_buff *skb);
-       int (*start_poll)(struct nfc_hci_dev *hdev, u32 protocols);
-       int (*target_from_gate)(struct nfc_hci_dev *hdev, u8 gate,
-                               struct nfc_target *target);
+       int (*xmit) (struct nfc_hci_dev *hdev, struct sk_buff *skb);
+       int (*start_poll) (struct nfc_hci_dev *hdev,
+                          u32 im_protocols, u32 tm_protocols);
+       int (*dep_link_up)(struct nfc_hci_dev *hdev, struct nfc_target *target,
+                          u8 comm_mode, u8 *gb, size_t gb_len);
+       int (*dep_link_down)(struct nfc_hci_dev *hdev);
+       int (*target_from_gate) (struct nfc_hci_dev *hdev, u8 gate,
+                                struct nfc_target *target);
        int (*complete_target_discovered) (struct nfc_hci_dev *hdev, u8 gate,
                                           struct nfc_target *target);
-       int (*data_exchange) (struct nfc_hci_dev *hdev,
-                             struct nfc_target *target,
-                             struct sk_buff *skb, struct sk_buff **res_skb);
+       int (*im_transceive) (struct nfc_hci_dev *hdev,
+                             struct nfc_target *target, struct sk_buff *skb,
+                             data_exchange_cb_t cb, void *cb_context);
+       int (*tm_send)(struct nfc_hci_dev *hdev, struct sk_buff *skb);
        int (*check_presence)(struct nfc_hci_dev *hdev,
                              struct nfc_target *target);
+       int (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event,
+                             struct sk_buff *skb);
 };
 
 - open() and close() shall turn the hardware on and off.
 - hci_ready() is an optional entry point that is called right after the hci
 session has been set up. The driver can use it to do additional initialization
 that must be performed using HCI commands.
-- xmit() shall simply write a frame to the chip.
+- xmit() shall simply write a frame to the physical link.
 - start_poll() is an optional entrypoint that shall set the hardware in polling
 mode. This must be implemented only if the hardware uses proprietary gates or a
 mechanism slightly different from the HCI standard.
+- dep_link_up() is called after a p2p target has been detected, to finish
+the p2p connection setup with hardware parameters that need to be passed back
+to nfc core.
+- dep_link_down() is called to bring the p2p link down.
 - target_from_gate() is an optional entrypoint to return the nfc protocols
 corresponding to a proprietary gate.
 - complete_target_discovered() is an optional entry point to let the driver
 perform additional proprietary processing necessary to auto activate the
 discovered target.
-- data_exchange() must be implemented by the driver if proprietary HCI commands
+- im_transceive() must be implemented by the driver if proprietary HCI commands
 are required to send data to the tag. Some tag types will require custom
 commands, others can be written to using the standard HCI commands. The driver
 can check the tag type and either do proprietary processing, or return 1 to ask
-for standard processing.
+for standard processing. The data exchange command itself must be sent
+asynchronously.
+- tm_send() is called to send data in the case of a p2p connection
 - check_presence() is an optional entry point that will be called regularly
 by the core to check that an activated tag is still in the field. If this is
 not implemented, the core will not be able to push tag_lost events to the user
 space
+- event_received() is called to handle an event coming from the chip. Driver
+can handle the event or return 1 to let HCI attempt standard processing.
 
 On the rx path, the driver is responsible to push incoming HCP frames to HCI
 using nfc_hci_recv_frame(). HCI will take care of re-aggregation and handling
 This must be done from a context that can sleep.
 
-SHDLC
------
+PHY Management
+--------------
+
+The physical link (i2c, ...) management is defined by the following struture:
+
+struct nfc_phy_ops {
+       int (*write)(void *dev_id, struct sk_buff *skb);
+       int (*enable)(void *dev_id);
+       void (*disable)(void *dev_id);
+};
+
+enable(): turn the phy on (power on), make it ready to transfer data
+disable(): turn the phy off
+write(): Send a data frame to the chip. Note that to enable higher
+layers such as an llc to store the frame for re-emission, this function must
+not alter the skb. It must also not return a positive result (return 0 for
+success, negative for failure).
+
+Data coming from the chip shall be sent directly to nfc_hci_recv_frame().
+
+LLC
+---
+
+Communication between the CPU and the chip often requires some link layer
+protocol. Those are isolated as modules managed by the HCI layer. There are
+currently two modules : nop (raw transfert) and shdlc.
+A new llc must implement the following functions:
+
+struct nfc_llc_ops {
+       void *(*init) (struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv,
+                      rcv_to_hci_t rcv_to_hci, int tx_headroom,
+                      int tx_tailroom, int *rx_headroom, int *rx_tailroom,
+                      llc_failure_t llc_failure);
+       void (*deinit) (struct nfc_llc *llc);
+       int (*start) (struct nfc_llc *llc);
+       int (*stop) (struct nfc_llc *llc);
+       void (*rcv_from_drv) (struct nfc_llc *llc, struct sk_buff *skb);
+       int (*xmit_from_hci) (struct nfc_llc *llc, struct sk_buff *skb);
+};
+
+- init() : allocate and init your private storage
+- deinit() : cleanup
+- start() : establish the logical connection
+- stop () : terminate the logical connection
+- rcv_from_drv() : handle data coming from the chip, going to HCI
+- xmit_from_hci() : handle data sent by HCI, going to the chip
 
-Most chips use shdlc to ensure integrity and delivery ordering of the HCP
-frames between the host controller (the chip) and hosts (entities connected
-to the chip, like the cpu). In order to simplify writing the driver, an shdlc
-layer is available for use by the driver.
-When used, the driver actually registers with shdlc, and shdlc will register
-with HCI. HCI sees shdlc as the driver and thus send its HCP frames
-through shdlc->xmit.
-SHDLC adds a new execution context (nfc_shdlc_sm_work()) to run its state
-machine and handle both its rx and tx path.
+The llc must be registered with nfc before it can be used. Do that by
+calling nfc_llc_register(const char *name, struct nfc_llc_ops *ops);
+
+Again, note that the llc does not handle the physical link. It is thus very
+easy to mix any physical link with any llc for a given chip driver.
 
 Included Drivers
 ----------------
@@ -117,10 +183,12 @@ Execution Contexts
 
 The execution contexts are the following:
 - IRQ handler (IRQH):
-fast, cannot sleep. stores incoming frames into an shdlc rx queue
+fast, cannot sleep. sends incoming frames to HCI where they are passed to
+the current llc. In case of shdlc, the frame is queued in shdlc rx queue.
 
 - SHDLC State Machine worker (SMW)
-handles shdlc rx & tx queues. Dispatches HCI cmd responses.
+Only when llc_shdlc is used: handles shdlc rx & tx queues.
+Dispatches HCI cmd responses.
 
 - HCI Tx Cmd worker (MSGTXWQ)
 Serializes execution of HCI commands. Completes execution in case of response
@@ -166,6 +234,15 @@ waiting command execution. Response processing involves invoking the completion
 callback that was provided by nfc_hci_msg_tx_work() when it sent the command.
 The completion callback will then wake the syscall context.
 
+It is also possible to execute the command asynchronously using this API:
+
+static int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
+                              const u8 *param, size_t param_len,
+                              data_exchange_cb_t cb, void *cb_context)
+
+The workflow is the same, except that the API call returns immediately, and
+the callback will be called with the result from the SMW context.
+
 Workflow receiving an HCI event or command
 ------------------------------------------
 
index 2fcac9f..b36ca14 100644 (file)
@@ -1,32 +1,15 @@
 Kernel driver for the NXP Semiconductors PN544 Near Field
 Communication chip
 
-Author: Jari Vanhala
-Contact: Matti Aaltonen (matti.j.aaltonen at nokia.com)
-
 General
 -------
 
 The PN544 is an integrated transmission module for contactless
 communication. The driver goes under drives/nfc/ and is compiled as a
-module named "pn544". It registers a misc device and creates a device
-file named "/dev/pn544".
+module named "pn544".
 
 Host Interfaces: I2C, SPI and HSU, this driver supports currently only I2C.
 
-The Interface
--------------
-
-The driver offers a sysfs interface for a hardware test and an IOCTL
-interface for selecting between two operating modes. There are read,
-write and poll functions for transferring messages. The two operating
-modes are the normal (HCI) mode and the firmware update mode.
-
-PN544 is controlled by sending messages from the userspace to the
-chip. The main function of the driver is just to pass those messages
-without caring about the message content.
-
-
 Protocols
 ---------
 
@@ -47,68 +30,3 @@ and third (LSB) bytes of the message. The maximum FW message length is
 
 For the ETSI HCI specification see
 http://www.etsi.org/WebSite/Technologies/ProtocolSpecification.aspx
-
-The Hardware Test
------------------
-
-The idea of the test is that it can performed by reading from the
-corresponding sysfs file. The test is implemented in the board file
-and it should test that PN544 can be put into the firmware update
-mode. If the test is not implemented the sysfs file does not get
-created.
-
-Example:
-> cat /sys/module/pn544/drivers/i2c\:pn544/3-002b/nfc_test
-1
-
-Normal Operation
-----------------
-
-PN544 is powered up when the device file is opened, otherwise it's
-turned off. Only one instance can use the device at a time.
-
-Userspace applications control PN544 with HCI messages. The hardware
-sends an interrupt when data is available for reading. Data is
-physically read when the read function is called by a userspace
-application. Poll() checks the read interrupt state. Configuration and
-self testing are also done from the userspace using read and write.
-
-Example platform data:
-
-static int rx71_pn544_nfc_request_resources(struct i2c_client *client)
-{
-       /* Get and setup the HW resources for the device */
-}
-
-static void rx71_pn544_nfc_free_resources(void)
-{
-       /* Release the HW resources */
-}
-
-static void rx71_pn544_nfc_enable(int fw)
-{
-       /* Turn the device on */
-}
-
-static int rx71_pn544_nfc_test(void)
-{
-       /*
-        * Put the device into the FW update mode
-        * and then back to the normal mode.
-        * Check the behavior and return one on success,
-        * zero on failure.
-        */
-}
-
-static void rx71_pn544_nfc_disable(void)
-{
-       /* turn the power off */
-}
-
-static struct pn544_nfc_platform_data rx71_nfc_data = {
-       .request_resources = rx71_pn544_nfc_request_resources,
-       .free_resources = rx71_pn544_nfc_free_resources,
-       .enable = rx71_pn544_nfc_enable,
-       .test = rx71_pn544_nfc_test,
-       .disable = rx71_pn544_nfc_disable,
-};
index ec85767..e570349 100644 (file)
@@ -5,19 +5,6 @@
 menu "Near Field Communication (NFC) devices"
        depends on NFC
 
-config PN544_HCI_NFC
-       tristate "HCI PN544 NFC driver"
-       depends on I2C && NFC_HCI && NFC_SHDLC
-       select CRC_CCITT
-       default n
-       ---help---
-         NXP PN544 i2c driver.
-         This is a driver based on the SHDLC and HCI NFC kernel layers and
-         will thus not work with NXP libnfc library.
-
-         To compile this driver as a module, choose m here. The module will
-         be called pn544_hci.
-
 config NFC_PN533
        tristate "NXP PN533 USB driver"
        depends on USB
@@ -39,4 +26,7 @@ config NFC_WILINK
          Say Y here to compile support for Texas Instrument's NFC WiLink driver
          into the kernel or say M to compile it as module.
 
+source "drivers/nfc/pn544/Kconfig"
+source "drivers/nfc/microread/Kconfig"
+
 endmenu
index 36c3590..a189ada 100644 (file)
@@ -2,7 +2,8 @@
 # Makefile for nfc devices
 #
 
-obj-$(CONFIG_PN544_HCI_NFC)    += pn544/
+obj-$(CONFIG_NFC_PN544)                += pn544/
+obj-$(CONFIG_NFC_MICROREAD)    += microread/
 obj-$(CONFIG_NFC_PN533)                += pn533.o
 obj-$(CONFIG_NFC_WILINK)       += nfcwilink.o
 
diff --git a/drivers/nfc/microread/Kconfig b/drivers/nfc/microread/Kconfig
new file mode 100644 (file)
index 0000000..572305b
--- /dev/null
@@ -0,0 +1,35 @@
+config NFC_MICROREAD
+       tristate "Inside Secure microread NFC driver"
+       depends on NFC_HCI
+       select CRC_CCITT
+       default n
+       ---help---
+         This module contains the main code for Inside Secure microread
+         NFC chipsets. It implements the chipset HCI logic and hooks into
+         the NFC kernel APIs. Physical layers will register against it.
+
+         To compile this driver as a module, choose m here. The module will
+         be called microread.
+         Say N if unsure.
+
+config NFC_MICROREAD_I2C
+       tristate "NFC Microread i2c support"
+       depends on NFC_MICROREAD && I2C && NFC_SHDLC
+       ---help---
+         This module adds support for the i2c interface of adapters using
+         Inside microread chipsets.  Select this if your platform is using
+         the i2c bus.
+
+         If you choose to build a module, it'll be called microread_i2c.
+         Say N if unsure.
+
+config NFC_MICROREAD_MEI
+       tristate "NFC Microread MEI support"
+       depends on NFC_MICROREAD && INTEL_MEI_BUS_NFC
+       ---help---
+         This module adds support for the mei interface of adapters using
+         Inside microread chipsets.  Select this if your microread chipset
+         is handled by Intel's Management Engine Interface on your platform.
+
+         If you choose to build a module, it'll be called microread_mei.
+         Say N if unsure.
diff --git a/drivers/nfc/microread/Makefile b/drivers/nfc/microread/Makefile
new file mode 100644 (file)
index 0000000..755c24c
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# Makefile for Microread HCI based NFC driver
+#
+
+microread_i2c-objs  = i2c.o
+microread_mei-objs  = mei.o
+
+obj-$(CONFIG_NFC_MICROREAD)     += microread.o
+obj-$(CONFIG_NFC_MICROREAD_I2C) += microread_i2c.o
+obj-$(CONFIG_NFC_MICROREAD_MEI) += microread_mei.o
diff --git a/drivers/nfc/microread/i2c.c b/drivers/nfc/microread/i2c.c
new file mode 100644 (file)
index 0000000..1010894
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * HCI based Driver for Inside Secure microread NFC Chip - i2c layer
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+
+#include <linux/nfc.h>
+#include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
+
+#include "microread.h"
+
+#define MICROREAD_I2C_DRIVER_NAME "microread"
+
+#define MICROREAD_I2C_FRAME_HEADROOM 1
+#define MICROREAD_I2C_FRAME_TAILROOM 1
+
+/* framing in HCI mode */
+#define MICROREAD_I2C_LLC_LEN          1
+#define MICROREAD_I2C_LLC_CRC          1
+#define MICROREAD_I2C_LLC_LEN_CRC      (MICROREAD_I2C_LLC_LEN + \
+                                       MICROREAD_I2C_LLC_CRC)
+#define MICROREAD_I2C_LLC_MIN_SIZE     (1 + MICROREAD_I2C_LLC_LEN_CRC)
+#define MICROREAD_I2C_LLC_MAX_PAYLOAD  29
+#define MICROREAD_I2C_LLC_MAX_SIZE     (MICROREAD_I2C_LLC_LEN_CRC + 1 + \
+                                       MICROREAD_I2C_LLC_MAX_PAYLOAD)
+
+struct microread_i2c_phy {
+       struct i2c_client *i2c_dev;
+       struct nfc_hci_dev *hdev;
+
+       int irq;
+
+       int hard_fault;         /*
+                                * < 0 if hardware error occured (e.g. i2c err)
+                                * and prevents normal operation.
+                                */
+};
+
+#define I2C_DUMP_SKB(info, skb)                                        \
+do {                                                           \
+       pr_debug("%s:\n", info);                                \
+       print_hex_dump(KERN_DEBUG, "i2c: ", DUMP_PREFIX_OFFSET, \
+                      16, 1, (skb)->data, (skb)->len, 0);      \
+} while (0)
+
+static void microread_i2c_add_len_crc(struct sk_buff *skb)
+{
+       int i;
+       u8 crc = 0;
+       int len;
+
+       len = skb->len;
+       *skb_push(skb, 1) = len;
+
+       for (i = 0; i < skb->len; i++)
+               crc = crc ^ skb->data[i];
+
+       *skb_put(skb, 1) = crc;
+}
+
+static void microread_i2c_remove_len_crc(struct sk_buff *skb)
+{
+       skb_pull(skb, MICROREAD_I2C_FRAME_HEADROOM);
+       skb_trim(skb, MICROREAD_I2C_FRAME_TAILROOM);
+}
+
+static int check_crc(struct sk_buff *skb)
+{
+       int i;
+       u8 crc = 0;
+
+       for (i = 0; i < skb->len - 1; i++)
+               crc = crc ^ skb->data[i];
+
+       if (crc != skb->data[skb->len-1]) {
+               pr_err(MICROREAD_I2C_DRIVER_NAME
+                      ": CRC error 0x%x != 0x%x\n",
+                      crc, skb->data[skb->len-1]);
+
+               pr_info(DRIVER_DESC ": %s : BAD CRC\n", __func__);
+
+               return -EPERM;
+       }
+
+       return 0;
+}
+
+static int microread_i2c_enable(void *phy_id)
+{
+       return 0;
+}
+
+static void microread_i2c_disable(void *phy_id)
+{
+       return;
+}
+
+static int microread_i2c_write(void *phy_id, struct sk_buff *skb)
+{
+       int r;
+       struct microread_i2c_phy *phy = phy_id;
+       struct i2c_client *client = phy->i2c_dev;
+
+       if (phy->hard_fault != 0)
+               return phy->hard_fault;
+
+       usleep_range(3000, 6000);
+
+       microread_i2c_add_len_crc(skb);
+
+       I2C_DUMP_SKB("i2c frame written", skb);
+
+       r = i2c_master_send(client, skb->data, skb->len);
+
+       if (r == -EREMOTEIO) {  /* Retry, chip was in standby */
+               usleep_range(6000, 10000);
+               r = i2c_master_send(client, skb->data, skb->len);
+       }
+
+       if (r >= 0) {
+               if (r != skb->len)
+                       r = -EREMOTEIO;
+               else
+                       r = 0;
+       }
+
+       microread_i2c_remove_len_crc(skb);
+
+       return r;
+}
+
+
+static int microread_i2c_read(struct microread_i2c_phy *phy,
+                             struct sk_buff **skb)
+{
+       int r;
+       u8 len;
+       u8 tmp[MICROREAD_I2C_LLC_MAX_SIZE - 1];
+       struct i2c_client *client = phy->i2c_dev;
+
+       pr_debug("%s\n", __func__);
+
+       r = i2c_master_recv(client, &len, 1);
+       if (r != 1) {
+               dev_err(&client->dev, "cannot read len byte\n");
+               return -EREMOTEIO;
+       }
+
+       if ((len < MICROREAD_I2C_LLC_MIN_SIZE) ||
+           (len > MICROREAD_I2C_LLC_MAX_SIZE)) {
+               dev_err(&client->dev, "invalid len byte\n");
+               pr_err("invalid len byte\n");
+               r = -EBADMSG;
+               goto flush;
+       }
+
+       *skb = alloc_skb(1 + len, GFP_KERNEL);
+       if (*skb == NULL) {
+               r = -ENOMEM;
+               goto flush;
+       }
+
+       *skb_put(*skb, 1) = len;
+
+       r = i2c_master_recv(client, skb_put(*skb, len), len);
+       if (r != len) {
+               kfree_skb(*skb);
+               return -EREMOTEIO;
+       }
+
+       I2C_DUMP_SKB("cc frame read", *skb);
+
+       r = check_crc(*skb);
+       if (r != 0) {
+               kfree_skb(*skb);
+               r = -EBADMSG;
+               goto flush;
+       }
+
+       skb_pull(*skb, 1);
+       skb_trim(*skb, (*skb)->len - MICROREAD_I2C_FRAME_TAILROOM);
+
+       usleep_range(3000, 6000);
+
+       return 0;
+
+flush:
+       if (i2c_master_recv(client, tmp, sizeof(tmp)) < 0)
+               r = -EREMOTEIO;
+
+       usleep_range(3000, 6000);
+
+       return r;
+}
+
+static irqreturn_t microread_i2c_irq_thread_fn(int irq, void *phy_id)
+{
+       struct microread_i2c_phy *phy = phy_id;
+       struct i2c_client *client;
+       struct sk_buff *skb = NULL;
+       int r;
+
+       if (!phy || irq != phy->i2c_dev->irq) {
+               WARN_ON_ONCE(1);
+               return IRQ_NONE;
+       }
+
+       client = phy->i2c_dev;
+       dev_dbg(&client->dev, "IRQ\n");
+
+       if (phy->hard_fault != 0)
+               return IRQ_HANDLED;
+
+       r = microread_i2c_read(phy, &skb);
+       if (r == -EREMOTEIO) {
+               phy->hard_fault = r;
+
+               nfc_hci_recv_frame(phy->hdev, NULL);
+
+               return IRQ_HANDLED;
+       } else if ((r == -ENOMEM) || (r == -EBADMSG)) {
+               return IRQ_HANDLED;
+       }
+
+       nfc_hci_recv_frame(phy->hdev, skb);
+
+       return IRQ_HANDLED;
+}
+
+static struct nfc_phy_ops i2c_phy_ops = {
+       .write = microread_i2c_write,
+       .enable = microread_i2c_enable,
+       .disable = microread_i2c_disable,
+};
+
+static int microread_i2c_probe(struct i2c_client *client,
+                              const struct i2c_device_id *id)
+{
+       struct microread_i2c_phy *phy;
+       struct microread_nfc_platform_data *pdata =
+               dev_get_platdata(&client->dev);
+       int r;
+
+       dev_dbg(&client->dev, "client %p", client);
+
+       if (!pdata) {
+               dev_err(&client->dev, "client %p: missing platform data",
+                       client);
+               return -EINVAL;
+       }
+
+       phy = devm_kzalloc(&client->dev, sizeof(struct microread_i2c_phy),
+                          GFP_KERNEL);
+       if (!phy) {
+               dev_err(&client->dev, "Can't allocate microread phy");
+               return -ENOMEM;
+       }
+
+       i2c_set_clientdata(client, phy);
+       phy->i2c_dev = client;
+
+       r = request_threaded_irq(client->irq, NULL, microread_i2c_irq_thread_fn,
+                                IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                MICROREAD_I2C_DRIVER_NAME, phy);
+       if (r) {
+               dev_err(&client->dev, "Unable to register IRQ handler");
+               return r;
+       }
+
+       r = microread_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME,
+                           MICROREAD_I2C_FRAME_HEADROOM,
+                           MICROREAD_I2C_FRAME_TAILROOM,
+                           MICROREAD_I2C_LLC_MAX_PAYLOAD, &phy->hdev);
+       if (r < 0)
+               goto err_irq;
+
+       dev_info(&client->dev, "Probed");
+
+       return 0;
+
+err_irq:
+       free_irq(client->irq, phy);
+
+       return r;
+}
+
+static int microread_i2c_remove(struct i2c_client *client)
+{
+       struct microread_i2c_phy *phy = i2c_get_clientdata(client);
+
+       dev_dbg(&client->dev, "%s\n", __func__);
+
+       microread_remove(phy->hdev);
+
+       free_irq(client->irq, phy);
+
+       return 0;
+}
+
+static struct i2c_device_id microread_i2c_id[] = {
+       { MICROREAD_I2C_DRIVER_NAME, 0},
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, microread_i2c_id);
+
+static struct i2c_driver microread_i2c_driver = {
+       .driver = {
+               .name = MICROREAD_I2C_DRIVER_NAME,
+       },
+       .probe          = microread_i2c_probe,
+       .remove         = microread_i2c_remove,
+       .id_table       = microread_i2c_id,
+};
+
+module_i2c_driver(microread_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/nfc/microread/mei.c b/drivers/nfc/microread/mei.c
new file mode 100644 (file)
index 0000000..ca33ae1
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * HCI based Driver for Inside Secure microread NFC Chip
+ *
+ * Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/mei_cl_bus.h>
+
+#include <linux/nfc.h>
+#include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
+
+#include "microread.h"
+
+#define MICROREAD_DRIVER_NAME "microread"
+
+struct mei_nfc_hdr {
+       u8 cmd;
+       u8 status;
+       u16 req_id;
+       u32 reserved;
+       u16 data_size;
+} __attribute__((packed));
+
+#define MEI_NFC_HEADER_SIZE 10
+#define MEI_NFC_MAX_HCI_PAYLOAD 300
+#define MEI_NFC_MAX_READ (MEI_NFC_HEADER_SIZE + MEI_NFC_MAX_HCI_PAYLOAD)
+
+struct microread_mei_phy {
+       struct mei_cl_device *device;
+       struct nfc_hci_dev *hdev;
+
+       int powered;
+
+       int hard_fault;         /*
+                                * < 0 if hardware error occured (e.g. i2c err)
+                                * and prevents normal operation.
+                                */
+};
+
+#define MEI_DUMP_SKB_IN(info, skb)                                     \
+do {                                                           \
+       pr_debug("%s:\n", info);                                \
+       print_hex_dump(KERN_DEBUG, "mei in : ", DUMP_PREFIX_OFFSET,     \
+                      16, 1, (skb)->data, (skb)->len, 0);      \
+} while (0)
+
+#define MEI_DUMP_SKB_OUT(info, skb)                                    \
+do {                                                           \
+       pr_debug("%s:\n", info);                                \
+       print_hex_dump(KERN_DEBUG, "mei out: ", DUMP_PREFIX_OFFSET,     \
+                      16, 1, (skb)->data, (skb)->len, 0);      \
+} while (0)
+
+static int microread_mei_enable(void *phy_id)
+{
+       struct microread_mei_phy *phy = phy_id;
+
+       pr_info(DRIVER_DESC ": %s\n", __func__);
+
+       phy->powered = 1;
+
+       return 0;
+}
+
+static void microread_mei_disable(void *phy_id)
+{
+       struct microread_mei_phy *phy = phy_id;
+
+       pr_info(DRIVER_DESC ": %s\n", __func__);
+
+       phy->powered = 0;
+}
+
+/*
+ * Writing a frame must not return the number of written bytes.
+ * It must return either zero for success, or <0 for error.
+ * In addition, it must not alter the skb
+ */
+static int microread_mei_write(void *phy_id, struct sk_buff *skb)
+{
+       struct microread_mei_phy *phy = phy_id;
+       int r;
+
+       MEI_DUMP_SKB_OUT("mei frame sent", skb);
+
+       r = mei_cl_send(phy->device, skb->data, skb->len);
+       if (r > 0)
+               r = 0;
+
+       return r;
+}
+
+static void microread_event_cb(struct mei_cl_device *device, u32 events,
+                              void *context)
+{
+       struct microread_mei_phy *phy = context;
+
+       if (phy->hard_fault != 0)
+               return;
+
+       if (events & BIT(MEI_CL_EVENT_RX)) {
+               struct sk_buff *skb;
+               int reply_size;
+
+               skb = alloc_skb(MEI_NFC_MAX_READ, GFP_KERNEL);
+               if (!skb)
+                       return;
+
+               reply_size = mei_cl_recv(device, skb->data, MEI_NFC_MAX_READ);
+               if (reply_size < MEI_NFC_HEADER_SIZE) {
+                       kfree(skb);
+                       return;
+               }
+
+               skb_put(skb, reply_size);
+               skb_pull(skb, MEI_NFC_HEADER_SIZE);
+
+               MEI_DUMP_SKB_IN("mei frame read", skb);
+
+               nfc_hci_recv_frame(phy->hdev, skb);
+       }
+}
+
+static struct nfc_phy_ops mei_phy_ops = {
+       .write = microread_mei_write,
+       .enable = microread_mei_enable,
+       .disable = microread_mei_disable,
+};
+
+static int microread_mei_probe(struct mei_cl_device *device,
+                              const struct mei_cl_device_id *id)
+{
+       struct microread_mei_phy *phy;
+       int r;
+
+       pr_info("Probing NFC microread\n");
+
+       phy = kzalloc(sizeof(struct microread_mei_phy), GFP_KERNEL);
+       if (!phy) {
+               pr_err("Cannot allocate memory for microread mei phy.\n");
+               return -ENOMEM;
+       }
+
+       phy->device = device;
+       mei_cl_set_drvdata(device, phy);
+
+       r = mei_cl_register_event_cb(device, microread_event_cb, phy);
+       if (r) {
+               pr_err(MICROREAD_DRIVER_NAME ": event cb registration failed\n");
+               goto err_out;
+       }
+
+       r = microread_probe(phy, &mei_phy_ops, LLC_NOP_NAME,
+                           MEI_NFC_HEADER_SIZE, 0, MEI_NFC_MAX_HCI_PAYLOAD,
+                           &phy->hdev);
+       if (r < 0)
+               goto err_out;
+
+       return 0;
+
+err_out:
+       kfree(phy);
+
+       return r;
+}
+
+static int microread_mei_remove(struct mei_cl_device *device)
+{
+       struct microread_mei_phy *phy = mei_cl_get_drvdata(device);
+
+       pr_info("Removing microread\n");
+
+       microread_remove(phy->hdev);
+
+       if (phy->powered)
+               microread_mei_disable(phy);
+
+       kfree(phy);
+
+       return 0;
+}
+
+static struct mei_cl_device_id microread_mei_tbl[] = {
+       { MICROREAD_DRIVER_NAME },
+
+       /* required last entry */
+       { }
+};
+MODULE_DEVICE_TABLE(mei, microread_mei_tbl);
+
+static struct mei_cl_driver microread_driver = {
+       .id_table = microread_mei_tbl,
+       .name = MICROREAD_DRIVER_NAME,
+
+       .probe = microread_mei_probe,
+       .remove = microread_mei_remove,
+};
+
+static int microread_mei_init(void)
+{
+       int r;
+
+       pr_debug(DRIVER_DESC ": %s\n", __func__);
+
+       r = mei_cl_driver_register(&microread_driver);
+       if (r) {
+               pr_err(MICROREAD_DRIVER_NAME ": driver registration failed\n");
+               return r;
+       }
+
+       return 0;
+}
+
+static void microread_mei_exit(void)
+{
+       mei_cl_driver_unregister(&microread_driver);
+}
+
+module_init(microread_mei_init);
+module_exit(microread_mei_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/nfc/microread/microread.c b/drivers/nfc/microread/microread.c
new file mode 100644 (file)
index 0000000..3420d83
--- /dev/null
@@ -0,0 +1,728 @@
+/*
+ * HCI based Driver for Inside Secure microread NFC Chip
+ *
+ * Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/crc-ccitt.h>
+
+#include <linux/nfc.h>
+#include <net/nfc/nfc.h>
+#include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
+
+#include "microread.h"
+
+/* Proprietary gates, events, commands and registers */
+/* Admin */
+#define MICROREAD_GATE_ID_ADM NFC_HCI_ADMIN_GATE
+#define MICROREAD_GATE_ID_MGT 0x01
+#define MICROREAD_GATE_ID_OS 0x02
+#define MICROREAD_GATE_ID_TESTRF 0x03
+#define MICROREAD_GATE_ID_LOOPBACK NFC_HCI_LOOPBACK_GATE
+#define MICROREAD_GATE_ID_IDT NFC_HCI_ID_MGMT_GATE
+#define MICROREAD_GATE_ID_LMS NFC_HCI_LINK_MGMT_GATE
+
+/* Reader */
+#define MICROREAD_GATE_ID_MREAD_GEN 0x10
+#define MICROREAD_GATE_ID_MREAD_ISO_B NFC_HCI_RF_READER_B_GATE
+#define MICROREAD_GATE_ID_MREAD_NFC_T1 0x12
+#define MICROREAD_GATE_ID_MREAD_ISO_A NFC_HCI_RF_READER_A_GATE
+#define MICROREAD_GATE_ID_MREAD_NFC_T3 0x14
+#define MICROREAD_GATE_ID_MREAD_ISO_15_3 0x15
+#define MICROREAD_GATE_ID_MREAD_ISO_15_2 0x16
+#define MICROREAD_GATE_ID_MREAD_ISO_B_3 0x17
+#define MICROREAD_GATE_ID_MREAD_BPRIME 0x18
+#define MICROREAD_GATE_ID_MREAD_ISO_A_3 0x19
+
+/* Card */
+#define MICROREAD_GATE_ID_MCARD_GEN 0x20
+#define MICROREAD_GATE_ID_MCARD_ISO_B 0x21
+#define MICROREAD_GATE_ID_MCARD_BPRIME 0x22
+#define MICROREAD_GATE_ID_MCARD_ISO_A 0x23
+#define MICROREAD_GATE_ID_MCARD_NFC_T3 0x24
+#define MICROREAD_GATE_ID_MCARD_ISO_15_3 0x25
+#define MICROREAD_GATE_ID_MCARD_ISO_15_2 0x26
+#define MICROREAD_GATE_ID_MCARD_ISO_B_2 0x27
+#define MICROREAD_GATE_ID_MCARD_ISO_CUSTOM 0x28
+#define MICROREAD_GATE_ID_SECURE_ELEMENT 0x2F
+
+/* P2P */
+#define MICROREAD_GATE_ID_P2P_GEN 0x30
+#define MICROREAD_GATE_ID_P2P_TARGET 0x31
+#define MICROREAD_PAR_P2P_TARGET_MODE 0x01
+#define MICROREAD_PAR_P2P_TARGET_GT 0x04
+#define MICROREAD_GATE_ID_P2P_INITIATOR 0x32
+#define MICROREAD_PAR_P2P_INITIATOR_GI 0x01
+#define MICROREAD_PAR_P2P_INITIATOR_GT 0x03
+
+/* Those pipes are created/opened by default in the chip */
+#define MICROREAD_PIPE_ID_LMS 0x00
+#define MICROREAD_PIPE_ID_ADMIN 0x01
+#define MICROREAD_PIPE_ID_MGT 0x02
+#define MICROREAD_PIPE_ID_OS 0x03
+#define MICROREAD_PIPE_ID_HDS_LOOPBACK 0x04
+#define MICROREAD_PIPE_ID_HDS_IDT 0x05
+#define MICROREAD_PIPE_ID_HDS_MCARD_ISO_B 0x08
+#define MICROREAD_PIPE_ID_HDS_MCARD_ISO_BPRIME 0x09
+#define MICROREAD_PIPE_ID_HDS_MCARD_ISO_A 0x0A
+#define MICROREAD_PIPE_ID_HDS_MCARD_ISO_15_3 0x0B
+#define MICROREAD_PIPE_ID_HDS_MCARD_ISO_15_2 0x0C
+#define MICROREAD_PIPE_ID_HDS_MCARD_NFC_T3 0x0D
+#define MICROREAD_PIPE_ID_HDS_MCARD_ISO_B_2 0x0E
+#define MICROREAD_PIPE_ID_HDS_MCARD_CUSTOM 0x0F
+#define MICROREAD_PIPE_ID_HDS_MREAD_ISO_B 0x10
+#define MICROREAD_PIPE_ID_HDS_MREAD_NFC_T1 0x11
+#define MICROREAD_PIPE_ID_HDS_MREAD_ISO_A 0x12
+#define MICROREAD_PIPE_ID_HDS_MREAD_ISO_15_3 0x13
+#define MICROREAD_PIPE_ID_HDS_MREAD_ISO_15_2 0x14
+#define MICROREAD_PIPE_ID_HDS_MREAD_NFC_T3 0x15
+#define MICROREAD_PIPE_ID_HDS_MREAD_ISO_B_3 0x16
+#define MICROREAD_PIPE_ID_HDS_MREAD_BPRIME 0x17
+#define MICROREAD_PIPE_ID_HDS_MREAD_ISO_A_3 0x18
+#define MICROREAD_PIPE_ID_HDS_MREAD_GEN 0x1B
+#define MICROREAD_PIPE_ID_HDS_STACKED_ELEMENT 0x1C
+#define MICROREAD_PIPE_ID_HDS_INSTANCES 0x1D
+#define MICROREAD_PIPE_ID_HDS_TESTRF 0x1E
+#define MICROREAD_PIPE_ID_HDS_P2P_TARGET 0x1F
+#define MICROREAD_PIPE_ID_HDS_P2P_INITIATOR 0x20
+
+/* Events */
+#define MICROREAD_EVT_MREAD_DISCOVERY_OCCURED NFC_HCI_EVT_TARGET_DISCOVERED
+#define MICROREAD_EVT_MREAD_CARD_FOUND 0x3D
+#define MICROREAD_EMCF_A_ATQA 0
+#define MICROREAD_EMCF_A_SAK 2
+#define MICROREAD_EMCF_A_LEN 3
+#define MICROREAD_EMCF_A_UID 4
+#define MICROREAD_EMCF_A3_ATQA 0
+#define MICROREAD_EMCF_A3_SAK 2
+#define MICROREAD_EMCF_A3_LEN 3
+#define MICROREAD_EMCF_A3_UID 4
+#define MICROREAD_EMCF_B_UID 0
+#define MICROREAD_EMCF_T1_ATQA 0
+#define MICROREAD_EMCF_T1_UID 4
+#define MICROREAD_EMCF_T3_UID 0
+#define MICROREAD_EVT_MREAD_DISCOVERY_START NFC_HCI_EVT_READER_REQUESTED
+#define MICROREAD_EVT_MREAD_DISCOVERY_START_SOME 0x3E
+#define MICROREAD_EVT_MREAD_DISCOVERY_STOP NFC_HCI_EVT_END_OPERATION
+#define MICROREAD_EVT_MREAD_SIM_REQUESTS 0x3F
+#define MICROREAD_EVT_MCARD_EXCHANGE NFC_HCI_EVT_TARGET_DISCOVERED
+#define MICROREAD_EVT_P2P_INITIATOR_EXCHANGE_TO_RF 0x20
+#define MICROREAD_EVT_P2P_INITIATOR_EXCHANGE_FROM_RF 0x21
+#define MICROREAD_EVT_MCARD_FIELD_ON 0x11
+#define MICROREAD_EVT_P2P_TARGET_ACTIVATED 0x13
+#define MICROREAD_EVT_P2P_TARGET_DEACTIVATED 0x12
+#define MICROREAD_EVT_MCARD_FIELD_OFF 0x14
+
+/* Commands */
+#define MICROREAD_CMD_MREAD_EXCHANGE 0x10
+#define MICROREAD_CMD_MREAD_SUBSCRIBE 0x3F
+
+/* Hosts IDs */
+#define MICROREAD_ELT_ID_HDS NFC_HCI_TERMINAL_HOST_ID
+#define MICROREAD_ELT_ID_SIM NFC_HCI_UICC_HOST_ID
+#define MICROREAD_ELT_ID_SE1 0x03
+#define MICROREAD_ELT_ID_SE2 0x04
+#define MICROREAD_ELT_ID_SE3 0x05
+
+static struct nfc_hci_gate microread_gates[] = {
+       {MICROREAD_GATE_ID_ADM, MICROREAD_PIPE_ID_ADMIN},
+       {MICROREAD_GATE_ID_LOOPBACK, MICROREAD_PIPE_ID_HDS_LOOPBACK},
+       {MICROREAD_GATE_ID_IDT, MICROREAD_PIPE_ID_HDS_IDT},
+       {MICROREAD_GATE_ID_LMS, MICROREAD_PIPE_ID_LMS},
+       {MICROREAD_GATE_ID_MREAD_ISO_B, MICROREAD_PIPE_ID_HDS_MREAD_ISO_B},
+       {MICROREAD_GATE_ID_MREAD_ISO_A, MICROREAD_PIPE_ID_HDS_MREAD_ISO_A},
+       {MICROREAD_GATE_ID_MREAD_ISO_A_3, MICROREAD_PIPE_ID_HDS_MREAD_ISO_A_3},
+       {MICROREAD_GATE_ID_MGT, MICROREAD_PIPE_ID_MGT},
+       {MICROREAD_GATE_ID_OS, MICROREAD_PIPE_ID_OS},
+       {MICROREAD_GATE_ID_MREAD_NFC_T1, MICROREAD_PIPE_ID_HDS_MREAD_NFC_T1},
+       {MICROREAD_GATE_ID_MREAD_NFC_T3, MICROREAD_PIPE_ID_HDS_MREAD_NFC_T3},
+       {MICROREAD_GATE_ID_P2P_TARGET, MICROREAD_PIPE_ID_HDS_P2P_TARGET},
+       {MICROREAD_GATE_ID_P2P_INITIATOR, MICROREAD_PIPE_ID_HDS_P2P_INITIATOR}
+};
+
+/* Largest headroom needed for outgoing custom commands */
+#define MICROREAD_CMDS_HEADROOM        2
+#define MICROREAD_CMD_TAILROOM 2
+
+struct microread_info {
+       struct nfc_phy_ops *phy_ops;
+       void *phy_id;
+
+       struct nfc_hci_dev *hdev;
+
+       int async_cb_type;
+       data_exchange_cb_t async_cb;
+       void *async_cb_context;
+};
+
+static int microread_open(struct nfc_hci_dev *hdev)
+{
+       struct microread_info *info = nfc_hci_get_clientdata(hdev);
+
+       return info->phy_ops->enable(info->phy_id);
+}
+
+static void microread_close(struct nfc_hci_dev *hdev)
+{
+       struct microread_info *info = nfc_hci_get_clientdata(hdev);
+
+       info->phy_ops->disable(info->phy_id);
+}
+
+static int microread_hci_ready(struct nfc_hci_dev *hdev)
+{
+       int r;
+       u8 param[4];
+
+       param[0] = 0x03;
+       r = nfc_hci_send_cmd(hdev, MICROREAD_GATE_ID_MREAD_ISO_A,
+                            MICROREAD_CMD_MREAD_SUBSCRIBE, param, 1, NULL);
+       if (r)
+               return r;
+
+       r = nfc_hci_send_cmd(hdev, MICROREAD_GATE_ID_MREAD_ISO_A_3,
+                            MICROREAD_CMD_MREAD_SUBSCRIBE, NULL, 0, NULL);
+       if (r)
+               return r;
+
+       param[0] = 0x00;
+       param[1] = 0x03;
+       param[2] = 0x00;
+       r = nfc_hci_send_cmd(hdev, MICROREAD_GATE_ID_MREAD_ISO_B,
+                            MICROREAD_CMD_MREAD_SUBSCRIBE, param, 3, NULL);
+       if (r)
+               return r;
+
+       r = nfc_hci_send_cmd(hdev, MICROREAD_GATE_ID_MREAD_NFC_T1,
+                            MICROREAD_CMD_MREAD_SUBSCRIBE, NULL, 0, NULL);
+       if (r)
+               return r;
+
+       param[0] = 0xFF;
+       param[1] = 0xFF;
+       param[2] = 0x00;
+       param[3] = 0x00;
+       r = nfc_hci_send_cmd(hdev, MICROREAD_GATE_ID_MREAD_NFC_T3,
+                            MICROREAD_CMD_MREAD_SUBSCRIBE, param, 4, NULL);
+
+       return r;
+}
+
+static int microread_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+{
+       struct microread_info *info = nfc_hci_get_clientdata(hdev);
+
+       return info->phy_ops->write(info->phy_id, skb);
+}
+
+static int microread_start_poll(struct nfc_hci_dev *hdev,
+                               u32 im_protocols, u32 tm_protocols)
+{
+       int r;
+
+       u8 param[2];
+       u8 mode;
+
+       param[0] = 0x00;
+       param[1] = 0x00;
+
+       if (im_protocols & NFC_PROTO_ISO14443_MASK)
+               param[0] |= (1 << 2);
+
+       if (im_protocols & NFC_PROTO_ISO14443_B_MASK)
+               param[0] |= 1;
+
+       if (im_protocols & NFC_PROTO_MIFARE_MASK)
+               param[1] |= 1;
+
+       if (im_protocols & NFC_PROTO_JEWEL_MASK)
+               param[0] |= (1 << 1);
+
+       if (im_protocols & NFC_PROTO_FELICA_MASK)
+               param[0] |= (1 << 5);
+
+       if (im_protocols & NFC_PROTO_NFC_DEP_MASK)
+               param[1] |= (1 << 1);
+
+       if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) {
+               hdev->gb = nfc_get_local_general_bytes(hdev->ndev,
+                                                      &hdev->gb_len);
+               if (hdev->gb == NULL || hdev->gb_len == 0) {
+                       im_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
+                       tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
+               }
+       }
+
+       r = nfc_hci_send_event(hdev, MICROREAD_GATE_ID_MREAD_ISO_A,
+                              MICROREAD_EVT_MREAD_DISCOVERY_STOP, NULL, 0);
+       if (r)
+               return r;
+
+       mode = 0xff;
+       r = nfc_hci_set_param(hdev, MICROREAD_GATE_ID_P2P_TARGET,
+                             MICROREAD_PAR_P2P_TARGET_MODE, &mode, 1);
+       if (r)
+               return r;
+
+       if (im_protocols & NFC_PROTO_NFC_DEP_MASK) {
+               r = nfc_hci_set_param(hdev, MICROREAD_GATE_ID_P2P_INITIATOR,
+                                     MICROREAD_PAR_P2P_INITIATOR_GI,
+                                     hdev->gb, hdev->gb_len);
+               if (r)
+                       return r;
+       }
+
+       if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) {
+               r = nfc_hci_set_param(hdev, MICROREAD_GATE_ID_P2P_TARGET,
+                                     MICROREAD_PAR_P2P_TARGET_GT,
+                                     hdev->gb, hdev->gb_len);
+               if (r)
+                       return r;
+
+               mode = 0x02;
+               r = nfc_hci_set_param(hdev, MICROREAD_GATE_ID_P2P_TARGET,
+                                     MICROREAD_PAR_P2P_TARGET_MODE, &mode, 1);
+               if (r)
+                       return r;
+       }
+
+       return nfc_hci_send_event(hdev, MICROREAD_GATE_ID_MREAD_ISO_A,
+                                 MICROREAD_EVT_MREAD_DISCOVERY_START_SOME,
+                                 param, 2);
+}
+
+static int microread_dep_link_up(struct nfc_hci_dev *hdev,
+                               struct nfc_target *target, u8 comm_mode,
+                               u8 *gb, size_t gb_len)
+{
+       struct sk_buff *rgb_skb = NULL;
+       int r;
+
+       r = nfc_hci_get_param(hdev, target->hci_reader_gate,
+                             MICROREAD_PAR_P2P_INITIATOR_GT, &rgb_skb);
+       if (r < 0)
+               return r;
+
+       if (rgb_skb->len == 0 || rgb_skb->len > NFC_GB_MAXSIZE) {
+               r = -EPROTO;
+               goto exit;
+       }
+
+       r = nfc_set_remote_general_bytes(hdev->ndev, rgb_skb->data,
+                                        rgb_skb->len);
+       if (r == 0)
+               r = nfc_dep_link_is_up(hdev->ndev, target->idx, comm_mode,
+                                      NFC_RF_INITIATOR);
+exit:
+       kfree_skb(rgb_skb);
+
+       return r;
+}
+
+static int microread_dep_link_down(struct nfc_hci_dev *hdev)
+{
+       return nfc_hci_send_event(hdev, MICROREAD_GATE_ID_P2P_INITIATOR,
+                                 MICROREAD_EVT_MREAD_DISCOVERY_STOP, NULL, 0);
+}
+
+static int microread_target_from_gate(struct nfc_hci_dev *hdev, u8 gate,
+                                     struct nfc_target *target)
+{
+       switch (gate) {
+       case MICROREAD_GATE_ID_P2P_INITIATOR:
+               target->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+               break;
+       default:
+               return -EPROTO;
+       }
+
+       return 0;
+}
+
+static int microread_complete_target_discovered(struct nfc_hci_dev *hdev,
+                                               u8 gate,
+                                               struct nfc_target *target)
+{
+       return 0;
+}
+
+#define MICROREAD_CB_TYPE_READER_ALL 1
+
+static void microread_im_transceive_cb(void *context, struct sk_buff *skb,
+                                      int err)
+{
+       struct microread_info *info = context;
+
+       switch (info->async_cb_type) {
+       case MICROREAD_CB_TYPE_READER_ALL:
+               if (err == 0) {
+                       if (skb->len == 0) {
+                               err = -EPROTO;
+                               kfree_skb(skb);
+                               info->async_cb(info->async_cb_context, NULL,
+                                              -EPROTO);
+                               return;
+                       }
+
+                       if (skb->data[skb->len - 1] != 0) {
+                               err = nfc_hci_result_to_errno(
+                                                      skb->data[skb->len - 1]);
+                               kfree_skb(skb);
+                               info->async_cb(info->async_cb_context, NULL,
+                                              err);
+                               return;
+                       }
+
+                       skb_trim(skb, skb->len - 1);    /* RF Error ind. */
+               }
+               info->async_cb(info->async_cb_context, skb, err);
+               break;
+       default:
+               if (err == 0)
+                       kfree_skb(skb);
+               break;
+       }
+}
+
+/*
+ * Returns:
+ * <= 0: driver handled the data exchange
+ *    1: driver doesn't especially handle, please do standard processing
+ */
+static int microread_im_transceive(struct nfc_hci_dev *hdev,
+                                  struct nfc_target *target,
+                                  struct sk_buff *skb, data_exchange_cb_t cb,
+                                  void *cb_context)
+{
+       struct microread_info *info = nfc_hci_get_clientdata(hdev);
+       u8 control_bits;
+       u16 crc;
+
+       pr_info("data exchange to gate 0x%x\n", target->hci_reader_gate);
+
+       if (target->hci_reader_gate == MICROREAD_GATE_ID_P2P_INITIATOR) {
+               *skb_push(skb, 1) = 0;
+
+               return nfc_hci_send_event(hdev, target->hci_reader_gate,
+                                    MICROREAD_EVT_P2P_INITIATOR_EXCHANGE_TO_RF,
+                                    skb->data, skb->len);
+       }
+
+       switch (target->hci_reader_gate) {
+       case MICROREAD_GATE_ID_MREAD_ISO_A:
+               control_bits = 0xCB;
+               break;
+       case MICROREAD_GATE_ID_MREAD_ISO_A_3:
+               control_bits = 0xCB;
+               break;
+       case MICROREAD_GATE_ID_MREAD_ISO_B:
+               control_bits = 0xCB;
+               break;
+       case MICROREAD_GATE_ID_MREAD_NFC_T1:
+               control_bits = 0x1B;
+
+               crc = crc_ccitt(0xffff, skb->data, skb->len);
+               crc = ~crc;
+               *skb_put(skb, 1) = crc & 0xff;
+               *skb_put(skb, 1) = crc >> 8;
+               break;
+       case MICROREAD_GATE_ID_MREAD_NFC_T3:
+               control_bits = 0xDB;
+               break;
+       default:
+               pr_info("Abort im_transceive to invalid gate 0x%x\n",
+                       target->hci_reader_gate);
+               return 1;
+       }
+
+       *skb_push(skb, 1) = control_bits;
+
+       info->async_cb_type = MICROREAD_CB_TYPE_READER_ALL;
+       info->async_cb = cb;
+       info->async_cb_context = cb_context;
+
+       return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+                                     MICROREAD_CMD_MREAD_EXCHANGE,
+                                     skb->data, skb->len,
+                                     microread_im_transceive_cb, info);
+}
+
+static int microread_tm_send(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+{
+       int r;
+
+       r = nfc_hci_send_event(hdev, MICROREAD_GATE_ID_P2P_TARGET,
+                              MICROREAD_EVT_MCARD_EXCHANGE,
+                              skb->data, skb->len);
+
+       kfree_skb(skb);
+
+       return r;
+}
+
+static void microread_target_discovered(struct nfc_hci_dev *hdev, u8 gate,
+                                       struct sk_buff *skb)
+{
+       struct nfc_target *targets;
+       int r = 0;
+
+       pr_info("target discovered to gate 0x%x\n", gate);
+
+       targets = kzalloc(sizeof(struct nfc_target), GFP_KERNEL);
+       if (targets == NULL) {
+               r = -ENOMEM;
+               goto exit;
+       }
+
+       targets->hci_reader_gate = gate;
+
+       switch (gate) {
+       case MICROREAD_GATE_ID_MREAD_ISO_A:
+               targets->supported_protocols =
+                     nfc_hci_sak_to_protocol(skb->data[MICROREAD_EMCF_A_SAK]);
+               targets->sens_res =
+                        be16_to_cpu(*(u16 *)&skb->data[MICROREAD_EMCF_A_ATQA]);
+               targets->sel_res = skb->data[MICROREAD_EMCF_A_SAK];
+               memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_A_UID],
+                      skb->data[MICROREAD_EMCF_A_LEN]);
+               targets->nfcid1_len = skb->data[MICROREAD_EMCF_A_LEN];
+               break;
+       case MICROREAD_GATE_ID_MREAD_ISO_A_3:
+               targets->supported_protocols =
+                     nfc_hci_sak_to_protocol(skb->data[MICROREAD_EMCF_A3_SAK]);
+               targets->sens_res =
+                        be16_to_cpu(*(u16 *)&skb->data[MICROREAD_EMCF_A3_ATQA]);
+               targets->sel_res = skb->data[MICROREAD_EMCF_A3_SAK];
+               memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_A3_UID],
+                      skb->data[MICROREAD_EMCF_A3_LEN]);
+               targets->nfcid1_len = skb->data[MICROREAD_EMCF_A3_LEN];
+               break;
+       case MICROREAD_GATE_ID_MREAD_ISO_B:
+               targets->supported_protocols = NFC_PROTO_ISO14443_B_MASK;
+               memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_B_UID], 4);
+               targets->nfcid1_len = 4;
+               break;
+       case MICROREAD_GATE_ID_MREAD_NFC_T1:
+               targets->supported_protocols = NFC_PROTO_JEWEL_MASK;
+               targets->sens_res =
+                       le16_to_cpu(*(u16 *)&skb->data[MICROREAD_EMCF_T1_ATQA]);
+               memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_T1_UID], 4);
+               targets->nfcid1_len = 4;
+               break;
+       case MICROREAD_GATE_ID_MREAD_NFC_T3:
+               targets->supported_protocols = NFC_PROTO_FELICA_MASK;
+               memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_T3_UID], 8);
+               targets->nfcid1_len = 8;
+               break;
+       default:
+               pr_info("discard target discovered to gate 0x%x\n", gate);
+               goto exit_free;
+       }
+
+       r = nfc_targets_found(hdev->ndev, targets, 1);
+
+exit_free:
+       kfree(targets);
+
+exit:
+       kfree_skb(skb);
+
+       if (r)
+               pr_err("Failed to handle discovered target err=%d", r);
+}
+
+static int microread_event_received(struct nfc_hci_dev *hdev, u8 gate,
+                                    u8 event, struct sk_buff *skb)
+{
+       int r;
+       u8 mode;
+
+       pr_info("Microread received event 0x%x to gate 0x%x\n", event, gate);
+
+       switch (event) {
+       case MICROREAD_EVT_MREAD_CARD_FOUND:
+               microread_target_discovered(hdev, gate, skb);
+               return 0;
+
+       case MICROREAD_EVT_P2P_INITIATOR_EXCHANGE_FROM_RF:
+               if (skb->len < 1) {
+                       kfree_skb(skb);
+                       return -EPROTO;
+               }
+
+               if (skb->data[skb->len - 1]) {
+                       kfree_skb(skb);
+                       return -EIO;
+               }
+
+               skb_trim(skb, skb->len - 1);
+
+               r = nfc_tm_data_received(hdev->ndev, skb);
+               break;
+
+       case MICROREAD_EVT_MCARD_FIELD_ON:
+       case MICROREAD_EVT_MCARD_FIELD_OFF:
+               kfree_skb(skb);
+               return 0;
+
+       case MICROREAD_EVT_P2P_TARGET_ACTIVATED:
+               r = nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK,
+                                    NFC_COMM_PASSIVE, skb->data,
+                                    skb->len);
+
+               kfree_skb(skb);
+               break;
+
+       case MICROREAD_EVT_MCARD_EXCHANGE:
+               if (skb->len < 1) {
+                       kfree_skb(skb);
+                       return -EPROTO;
+               }
+
+               if (skb->data[skb->len-1]) {
+                       kfree_skb(skb);
+                       return -EIO;
+               }
+
+               skb_trim(skb, skb->len - 1);
+
+               r = nfc_tm_data_received(hdev->ndev, skb);
+               break;
+
+       case MICROREAD_EVT_P2P_TARGET_DEACTIVATED:
+               kfree_skb(skb);
+
+               mode = 0xff;
+               r = nfc_hci_set_param(hdev, MICROREAD_GATE_ID_P2P_TARGET,
+                                     MICROREAD_PAR_P2P_TARGET_MODE, &mode, 1);
+               if (r)
+                       break;
+
+               r = nfc_hci_send_event(hdev, gate,
+                                      MICROREAD_EVT_MREAD_DISCOVERY_STOP, NULL,
+                                      0);
+               break;
+
+       default:
+               return 1;
+       }
+
+       return r;
+}
+
+static struct nfc_hci_ops microread_hci_ops = {
+       .open = microread_open,
+       .close = microread_close,
+       .hci_ready = microread_hci_ready,
+       .xmit = microread_xmit,
+       .start_poll = microread_start_poll,
+       .dep_link_up = microread_dep_link_up,
+       .dep_link_down = microread_dep_link_down,
+       .target_from_gate = microread_target_from_gate,
+       .complete_target_discovered = microread_complete_target_discovered,
+       .im_transceive = microread_im_transceive,
+       .tm_send = microread_tm_send,
+       .check_presence = NULL,
+       .event_received = microread_event_received,
+};
+
+int microread_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
+                   int phy_headroom, int phy_tailroom, int phy_payload,
+                   struct nfc_hci_dev **hdev)
+{
+       struct microread_info *info;
+       unsigned long quirks = 0;
+       u32 protocols, se;
+       struct nfc_hci_init_data init_data;
+       int r;
+
+       info = kzalloc(sizeof(struct microread_info), GFP_KERNEL);
+       if (!info) {
+               pr_err("Cannot allocate memory for microread_info.\n");
+               r = -ENOMEM;
+               goto err_info_alloc;
+       }
+
+       info->phy_ops = phy_ops;
+       info->phy_id = phy_id;
+
+       init_data.gate_count = ARRAY_SIZE(microread_gates);
+       memcpy(init_data.gates, microread_gates, sizeof(microread_gates));
+
+       strcpy(init_data.session_id, "MICROREA");
+
+       set_bit(NFC_HCI_QUIRK_SHORT_CLEAR, &quirks);
+
+       protocols = NFC_PROTO_JEWEL_MASK |
+                   NFC_PROTO_MIFARE_MASK |
+                   NFC_PROTO_FELICA_MASK |
+                   NFC_PROTO_ISO14443_MASK |
+                   NFC_PROTO_ISO14443_B_MASK |
+                   NFC_PROTO_NFC_DEP_MASK;
+
+       se = NFC_SE_UICC | NFC_SE_EMBEDDED;
+
+       info->hdev = nfc_hci_allocate_device(&microread_hci_ops, &init_data,
+                                            quirks, protocols, se, llc_name,
+                                            phy_headroom +
+                                            MICROREAD_CMDS_HEADROOM,
+                                            phy_tailroom +
+                                            MICROREAD_CMD_TAILROOM,
+                                            phy_payload);
+       if (!info->hdev) {
+               pr_err("Cannot allocate nfc hdev.\n");
+               r = -ENOMEM;
+               goto err_alloc_hdev;
+       }
+
+       nfc_hci_set_clientdata(info->hdev, info);
+
+       r = nfc_hci_register_device(info->hdev);
+       if (r)
+               goto err_regdev;
+
+       *hdev = info->hdev;
+
+       return 0;
+
+err_regdev:
+       nfc_hci_free_device(info->hdev);
+
+err_alloc_hdev:
+       kfree(info);
+
+err_info_alloc:
+       return r;
+}
+EXPORT_SYMBOL(microread_probe);
+
+void microread_remove(struct nfc_hci_dev *hdev)
+{
+       struct microread_info *info = nfc_hci_get_clientdata(hdev);
+
+       nfc_hci_unregister_device(hdev);
+       nfc_hci_free_device(hdev);
+       kfree(info);
+}
+EXPORT_SYMBOL(microread_remove);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/nfc/microread/microread.h b/drivers/nfc/microread/microread.h
new file mode 100644 (file)
index 0000000..64b447a
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2011 - 2012  Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __LOCAL_MICROREAD_H_
+#define __LOCAL_MICROREAD_H_
+
+#include <net/nfc/hci.h>
+
+#define DRIVER_DESC "NFC driver for microread"
+
+int microread_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
+                   int phy_headroom, int phy_tailroom, int phy_payload,
+                   struct nfc_hci_dev **hdev);
+
+void microread_remove(struct nfc_hci_dev *hdev);
+
+#endif /* __LOCAL_MICROREAD_H_ */
index 50b1ee4..3b731ac 100644 (file)
@@ -526,7 +526,7 @@ static int nfcwilink_probe(struct platform_device *pdev)
 
        nfc_dev_dbg(&pdev->dev, "probe entry");
 
-       drv = kzalloc(sizeof(struct nfcwilink), GFP_KERNEL);
+       drv = devm_kzalloc(&pdev->dev, sizeof(struct nfcwilink), GFP_KERNEL);
        if (!drv) {
                rc = -ENOMEM;
                goto exit;
@@ -542,12 +542,13 @@ static int nfcwilink_probe(struct platform_device *pdev)
 
        drv->ndev = nci_allocate_device(&nfcwilink_ops,
                                        protocols,
+                                       NFC_SE_NONE,
                                        NFCWILINK_HDR_LEN,
                                        0);
        if (!drv->ndev) {
                nfc_dev_err(&pdev->dev, "nci_allocate_device failed");
                rc = -ENOMEM;
-               goto free_exit;
+               goto exit;
        }
 
        nci_set_parent_dev(drv->ndev, &pdev->dev);
@@ -566,9 +567,6 @@ static int nfcwilink_probe(struct platform_device *pdev)
 free_dev_exit:
        nci_free_device(drv->ndev);
 
-free_exit:
-       kfree(drv);
-
 exit:
        return rc;
 }
@@ -588,8 +586,6 @@ static int nfcwilink_remove(struct platform_device *pdev)
        nci_unregister_device(ndev);
        nci_free_device(ndev);
 
-       kfree(drv);
-
        dev_set_drvdata(&pdev->dev, NULL);
 
        return 0;
index 97c440a..0f2a801 100644 (file)
 #define SONY_VENDOR_ID         0x054c
 #define PASORI_PRODUCT_ID      0x02e1
 
-#define PN533_QUIRKS_TYPE_A          BIT(0)
-#define PN533_QUIRKS_TYPE_F          BIT(1)
-#define PN533_QUIRKS_DEP             BIT(2)
-#define PN533_QUIRKS_RAW_EXCHANGE    BIT(3)
-
 #define PN533_DEVICE_STD    0x1
 #define PN533_DEVICE_PASORI 0x2
 
@@ -84,10 +79,18 @@ MODULE_DEVICE_TABLE(usb, pn533_table);
 #define PN533_LISTEN_TIME 2
 
 /* 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_HEADER_LEN (sizeof(struct pn533_frame) \
+                                       + 2) /* data[0] TFI, data[1] CC */
+#define PN533_FRAME_TAIL_LEN 2 /* data[len] DCS, data[len + 1] postamble*/
+
+/*
+ * Max extended frame payload len, excluding TFI and CC
+ * which are already in PN533_FRAME_HEADER_LEN.
+ */
+#define PN533_FRAME_MAX_PAYLOAD_LEN 263
+
+#define PN533_FRAME_ACK_SIZE 6 /* Preamble (1), SoPC (2), ACK Code (2),
+                                 Postamble (1) */
 #define PN533_FRAME_CHECKSUM(f) (f->data[f->datalen])
 #define PN533_FRAME_POSTAMBLE(f) (f->data[f->datalen + 1])
 
@@ -101,8 +104,6 @@ MODULE_DEVICE_TABLE(usb, pn533_table);
 
 /* 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
@@ -116,6 +117,7 @@ MODULE_DEVICE_TABLE(usb, pn533_table);
 #define PN533_CMD_TG_INIT_AS_TARGET 0x8c
 #define PN533_CMD_TG_GET_DATA 0x86
 #define PN533_CMD_TG_SET_DATA 0x8e
+#define PN533_CMD_UNDEF 0xff
 
 #define PN533_CMD_RESPONSE(cmd) (cmd + 1)
 
@@ -124,13 +126,12 @@ MODULE_DEVICE_TABLE(usb, pn533_table);
 #define PN533_CMD_MI_MASK 0x40
 #define PN533_CMD_RET_SUCCESS 0x00
 
-/* PN533 status codes */
-#define PN533_STATUS_TARGET_RELEASED 0x29
-
 struct pn533;
 
-typedef int (*pn533_cmd_complete_t) (struct pn533 *dev, void *arg,
-                                       u8 *params, int params_len);
+typedef int (*pn533_cmd_complete_t) (struct pn533 *dev, void *arg, int status);
+
+typedef int (*pn533_send_async_complete_t) (struct pn533 *dev, void *arg,
+                                       struct sk_buff *resp);
 
 /* structs for pn533 commands */
 
@@ -278,11 +279,6 @@ const struct pn533_poll_modulations poll_mod[] = {
 
 /* PN533_CMD_IN_ATR */
 
-struct pn533_cmd_activate_param {
-       u8 tg;
-       u8 next;
-} __packed;
-
 struct pn533_cmd_activate_response {
        u8 status;
        u8 nfcid3t[10];
@@ -295,14 +291,6 @@ struct pn533_cmd_activate_response {
        u8 gt[];
 } __packed;
 
-/* PN533_CMD_IN_JUMP_FOR_DEP */
-struct pn533_cmd_jump_dep {
-       u8 active;
-       u8 baud;
-       u8 next;
-       u8 data[];
-} __packed;
-
 struct pn533_cmd_jump_dep_response {
        u8 status;
        u8 tg;
@@ -325,32 +313,13 @@ struct pn533_cmd_jump_dep_response {
 #define PN533_INIT_TARGET_RESP_ACTIVE     0x1
 #define PN533_INIT_TARGET_RESP_DEP        0x4
 
-struct pn533_cmd_init_target {
-       u8 mode;
-       u8 mifare[6];
-       u8 felica[18];
-       u8 nfcid3[10];
-       u8 gb_len;
-       u8 gb[];
-} __packed;
-
-struct pn533_cmd_init_target_response {
-       u8 mode;
-       u8 cmd[];
-} __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 sk_buff_head resp_q;
 
@@ -361,12 +330,12 @@ struct pn533 {
        struct work_struct mi_work;
        struct work_struct tg_work;
        struct timer_list listen_timer;
-       struct pn533_frame *wq_in_frame;
        int wq_in_error;
        int cancel_listen;
 
        pn533_cmd_complete_t cmd_complete;
        void *cmd_complete_arg;
+       void *cmd_complete_mi_arg;
        struct mutex cmd_lock;
        u8 cmd;
 
@@ -387,16 +356,17 @@ struct pn533 {
 
        struct list_head cmd_queue;
        u8 cmd_pending;
+
+       struct pn533_frame_ops *ops;
 };
 
 struct pn533_cmd {
        struct list_head queue;
-       struct pn533_frame *out_frame;
-       struct pn533_frame *in_frame;
-       int in_frame_len;
-       pn533_cmd_complete_t cmd_complete;
+       u8 cmd_code;
+       struct sk_buff *req;
+       struct sk_buff *resp;
+       int resp_len;
        void *arg;
-       gfp_t flags;
 };
 
 struct pn533_frame {
@@ -407,6 +377,22 @@ struct pn533_frame {
        u8 data[];
 } __packed;
 
+struct pn533_frame_ops {
+       void (*tx_frame_init)(void *frame, u8 cmd_code);
+       void (*tx_frame_finish)(void *frame);
+       void (*tx_update_payload_len)(void *frame, int len);
+       int tx_header_len;
+       int tx_tail_len;
+
+       bool (*rx_is_frame_valid)(void *frame);
+       int (*rx_frame_size)(void *frame);
+       int rx_header_len;
+       int rx_tail_len;
+
+       int max_payload_len;
+       u8 (*get_cmd_code)(void *frame);
+};
+
 /* The rule: value + checksum = 0 */
 static inline u8 pn533_checksum(u8 value)
 {
@@ -425,37 +411,21 @@ static u8 pn533_data_checksum(u8 *data, int datalen)
        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)
+static void pn533_tx_frame_init(void *_frame, u8 cmd_code)
 {
-       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;
-}
+       struct pn533_frame *frame = _frame;
 
-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;
+       PN533_FRAME_CMD(frame) = cmd_code;
        frame->datalen = 2;
 }
 
-static void pn533_tx_frame_finish(struct pn533_frame *frame)
+static void pn533_tx_frame_finish(void *_frame)
 {
+       struct pn533_frame *frame = _frame;
+
        frame->datalen_checksum = pn533_checksum(frame->datalen);
 
        PN533_FRAME_CHECKSUM(frame) =
@@ -464,9 +434,17 @@ static void pn533_tx_frame_finish(struct pn533_frame *frame)
        PN533_FRAME_POSTAMBLE(frame) = 0;
 }
 
-static bool pn533_rx_frame_is_valid(struct pn533_frame *frame)
+static void pn533_tx_update_payload_len(void *_frame, int len)
+{
+       struct pn533_frame *frame = _frame;
+
+       frame->datalen += len;
+}
+
+static bool pn533_rx_frame_is_valid(void *_frame)
 {
        u8 checksum;
+       struct pn533_frame *frame = _frame;
 
        if (frame->start_frame != cpu_to_be16(PN533_SOF))
                return false;
@@ -493,28 +471,48 @@ static bool pn533_rx_frame_is_ack(struct pn533_frame *frame)
        return true;
 }
 
-static bool pn533_rx_frame_is_cmd_response(struct pn533_frame *frame, u8 cmd)
+static inline int pn533_rx_frame_size(void *frame)
+{
+       struct pn533_frame *f = frame;
+
+       return sizeof(struct pn533_frame) + f->datalen + PN533_FRAME_TAIL_LEN;
+}
+
+static u8 pn533_get_cmd_code(void *frame)
+{
+       struct pn533_frame *f = frame;
+
+       return PN533_FRAME_CMD(f);
+}
+
+struct pn533_frame_ops pn533_std_frame_ops = {
+       .tx_frame_init = pn533_tx_frame_init,
+       .tx_frame_finish = pn533_tx_frame_finish,
+       .tx_update_payload_len = pn533_tx_update_payload_len,
+       .tx_header_len = PN533_FRAME_HEADER_LEN,
+       .tx_tail_len = PN533_FRAME_TAIL_LEN,
+
+       .rx_is_frame_valid = pn533_rx_frame_is_valid,
+       .rx_frame_size = pn533_rx_frame_size,
+       .rx_header_len = PN533_FRAME_HEADER_LEN,
+       .rx_tail_len = PN533_FRAME_TAIL_LEN,
+
+       .max_payload_len =  PN533_FRAME_MAX_PAYLOAD_LEN,
+       .get_cmd_code = pn533_get_cmd_code,
+};
+
+static bool pn533_rx_frame_is_cmd_response(struct pn533 *dev, void *frame)
 {
-       return (PN533_FRAME_CMD(frame) == PN533_CMD_RESPONSE(cmd));
+       return (dev->ops->get_cmd_code(frame) == PN533_CMD_RESPONSE(dev->cmd));
 }
 
 
 static void pn533_wq_cmd_complete(struct work_struct *work)
 {
        struct pn533 *dev = container_of(work, struct pn533, cmd_complete_work);
-       struct pn533_frame *in_frame;
        int rc;
 
-       in_frame = dev->wq_in_frame;
-
-       if (dev->wq_in_error)
-               rc = dev->cmd_complete(dev, dev->cmd_complete_arg, NULL,
-                                                       dev->wq_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));
-
+       rc = dev->cmd_complete(dev, dev->cmd_complete_arg, dev->wq_in_error);
        if (rc != -EINPROGRESS)
                queue_work(dev->wq, &dev->cmd_work);
 }
@@ -522,46 +520,47 @@ static void pn533_wq_cmd_complete(struct work_struct *work)
 static void pn533_recv_response(struct urb *urb)
 {
        struct pn533 *dev = urb->context;
-       struct pn533_frame *in_frame;
-
-       dev->wq_in_frame = NULL;
+       u8 *in_frame;
 
        switch (urb->status) {
        case 0:
-               /* success */
-               break;
+               break; /* success */
        case -ECONNRESET:
        case -ENOENT:
-       case -ESHUTDOWN:
-               nfc_dev_dbg(&dev->interface->dev, "Urb shutting down with"
-                                               " status: %d", urb->status);
+               nfc_dev_dbg(&dev->interface->dev,
+                           "The urb has been canceled (status %d)",
+                           urb->status);
                dev->wq_in_error = urb->status;
                goto sched_wq;
+               break;
+       case -ESHUTDOWN:
        default:
-               nfc_dev_err(&dev->interface->dev, "Nonzero urb status received:"
-                                                       " %d", urb->status);
+               nfc_dev_err(&dev->interface->dev,
+                           "Urb failure (status %d)", urb->status);
                dev->wq_in_error = urb->status;
                goto sched_wq;
        }
 
        in_frame = dev->in_urb->transfer_buffer;
 
-       if (!pn533_rx_frame_is_valid(in_frame)) {
+       nfc_dev_dbg(&dev->interface->dev, "Received a frame.");
+       print_hex_dump_debug("PN533 RX: ", DUMP_PREFIX_NONE, 16, 1, in_frame,
+                            dev->ops->rx_frame_size(in_frame), false);
+
+       if (!dev->ops->rx_is_frame_valid(in_frame)) {
                nfc_dev_err(&dev->interface->dev, "Received an invalid frame");
                dev->wq_in_error = -EIO;
                goto sched_wq;
        }
 
-       if (!pn533_rx_frame_is_cmd_response(in_frame, dev->cmd)) {
-               nfc_dev_err(&dev->interface->dev, "The received frame is not "
-                                               "response to the last command");
+       if (!pn533_rx_frame_is_cmd_response(dev, in_frame)) {
+               nfc_dev_err(&dev->interface->dev,
+                           "It it not the response to the last command");
                dev->wq_in_error = -EIO;
                goto sched_wq;
        }
 
-       nfc_dev_dbg(&dev->interface->dev, "Received a valid frame");
        dev->wq_in_error = 0;
-       dev->wq_in_frame = in_frame;
 
 sched_wq:
        queue_work(dev->wq, &dev->cmd_complete_work);
@@ -582,18 +581,19 @@ static void pn533_recv_ack(struct urb *urb)
 
        switch (urb->status) {
        case 0:
-               /* success */
-               break;
+               break; /* success */
        case -ECONNRESET:
        case -ENOENT:
-       case -ESHUTDOWN:
-               nfc_dev_dbg(&dev->interface->dev, "Urb shutting down with"
-                                               " status: %d", urb->status);
+               nfc_dev_dbg(&dev->interface->dev,
+                           "The urb has been stopped (status %d)",
+                           urb->status);
                dev->wq_in_error = urb->status;
                goto sched_wq;
+               break;
+       case -ESHUTDOWN:
        default:
-               nfc_dev_err(&dev->interface->dev, "Nonzero urb status received:"
-                                                       " %d", urb->status);
+               nfc_dev_err(&dev->interface->dev,
+                           "Urb failure (status %d)", urb->status);
                dev->wq_in_error = urb->status;
                goto sched_wq;
        }
@@ -606,12 +606,10 @@ static void pn533_recv_ack(struct urb *urb)
                goto sched_wq;
        }
 
-       nfc_dev_dbg(&dev->interface->dev, "Received a valid ack");
-
        rc = pn533_submit_urb_for_response(dev, GFP_ATOMIC);
        if (rc) {
-               nfc_dev_err(&dev->interface->dev, "usb_submit_urb failed with"
-                                                       " result %d", rc);
+               nfc_dev_err(&dev->interface->dev,
+                           "usb_submit_urb failed with result %d", rc);
                dev->wq_in_error = rc;
                goto sched_wq;
        }
@@ -619,7 +617,6 @@ static void pn533_recv_ack(struct urb *urb)
        return;
 
 sched_wq:
-       dev->wq_in_frame = NULL;
        queue_work(dev->wq, &dev->cmd_complete_work);
 }
 
@@ -632,47 +629,46 @@ static int pn533_submit_urb_for_ack(struct pn533 *dev, gfp_t flags)
 
 static int pn533_send_ack(struct pn533 *dev, gfp_t flags)
 {
+       u8 ack[PN533_FRAME_ACK_SIZE] = {0x00, 0x00, 0xff, 0x00, 0xff, 0x00};
+       /* spec 7.1.1.3:  Preamble, SoPC (2), ACK Code (2), Postamble */
        int rc;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       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;
+       dev->out_urb->transfer_buffer = ack;
+       dev->out_urb->transfer_buffer_length = sizeof(ack);
        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,
+static int __pn533_send_frame_async(struct pn533 *dev,
+                                       struct sk_buff *out,
+                                       struct sk_buff *in,
+                                       int in_len,
                                        pn533_cmd_complete_t cmd_complete,
-                                       void *arg, gfp_t flags)
+                                       void *arg)
 {
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "Sending command 0x%x",
-                                               PN533_FRAME_CMD(out_frame));
-
-       dev->cmd = PN533_FRAME_CMD(out_frame);
+       dev->cmd = dev->ops->get_cmd_code(out->data);
        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->out_urb->transfer_buffer = out->data;
+       dev->out_urb->transfer_buffer_length = out->len;
 
-       dev->in_urb->transfer_buffer = in_frame;
-       dev->in_urb->transfer_buffer_length = in_frame_len;
+       dev->in_urb->transfer_buffer = in->data;
+       dev->in_urb->transfer_buffer_length = in_len;
 
-       rc = usb_submit_urb(dev->out_urb, flags);
+       print_hex_dump_debug("PN533 TX: ", DUMP_PREFIX_NONE, 16, 1,
+                            out->data, out->len, false);
+
+       rc = usb_submit_urb(dev->out_urb, GFP_KERNEL);
        if (rc)
                return rc;
 
-       rc = pn533_submit_urb_for_ack(dev, flags);
+       rc = pn533_submit_urb_for_ack(dev, GFP_KERNEL);
        if (rc)
                goto error;
 
@@ -683,145 +679,325 @@ error:
        return rc;
 }
 
-static void pn533_wq_cmd(struct work_struct *work)
+static void pn533_build_cmd_frame(struct pn533 *dev, u8 cmd_code,
+                                 struct sk_buff *skb)
 {
-       struct pn533 *dev = container_of(work, struct pn533, cmd_work);
-       struct pn533_cmd *cmd;
+       /* payload is already there, just update datalen */
+       int payload_len = skb->len;
+       struct pn533_frame_ops *ops = dev->ops;
 
-       mutex_lock(&dev->cmd_lock);
 
-       if (list_empty(&dev->cmd_queue)) {
-               dev->cmd_pending = 0;
-               mutex_unlock(&dev->cmd_lock);
-               return;
-       }
+       skb_push(skb, ops->tx_header_len);
+       skb_put(skb, ops->tx_tail_len);
 
-       cmd = list_first_entry(&dev->cmd_queue, struct pn533_cmd, queue);
+       ops->tx_frame_init(skb->data, cmd_code);
+       ops->tx_update_payload_len(skb->data, payload_len);
+       ops->tx_frame_finish(skb->data);
+}
 
-       mutex_unlock(&dev->cmd_lock);
+struct pn533_send_async_complete_arg {
+       pn533_send_async_complete_t  complete_cb;
+       void *complete_cb_context;
+       struct sk_buff *resp;
+       struct sk_buff *req;
+};
 
-       __pn533_send_cmd_frame_async(dev, cmd->out_frame, cmd->in_frame,
-                                    cmd->in_frame_len, cmd->cmd_complete,
-                                    cmd->arg, cmd->flags);
+static int pn533_send_async_complete(struct pn533 *dev, void *_arg, int status)
+{
+       struct pn533_send_async_complete_arg *arg = _arg;
 
-       list_del(&cmd->queue);
-       kfree(cmd);
+       struct sk_buff *req = arg->req;
+       struct sk_buff *resp = arg->resp;
+
+       int rc;
+
+       dev_kfree_skb(req);
+
+       if (status < 0) {
+               arg->complete_cb(dev, arg->complete_cb_context,
+                                ERR_PTR(status));
+               dev_kfree_skb(resp);
+               kfree(arg);
+               return status;
+       }
+
+       skb_put(resp, dev->ops->rx_frame_size(resp->data));
+       skb_pull(resp, dev->ops->rx_header_len);
+       skb_trim(resp, resp->len - dev->ops->rx_tail_len);
+
+       rc = arg->complete_cb(dev, arg->complete_cb_context, resp);
+
+       kfree(arg);
+       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)
+static int __pn533_send_async(struct pn533 *dev, u8 cmd_code,
+                             struct sk_buff *req, struct sk_buff *resp,
+                             int resp_len,
+                             pn533_send_async_complete_t complete_cb,
+                             void *complete_cb_context)
 {
        struct pn533_cmd *cmd;
+       struct pn533_send_async_complete_arg *arg;
        int rc = 0;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       nfc_dev_dbg(&dev->interface->dev, "Sending command 0x%x", cmd_code);
+
+       arg = kzalloc(sizeof(*arg), GFP_KERNEL);
+       if (!arg)
+               return -ENOMEM;
+
+       arg->complete_cb = complete_cb;
+       arg->complete_cb_context = complete_cb_context;
+       arg->resp = resp;
+       arg->req = req;
+
+       pn533_build_cmd_frame(dev, cmd_code, req);
 
        mutex_lock(&dev->cmd_lock);
 
        if (!dev->cmd_pending) {
-               rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame,
-                                                 in_frame_len, cmd_complete,
-                                                 arg, flags);
-               if (!rc)
-                       dev->cmd_pending = 1;
+               rc = __pn533_send_frame_async(dev, req, resp, resp_len,
+                                             pn533_send_async_complete, arg);
+               if (rc)
+                       goto error;
 
+               dev->cmd_pending = 1;
                goto unlock;
        }
 
-       nfc_dev_dbg(&dev->interface->dev, "%s Queueing command", __func__);
+       nfc_dev_dbg(&dev->interface->dev, "%s Queueing command 0x%x", __func__,
+                   cmd_code);
 
-       cmd = kzalloc(sizeof(struct pn533_cmd), flags);
+       cmd = kzalloc(sizeof(struct pn533_cmd), GFP_KERNEL);
        if (!cmd) {
                rc = -ENOMEM;
-               goto unlock;
+               goto error;
        }
 
        INIT_LIST_HEAD(&cmd->queue);
-       cmd->out_frame = out_frame;
-       cmd->in_frame = in_frame;
-       cmd->in_frame_len = in_frame_len;
-       cmd->cmd_complete = cmd_complete;
+       cmd->cmd_code = cmd_code;
+       cmd->req = req;
+       cmd->resp = resp;
+       cmd->resp_len = resp_len;
        cmd->arg = arg;
-       cmd->flags = flags;
 
        list_add_tail(&cmd->queue, &dev->cmd_queue);
 
+       goto unlock;
+
+error:
+       kfree(arg);
 unlock:
        mutex_unlock(&dev->cmd_lock);
+       return rc;
+}
+
+static int pn533_send_data_async(struct pn533 *dev, u8 cmd_code,
+                                struct sk_buff *req,
+                                pn533_send_async_complete_t complete_cb,
+                                void *complete_cb_context)
+{
+       struct sk_buff *resp;
+       int rc;
+       int  resp_len = dev->ops->rx_header_len +
+                       dev->ops->max_payload_len +
+                       dev->ops->rx_tail_len;
+
+       resp = nfc_alloc_recv_skb(resp_len, GFP_KERNEL);
+       if (!resp)
+               return -ENOMEM;
+
+       rc = __pn533_send_async(dev, cmd_code, req, resp, resp_len, complete_cb,
+                               complete_cb_context);
+       if (rc)
+               dev_kfree_skb(resp);
 
        return rc;
 }
 
-struct pn533_sync_cmd_response {
+static int pn533_send_cmd_async(struct pn533 *dev, u8 cmd_code,
+                               struct sk_buff *req,
+                               pn533_send_async_complete_t complete_cb,
+                               void *complete_cb_context)
+{
+       struct sk_buff *resp;
        int rc;
-       struct completion done;
-};
+       int  resp_len = dev->ops->rx_header_len +
+                       dev->ops->max_payload_len +
+                       dev->ops->rx_tail_len;
+
+       resp = alloc_skb(resp_len, GFP_KERNEL);
+       if (!resp)
+               return -ENOMEM;
+
+       rc = __pn533_send_async(dev, cmd_code, req, resp, resp_len, complete_cb,
+                               complete_cb_context);
+       if (rc)
+               dev_kfree_skb(resp);
 
-static int pn533_sync_cmd_complete(struct pn533 *dev, void *_arg,
-                                       u8 *params, int params_len)
+       return rc;
+}
+
+/*
+ * pn533_send_cmd_direct_async
+ *
+ * The function sends a piority cmd directly to the chip omiting the cmd
+ * queue. It's intended to be used by chaining mechanism of received responses
+ * where the host has to request every single chunk of data before scheduling
+ * next cmd from the queue.
+ */
+static int pn533_send_cmd_direct_async(struct pn533 *dev, u8 cmd_code,
+                                      struct sk_buff *req,
+                                      pn533_send_async_complete_t complete_cb,
+                                      void *complete_cb_context)
 {
-       struct pn533_sync_cmd_response *arg = _arg;
+       struct pn533_send_async_complete_arg *arg;
+       struct sk_buff *resp;
+       int rc;
+       int resp_len = dev->ops->rx_header_len +
+                      dev->ops->max_payload_len +
+                      dev->ops->rx_tail_len;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       resp = alloc_skb(resp_len, GFP_KERNEL);
+       if (!resp)
+               return -ENOMEM;
+
+       arg = kzalloc(sizeof(*arg), GFP_KERNEL);
+       if (!arg) {
+               dev_kfree_skb(resp);
+               return -ENOMEM;
+       }
 
-       arg->rc = 0;
+       arg->complete_cb = complete_cb;
+       arg->complete_cb_context = complete_cb_context;
+       arg->resp = resp;
+       arg->req = req;
 
-       if (params_len < 0) /* error */
-               arg->rc = params_len;
+       pn533_build_cmd_frame(dev, cmd_code, req);
 
+       rc = __pn533_send_frame_async(dev, req, resp, resp_len,
+                                     pn533_send_async_complete, arg);
+       if (rc < 0) {
+               dev_kfree_skb(resp);
+               kfree(arg);
+       }
+
+       return rc;
+}
+
+static void pn533_wq_cmd(struct work_struct *work)
+{
+       struct pn533 *dev = container_of(work, struct pn533, cmd_work);
+       struct pn533_cmd *cmd;
+
+       mutex_lock(&dev->cmd_lock);
+
+       if (list_empty(&dev->cmd_queue)) {
+               dev->cmd_pending = 0;
+               mutex_unlock(&dev->cmd_lock);
+               return;
+       }
+
+       cmd = list_first_entry(&dev->cmd_queue, struct pn533_cmd, queue);
+
+       list_del(&cmd->queue);
+
+       mutex_unlock(&dev->cmd_lock);
+
+       __pn533_send_frame_async(dev, cmd->req, cmd->resp, cmd->resp_len,
+                                pn533_send_async_complete, cmd->arg);
+
+       kfree(cmd);
+}
+
+struct pn533_sync_cmd_response {
+       struct sk_buff *resp;
+       struct completion done;
+};
+
+static int pn533_send_sync_complete(struct pn533 *dev, void *_arg,
+                                   struct sk_buff *resp)
+{
+       struct pn533_sync_cmd_response *arg = _arg;
+
+       arg->resp = resp;
        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)
+/*  pn533_send_cmd_sync
+ *
+ *  Please note the req parameter is freed inside the function to
+ *  limit a number of return value interpretations by the caller.
+ *
+ *  1. negative in case of error during TX path -> req should be freed
+ *
+ *  2. negative in case of error during RX path -> req should not be freed
+ *     as it's been already freed at the begining of RX path by
+ *     async_complete_cb.
+ *
+ *  3. valid pointer in case of succesfult RX path
+ *
+ *  A caller has to check a return value with IS_ERR macro. If the test pass,
+ *  the returned pointer is valid.
+ *
+ * */
+static struct sk_buff *pn533_send_cmd_sync(struct pn533 *dev, u8 cmd_code,
+                                              struct sk_buff *req)
 {
        int rc;
        struct pn533_sync_cmd_response arg;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
-
        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;
+       rc = pn533_send_cmd_async(dev, cmd_code, req,
+                                 pn533_send_sync_complete, &arg);
+       if (rc) {
+               dev_kfree_skb(req);
+               return ERR_PTR(rc);
+       }
 
        wait_for_completion(&arg.done);
 
-       return arg.rc;
+       return arg.resp;
 }
 
 static void pn533_send_complete(struct urb *urb)
 {
        struct pn533 *dev = urb->context;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
-
        switch (urb->status) {
        case 0:
-               /* success */
-               break;
+               break; /* success */
        case -ECONNRESET:
        case -ENOENT:
-       case -ESHUTDOWN:
-               nfc_dev_dbg(&dev->interface->dev, "Urb shutting down with"
-                                               " status: %d", urb->status);
+               nfc_dev_dbg(&dev->interface->dev,
+                           "The urb has been stopped (status %d)",
+                           urb->status);
                break;
+       case -ESHUTDOWN:
        default:
-               nfc_dev_dbg(&dev->interface->dev, "Nonzero urb status received:"
-                                                       " %d", urb->status);
+               nfc_dev_err(&dev->interface->dev,
+                           "Urb failure (status %d)", urb->status);
        }
 }
 
+static struct sk_buff *pn533_alloc_skb(struct pn533 *dev, unsigned int size)
+{
+       struct sk_buff *skb;
+
+       skb = alloc_skb(dev->ops->tx_header_len +
+                       size +
+                       dev->ops->tx_tail_len, GFP_KERNEL);
+
+       if (skb)
+               skb_reserve(skb, dev->ops->tx_header_len);
+
+       return skb;
+}
+
 struct pn533_target_type_a {
        __be16 sens_res;
        u8 sel_res;
@@ -862,9 +1038,9 @@ static bool pn533_target_type_a_is_valid(struct pn533_target_type_a *type_a,
        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))
+            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 */
@@ -879,7 +1055,7 @@ static int pn533_target_found_type_a(struct nfc_target *nfc_tgt, u8 *tgt_data,
 {
        struct pn533_target_type_a *tgt_type_a;
 
-       tgt_type_a = (struct pn533_target_type_a *) tgt_data;
+       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;
@@ -937,14 +1113,13 @@ static int pn533_target_found_felica(struct nfc_target *nfc_tgt, u8 *tgt_data,
 {
        struct pn533_target_felica *tgt_felica;
 
-       tgt_felica = (struct pn533_target_felica *) tgt_data;
+       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)
+       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;
@@ -974,9 +1149,9 @@ static bool pn533_target_jewel_is_valid(struct pn533_target_jewel *jewel,
        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))
+            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;
@@ -987,7 +1162,7 @@ static int pn533_target_found_jewel(struct nfc_target *nfc_tgt, u8 *tgt_data,
 {
        struct pn533_target_jewel *tgt_jewel;
 
-       tgt_jewel = (struct pn533_target_jewel *) tgt_data;
+       tgt_jewel = (struct pn533_target_jewel *)tgt_data;
 
        if (!pn533_target_jewel_is_valid(tgt_jewel, tgt_data_len))
                return -EPROTO;
@@ -1046,7 +1221,7 @@ static int pn533_target_found_type_b(struct nfc_target *nfc_tgt, u8 *tgt_data,
 {
        struct pn533_target_type_b *tgt_type_b;
 
-       tgt_type_b = (struct pn533_target_type_b *) tgt_data;
+       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;
@@ -1056,50 +1231,37 @@ static int pn533_target_found_type_b(struct nfc_target *nfc_tgt, u8 *tgt_data,
        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)
+static int pn533_target_found(struct pn533 *dev, u8 tg, u8 *tgdata,
+                             int tgdata_len)
 {
-       int target_data_len;
        struct nfc_target nfc_tgt;
        int rc;
 
        nfc_dev_dbg(&dev->interface->dev, "%s - modulation=%d", __func__,
-                                                       dev->poll_mod_curr);
+                   dev->poll_mod_curr);
 
-       if (resp->tg != 1)
+       if (tg != 1)
                return -EPROTO;
 
        memset(&nfc_tgt, 0, sizeof(struct nfc_target));
 
-       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);
+               rc = pn533_target_found_type_a(&nfc_tgt, tgdata, tgdata_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);
+               rc = pn533_target_found_felica(&nfc_tgt, tgdata, tgdata_len);
                break;
        case PN533_POLL_MOD_106KBPS_JEWEL:
-               rc = pn533_target_found_jewel(&nfc_tgt, resp->target_data,
-                                                       target_data_len);
+               rc = pn533_target_found_jewel(&nfc_tgt, tgdata, tgdata_len);
                break;
        case PN533_POLL_MOD_847KBPS_B:
-               rc = pn533_target_found_type_b(&nfc_tgt, resp->target_data,
-                                                       target_data_len);
+               rc = pn533_target_found_type_b(&nfc_tgt, tgdata, tgdata_len);
                break;
        default:
-               nfc_dev_err(&dev->interface->dev, "Unknown current poll"
-                                                               " modulation");
+               nfc_dev_err(&dev->interface->dev,
+                           "Unknown current poll modulation");
                return -EPROTO;
        }
 
@@ -1107,13 +1269,14 @@ static int pn533_target_found(struct pn533 *dev,
                return rc;
 
        if (!(nfc_tgt.supported_protocols & dev->poll_protocols)) {
-               nfc_dev_dbg(&dev->interface->dev, "The target found does not"
-                                               " have the desired protocol");
+               nfc_dev_dbg(&dev->interface->dev,
+                           "The Tg found doesn't have the desired protocol");
                return -EAGAIN;
        }
 
-       nfc_dev_dbg(&dev->interface->dev, "Target found - supported protocols: "
-                                       "0x%x", nfc_tgt.supported_protocols);
+       nfc_dev_dbg(&dev->interface->dev,
+                   "Target found - supported protocols: 0x%x",
+                   nfc_tgt.supported_protocols);
 
        dev->tgt_available_prots = nfc_tgt.supported_protocols;
 
@@ -1135,7 +1298,7 @@ static void pn533_poll_reset_mod_list(struct pn533 *dev)
 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];
+               (struct pn533_poll_modulations *)&poll_mod[mod_index];
        dev->poll_mod_count++;
 }
 
@@ -1144,13 +1307,13 @@ static void pn533_poll_create_mod_list(struct pn533 *dev,
 {
        pn533_poll_reset_mod_list(dev);
 
-       if (im_protocols & NFC_PROTO_MIFARE_MASK
-           || im_protocols & NFC_PROTO_ISO14443_MASK
-           || im_protocols & NFC_PROTO_NFC_DEP_MASK)
+       if ((im_protocols & NFC_PROTO_MIFARE_MASK) ||
+           (im_protocols & NFC_PROTO_ISO14443_MASK) ||
+           (im_protocols & NFC_PROTO_NFC_DEP_MASK))
                pn533_poll_add_mod(dev, PN533_POLL_MOD_106KBPS_A);
 
-       if (im_protocols & NFC_PROTO_FELICA_MASK
-           || im_protocols & NFC_PROTO_NFC_DEP_MASK) {
+       if (im_protocols & NFC_PROTO_FELICA_MASK ||
+           im_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);
        }
@@ -1165,17 +1328,20 @@ static void pn533_poll_create_mod_list(struct pn533 *dev,
                pn533_poll_add_mod(dev, PN533_LISTEN_MOD);
 }
 
-static int pn533_start_poll_complete(struct pn533 *dev, void *arg,
-                                    u8 *params, int params_len)
+static int pn533_start_poll_complete(struct pn533 *dev, struct sk_buff *resp)
 {
-       struct pn533_poll_response *resp;
-       int rc;
+       u8 nbtg, tg, *tgdata;
+       int rc, tgdata_len;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       resp = (struct pn533_poll_response *) params;
-       if (resp->nbtg) {
-               rc = pn533_target_found(dev, resp, params_len);
+       nbtg = resp->data[0];
+       tg = resp->data[1];
+       tgdata = &resp->data[2];
+       tgdata_len = resp->len - 2;  /* nbtg + tg */
+
+       if (nbtg) {
+               rc = pn533_target_found(dev, tg, tgdata, tgdata_len);
 
                /* We must stop the poll after a valid target found */
                if (rc == 0) {
@@ -1187,159 +1353,134 @@ static int pn533_start_poll_complete(struct pn533 *dev, void *arg,
        return -EAGAIN;
 }
 
-static int pn533_init_target_frame(struct pn533_frame *frame,
-                                  u8 *gb, size_t gb_len)
+static struct sk_buff *pn533_alloc_poll_tg_frame(struct pn533 *dev)
 {
-       struct pn533_cmd_init_target *cmd;
-       size_t cmd_len;
+       struct sk_buff *skb;
+       u8 *felica, *nfcid3, *gb;
+
+       u8 *gbytes = dev->gb;
+       size_t gbytes_len = dev->gb_len;
+
        u8 felica_params[18] = {0x1, 0xfe, /* DEP */
                                0x0, 0x0, 0x0, 0x0, 0x0, 0x0, /* random */
                                0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
                                0xff, 0xff}; /* System code */
+
        u8 mifare_params[6] = {0x1, 0x1, /* SENS_RES */
                               0x0, 0x0, 0x0,
                               0x40}; /* SEL_RES for DEP */
 
-       cmd_len = sizeof(struct pn533_cmd_init_target) + gb_len + 1;
-       cmd = kzalloc(cmd_len, GFP_KERNEL);
-       if (cmd == NULL)
-               return -ENOMEM;
+       unsigned int skb_len = 36 + /* mode (1), mifare (6),
+                                      felica (18), nfcid3 (10), gb_len (1) */
+                              gbytes_len +
+                              1;  /* len Tk*/
 
-       pn533_tx_frame_init(frame, PN533_CMD_TG_INIT_AS_TARGET);
+       skb = pn533_alloc_skb(dev, skb_len);
+       if (!skb)
+               return NULL;
 
        /* DEP support only */
-       cmd->mode |= PN533_INIT_TARGET_DEP;
+       *skb_put(skb, 1) = PN533_INIT_TARGET_DEP;
+
+       /* MIFARE params */
+       memcpy(skb_put(skb, 6), mifare_params, 6);
 
        /* Felica params */
-       memcpy(cmd->felica, felica_params, 18);
-       get_random_bytes(cmd->felica + 2, 6);
+       felica = skb_put(skb, 18);
+       memcpy(felica, felica_params, 18);
+       get_random_bytes(felica + 2, 6);
 
        /* NFCID3 */
-       memset(cmd->nfcid3, 0, 10);
-       memcpy(cmd->nfcid3, cmd->felica, 8);
-
-       /* MIFARE params */
-       memcpy(cmd->mifare, mifare_params, 6);
+       nfcid3 = skb_put(skb, 10);
+       memset(nfcid3, 0, 10);
+       memcpy(nfcid3, felica, 8);
 
        /* General bytes */
-       cmd->gb_len = gb_len;
-       memcpy(cmd->gb, gb, gb_len);
-
-       /* Len Tk */
-       cmd->gb[gb_len] = 0;
-
-       memcpy(PN533_FRAME_CMD_PARAMS_PTR(frame), cmd, cmd_len);
+       *skb_put(skb, 1) = gbytes_len;
 
-       frame->datalen += cmd_len;
+       gb = skb_put(skb, gbytes_len);
+       memcpy(gb, gbytes, gbytes_len);
 
-       pn533_tx_frame_finish(frame);
-
-       kfree(cmd);
+       /* Len Tk */
+       *skb_put(skb, 1) = 0;
 
-       return 0;
+       return skb;
 }
 
-#define PN533_CMD_DATAEXCH_HEAD_LEN (sizeof(struct pn533_frame) + 3)
+#define PN533_CMD_DATAEXCH_HEAD_LEN 1
 #define PN533_CMD_DATAEXCH_DATA_MAXLEN 262
 static int pn533_tm_get_data_complete(struct pn533 *dev, void *arg,
-                                     u8 *params, int params_len)
+                                     struct sk_buff *resp)
 {
-       struct sk_buff *skb_resp = arg;
-       struct pn533_frame *in_frame = (struct pn533_frame *) skb_resp->data;
+       u8 status;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       if (params_len < 0) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Error %d when starting as a target",
-                           params_len);
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
 
-               return params_len;
-       }
+       status = resp->data[0];
+       skb_pull(resp, sizeof(status));
 
-       if (params_len > 0 && params[0] != 0) {
+       if (status != 0) {
                nfc_tm_deactivated(dev->nfc_dev);
-
                dev->tgt_mode = 0;
-
-               kfree_skb(skb_resp);
+               dev_kfree_skb(resp);
                return 0;
        }
 
-       skb_put(skb_resp, PN533_FRAME_SIZE(in_frame));
-       skb_pull(skb_resp, PN533_CMD_DATAEXCH_HEAD_LEN);
-       skb_trim(skb_resp, skb_resp->len - PN533_FRAME_TAIL_SIZE);
-
-       return nfc_tm_data_received(dev->nfc_dev, skb_resp);
+       return nfc_tm_data_received(dev->nfc_dev, resp);
 }
 
 static void pn533_wq_tg_get_data(struct work_struct *work)
 {
        struct pn533 *dev = container_of(work, struct pn533, tg_work);
-       struct pn533_frame *in_frame;
-       struct sk_buff *skb_resp;
-       size_t skb_resp_len;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       struct sk_buff *skb;
+       int rc;
 
-       skb_resp_len = PN533_CMD_DATAEXCH_HEAD_LEN +
-               PN533_CMD_DATAEXCH_DATA_MAXLEN +
-               PN533_FRAME_TAIL_SIZE;
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       skb_resp = nfc_alloc_recv_skb(skb_resp_len, GFP_KERNEL);
-       if (!skb_resp)
+       skb = pn533_alloc_skb(dev, 0);
+       if (!skb)
                return;
 
-       in_frame = (struct pn533_frame *)skb_resp->data;
+       rc = pn533_send_data_async(dev, PN533_CMD_TG_GET_DATA, skb,
+                                  pn533_tm_get_data_complete, NULL);
 
-       pn533_tx_frame_init(dev->out_frame, PN533_CMD_TG_GET_DATA);
-       pn533_tx_frame_finish(dev->out_frame);
-
-       pn533_send_cmd_frame_async(dev, dev->out_frame, in_frame,
-                                  skb_resp_len,
-                                  pn533_tm_get_data_complete,
-                                  skb_resp, GFP_KERNEL);
+       if (rc < 0)
+               dev_kfree_skb(skb);
 
        return;
 }
 
 #define ATR_REQ_GB_OFFSET 17
-static int pn533_init_target_complete(struct pn533 *dev, void *arg,
-                                     u8 *params, int params_len)
+static int pn533_init_target_complete(struct pn533 *dev, struct sk_buff *resp)
 {
-       struct pn533_cmd_init_target_response *resp;
-       u8 frame, comm_mode = NFC_COMM_PASSIVE, *gb;
+       u8 mode, *cmd, comm_mode = NFC_COMM_PASSIVE, *gb;
        size_t gb_len;
        int rc;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       if (params_len < 0) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Error %d when starting as a target",
-                           params_len);
-
-               return params_len;
-       }
-
-       if (params_len < ATR_REQ_GB_OFFSET + 1)
+       if (resp->len < ATR_REQ_GB_OFFSET + 1)
                return -EINVAL;
 
-       resp = (struct pn533_cmd_init_target_response *) params;
+       mode = resp->data[0];
+       cmd = &resp->data[1];
 
-       nfc_dev_dbg(&dev->interface->dev, "Target mode 0x%x param len %d\n",
-                   resp->mode, params_len);
+       nfc_dev_dbg(&dev->interface->dev, "Target mode 0x%x len %d\n",
+                   mode, resp->len);
 
-       frame = resp->mode & PN533_INIT_TARGET_RESP_FRAME_MASK;
-       if (frame == PN533_INIT_TARGET_RESP_ACTIVE)
+       if ((mode & PN533_INIT_TARGET_RESP_FRAME_MASK) ==
+           PN533_INIT_TARGET_RESP_ACTIVE)
                comm_mode = NFC_COMM_ACTIVE;
 
-       /* Again, only DEP */
-       if ((resp->mode & PN533_INIT_TARGET_RESP_DEP) == 0)
+       if ((mode & PN533_INIT_TARGET_RESP_DEP) == 0)  /* Only DEP supported */
                return -EOPNOTSUPP;
 
-       gb = resp->cmd + ATR_REQ_GB_OFFSET;
-       gb_len = params_len - (ATR_REQ_GB_OFFSET + 1);
+       gb = cmd + ATR_REQ_GB_OFFSET;
+       gb_len = resp->len - (ATR_REQ_GB_OFFSET + 1);
 
        rc = nfc_tm_activated(dev->nfc_dev, NFC_PROTO_NFC_DEP_MASK,
                              comm_mode, gb, gb_len);
@@ -1350,7 +1491,6 @@ static int pn533_init_target_complete(struct pn533 *dev, void *arg,
        }
 
        dev->tgt_mode = 1;
-
        queue_work(dev->wq, &dev->tg_work);
 
        return 0;
@@ -1358,7 +1498,7 @@ static int pn533_init_target_complete(struct pn533 *dev, void *arg,
 
 static void pn533_listen_mode_timer(unsigned long data)
 {
-       struct pn533 *dev = (struct pn533 *) data;
+       struct pn533 *dev = (struct pn533 *)data;
 
        nfc_dev_dbg(&dev->interface->dev, "Listen mode timeout");
 
@@ -1373,88 +1513,104 @@ static void pn533_listen_mode_timer(unsigned long data)
 }
 
 static int pn533_poll_complete(struct pn533 *dev, void *arg,
-                              u8 *params, int params_len)
+                              struct sk_buff *resp)
 {
        struct pn533_poll_modulations *cur_mod;
        int rc;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       if (params_len == -ENOENT) {
-               if (dev->poll_mod_count != 0)
-                       return 0;
-
-               nfc_dev_err(&dev->interface->dev,
-                           "Polling operation has been stopped");
-
-               goto stop_poll;
-       }
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
 
-       if (params_len < 0) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Error %d when running poll", params_len);
+               nfc_dev_err(&dev->interface->dev, "%s  Poll complete error %d",
+                           __func__, rc);
 
-               goto stop_poll;
+               if (rc == -ENOENT) {
+                       if (dev->poll_mod_count != 0)
+                               return rc;
+                       else
+                               goto stop_poll;
+               } else if (rc < 0) {
+                       nfc_dev_err(&dev->interface->dev,
+                                   "Error %d when running poll", rc);
+                       goto stop_poll;
+               }
        }
 
        cur_mod = dev->poll_mod_active[dev->poll_mod_curr];
 
-       if (cur_mod->len == 0) {
+       if (cur_mod->len == 0) { /* Target mode */
                del_timer(&dev->listen_timer);
-
-               return pn533_init_target_complete(dev, arg, params, params_len);
-       } else {
-               rc = pn533_start_poll_complete(dev, arg, params, params_len);
-               if (!rc)
-                       return rc;
+               rc = pn533_init_target_complete(dev, resp);
+               goto done;
        }
 
-       pn533_poll_next_mod(dev);
+       /* Initiator mode */
+       rc = pn533_start_poll_complete(dev, resp);
+       if (!rc)
+               goto done;
 
+       pn533_poll_next_mod(dev);
        queue_work(dev->wq, &dev->poll_work);
 
-       return 0;
+done:
+       dev_kfree_skb(resp);
+       return rc;
 
 stop_poll:
+       nfc_dev_err(&dev->interface->dev, "Polling operation has been stopped");
+
        pn533_poll_reset_mod_list(dev);
        dev->poll_protocols = 0;
-       return 0;
+       return rc;
 }
 
-static void pn533_build_poll_frame(struct pn533 *dev,
-                                  struct pn533_frame *frame,
-                                  struct pn533_poll_modulations *mod)
+static struct sk_buff *pn533_alloc_poll_in_frame(struct pn533 *dev,
+                                       struct pn533_poll_modulations *mod)
 {
-       nfc_dev_dbg(&dev->interface->dev, "mod len %d\n", mod->len);
+       struct sk_buff *skb;
 
-       if (mod->len == 0) {
-               /* Listen mode */
-               pn533_init_target_frame(frame, dev->gb, dev->gb_len);
-       } else {
-               /* Polling mode */
-               pn533_tx_frame_init(frame, PN533_CMD_IN_LIST_PASSIVE_TARGET);
+       skb = pn533_alloc_skb(dev, mod->len);
+       if (!skb)
+               return NULL;
 
-               memcpy(PN533_FRAME_CMD_PARAMS_PTR(frame), &mod->data, mod->len);
-               frame->datalen += mod->len;
+       memcpy(skb_put(skb, mod->len), &mod->data, mod->len);
 
-               pn533_tx_frame_finish(frame);
-       }
+       return skb;
 }
 
 static int pn533_send_poll_frame(struct pn533 *dev)
 {
-       struct pn533_poll_modulations *cur_mod;
+       struct pn533_poll_modulations *mod;
+       struct sk_buff *skb;
        int rc;
+       u8 cmd_code;
 
-       cur_mod = dev->poll_mod_active[dev->poll_mod_curr];
+       mod = dev->poll_mod_active[dev->poll_mod_curr];
 
-       pn533_build_poll_frame(dev, dev->out_frame, cur_mod);
+       nfc_dev_dbg(&dev->interface->dev, "%s mod len %d\n",
+                   __func__, mod->len);
 
-       rc = pn533_send_cmd_frame_async(dev, dev->out_frame, dev->in_frame,
-                               dev->in_maxlen, pn533_poll_complete,
-                               NULL, GFP_KERNEL);
-       if (rc)
+       if (mod->len == 0) {  /* Listen mode */
+               cmd_code = PN533_CMD_TG_INIT_AS_TARGET;
+               skb = pn533_alloc_poll_tg_frame(dev);
+       } else {  /* Polling mode */
+               cmd_code =  PN533_CMD_IN_LIST_PASSIVE_TARGET;
+               skb = pn533_alloc_poll_in_frame(dev, mod);
+       }
+
+       if (!skb) {
+               nfc_dev_err(&dev->interface->dev, "Failed to allocate skb.");
+               return -ENOMEM;
+       }
+
+       rc = pn533_send_cmd_async(dev, cmd_code, skb, pn533_poll_complete,
+                                 NULL);
+       if (rc < 0) {
+               dev_kfree_skb(skb);
                nfc_dev_err(&dev->interface->dev, "Polling loop error %d", rc);
+       }
 
        return rc;
 }
@@ -1530,8 +1686,8 @@ static void pn533_stop_poll(struct nfc_dev *nfc_dev)
        del_timer(&dev->listen_timer);
 
        if (!dev->poll_mod_count) {
-               nfc_dev_dbg(&dev->interface->dev, "Polling operation was not"
-                                                               " running");
+               nfc_dev_dbg(&dev->interface->dev,
+                           "Polling operation was not running");
                return;
        }
 
@@ -1546,38 +1702,38 @@ static void pn533_stop_poll(struct nfc_dev *nfc_dev)
 
 static int pn533_activate_target_nfcdep(struct pn533 *dev)
 {
-       struct pn533_cmd_activate_param param;
-       struct pn533_cmd_activate_response *resp;
+       struct pn533_cmd_activate_response *rsp;
        u16 gt_len;
        int rc;
 
+       struct sk_buff *skb;
+       struct sk_buff *resp;
+
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       pn533_tx_frame_init(dev->out_frame, PN533_CMD_IN_ATR);
+       skb = pn533_alloc_skb(dev, sizeof(u8) * 2); /*TG + Next*/
+       if (!skb)
+               return -ENOMEM;
 
-       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);
+       *skb_put(skb, sizeof(u8)) = 1; /* TG */
+       *skb_put(skb, sizeof(u8)) = 0; /* Next */
 
-       pn533_tx_frame_finish(dev->out_frame);
+       resp = pn533_send_cmd_sync(dev, PN533_CMD_IN_ATR, skb);
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
 
-       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)
+       rsp = (struct pn533_cmd_activate_response *)resp->data;
+       rc = rsp->status & PN533_CMD_RET_MASK;
+       if (rc != PN533_CMD_RET_SUCCESS) {
+               dev_kfree_skb(resp);
                return -EIO;
+       }
 
        /* ATR_RES general bytes are located at offset 16 */
-       gt_len = PN533_FRAME_CMD_PARAMS_LEN(dev->in_frame) - 16;
-       rc = nfc_set_remote_general_bytes(dev->nfc_dev, resp->gt, gt_len);
+       gt_len = resp->len - 16;
+       rc = nfc_set_remote_general_bytes(dev->nfc_dev, rsp->gt, gt_len);
 
+       dev_kfree_skb(resp);
        return rc;
 }
 
@@ -1588,38 +1744,38 @@ static int pn533_activate_target(struct nfc_dev *nfc_dev,
        int rc;
 
        nfc_dev_dbg(&dev->interface->dev, "%s - protocol=%u", __func__,
-                                                               protocol);
+                   protocol);
 
        if (dev->poll_mod_count) {
-               nfc_dev_err(&dev->interface->dev, "Cannot activate while"
-                                                               " polling");
+               nfc_dev_err(&dev->interface->dev,
+                           "Cannot activate while polling");
                return -EBUSY;
        }
 
        if (dev->tgt_active_prot) {
-               nfc_dev_err(&dev->interface->dev, "There is already an active"
-                                                               " target");
+               nfc_dev_err(&dev->interface->dev,
+                           "There is already an active target");
                return -EBUSY;
        }
 
        if (!dev->tgt_available_prots) {
-               nfc_dev_err(&dev->interface->dev, "There is no available target"
-                                                               " to activate");
+               nfc_dev_err(&dev->interface->dev,
+                           "There is no available target to activate");
                return -EINVAL;
        }
 
        if (!(dev->tgt_available_prots & (1 << protocol))) {
-               nfc_dev_err(&dev->interface->dev, "The target does not support"
-                                       " the requested protocol %u", protocol);
+               nfc_dev_err(&dev->interface->dev,
+                           "Target doesn't support requested proto %u",
+                           protocol);
                return -EINVAL;
        }
 
        if (protocol == NFC_PROTO_NFC_DEP) {
                rc = pn533_activate_target_nfcdep(dev);
                if (rc) {
-                       nfc_dev_err(&dev->interface->dev, "Error %d when"
-                                               " activating target with"
-                                               " NFC_DEP protocol", rc);
+                       nfc_dev_err(&dev->interface->dev,
+                                   "Activating target with DEP failed %d", rc);
                        return rc;
                }
        }
@@ -1634,8 +1790,10 @@ static void pn533_deactivate_target(struct nfc_dev *nfc_dev,
                                    struct nfc_target *target)
 {
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
-       u8 tg;
-       u8 status;
+
+       struct sk_buff *skb;
+       struct sk_buff *resp;
+
        int rc;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
@@ -1646,81 +1804,69 @@ static void pn533_deactivate_target(struct nfc_dev *nfc_dev,
        }
 
        dev->tgt_active_prot = 0;
-
        skb_queue_purge(&dev->resp_q);
 
-       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);
+       skb = pn533_alloc_skb(dev, sizeof(u8));
+       if (!skb)
+               return;
 
-       pn533_tx_frame_finish(dev->out_frame);
+       *skb_put(skb, 1) = 1; /* TG*/
 
-       rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame,
-                                                               dev->in_maxlen);
-       if (rc) {
-               nfc_dev_err(&dev->interface->dev, "Error when sending release"
-                                               " command to the controller");
+       resp = pn533_send_cmd_sync(dev, PN533_CMD_IN_RELEASE, skb);
+       if (IS_ERR(resp))
                return;
-       }
 
-       status = PN533_FRAME_CMD_PARAMS_PTR(dev->in_frame)[0];
-       rc = status & PN533_CMD_RET_MASK;
+       rc = resp->data[0] & PN533_CMD_RET_MASK;
        if (rc != PN533_CMD_RET_SUCCESS)
-               nfc_dev_err(&dev->interface->dev, "Error 0x%x when releasing"
-                                                       " the target", rc);
+               nfc_dev_err(&dev->interface->dev,
+                           "Error 0x%x when releasing the target", rc);
 
+       dev_kfree_skb(resp);
        return;
 }
 
 
 static int pn533_in_dep_link_up_complete(struct pn533 *dev, void *arg,
-                                               u8 *params, int params_len)
+                                        struct sk_buff *resp)
 {
-       struct pn533_cmd_jump_dep *cmd;
-       struct pn533_cmd_jump_dep_response *resp;
-       struct nfc_target nfc_target;
+       struct pn533_cmd_jump_dep_response *rsp;
        u8 target_gt_len;
        int rc;
+       u8 active = *(u8 *)arg;
 
-       if (params_len == -ENOENT) {
-               nfc_dev_dbg(&dev->interface->dev, "");
-               return 0;
-       }
+       kfree(arg);
 
-       if (params_len < 0) {
-               nfc_dev_err(&dev->interface->dev,
-                               "Error %d when bringing DEP link up",
-                                                               params_len);
-               return 0;
-       }
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
 
        if (dev->tgt_available_prots &&
            !(dev->tgt_available_prots & (1 << NFC_PROTO_NFC_DEP))) {
                nfc_dev_err(&dev->interface->dev,
-                       "The target does not support DEP");
-               return -EINVAL;
+                           "The target does not support DEP");
+               rc =  -EINVAL;
+               goto error;
        }
 
-       resp = (struct pn533_cmd_jump_dep_response *) params;
-       cmd = (struct pn533_cmd_jump_dep *) arg;
-       rc = resp->status & PN533_CMD_RET_MASK;
+       rsp = (struct pn533_cmd_jump_dep_response *)resp->data;
+
+       rc = rsp->status & PN533_CMD_RET_MASK;
        if (rc != PN533_CMD_RET_SUCCESS) {
                nfc_dev_err(&dev->interface->dev,
-                               "Bringing DEP link up failed %d", rc);
-               return 0;
+                           "Bringing DEP link up failed %d", rc);
+               goto error;
        }
 
        if (!dev->tgt_available_prots) {
+               struct nfc_target nfc_target;
+
                nfc_dev_dbg(&dev->interface->dev, "Creating new target");
 
                nfc_target.supported_protocols = NFC_PROTO_NFC_DEP_MASK;
                nfc_target.nfcid1_len = 10;
-               memcpy(nfc_target.nfcid1, resp->nfcid3t, nfc_target.nfcid1_len);
+               memcpy(nfc_target.nfcid1, rsp->nfcid3t, nfc_target.nfcid1_len);
                rc = nfc_targets_found(dev->nfc_dev, &nfc_target, 1);
                if (rc)
-                       return 0;
+                       goto error;
 
                dev->tgt_available_prots = 0;
        }
@@ -1728,15 +1874,17 @@ static int pn533_in_dep_link_up_complete(struct pn533 *dev, void *arg,
        dev->tgt_active_prot = NFC_PROTO_NFC_DEP;
 
        /* ATR_RES general bytes are located at offset 17 */
-       target_gt_len = PN533_FRAME_CMD_PARAMS_LEN(dev->in_frame) - 17;
+       target_gt_len = resp->len - 17;
        rc = nfc_set_remote_general_bytes(dev->nfc_dev,
-                                               resp->gt, target_gt_len);
+                                         rsp->gt, target_gt_len);
        if (rc == 0)
                rc = nfc_dep_link_is_up(dev->nfc_dev,
-                                               dev->nfc_dev->targets[0].idx,
-                                               !cmd->active, NFC_RF_INITIATOR);
+                                       dev->nfc_dev->targets[0].idx,
+                                       !active, NFC_RF_INITIATOR);
 
-       return 0;
+error:
+       dev_kfree_skb(resp);
+       return rc;
 }
 
 static int pn533_mod_to_baud(struct pn533 *dev)
@@ -1755,25 +1903,26 @@ static int pn533_mod_to_baud(struct pn533 *dev)
 
 #define PASSIVE_DATA_LEN 5
 static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
-                            u8 comm_mode, u8gb, size_t gb_len)
+                            u8 comm_mode, u8 *gb, size_t gb_len)
 {
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
-       struct pn533_cmd_jump_dep *cmd;
-       u8 cmd_len, *data_ptr;
+       struct sk_buff *skb;
+       int rc, baud, skb_len;
+       u8 *next, *arg;
+
        u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3};
-       int rc, baud;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
        if (dev->poll_mod_count) {
                nfc_dev_err(&dev->interface->dev,
-                               "Cannot bring the DEP link up while polling");
+                           "Cannot bring the DEP link up while polling");
                return -EBUSY;
        }
 
        if (dev->tgt_active_prot) {
                nfc_dev_err(&dev->interface->dev,
-                               "There is already an active target");
+                           "There is already an active target");
                return -EBUSY;
        }
 
@@ -1784,47 +1933,48 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
                return baud;
        }
 
-       cmd_len = sizeof(struct pn533_cmd_jump_dep) + gb_len;
+       skb_len = 3 + gb_len; /* ActPass + BR + Next */
        if (comm_mode == NFC_COMM_PASSIVE)
-               cmd_len += PASSIVE_DATA_LEN;
+               skb_len += PASSIVE_DATA_LEN;
 
-       cmd = kzalloc(cmd_len, GFP_KERNEL);
-       if (cmd == NULL)
+       skb = pn533_alloc_skb(dev, skb_len);
+       if (!skb)
                return -ENOMEM;
 
-       pn533_tx_frame_init(dev->out_frame, PN533_CMD_IN_JUMP_FOR_DEP);
+       *skb_put(skb, 1) = !comm_mode;  /* ActPass */
+       *skb_put(skb, 1) = baud;  /* Baud rate */
+
+       next = skb_put(skb, 1);  /* Next */
+       *next = 0;
 
-       cmd->active = !comm_mode;
-       cmd->next = 0;
-       cmd->baud = baud;
-       data_ptr = cmd->data;
-       if (comm_mode == NFC_COMM_PASSIVE && cmd->baud > 0) {
-               memcpy(data_ptr, passive_data, PASSIVE_DATA_LEN);
-               cmd->next |= 1;
-               data_ptr += PASSIVE_DATA_LEN;
+       if (comm_mode == NFC_COMM_PASSIVE && baud > 0) {
+               memcpy(skb_put(skb, PASSIVE_DATA_LEN), passive_data,
+                      PASSIVE_DATA_LEN);
+               *next |= 1;
        }
 
        if (gb != NULL && gb_len > 0) {
-               cmd->next |= 4; /* We have some Gi */
-               memcpy(data_ptr, gb, gb_len);
+               memcpy(skb_put(skb, gb_len), gb, gb_len);
+               *next |= 4; /* We have some Gi */
        } else {
-               cmd->next = 0;
+               *next = 0;
        }
 
-       memcpy(PN533_FRAME_CMD_PARAMS_PTR(dev->out_frame), cmd, cmd_len);
-       dev->out_frame->datalen += cmd_len;
+       arg = kmalloc(sizeof(*arg), GFP_KERNEL);
+       if (!arg) {
+               dev_kfree_skb(skb);
+               return -ENOMEM;
+       }
 
-       pn533_tx_frame_finish(dev->out_frame);
-
-       rc = pn533_send_cmd_frame_async(dev, dev->out_frame, dev->in_frame,
-                               dev->in_maxlen, pn533_in_dep_link_up_complete,
-                               cmd, GFP_KERNEL);
-       if (rc)
-               goto out;
+       *arg = !comm_mode;
 
+       rc = pn533_send_cmd_async(dev, PN533_CMD_IN_JUMP_FOR_DEP, skb,
+                                 pn533_in_dep_link_up_complete, arg);
 
-out:
-       kfree(cmd);
+       if (rc < 0) {
+               dev_kfree_skb(skb);
+               kfree(arg);
+       }
 
        return rc;
 }
@@ -1833,6 +1983,8 @@ static int pn533_dep_link_down(struct nfc_dev *nfc_dev)
 {
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
 
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
        pn533_poll_reset_mod_list(dev);
 
        if (dev->tgt_mode || dev->tgt_active_prot) {
@@ -1848,68 +2000,7 @@ static int pn533_dep_link_down(struct nfc_dev *nfc_dev)
        return 0;
 }
 
-static int pn533_build_tx_frame(struct pn533 *dev, struct sk_buff *skb,
-                               bool target)
-{
-       int payload_len = skb->len;
-       struct pn533_frame *out_frame;
-       u8 tg;
-
-       nfc_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 */
-               nfc_dev_err(&dev->interface->dev, "Data length greater than the"
-                                               " max allowed: %d",
-                                               PN533_CMD_DATAEXCH_DATA_MAXLEN);
-               return -ENOSYS;
-       }
-
-       if (target == true) {
-               switch (dev->device_type) {
-               case PN533_DEVICE_PASORI:
-                       if (dev->tgt_active_prot == NFC_PROTO_FELICA) {
-                               skb_push(skb, PN533_CMD_DATAEXCH_HEAD_LEN - 1);
-                               out_frame = (struct pn533_frame *) skb->data;
-                               pn533_tx_frame_init(out_frame,
-                                                   PN533_CMD_IN_COMM_THRU);
-
-                               break;
-                       }
-
-               default:
-                       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);
-
-                       break;
-               }
-
-       } else {
-               skb_push(skb, PN533_CMD_DATAEXCH_HEAD_LEN - 1);
-               out_frame = (struct pn533_frame *) skb->data;
-               pn533_tx_frame_init(out_frame, PN533_CMD_TG_SET_DATA);
-       }
-
-
-       /* 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;
 };
@@ -1919,7 +2010,7 @@ static struct sk_buff *pn533_build_response(struct pn533 *dev)
        struct sk_buff *skb, *tmp, *t;
        unsigned int skb_len = 0, tmp_len = 0;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s\n", __func__);
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
        if (skb_queue_empty(&dev->resp_q))
                return NULL;
@@ -1953,46 +2044,44 @@ out:
 }
 
 static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
-                                               u8 *params, int params_len)
+                                       struct sk_buff *resp)
 {
        struct pn533_data_exchange_arg *arg = _arg;
-       struct sk_buff *skb = NULL, *skb_resp = arg->skb_resp;
-       struct pn533_frame *in_frame = (struct pn533_frame *) skb_resp->data;
-       int err = 0;
-       u8 status;
-       u8 cmd_ret;
+       struct sk_buff *skb;
+       int rc = 0;
+       u8 status, ret, mi;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       dev_kfree_skb(arg->skb_out);
-
-       if (params_len < 0) { /* error */
-               err = params_len;
-               goto error;
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               goto _error;
        }
 
-       status = params[0];
+       status = resp->data[0];
+       ret = status & PN533_CMD_RET_MASK;
+       mi = status & PN533_CMD_MI_MASK;
+
+       skb_pull(resp, sizeof(status));
 
-       cmd_ret = status & PN533_CMD_RET_MASK;
-       if (cmd_ret != PN533_CMD_RET_SUCCESS) {
-               nfc_dev_err(&dev->interface->dev, "PN533 reported error %d when"
-                                               " exchanging data", cmd_ret);
-               err = -EIO;
+       if (ret != PN533_CMD_RET_SUCCESS) {
+               nfc_dev_err(&dev->interface->dev,
+                           "PN533 reported error %d when exchanging data",
+                           ret);
+               rc = -EIO;
                goto error;
        }
 
-       skb_put(skb_resp, PN533_FRAME_SIZE(in_frame));
-       skb_pull(skb_resp, PN533_CMD_DATAEXCH_HEAD_LEN);
-       skb_trim(skb_resp, skb_resp->len - PN533_FRAME_TAIL_SIZE);
-       skb_queue_tail(&dev->resp_q, skb_resp);
+       skb_queue_tail(&dev->resp_q, resp);
 
-       if (status & PN533_CMD_MI_MASK) {
+       if (mi) {
+               dev->cmd_complete_mi_arg = arg;
                queue_work(dev->wq, &dev->mi_work);
                return -EINPROGRESS;
        }
 
        skb = pn533_build_response(dev);
-       if (skb == NULL)
+       if (!skb)
                goto error;
 
        arg->cb(arg->cb_context, skb, 0);
@@ -2000,11 +2089,12 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
        return 0;
 
 error:
+       dev_kfree_skb(resp);
+_error:
        skb_queue_purge(&dev->resp_q);
-       dev_kfree_skb(skb_resp);
-       arg->cb(arg->cb_context, NULL, err);
+       arg->cb(arg->cb_context, NULL, rc);
        kfree(arg);
-       return 0;
+       return rc;
 }
 
 static int pn533_transceive(struct nfc_dev *nfc_dev,
@@ -2012,83 +2102,82 @@ static int pn533_transceive(struct nfc_dev *nfc_dev,
                            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;
+       struct pn533_data_exchange_arg *arg = NULL;
        int rc;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       if (!dev->tgt_active_prot) {
-               nfc_dev_err(&dev->interface->dev, "Cannot exchange data if"
-                                               " there is no active target");
-               rc = -EINVAL;
+       if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
+               /* TODO: Implement support to multi-part data exchange */
+               nfc_dev_err(&dev->interface->dev,
+                           "Data length greater than the max allowed: %d",
+                           PN533_CMD_DATAEXCH_DATA_MAXLEN);
+               rc = -ENOSYS;
                goto error;
        }
 
-       rc = pn533_build_tx_frame(dev, skb, true);
-       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_recv_skb(skb_resp_len, GFP_KERNEL);
-       if (!skb_resp) {
-               rc = -ENOMEM;
+       if (!dev->tgt_active_prot) {
+               nfc_dev_err(&dev->interface->dev,
+                           "Can't exchange data if there is no active target");
+               rc = -EINVAL;
                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);
+       arg = kmalloc(sizeof(*arg), GFP_KERNEL);
        if (!arg) {
                rc = -ENOMEM;
-               goto free_skb_resp;
+               goto error;
        }
 
-       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) {
-               nfc_dev_err(&dev->interface->dev, "Error %d when trying to"
-                                               " perform data_exchange", rc);
-               goto free_arg;
+       switch (dev->device_type) {
+       case PN533_DEVICE_PASORI:
+               if (dev->tgt_active_prot == NFC_PROTO_FELICA) {
+                       rc = pn533_send_data_async(dev, PN533_CMD_IN_COMM_THRU,
+                                                  skb,
+                                                  pn533_data_exchange_complete,
+                                                  arg);
+
+                       break;
+               }
+       default:
+               *skb_push(skb, sizeof(u8)) =  1; /*TG*/
+
+               rc = pn533_send_data_async(dev, PN533_CMD_IN_DATA_EXCHANGE,
+                                          skb, pn533_data_exchange_complete,
+                                          arg);
+
+               break;
        }
 
+       if (rc < 0) /* rc from send_async */
+               goto error;
+
        return 0;
 
-free_arg:
-       kfree(arg);
-free_skb_resp:
-       kfree_skb(skb_resp);
 error:
-       kfree_skb(skb);
+       kfree(arg);
+       dev_kfree_skb(skb);
        return rc;
 }
 
 static int pn533_tm_send_complete(struct pn533 *dev, void *arg,
-                                 u8 *params, int params_len)
+                                 struct sk_buff *resp)
 {
+       u8 status;
+
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       if (params_len < 0) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Error %d when sending data",
-                           params_len);
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
 
-               return params_len;
-       }
+       status = resp->data[0];
+
+       dev_kfree_skb(resp);
 
-       if (params_len > 0 && params[0] != 0) {
+       if (status != 0) {
                nfc_tm_deactivated(dev->nfc_dev);
 
                dev->tgt_mode = 0;
@@ -2104,30 +2193,21 @@ static int pn533_tm_send_complete(struct pn533 *dev, void *arg,
 static int pn533_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
 {
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
-       struct pn533_frame *out_frame;
        int rc;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       rc = pn533_build_tx_frame(dev, skb, false);
-       if (rc)
-               goto error;
-
-       out_frame = (struct pn533_frame *) skb->data;
-
-       rc = pn533_send_cmd_frame_async(dev, out_frame, dev->in_frame,
-                                       dev->in_maxlen, pn533_tm_send_complete,
-                                       NULL, GFP_KERNEL);
-       if (rc) {
+       if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
                nfc_dev_err(&dev->interface->dev,
-                           "Error %d when trying to send data", rc);
-               goto error;
+                           "Data length greater than the max allowed: %d",
+                           PN533_CMD_DATAEXCH_DATA_MAXLEN);
+               return -ENOSYS;
        }
 
-       return 0;
-
-error:
-       kfree_skb(skb);
+       rc = pn533_send_data_async(dev, PN533_CMD_TG_SET_DATA, skb,
+                                  pn533_tm_send_complete, NULL);
+       if (rc < 0)
+               dev_kfree_skb(skb);
 
        return rc;
 }
@@ -2135,107 +2215,123 @@ error:
 static void pn533_wq_mi_recv(struct work_struct *work)
 {
        struct pn533 *dev = container_of(work, struct pn533, mi_work);
-       struct sk_buff *skb_cmd;
-       struct pn533_data_exchange_arg *arg = dev->cmd_complete_arg;
-       struct pn533_frame *out_frame, *in_frame;
-       struct sk_buff *skb_resp;
-       int skb_resp_len;
+
+       struct sk_buff *skb;
        int rc;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       /* This is a zero payload size skb */
-       skb_cmd = alloc_skb(PN533_CMD_DATAEXCH_HEAD_LEN + PN533_FRAME_TAIL_SIZE,
-                           GFP_KERNEL);
-       if (skb_cmd == NULL)
-               goto error_cmd;
-
-       skb_reserve(skb_cmd, PN533_CMD_DATAEXCH_HEAD_LEN);
+       skb = pn533_alloc_skb(dev, PN533_CMD_DATAEXCH_HEAD_LEN);
+       if (!skb)
+               goto error;
 
-       rc = pn533_build_tx_frame(dev, skb_cmd, true);
-       if (rc)
-               goto error_frame;
+       switch (dev->device_type) {
+       case PN533_DEVICE_PASORI:
+               if (dev->tgt_active_prot == NFC_PROTO_FELICA) {
+                       rc = pn533_send_cmd_direct_async(dev,
+                                               PN533_CMD_IN_COMM_THRU,
+                                               skb,
+                                               pn533_data_exchange_complete,
+                                                dev->cmd_complete_mi_arg);
 
-       skb_resp_len = PN533_CMD_DATAEXCH_HEAD_LEN +
-                       PN533_CMD_DATAEXCH_DATA_MAXLEN +
-                       PN533_FRAME_TAIL_SIZE;
-       skb_resp = alloc_skb(skb_resp_len, GFP_KERNEL);
-       if (!skb_resp) {
-               rc = -ENOMEM;
-               goto error_frame;
-       }
+                       break;
+               }
+       default:
+               *skb_put(skb, sizeof(u8)) =  1; /*TG*/
 
-       in_frame = (struct pn533_frame *) skb_resp->data;
-       out_frame = (struct pn533_frame *) skb_cmd->data;
+               rc = pn533_send_cmd_direct_async(dev,
+                                                PN533_CMD_IN_DATA_EXCHANGE,
+                                                skb,
+                                                pn533_data_exchange_complete,
+                                                dev->cmd_complete_mi_arg);
 
-       arg->skb_resp = skb_resp;
-       arg->skb_out = skb_cmd;
+               break;
+       }
 
-       rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame,
-                                         skb_resp_len,
-                                         pn533_data_exchange_complete,
-                                         dev->cmd_complete_arg, GFP_KERNEL);
-       if (!rc)
+       if (rc == 0) /* success */
                return;
 
-       nfc_dev_err(&dev->interface->dev, "Error %d when trying to"
-                                               " perform data_exchange", rc);
+       nfc_dev_err(&dev->interface->dev,
+                   "Error %d when trying to perform data_exchange", rc);
 
-       kfree_skb(skb_resp);
+       dev_kfree_skb(skb);
+       kfree(dev->cmd_complete_arg);
 
-error_frame:
-       kfree_skb(skb_cmd);
-
-error_cmd:
+error:
        pn533_send_ack(dev, GFP_KERNEL);
-
-       kfree(arg);
-
        queue_work(dev->wq, &dev->cmd_work);
 }
 
 static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata,
                                                                u8 cfgdata_len)
 {
-       int rc;
-       u8 *params;
+       struct sk_buff *skb;
+       struct sk_buff *resp;
+
+       int skb_len;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       pn533_tx_frame_init(dev->out_frame, PN533_CMD_RF_CONFIGURATION);
+       skb_len = sizeof(cfgitem) + cfgdata_len; /* cfgitem + cfgdata */
 
-       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);
+       skb = pn533_alloc_skb(dev, skb_len);
+       if (!skb)
+               return -ENOMEM;
 
-       pn533_tx_frame_finish(dev->out_frame);
+       *skb_put(skb, sizeof(cfgitem)) = cfgitem;
+       memcpy(skb_put(skb, cfgdata_len), cfgdata, cfgdata_len);
 
-       rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame,
-                                                               dev->in_maxlen);
+       resp = pn533_send_cmd_sync(dev, PN533_CMD_RF_CONFIGURATION, skb);
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
 
-       return rc;
+       dev_kfree_skb(resp);
+       return 0;
+}
+
+static int pn533_get_firmware_version(struct pn533 *dev,
+                                     struct pn533_fw_version *fv)
+{
+       struct sk_buff *skb;
+       struct sk_buff *resp;
+
+       skb = pn533_alloc_skb(dev, 0);
+       if (!skb)
+               return -ENOMEM;
+
+       resp = pn533_send_cmd_sync(dev, PN533_CMD_GET_FIRMWARE_VERSION, skb);
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
+
+       fv->ic = resp->data[0];
+       fv->ver = resp->data[1];
+       fv->rev = resp->data[2];
+       fv->support = resp->data[3];
+
+       dev_kfree_skb(resp);
+       return 0;
 }
 
 static int pn533_fw_reset(struct pn533 *dev)
 {
-       int rc;
-       u8 *params;
+       struct sk_buff *skb;
+       struct sk_buff *resp;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       pn533_tx_frame_init(dev->out_frame, 0x18);
+       skb = pn533_alloc_skb(dev, sizeof(u8));
+       if (!skb)
+               return -ENOMEM;
 
-       params = PN533_FRAME_CMD_PARAMS_PTR(dev->out_frame);
-       params[0] = 0x1;
-       dev->out_frame->datalen += 1;
+       *skb_put(skb, sizeof(u8)) = 0x1;
 
-       pn533_tx_frame_finish(dev->out_frame);
+       resp = pn533_send_cmd_sync(dev, 0x18, skb);
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
 
-       rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame,
-                                      dev->in_maxlen);
+       dev_kfree_skb(resp);
 
-       return rc;
+       return 0;
 }
 
 static struct nfc_ops pn533_nfc_ops = {
@@ -2332,7 +2428,7 @@ static int pn533_setup(struct pn533 *dev)
 static int pn533_probe(struct usb_interface *interface,
                        const struct usb_device_id *id)
 {
-       struct pn533_fw_version *fw_ver;
+       struct pn533_fw_version fw_ver;
        struct pn533 *dev;
        struct usb_host_interface *iface_desc;
        struct usb_endpoint_descriptor *endpoint;
@@ -2354,41 +2450,32 @@ static int pn533_probe(struct usb_interface *interface,
        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);
+               if (!in_endpoint && usb_endpoint_is_bulk_in(endpoint))
                        in_endpoint = endpoint->bEndpointAddress;
-               }
 
-               if (!out_endpoint && usb_endpoint_is_bulk_out(endpoint)) {
-                       dev->out_maxlen =
-                               le16_to_cpu(endpoint->wMaxPacketSize);
+               if (!out_endpoint && usb_endpoint_is_bulk_out(endpoint))
                        out_endpoint = endpoint->bEndpointAddress;
-               }
        }
 
        if (!in_endpoint || !out_endpoint) {
-               nfc_dev_err(&interface->dev, "Could not find bulk-in or"
-                                                       " bulk-out endpoint");
+               nfc_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)
+       if (!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_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);
+                         usb_sndbulkpipe(dev->udev, out_endpoint),
+                         NULL, 0, pn533_send_complete, dev);
 
        INIT_WORK(&dev->cmd_work, pn533_wq_cmd);
        INIT_WORK(&dev->cmd_complete_work, pn533_wq_cmd_complete);
@@ -2409,18 +2496,7 @@ static int pn533_probe(struct usb_interface *interface,
 
        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 destroy_wq;
-
-       fw_ver = (struct pn533_fw_version *)
-                               PN533_FRAME_CMD_PARAMS_PTR(dev->in_frame);
-       nfc_dev_info(&dev->interface->dev, "NXP PN533 firmware ver %d.%d now"
-                                       " attached", fw_ver->ver, fw_ver->rev);
+       dev->ops = &pn533_std_frame_ops;
 
        dev->device_type = id->driver_info;
        switch (dev->device_type) {
@@ -2439,9 +2515,21 @@ static int pn533_probe(struct usb_interface *interface,
                goto destroy_wq;
        }
 
+       memset(&fw_ver, 0, sizeof(fw_ver));
+       rc = pn533_get_firmware_version(dev, &fw_ver);
+       if (rc < 0)
+               goto destroy_wq;
+
+       nfc_dev_info(&dev->interface->dev,
+                    "NXP PN533 firmware ver %d.%d now attached",
+                    fw_ver.ver, fw_ver.rev);
+
+
        dev->nfc_dev = nfc_allocate_device(&pn533_nfc_ops, protocols,
+                                          NFC_SE_NONE,
+                                          dev->ops->tx_header_len +
                                           PN533_CMD_DATAEXCH_HEAD_LEN,
-                                          PN533_FRAME_TAIL_SIZE);
+                                          dev->ops->tx_tail_len);
        if (!dev->nfc_dev)
                goto destroy_wq;
 
@@ -2467,9 +2555,7 @@ free_nfc_dev:
 destroy_wq:
        destroy_workqueue(dev->wq);
 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;
@@ -2500,9 +2586,7 @@ static void pn533_disconnect(struct usb_interface *interface)
                kfree(cmd);
        }
 
-       kfree(dev->in_frame);
        usb_free_urb(dev->in_urb);
-       kfree(dev->out_frame);
        usb_free_urb(dev->out_urb);
        kfree(dev);
 
diff --git a/drivers/nfc/pn544.c b/drivers/nfc/pn544.c
deleted file mode 100644 (file)
index 724f65d..0000000
+++ /dev/null
@@ -1,893 +0,0 @@
-/*
- * Driver for the PN544 NFC chip.
- *
- * Copyright (C) Nokia Corporation
- *
- * Author: Jari Vanhala <ext-jari.vanhala@nokia.com>
- * Contact: Matti Aaltonen <matti.j.aaltonen@nokia.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.        See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/completion.h>
-#include <linux/crc-ccitt.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/miscdevice.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/nfc/pn544.h>
-#include <linux/poll.h>
-#include <linux/regulator/consumer.h>
-#include <linux/serial_core.h> /* for TCGETS */
-#include <linux/slab.h>
-
-#define DRIVER_CARD    "PN544 NFC"
-#define DRIVER_DESC    "NFC driver for PN544"
-
-static struct i2c_device_id pn544_id_table[] = {
-       { PN544_DRIVER_NAME, 0 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, pn544_id_table);
-
-#define HCI_MODE       0
-#define FW_MODE                1
-
-enum pn544_state {
-       PN544_ST_COLD,
-       PN544_ST_FW_READY,
-       PN544_ST_READY,
-};
-
-enum pn544_irq {
-       PN544_NONE,
-       PN544_INT,
-};
-
-struct pn544_info {
-       struct miscdevice miscdev;
-       struct i2c_client *i2c_dev;
-       struct regulator_bulk_data regs[3];
-
-       enum pn544_state state;
-       wait_queue_head_t read_wait;
-       loff_t read_offset;
-       enum pn544_irq read_irq;
-       struct mutex read_mutex; /* Serialize read_irq access */
-       struct mutex mutex; /* Serialize info struct access */
-       u8 *buf;
-       size_t buflen;
-};
-
-static const char reg_vdd_io[] = "Vdd_IO";
-static const char reg_vbat[]   = "VBat";
-static const char reg_vsim[]   = "VSim";
-
-/* sysfs interface */
-static ssize_t pn544_test(struct device *dev,
-                         struct device_attribute *attr, char *buf)
-{
-       struct pn544_info *info = dev_get_drvdata(dev);
-       struct i2c_client *client = info->i2c_dev;
-       struct pn544_nfc_platform_data *pdata = client->dev.platform_data;
-
-       return snprintf(buf, PAGE_SIZE, "%d\n", pdata->test());
-}
-
-static int pn544_enable(struct pn544_info *info, int mode)
-{
-       struct pn544_nfc_platform_data *pdata;
-       struct i2c_client *client = info->i2c_dev;
-
-       int r;
-
-       r = regulator_bulk_enable(ARRAY_SIZE(info->regs), info->regs);
-       if (r < 0)
-               return r;
-
-       pdata = client->dev.platform_data;
-       info->read_irq = PN544_NONE;
-       if (pdata->enable)
-               pdata->enable(mode);
-
-       if (mode) {
-               info->state = PN544_ST_FW_READY;
-               dev_dbg(&client->dev, "now in FW-mode\n");
-       } else {
-               info->state = PN544_ST_READY;
-               dev_dbg(&client->dev, "now in HCI-mode\n");
-       }
-
-       usleep_range(10000, 15000);
-
-       return 0;
-}
-
-static void pn544_disable(struct pn544_info *info)
-{
-       struct pn544_nfc_platform_data *pdata;
-       struct i2c_client *client = info->i2c_dev;
-
-       pdata = client->dev.platform_data;
-       if (pdata->disable)
-               pdata->disable();
-
-       info->state = PN544_ST_COLD;
-
-       dev_dbg(&client->dev, "Now in OFF-mode\n");
-
-       msleep(PN544_RESETVEN_TIME);
-
-       info->read_irq = PN544_NONE;
-       regulator_bulk_disable(ARRAY_SIZE(info->regs), info->regs);
-}
-
-static int check_crc(u8 *buf, int buflen)
-{
-       u8 len;
-       u16 crc;
-
-       len = buf[0] + 1;
-       if (len < 4 || len != buflen || len > PN544_MSG_MAX_SIZE) {
-               pr_err(PN544_DRIVER_NAME
-                      ": CRC; corrupt packet len %u (%d)\n", len, buflen);
-               print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE,
-                              16, 2, buf, buflen, false);
-               return -EPERM;
-       }
-       crc = crc_ccitt(0xffff, buf, len - 2);
-       crc = ~crc;
-
-       if (buf[len-2] != (crc & 0xff) || buf[len-1] != (crc >> 8)) {
-               pr_err(PN544_DRIVER_NAME ": CRC error 0x%x != 0x%x 0x%x\n",
-                      crc, buf[len-1], buf[len-2]);
-
-               print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE,
-                              16, 2, buf, buflen, false);
-               return -EPERM;
-       }
-       return 0;
-}
-
-static int pn544_i2c_write(struct i2c_client *client, u8 *buf, int len)
-{
-       int r;
-
-       if (len < 4 || len != (buf[0] + 1)) {
-               dev_err(&client->dev, "%s: Illegal message length: %d\n",
-                       __func__, len);
-               return -EINVAL;
-       }
-
-       if (check_crc(buf, len))
-               return -EINVAL;
-
-       usleep_range(3000, 6000);
-
-       r = i2c_master_send(client, buf, len);
-       dev_dbg(&client->dev, "send: %d\n", r);
-
-       if (r == -EREMOTEIO) { /* Retry, chip was in standby */
-               usleep_range(6000, 10000);
-               r = i2c_master_send(client, buf, len);
-               dev_dbg(&client->dev, "send2: %d\n", r);
-       }
-
-       if (r != len)
-               return -EREMOTEIO;
-
-       return r;
-}
-
-static int pn544_i2c_read(struct i2c_client *client, u8 *buf, int buflen)
-{
-       int r;
-       u8 len;
-
-       /*
-        * You could read a packet in one go, but then you'd need to read
-        * max size and rest would be 0xff fill, so we do split reads.
-        */
-       r = i2c_master_recv(client, &len, 1);
-       dev_dbg(&client->dev, "recv1: %d\n", r);
-
-       if (r != 1)
-               return -EREMOTEIO;
-
-       if (len < PN544_LLC_HCI_OVERHEAD)
-               len = PN544_LLC_HCI_OVERHEAD;
-       else if (len > (PN544_MSG_MAX_SIZE - 1))
-               len = PN544_MSG_MAX_SIZE - 1;
-
-       if (1 + len > buflen) /* len+(data+crc16) */
-               return -EMSGSIZE;
-
-       buf[0] = len;
-
-       r = i2c_master_recv(client, buf + 1, len);
-       dev_dbg(&client->dev, "recv2: %d\n", r);
-
-       if (r != len)
-               return -EREMOTEIO;
-
-       usleep_range(3000, 6000);
-
-       return r + 1;
-}
-
-static int pn544_fw_write(struct i2c_client *client, u8 *buf, int len)
-{
-       int r;
-
-       dev_dbg(&client->dev, "%s\n", __func__);
-
-       if (len < PN544_FW_HEADER_SIZE ||
-           (PN544_FW_HEADER_SIZE + (buf[1] << 8) + buf[2]) != len)
-               return -EINVAL;
-
-       r = i2c_master_send(client, buf, len);
-       dev_dbg(&client->dev, "fw send: %d\n", r);
-
-       if (r == -EREMOTEIO) { /* Retry, chip was in standby */
-               usleep_range(6000, 10000);
-               r = i2c_master_send(client, buf, len);
-               dev_dbg(&client->dev, "fw send2: %d\n", r);
-       }
-
-       if (r != len)
-               return -EREMOTEIO;
-
-       return r;
-}
-
-static int pn544_fw_read(struct i2c_client *client, u8 *buf, int buflen)
-{
-       int r, len;
-
-       if (buflen < PN544_FW_HEADER_SIZE)
-               return -EINVAL;
-
-       r = i2c_master_recv(client, buf, PN544_FW_HEADER_SIZE);
-       dev_dbg(&client->dev, "FW recv1: %d\n", r);
-
-       if (r < 0)
-               return r;
-
-       if (r < PN544_FW_HEADER_SIZE)
-               return -EINVAL;
-
-       len = (buf[1] << 8) + buf[2];
-       if (len == 0) /* just header, no additional data */
-               return r;
-
-       if (len > buflen - PN544_FW_HEADER_SIZE)
-               return -EMSGSIZE;
-
-       r = i2c_master_recv(client, buf + PN544_FW_HEADER_SIZE, len);
-       dev_dbg(&client->dev, "fw recv2: %d\n", r);
-
-       if (r != len)
-               return -EINVAL;
-
-       return r + PN544_FW_HEADER_SIZE;
-}
-
-static irqreturn_t pn544_irq_thread_fn(int irq, void *dev_id)
-{
-       struct pn544_info *info = dev_id;
-       struct i2c_client *client = info->i2c_dev;
-
-       BUG_ON(!info);
-       BUG_ON(irq != info->i2c_dev->irq);
-
-       dev_dbg(&client->dev, "IRQ\n");
-
-       mutex_lock(&info->read_mutex);
-       info->read_irq = PN544_INT;
-       mutex_unlock(&info->read_mutex);
-
-       wake_up_interruptible(&info->read_wait);
-
-       return IRQ_HANDLED;
-}
-
-static enum pn544_irq pn544_irq_state(struct pn544_info *info)
-{
-       enum pn544_irq irq;
-
-       mutex_lock(&info->read_mutex);
-       irq = info->read_irq;
-       mutex_unlock(&info->read_mutex);
-       /*
-        * XXX: should we check GPIO-line status directly?
-        * return pdata->irq_status() ? PN544_INT : PN544_NONE;
-        */
-
-       return irq;
-}
-
-static ssize_t pn544_read(struct file *file, char __user *buf,
-                         size_t count, loff_t *offset)
-{
-       struct pn544_info *info = container_of(file->private_data,
-                                              struct pn544_info, miscdev);
-       struct i2c_client *client = info->i2c_dev;
-       enum pn544_irq irq;
-       size_t len;
-       int r = 0;
-
-       dev_dbg(&client->dev, "%s: info: %p, count: %zu\n", __func__,
-               info, count);
-
-       mutex_lock(&info->mutex);
-
-       if (info->state == PN544_ST_COLD) {
-               r = -ENODEV;
-               goto out;
-       }
-
-       irq = pn544_irq_state(info);
-       if (irq == PN544_NONE) {
-               if (file->f_flags & O_NONBLOCK) {
-                       r = -EAGAIN;
-                       goto out;
-               }
-
-               if (wait_event_interruptible(info->read_wait,
-                                            (info->read_irq == PN544_INT))) {
-                       r = -ERESTARTSYS;
-                       goto out;
-               }
-       }
-
-       if (info->state == PN544_ST_FW_READY) {
-               len = min(count, info->buflen);
-
-               mutex_lock(&info->read_mutex);
-               r = pn544_fw_read(info->i2c_dev, info->buf, len);
-               info->read_irq = PN544_NONE;
-               mutex_unlock(&info->read_mutex);
-
-               if (r < 0) {
-                       dev_err(&info->i2c_dev->dev, "FW read failed: %d\n", r);
-                       goto out;
-               }
-
-               print_hex_dump(KERN_DEBUG, "FW read: ", DUMP_PREFIX_NONE,
-                              16, 2, info->buf, r, false);
-
-               *offset += r;
-               if (copy_to_user(buf, info->buf, r)) {
-                       r = -EFAULT;
-                       goto out;
-               }
-       } else {
-               len = min(count, info->buflen);
-
-               mutex_lock(&info->read_mutex);
-               r = pn544_i2c_read(info->i2c_dev, info->buf, len);
-               info->read_irq = PN544_NONE;
-               mutex_unlock(&info->read_mutex);
-
-               if (r < 0) {
-                       dev_err(&info->i2c_dev->dev, "read failed (%d)\n", r);
-                       goto out;
-               }
-               print_hex_dump(KERN_DEBUG, "read: ", DUMP_PREFIX_NONE,
-                              16, 2, info->buf, r, false);
-
-               *offset += r;
-               if (copy_to_user(buf, info->buf, r)) {
-                       r = -EFAULT;
-                       goto out;
-               }
-       }
-
-out:
-       mutex_unlock(&info->mutex);
-
-       return r;
-}
-
-static unsigned int pn544_poll(struct file *file, poll_table *wait)
-{
-       struct pn544_info *info = container_of(file->private_data,
-                                              struct pn544_info, miscdev);
-       struct i2c_client *client = info->i2c_dev;
-       int r = 0;
-
-       dev_dbg(&client->dev, "%s: info: %p\n", __func__, info);
-
-       mutex_lock(&info->mutex);
-
-       if (info->state == PN544_ST_COLD) {
-               r = -ENODEV;
-               goto out;
-       }
-
-       poll_wait(file, &info->read_wait, wait);
-
-       if (pn544_irq_state(info) == PN544_INT) {
-               r = POLLIN | POLLRDNORM;
-               goto out;
-       }
-out:
-       mutex_unlock(&info->mutex);
-
-       return r;
-}
-
-static ssize_t pn544_write(struct file *file, const char __user *buf,
-                          size_t count, loff_t *ppos)
-{
-       struct pn544_info *info = container_of(file->private_data,
-                                              struct pn544_info, miscdev);
-       struct i2c_client *client = info->i2c_dev;
-       ssize_t len;
-       int r;
-
-       dev_dbg(&client->dev, "%s: info: %p, count %zu\n", __func__,
-               info, count);
-
-       mutex_lock(&info->mutex);
-
-       if (info->state == PN544_ST_COLD) {
-               r = -ENODEV;
-               goto out;
-       }
-
-       /*
-        * XXX: should we detect rset-writes and clean possible
-        * read_irq state
-        */
-       if (info->state == PN544_ST_FW_READY) {
-               size_t fw_len;
-
-               if (count < PN544_FW_HEADER_SIZE) {
-                       r = -EINVAL;
-                       goto out;
-               }
-
-               len = min(count, info->buflen);
-               if (copy_from_user(info->buf, buf, len)) {
-                       r = -EFAULT;
-                       goto out;
-               }
-
-               print_hex_dump(KERN_DEBUG, "FW write: ", DUMP_PREFIX_NONE,
-                              16, 2, info->buf, len, false);
-
-               fw_len = PN544_FW_HEADER_SIZE + (info->buf[1] << 8) +
-                       info->buf[2];
-
-               if (len > fw_len) /* 1 msg at a time */
-                       len = fw_len;
-
-               r = pn544_fw_write(info->i2c_dev, info->buf, len);
-       } else {
-               if (count < PN544_LLC_MIN_SIZE) {
-                       r = -EINVAL;
-                       goto out;
-               }
-
-               len = min(count, info->buflen);
-               if (copy_from_user(info->buf, buf, len)) {
-                       r = -EFAULT;
-                       goto out;
-               }
-
-               print_hex_dump(KERN_DEBUG, "write: ", DUMP_PREFIX_NONE,
-                              16, 2, info->buf, len, false);
-
-               if (len > (info->buf[0] + 1)) /* 1 msg at a time */
-                       len  = info->buf[0] + 1;
-
-               r = pn544_i2c_write(info->i2c_dev, info->buf, len);
-       }
-out:
-       mutex_unlock(&info->mutex);
-
-       return r;
-
-}
-
-static long pn544_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
-       struct pn544_info *info = container_of(file->private_data,
-                                              struct pn544_info, miscdev);
-       struct i2c_client *client = info->i2c_dev;
-       struct pn544_nfc_platform_data *pdata;
-       unsigned int val;
-       int r = 0;
-
-       dev_dbg(&client->dev, "%s: info: %p, cmd: 0x%x\n", __func__, info, cmd);
-
-       mutex_lock(&info->mutex);
-
-       if (info->state == PN544_ST_COLD) {
-               r = -ENODEV;
-               goto out;
-       }
-
-       pdata = info->i2c_dev->dev.platform_data;
-       switch (cmd) {
-       case PN544_GET_FW_MODE:
-               dev_dbg(&client->dev, "%s:  PN544_GET_FW_MODE\n", __func__);
-
-               val = (info->state == PN544_ST_FW_READY);
-               if (copy_to_user((void __user *)arg, &val, sizeof(val))) {
-                       r = -EFAULT;
-                       goto out;
-               }
-
-               break;
-
-       case PN544_SET_FW_MODE:
-               dev_dbg(&client->dev, "%s:  PN544_SET_FW_MODE\n", __func__);
-
-               if (copy_from_user(&val, (void __user *)arg, sizeof(val))) {
-                       r = -EFAULT;
-                       goto out;
-               }
-
-               if (val) {
-                       if (info->state == PN544_ST_FW_READY)
-                               break;
-
-                       pn544_disable(info);
-                       r = pn544_enable(info, FW_MODE);
-                       if (r < 0)
-                               goto out;
-               } else {
-                       if (info->state == PN544_ST_READY)
-                               break;
-                       pn544_disable(info);
-                       r = pn544_enable(info, HCI_MODE);
-                       if (r < 0)
-                               goto out;
-               }
-               file->f_pos = info->read_offset;
-               break;
-
-       case TCGETS:
-               dev_dbg(&client->dev, "%s:  TCGETS\n", __func__);
-
-               r = -ENOIOCTLCMD;
-               break;
-
-       default:
-               dev_err(&client->dev, "Unknown ioctl 0x%x\n", cmd);
-               r = -ENOIOCTLCMD;
-               break;
-       }
-
-out:
-       mutex_unlock(&info->mutex);
-
-       return r;
-}
-
-static int pn544_open(struct inode *inode, struct file *file)
-{
-       struct pn544_info *info = container_of(file->private_data,
-                                              struct pn544_info, miscdev);
-       struct i2c_client *client = info->i2c_dev;
-       int r = 0;
-
-       dev_dbg(&client->dev, "%s: info: %p, client %p\n", __func__,
-               info, info->i2c_dev);
-
-       mutex_lock(&info->mutex);
-
-       /*
-        * Only 1 at a time.
-        * XXX: maybe user (counter) would work better
-        */
-       if (info->state != PN544_ST_COLD) {
-               r = -EBUSY;
-               goto out;
-       }
-
-       file->f_pos = info->read_offset;
-       r = pn544_enable(info, HCI_MODE);
-
-out:
-       mutex_unlock(&info->mutex);
-       return r;
-}
-
-static int pn544_close(struct inode *inode, struct file *file)
-{
-       struct pn544_info *info = container_of(file->private_data,
-                                              struct pn544_info, miscdev);
-       struct i2c_client *client = info->i2c_dev;
-
-       dev_dbg(&client->dev, "%s: info: %p, client %p\n",
-               __func__, info, info->i2c_dev);
-
-       mutex_lock(&info->mutex);
-       pn544_disable(info);
-       mutex_unlock(&info->mutex);
-
-       return 0;
-}
-
-static const struct file_operations pn544_fops = {
-       .owner          = THIS_MODULE,
-       .llseek         = no_llseek,
-       .read           = pn544_read,
-       .write          = pn544_write,
-       .poll           = pn544_poll,
-       .open           = pn544_open,
-       .release        = pn544_close,
-       .unlocked_ioctl = pn544_ioctl,
-};
-
-#ifdef CONFIG_PM
-static int pn544_suspend(struct device *dev)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-       struct pn544_info *info;
-       int r = 0;
-
-       dev_info(&client->dev, "***\n%s: client %p\n***\n", __func__, client);
-
-       info = i2c_get_clientdata(client);
-       dev_info(&client->dev, "%s: info: %p, client %p\n", __func__,
-                info, client);
-
-       mutex_lock(&info->mutex);
-
-       switch (info->state) {
-       case PN544_ST_FW_READY:
-               /* Do not suspend while upgrading FW, please! */
-               r = -EPERM;
-               break;
-
-       case PN544_ST_READY:
-               /*
-                * CHECK: Device should be in standby-mode. No way to check?
-                * Allowing low power mode for the regulator is potentially
-                * dangerous if pn544 does not go to suspension.
-                */
-               break;
-
-       case PN544_ST_COLD:
-               break;
-       };
-
-       mutex_unlock(&info->mutex);
-       return r;
-}
-
-static int pn544_resume(struct device *dev)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-       struct pn544_info *info = i2c_get_clientdata(client);
-       int r = 0;
-
-       dev_dbg(&client->dev, "%s: info: %p, client %p\n", __func__,
-               info, client);
-
-       mutex_lock(&info->mutex);
-
-       switch (info->state) {
-       case PN544_ST_READY:
-               /*
-                * CHECK: If regulator low power mode is allowed in
-                * pn544_suspend, we should go back to normal mode
-                * here.
-                */
-               break;
-
-       case PN544_ST_COLD:
-               break;
-
-       case PN544_ST_FW_READY:
-               break;
-       };
-
-       mutex_unlock(&info->mutex);
-
-       return r;
-}
-
-static SIMPLE_DEV_PM_OPS(pn544_pm_ops, pn544_suspend, pn544_resume);
-#endif
-
-static struct device_attribute pn544_attr =
-       __ATTR(nfc_test, S_IRUGO, pn544_test, NULL);
-
-static int __devinit pn544_probe(struct i2c_client *client,
-                                const struct i2c_device_id *id)
-{
-       struct pn544_info *info;
-       struct pn544_nfc_platform_data *pdata;
-       int r = 0;
-
-       dev_dbg(&client->dev, "%s\n", __func__);
-       dev_dbg(&client->dev, "IRQ: %d\n", client->irq);
-
-       /* private data allocation */
-       info = kzalloc(sizeof(struct pn544_info), GFP_KERNEL);
-       if (!info) {
-               dev_err(&client->dev,
-                       "Cannot allocate memory for pn544_info.\n");
-               r = -ENOMEM;
-               goto err_info_alloc;
-       }
-
-       info->buflen = max(PN544_MSG_MAX_SIZE, PN544_MAX_I2C_TRANSFER);
-       info->buf = kzalloc(info->buflen, GFP_KERNEL);
-       if (!info->buf) {
-               dev_err(&client->dev,
-                       "Cannot allocate memory for pn544_info->buf.\n");
-               r = -ENOMEM;
-               goto err_buf_alloc;
-       }
-
-       info->regs[0].supply = reg_vdd_io;
-       info->regs[1].supply = reg_vbat;
-       info->regs[2].supply = reg_vsim;
-       r = regulator_bulk_get(&client->dev, ARRAY_SIZE(info->regs),
-                                info->regs);
-       if (r < 0)
-               goto err_kmalloc;
-
-       info->i2c_dev = client;
-       info->state = PN544_ST_COLD;
-       info->read_irq = PN544_NONE;
-       mutex_init(&info->read_mutex);
-       mutex_init(&info->mutex);
-       init_waitqueue_head(&info->read_wait);
-       i2c_set_clientdata(client, info);
-       pdata = client->dev.platform_data;
-       if (!pdata) {
-               dev_err(&client->dev, "No platform data\n");
-               r = -EINVAL;
-               goto err_reg;
-       }
-
-       if (!pdata->request_resources) {
-               dev_err(&client->dev, "request_resources() missing\n");
-               r = -EINVAL;
-               goto err_reg;
-       }
-
-       r = pdata->request_resources(client);
-       if (r) {
-               dev_err(&client->dev, "Cannot get platform resources\n");
-               goto err_reg;
-       }
-
-       r = request_threaded_irq(client->irq, NULL, pn544_irq_thread_fn,
-                                IRQF_TRIGGER_RISING, PN544_DRIVER_NAME,
-                                info);
-       if (r < 0) {
-               dev_err(&client->dev, "Unable to register IRQ handler\n");
-               goto err_res;
-       }
-
-       /* If we don't have the test we don't need the sysfs file */
-       if (pdata->test) {
-               r = device_create_file(&client->dev, &pn544_attr);
-               if (r) {
-                       dev_err(&client->dev,
-                               "sysfs registration failed, error %d\n", r);
-                       goto err_irq;
-               }
-       }
-
-       info->miscdev.minor = MISC_DYNAMIC_MINOR;
-       info->miscdev.name = PN544_DRIVER_NAME;
-       info->miscdev.fops = &pn544_fops;
-       info->miscdev.parent = &client->dev;
-       r = misc_register(&info->miscdev);
-       if (r < 0) {
-               dev_err(&client->dev, "Device registration failed\n");
-               goto err_sysfs;
-       }
-
-       dev_dbg(&client->dev, "%s: info: %p, pdata %p, client %p\n",
-               __func__, info, pdata, client);
-
-       return 0;
-
-err_sysfs:
-       if (pdata->test)
-               device_remove_file(&client->dev, &pn544_attr);
-err_irq:
-       free_irq(client->irq, info);
-err_res:
-       if (pdata->free_resources)
-               pdata->free_resources();
-err_reg:
-       regulator_bulk_free(ARRAY_SIZE(info->regs), info->regs);
-err_kmalloc:
-       kfree(info->buf);
-err_buf_alloc:
-       kfree(info);
-err_info_alloc:
-       return r;
-}
-
-static __devexit int pn544_remove(struct i2c_client *client)
-{
-       struct pn544_info *info = i2c_get_clientdata(client);
-       struct pn544_nfc_platform_data *pdata = client->dev.platform_data;
-
-       dev_dbg(&client->dev, "%s\n", __func__);
-
-       misc_deregister(&info->miscdev);
-       if (pdata->test)
-               device_remove_file(&client->dev, &pn544_attr);
-
-       if (info->state != PN544_ST_COLD) {
-               if (pdata->disable)
-                       pdata->disable();
-
-               info->read_irq = PN544_NONE;
-       }
-
-       free_irq(client->irq, info);
-       if (pdata->free_resources)
-               pdata->free_resources();
-
-       regulator_bulk_free(ARRAY_SIZE(info->regs), info->regs);
-       kfree(info->buf);
-       kfree(info);
-
-       return 0;
-}
-
-static struct i2c_driver pn544_driver = {
-       .driver = {
-               .name = PN544_DRIVER_NAME,
-#ifdef CONFIG_PM
-               .pm = &pn544_pm_ops,
-#endif
-       },
-       .probe = pn544_probe,
-       .id_table = pn544_id_table,
-       .remove = __devexit_p(pn544_remove),
-};
-
-static int __init pn544_init(void)
-{
-       int r;
-
-       pr_debug(DRIVER_DESC ": %s\n", __func__);
-
-       r = i2c_add_driver(&pn544_driver);
-       if (r) {
-               pr_err(PN544_DRIVER_NAME ": driver registration failed\n");
-               return r;
-       }
-
-       return 0;
-}
-
-static void __exit pn544_exit(void)
-{
-       i2c_del_driver(&pn544_driver);
-       pr_info(DRIVER_DESC ", Exiting.\n");
-}
-
-module_init(pn544_init);
-module_exit(pn544_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/nfc/pn544/Kconfig b/drivers/nfc/pn544/Kconfig
new file mode 100644 (file)
index 0000000..c277790
--- /dev/null
@@ -0,0 +1,23 @@
+config NFC_PN544
+       tristate "NXP PN544 NFC driver"
+       depends on NFC_HCI
+       select CRC_CCITT
+       default n
+       ---help---
+         NXP PN544 core driver.
+         This is a driver based on the HCI NFC kernel layers and
+         will thus not work with NXP libnfc library.
+
+         To compile this driver as a module, choose m here. The module will
+         be called pn544.
+         Say N if unsure.
+
+config NFC_PN544_I2C
+       tristate "NFC PN544 i2c support"
+       depends on NFC_PN544 && I2C && NFC_SHDLC
+       ---help---
+         This module adds support for the NXP pn544 i2c interface.
+         Select this if your platform is using the i2c bus.
+
+         If you choose to build a module, it'll be called pn544_i2c.
+         Say N if unsure.
\ No newline at end of file
index 7257338..ac07679 100644 (file)
@@ -2,6 +2,7 @@
 # Makefile for PN544 HCI based NFC driver
 #
 
-obj-$(CONFIG_PN544_HCI_NFC)    += pn544_i2c.o
+pn544_i2c-objs  = i2c.o
 
-pn544_i2c-y            := pn544.o i2c.o
+obj-$(CONFIG_NFC_PN544)     += pn544.o
+obj-$(CONFIG_NFC_PN544_I2C) += pn544_i2c.o
index fb430d8..7f96ca2 100644 (file)
@@ -26,7 +26,7 @@
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 
-#include <linux/nfc/pn544.h>
+#include <linux/platform_data/pn544.h>
 
 #include <net/nfc/hci.h>
 #include <net/nfc/llc.h>
@@ -376,12 +376,12 @@ static int __devinit pn544_hci_i2c_probe(struct i2c_client *client,
                return -ENODEV;
        }
 
-       phy = kzalloc(sizeof(struct pn544_i2c_phy), GFP_KERNEL);
+       phy = devm_kzalloc(&client->dev, sizeof(struct pn544_i2c_phy),
+                          GFP_KERNEL);
        if (!phy) {
                dev_err(&client->dev,
                        "Cannot allocate memory for pn544 i2c phy.\n");
-               r = -ENOMEM;
-               goto err_phy_alloc;
+               return -ENOMEM;
        }
 
        phy->i2c_dev = client;
@@ -390,20 +390,18 @@ static int __devinit pn544_hci_i2c_probe(struct i2c_client *client,
        pdata = client->dev.platform_data;
        if (pdata == NULL) {
                dev_err(&client->dev, "No platform data\n");
-               r = -EINVAL;
-               goto err_pdata;
+               return -EINVAL;
        }
 
        if (pdata->request_resources == NULL) {
                dev_err(&client->dev, "request_resources() missing\n");
-               r = -EINVAL;
-               goto err_pdata;
+               return -EINVAL;
        }
 
        r = pdata->request_resources(client);
        if (r) {
                dev_err(&client->dev, "Cannot get platform resources\n");
-               goto err_pdata;
+               return r;
        }
 
        phy->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE);
@@ -435,10 +433,6 @@ err_rti:
        if (pdata->free_resources != NULL)
                pdata->free_resources();
 
-err_pdata:
-       kfree(phy);
-
-err_phy_alloc:
        return r;
 }
 
@@ -458,8 +452,6 @@ static __devexit int pn544_hci_i2c_remove(struct i2c_client *client)
        if (pdata->free_resources)
                pdata->free_resources();
 
-       kfree(phy);
-
        return 0;
 }
 
@@ -472,29 +464,7 @@ static struct i2c_driver pn544_hci_i2c_driver = {
        .remove = __devexit_p(pn544_hci_i2c_remove),
 };
 
-static int __init pn544_hci_i2c_init(void)
-{
-       int r;
-
-       pr_debug(DRIVER_DESC ": %s\n", __func__);
-
-       r = i2c_add_driver(&pn544_hci_i2c_driver);
-       if (r) {
-               pr_err(PN544_HCI_I2C_DRIVER_NAME
-                      ": driver registration failed\n");
-               return r;
-       }
-
-       return 0;
-}
-
-static void __exit pn544_hci_i2c_exit(void)
-{
-       i2c_del_driver(&pn544_hci_i2c_driver);
-}
-
-module_init(pn544_hci_i2c_init);
-module_exit(pn544_hci_i2c_exit);
+module_i2c_driver(pn544_hci_i2c_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION(DRIVER_DESC);
index cc666de..9c5f16e 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <linux/delay.h>
 #include <linux/slab.h>
+#include <linux/module.h>
 
 #include <linux/nfc.h>
 #include <net/nfc/hci.h>
@@ -675,11 +676,17 @@ static int pn544_hci_im_transceive(struct nfc_hci_dev *hdev,
 
 static int pn544_hci_tm_send(struct nfc_hci_dev *hdev, struct sk_buff *skb)
 {
+       int r;
+
        /* Set default false for multiple information chaining */
        *skb_push(skb, 1) = 0;
 
-       return nfc_hci_send_event(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE,
-                               PN544_HCI_EVT_SND_DATA, skb->data, skb->len);
+       r = nfc_hci_send_event(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE,
+                              PN544_HCI_EVT_SND_DATA, skb->data, skb->len);
+
+       kfree_skb(skb);
+
+       return r;
 }
 
 static int pn544_hci_check_presence(struct nfc_hci_dev *hdev,
@@ -714,35 +721,40 @@ static int pn544_hci_check_presence(struct nfc_hci_dev *hdev,
        return 0;
 }
 
-static void pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate,
-                                       u8 event, struct sk_buff *skb)
+/*
+ * Returns:
+ * <= 0: driver handled the event, skb consumed
+ *    1: driver does not handle the event, please do standard processing
+ */
+static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, u8 event,
+                                   struct sk_buff *skb)
 {
        struct sk_buff *rgb_skb = NULL;
-       int r = 0;
+       int r;
 
        pr_debug("hci event %d", event);
        switch (event) {
        case PN544_HCI_EVT_ACTIVATED:
-               if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE)
-                       nfc_hci_target_discovered(hdev, gate);
-               else if (gate == PN544_RF_READER_NFCIP1_TARGET_GATE) {
+               if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE) {
+                       r = nfc_hci_target_discovered(hdev, gate);
+               else if (gate == PN544_RF_READER_NFCIP1_TARGET_GATE) {
                        r = nfc_hci_get_param(hdev, gate, PN544_DEP_ATR_REQ,
-                                               &rgb_skb);
-
+                                             &rgb_skb);
                        if (r < 0)
                                goto exit;
 
-                       nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK,
-                                       NFC_COMM_PASSIVE, rgb_skb->data,
-                                       rgb_skb->len);
+                       r = nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK,
+                                            NFC_COMM_PASSIVE, rgb_skb->data,
+                                            rgb_skb->len);
 
                        kfree_skb(rgb_skb);
+               } else {
+                       r = -EINVAL;
                }
-
                break;
        case PN544_HCI_EVT_DEACTIVATED:
-               nfc_hci_send_event(hdev, gate,
-                       NFC_HCI_EVT_END_OPERATION, NULL, 0);
+               r = nfc_hci_send_event(hdev, gate, NFC_HCI_EVT_END_OPERATION,
+                                      NULL, 0);
                break;
        case PN544_HCI_EVT_RCV_DATA:
                if (skb->len < 2) {
@@ -757,15 +769,15 @@ static void pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate,
                }
 
                skb_pull(skb, 2);
-               nfc_tm_data_received(hdev->ndev, skb);
-
-               return;
+               return nfc_tm_data_received(hdev->ndev, skb);
        default:
-               break;
+               return 1;
        }
 
 exit:
        kfree_skb(skb);
+
+       return r;
 }
 
 static struct nfc_hci_ops pn544_hci_ops = {
@@ -789,7 +801,7 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
                    struct nfc_hci_dev **hdev)
 {
        struct pn544_hci_info *info;
-       u32 protocols;
+       u32 protocols, se;
        struct nfc_hci_init_data init_data;
        int r;
 
@@ -822,8 +834,10 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
                    NFC_PROTO_ISO14443_B_MASK |
                    NFC_PROTO_NFC_DEP_MASK;
 
-       info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data,
-                                            protocols, llc_name,
+       se = NFC_SE_UICC | NFC_SE_EMBEDDED;
+
+       info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data, 0,
+                                            protocols, se, llc_name,
                                             phy_headroom + PN544_CMDS_HEADROOM,
                                             phy_tailroom, phy_payload);
        if (!info->hdev) {
@@ -851,6 +865,7 @@ err_alloc_hdev:
 err_info_alloc:
        return r;
 }
+EXPORT_SYMBOL(pn544_hci_probe);
 
 void pn544_hci_remove(struct nfc_hci_dev *hdev)
 {
@@ -860,3 +875,7 @@ void pn544_hci_remove(struct nfc_hci_dev *hdev)
        nfc_hci_free_device(hdev);
        kfree(info);
 }
+EXPORT_SYMBOL(pn544_hci_remove);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
index e4f62d8..95cd102 100644 (file)
@@ -904,4 +904,25 @@ extern long sysfs_deprecated;
 #define sysfs_deprecated 0
 #endif
 
+/**
+ * module_driver() - Helper macro for drivers that don't do anything
+ * special in module init/exit. This eliminates a lot of boilerplate.
+ * Each module may only use this macro once, and calling it replaces
+ * module_init() and module_exit().
+ *
+ * Use this macro to construct bus specific macros for registering
+ * drivers, and do not use it on its own.
+ */
+#define module_driver(__driver, __register, __unregister) \
+static int __init __driver##_init(void) \
+{ \
+       return __register(&(__driver)); \
+} \
+module_init(__driver##_init); \
+static void __exit __driver##_exit(void) \
+{ \
+       __unregister(&(__driver)); \
+} \
+module_exit(__driver##_exit);
+
 #endif /* _DEVICE_H_ */
index a6c652e..6fcc8ab 100644 (file)
@@ -468,6 +468,19 @@ static inline int i2c_adapter_id(struct i2c_adapter *adap)
 {
        return adap->nr;
 }
+
+/**
+ * module_i2c_driver() - Helper macro for registering a I2C driver
+ * @__i2c_driver: i2c_driver struct
+ *
+ * Helper macro for I2C drivers which do not do anything special in module
+ * init/exit. This eliminates a lot of boilerplate. Each module may only
+ * use this macro once, and calling it replaces module_init() and module_exit()
+ */
+#define module_i2c_driver(__i2c_driver) \
+       module_driver(__i2c_driver, i2c_add_driver, \
+                       i2c_del_driver)
+
 #endif /* I2C */
 #endif /* __KERNEL__ */
 
index 13a801f..255491c 100644 (file)
@@ -146,6 +146,10 @@ void ida_remove(struct ida *ida, int id);
 void ida_destroy(struct ida *ida);
 void ida_init(struct ida *ida);
 
+int ida_simple_get(struct ida *ida, unsigned int start, unsigned int end,
+                  gfp_t gfp_mask);
+void ida_simple_remove(struct ida *ida, unsigned int id);
+
 void __init idr_init_cache(void);
 
 #endif /* __IDR_H__ */
index d908d17..7c6f627 100644 (file)
@@ -5,20 +5,17 @@
  *    Lauro Ramos Venancio <lauro.venancio@openbossa.org>
  *    Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
  *
- * 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.
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * 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.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 #ifndef __LINUX_NFC_H
  *      target mode.
  * @NFC_EVENT_DEVICE_DEACTIVATED: event emitted when the adapter is deactivated
  *      from target mode.
+ * @NFC_CMD_LLC_GET_PARAMS: request LTO, RW, and MIUX parameters for a device
+ * @NFC_CMD_LLC_SET_PARAMS: set one or more of LTO, RW, and MIUX parameters for
+ *     a device. LTO must be set before the link is up otherwise -EINPROGRESS
+ *     is returned. RW and MIUX can be set at anytime and will be passed in
+ *     subsequent CONNECT and CC messages.
+ *     If one of the passed parameters is wrong none is set and -EINVAL is
+ *     returned.
+ * @NFC_CMD_ENABLE_SE: Enable the physical link to a specific secure element.
+ *     Once enabled a secure element will handle card emulation mode, i.e.
+ *     starting a poll from a device which has a secure element enabled means
+ *     we want to do SE based card emulation.
+ * @NFC_CMD_DISABLE_SE: Disable the physical link to a specific secure element.
  */
 enum nfc_commands {
        NFC_CMD_UNSPEC,
@@ -77,6 +86,12 @@ enum nfc_commands {
        NFC_EVENT_TARGET_LOST,
        NFC_EVENT_TM_ACTIVATED,
        NFC_EVENT_TM_DEACTIVATED,
+       NFC_CMD_LLC_GET_PARAMS,
+       NFC_CMD_LLC_SET_PARAMS,
+       NFC_CMD_ENABLE_SE,
+       NFC_CMD_DISABLE_SE,
+       NFC_CMD_LLC_SDREQ,
+       NFC_EVENT_LLC_SDRES,
 /* private: internal use only */
        __NFC_CMD_AFTER_LAST
 };
@@ -102,6 +117,10 @@ enum nfc_commands {
  * @NFC_ATTR_RF_MODE: Initiator or target
  * @NFC_ATTR_IM_PROTOCOLS: Initiator mode protocols to poll for
  * @NFC_ATTR_TM_PROTOCOLS: Target mode protocols to listen for
+ * @NFC_ATTR_LLC_PARAM_LTO: Link TimeOut parameter
+ * @NFC_ATTR_LLC_PARAM_RW: Receive Window size parameter
+ * @NFC_ATTR_LLC_PARAM_MIUX: MIU eXtension parameter
+ * @NFC_ATTR_SE: Available Secure Elements
  */
 enum nfc_attrs {
        NFC_ATTR_UNSPEC,
@@ -119,11 +138,25 @@ enum nfc_attrs {
        NFC_ATTR_DEVICE_POWERED,
        NFC_ATTR_IM_PROTOCOLS,
        NFC_ATTR_TM_PROTOCOLS,
+       NFC_ATTR_LLC_PARAM_LTO,
+       NFC_ATTR_LLC_PARAM_RW,
+       NFC_ATTR_LLC_PARAM_MIUX,
+       NFC_ATTR_SE,
+       NFC_ATTR_LLC_SDP,
 /* private: internal use only */
        __NFC_ATTR_AFTER_LAST
 };
 #define NFC_ATTR_MAX (__NFC_ATTR_AFTER_LAST - 1)
 
+enum nfc_sdp_attr {
+       NFC_SDP_ATTR_UNSPEC,
+       NFC_SDP_ATTR_URI,
+       NFC_SDP_ATTR_SAP,
+/* private: internal use only */
+       __NFC_SDP_ATTR_AFTER_LAST
+};
+#define NFC_SDP_ATTR_MAX (__NFC_SDP_ATTR_AFTER_LAST - 1)
+
 #define NFC_DEVICE_NAME_MAXSIZE 8
 #define NFC_NFCID1_MAXSIZE 10
 #define NFC_SENSB_RES_MAXSIZE 12
@@ -157,6 +190,11 @@ enum nfc_attrs {
 #define NFC_PROTO_NFC_DEP_MASK   (1 << NFC_PROTO_NFC_DEP)
 #define NFC_PROTO_ISO14443_B_MASK (1 << NFC_PROTO_ISO14443_B)
 
+/* NFC Secure Elements */
+#define NFC_SE_NONE     0x0
+#define NFC_SE_UICC     0x1
+#define NFC_SE_EMBEDDED 0x2
+
 struct sockaddr_nfc {
        sa_family_t sa_family;
        __u32 dev_idx;
@@ -194,4 +232,11 @@ struct sockaddr_nfc_llcp {
 #define NFC_LLCP_DIRECTION_RX          0x00
 #define NFC_LLCP_DIRECTION_TX          0x01
 
+/* socket option names */
+#define NFC_LLCP_RW            0
+#define NFC_LLCP_MIUX          1
+#define NFC_LLCP_REMOTE_MIU    2
+#define NFC_LLCP_REMOTE_LTO    3
+#define NFC_LLCP_REMOTE_RW     4
+
 #endif /*__LINUX_NFC_H */
diff --git a/include/linux/nfc/pn544.h b/include/linux/nfc/pn544.h
deleted file mode 100644 (file)
index 9890bba..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Driver include for the PN544 NFC chip.
- *
- * Copyright (C) Nokia Corporation
- *
- * Author: Jari Vanhala <ext-jari.vanhala@nokia.com>
- * Contact: Matti Aaltoenn <matti.j.aaltonen@nokia.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.        See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifndef _PN544_H_
-#define _PN544_H_
-
-#include <linux/i2c.h>
-
-#define PN544_DRIVER_NAME      "pn544"
-#define PN544_MAXWINDOW_SIZE   7
-#define PN544_WINDOW_SIZE      4
-#define PN544_RETRIES          10
-#define PN544_MAX_I2C_TRANSFER 0x0400
-#define PN544_MSG_MAX_SIZE     0x21 /* at normal HCI mode */
-
-/* ioctl */
-#define PN544_CHAR_BASE                'P'
-#define PN544_IOR(num, dtype)  _IOR(PN544_CHAR_BASE, num, dtype)
-#define PN544_IOW(num, dtype)  _IOW(PN544_CHAR_BASE, num, dtype)
-#define PN544_GET_FW_MODE      PN544_IOW(1, unsigned int)
-#define PN544_SET_FW_MODE      PN544_IOW(2, unsigned int)
-#define PN544_GET_DEBUG                PN544_IOW(3, unsigned int)
-#define PN544_SET_DEBUG                PN544_IOW(4, unsigned int)
-
-/* Timing restrictions (ms) */
-#define PN544_RESETVEN_TIME    30 /* 7 */
-#define PN544_PVDDVEN_TIME     0
-#define PN544_VBATVEN_TIME     0
-#define PN544_GPIO4VEN_TIME    0
-#define PN544_WAKEUP_ACK       5
-#define PN544_WAKEUP_GUARD     (PN544_WAKEUP_ACK + 1)
-#define PN544_INACTIVITY_TIME  1000
-#define PN544_INTERFRAME_DELAY 200 /* us */
-#define PN544_BAUDRATE_CHANGE  150 /* us */
-
-/* Debug bits */
-#define PN544_DEBUG_BUF                0x01
-#define PN544_DEBUG_READ       0x02
-#define PN544_DEBUG_WRITE      0x04
-#define PN544_DEBUG_IRQ                0x08
-#define PN544_DEBUG_CALLS      0x10
-#define PN544_DEBUG_MODE       0x20
-
-/* Normal (HCI) mode */
-#define PN544_LLC_HCI_OVERHEAD 3 /* header + crc (to length) */
-#define PN544_LLC_MIN_SIZE     (1 + PN544_LLC_HCI_OVERHEAD) /* length + */
-#define PN544_LLC_MAX_DATA     (PN544_MSG_MAX_SIZE - 2)
-#define PN544_LLC_MAX_HCI_SIZE (PN544_LLC_MAX_DATA - 2)
-
-struct pn544_llc_packet {
-       unsigned char length; /* of rest of packet */
-       unsigned char header;
-       unsigned char data[PN544_LLC_MAX_DATA]; /* includes crc-ccitt */
-};
-
-/* Firmware upgrade mode */
-#define PN544_FW_HEADER_SIZE   3
-/* max fw transfer is 1024bytes, but I2C limits it to 0xC0 */
-#define PN544_MAX_FW_DATA      (PN544_MAX_I2C_TRANSFER - PN544_FW_HEADER_SIZE)
-
-struct pn544_fw_packet {
-       unsigned char command; /* status in answer */
-       unsigned char length[2]; /* big-endian order (msf) */
-       unsigned char data[PN544_MAX_FW_DATA];
-};
-
-#ifdef __KERNEL__
-enum {
-       NFC_GPIO_ENABLE,
-       NFC_GPIO_FW_RESET,
-       NFC_GPIO_IRQ
-};
-
-/* board config */
-struct pn544_nfc_platform_data {
-       int (*request_resources) (struct i2c_client *client);
-       void (*free_resources) (void);
-       void (*enable) (int fw);
-       int (*test) (void);
-       void (*disable) (void);
-       int (*get_gpio)(int type);
-};
-#endif /* __KERNEL__ */
-
-#endif /* _PN544_H_ */
diff --git a/include/linux/platform_data/microread.h b/include/linux/platform_data/microread.h
new file mode 100644 (file)
index 0000000..cfda59b
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Driver include for the PN544 NFC chip.
+ *
+ * Copyright (C) 2011 Tieto Poland
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.        See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MICROREAD_H
+#define _MICROREAD_H
+
+#include <linux/i2c.h>
+
+#define MICROREAD_DRIVER_NAME  "microread"
+
+/* board config platform data for microread */
+struct microread_nfc_platform_data {
+       unsigned int rst_gpio;
+       unsigned int irq_gpio;
+       unsigned int ioh_gpio;
+};
+
+#endif /* _MICROREAD_H */
diff --git a/include/linux/platform_data/pn544.h b/include/linux/platform_data/pn544.h
new file mode 100644 (file)
index 0000000..713bfd7
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Driver include for the PN544 NFC chip.
+ *
+ * Copyright (C) Nokia Corporation
+ *
+ * Author: Jari Vanhala <ext-jari.vanhala@nokia.com>
+ * Contact: Matti Aaltoenn <matti.j.aaltonen@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.        See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _PN544_H_
+#define _PN544_H_
+
+#include <linux/i2c.h>
+
+enum {
+       NFC_GPIO_ENABLE,
+       NFC_GPIO_FW_RESET,
+       NFC_GPIO_IRQ
+};
+
+/* board config */
+struct pn544_nfc_platform_data {
+       int (*request_resources) (struct i2c_client *client);
+       void (*free_resources) (void);
+       void (*enable) (int fw);
+       int (*test) (void);
+       void (*disable) (void);
+       int (*get_gpio)(int type);
+};
+
+#endif /* _PN544_H_ */
index b2819f8..1ec5186 100644 (file)
@@ -311,6 +311,7 @@ struct ucred {
 #define SOL_IUCV       277
 #define SOL_CAIF       278
 #define SOL_ALG                279
+#define SOL_NFC                280
 
 /* IPX options */
 #define IPX_TYPE       1
index 639f50a..b87a169 100644 (file)
@@ -57,8 +57,10 @@ struct nfc_hci_ops {
        int (*tm_send)(struct nfc_hci_dev *hdev, struct sk_buff *skb);
        int (*check_presence)(struct nfc_hci_dev *hdev,
                              struct nfc_target *target);
-       void (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event,
-                               struct sk_buff *skb);
+       int (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event,
+                             struct sk_buff *skb);
+       int (*enable_se)(struct nfc_dev *dev, u32 secure_element);
+       int (*disable_se)(struct nfc_dev *dev, u32 secure_element);
 };
 
 /* Pipes */
@@ -82,11 +84,23 @@ typedef int (*xmit) (struct sk_buff *skb, void *cb_data);
 
 #define NFC_HCI_MAX_GATES              256
 
+/*
+ * These values can be specified by a driver to indicate it requires some
+ * adaptation of the HCI standard.
+ *
+ * NFC_HCI_QUIRK_SHORT_CLEAR - send HCI_ADM_CLEAR_ALL_PIPE cmd with no params
+ */
+enum {
+       NFC_HCI_QUIRK_SHORT_CLEAR       = 0,
+};
+
 struct nfc_hci_dev {
        struct nfc_dev *ndev;
 
        u32 max_data_link_payload;
 
+       bool shutting_down;
+
        struct mutex msg_tx_mutex;
 
        struct list_head msg_tx_queue;
@@ -129,12 +143,16 @@ struct nfc_hci_dev {
 
        u8 *gb;
        size_t gb_len;
+
+       unsigned long quirks;
 };
 
 /* hci device allocation */
 struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
                                            struct nfc_hci_init_data *init_data,
+                                           unsigned long quirks,
                                            u32 protocols,
+                                           u32 supported_se,
                                            const char *llc_name,
                                            int tx_headroom,
                                            int tx_tailroom,
@@ -149,6 +167,8 @@ void *nfc_hci_get_clientdata(struct nfc_hci_dev *hdev);
 
 void nfc_hci_driver_failure(struct nfc_hci_dev *hdev, int err);
 
+int nfc_hci_result_to_errno(u8 result);
+
 /* Host IDs */
 #define NFC_HCI_HOST_CONTROLLER_ID     0x00
 #define NFC_HCI_TERMINAL_HOST_ID       0x01
@@ -235,5 +255,6 @@ int nfc_hci_send_response(struct nfc_hci_dev *hdev, u8 gate, u8 response,
 int nfc_hci_send_event(struct nfc_hci_dev *hdev, u8 gate, u8 event,
                       const u8 *param, size_t param_len);
 int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate);
+u32 nfc_hci_sak_to_protocol(u8 sak);
 
 #endif /* __NET_HCI_H */
index d705d86..5bc0c46 100644 (file)
@@ -147,6 +147,7 @@ struct nci_dev {
 /* ----- NCI Devices ----- */
 struct nci_dev *nci_allocate_device(struct nci_ops *ops,
                                    __u32 supported_protocols,
+                                   __u32 supported_se,
                                    int tx_headroom,
                                    int tx_tailroom);
 void nci_free_device(struct nci_dev *ndev);
index bfbac73..76b0d37 100644 (file)
@@ -68,6 +68,8 @@ struct nfc_ops {
                             void *cb_context);
        int (*tm_send)(struct nfc_dev *dev, struct sk_buff *skb);
        int (*check_presence)(struct nfc_dev *dev, struct nfc_target *target);
+       int (*enable_se)(struct nfc_dev *dev, u32 secure_element);
+       int (*disable_se)(struct nfc_dev *dev, u32 secure_element);
 };
 
 #define NFC_TARGET_IDX_ANY -1
@@ -95,7 +97,7 @@ struct nfc_genl_data {
 };
 
 struct nfc_dev {
-       unsigned int idx;
+       int idx;
        u32 target_next_idx;
        struct nfc_target *targets;
        int n_targets;
@@ -109,12 +111,17 @@ struct nfc_dev {
        struct nfc_genl_data genl_data;
        u32 supported_protocols;
 
+       u32 supported_se;
+       u32 active_se;
+
        int tx_headroom;
        int tx_tailroom;
 
        struct timer_list check_pres_timer;
        struct work_struct check_pres_work;
 
+       bool shutting_down;
+
        struct nfc_ops *ops;
 };
 #define to_nfc_dev(_dev) container_of(_dev, struct nfc_dev, dev)
@@ -123,6 +130,7 @@ extern struct class nfc_class;
 
 struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
                                    u32 supported_protocols,
+                                   u32 supported_se,
                                    int tx_headroom,
                                    int tx_tailroom);
 
index e15502e..dabdf79 100644 (file)
--- a/lib/idr.c
+++ b/lib/idr.c
 #include <linux/err.h>
 #include <linux/string.h>
 #include <linux/idr.h>
+#include <linux/spinlock.h>
 
 static struct kmem_cache *idr_layer_cache;
+static DEFINE_SPINLOCK(simple_ida_lock);
 
 static struct idr_layer *get_from_free_list(struct idr *idp)
 {
@@ -926,6 +928,74 @@ void ida_destroy(struct ida *ida)
 EXPORT_SYMBOL(ida_destroy);
 
 /**
+ * ida_simple_get - get a new id.
+ * @ida: the (initialized) ida.
+ * @start: the minimum id (inclusive, < 0x8000000)
+ * @end: the maximum id (exclusive, < 0x8000000 or 0)
+ * @gfp_mask: memory allocation flags
+ *
+ * Allocates an id in the range start <= id < end, or returns -ENOSPC.
+ * On memory allocation failure, returns -ENOMEM.
+ *
+ * Use ida_simple_remove() to get rid of an id.
+ */
+int ida_simple_get(struct ida *ida, unsigned int start, unsigned int end,
+                  gfp_t gfp_mask)
+{
+       int ret, id;
+       unsigned int max;
+       unsigned long flags;
+
+       BUG_ON((int)start < 0);
+       BUG_ON((int)end < 0);
+
+       if (end == 0)
+               max = 0x80000000;
+       else {
+               BUG_ON(end < start);
+               max = end - 1;
+       }
+
+again:
+       if (!ida_pre_get(ida, gfp_mask))
+               return -ENOMEM;
+
+       spin_lock_irqsave(&simple_ida_lock, flags);
+       ret = ida_get_new_above(ida, start, &id);
+       if (!ret) {
+               if (id > max) {
+                       ida_remove(ida, id);
+                       ret = -ENOSPC;
+               } else {
+                       ret = id;
+               }
+       }
+       spin_unlock_irqrestore(&simple_ida_lock, flags);
+
+       if (unlikely(ret == -EAGAIN))
+               goto again;
+
+       return ret;
+}
+EXPORT_SYMBOL(ida_simple_get);
+
+/**
+ * ida_simple_remove - remove an allocated id.
+ * @ida: the (initialized) ida.
+ * @id: the id returned by ida_simple_get.
+ */
+void ida_simple_remove(struct ida *ida, unsigned int id)
+{
+       unsigned long flags;
+
+       BUG_ON((int)id < 0);
+       spin_lock_irqsave(&simple_ida_lock, flags);
+       ida_remove(ida, id);
+       spin_unlock_irqrestore(&simple_ida_lock, flags);
+}
+EXPORT_SYMBOL(ida_simple_remove);
+
+/**
  * ida_init - initialize ida handle
  * @ida:       ida handle
  *
index 8d8d9bc..60c3bbb 100644 (file)
@@ -3,8 +3,8 @@
 #
 
 menuconfig NFC
-       depends on NET && EXPERIMENTAL
-       tristate "NFC subsystem support (EXPERIMENTAL)"
+       depends on NET
+       tristate "NFC subsystem support"
        default n
        help
          Say Y here if you want to build support for NFC (Near field
index 43227a7..f80ca77 100644 (file)
@@ -40,6 +40,9 @@
 int nfc_devlist_generation;
 DEFINE_MUTEX(nfc_devlist_mutex);
 
+/* NFC device ID bitmap */
+static DEFINE_IDA(nfc_index_ida);
+
 /**
  * nfc_dev_up - turn on the NFC device
  *
@@ -140,6 +143,11 @@ int nfc_start_poll(struct nfc_dev *dev, u32 im_protocols, u32 tm_protocols)
                goto error;
        }
 
+       if (!dev->dev_up) {
+               rc = -ENODEV;
+               goto error;
+       }
+
        if (dev->polling) {
                rc = -EBUSY;
                goto error;
@@ -195,7 +203,7 @@ static struct nfc_target *nfc_find_target(struct nfc_dev *dev, u32 target_idx)
        if (dev->n_targets == 0)
                return NULL;
 
-       for (i = 0; i < dev->n_targets ; i++) {
+       for (i = 0; i < dev->n_targets; i++) {
                if (dev->targets[i].idx == target_idx)
                        return &dev->targets[i];
        }
@@ -335,7 +343,7 @@ int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol)
                dev->active_target = target;
                dev->rf_mode = NFC_RF_INITIATOR;
 
-               if (dev->ops->check_presence)
+               if (dev->ops->check_presence && !dev->shutting_down)
                        mod_timer(&dev->check_pres_timer, jiffies +
                                  msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
        }
@@ -426,7 +434,7 @@ int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx, struct sk_buff *skb,
                rc = dev->ops->im_transceive(dev, dev->active_target, skb, cb,
                                             cb_context);
 
-               if (!rc && dev->ops->check_presence)
+               if (!rc && dev->ops->check_presence && !dev->shutting_down)
                        mod_timer(&dev->check_pres_timer, jiffies +
                                  msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
        } else if (dev->rf_mode == NFC_RF_TARGET && dev->ops->tm_send != NULL) {
@@ -681,11 +689,6 @@ static void nfc_release(struct device *d)
 
        pr_debug("dev_name=%s\n", dev_name(&dev->dev));
 
-       if (dev->ops->check_presence) {
-               del_timer_sync(&dev->check_pres_timer);
-               cancel_work_sync(&dev->check_pres_work);
-       }
-
        nfc_genl_data_exit(&dev->genl_data);
        kfree(dev->targets);
        kfree(dev);
@@ -703,15 +706,16 @@ static void nfc_check_pres_work(struct work_struct *work)
                rc = dev->ops->check_presence(dev, dev->active_target);
                if (rc == -EOPNOTSUPP)
                        goto exit;
-               if (!rc) {
-                       mod_timer(&dev->check_pres_timer, jiffies +
-                                 msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
-               } else {
+               if (rc) {
                        u32 active_target_idx = dev->active_target->idx;
                        device_unlock(&dev->dev);
                        nfc_target_lost(dev, active_target_idx);
                        return;
                }
+
+               if (!dev->shutting_down)
+                       mod_timer(&dev->check_pres_timer, jiffies +
+                                 msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
        }
 
 exit:
@@ -722,7 +726,7 @@ static void nfc_check_pres_timeout(unsigned long data)
 {
        struct nfc_dev *dev = (struct nfc_dev *)data;
 
-       queue_work(system_nrt_wq, &dev->check_pres_work);
+       schedule_work(&dev->check_pres_work);
 }
 
 struct class nfc_class = {
@@ -758,9 +762,9 @@ struct nfc_dev *nfc_get_device(unsigned int idx)
  */
 struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
                                    u32 supported_protocols,
+                                   u32 supported_se,
                                    int tx_headroom, int tx_tailroom)
 {
-       static atomic_t dev_no = ATOMIC_INIT(0);
        struct nfc_dev *dev;
 
        if (!ops->start_poll || !ops->stop_poll || !ops->activate_target ||
@@ -774,13 +778,10 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
        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;
+       dev->supported_se = supported_se;
+       dev->active_se = NFC_SE_NONE;
        dev->tx_headroom = tx_headroom;
        dev->tx_tailroom = tx_tailroom;
 
@@ -814,6 +815,14 @@ int nfc_register_device(struct nfc_dev *dev)
 
        pr_debug("dev_name=%s\n", dev_name(&dev->dev));
 
+       dev->idx = ida_simple_get(&nfc_index_ida, 0, 0, GFP_KERNEL);
+       if (dev->idx < 0)
+               return dev->idx;
+
+       dev->dev.class = &nfc_class;
+       dev_set_name(&dev->dev, "nfc%d", dev->idx);
+       device_initialize(&dev->dev);
+
        mutex_lock(&nfc_devlist_mutex);
        nfc_devlist_generation++;
        rc = device_add(&dev->dev);
@@ -842,28 +851,33 @@ EXPORT_SYMBOL(nfc_register_device);
  */
 void nfc_unregister_device(struct nfc_dev *dev)
 {
-       int rc;
+       int rc, id;
 
        pr_debug("dev_name=%s\n", dev_name(&dev->dev));
 
-       mutex_lock(&nfc_devlist_mutex);
-       nfc_devlist_generation++;
+       id = dev->idx;
 
-       /* lock to avoid unregistering a device while an operation
-          is in progress */
-       device_lock(&dev->dev);
-       device_del(&dev->dev);
-       device_unlock(&dev->dev);
+       if (dev->ops->check_presence) {
+               device_lock(&dev->dev);
+               dev->shutting_down = true;
+               device_unlock(&dev->dev);
+               del_timer_sync(&dev->check_pres_timer);
+               cancel_work_sync(&dev->check_pres_work);
+       }
 
-       mutex_unlock(&nfc_devlist_mutex);
+       rc = nfc_genl_device_removed(dev);
+       if (rc)
+               pr_debug("The userspace won't be notified that the device %s "
+                        "was removed\n", dev_name(&dev->dev));
 
        nfc_llcp_unregister_device(dev);
 
-       rc = nfc_genl_device_removed(dev);
-       if (rc)
-               pr_debug("The userspace won't be notified that the device %s was removed\n",
-                        dev_name(&dev->dev));
+       mutex_lock(&nfc_devlist_mutex);
+       nfc_devlist_generation++;
+       device_del(&dev->dev);
+       mutex_unlock(&nfc_devlist_mutex);
 
+       ida_simple_remove(&nfc_index_ida, id);
 }
 EXPORT_SYMBOL(nfc_unregister_device);
 
index 07659cf..64f922b 100644 (file)
@@ -280,14 +280,19 @@ static int nfc_hci_delete_pipe(struct nfc_hci_dev *hdev, u8 pipe)
 static int nfc_hci_clear_all_pipes(struct nfc_hci_dev *hdev)
 {
        u8 param[2];
+       size_t param_len = 2;
 
        /* TODO: Find out what the identity reference data is
         * and fill param with it. HCI spec 6.1.3.5 */
 
        pr_debug("\n");
 
+       if (test_bit(NFC_HCI_QUIRK_SHORT_CLEAR, &hdev->quirks))
+               param_len = 0;
+
        return nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE,
-                                  NFC_HCI_ADM_CLEAR_ALL_PIPE, param, 2, NULL);
+                                  NFC_HCI_ADM_CLEAR_ALL_PIPE, param, param_len,
+                                  NULL);
 }
 
 int nfc_hci_disconnect_gate(struct nfc_hci_dev *hdev, u8 gate)
@@ -344,7 +349,7 @@ int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate,
                return -EADDRINUSE;
 
        if (pipe != NFC_HCI_INVALID_PIPE)
-               goto pipe_is_open;
+               goto open_pipe;
 
        switch (dest_gate) {
        case NFC_HCI_LINK_MGMT_GATE:
@@ -361,6 +366,7 @@ int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate,
                break;
        }
 
+open_pipe:
        r = nfc_hci_open_pipe(hdev, pipe);
        if (r < 0) {
                if (pipe_created)
@@ -371,7 +377,6 @@ int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate,
                return r;
        }
 
-pipe_is_open:
        hdev->gate2pipe[dest_gate] = pipe;
 
        return 0;
index 8af3052..91020b2 100644 (file)
 /* Largest headroom needed for outgoing HCI commands */
 #define HCI_CMDS_HEADROOM 1
 
-static int nfc_hci_result_to_errno(u8 result)
+int nfc_hci_result_to_errno(u8 result)
 {
        switch (result) {
        case NFC_HCI_ANY_OK:
                return 0;
+       case NFC_HCI_ANY_E_REG_PAR_UNKNOWN:
+               return -EOPNOTSUPP;
        case NFC_HCI_ANY_E_TIMEOUT:
                return -ETIME;
        default:
                return -1;
        }
 }
+EXPORT_SYMBOL(nfc_hci_result_to_errno);
 
 static void nfc_hci_msg_tx_work(struct work_struct *work)
 {
@@ -54,6 +57,8 @@ static void nfc_hci_msg_tx_work(struct work_struct *work)
        int r = 0;
 
        mutex_lock(&hdev->msg_tx_mutex);
+       if (hdev->shutting_down)
+               goto exit;
 
        if (hdev->cmd_pending_msg) {
                if (timer_pending(&hdev->cmd_timer) == 0) {
@@ -65,8 +70,9 @@ static void nfc_hci_msg_tx_work(struct work_struct *work)
                                                          -ETIME);
                        kfree(hdev->cmd_pending_msg);
                        hdev->cmd_pending_msg = NULL;
-               } else
+               } else {
                        goto exit;
+               }
        }
 
 next_msg:
@@ -141,7 +147,7 @@ static void __nfc_hci_cmd_completion(struct nfc_hci_dev *hdev, int err,
        kfree(hdev->cmd_pending_msg);
        hdev->cmd_pending_msg = NULL;
 
-       queue_work(system_nrt_wq, &hdev->msg_tx_work);
+       schedule_work(&hdev->msg_tx_work);
 }
 
 void nfc_hci_resp_received(struct nfc_hci_dev *hdev, u8 result,
@@ -166,7 +172,7 @@ void nfc_hci_cmd_received(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
        kfree_skb(skb);
 }
 
-static u32 nfc_hci_sak_to_protocol(u8 sak)
+u32 nfc_hci_sak_to_protocol(u8 sak)
 {
        switch (NFC_HCI_TYPE_A_SEL_PROT(sak)) {
        case NFC_HCI_TYPE_A_SEL_PROT_MIFARE:
@@ -181,6 +187,7 @@ static u32 nfc_hci_sak_to_protocol(u8 sak)
                return 0xffffffff;
        }
 }
+EXPORT_SYMBOL(nfc_hci_sak_to_protocol);
 
 int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate)
 {
@@ -283,6 +290,18 @@ void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event,
                            struct sk_buff *skb)
 {
        int r = 0;
+       u8 gate = nfc_hci_pipe2gate(hdev, pipe);
+
+       if (gate == 0xff) {
+               pr_err("Discarded event %x to unopened pipe %x\n", event, pipe);
+               goto exit;
+       }
+
+       if (hdev->ops->event_received) {
+               r = hdev->ops->event_received(hdev, gate, event, skb);
+               if (r <= 0)
+                       goto exit_noskb;
+       }
 
        switch (event) {
        case NFC_HCI_EVT_TARGET_DISCOVERED:
@@ -306,23 +325,18 @@ void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event,
                        goto exit;
                }
 
-               r = nfc_hci_target_discovered(hdev,
-                                             nfc_hci_pipe2gate(hdev, pipe));
+               r = nfc_hci_target_discovered(hdev, gate);
                break;
        default:
-               if (hdev->ops->event_received) {
-                       hdev->ops->event_received(hdev,
-                                               nfc_hci_pipe2gate(hdev, pipe),
-                                               event, skb);
-                       return;
-               }
-
+               pr_info("Discarded unknown event %x to gate %x\n", event, gate);
+               r = -EINVAL;
                break;
        }
 
 exit:
        kfree_skb(skb);
 
+exit_noskb:
        if (r) {
                /* TODO: There was an error dispatching the event,
                 * how to propagate up to nfc core?
@@ -334,7 +348,7 @@ static void nfc_hci_cmd_timeout(unsigned long data)
 {
        struct nfc_hci_dev *hdev = (struct nfc_hci_dev *)data;
 
-       queue_work(system_nrt_wq, &hdev->msg_tx_work);
+       schedule_work(&hdev->msg_tx_work);
 }
 
 static int hci_dev_connect_gates(struct nfc_hci_dev *hdev, u8 gate_count,
@@ -418,6 +432,10 @@ static int hci_dev_version(struct nfc_hci_dev *hdev)
 
        r = nfc_hci_get_param(hdev, NFC_HCI_ID_MGMT_GATE,
                              NFC_HCI_ID_MGMT_VERSION_SW, &skb);
+       if (r == -EOPNOTSUPP) {
+               pr_info("Software/Hardware info not available\n");
+               return 0;
+       }
        if (r < 0)
                return r;
 
@@ -535,7 +553,8 @@ static int hci_start_poll(struct nfc_dev *nfc_dev,
                return hdev->ops->start_poll(hdev, im_protocols, tm_protocols);
        else
                return nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
-                                      NFC_HCI_EVT_READER_REQUESTED, NULL, 0);
+                                         NFC_HCI_EVT_READER_REQUESTED,
+                                         NULL, 0);
 }
 
 static void hci_stop_poll(struct nfc_dev *nfc_dev)
@@ -639,9 +658,9 @@ static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
                                                     cb_context);
                        if (r == 1)
                                r = -ENOTSUPP;
-               }
-               else
+               } else {
                        r = -ENOTSUPP;
+               }
                break;
        }
 
@@ -656,8 +675,10 @@ static int hci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
 
        if (hdev->ops->tm_send)
                return hdev->ops->tm_send(hdev, skb);
-       else
-               return -ENOTSUPP;
+
+       kfree_skb(skb);
+
+       return -ENOTSUPP;
 }
 
 static int hci_check_presence(struct nfc_dev *nfc_dev,
@@ -754,7 +775,7 @@ static void nfc_hci_recv_from_llc(struct nfc_hci_dev *hdev, struct sk_buff *skb)
                nfc_hci_hcp_message_rx(hdev, pipe, type, instruction, hcp_skb);
        } else {
                skb_queue_tail(&hdev->msg_rx_queue, hcp_skb);
-               queue_work(system_nrt_wq, &hdev->msg_rx_work);
+               schedule_work(&hdev->msg_rx_work);
        }
 }
 
@@ -774,7 +795,9 @@ static struct nfc_ops hci_nfc_ops = {
 
 struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
                                            struct nfc_hci_init_data *init_data,
+                                           unsigned long quirks,
                                            u32 protocols,
+                                           u32 supported_se,
                                            const char *llc_name,
                                            int tx_headroom,
                                            int tx_tailroom,
@@ -800,7 +823,7 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
                return NULL;
        }
 
-       hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols,
+       hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols, supported_se,
                                         tx_headroom + HCI_CMDS_HEADROOM,
                                         tx_tailroom);
        if (!hdev->ndev) {
@@ -817,6 +840,8 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
 
        memset(hdev->gate2pipe, NFC_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe));
 
+       hdev->quirks = quirks;
+
        return hdev;
 }
 EXPORT_SYMBOL(nfc_hci_allocate_device);
@@ -855,6 +880,28 @@ void nfc_hci_unregister_device(struct nfc_hci_dev *hdev)
 {
        struct hci_msg *msg, *n;
 
+       mutex_lock(&hdev->msg_tx_mutex);
+
+       if (hdev->cmd_pending_msg) {
+               if (hdev->cmd_pending_msg->cb)
+                       hdev->cmd_pending_msg->cb(
+                                            hdev->cmd_pending_msg->cb_context,
+                                            NULL, -ESHUTDOWN);
+               kfree(hdev->cmd_pending_msg);
+               hdev->cmd_pending_msg = NULL;
+       }
+
+       hdev->shutting_down = true;
+
+       mutex_unlock(&hdev->msg_tx_mutex);
+
+       del_timer_sync(&hdev->cmd_timer);
+       cancel_work_sync(&hdev->msg_tx_work);
+
+       cancel_work_sync(&hdev->msg_rx_work);
+
+       nfc_unregister_device(hdev->ndev);
+
        skb_queue_purge(&hdev->rx_hcp_frags);
        skb_queue_purge(&hdev->msg_rx_queue);
 
@@ -863,13 +910,6 @@ void nfc_hci_unregister_device(struct nfc_hci_dev *hdev)
                skb_queue_purge(&msg->msg_frags);
                kfree(msg);
        }
-
-       del_timer_sync(&hdev->cmd_timer);
-
-       nfc_unregister_device(hdev->ndev);
-
-       cancel_work_sync(&hdev->msg_tx_work);
-       cancel_work_sync(&hdev->msg_rx_work);
 }
 EXPORT_SYMBOL(nfc_hci_unregister_device);
 
@@ -891,7 +931,7 @@ void nfc_hci_driver_failure(struct nfc_hci_dev *hdev, int err)
 }
 EXPORT_SYMBOL(nfc_hci_driver_failure);
 
-void inline nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+void nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb)
 {
        nfc_llc_rcv_from_drv(hdev->llc, skb);
 }
index 208eedd..b6b4109 100644 (file)
@@ -105,10 +105,17 @@ int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
        }
 
        mutex_lock(&hdev->msg_tx_mutex);
+
+       if (hdev->shutting_down) {
+               err = -ESHUTDOWN;
+               mutex_unlock(&hdev->msg_tx_mutex);
+               goto out_skb_err;
+       }
+
        list_add_tail(&cmd->msg_l, &hdev->msg_tx_queue);
        mutex_unlock(&hdev->msg_tx_mutex);
 
-       queue_work(system_nrt_wq, &hdev->msg_tx_work);
+       schedule_work(&hdev->msg_tx_work);
 
        return 0;
 
index ae1205d..fe5e966 100644 (file)
@@ -72,7 +72,7 @@ int nfc_llc_register(const char *name, struct nfc_llc_ops *ops)
        llc_engine->ops = ops;
 
        INIT_LIST_HEAD(&llc_engine->entry);
-       list_add_tail (&llc_engine->entry, &llc_engines);
+       list_add_tail(&llc_engine->entry, &llc_engines);
 
        return 0;
 }
index 8f69d79..27b313b 100644 (file)
@@ -588,7 +588,7 @@ static void llc_shdlc_connect_timeout(unsigned long data)
 
        pr_debug("\n");
 
-       queue_work(system_nrt_wq, &shdlc->sm_work);
+       schedule_work(&shdlc->sm_work);
 }
 
 static void llc_shdlc_t1_timeout(unsigned long data)
@@ -597,7 +597,7 @@ static void llc_shdlc_t1_timeout(unsigned long data)
 
        pr_debug("SoftIRQ: need to send ack\n");
 
-       queue_work(system_nrt_wq, &shdlc->sm_work);
+       schedule_work(&shdlc->sm_work);
 }
 
 static void llc_shdlc_t2_timeout(unsigned long data)
@@ -606,7 +606,7 @@ static void llc_shdlc_t2_timeout(unsigned long data)
 
        pr_debug("SoftIRQ: need to retransmit\n");
 
-       queue_work(system_nrt_wq, &shdlc->sm_work);
+       schedule_work(&shdlc->sm_work);
 }
 
 static void llc_shdlc_sm_work(struct work_struct *work)
@@ -634,9 +634,9 @@ static void llc_shdlc_sm_work(struct work_struct *work)
                        r = llc_shdlc_connect_initiate(shdlc);
                else
                        r = -ETIME;
-               if (r < 0)
+               if (r < 0) {
                        llc_shdlc_connect_complete(shdlc, r);
-               else {
+               else {
                        mod_timer(&shdlc->connect_timer, jiffies +
                                  msecs_to_jiffies(SHDLC_CONNECT_VALUE_MS));
 
@@ -646,7 +646,7 @@ static void llc_shdlc_sm_work(struct work_struct *work)
        case SHDLC_NEGOTIATING:
                if (timer_pending(&shdlc->connect_timer) == 0) {
                        shdlc->state = SHDLC_CONNECTING;
-                       queue_work(system_nrt_wq, &shdlc->sm_work);
+                       schedule_work(&shdlc->sm_work);
                }
 
                llc_shdlc_handle_rcv_queue(shdlc);
@@ -682,9 +682,8 @@ static void llc_shdlc_sm_work(struct work_struct *work)
                        llc_shdlc_handle_send_queue(shdlc);
                }
 
-               if (shdlc->hard_fault) {
+               if (shdlc->hard_fault)
                        shdlc->llc_failure(shdlc->hdev, shdlc->hard_fault);
-               }
                break;
        default:
                break;
@@ -711,7 +710,7 @@ static int llc_shdlc_connect(struct llc_shdlc *shdlc)
 
        mutex_unlock(&shdlc->state_mutex);
 
-       queue_work(system_nrt_wq, &shdlc->sm_work);
+       schedule_work(&shdlc->sm_work);
 
        wait_event(connect_wq, shdlc->connect_result != 1);
 
@@ -728,7 +727,7 @@ static void llc_shdlc_disconnect(struct llc_shdlc *shdlc)
 
        mutex_unlock(&shdlc->state_mutex);
 
-       queue_work(system_nrt_wq, &shdlc->sm_work);
+       schedule_work(&shdlc->sm_work);
 }
 
 /*
@@ -746,7 +745,7 @@ static void llc_shdlc_recv_frame(struct llc_shdlc *shdlc, struct sk_buff *skb)
                skb_queue_tail(&shdlc->rcv_q, skb);
        }
 
-       queue_work(system_nrt_wq, &shdlc->sm_work);
+       schedule_work(&shdlc->sm_work);
 }
 
 static void *llc_shdlc_init(struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv,
@@ -837,7 +836,7 @@ static int llc_shdlc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb)
 
        skb_queue_tail(&shdlc->send_q, skb);
 
-       queue_work(system_nrt_wq, &shdlc->sm_work);
+       schedule_work(&shdlc->sm_work);
 
        return 0;
 }
diff --git a/net/nfc/hci/shdlc.c b/net/nfc/hci/shdlc.c
deleted file mode 100644 (file)
index 256be0f..0000000
+++ /dev/null
@@ -1,950 +0,0 @@
-/*
- * Copyright (C) 2012  Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the
- * Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#define pr_fmt(fmt) "shdlc: %s: " fmt, __func__
-
-#include <linux/sched.h>
-#include <linux/wait.h>
-#include <linux/crc-ccitt.h>
-#include <linux/slab.h>
-#include <linux/skbuff.h>
-
-#include <net/nfc/hci.h>
-#include <net/nfc/shdlc.h>
-
-#define SHDLC_LLC_HEAD_ROOM    2
-#define SHDLC_LLC_TAIL_ROOM    2
-
-#define SHDLC_MAX_WINDOW       4
-#define SHDLC_SREJ_SUPPORT     false
-
-#define SHDLC_CONTROL_HEAD_MASK        0xe0
-#define SHDLC_CONTROL_HEAD_I   0x80
-#define SHDLC_CONTROL_HEAD_I2  0xa0
-#define SHDLC_CONTROL_HEAD_S   0xc0
-#define SHDLC_CONTROL_HEAD_U   0xe0
-
-#define SHDLC_CONTROL_NS_MASK  0x38
-#define SHDLC_CONTROL_NR_MASK  0x07
-#define SHDLC_CONTROL_TYPE_MASK        0x18
-
-#define SHDLC_CONTROL_M_MASK   0x1f
-
-enum sframe_type {
-       S_FRAME_RR = 0x00,
-       S_FRAME_REJ = 0x01,
-       S_FRAME_RNR = 0x02,
-       S_FRAME_SREJ = 0x03
-};
-
-enum uframe_modifier {
-       U_FRAME_UA = 0x06,
-       U_FRAME_RSET = 0x19
-};
-
-#define SHDLC_CONNECT_VALUE_MS 5
-#define SHDLC_T1_VALUE_MS(w)   ((5 * w) / 4)
-#define SHDLC_T2_VALUE_MS      300
-
-#define SHDLC_DUMP_SKB(info, skb)                                \
-do {                                                             \
-       pr_debug("%s:\n", info);                                  \
-       print_hex_dump(KERN_DEBUG, "shdlc: ", DUMP_PREFIX_OFFSET, \
-                      16, 1, skb->data, skb->len, 0);            \
-} while (0)
-
-/* checks x < y <= z modulo 8 */
-static bool nfc_shdlc_x_lt_y_lteq_z(int x, int y, int z)
-{
-       if (x < z)
-               return ((x < y) && (y <= z)) ? true : false;
-       else
-               return ((y > x) || (y <= z)) ? true : false;
-}
-
-/* checks x <= y < z modulo 8 */
-static bool nfc_shdlc_x_lteq_y_lt_z(int x, int y, int z)
-{
-       if (x <= z)
-               return ((x <= y) && (y < z)) ? true : false;
-       else                    /* x > z -> z+8 > x */
-               return ((y >= x) || (y < z)) ? true : false;
-}
-
-static struct sk_buff *nfc_shdlc_alloc_skb(struct nfc_shdlc *shdlc,
-                                          int payload_len)
-{
-       struct sk_buff *skb;
-
-       skb = alloc_skb(shdlc->client_headroom + SHDLC_LLC_HEAD_ROOM +
-                       shdlc->client_tailroom + SHDLC_LLC_TAIL_ROOM +
-                       payload_len, GFP_KERNEL);
-       if (skb)
-               skb_reserve(skb, shdlc->client_headroom + SHDLC_LLC_HEAD_ROOM);
-
-       return skb;
-}
-
-static void nfc_shdlc_add_len_crc(struct sk_buff *skb)
-{
-       u16 crc;
-       int len;
-
-       len = skb->len + 2;
-       *skb_push(skb, 1) = len;
-
-       crc = crc_ccitt(0xffff, skb->data, skb->len);
-       crc = ~crc;
-       *skb_put(skb, 1) = crc & 0xff;
-       *skb_put(skb, 1) = crc >> 8;
-}
-
-/* immediately sends an S frame. */
-static int nfc_shdlc_send_s_frame(struct nfc_shdlc *shdlc,
-                                 enum sframe_type sframe_type, int nr)
-{
-       int r;
-       struct sk_buff *skb;
-
-       pr_debug("sframe_type=%d nr=%d\n", sframe_type, nr);
-
-       skb = nfc_shdlc_alloc_skb(shdlc, 0);
-       if (skb == NULL)
-               return -ENOMEM;
-
-       *skb_push(skb, 1) = SHDLC_CONTROL_HEAD_S | (sframe_type << 3) | nr;
-
-       nfc_shdlc_add_len_crc(skb);
-
-       r = shdlc->ops->xmit(shdlc, skb);
-
-       kfree_skb(skb);
-
-       return r;
-}
-
-/* immediately sends an U frame. skb may contain optional payload */
-static int nfc_shdlc_send_u_frame(struct nfc_shdlc *shdlc,
-                                 struct sk_buff *skb,
-                                 enum uframe_modifier uframe_modifier)
-{
-       int r;
-
-       pr_debug("uframe_modifier=%d\n", uframe_modifier);
-
-       *skb_push(skb, 1) = SHDLC_CONTROL_HEAD_U | uframe_modifier;
-
-       nfc_shdlc_add_len_crc(skb);
-
-       r = shdlc->ops->xmit(shdlc, skb);
-
-       kfree_skb(skb);
-
-       return r;
-}
-
-/*
- * Free ack_pending frames until y_nr - 1, and reset t2 according to
- * the remaining oldest ack_pending frame sent time
- */
-static void nfc_shdlc_reset_t2(struct nfc_shdlc *shdlc, int y_nr)
-{
-       struct sk_buff *skb;
-       int dnr = shdlc->dnr;   /* MUST initially be < y_nr */
-
-       pr_debug("release ack pending up to frame %d excluded\n", y_nr);
-
-       while (dnr != y_nr) {
-               pr_debug("release ack pending frame %d\n", dnr);
-
-               skb = skb_dequeue(&shdlc->ack_pending_q);
-               kfree_skb(skb);
-
-               dnr = (dnr + 1) % 8;
-       }
-
-       if (skb_queue_empty(&shdlc->ack_pending_q)) {
-               if (shdlc->t2_active) {
-                       del_timer_sync(&shdlc->t2_timer);
-                       shdlc->t2_active = false;
-
-                       pr_debug
-                           ("All sent frames acked. Stopped T2(retransmit)\n");
-               }
-       } else {
-               skb = skb_peek(&shdlc->ack_pending_q);
-
-               mod_timer(&shdlc->t2_timer, *(unsigned long *)skb->cb +
-                         msecs_to_jiffies(SHDLC_T2_VALUE_MS));
-               shdlc->t2_active = true;
-
-               pr_debug
-                   ("Start T2(retransmit) for remaining unacked sent frames\n");
-       }
-}
-
-/*
- * Receive validated frames from lower layer. skb contains HCI payload only.
- * Handle according to algorithm at spec:10.8.2
- */
-static void nfc_shdlc_rcv_i_frame(struct nfc_shdlc *shdlc,
-                                 struct sk_buff *skb, int ns, int nr)
-{
-       int x_ns = ns;
-       int y_nr = nr;
-
-       pr_debug("recvd I-frame %d, remote waiting frame %d\n", ns, nr);
-
-       if (shdlc->state != SHDLC_CONNECTED)
-               goto exit;
-
-       if (x_ns != shdlc->nr) {
-               nfc_shdlc_send_s_frame(shdlc, S_FRAME_REJ, shdlc->nr);
-               goto exit;
-       }
-
-       if (shdlc->t1_active == false) {
-               shdlc->t1_active = true;
-               mod_timer(&shdlc->t1_timer, jiffies +
-                         msecs_to_jiffies(SHDLC_T1_VALUE_MS(shdlc->w)));
-               pr_debug("(re)Start T1(send ack)\n");
-       }
-
-       if (skb->len) {
-               nfc_hci_recv_frame(shdlc->hdev, skb);
-               skb = NULL;
-       }
-
-       shdlc->nr = (shdlc->nr + 1) % 8;
-
-       if (nfc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) {
-               nfc_shdlc_reset_t2(shdlc, y_nr);
-
-               shdlc->dnr = y_nr;
-       }
-
-exit:
-       if (skb)
-               kfree_skb(skb);
-}
-
-static void nfc_shdlc_rcv_ack(struct nfc_shdlc *shdlc, int y_nr)
-{
-       pr_debug("remote acked up to frame %d excluded\n", y_nr);
-
-       if (nfc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) {
-               nfc_shdlc_reset_t2(shdlc, y_nr);
-               shdlc->dnr = y_nr;
-       }
-}
-
-static void nfc_shdlc_requeue_ack_pending(struct nfc_shdlc *shdlc)
-{
-       struct sk_buff *skb;
-
-       pr_debug("ns reset to %d\n", shdlc->dnr);
-
-       while ((skb = skb_dequeue_tail(&shdlc->ack_pending_q))) {
-               skb_pull(skb, 2);       /* remove len+control */
-               skb_trim(skb, skb->len - 2);    /* remove crc */
-               skb_queue_head(&shdlc->send_q, skb);
-       }
-       shdlc->ns = shdlc->dnr;
-}
-
-static void nfc_shdlc_rcv_rej(struct nfc_shdlc *shdlc, int y_nr)
-{
-       struct sk_buff *skb;
-
-       pr_debug("remote asks retransmition from frame %d\n", y_nr);
-
-       if (nfc_shdlc_x_lteq_y_lt_z(shdlc->dnr, y_nr, shdlc->ns)) {
-               if (shdlc->t2_active) {
-                       del_timer_sync(&shdlc->t2_timer);
-                       shdlc->t2_active = false;
-                       pr_debug("Stopped T2(retransmit)\n");
-               }
-
-               if (shdlc->dnr != y_nr) {
-                       while ((shdlc->dnr = ((shdlc->dnr + 1) % 8)) != y_nr) {
-                               skb = skb_dequeue(&shdlc->ack_pending_q);
-                               kfree_skb(skb);
-                       }
-               }
-
-               nfc_shdlc_requeue_ack_pending(shdlc);
-       }
-}
-
-/* See spec RR:10.8.3 REJ:10.8.4 */
-static void nfc_shdlc_rcv_s_frame(struct nfc_shdlc *shdlc,
-                                 enum sframe_type s_frame_type, int nr)
-{
-       struct sk_buff *skb;
-
-       if (shdlc->state != SHDLC_CONNECTED)
-               return;
-
-       switch (s_frame_type) {
-       case S_FRAME_RR:
-               nfc_shdlc_rcv_ack(shdlc, nr);
-               if (shdlc->rnr == true) {       /* see SHDLC 10.7.7 */
-                       shdlc->rnr = false;
-                       if (shdlc->send_q.qlen == 0) {
-                               skb = nfc_shdlc_alloc_skb(shdlc, 0);
-                               if (skb)
-                                       skb_queue_tail(&shdlc->send_q, skb);
-                       }
-               }
-               break;
-       case S_FRAME_REJ:
-               nfc_shdlc_rcv_rej(shdlc, nr);
-               break;
-       case S_FRAME_RNR:
-               nfc_shdlc_rcv_ack(shdlc, nr);
-               shdlc->rnr = true;
-               break;
-       default:
-               break;
-       }
-}
-
-static void nfc_shdlc_connect_complete(struct nfc_shdlc *shdlc, int r)
-{
-       pr_debug("result=%d\n", r);
-
-       del_timer_sync(&shdlc->connect_timer);
-
-       if (r == 0) {
-               shdlc->ns = 0;
-               shdlc->nr = 0;
-               shdlc->dnr = 0;
-
-               shdlc->state = SHDLC_CONNECTED;
-       } else {
-               shdlc->state = SHDLC_DISCONNECTED;
-       }
-
-       shdlc->connect_result = r;
-
-       wake_up(shdlc->connect_wq);
-}
-
-static int nfc_shdlc_connect_initiate(struct nfc_shdlc *shdlc)
-{
-       struct sk_buff *skb;
-
-       pr_debug("\n");
-
-       skb = nfc_shdlc_alloc_skb(shdlc, 2);
-       if (skb == NULL)
-               return -ENOMEM;
-
-       *skb_put(skb, 1) = SHDLC_MAX_WINDOW;
-       *skb_put(skb, 1) = SHDLC_SREJ_SUPPORT ? 1 : 0;
-
-       return nfc_shdlc_send_u_frame(shdlc, skb, U_FRAME_RSET);
-}
-
-static int nfc_shdlc_connect_send_ua(struct nfc_shdlc *shdlc)
-{
-       struct sk_buff *skb;
-
-       pr_debug("\n");
-
-       skb = nfc_shdlc_alloc_skb(shdlc, 0);
-       if (skb == NULL)
-               return -ENOMEM;
-
-       return nfc_shdlc_send_u_frame(shdlc, skb, U_FRAME_UA);
-}
-
-static void nfc_shdlc_rcv_u_frame(struct nfc_shdlc *shdlc,
-                                 struct sk_buff *skb,
-                                 enum uframe_modifier u_frame_modifier)
-{
-       u8 w = SHDLC_MAX_WINDOW;
-       bool srej_support = SHDLC_SREJ_SUPPORT;
-       int r;
-
-       pr_debug("u_frame_modifier=%d\n", u_frame_modifier);
-
-       switch (u_frame_modifier) {
-       case U_FRAME_RSET:
-               if (shdlc->state == SHDLC_NEGOCIATING) {
-                       /* we sent RSET, but chip wants to negociate */
-                       if (skb->len > 0)
-                               w = skb->data[0];
-
-                       if (skb->len > 1)
-                               srej_support = skb->data[1] & 0x01 ? true :
-                                              false;
-
-                       if ((w <= SHDLC_MAX_WINDOW) &&
-                           (SHDLC_SREJ_SUPPORT || (srej_support == false))) {
-                               shdlc->w = w;
-                               shdlc->srej_support = srej_support;
-                               r = nfc_shdlc_connect_send_ua(shdlc);
-                               nfc_shdlc_connect_complete(shdlc, r);
-                       }
-               } else if (shdlc->state == SHDLC_CONNECTED) {
-                       /*
-                        * Chip wants to reset link. This is unexpected and
-                        * unsupported.
-                        */
-                       shdlc->hard_fault = -ECONNRESET;
-               }
-               break;
-       case U_FRAME_UA:
-               if ((shdlc->state == SHDLC_CONNECTING &&
-                    shdlc->connect_tries > 0) ||
-                   (shdlc->state == SHDLC_NEGOCIATING))
-                       nfc_shdlc_connect_complete(shdlc, 0);
-               break;
-       default:
-               break;
-       }
-
-       kfree_skb(skb);
-}
-
-static void nfc_shdlc_handle_rcv_queue(struct nfc_shdlc *shdlc)
-{
-       struct sk_buff *skb;
-       u8 control;
-       int nr;
-       int ns;
-       enum sframe_type s_frame_type;
-       enum uframe_modifier u_frame_modifier;
-
-       if (shdlc->rcv_q.qlen)
-               pr_debug("rcvQlen=%d\n", shdlc->rcv_q.qlen);
-
-       while ((skb = skb_dequeue(&shdlc->rcv_q)) != NULL) {
-               control = skb->data[0];
-               skb_pull(skb, 1);
-               switch (control & SHDLC_CONTROL_HEAD_MASK) {
-               case SHDLC_CONTROL_HEAD_I:
-               case SHDLC_CONTROL_HEAD_I2:
-                       ns = (control & SHDLC_CONTROL_NS_MASK) >> 3;
-                       nr = control & SHDLC_CONTROL_NR_MASK;
-                       nfc_shdlc_rcv_i_frame(shdlc, skb, ns, nr);
-                       break;
-               case SHDLC_CONTROL_HEAD_S:
-                       s_frame_type = (control & SHDLC_CONTROL_TYPE_MASK) >> 3;
-                       nr = control & SHDLC_CONTROL_NR_MASK;
-                       nfc_shdlc_rcv_s_frame(shdlc, s_frame_type, nr);
-                       kfree_skb(skb);
-                       break;
-               case SHDLC_CONTROL_HEAD_U:
-                       u_frame_modifier = control & SHDLC_CONTROL_M_MASK;
-                       nfc_shdlc_rcv_u_frame(shdlc, skb, u_frame_modifier);
-                       break;
-               default:
-                       pr_err("UNKNOWN Control=%d\n", control);
-                       kfree_skb(skb);
-                       break;
-               }
-       }
-}
-
-static int nfc_shdlc_w_used(int ns, int dnr)
-{
-       int unack_count;
-
-       if (dnr <= ns)
-               unack_count = ns - dnr;
-       else
-               unack_count = 8 - dnr + ns;
-
-       return unack_count;
-}
-
-/* Send frames according to algorithm at spec:10.8.1 */
-static void nfc_shdlc_handle_send_queue(struct nfc_shdlc *shdlc)
-{
-       struct sk_buff *skb;
-       int r;
-       unsigned long time_sent;
-
-       if (shdlc->send_q.qlen)
-               pr_debug
-                   ("sendQlen=%d ns=%d dnr=%d rnr=%s w_room=%d unackQlen=%d\n",
-                    shdlc->send_q.qlen, shdlc->ns, shdlc->dnr,
-                    shdlc->rnr == false ? "false" : "true",
-                    shdlc->w - nfc_shdlc_w_used(shdlc->ns, shdlc->dnr),
-                    shdlc->ack_pending_q.qlen);
-
-       while (shdlc->send_q.qlen && shdlc->ack_pending_q.qlen < shdlc->w &&
-              (shdlc->rnr == false)) {
-
-               if (shdlc->t1_active) {
-                       del_timer_sync(&shdlc->t1_timer);
-                       shdlc->t1_active = false;
-                       pr_debug("Stopped T1(send ack)\n");
-               }
-
-               skb = skb_dequeue(&shdlc->send_q);
-
-               *skb_push(skb, 1) = SHDLC_CONTROL_HEAD_I | (shdlc->ns << 3) |
-                                   shdlc->nr;
-
-               pr_debug("Sending I-Frame %d, waiting to rcv %d\n", shdlc->ns,
-                        shdlc->nr);
-       /*      SHDLC_DUMP_SKB("shdlc frame written", skb); */
-
-               nfc_shdlc_add_len_crc(skb);
-
-               r = shdlc->ops->xmit(shdlc, skb);
-               if (r < 0) {
-                       shdlc->hard_fault = r;
-                       break;
-               }
-
-               shdlc->ns = (shdlc->ns + 1) % 8;
-
-               time_sent = jiffies;
-               *(unsigned long *)skb->cb = time_sent;
-
-               skb_queue_tail(&shdlc->ack_pending_q, skb);
-
-               if (shdlc->t2_active == false) {
-                       shdlc->t2_active = true;
-                       mod_timer(&shdlc->t2_timer, time_sent +
-                                 msecs_to_jiffies(SHDLC_T2_VALUE_MS));
-                       pr_debug("Started T2 (retransmit)\n");
-               }
-       }
-}
-
-static void nfc_shdlc_connect_timeout(unsigned long data)
-{
-       struct nfc_shdlc *shdlc = (struct nfc_shdlc *)data;
-
-       pr_debug("\n");
-
-       queue_work(shdlc->sm_wq, &shdlc->sm_work);
-}
-
-static void nfc_shdlc_t1_timeout(unsigned long data)
-{
-       struct nfc_shdlc *shdlc = (struct nfc_shdlc *)data;
-
-       pr_debug("SoftIRQ: need to send ack\n");
-
-       queue_work(shdlc->sm_wq, &shdlc->sm_work);
-}
-
-static void nfc_shdlc_t2_timeout(unsigned long data)
-{
-       struct nfc_shdlc *shdlc = (struct nfc_shdlc *)data;
-
-       pr_debug("SoftIRQ: need to retransmit\n");
-
-       queue_work(shdlc->sm_wq, &shdlc->sm_work);
-}
-
-static void nfc_shdlc_sm_work(struct work_struct *work)
-{
-       struct nfc_shdlc *shdlc = container_of(work, struct nfc_shdlc, sm_work);
-       int r;
-
-       pr_debug("\n");
-
-       mutex_lock(&shdlc->state_mutex);
-
-       switch (shdlc->state) {
-       case SHDLC_DISCONNECTED:
-               skb_queue_purge(&shdlc->rcv_q);
-               skb_queue_purge(&shdlc->send_q);
-               skb_queue_purge(&shdlc->ack_pending_q);
-               break;
-       case SHDLC_CONNECTING:
-               if (shdlc->hard_fault) {
-                       nfc_shdlc_connect_complete(shdlc, shdlc->hard_fault);
-                       break;
-               }
-
-               if (shdlc->connect_tries++ < 5)
-                       r = nfc_shdlc_connect_initiate(shdlc);
-               else
-                       r = -ETIME;
-               if (r < 0)
-                       nfc_shdlc_connect_complete(shdlc, r);
-               else {
-                       mod_timer(&shdlc->connect_timer, jiffies +
-                                 msecs_to_jiffies(SHDLC_CONNECT_VALUE_MS));
-
-                       shdlc->state = SHDLC_NEGOCIATING;
-               }
-               break;
-       case SHDLC_NEGOCIATING:
-               if (timer_pending(&shdlc->connect_timer) == 0) {
-                       shdlc->state = SHDLC_CONNECTING;
-                       queue_work(shdlc->sm_wq, &shdlc->sm_work);
-               }
-
-               nfc_shdlc_handle_rcv_queue(shdlc);
-
-               if (shdlc->hard_fault) {
-                       nfc_shdlc_connect_complete(shdlc, shdlc->hard_fault);
-                       break;
-               }
-               break;
-       case SHDLC_CONNECTED:
-               nfc_shdlc_handle_rcv_queue(shdlc);
-               nfc_shdlc_handle_send_queue(shdlc);
-
-               if (shdlc->t1_active && timer_pending(&shdlc->t1_timer) == 0) {
-                       pr_debug
-                           ("Handle T1(send ack) elapsed (T1 now inactive)\n");
-
-                       shdlc->t1_active = false;
-                       r = nfc_shdlc_send_s_frame(shdlc, S_FRAME_RR,
-                                                  shdlc->nr);
-                       if (r < 0)
-                               shdlc->hard_fault = r;
-               }
-
-               if (shdlc->t2_active && timer_pending(&shdlc->t2_timer) == 0) {
-                       pr_debug
-                           ("Handle T2(retransmit) elapsed (T2 inactive)\n");
-
-                       shdlc->t2_active = false;
-
-                       nfc_shdlc_requeue_ack_pending(shdlc);
-                       nfc_shdlc_handle_send_queue(shdlc);
-               }
-
-               if (shdlc->hard_fault) {
-                       nfc_hci_driver_failure(shdlc->hdev, shdlc->hard_fault);
-               }
-               break;
-       default:
-               break;
-       }
-       mutex_unlock(&shdlc->state_mutex);
-}
-
-/*
- * Called from syscall context to establish shdlc link. Sleeps until
- * link is ready or failure.
- */
-static int nfc_shdlc_connect(struct nfc_shdlc *shdlc)
-{
-       DECLARE_WAIT_QUEUE_HEAD_ONSTACK(connect_wq);
-
-       pr_debug("\n");
-
-       mutex_lock(&shdlc->state_mutex);
-
-       shdlc->state = SHDLC_CONNECTING;
-       shdlc->connect_wq = &connect_wq;
-       shdlc->connect_tries = 0;
-       shdlc->connect_result = 1;
-
-       mutex_unlock(&shdlc->state_mutex);
-
-       queue_work(shdlc->sm_wq, &shdlc->sm_work);
-
-       wait_event(connect_wq, shdlc->connect_result != 1);
-
-       return shdlc->connect_result;
-}
-
-static void nfc_shdlc_disconnect(struct nfc_shdlc *shdlc)
-{
-       pr_debug("\n");
-
-       mutex_lock(&shdlc->state_mutex);
-
-       shdlc->state = SHDLC_DISCONNECTED;
-
-       mutex_unlock(&shdlc->state_mutex);
-
-       queue_work(shdlc->sm_wq, &shdlc->sm_work);
-}
-
-/*
- * Receive an incoming shdlc frame. Frame has already been crc-validated.
- * skb contains only LLC header and payload.
- * If skb == NULL, it is a notification that the link below is dead.
- */
-void nfc_shdlc_recv_frame(struct nfc_shdlc *shdlc, struct sk_buff *skb)
-{
-       if (skb == NULL) {
-               pr_err("NULL Frame -> link is dead\n");
-               shdlc->hard_fault = -EREMOTEIO;
-       } else {
-               SHDLC_DUMP_SKB("incoming frame", skb);
-               skb_queue_tail(&shdlc->rcv_q, skb);
-       }
-
-       queue_work(shdlc->sm_wq, &shdlc->sm_work);
-}
-EXPORT_SYMBOL(nfc_shdlc_recv_frame);
-
-static int nfc_shdlc_open(struct nfc_hci_dev *hdev)
-{
-       struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-       int r;
-
-       pr_debug("\n");
-
-       if (shdlc->ops->open) {
-               r = shdlc->ops->open(shdlc);
-               if (r < 0)
-                       return r;
-       }
-
-       r = nfc_shdlc_connect(shdlc);
-       if (r < 0 && shdlc->ops->close)
-               shdlc->ops->close(shdlc);
-
-       return r;
-}
-
-static void nfc_shdlc_close(struct nfc_hci_dev *hdev)
-{
-       struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
-       pr_debug("\n");
-
-       nfc_shdlc_disconnect(shdlc);
-
-       if (shdlc->ops->close)
-               shdlc->ops->close(shdlc);
-}
-
-static int nfc_shdlc_hci_ready(struct nfc_hci_dev *hdev)
-{
-       struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-       int r = 0;
-
-       pr_debug("\n");
-
-       if (shdlc->ops->hci_ready)
-               r = shdlc->ops->hci_ready(shdlc);
-
-       return r;
-}
-
-static int nfc_shdlc_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb)
-{
-       struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
-       SHDLC_DUMP_SKB("queuing HCP packet to shdlc", skb);
-
-       skb_queue_tail(&shdlc->send_q, skb);
-
-       queue_work(shdlc->sm_wq, &shdlc->sm_work);
-
-       return 0;
-}
-
-static int nfc_shdlc_start_poll(struct nfc_hci_dev *hdev,
-                               u32 im_protocols, u32 tm_protocols)
-{
-       struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
-       pr_debug("\n");
-
-       if (shdlc->ops->start_poll)
-               return shdlc->ops->start_poll(shdlc,
-                                             im_protocols, tm_protocols);
-
-       return 0;
-}
-
-static int nfc_shdlc_target_from_gate(struct nfc_hci_dev *hdev, u8 gate,
-                                     struct nfc_target *target)
-{
-       struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
-       if (shdlc->ops->target_from_gate)
-               return shdlc->ops->target_from_gate(shdlc, gate, target);
-
-       return -EPERM;
-}
-
-static int nfc_shdlc_complete_target_discovered(struct nfc_hci_dev *hdev,
-                                               u8 gate,
-                                               struct nfc_target *target)
-{
-       struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
-       pr_debug("\n");
-
-       if (shdlc->ops->complete_target_discovered)
-               return shdlc->ops->complete_target_discovered(shdlc, gate,
-                                                             target);
-
-       return 0;
-}
-
-static int nfc_shdlc_data_exchange(struct nfc_hci_dev *hdev,
-                                  struct nfc_target *target,
-                                  struct sk_buff *skb,
-                                  struct sk_buff **res_skb)
-{
-       struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
-       if (shdlc->ops->data_exchange)
-               return shdlc->ops->data_exchange(shdlc, target, skb, res_skb);
-
-       return -EPERM;
-}
-
-static int nfc_shdlc_check_presence(struct nfc_hci_dev *hdev,
-                                   struct nfc_target *target)
-{
-       struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
-       if (shdlc->ops->check_presence)
-               return shdlc->ops->check_presence(shdlc, target);
-
-       return 0;
-}
-
-static struct nfc_hci_ops shdlc_ops = {
-       .open = nfc_shdlc_open,
-       .close = nfc_shdlc_close,
-       .hci_ready = nfc_shdlc_hci_ready,
-       .xmit = nfc_shdlc_xmit,
-       .start_poll = nfc_shdlc_start_poll,
-       .target_from_gate = nfc_shdlc_target_from_gate,
-       .complete_target_discovered = nfc_shdlc_complete_target_discovered,
-       .data_exchange = nfc_shdlc_data_exchange,
-       .check_presence = nfc_shdlc_check_presence,
-};
-
-struct nfc_shdlc *nfc_shdlc_allocate(struct nfc_shdlc_ops *ops,
-                                    struct nfc_hci_init_data *init_data,
-                                    u32 protocols,
-                                    int tx_headroom, int tx_tailroom,
-                                    int max_link_payload, const char *devname)
-{
-       struct nfc_shdlc *shdlc;
-       int r;
-       char name[32];
-
-       if (ops->xmit == NULL)
-               return NULL;
-
-       shdlc = kzalloc(sizeof(struct nfc_shdlc), GFP_KERNEL);
-       if (shdlc == NULL)
-               return NULL;
-
-       mutex_init(&shdlc->state_mutex);
-       shdlc->ops = ops;
-       shdlc->state = SHDLC_DISCONNECTED;
-
-       init_timer(&shdlc->connect_timer);
-       shdlc->connect_timer.data = (unsigned long)shdlc;
-       shdlc->connect_timer.function = nfc_shdlc_connect_timeout;
-
-       init_timer(&shdlc->t1_timer);
-       shdlc->t1_timer.data = (unsigned long)shdlc;
-       shdlc->t1_timer.function = nfc_shdlc_t1_timeout;
-
-       init_timer(&shdlc->t2_timer);
-       shdlc->t2_timer.data = (unsigned long)shdlc;
-       shdlc->t2_timer.function = nfc_shdlc_t2_timeout;
-
-       shdlc->w = SHDLC_MAX_WINDOW;
-       shdlc->srej_support = SHDLC_SREJ_SUPPORT;
-
-       skb_queue_head_init(&shdlc->rcv_q);
-       skb_queue_head_init(&shdlc->send_q);
-       skb_queue_head_init(&shdlc->ack_pending_q);
-
-       INIT_WORK(&shdlc->sm_work, nfc_shdlc_sm_work);
-       snprintf(name, sizeof(name), "%s_shdlc_sm_wq", devname);
-       shdlc->sm_wq = alloc_workqueue(name, WQ_NON_REENTRANT | WQ_UNBOUND |
-                                      WQ_MEM_RECLAIM, 1);
-       if (shdlc->sm_wq == NULL)
-               goto err_allocwq;
-
-       shdlc->client_headroom = tx_headroom;
-       shdlc->client_tailroom = tx_tailroom;
-
-       shdlc->hdev = nfc_hci_allocate_device(&shdlc_ops, init_data, protocols,
-                                             tx_headroom + SHDLC_LLC_HEAD_ROOM,
-                                             tx_tailroom + SHDLC_LLC_TAIL_ROOM,
-                                             max_link_payload);
-       if (shdlc->hdev == NULL)
-               goto err_allocdev;
-
-       nfc_hci_set_clientdata(shdlc->hdev, shdlc);
-
-       r = nfc_hci_register_device(shdlc->hdev);
-       if (r < 0)
-               goto err_regdev;
-
-       return shdlc;
-
-err_regdev:
-       nfc_hci_free_device(shdlc->hdev);
-
-err_allocdev:
-       destroy_workqueue(shdlc->sm_wq);
-
-err_allocwq:
-       kfree(shdlc);
-
-       return NULL;
-}
-EXPORT_SYMBOL(nfc_shdlc_allocate);
-
-void nfc_shdlc_free(struct nfc_shdlc *shdlc)
-{
-       pr_debug("\n");
-
-       nfc_hci_unregister_device(shdlc->hdev);
-       nfc_hci_free_device(shdlc->hdev);
-
-       destroy_workqueue(shdlc->sm_wq);
-
-       skb_queue_purge(&shdlc->rcv_q);
-       skb_queue_purge(&shdlc->send_q);
-       skb_queue_purge(&shdlc->ack_pending_q);
-
-       kfree(shdlc);
-}
-EXPORT_SYMBOL(nfc_shdlc_free);
-
-void nfc_shdlc_set_clientdata(struct nfc_shdlc *shdlc, void *clientdata)
-{
-       pr_debug("\n");
-
-       shdlc->clientdata = clientdata;
-}
-EXPORT_SYMBOL(nfc_shdlc_set_clientdata);
-
-void *nfc_shdlc_get_clientdata(struct nfc_shdlc *shdlc)
-{
-       return shdlc->clientdata;
-}
-EXPORT_SYMBOL(nfc_shdlc_get_clientdata);
-
-struct nfc_hci_dev *nfc_shdlc_get_hci_dev(struct nfc_shdlc *shdlc)
-{
-       return shdlc->hdev;
-}
-EXPORT_SYMBOL(nfc_shdlc_get_hci_dev);
index fbf5e81..a1a41cd 100644 (file)
@@ -1,6 +1,6 @@
 config NFC_LLCP
-       depends on NFC && EXPERIMENTAL
-       bool "NFC LLCP support (EXPERIMENTAL)"
+       depends on NFC
+       bool "NFC LLCP support"
        default n
        help
         Say Y here if you want to build support for a kernel NFC LLCP
index 7941535..094f7e2 100644 (file)
@@ -117,6 +117,88 @@ u8 *nfc_llcp_build_tlv(u8 type, u8 *value, u8 value_length, u8 *tlv_length)
        return tlv;
 }
 
+struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdres_tlv(u8 tid, u8 sap)
+{
+       struct nfc_llcp_sdp_tlv *sdres;
+       u8 value[2];
+
+       sdres = kzalloc(sizeof(struct nfc_llcp_sdp_tlv), GFP_KERNEL);
+       if (sdres == NULL)
+               return NULL;
+
+       value[0] = tid;
+       value[1] = sap;
+
+       sdres->tlv = nfc_llcp_build_tlv(LLCP_TLV_SDRES, value, 2,
+                                       &sdres->tlv_len);
+       if (sdres->tlv == NULL) {
+               kfree(sdres);
+               return NULL;
+       }
+
+       sdres->tid = tid;
+       sdres->sap = sap;
+
+       INIT_HLIST_NODE(&sdres->node);
+
+       return sdres;
+}
+
+struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdreq_tlv(u8 tid, char *uri,
+                                                 size_t uri_len)
+{
+       struct nfc_llcp_sdp_tlv *sdreq;
+
+       pr_debug("uri: %s, len: %zu\n", uri, uri_len);
+
+       sdreq = kzalloc(sizeof(struct nfc_llcp_sdp_tlv), GFP_KERNEL);
+       if (sdreq == NULL)
+               return NULL;
+
+       sdreq->tlv_len = uri_len + 3;
+
+       if (uri[uri_len - 1] == 0)
+               sdreq->tlv_len--;
+
+       sdreq->tlv = kzalloc(sdreq->tlv_len + 1, GFP_KERNEL);
+       if (sdreq->tlv == NULL) {
+               kfree(sdreq);
+               return NULL;
+       }
+
+       sdreq->tlv[0] = LLCP_TLV_SDREQ;
+       sdreq->tlv[1] = sdreq->tlv_len - 2;
+       sdreq->tlv[2] = tid;
+
+       sdreq->tid = tid;
+       sdreq->uri = sdreq->tlv + 3;
+       memcpy(sdreq->uri, uri, uri_len);
+
+       sdreq->time = jiffies;
+
+       INIT_HLIST_NODE(&sdreq->node);
+
+       return sdreq;
+}
+
+void nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp)
+{
+       kfree(sdp->tlv);
+       kfree(sdp);
+}
+
+void nfc_llcp_free_sdp_tlv_list(struct hlist_head *head)
+{
+       struct nfc_llcp_sdp_tlv *sdp;
+       struct hlist_node *n;
+
+       hlist_for_each_entry_safe(sdp, n, head, node) {
+               hlist_del(&sdp->node);
+
+               nfc_llcp_free_sdp_tlv(sdp);
+       }
+}
+
 int nfc_llcp_parse_gb_tlv(struct nfc_llcp_local *local,
                          u8 *tlv_array, u16 tlv_array_len)
 {
@@ -184,10 +266,10 @@ int nfc_llcp_parse_connection_tlv(struct nfc_llcp_sock *sock,
 
                switch (type) {
                case LLCP_TLV_MIUX:
-                       sock->miu = llcp_tlv_miux(tlv) + 128;
+                       sock->remote_miu = llcp_tlv_miux(tlv) + 128;
                        break;
                case LLCP_TLV_RW:
-                       sock->rw = llcp_tlv_rw(tlv);
+                       sock->remote_rw = llcp_tlv_rw(tlv);
                        break;
                case LLCP_TLV_SN:
                        break;
@@ -200,7 +282,8 @@ int nfc_llcp_parse_connection_tlv(struct nfc_llcp_sock *sock,
                tlv += length + 2;
        }
 
-       pr_debug("sock %p rw %d miu %d\n", sock, sock->rw, sock->miu);
+       pr_debug("sock %p rw %d miu %d\n", sock,
+                sock->remote_rw, sock->remote_miu);
 
        return 0;
 }
@@ -304,6 +387,8 @@ int nfc_llcp_send_symm(struct nfc_dev *dev)
 
        skb = llcp_add_header(skb, 0, 0, LLCP_PDU_SYMM);
 
+       __net_timestamp(skb);
+
        nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_TX);
 
        return nfc_data_exchange(dev, local->target_idx, skb,
@@ -317,9 +402,8 @@ int nfc_llcp_send_connect(struct nfc_llcp_sock *sock)
        u8 *service_name_tlv = NULL, service_name_tlv_length;
        u8 *miux_tlv = NULL, miux_tlv_length;
        u8 *rw_tlv = NULL, rw_tlv_length, rw;
-       __be16 miux;
        int err;
-       u16 size = 0;
+       u16 size = 0, miux;
 
        pr_debug("Sending CONNECT\n");
 
@@ -335,12 +419,15 @@ int nfc_llcp_send_connect(struct nfc_llcp_sock *sock)
                size += service_name_tlv_length;
        }
 
-       miux = cpu_to_be16(LLCP_MAX_MIUX);
+       /* If the socket parameters are not set, use the local ones */
+       miux = be16_to_cpu(sock->miux) > LLCP_MAX_MIUX ?
+               local->miux : sock->miux;
+       rw = sock->rw > LLCP_MAX_RW ? local->rw : sock->rw;
+
        miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0,
                                      &miux_tlv_length);
        size += miux_tlv_length;
 
-       rw = LLCP_MAX_RW;
        rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length);
        size += rw_tlv_length;
 
@@ -379,9 +466,8 @@ int nfc_llcp_send_cc(struct nfc_llcp_sock *sock)
        struct sk_buff *skb;
        u8 *miux_tlv = NULL, miux_tlv_length;
        u8 *rw_tlv = NULL, rw_tlv_length, rw;
-       __be16 miux;
        int err;
-       u16 size = 0;
+       u16 size = 0, miux;
 
        pr_debug("Sending CC\n");
 
@@ -389,12 +475,15 @@ int nfc_llcp_send_cc(struct nfc_llcp_sock *sock)
        if (local == NULL)
                return -ENODEV;
 
-       miux = cpu_to_be16(LLCP_MAX_MIUX);
+       /* If the socket parameters are not set, use the local ones */
+       miux = be16_to_cpu(sock->miux) > LLCP_MAX_MIUX ?
+               local->miux : sock->miux;
+       rw = sock->rw > LLCP_MAX_RW ? local->rw : sock->rw;
+
        miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0,
                                      &miux_tlv_length);
        size += miux_tlv_length;
 
-       rw = LLCP_MAX_RW;
        rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length);
        size += rw_tlv_length;
 
@@ -420,48 +509,90 @@ error_tlv:
        return err;
 }
 
-int nfc_llcp_send_snl(struct nfc_llcp_local *local, u8 tid, u8 sap)
+static struct sk_buff *nfc_llcp_allocate_snl(struct nfc_llcp_local *local,
+                                            size_t tlv_length)
 {
        struct sk_buff *skb;
        struct nfc_dev *dev;
-       u8 *sdres_tlv = NULL, sdres_tlv_length, sdres[2];
        u16 size = 0;
 
-       pr_debug("Sending SNL tid 0x%x sap 0x%x\n", tid, sap);
-
        if (local == NULL)
-               return -ENODEV;
+               return ERR_PTR(-ENODEV);
 
        dev = local->dev;
        if (dev == NULL)
-               return -ENODEV;
-
-       sdres[0] = tid;
-       sdres[1] = sap;
-       sdres_tlv = nfc_llcp_build_tlv(LLCP_TLV_SDRES, sdres, 0,
-                                      &sdres_tlv_length);
-       if (sdres_tlv == NULL)
-               return -ENOMEM;
+               return ERR_PTR(-ENODEV);
 
        size += LLCP_HEADER_SIZE;
        size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE;
-       size += sdres_tlv_length;
+       size += tlv_length;
 
        skb = alloc_skb(size, GFP_KERNEL);
-       if (skb == NULL) {
-               kfree(sdres_tlv);
-               return -ENOMEM;
-       }
+       if (skb == NULL)
+               return ERR_PTR(-ENOMEM);
 
        skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE);
 
        skb = llcp_add_header(skb, LLCP_SAP_SDP, LLCP_SAP_SDP, LLCP_PDU_SNL);
 
-       memcpy(skb_put(skb, sdres_tlv_length), sdres_tlv, sdres_tlv_length);
+       return skb;
+}
+
+int nfc_llcp_send_snl_sdres(struct nfc_llcp_local *local,
+                           struct hlist_head *tlv_list, size_t tlvs_len)
+{
+       struct nfc_llcp_sdp_tlv *sdp;
+       struct hlist_node *n;
+       struct sk_buff *skb;
+
+       skb = nfc_llcp_allocate_snl(local, tlvs_len);
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       hlist_for_each_entry_safe(sdp, n, tlv_list, node) {
+               memcpy(skb_put(skb, sdp->tlv_len), sdp->tlv, sdp->tlv_len);
+
+               hlist_del(&sdp->node);
+
+               nfc_llcp_free_sdp_tlv(sdp);
+       }
 
        skb_queue_tail(&local->tx_queue, skb);
 
-       kfree(sdres_tlv);
+       return 0;
+}
+
+int nfc_llcp_send_snl_sdreq(struct nfc_llcp_local *local,
+                           struct hlist_head *tlv_list, size_t tlvs_len)
+{
+       struct nfc_llcp_sdp_tlv *sdreq;
+       struct hlist_node *n;
+       struct sk_buff *skb;
+
+       skb = nfc_llcp_allocate_snl(local, tlvs_len);
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       mutex_lock(&local->sdreq_lock);
+
+       if (hlist_empty(&local->pending_sdreqs))
+               mod_timer(&local->sdreq_timer,
+                         jiffies + msecs_to_jiffies(3 * local->remote_lto));
+
+       hlist_for_each_entry_safe(sdreq, n, tlv_list, node) {
+               pr_debug("tid %d for %s\n", sdreq->tid, sdreq->uri);
+
+               memcpy(skb_put(skb, sdreq->tlv_len), sdreq->tlv,
+                      sdreq->tlv_len);
+
+               hlist_del(&sdreq->node);
+
+               hlist_add_head(&sdreq->node, &local->pending_sdreqs);
+       }
+
+       mutex_unlock(&local->sdreq_lock);
+
+       skb_queue_tail(&local->tx_queue, skb);
 
        return 0;
 }
@@ -527,6 +658,7 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
        struct nfc_llcp_local *local;
        size_t frag_len = 0, remaining_len;
        u8 *msg_data, *msg_ptr;
+       u16 remote_miu;
 
        pr_debug("Send I frame len %zd\n", len);
 
@@ -534,6 +666,23 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
        if (local == NULL)
                return -ENODEV;
 
+       /* Remote is ready but has not acknowledged our frames */
+       if((sock->remote_ready &&
+           skb_queue_len(&sock->tx_pending_queue) >= sock->remote_rw &&
+           skb_queue_len(&sock->tx_queue) >= 2 * sock->remote_rw)) {
+               pr_err("Pending queue is full %d frames\n",
+                      skb_queue_len(&sock->tx_pending_queue));
+               return -ENOBUFS;
+       }
+
+       /* Remote is not ready and we've been queueing enough frames */
+       if ((!sock->remote_ready &&
+            skb_queue_len(&sock->tx_queue) >= 2 * sock->remote_rw)) {
+               pr_err("Tx queue is full %d frames\n",
+                      skb_queue_len(&sock->tx_queue));
+               return -ENOBUFS;
+       }
+
        msg_data = kzalloc(len, GFP_KERNEL);
        if (msg_data == NULL)
                return -ENOMEM;
@@ -546,9 +695,11 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
        remaining_len = len;
        msg_ptr = msg_data;
 
-       while (remaining_len > 0) {
+       do {
+               remote_miu = sock->remote_miu > LLCP_MAX_MIU ?
+                               local->remote_miu : sock->remote_miu;
 
-               frag_len = min_t(size_t, sock->miu, remaining_len);
+               frag_len = min_t(size_t, remote_miu, remaining_len);
 
                pr_debug("Fragment %zd bytes remaining %zd",
                         frag_len, remaining_len);
@@ -560,7 +711,8 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
 
                skb_put(pdu, LLCP_SEQUENCE_SIZE);
 
-               memcpy(skb_put(pdu, frag_len), msg_ptr, frag_len);
+               if (likely(frag_len > 0))
+                       memcpy(skb_put(pdu, frag_len), msg_ptr, frag_len);
 
                skb_queue_tail(&sock->tx_queue, pdu);
 
@@ -572,7 +724,7 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
 
                remaining_len -= frag_len;
                msg_ptr += frag_len;
-       }
+       } while (remaining_len > 0);
 
        kfree(msg_data);
 
@@ -585,7 +737,8 @@ int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap,
        struct sk_buff *pdu;
        struct nfc_llcp_local *local;
        size_t frag_len = 0, remaining_len;
-       u8 *msg_ptr;
+       u8 *msg_ptr, *msg_data;
+       u16 remote_miu;
        int err;
 
        pr_debug("Send UI frame len %zd\n", len);
@@ -594,12 +747,23 @@ int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap,
        if (local == NULL)
                return -ENODEV;
 
+       msg_data = kzalloc(len, GFP_KERNEL);
+       if (msg_data == NULL)
+               return -ENOMEM;
+
+       if (memcpy_fromiovec(msg_data, msg->msg_iov, len)) {
+               kfree(msg_data);
+               return -EFAULT;
+       }
+
        remaining_len = len;
-       msg_ptr = (u8 *) msg->msg_iov;
+       msg_ptr = msg_data;
 
-       while (remaining_len > 0) {
+       do {
+               remote_miu = sock->remote_miu > LLCP_MAX_MIU ?
+                               local->remote_miu : sock->remote_miu;
 
-               frag_len = min_t(size_t, sock->miu, remaining_len);
+               frag_len = min_t(size_t, remote_miu, remaining_len);
 
                pr_debug("Fragment %zd bytes remaining %zd",
                         frag_len, remaining_len);
@@ -613,14 +777,17 @@ int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap,
 
                pdu = llcp_add_header(pdu, dsap, ssap, LLCP_PDU_UI);
 
-               memcpy(skb_put(pdu, frag_len), msg_ptr, frag_len);
+               if (likely(frag_len > 0))
+                       memcpy(skb_put(pdu, frag_len), msg_ptr, frag_len);
 
                /* No need to check for the peer RW for UI frames */
                skb_queue_tail(&local->tx_queue, pdu);
 
                remaining_len -= frag_len;
                msg_ptr += frag_len;
-       }
+       } while (remaining_len > 0);
+
+       kfree(msg_data);
 
        return len;
 }
index 6ae2e68..e5dbec1 100644 (file)
@@ -31,6 +31,8 @@ static u8 llcp_magic[3] = {0x46, 0x66, 0x6d};
 
 static struct list_head llcp_devices;
 
+static void nfc_llcp_rx_skb(struct nfc_llcp_local *local, struct sk_buff *skb);
+
 void nfc_llcp_sock_link(struct llcp_sock_list *l, struct sock *sk)
 {
        write_lock(&l->lock);
@@ -45,12 +47,44 @@ void nfc_llcp_sock_unlink(struct llcp_sock_list *l, struct sock *sk)
        write_unlock(&l->lock);
 }
 
-static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool listen)
+void nfc_llcp_socket_remote_param_init(struct nfc_llcp_sock *sock)
+{
+       sock->remote_rw = LLCP_DEFAULT_RW;
+       sock->remote_miu = LLCP_MAX_MIU + 1;
+}
+
+static void nfc_llcp_socket_purge(struct nfc_llcp_sock *sock)
+{
+       struct nfc_llcp_local *local = sock->local;
+       struct sk_buff *s, *tmp;
+
+       pr_debug("%p\n", &sock->sk);
+
+       skb_queue_purge(&sock->tx_queue);
+       skb_queue_purge(&sock->tx_pending_queue);
+
+       if (local == NULL)
+               return;
+
+       /* Search for local pending SKBs that are related to this socket */
+       skb_queue_walk_safe(&local->tx_queue, s, tmp) {
+               if (s->sk != &sock->sk)
+                       continue;
+
+               skb_unlink(s, &local->tx_queue);
+               kfree_skb(s);
+       }
+}
+
+static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool device,
+                                   int err)
 {
        struct sock *sk;
        struct hlist_node *node, *tmp;
        struct nfc_llcp_sock *llcp_sock;
 
+       skb_queue_purge(&local->tx_queue);
+
        write_lock(&local->sockets.lock);
 
        sk_for_each_safe(sk, node, tmp, &local->sockets.head) {
@@ -58,6 +92,8 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool listen)
 
                bh_lock_sock(sk);
 
+               nfc_llcp_socket_purge(llcp_sock);
+
                if (sk->sk_state == LLCP_CONNECTED)
                        nfc_put_device(llcp_sock->dev);
 
@@ -65,46 +101,59 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool listen)
                        struct nfc_llcp_sock *lsk, *n;
                        struct sock *accept_sk;
 
-                       list_for_each_entry_safe(lsk, n, &llcp_sock->accept_queue,
+                       list_for_each_entry_safe(lsk, n,
+                                                &llcp_sock->accept_queue,
                                                 accept_queue) {
                                accept_sk = &lsk->sk;
                                bh_lock_sock(accept_sk);
 
                                nfc_llcp_accept_unlink(accept_sk);
 
+                               if (err)
+                                       accept_sk->sk_err = err;
                                accept_sk->sk_state = LLCP_CLOSED;
+                               accept_sk->sk_state_change(sk);
 
                                bh_unlock_sock(accept_sk);
-
-                               sock_orphan(accept_sk);
                        }
-
-                       if (listen == true) {
-                               bh_unlock_sock(sk);
-                               continue;
-                       }
-               }
-
-               /*
-                * If we have a connection less socket bound, we keep it alive
-                * if the device is still present.
-                */
-               if (sk->sk_state == LLCP_BOUND && sk->sk_type == SOCK_DGRAM &&
-                   listen == true) {
-                       bh_unlock_sock(sk);
-                       continue;
                }
 
+               if (err)
+                       sk->sk_err = err;
                sk->sk_state = LLCP_CLOSED;
+               sk->sk_state_change(sk);
 
                bh_unlock_sock(sk);
 
-               sock_orphan(sk);
-
                sk_del_node_init(sk);
        }
 
        write_unlock(&local->sockets.lock);
+
+       /* If we still have a device, we keep the RAW sockets alive */
+       if (device == true)
+               return;
+
+       write_lock(&local->raw_sockets.lock);
+
+       sk_for_each_safe(sk, tmp, &local->raw_sockets.head) {
+               llcp_sock = nfc_llcp_sock(sk);
+
+               bh_lock_sock(sk);
+
+               nfc_llcp_socket_purge(llcp_sock);
+
+               if (err)
+                       sk->sk_err = err;
+               sk->sk_state = LLCP_CLOSED;
+               sk->sk_state_change(sk);
+
+               bh_unlock_sock(sk);
+
+               sk_del_node_init(sk);
+       }
+
+       write_unlock(&local->raw_sockets.lock);
 }
 
 struct nfc_llcp_local *nfc_llcp_local_get(struct nfc_llcp_local *local)
@@ -114,20 +163,28 @@ struct nfc_llcp_local *nfc_llcp_local_get(struct nfc_llcp_local *local)
        return local;
 }
 
-static void local_release(struct kref *ref)
+static void local_cleanup(struct nfc_llcp_local *local)
 {
-       struct nfc_llcp_local *local;
-
-       local = container_of(ref, struct nfc_llcp_local, ref);
-
-       list_del(&local->list);
-       nfc_llcp_socket_release(local, false);
+       nfc_llcp_socket_release(local, false, ENXIO);
        del_timer_sync(&local->link_timer);
        skb_queue_purge(&local->tx_queue);
        cancel_work_sync(&local->tx_work);
        cancel_work_sync(&local->rx_work);
        cancel_work_sync(&local->timeout_work);
        kfree_skb(local->rx_pending);
+       del_timer_sync(&local->sdreq_timer);
+       cancel_work_sync(&local->sdreq_timeout_work);
+       nfc_llcp_free_sdp_tlv_list(&local->pending_sdreqs);
+}
+
+static void local_release(struct kref *ref)
+{
+       struct nfc_llcp_local *local;
+
+       local = container_of(ref, struct nfc_llcp_local, ref);
+
+       list_del(&local->list);
+       local_cleanup(local);
        kfree(local);
 }
 
@@ -193,7 +250,48 @@ static void nfc_llcp_symm_timer(unsigned long data)
 
        pr_err("SYMM timeout\n");
 
-       queue_work(system_nrt_wq, &local->timeout_work);
+       schedule_work(&local->timeout_work);
+}
+
+static void nfc_llcp_sdreq_timeout_work(struct work_struct *work)
+{
+       unsigned long time;
+       HLIST_HEAD(nl_sdres_list);
+       struct hlist_node *n;
+       struct nfc_llcp_sdp_tlv *sdp;
+       struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local,
+                                                   sdreq_timeout_work);
+
+       mutex_lock(&local->sdreq_lock);
+
+       time = jiffies - msecs_to_jiffies(3 * local->remote_lto);
+
+       hlist_for_each_entry_safe(sdp, n, &local->pending_sdreqs, node) {
+               if (time_after(sdp->time, time))
+                       continue;
+
+               sdp->sap = LLCP_SDP_UNBOUND;
+
+               hlist_del(&sdp->node);
+
+               hlist_add_head(&sdp->node, &nl_sdres_list);
+       }
+
+       if (!hlist_empty(&local->pending_sdreqs))
+               mod_timer(&local->sdreq_timer,
+                         jiffies + msecs_to_jiffies(3 * local->remote_lto));
+
+       mutex_unlock(&local->sdreq_lock);
+
+       if (!hlist_empty(&nl_sdres_list))
+               nfc_genl_llc_send_sdres(local->dev, &nl_sdres_list);
+}
+
+static void nfc_llcp_sdreq_timer(unsigned long data)
+{
+       struct nfc_llcp_local *local = (struct nfc_llcp_local *) data;
+
+       schedule_work(&local->sdreq_timeout_work);
 }
 
 struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev)
@@ -438,10 +536,9 @@ static u8 nfc_llcp_reserve_sdp_ssap(struct nfc_llcp_local *local)
 static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
 {
        u8 *gb_cur, *version_tlv, version, version_length;
-       u8 *lto_tlv, lto, lto_length;
+       u8 *lto_tlv, lto_length;
        u8 *wks_tlv, wks_length;
        u8 *miux_tlv, miux_length;
-       __be16 miux;
        u8 gb_len = 0;
        int ret = 0;
 
@@ -450,9 +547,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
                                         1, &version_length);
        gb_len += version_length;
 
-       /* 1500 ms */
-       lto = 150;
-       lto_tlv = nfc_llcp_build_tlv(LLCP_TLV_LTO, &lto, 1, &lto_length);
+       lto_tlv = nfc_llcp_build_tlv(LLCP_TLV_LTO, &local->lto, 1, &lto_length);
        gb_len += lto_length;
 
        pr_debug("Local wks 0x%lx\n", local->local_wks);
@@ -460,8 +555,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
                                     &wks_length);
        gb_len += wks_length;
 
-       miux = cpu_to_be16(LLCP_MAX_MIUX);
-       miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0,
+       miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0,
                                      &miux_length);
        gb_len += miux_length;
 
@@ -525,14 +619,13 @@ int nfc_llcp_set_remote_gb(struct nfc_dev *dev, u8 *gb, u8 gb_len)
                pr_err("No LLCP device\n");
                return -ENODEV;
        }
+       if (gb_len < 3)
+               return -EINVAL;
 
        memset(local->remote_gb, 0, NFC_MAX_GT_LEN);
        memcpy(local->remote_gb, gb, gb_len);
        local->remote_gb_len = gb_len;
 
-       if (local->remote_gb == NULL || local->remote_gb_len == 0)
-               return -ENODEV;
-
        if (memcmp(local->remote_gb, llcp_magic, 3)) {
                pr_err("MAC does not support LLCP\n");
                return -EINVAL;
@@ -631,6 +724,8 @@ static void nfc_llcp_tx_work(struct work_struct *work)
                if (llcp_sock == NULL && nfc_llcp_ptype(skb) == LLCP_PDU_I) {
                        nfc_llcp_send_symm(local->dev);
                } else {
+                       struct sk_buff *copy_skb = NULL;
+                       u8 ptype = nfc_llcp_ptype(skb);
                        int ret;
 
                        pr_debug("Sending pending skb\n");
@@ -638,22 +733,31 @@ static void nfc_llcp_tx_work(struct work_struct *work)
                                       DUMP_PREFIX_OFFSET, 16, 1,
                                       skb->data, skb->len, true);
 
+                       if (ptype == LLCP_PDU_I)
+                               copy_skb = skb_copy(skb, GFP_ATOMIC);
+
+                       __net_timestamp(skb);
+
                        nfc_llcp_send_to_raw_sock(local, skb,
                                                  NFC_LLCP_DIRECTION_TX);
 
                        ret = nfc_data_exchange(local->dev, local->target_idx,
                                                skb, nfc_llcp_recv, local);
 
-                       if (!ret && nfc_llcp_ptype(skb) == LLCP_PDU_I) {
-                               skb = skb_get(skb);
-                               skb_queue_tail(&llcp_sock->tx_pending_queue,
-                                              skb);
+                       if (ret) {
+                               kfree_skb(copy_skb);
+                               goto out;
                        }
+
+                       if (ptype == LLCP_PDU_I && copy_skb)
+                               skb_queue_tail(&llcp_sock->tx_pending_queue,
+                                              copy_skb);
                }
        } else {
                nfc_llcp_send_symm(local->dev);
        }
 
+out:
        mod_timer(&local->link_timer,
                  jiffies + msecs_to_jiffies(2 * local->remote_lto));
 }
@@ -736,8 +840,6 @@ static void nfc_llcp_recv_ui(struct nfc_llcp_local *local,
        ui_cb->dsap = dsap;
        ui_cb->ssap = ssap;
 
-       printk("%s %d %d\n", __func__, dsap, ssap);
-
        pr_debug("%d %d\n", dsap, ssap);
 
        /* We're looking for a bound socket, not a client one */
@@ -747,9 +849,14 @@ static void nfc_llcp_recv_ui(struct nfc_llcp_local *local,
 
        /* There is no sequence with UI frames */
        skb_pull(skb, LLCP_HEADER_SIZE);
-       if (sock_queue_rcv_skb(&llcp_sock->sk, skb)) {
-               pr_err("receive queue is full\n");
-               skb_queue_head(&llcp_sock->tx_backlog_queue, skb);
+       if (!sock_queue_rcv_skb(&llcp_sock->sk, skb)) {
+               /*
+                * UI frames will be freed from the socket layer, so we
+                * need to keep them alive until someone receives them.
+                */
+               skb_get(skb);
+       } else {
+               pr_err("Receive queue is full\n");
        }
 
        nfc_llcp_sock_put(llcp_sock);
@@ -829,7 +936,9 @@ static void nfc_llcp_recv_connect(struct nfc_llcp_local *local,
        new_sock = nfc_llcp_sock(new_sk);
        new_sock->dev = local->dev;
        new_sock->local = nfc_llcp_local_get(local);
-       new_sock->miu = local->remote_miu;
+       new_sock->rw = sock->rw;
+       new_sock->miux = sock->miux;
+       new_sock->remote_miu = local->remote_miu;
        new_sock->nfc_protocol = sock->nfc_protocol;
        new_sock->dsap = ssap;
        new_sock->target_idx = local->target_idx;
@@ -883,11 +992,11 @@ int nfc_llcp_queue_i_frames(struct nfc_llcp_sock *sock)
 
        pr_debug("Remote ready %d tx queue len %d remote rw %d",
                 sock->remote_ready, skb_queue_len(&sock->tx_pending_queue),
-                sock->rw);
+                sock->remote_rw);
 
        /* Try to queue some I frames for transmission */
        while (sock->remote_ready &&
-              skb_queue_len(&sock->tx_pending_queue) < sock->rw) {
+              skb_queue_len(&sock->tx_pending_queue) < sock->remote_rw) {
                struct sk_buff *pdu;
 
                pdu = skb_dequeue(&sock->tx_queue);
@@ -942,24 +1051,32 @@ static void nfc_llcp_recv_hdlc(struct nfc_llcp_local *local,
                        pr_err("Received out of sequence I PDU\n");
 
                skb_pull(skb, LLCP_HEADER_SIZE + LLCP_SEQUENCE_SIZE);
-               if (sock_queue_rcv_skb(&llcp_sock->sk, skb)) {
-                       pr_err("receive queue is full\n");
-                       skb_queue_head(&llcp_sock->tx_backlog_queue, skb);
+               if (!sock_queue_rcv_skb(&llcp_sock->sk, skb)) {
+                       /*
+                        * I frames will be freed from the socket layer, so we
+                        * need to keep them alive until someone receives them.
+                        */
+                       skb_get(skb);
+               } else {
+                       pr_err("Receive queue is full\n");
                }
        }
 
        /* Remove skbs from the pending queue */
        if (llcp_sock->send_ack_n != nr) {
                struct sk_buff *s, *tmp;
+               u8 n;
 
                llcp_sock->send_ack_n = nr;
 
                /* Remove and free all skbs until ns == nr */
                skb_queue_walk_safe(&llcp_sock->tx_pending_queue, s, tmp) {
+                       n = nfc_llcp_ns(s);
+
                        skb_unlink(s, &llcp_sock->tx_pending_queue);
                        kfree_skb(s);
 
-                       if (nfc_llcp_ns(s) == nr)
+                       if (n == nr)
                                break;
                }
 
@@ -993,6 +1110,12 @@ static void nfc_llcp_recv_disc(struct nfc_llcp_local *local,
        dsap = nfc_llcp_dsap(skb);
        ssap = nfc_llcp_ssap(skb);
 
+       if ((dsap == 0) && (ssap == 0)) {
+               pr_debug("Connection termination");
+               nfc_dep_link_down(local->dev);
+               return;
+       }
+
        llcp_sock = nfc_llcp_sock_get(local, dsap, ssap);
        if (llcp_sock == NULL) {
                nfc_llcp_send_dm(local, dsap, ssap, LLCP_DM_NOCONN);
@@ -1001,6 +1124,9 @@ static void nfc_llcp_recv_disc(struct nfc_llcp_local *local,
 
        sk = &llcp_sock->sk;
        lock_sock(sk);
+
+       nfc_llcp_socket_purge(llcp_sock);
+
        if (sk->sk_state == LLCP_CLOSED) {
                release_sock(sk);
                nfc_llcp_sock_put(llcp_sock);
@@ -1096,6 +1222,10 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
        u16 tlv_len, offset;
        char *service_name;
        size_t service_name_len;
+       struct nfc_llcp_sdp_tlv *sdp;
+       HLIST_HEAD(llc_sdres_list);
+       size_t sdres_tlvs_len;
+       HLIST_HEAD(nl_sdres_list);
 
        dsap = nfc_llcp_dsap(skb);
        ssap = nfc_llcp_ssap(skb);
@@ -1110,8 +1240,9 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
        tlv = &skb->data[LLCP_HEADER_SIZE];
        tlv_len = skb->len - LLCP_HEADER_SIZE;
        offset = 0;
+       sdres_tlvs_len = 0;
 
-       while(offset < tlv_len) {
+       while (offset < tlv_len) {
                type = tlv[0];
                length = tlv[1];
 
@@ -1127,14 +1258,14 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
                            !strncmp(service_name, "urn:nfc:sn:sdp",
                                     service_name_len)) {
                                sap = 1;
-                               goto send_snl;
+                               goto add_snl;
                        }
 
                        llcp_sock = nfc_llcp_sock_from_sn(local, service_name,
                                                          service_name_len);
                        if (!llcp_sock) {
                                sap = 0;
-                               goto send_snl;
+                               goto add_snl;
                        }
 
                        /*
@@ -1151,7 +1282,7 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
 
                                if (sap == LLCP_SAP_MAX) {
                                        sap = 0;
-                                       goto send_snl;
+                                       goto add_snl;
                                }
 
                                client_count =
@@ -1168,8 +1299,37 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
 
                        pr_debug("%p %d\n", llcp_sock, sap);
 
-               send_snl:
-                       nfc_llcp_send_snl(local, tid, sap);
+add_snl:
+                       sdp = nfc_llcp_build_sdres_tlv(tid, sap);
+                       if (sdp == NULL)
+                               goto exit;
+
+                       sdres_tlvs_len += sdp->tlv_len;
+                       hlist_add_head(&sdp->node, &llc_sdres_list);
+                       break;
+
+               case LLCP_TLV_SDRES:
+                       mutex_lock(&local->sdreq_lock);
+
+                       pr_debug("LLCP_TLV_SDRES: searching tid %d\n", tlv[2]);
+
+                       hlist_for_each_entry(sdp, &local->pending_sdreqs, node) {
+                               if (sdp->tid != tlv[2])
+                                       continue;
+
+                               sdp->sap = tlv[3];
+
+                               pr_debug("Found: uri=%s, sap=%d\n",
+                                        sdp->uri, sdp->sap);
+
+                               hlist_del(&sdp->node);
+
+                               hlist_add_head(&sdp->node, &nl_sdres_list);
+
+                               break;
+                       }
+
+                       mutex_unlock(&local->sdreq_lock);
                        break;
 
                default:
@@ -1180,21 +1340,63 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
                offset += length + 2;
                tlv += length + 2;
        }
+
+exit:
+       if (!hlist_empty(&nl_sdres_list))
+               nfc_genl_llc_send_sdres(local->dev, &nl_sdres_list);
+
+       if (!hlist_empty(&llc_sdres_list))
+               nfc_llcp_send_snl_sdres(local, &llc_sdres_list, sdres_tlvs_len);
 }
 
-static void nfc_llcp_rx_work(struct work_struct *work)
+static void nfc_llcp_recv_agf(struct nfc_llcp_local *local, struct sk_buff *skb)
 {
-       struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local,
-                                                   rx_work);
-       u8 dsap, ssap, ptype;
-       struct sk_buff *skb;
+       u8 ptype;
+       u16 pdu_len;
+       struct sk_buff *new_skb;
 
-       skb = local->rx_pending;
-       if (skb == NULL) {
-               pr_debug("No pending SKB\n");
+       if (skb->len <= LLCP_HEADER_SIZE) {
+               pr_err("Malformed AGF PDU\n");
                return;
        }
 
+       skb_pull(skb, LLCP_HEADER_SIZE);
+
+       while (skb->len > LLCP_AGF_PDU_HEADER_SIZE) {
+               pdu_len = skb->data[0] << 8 | skb->data[1];
+
+               skb_pull(skb, LLCP_AGF_PDU_HEADER_SIZE);
+
+               if (pdu_len < LLCP_HEADER_SIZE || pdu_len > skb->len) {
+                       pr_err("Malformed AGF PDU\n");
+                       return;
+               }
+
+               ptype = nfc_llcp_ptype(skb);
+
+               if (ptype == LLCP_PDU_SYMM || ptype == LLCP_PDU_AGF)
+                       goto next;
+
+               new_skb = nfc_alloc_recv_skb(pdu_len, GFP_KERNEL);
+               if (new_skb == NULL) {
+                       pr_err("Could not allocate PDU\n");
+                       return;
+               }
+
+               memcpy(skb_put(new_skb, pdu_len), skb->data, pdu_len);
+
+               nfc_llcp_rx_skb(local, new_skb);
+
+               kfree_skb(new_skb);
+next:
+               skb_pull(skb, pdu_len);
+       }
+}
+
+static void nfc_llcp_rx_skb(struct nfc_llcp_local *local, struct sk_buff *skb)
+{
+       u8 dsap, ssap, ptype;
+
        ptype = nfc_llcp_ptype(skb);
        dsap = nfc_llcp_dsap(skb);
        ssap = nfc_llcp_ssap(skb);
@@ -1205,8 +1407,6 @@ static void nfc_llcp_rx_work(struct work_struct *work)
                print_hex_dump(KERN_DEBUG, "LLCP Rx: ", DUMP_PREFIX_OFFSET,
                               16, 1, skb->data, skb->len, true);
 
-       nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_RX);
-
        switch (ptype) {
        case LLCP_PDU_SYMM:
                pr_debug("SYMM\n");
@@ -1249,13 +1449,43 @@ static void nfc_llcp_rx_work(struct work_struct *work)
                nfc_llcp_recv_hdlc(local, skb);
                break;
 
+       case LLCP_PDU_AGF:
+               pr_debug("AGF frame\n");
+               nfc_llcp_recv_agf(local, skb);
+               break;
+       }
+}
+
+static void nfc_llcp_rx_work(struct work_struct *work)
+{
+       struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local,
+                                                   rx_work);
+       struct sk_buff *skb;
+
+       skb = local->rx_pending;
+       if (skb == NULL) {
+               pr_debug("No pending SKB\n");
+               return;
        }
 
-       queue_work(system_nrt_wq, &local->tx_work);
+       __net_timestamp(skb);
+
+       nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_RX);
+
+       nfc_llcp_rx_skb(local, skb);
+
+       schedule_work(&local->tx_work);
        kfree_skb(local->rx_pending);
        local->rx_pending = NULL;
 }
 
+static void __nfc_llcp_recv(struct nfc_llcp_local *local, struct sk_buff *skb)
+{
+       local->rx_pending = skb;
+       del_timer(&local->link_timer);
+       schedule_work(&local->rx_work);
+}
+
 void nfc_llcp_recv(void *data, struct sk_buff *skb, int err)
 {
        struct nfc_llcp_local *local = (struct nfc_llcp_local *) data;
@@ -1266,9 +1496,7 @@ void nfc_llcp_recv(void *data, struct sk_buff *skb, int err)
                return;
        }
 
-       local->rx_pending = skb_get(skb);
-       del_timer(&local->link_timer);
-       queue_work(system_nrt_wq, &local->rx_work);
+       __nfc_llcp_recv(local, skb);
 }
 
 int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb)
@@ -1279,9 +1507,7 @@ int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb)
        if (local == NULL)
                return -ENODEV;
 
-       local->rx_pending = skb_get(skb);
-       del_timer(&local->link_timer);
-       queue_work(system_nrt_wq, &local->rx_work);
+       __nfc_llcp_recv(local, skb);
 
        return 0;
 }
@@ -1294,8 +1520,11 @@ void nfc_llcp_mac_is_down(struct nfc_dev *dev)
        if (local == NULL)
                return;
 
+       local->remote_miu = LLCP_DEFAULT_MIU;
+       local->remote_lto = LLCP_DEFAULT_LTO;
+
        /* Close and purge all existing sockets */
-       nfc_llcp_socket_release(local, true);
+       nfc_llcp_socket_release(local, true, 0);
 }
 
 void nfc_llcp_mac_is_up(struct nfc_dev *dev, u32 target_idx,
@@ -1316,7 +1545,7 @@ void nfc_llcp_mac_is_up(struct nfc_dev *dev, u32 target_idx,
        if (rf_mode == NFC_RF_INITIATOR) {
                pr_debug("Queueing Tx work\n");
 
-               queue_work(system_nrt_wq, &local->tx_work);
+               schedule_work(&local->tx_work);
        } else {
                mod_timer(&local->link_timer,
                          jiffies + msecs_to_jiffies(local->remote_lto));
@@ -1351,12 +1580,23 @@ int nfc_llcp_register_device(struct nfc_dev *ndev)
        rwlock_init(&local->connecting_sockets.lock);
        rwlock_init(&local->raw_sockets.lock);
 
+       local->lto = 150; /* 1500 ms */
+       local->rw = LLCP_MAX_RW;
+       local->miux = cpu_to_be16(LLCP_MAX_MIUX);
+
        nfc_llcp_build_gb(local);
 
        local->remote_miu = LLCP_DEFAULT_MIU;
        local->remote_lto = LLCP_DEFAULT_LTO;
 
-       list_add(&llcp_devices, &local->list);
+       mutex_init(&local->sdreq_lock);
+       INIT_HLIST_HEAD(&local->pending_sdreqs);
+       init_timer(&local->sdreq_timer);
+       local->sdreq_timer.data = (unsigned long) local;
+       local->sdreq_timer.function = nfc_llcp_sdreq_timer;
+       INIT_WORK(&local->sdreq_timeout_work, nfc_llcp_sdreq_timeout_work);
+
+       list_add(&local->list, &llcp_devices);
 
        return 0;
 }
@@ -1370,6 +1610,8 @@ void nfc_llcp_unregister_device(struct nfc_dev *dev)
                return;
        }
 
+       local_cleanup(local);
+
        nfc_llcp_local_put(local);
 }
 
index 276da3a..ff8c434 100644 (file)
@@ -31,6 +31,7 @@ enum llcp_state {
 #define LLCP_MAX_LTO  0xff
 #define LLCP_MAX_RW   15
 #define LLCP_MAX_MIUX 0x7ff
+#define LLCP_MAX_MIU (LLCP_MAX_MIUX + 128)
 
 #define LLCP_WKS_NUM_SAP   16
 #define LLCP_SDP_NUM_SAP   16
@@ -46,6 +47,19 @@ struct llcp_sock_list {
        rwlock_t          lock;
 };
 
+struct nfc_llcp_sdp_tlv {
+       u8 *tlv;
+       u8 tlv_len;
+
+       char *uri;
+       u8 tid;
+       u8 sap;
+
+       unsigned long time;
+
+       struct hlist_node node;
+};
+
 struct nfc_llcp_local {
        struct list_head list;
        struct nfc_dev *dev;
@@ -64,6 +78,9 @@ struct nfc_llcp_local {
        u32 target_idx;
        u8 rf_mode;
        u8 comm_mode;
+       u8 lto;
+       u8 rw;
+       __be16 miux;
        unsigned long local_wks;      /* Well known services */
        unsigned long local_sdp;      /* Local services  */
        unsigned long local_sap; /* Local SAPs, not available for discovery */
@@ -83,6 +100,12 @@ struct nfc_llcp_local {
        u8  remote_opt;
        u16 remote_wks;
 
+       struct mutex sdreq_lock;
+       struct hlist_head pending_sdreqs;
+       struct timer_list sdreq_timer;
+       struct work_struct sdreq_timeout_work;
+       u8 sdreq_next_tid;
+
        /* sockets array */
        struct llcp_sock_list sockets;
        struct llcp_sock_list connecting_sockets;
@@ -102,7 +125,12 @@ struct nfc_llcp_sock {
        char *service_name;
        size_t service_name_len;
        u8 rw;
-       u16 miu;
+       __be16 miux;
+
+
+       /* Remote link parameters */
+       u8 remote_rw;
+       u16 remote_miu;
 
        /* Link variables */
        u8 send_n;
@@ -118,7 +146,6 @@ struct nfc_llcp_sock {
 
        struct sk_buff_head tx_queue;
        struct sk_buff_head tx_pending_queue;
-       struct sk_buff_head tx_backlog_queue;
 
        struct list_head accept_queue;
        struct sock *parent;
@@ -136,6 +163,7 @@ struct nfc_llcp_ui_cb {
 
 #define LLCP_HEADER_SIZE   2
 #define LLCP_SEQUENCE_SIZE 1
+#define LLCP_AGF_PDU_HEADER_SIZE 2
 
 /* LLCP versions: 1.1 is 1.0 plus SDP */
 #define LLCP_VERSION_10 0x10
@@ -184,6 +212,7 @@ struct nfc_llcp_ui_cb {
 
 void nfc_llcp_sock_link(struct llcp_sock_list *l, struct sock *s);
 void nfc_llcp_sock_unlink(struct llcp_sock_list *l, struct sock *s);
+void nfc_llcp_socket_remote_param_init(struct nfc_llcp_sock *sock);
 struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev);
 struct nfc_llcp_local *nfc_llcp_local_get(struct nfc_llcp_local *local);
 int nfc_llcp_local_put(struct nfc_llcp_local *local);
@@ -211,12 +240,20 @@ int nfc_llcp_parse_connection_tlv(struct nfc_llcp_sock *sock,
 /* Commands API */
 void nfc_llcp_recv(void *data, struct sk_buff *skb, int err);
 u8 *nfc_llcp_build_tlv(u8 type, u8 *value, u8 value_length, u8 *tlv_length);
+struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdres_tlv(u8 tid, u8 sap);
+struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdreq_tlv(u8 tid, char *uri,
+                                                 size_t uri_len);
+void nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp);
+void nfc_llcp_free_sdp_tlv_list(struct hlist_head *sdp_head);
 void nfc_llcp_recv(void *data, struct sk_buff *skb, int err);
 int nfc_llcp_disconnect(struct nfc_llcp_sock *sock);
 int nfc_llcp_send_symm(struct nfc_dev *dev);
 int nfc_llcp_send_connect(struct nfc_llcp_sock *sock);
 int nfc_llcp_send_cc(struct nfc_llcp_sock *sock);
-int nfc_llcp_send_snl(struct nfc_llcp_local *local, u8 tid, u8 sap);
+int nfc_llcp_send_snl_sdres(struct nfc_llcp_local *local,
+                           struct hlist_head *tlv_list, size_t tlvs_len);
+int nfc_llcp_send_snl_sdreq(struct nfc_llcp_local *local,
+                           struct hlist_head *tlv_list, size_t tlvs_len);
 int nfc_llcp_send_dm(struct nfc_llcp_local *local, u8 ssap, u8 dsap, u8 reason);
 int nfc_llcp_send_disconnect(struct nfc_llcp_sock *sock);
 int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
index b2b1c9f..dfbe50a 100644 (file)
@@ -223,6 +223,156 @@ error:
        return ret;
 }
 
+static int nfc_llcp_setsockopt(struct socket *sock, int level, int optname,
+                              char __user *optval, unsigned int optlen)
+{
+       struct sock *sk = sock->sk;
+       struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk);
+       u32 opt;
+       int err = 0;
+
+       pr_debug("%p optname %d\n", sk, optname);
+
+       if (level != SOL_NFC)
+               return -ENOPROTOOPT;
+
+       lock_sock(sk);
+
+       switch (optname) {
+       case NFC_LLCP_RW:
+               if (sk->sk_state == LLCP_CONNECTED ||
+                   sk->sk_state == LLCP_BOUND ||
+                   sk->sk_state == LLCP_LISTEN) {
+                       err = -EINVAL;
+                       break;
+               }
+
+               if (get_user(opt, (u32 __user *) optval)) {
+                       err = -EFAULT;
+                       break;
+               }
+
+               if (opt > LLCP_MAX_RW) {
+                       err = -EINVAL;
+                       break;
+               }
+
+               llcp_sock->rw = (u8) opt;
+
+               break;
+
+       case NFC_LLCP_MIUX:
+               if (sk->sk_state == LLCP_CONNECTED ||
+                   sk->sk_state == LLCP_BOUND ||
+                   sk->sk_state == LLCP_LISTEN) {
+                       err = -EINVAL;
+                       break;
+               }
+
+               if (get_user(opt, (u32 __user *) optval)) {
+                       err = -EFAULT;
+                       break;
+               }
+
+               if (opt > LLCP_MAX_MIUX) {
+                       err = -EINVAL;
+                       break;
+               }
+
+               llcp_sock->miux = cpu_to_be16((u16) opt);
+
+               break;
+
+       default:
+               err = -ENOPROTOOPT;
+               break;
+       }
+
+       release_sock(sk);
+
+       pr_debug("%p rw %d miux %d\n", llcp_sock,
+                llcp_sock->rw, llcp_sock->miux);
+
+       return err;
+}
+
+static int nfc_llcp_getsockopt(struct socket *sock, int level, int optname,
+                              char __user *optval, int __user *optlen)
+{
+       struct nfc_llcp_local *local;
+       struct sock *sk = sock->sk;
+       struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk);
+       int len, err = 0;
+       u16 miux, remote_miu;
+       u8 rw;
+
+       pr_debug("%p optname %d\n", sk, optname);
+
+       if (level != SOL_NFC)
+               return -ENOPROTOOPT;
+
+       if (get_user(len, optlen))
+               return -EFAULT;
+
+       local = llcp_sock->local;
+       if (!local)
+               return -ENODEV;
+
+       len = min_t(u32, len, sizeof(u32));
+
+       lock_sock(sk);
+
+       switch (optname) {
+       case NFC_LLCP_RW:
+               rw = llcp_sock->rw > LLCP_MAX_RW ? local->rw : llcp_sock->rw;
+               if (put_user(rw, (u32 __user *) optval))
+                       err = -EFAULT;
+
+               break;
+
+       case NFC_LLCP_MIUX:
+               miux = be16_to_cpu(llcp_sock->miux) > LLCP_MAX_MIUX ?
+                       be16_to_cpu(local->miux) : be16_to_cpu(llcp_sock->miux);
+
+               if (put_user(miux, (u32 __user *) optval))
+                       err = -EFAULT;
+
+               break;
+
+       case NFC_LLCP_REMOTE_MIU:
+               remote_miu = llcp_sock->remote_miu > LLCP_MAX_MIU ?
+                               local->remote_miu : llcp_sock->remote_miu;
+
+               if (put_user(remote_miu, (u32 __user *) optval))
+                       err = -EFAULT;
+
+               break;
+
+       case NFC_LLCP_REMOTE_LTO:
+               if (put_user(local->remote_lto / 10, (u32 __user *) optval))
+                       err = -EFAULT;
+
+               break;
+
+       case NFC_LLCP_REMOTE_RW:
+               if (put_user(llcp_sock->remote_rw, (u32 __user *) optval))
+                       err = -EFAULT;
+
+               break;
+
+       default:
+               err = -ENOPROTOOPT;
+               break;
+       }
+
+       release_sock(sk);
+
+       if (put_user(len, optlen))
+               return -EFAULT;
+
+       return err;
+}
+
 void nfc_llcp_accept_unlink(struct sock *sk)
 {
        struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk);
@@ -270,7 +420,9 @@ struct sock *nfc_llcp_accept_dequeue(struct sock *parent,
                }
 
                if (sk->sk_state == LLCP_CONNECTED || !newsock) {
-                       nfc_llcp_accept_unlink(sk);
+                       list_del_init(&lsk->accept_queue);
+                       sock_put(sk);
+
                        if (newsock)
                                sock_graft(sk, newsock);
 
@@ -278,6 +430,8 @@ struct sock *nfc_llcp_accept_dequeue(struct sock *parent,
 
                        pr_debug("Returning sk state %d\n", sk->sk_state);
 
+                       sk_acceptq_removed(parent);
+
                        return sk;
                }
 
@@ -462,8 +616,6 @@ static int llcp_sock_release(struct socket *sock)
                        nfc_llcp_accept_unlink(accept_sk);
 
                        release_sock(accept_sk);
-
-                       sock_orphan(accept_sk);
                }
        }
 
@@ -545,7 +697,7 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr *_addr,
 
        llcp_sock->dev = dev;
        llcp_sock->local = nfc_llcp_local_get(local);
-       llcp_sock->miu = llcp_sock->local->remote_miu;
+       llcp_sock->remote_miu = llcp_sock->local->remote_miu;
        llcp_sock->ssap = nfc_llcp_get_local_ssap(local);
        if (llcp_sock->ssap == LLCP_SAP_MAX) {
                ret = -ENOMEM;
@@ -680,25 +832,27 @@ static int llcp_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
        copied = min_t(unsigned int, rlen, len);
 
        cskb = skb;
-       if (memcpy_toiovec(msg->msg_iov, cskb->data, copied)) {
+       if (skb_copy_datagram_iovec(cskb, 0, msg->msg_iov, copied)) {
                if (!(flags & MSG_PEEK))
                        skb_queue_head(&sk->sk_receive_queue, skb);
                return -EFAULT;
        }
 
+       sock_recv_timestamp(msg, sk, skb);
+
        if (sk->sk_type == SOCK_DGRAM && msg->msg_name) {
                struct nfc_llcp_ui_cb *ui_cb = nfc_llcp_ui_skb_cb(skb);
-               struct sockaddr_nfc_llcp sockaddr;
+               struct sockaddr_nfc_llcp *sockaddr =
+                       (struct sockaddr_nfc_llcp *) msg->msg_name;
 
-               pr_debug("Datagram socket %d %d\n", ui_cb->dsap, ui_cb->ssap);
+               msg->msg_namelen = sizeof(struct sockaddr_nfc_llcp);
 
-               sockaddr.sa_family = AF_NFC;
-               sockaddr.nfc_protocol = NFC_PROTO_NFC_DEP;
-               sockaddr.dsap = ui_cb->dsap;
-               sockaddr.ssap = ui_cb->ssap;
+               pr_debug("Datagram socket %d %d\n", ui_cb->dsap, ui_cb->ssap);
 
-               memcpy(msg->msg_name, &sockaddr, sizeof(sockaddr));
-               msg->msg_namelen = sizeof(sockaddr);
+               sockaddr->sa_family = AF_NFC;
+               sockaddr->nfc_protocol = NFC_PROTO_NFC_DEP;
+               sockaddr->dsap = ui_cb->dsap;
+               sockaddr->ssap = ui_cb->ssap;
        }
 
        /* Mark read part of skb as used */
@@ -741,8 +895,8 @@ static const struct proto_ops llcp_sock_ops = {
        .ioctl          = sock_no_ioctl,
        .listen         = llcp_sock_listen,
        .shutdown       = sock_no_shutdown,
-       .setsockopt     = sock_no_setsockopt,
-       .getsockopt     = sock_no_getsockopt,
+       .setsockopt     = nfc_llcp_setsockopt,
+       .getsockopt     = nfc_llcp_getsockopt,
        .sendmsg        = llcp_sock_sendmsg,
        .recvmsg        = llcp_sock_recvmsg,
        .mmap           = sock_no_mmap,
@@ -806,15 +960,15 @@ struct sock *nfc_llcp_sock_alloc(struct socket *sock, int type, gfp_t gfp)
 
        llcp_sock->ssap = 0;
        llcp_sock->dsap = LLCP_SAP_SDP;
-       llcp_sock->rw = LLCP_DEFAULT_RW;
-       llcp_sock->miu = LLCP_DEFAULT_MIU;
+       llcp_sock->rw = LLCP_MAX_RW + 1;
+       llcp_sock->miux = cpu_to_be16(LLCP_MAX_MIUX + 1);
        llcp_sock->send_n = llcp_sock->send_ack_n = 0;
        llcp_sock->recv_n = llcp_sock->recv_ack_n = 0;
        llcp_sock->remote_ready = 1;
        llcp_sock->reserved_ssap = LLCP_SAP_MAX;
+       nfc_llcp_socket_remote_param_init(llcp_sock);
        skb_queue_head_init(&llcp_sock->tx_queue);
        skb_queue_head_init(&llcp_sock->tx_pending_queue);
-       skb_queue_head_init(&llcp_sock->tx_backlog_queue);
        INIT_LIST_HEAD(&llcp_sock->accept_queue);
 
        if (sock != NULL)
@@ -829,7 +983,6 @@ void nfc_llcp_sock_free(struct nfc_llcp_sock *sock)
 
        skb_queue_purge(&sock->tx_queue);
        skb_queue_purge(&sock->tx_pending_queue);
-       skb_queue_purge(&sock->tx_backlog_queue);
 
        list_del_init(&sock->accept_queue);
 
index decdc49..6d69b5f 100644 (file)
@@ -1,6 +1,6 @@
 config NFC_NCI
-       depends on NFC && EXPERIMENTAL
-       tristate "NCI protocol support (EXPERIMENTAL)"
+       depends on NFC
+       tristate "NCI protocol support"
        default n
        help
          NCI (NFC Controller Interface) is a communication protocol between
index f8dd44d..06ed982 100644 (file)
@@ -576,7 +576,6 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev,
        }
 }
 
-
 static int nci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
                           __u8 comm_mode, __u8 *gb, size_t gb_len)
 {
@@ -658,6 +657,7 @@ static struct nfc_ops nci_nfc_ops = {
  */
 struct nci_dev *nci_allocate_device(struct nci_ops *ops,
                                    __u32 supported_protocols,
+                                   __u32 supported_se,
                                    int tx_headroom, int tx_tailroom)
 {
        struct nci_dev *ndev;
@@ -680,6 +680,7 @@ struct nci_dev *nci_allocate_device(struct nci_ops *ops,
 
        ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops,
                                            supported_protocols,
+                                           supported_se,
                                            tx_headroom + NCI_DATA_HDR_SIZE,
                                            tx_tailroom);
        if (!ndev->nfc_dev)
index 77f7eb5..5b645c0 100644 (file)
@@ -29,6 +29,8 @@
 
 #include "nfc.h"
 
+#include "llcp/llcp.h"
+
 static struct genl_multicast_group nfc_genl_event_mcgrp = {
        .name = NFC_GENL_MCAST_EVENT_NAME,
 };
@@ -51,6 +53,15 @@ static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = {
        [NFC_ATTR_DEVICE_POWERED] = { .type = NLA_U8 },
        [NFC_ATTR_IM_PROTOCOLS] = { .type = NLA_U32 },
        [NFC_ATTR_TM_PROTOCOLS] = { .type = NLA_U32 },
+       [NFC_ATTR_LLC_PARAM_LTO] = { .type = NLA_U8 },
+       [NFC_ATTR_LLC_PARAM_RW] = { .type = NLA_U8 },
+       [NFC_ATTR_LLC_PARAM_MIUX] = { .type = NLA_U16 },
+       [NFC_ATTR_LLC_SDP] = { .type = NLA_NESTED },
+};
+
+static const struct nla_policy nfc_sdp_genl_policy[NFC_SDP_ATTR_MAX + 1] = {
+       [NFC_SDP_ATTR_URI] = { .type = NLA_STRING },
+       [NFC_SDP_ATTR_SAP] = { .type = NLA_U8 },
 };
 
 static int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target,
@@ -346,6 +357,74 @@ free_msg:
        return -EMSGSIZE;
 }
 
+int nfc_genl_llc_send_sdres(struct nfc_dev *dev, struct hlist_head *sdres_list)
+{
+       struct sk_buff *msg;
+       struct nlattr *sdp_attr, *uri_attr;
+       struct nfc_llcp_sdp_tlv *sdres;
+       struct hlist_node *n;
+       void *hdr;
+       int rc = -EMSGSIZE;
+       int i;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+                         NFC_EVENT_LLC_SDRES);
+       if (!hdr)
+               goto free_msg;
+
+       if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
+               goto nla_put_failure;
+
+       sdp_attr = nla_nest_start(msg, NFC_ATTR_LLC_SDP);
+       if (sdp_attr == NULL) {
+               rc = -ENOMEM;
+               goto nla_put_failure;
+       }
+
+       i = 1;
+       hlist_for_each_entry_safe(sdres, n, sdres_list, node) {
+               pr_debug("uri: %s, sap: %d\n", sdres->uri, sdres->sap);
+
+               uri_attr = nla_nest_start(msg, i++);
+               if (uri_attr == NULL) {
+                       rc = -ENOMEM;
+                       goto nla_put_failure;
+               }
+
+               if (nla_put_u8(msg, NFC_SDP_ATTR_SAP, sdres->sap))
+                       goto nla_put_failure;
+
+               if (nla_put_string(msg, NFC_SDP_ATTR_URI, sdres->uri))
+                       goto nla_put_failure;
+
+               nla_nest_end(msg, uri_attr);
+
+               hlist_del(&sdres->node);
+
+               nfc_llcp_free_sdp_tlv(sdres);
+       }
+
+       nla_nest_end(msg, sdp_attr);
+
+       genlmsg_end(msg, hdr);
+
+       return genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_ATOMIC);
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+
+free_msg:
+       nlmsg_free(msg);
+
+       nfc_llcp_free_sdp_tlv_list(sdres_list);
+
+       return rc;
+}
+
 static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev,
                                u32 pid, u32 seq,
                                struct netlink_callback *cb,
@@ -364,6 +443,7 @@ static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev,
        if (nla_put_string(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)) ||
            nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
            nla_put_u32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols) ||
+           nla_put_u32(msg, NFC_ATTR_SE, dev->supported_se) ||
            nla_put_u8(msg, NFC_ATTR_DEVICE_POWERED, dev->dev_up) ||
            nla_put_u8(msg, NFC_ATTR_RF_MODE, dev->rf_mode))
                goto nla_put_failure;
@@ -591,7 +671,7 @@ static int nfc_genl_start_poll(struct sk_buff *skb, struct genl_info *info)
        if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
            ((!info->attrs[NFC_ATTR_IM_PROTOCOLS] &&
              !info->attrs[NFC_ATTR_PROTOCOLS]) &&
-            !info->attrs[NFC_ATTR_TM_PROTOCOLS]))
+             !info->attrs[NFC_ATTR_TM_PROTOCOLS]))
                return -EINVAL;
 
        idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
@@ -716,6 +796,236 @@ static int nfc_genl_dep_link_down(struct sk_buff *skb, struct genl_info *info)
        return rc;
 }
 
+static int nfc_genl_send_params(struct sk_buff *msg,
+                               struct nfc_llcp_local *local,
+                               u32 portid, u32 seq)
+{
+       void *hdr;
+
+       hdr = genlmsg_put(msg, portid, seq, &nfc_genl_family, 0,
+                         NFC_CMD_LLC_GET_PARAMS);
+       if (!hdr)
+               return -EMSGSIZE;
+
+       if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, local->dev->idx) ||
+           nla_put_u8(msg, NFC_ATTR_LLC_PARAM_LTO, local->lto) ||
+           nla_put_u8(msg, NFC_ATTR_LLC_PARAM_RW, local->rw) ||
+           nla_put_u16(msg, NFC_ATTR_LLC_PARAM_MIUX, be16_to_cpu(local->miux)))
+               goto nla_put_failure;
+
+       return genlmsg_end(msg, hdr);
+
+nla_put_failure:
+
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+static int nfc_genl_llc_get_params(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nfc_dev *dev;
+       struct nfc_llcp_local *local;
+       int rc = 0;
+       struct sk_buff *msg = NULL;
+       u32 idx;
+
+       if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
+               return -EINVAL;
+
+       idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+
+       dev = nfc_get_device(idx);
+       if (!dev)
+               return -ENODEV;
+
+       device_lock(&dev->dev);
+
+       local = nfc_llcp_find_local(dev);
+       if (!local) {
+               rc = -ENODEV;
+               goto exit;
+       }
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg) {
+               rc = -ENOMEM;
+               goto exit;
+       }
+
+       rc = nfc_genl_send_params(msg, local, info->snd_portid, info->snd_seq);
+
+exit:
+       device_unlock(&dev->dev);
+
+       nfc_put_device(dev);
+
+       if (rc < 0) {
+               if (msg)
+                       nlmsg_free(msg);
+
+               return rc;
+       }
+
+       return genlmsg_reply(msg, info);
+}
+
+static int nfc_genl_llc_set_params(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nfc_dev *dev;
+       struct nfc_llcp_local *local;
+       u8 rw = 0;
+       u16 miux = 0;
+       u32 idx;
+       int rc = 0;
+
+       if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
+           (!info->attrs[NFC_ATTR_LLC_PARAM_LTO] &&
+            !info->attrs[NFC_ATTR_LLC_PARAM_RW] &&
+            !info->attrs[NFC_ATTR_LLC_PARAM_MIUX]))
+               return -EINVAL;
+
+       if (info->attrs[NFC_ATTR_LLC_PARAM_RW]) {
+               rw = nla_get_u8(info->attrs[NFC_ATTR_LLC_PARAM_RW]);
+
+               if (rw > LLCP_MAX_RW)
+                       return -EINVAL;
+       }
+
+       if (info->attrs[NFC_ATTR_LLC_PARAM_MIUX]) {
+               miux = nla_get_u16(info->attrs[NFC_ATTR_LLC_PARAM_MIUX]);
+
+               if (miux > LLCP_MAX_MIUX)
+                       return -EINVAL;
+       }
+
+       idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+
+       dev = nfc_get_device(idx);
+       if (!dev)
+               return -ENODEV;
+
+       device_lock(&dev->dev);
+
+       local = nfc_llcp_find_local(dev);
+       if (!local) {
+               nfc_put_device(dev);
+               rc = -ENODEV;
+               goto exit;
+       }
+
+       if (info->attrs[NFC_ATTR_LLC_PARAM_LTO]) {
+               if (dev->dep_link_up) {
+                       rc = -EINPROGRESS;
+                       goto exit;
+               }
+
+               local->lto = nla_get_u8(info->attrs[NFC_ATTR_LLC_PARAM_LTO]);
+       }
+
+       if (info->attrs[NFC_ATTR_LLC_PARAM_RW])
+               local->rw = rw;
+
+       if (info->attrs[NFC_ATTR_LLC_PARAM_MIUX])
+               local->miux = cpu_to_be16(miux);
+
+exit:
+       device_unlock(&dev->dev);
+
+       nfc_put_device(dev);
+
+       return rc;
+}
+
+static int nfc_genl_llc_sdreq(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nfc_dev *dev;
+       struct nfc_llcp_local *local;
+       struct nlattr *attr, *sdp_attrs[NFC_SDP_ATTR_MAX+1];
+       u32 idx;
+       u8 tid;
+       char *uri;
+       int rc = 0, rem;
+       size_t uri_len, tlvs_len;
+       struct hlist_head sdreq_list;
+       struct nfc_llcp_sdp_tlv *sdreq;
+
+       if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
+           !info->attrs[NFC_ATTR_LLC_SDP])
+               return -EINVAL;
+
+       idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+
+       dev = nfc_get_device(idx);
+       if (!dev) {
+               rc = -ENODEV;
+               goto exit;
+       }
+
+       device_lock(&dev->dev);
+
+       if (dev->dep_link_up == false) {
+               rc = -ENOLINK;
+               goto exit;
+       }
+
+       local = nfc_llcp_find_local(dev);
+       if (!local) {
+               nfc_put_device(dev);
+               rc = -ENODEV;
+               goto exit;
+       }
+
+       INIT_HLIST_HEAD(&sdreq_list);
+
+       tlvs_len = 0;
+
+       nla_for_each_nested(attr, info->attrs[NFC_ATTR_LLC_SDP], rem) {
+               rc = nla_parse_nested(sdp_attrs, NFC_SDP_ATTR_MAX, attr,
+                                     nfc_sdp_genl_policy);
+
+               if (rc != 0) {
+                       rc = -EINVAL;
+                       goto exit;
+               }
+
+               if (!sdp_attrs[NFC_SDP_ATTR_URI])
+                       continue;
+
+               uri_len = nla_len(sdp_attrs[NFC_SDP_ATTR_URI]);
+               if (uri_len == 0)
+                       continue;
+
+               uri = nla_data(sdp_attrs[NFC_SDP_ATTR_URI]);
+               if (uri == NULL || *uri == 0)
+                       continue;
+
+               tid = local->sdreq_next_tid++;
+
+               sdreq = nfc_llcp_build_sdreq_tlv(tid, uri, uri_len);
+               if (sdreq == NULL) {
+                       rc = -ENOMEM;
+                       goto exit;
+               }
+
+               tlvs_len += sdreq->tlv_len;
+
+               hlist_add_head(&sdreq->node, &sdreq_list);
+       }
+
+       if (hlist_empty(&sdreq_list)) {
+               rc = -EINVAL;
+               goto exit;
+       }
+
+       rc = nfc_llcp_send_snl_sdreq(local, &sdreq_list, tlvs_len);
+exit:
+       device_unlock(&dev->dev);
+
+       nfc_put_device(dev);
+
+       return rc;
+}
+
 static struct genl_ops nfc_genl_ops[] = {
        {
                .cmd = NFC_CMD_GET_DEVICE,
@@ -760,6 +1070,21 @@ static struct genl_ops nfc_genl_ops[] = {
                .done = nfc_genl_dump_targets_done,
                .policy = nfc_genl_policy,
        },
+       {
+               .cmd = NFC_CMD_LLC_GET_PARAMS,
+               .doit = nfc_genl_llc_get_params,
+               .policy = nfc_genl_policy,
+       },
+       {
+               .cmd = NFC_CMD_LLC_SET_PARAMS,
+               .doit = nfc_genl_llc_set_params,
+               .policy = nfc_genl_policy,
+       },
+       {
+               .cmd = NFC_CMD_LLC_SDREQ,
+               .doit = nfc_genl_llc_sdreq,
+               .policy = nfc_genl_policy,
+       },
 };
 
 
index c5e42b7..94bfe19 100644 (file)
@@ -46,6 +46,8 @@ struct nfc_rawsock {
 #define to_rawsock_sk(_tx_work) \
        ((struct sock *) container_of(_tx_work, struct nfc_rawsock, tx_work))
 
+struct nfc_llcp_sdp_tlv;
+
 #ifdef CONFIG_NFC_LLCP
 
 void nfc_llcp_mac_is_down(struct nfc_dev *dev);
@@ -56,8 +58,11 @@ void nfc_llcp_unregister_device(struct nfc_dev *dev);
 int nfc_llcp_set_remote_gb(struct nfc_dev *dev, u8 *gb, u8 gb_len);
 u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len);
 int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb);
+struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev);
 int __init nfc_llcp_init(void);
 void nfc_llcp_exit(void);
+void nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp);
+void nfc_llcp_free_sdp_tlv_list(struct hlist_head *head);
 
 #else
 
@@ -97,6 +102,11 @@ static inline int nfc_llcp_data_received(struct nfc_dev *dev,
        return 0;
 }
 
+static inline struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev)
+{
+       return NULL;
+}
+
 static inline int nfc_llcp_init(void)
 {
        return 0;
@@ -106,6 +116,14 @@ static inline void nfc_llcp_exit(void)
 {
 }
 
+static inline void nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp)
+{
+}
+
+static inline void nfc_llcp_free_sdp_tlv_list(struct hlist_head *sdp_head)
+{
+}
+
 #endif
 
 int __init rawsock_init(void);
@@ -138,6 +156,8 @@ int nfc_genl_dep_link_down_event(struct nfc_dev *dev);
 int nfc_genl_tm_activated(struct nfc_dev *dev, u32 protocol);
 int nfc_genl_tm_deactivated(struct nfc_dev *dev);
 
+int nfc_genl_llc_send_sdres(struct nfc_dev *dev, struct hlist_head *sdres_list);
+
 struct nfc_dev *nfc_get_device(unsigned int idx);
 
 static inline void nfc_put_device(struct nfc_dev *dev)
index 9d5f928..deb87a5 100644 (file)
@@ -255,7 +255,6 @@ static int rawsock_recvmsg(struct kiocb *iocb, struct socket *sock,
        return rc ? : copied;
 }
 
-
 static const struct proto_ops rawsock_ops = {
        .family         = PF_NFC,
        .owner          = THIS_MODULE,