Tizen 2.0 Release 2.0_release
authorHyungKyu Song <hk76.song@samsung.com>
Fri, 15 Feb 2013 06:09:00 +0000 (15:09 +0900)
committerHyungKyu Song <hk76.song@samsung.com>
Fri, 15 Feb 2013 06:09:00 +0000 (15:09 +0900)
86 files changed:
AUTHORS [new file with mode: 0644]
COPYING [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
HACKING [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
Makefile.plugins [new file with mode: 0644]
NEWS [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
acinclude.m4 [new file with mode: 0644]
bootstrap [new file with mode: 0755]
bootstrap-configure [new file with mode: 0755]
configure.ac [new file with mode: 0644]
doc/adapter-api.txt [new file with mode: 0644]
doc/agent-api.txt [new file with mode: 0644]
doc/coding-style.txt [new file with mode: 0644]
doc/device-api.txt [new file with mode: 0644]
doc/features.txt [new file with mode: 0644]
doc/manager-api.txt [new file with mode: 0644]
doc/tag-api.txt [new file with mode: 0644]
gdbus/gdbus.h [new file with mode: 0644]
gdbus/mainloop.c [new file with mode: 0644]
gdbus/object.c [new file with mode: 0644]
gdbus/polkit.c [new file with mode: 0644]
gdbus/watch.c [new file with mode: 0644]
include/adapter.h [new file with mode: 0644]
include/dbus.h [new file with mode: 0644]
include/device.h [new file with mode: 0644]
include/log.h [new file with mode: 0644]
include/ndef.h [new file with mode: 0644]
include/nfc.h [new file with mode: 0644]
include/plugin.h [new file with mode: 0644]
include/setting.h [new file with mode: 0644]
include/tag.h [new file with mode: 0644]
include/tlv.h [new file with mode: 0644]
include/types.h [new file with mode: 0644]
include/version.h.in [new file with mode: 0644]
neard.pc.in [new file with mode: 0644]
packaging/init [new file with mode: 0644]
packaging/neard.changes [new file with mode: 0644]
packaging/neard.service [new file with mode: 0644]
packaging/neard.spec [new file with mode: 0644]
plugins/handover.c [new file with mode: 0644]
plugins/mifare.c [new file with mode: 0644]
plugins/nfctype1.c [new file with mode: 0644]
plugins/nfctype2.c [new file with mode: 0644]
plugins/nfctype3.c [new file with mode: 0644]
plugins/nfctype4.c [new file with mode: 0644]
plugins/npp.c [new file with mode: 0644]
plugins/p2p.c [new file with mode: 0644]
plugins/p2p.h [new file with mode: 0644]
plugins/snep.c [new file with mode: 0644]
src/adapter.c [new file with mode: 0644]
src/agent.c [new file with mode: 0644]
src/bluetooth.c [new file with mode: 0644]
src/dbus.c [new file with mode: 0644]
src/device.c [new file with mode: 0644]
src/error.c [new file with mode: 0644]
src/genbuiltin [new file with mode: 0755]
src/log.c [new file with mode: 0644]
src/main.c [new file with mode: 0644]
src/main.conf [new file with mode: 0644]
src/manager.c [new file with mode: 0644]
src/ndef.c [new file with mode: 0644]
src/near.h [new file with mode: 0644]
src/netlink.c [new file with mode: 0644]
src/org.neard.conf [new file with mode: 0644]
src/plugin.c [new file with mode: 0644]
src/tag.c [new file with mode: 0644]
src/tlv.c [new file with mode: 0644]
test/bt-handover [new file with mode: 0755]
test/disable-adapter [new file with mode: 0755]
test/dump-device [new file with mode: 0755]
test/dump-record [new file with mode: 0755]
test/dump-tag [new file with mode: 0755]
test/enable-adapter [new file with mode: 0755]
test/list-adapters [new file with mode: 0755]
test/monitor-near [new file with mode: 0755]
test/neard-ui.py [new file with mode: 0755]
test/push-device [new file with mode: 0755]
test/simple-agent [new file with mode: 0755]
test/start-poll [new file with mode: 0755]
test/stop-poll [new file with mode: 0755]
test/write-tag [new file with mode: 0755]
tools/snep-send.c [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..446d2ed
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,11 @@
+Samuel Ortiz <sameo@linux.intel.com>
+Marcel Holtmann <marcel@holtmann.org>
+Olivier Guiter <olivier.guiter@linux.intel.com>
+Ravikumar Veeramally <ravikumar.veeramally@linux.intel.com>
+Vinicius Costa Gomes <vcgomes@gmail.com>
+Eyal Reizer <eyalr@ti.com>
+Jeff Zheng <jeff.zheng@linux.intel.com>
+Szymon Janc <szymon.janc@tieto.com>
+Wiktor Lawski <wiktor.lawski@tieto.com>
+Dorota Moskal <dorota.moskal@tieto.com>
+Krzysztof Lyczkowski <krzysztof.lyczkowski@tieto.com>
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..6d45519
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..343ec98
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,73 @@
+ver 0.8
+       Added raw NDEF push through the NDEF agent API.
+       Fixed a couple of p2p file descriptor leaks.
+       Fixed CR handover record handling in handover Select frames.
+       Fixed handover version check support.
+       Fixed handover code to support AC less frames.
+       Fixed p2p client event routine to support multiple incoming requests.
+       Fixed netlink support for nested events.
+
+ver 0.7
+       Added support for Android Jelly Bean handover.
+       Added a Bluetooth handover automated script.
+       Added MIFARE Classic writing support.
+       Added MIFARE read only check support.
+       Added MIFARE check presence support.
+       Fixed a tag writing related double free.
+       Fixed tag plugins memory leaks.
+       Fixed tag plugins callback calls code paths.
+       Fixed URI NDEF writing.
+
+ver 0.6
+       Added tag formatting support for Type 1, 3 an 4.
+       Added blank tag detection for Type 1, 3 and 4.
+       Added an NDEF agent API implementation.
+       Fixed the handover agent API.
+       Fixed potential NULL pointer dereferences.
+       Fixed a few plugin memory leaks.
+       Fixed p2p NDEF push while p2p link is down.
+
+ver 0.5:
+       Added Handover Initiator support.
+       Added Handover selector support.
+       Added support for Type 2 tags formatting.
+       Added early binding support.
+       Fixed handover and p2p memory leaks.
+       Fixed Type 2 tag read command.
+       Fixed NDEF push return value.
+       Removed plugins enablement configure options.
+
+ver 0.4:
+       Added target mode support for peer to peer.
+       Added a handover agent API.
+       Fixed SNEP read fragmentation.
+       Fixed SNEP response frames version field.
+       Fixed build dependencies.
+       Fixed netlink and adapter memory leaks.
+
+ver 0.3:
+       Added a Tag D-Bus API.
+       Added an SNEP testing tool.
+       Added SNEP fragmentation support.
+       Added BlueZ signal handlers for adapter tracking.
+       Added BlueZ adapter properties handling.
+       Added a main.conf template.
+       Removed the Target D-Bus API.
+       Fixed Type 1 commands handling.
+
+ver 0.2:
+       Added SNEP support.
+       Added tag presence check implementation.
+       Added initial publishing API implementation.
+       Added MIFARE reader support.
+       Added NFC type 3 writer mode support.
+       Added netlink Powered property handling implementation.
+       Fixed p2p network conditions errors.
+       Fixed valgrind memory leak reports.
+
+ver 0.1:
+       Added reader mode for NFC types 1,2,3 and 4.
+       Added writer mode for NFC types 1,2, and 4.
+       Added peer to peer reader mode (NPP).
+       Added initial SNEP support.
+       Added initial Bluetooth OOB support.
diff --git a/HACKING b/HACKING
new file mode 100644 (file)
index 0000000..478653c
--- /dev/null
+++ b/HACKING
@@ -0,0 +1,87 @@
+Hacking on Near Field Communication manager
+*******************************************
+
+
+Build tools requirements
+========================
+
+When building and testing directly from the repository it is important to
+have at least automake version 1.10 or later installed. All modern
+distributions should default to the latest version, but it seems that
+Debian's default is still an earlier version:
+
+  Check version
+    # dpkg -l '*automake*'
+
+  Install new version
+    # apt-get install automake1.10
+    # update-alternatives --config automake
+
+
+Working with the source code repository
+=======================================
+
+The repository contains two extra scripts that accomplish the bootstrap
+process. One is called "bootstrap" which is the basic scripts that uses the
+autotools scripts to create the needed files for building and installing.
+It makes sure to call the right programs depending on the usage of shared or
+static libraries or translations etc.
+
+The second program is called "bootstrap-configure". This program will make
+sure to properly clean the repository, call the "bootstrap" script and then
+call configure with proper settings for development. It will use the best
+options and pass them over to configure. These options normally include
+the enabling the maintainer mode and the debugging features.
+
+So while in a normal source project the call "./configure ..." is used to
+configure the project with its settings like prefix and extra options. In
+case of bare repositories call "./bootstrap-configure" and it will bootstrap
+the repository and calls configure with all the correct options to make
+development easier.
+
+In case of preparing for a release with "make distcheck", don't use
+bootstrap-configure since it could export development specific settings.
+
+So the normal steps to checkout, build and install such a repository is
+like this:
+
+  Checkout repository
+    # git clone git://git.kernel.org/pub/scm/network/.../neard.git
+    # cd neard
+
+  Configure and build
+    # ./bootstrap-configure
+    # make
+
+  Check installation
+    # make install DESTDIR=$PWD/x
+    # find x
+    # rm -rf x
+
+  Check distribution
+    # make distcheck
+
+  Final installation
+    # sudo make install
+
+  Remove autogenerated files
+    # make maintainer-clean
+
+
+Running from within the source code repository
+==============================================
+
+When using "./configure --enable-maintainer-mode" the automake scripts will
+use the plugins directly from within the repository. This removes the need
+to use "make install" when testing "neard". The "bootstrap-configure"
+automatically includes this option.
+
+  Run daemon in foreground with debugging
+    # ./src/neard -n -d 'src/*'
+
+For production installations or distribution packaging it is important that
+the "--enable-maintainer-mode" option is NOT used.
+
+The debugging option -d takes an argument. This argument can be a comma
+separated list of file names like 'src/main.c,src/manager.c' to enable debugs
+in these files. Simple glob style pattern matching is supported in this list.
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..56b077d
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,236 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
+Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+These are generic installation instructions.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+   It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring.  (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+   The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'.  You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.  If you're
+     using `csh' on an old version of System V, you might need to type
+     `sh ./configure' instead to prevent `csh' from trying to execute
+     `configure' itself.
+
+     Running `configure' takes awhile.  While running, it prints some
+     messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Optionally, type `make check' to run any self-tests that come with
+     the package.
+
+  4. Type `make install' to install the programs and any data files and
+     documentation.
+
+  5. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  To also remove the
+     files that `configure' created (so you can compile the package for
+     a different kind of computer), type `make distclean'.  There is
+     also a `make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about.  Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+   You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment.  Here
+is an example:
+
+     ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+   *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory.  After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc.  You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PREFIX'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+give `configure' the option `--exec-prefix=PREFIX', the package will
+use PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+   In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files.  Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+     CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+     OS KERNEL-OS
+
+   See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+   If you are _building_ compiler tools for cross-compiling, you should
+use the `--target=TYPE' option to select the type of system they will
+produce code for.
+
+   If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'.  However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost.  In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'.  For example:
+
+     ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).  Here is a another example:
+
+     /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
+configuration-related scripts to be executed by `/bin/bash'.
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+     Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`--cache-file=FILE'
+     Enable the cache: use and save the results of the tests in FILE,
+     traditionally `config.cache'.  FILE defaults to `/dev/null' to
+     disable caching.
+
+`--config-cache'
+`-C'
+     Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.  To
+     suppress all normal output, redirect it to `/dev/null' (any error
+     messages will still be shown).
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options.  Run
+`configure --help' for more details.
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..07e1437
--- /dev/null
@@ -0,0 +1,128 @@
+
+AM_MAKEFLAGS = --no-print-directory
+
+includedir = @includedir@/near
+
+include_HEADERS = include/types.h include/log.h include/plugin.h \
+                       include/tag.h include/adapter.h include/ndef.h \
+                       include/tlv.h include/setting.h include/device.h \
+                       include/nfc.h
+
+nodist_include_HEADERS = include/version.h
+
+noinst_HEADERS = include/dbus.h
+
+local_headers = $(foreach file,$(include_HEADERS) $(nodist_include_HEADERS) \
+                       $(noinst_HEADERS), include/near/$(notdir $(file)))
+
+gdbus_sources = gdbus/gdbus.h gdbus/mainloop.c gdbus/watch.c \
+                                       gdbus/object.c gdbus/polkit.c
+
+plugin_LTLIBRARIES =
+
+plugin_objects =
+
+builtin_modules =
+builtin_sources =
+builtin_cflags =
+builtin_libadd =
+
+libexec_PROGRAMS = src/neard
+
+src_neard_SOURCES = $(gdbus_sources) $(gweb_sources) $(builtin_sources) \
+                       src/main.c src/error.c src/near.h src/log.c \
+                       src/dbus.c src/manager.c src/adapter.c src/device.c \
+                       src/tag.c src/plugin.c src/netlink.c src/ndef.c \
+                       src/tlv.c src/bluetooth.c src/agent.c
+
+src_neard_LDADD = $(builtin_libadd) @GLIB_LIBS@ @DBUS_LIBS@ @NETLINK_LIBS@ -lresolv -ldl
+
+src_neard_LDFLAGS = -Wl,--export-dynamic
+
+nodist_src_neard_SOURCES = src/builtin.h
+
+AM_CFLAGS = @GLIB_CFLAGS@ @DBUS_CFLAGS@ @NETLINK_CFLAGS@ $(builtin_cflags) \
+                                       -DNEAR_PLUGIN_BUILTIN \
+                                       -DPLUGINDIR=\""$(plugindir)"\" \
+                                       -DCONFIGDIR=\""$(configdir)\""
+
+INCLUDES = -I$(builddir)/include -I$(builddir)/src -I$(srcdir)/gdbus
+
+CLEANFILES = src/builtin.h $(local_headers)
+
+plugindir = $(libdir)/near/plugins
+
+configdir = ${sysconfdir}/neard
+
+dist_noinst_DATA = src/main.conf
+
+dbusdir = ${sysconfdir}/dbus-1/system.d/
+
+dist_dbus_DATA = src/org.neard.conf
+
+if MAINTAINER_MODE
+build_plugindir = $(abs_top_srcdir)/plugins/.libs
+else
+build_plugindir = $(plugindir)
+endif
+
+doc_files = doc/manager-api.txt doc/tag-api.txt doc/device-api.txt doc/adapter-api.txt
+
+EXTRA_DIST = src/genbuiltin $(doc_files)
+
+test_scripts = test/disable-adapter test/enable-adapter test/list-adapters \
+               test/dump-device test/dump-tag test/dump-record \
+               test/monitor-near test/start-poll test/stop-poll test/write-tag \
+               test/push-device test/bt-handover
+
+if TEST
+testdir = $(pkglibdir)/test
+test_SCRIPTS = $(test_scripts)
+endif
+
+if TOOLS
+noinst_PROGRAMS = tools/snep-send
+
+tools_snep_send_SOURCES = $(gdbus_sources) src/log.c src/dbus.c \
+                       src/bluetooth.c src/ndef.c tools/snep-send.c
+tools_snep_send_LDADD = @GLIB_LIBS@ @DBUS_LIBS@
+
+endif
+
+include Makefile.plugins
+
+EXTRA_DIST += $(test_scripts)
+
+pkgconfigdir = $(libdir)/pkgconfig
+
+pkgconfig_DATA = neard.pc
+
+DISTCHECK_CONFIGURE_FLAGS = --enable-nfctype1 \
+                           --enable-nfctype2 \
+                           --enable-nfctype3 \
+                           --enable-nfctype4 \
+                           --enable-p2p
+
+DISTCLEANFILES = $(pkgconfig_DATA)
+
+MAINTAINERCLEANFILES = Makefile.in \
+       aclocal.m4 configure config.h.in config.sub config.guess \
+       ltmain.sh depcomp compile missing install-sh mkinstalldirs
+
+src/plugin.$(OBJEXT): src/builtin.h
+
+src/builtin.h: src/genbuiltin $(builtin_sources)
+       $(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_modules) > $@
+
+$(src_neard_OBJECTS) $(plugin_objects): $(local_headers)
+
+include/near/version.h: include/version.h
+       $(AM_V_at)$(MKDIR_P) include/near
+       $(AM_V_GEN)$(LN_S) $(abs_top_builddir)/$< $@
+
+include/near/%.h: include/%.h
+       $(AM_V_at)$(MKDIR_P) include/near
+       $(AM_V_GEN)$(LN_S) $(abs_top_srcdir)/$< $@
+
+clean-local:
+       @$(RM) -rf include/near
diff --git a/Makefile.plugins b/Makefile.plugins
new file mode 100644 (file)
index 0000000..e561f8f
--- /dev/null
@@ -0,0 +1,30 @@
+
+plugin_cflags = -fvisibility=hidden -I$(srcdir)/gdbus \
+                                       @DBUS_CFLAGS@ @GLIB_CFLAGS@
+plugin_ldflags = -no-undefined -module -avoid-version
+
+if NFCTYPE1
+builtin_modules += nfctype1
+builtin_sources += plugins/nfctype1.c
+endif
+
+if NFCTYPE2
+builtin_modules += nfctype2
+builtin_sources += plugins/nfctype2.c plugins/mifare.c
+endif
+
+if NFCTYPE3
+builtin_modules += nfctype3
+builtin_sources += plugins/nfctype3.c
+endif
+
+if NFCTYPE4
+builtin_modules += nfctype4
+builtin_sources += plugins/nfctype4.c
+endif
+
+if P2P
+builtin_modules += p2p
+builtin_sources += plugins/p2p.c plugins/npp.c plugins/snep.c \
+                               plugins/handover.c plugins/p2p.h
+endif
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..4074749
--- /dev/null
+++ b/README
@@ -0,0 +1,52 @@
+Near Field Communication manager
+********************************
+
+Copyright (C) 2011  Intel Corporation. All rights reserved.
+
+
+Compilation and installation
+============================
+
+In order to compile neard you need following software packages:
+       - GCC compiler
+       - D-Bus library
+       - GLib library
+       - Netlink (libnl) library, version 1 or 2.
+
+To configure run:
+       ./configure --prefix=/usr
+
+Configure automatically searches for all required components and packages.
+
+To compile and install run:
+       make && make install
+
+Configuration and options
+=========================
+
+By default all neard plugins and features are built in. They can be
+disabled with the following configuration options:
+
+       --disable-nfctype1
+
+               Disable support for type 1 NFC tags.
+
+       --disable-nfctype2
+
+               Disable support for type 2 NFC tags.
+
+       --disable-nfctype3
+
+               Disable support for type 3 NFC tags.
+
+       --disable-nfctype4
+
+               Disable support for type 1 NFC tags.
+
+       --disable-p2p
+
+               Disable support for peer to peer mode.
+
+Running ./bootstrap-configure will build the configure script and then
+run it, with maintainer mode enabled. bootstrap-configure will configure
+neard with all features enabled.
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..bd13585
--- /dev/null
+++ b/TODO
@@ -0,0 +1,82 @@
+Background
+==========
+
+- Priority scale: High, Medium and Low
+
+- Complexity scale: C1, C2, C4 and C8.
+   The complexity scale is exponential, with complexity 1 being the
+   lowest complexity. Complexity is a function of both task 'complexity'
+   and task 'scope'.
+
+Core
+====
+
+- Handover Agent API
+
+  Priority: Medium
+  Complexity: C4
+  Owner: Szymon Janc <szymon.janc@tieto.com>
+
+  neard's handover agent API will allow an eventual BlueZ neard plugin to
+  pass OOB data to neard. From this OOB data, neard will build an Hs record
+  and reply to the handover requester.
+
+- Card Emulation Mode
+
+  Priority: Low
+  Complexity: C8
+  Dependencies: Core:NDEF building library
+  Owner: Samuel Ortiz <sameo@linux.intel.com>
+
+  In card emulation mode, the NFC adapter is in passive mode, and gets
+  activated by an initiator mode device. Then the initiator sends command
+  that the target is supposed to interpret and respond to. Most of the
+  commands are tag specific.
+  For a proper card emulation mode support, the NFC adapter must enter the
+  polling loop and not only initiate the RF field, but then wait for intiator
+  to activate it. Once activated, the NFC socket should enter the accept
+  state, and dispatch the various commands to the right plugins (one per
+  protocol, a.k.a. tags).
+
+
+Reader mode
+===========
+
+
+Writer mode
+===========
+
+- MIFARE writer mode support
+
+  Priority: Low
+  Complexity: C2
+  Owner: Dorota Moskal <dorota.moskal@tieto.com>
+
+
+p2p mode
+========
+
+- Bluetooth Handover integration
+
+  Priority: Medium
+  Complexity: C4
+  Dependencies: Core:Handover Agent API
+  Owner: Olivier Guiter <olivier.guiter@linux.intel.com>
+
+  The handover integration has to be done between bluetoothd, obexd and
+  neard. Obexd or BlueZ should be able to call a handover requester
+  org.neard.Manager method to send an Hr to a remote peer. This asynchronous
+  method will return upon reception of an Hs record. This is when BlueZ
+  will be able to initiate the pairing.
+  On the other hand, neard should be able to get the appropriate OOB data
+  from BlueZ through the handover agent API, and build an Hs to send to
+  the handover requester. The latter will then initiate the pairing.
+
+- SNEP and LLCP validation tests
+
+  Priority: Medium
+  Complexity: C4
+  Owner: Ravi kumar Veeramally <ravikumar.veeramally@linux.intel.com>
+
+  Implement SNEP validation test cases as an optional neard plugin, and
+  LLCP ones as a set of tools/ binaries.
diff --git a/acinclude.m4 b/acinclude.m4
new file mode 100644 (file)
index 0000000..329c6a9
--- /dev/null
@@ -0,0 +1,27 @@
+AC_DEFUN([AC_PROG_CC_PIE], [
+       AC_CACHE_CHECK([whether ${CC-cc} accepts -fPIE], ac_cv_prog_cc_pie, [
+               echo 'void f(){}' > conftest.c
+               if test -z "`${CC-cc} -fPIE -pie -c conftest.c 2>&1`"; then
+                       ac_cv_prog_cc_pie=yes
+               else
+                       ac_cv_prog_cc_pie=no
+               fi
+               rm -rf conftest*
+       ])
+])
+
+AC_DEFUN([COMPILER_FLAGS], [
+       if (test "${CFLAGS}" = ""); then
+               CFLAGS="-Wall -O2 -D_FORTIFY_SOURCE=2"
+       fi
+       if (test "$USE_MAINTAINER_MODE" = "yes"); then
+               CFLAGS+=" -Werror -Wextra"
+               CFLAGS+=" -Wno-unused-parameter"
+               CFLAGS+=" -Wno-missing-field-initializers"
+               CFLAGS+=" -Wdeclaration-after-statement"
+               CFLAGS+=" -Wmissing-declarations"
+               CFLAGS+=" -Wredundant-decls"
+               CFLAGS+=" -Wcast-align"
+               CFLAGS+=" -DG_DISABLE_DEPRECATED"
+       fi
+])
diff --git a/bootstrap b/bootstrap
new file mode 100755 (executable)
index 0000000..91756f9
--- /dev/null
+++ b/bootstrap
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+aclocal && \
+    autoheader && \
+       libtoolize --automake --copy --force && \
+           automake --add-missing --copy && \
+               autoconf
diff --git a/bootstrap-configure b/bootstrap-configure
new file mode 100755 (executable)
index 0000000..4e705bc
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+if [ -f config.status ]; then
+       make maintainer-clean
+fi
+
+./bootstrap && \
+    ./configure --enable-maintainer-mode \
+               --enable-debug \
+               --prefix=/usr \
+               --sysconfdir=/etc \
+               --enable-tools $*
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..59d9a0a
--- /dev/null
@@ -0,0 +1,126 @@
+AC_PREREQ(2.60)
+AC_INIT(neard, 0.8)
+
+AM_INIT_AUTOMAKE([foreign subdir-objects color-tests])
+AM_CONFIG_HEADER(config.h)
+
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+AM_MAINTAINER_MODE
+
+AC_PREFIX_DEFAULT(/usr/local)
+
+if (test "${libdir}" = '${exec_prefix}/lib'); then
+       libdir="${prefix}/lib"
+fi
+
+plugindir="${libdir}/near/plugins"
+
+PKG_PROG_PKG_CONFIG
+
+COMPILER_FLAGS
+
+AC_LANG_C
+
+AC_PROG_CC
+AC_PROG_CC_PIE
+AC_PROG_INSTALL
+
+m4_define([_LT_AC_TAGCONFIG], [])
+m4_ifdef([AC_LIBTOOL_TAGS], [AC_LIBTOOL_TAGS([])])
+
+AC_DISABLE_STATIC
+AC_PROG_LIBTOOL
+
+AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization],
+                       [disable code optimization through compiler]), [
+       if (test "${enableval}" = "no"); then
+               CFLAGS="$CFLAGS -O0"
+       fi
+])
+
+AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug],
+                       [enable compiling with debugging information]), [
+       if (test "${enableval}" = "yes" &&
+                               test "${ac_cv_prog_cc_g}" = "yes"); then
+               CFLAGS="$CFLAGS -g"
+       fi
+])
+
+AC_ARG_ENABLE(pie, AC_HELP_STRING([--enable-pie],
+                       [enable position independent executables flag]), [
+       if (test "${enableval}" = "yes" &&
+                               test "${ac_cv_prog_cc_pie}" = "yes"); then
+               CFLAGS="$CFLAGS -fPIE"
+               LDFLAGS="$LDFLAGS -pie"
+       fi
+])
+
+AC_CHECK_LIB(dl, dlopen, dummy=yes,
+                       AC_MSG_ERROR(dynamic linking loader is required))
+
+PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.28, dummy=yes,
+                               AC_MSG_ERROR(GLib >= 2.28 is required))
+AC_SUBST(GLIB_CFLAGS)
+AC_SUBST(GLIB_LIBS)
+
+PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.2, dummy=yes,
+                               AC_MSG_ERROR(D-Bus >= 1.2 is required))
+AC_SUBST(DBUS_CFLAGS)
+AC_SUBST(DBUS_LIBS)
+
+PKG_CHECK_MODULES(LIBNL3, libnl-3.0 libnl-genl-3.0, [
+       NETLINK_CFLAGS=${LIBNL3_CFLAGS}
+       NETLINK_LIBS=${LIBNL3_LIBS}
+], [
+       PKG_CHECK_MODULES(LIBNL2, libnl-2.0, [
+               NETLINK_CFLAGS=${LIBNL2_CFLAGS}
+               NETLINK_LIBS=${LIBNL2_LIBS}
+       ], [
+               PKG_CHECK_MODULES(LIBNL1, libnl-1, dummy=yes,
+                       AC_MSG_ERROR(Netlink library is required))
+               AC_DEFINE(NEED_LIBNL_COMPAT, 1,
+                       [Define to 1 if you need libnl-1 compat functions.])
+               NETLINK_CFLAGS=${LIBNL1_CFLAGS}
+               NETLINK_LIBS=${LIBNL1_LIBS}
+       ])
+])
+AC_SUBST(NETLINK_CFLAGS)
+AC_SUBST(NETLINK_LIBS)
+
+AC_ARG_ENABLE(test, AC_HELP_STRING([--enable-test],
+                                       [enable test/example scripts]),
+                                       [enable_test=${enableval}])
+AM_CONDITIONAL(TEST, test "${enable_test}" = "yes")
+
+AC_ARG_ENABLE(tools, AC_HELP_STRING([--enable-tools],
+                                       [enable testing tools]),
+                                       [enable_tools=${enableval}])
+AM_CONDITIONAL(TOOLS, test "${enable_tools}" = "yes")
+
+AC_ARG_ENABLE(nfctype1, AC_HELP_STRING([--disable-nfctype1],
+                               [disable NFC forum type 1 tags support]),
+                               [enable_nfctype1=${enableval}])
+AM_CONDITIONAL(NFCTYPE1, test "${enable_nfctype1}" != "no")
+
+AC_ARG_ENABLE(nfctype2, AC_HELP_STRING([--disable-nfctype2],
+                               [disable NFC forum type 2 tags support]),
+                               [enable_nfctype2=${enableval}])
+AM_CONDITIONAL(NFCTYPE2, test "${enable_nfctype2}" != "no")
+
+AC_ARG_ENABLE(nfctype3, AC_HELP_STRING([--disable-nfctype3],
+                               [disable NFC forum type 3 tags support]),
+                               [enable_nfctype3=${enableval}])
+AM_CONDITIONAL(NFCTYPE3, test "${enable_nfctype3}" != "no")
+
+AC_ARG_ENABLE(nfctype4, AC_HELP_STRING([--disable-nfctype4],
+                               [disable NFC forum type 4 tags support]),
+                               [enable_nfctype4=${enableval}])
+AM_CONDITIONAL(NFCTYPE4, test "${enable_nfctype4}" != "no")
+
+AC_ARG_ENABLE(p2p, AC_HELP_STRING([--disable-p2p],
+                               [disable NFC peer to peer support]),
+                               [enable_p2p=${enableval}])
+AM_CONDITIONAL(P2P, test "${enable_p2p}" != "no")
+
+AC_OUTPUT(Makefile include/version.h neard.pc)
diff --git a/doc/adapter-api.txt b/doc/adapter-api.txt
new file mode 100644 (file)
index 0000000..8b6906c
--- /dev/null
@@ -0,0 +1,126 @@
+Adapter hierarchy
+=================
+
+Service                org.neard
+Interface      org.neard.Adapter
+Object path    [variable prefix]/{nfc0,nfc1,...}
+
+Methods:       dict GetProperties()
+
+                       Returns all properties for the device. See the
+                       properties section for available properties.
+
+                       Possible Errors: org.neard.Error.DoesNotExist
+
+               void SetProperty(string name, variant value)
+
+                       Changes the value of the specified property. Only
+                       properties that are listed a read-write are changeable.
+                       On success this will emit a PropertyChanged signal.
+
+                       Possible Errors: org.neard.Error.DoesNotExist
+                                        org.neard.Error.InvalidArguments
+
+               void StartPollLoop(string mode)
+
+                       Starts the adapter polling loop. Depending on the mode,
+                       the adapter will start polling for targets, listening
+                       for NFC devices or both.
+                       The mode parameter can have the following values:
+                       "Initiator", "Target" or "Dual". For any other value, the
+                       adapter will fall back to initiator mode.
+                       Dual mode will have the adapter alternate between target
+                       and initiator modes during the polling loop.
+
+                       This process will start emitting TagFound and
+                       PropertyChanged "Polling" signals.
+
+                       Possible errors: org.neard.Error.NotReady
+                                        org.neard.Error.Failed
+                                        org.neard.Error.NotSupported
+
+               void StopPollLoop()
+
+                       The adapter polling loop will stop.
+
+                       Possible errors: org.neard.Error.NotReady
+                                        org.neard.Error.Failed
+                                        org.neard.Error.NotSupported
+
+               object StartEmulation(dict attributes)
+
+                       Starts tag emulation mode.
+
+                       Adapters can only emulate one target at a time, so
+                       subsequent calls to this method will always return
+                       the same object path.
+
+                       The attributes dictionary is described by the
+                       Record properties.
+                       For example, one would add a type, a Language, an
+                       Encoding and a Representation for emulating a text
+                       NDEF record.
+
+                       Returns the object path for the emulated target.
+
+                       Possible errors: org.neard.Error.NotReady
+                                        org.neard.Error.Failed
+                                        org.neard.Error.NotSupported
+
+               void StopEmulation()
+
+                       Stops tag emulation mode.
+
+                       Possible errors: org.neard.Error.NotReady
+                                        org.neard.Error.Failed
+                                        org.neard.Error.NotSupported
+
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+               TagFound(string address, dict values)
+
+                       This signal is sent whenever an NFC tag is found,
+                       as a result of a probe response reception.
+                       The dictionary contains basically the same values
+                       that are returned by the GetProperties method
+                       from the org.neard.Tag interface.
+
+               TagLost(string address)
+
+                       This signal is sent whenever the NFC tag is no longer
+                       in sight, or when it's been de-activated.
+
+
+Properties:    string Mode [readonly]
+
+                       The adapter NFC radio mode.
+
+                       Valid types are "Initiator", "Target" and "Idle"
+
+               boolean Powered [readwrite]
+
+                       Switch an adapter on or off.
+
+               boolean Polling [readonly]
+
+                       Indicates that the adapter is currently polling for targets.
+                       This is only valid when the adapter is in initiator mode.
+
+               array{string} Protocols [readonly]
+
+                       The adapter supported protocols.
+                       Possible values are "Felica", "MIFARE", "Jewel",
+                       "ISO-DEP" and "NFC-DEP".
+
+               array{object} Tags [readonly]
+
+                       The tags object paths.
+
+               array{object} Devices [readonly]
+
+                       The devices object paths.
diff --git a/doc/agent-api.txt b/doc/agent-api.txt
new file mode 100644 (file)
index 0000000..e1a5330
--- /dev/null
@@ -0,0 +1,105 @@
+HandoverAgent hierarchy
+=======================
+
+Service                unique name
+Interface      org.neard.HandoverAgent
+Object path    freely definable
+
+Methods                dict RequestOOB(dict values) [experimental]
+
+                       This method gets called when the service daemon
+                       needs to get Out Of Band data from the handover
+                       agent, typically the BlueZ daemon.
+
+                       The service daemon will use this OOB data to build
+                       a Handover Request or Select message and send it to
+                       remote device.
+
+                       Values parameter is optional. It should be a dictionary
+                       where the keys are the field names and the values are
+                       the actual fields. If provided it should contain remote
+                       Out Of Band data received in Handover Request message.
+                       Those data will be stored for future use (i.e. when
+                       remote initialize pairing) and providing those will not
+                       initialize pairing.
+
+                       The return value should be a dictionary where the
+                       keys are the field names and the values are the
+                       actual fields.
+
+                       Possible Errors: org.neard.HandoverAgent.Error.NotSupported
+                                        org.neard.HandoverAgent.Error.NoSuchDevice
+                                        org.neard.HandoverAgent.Error.InProgress
+                                        org.neard.HandoverAgent.Error.Failed
+
+               void PushOOB(dict values) [experimental]
+
+                       This method gets called when service daemon received
+                       Handover Select message from selector and needs to pass
+                       remote Out Of Band data to agent to start handover.
+
+                       If there is no Bluetooth adapter or if it doesn't
+                       support simple pairing the agent will return an error.
+
+                       Agent shall implicitly initialize pairing if needed.
+
+                       This function returns when alternative carrier
+                       (Bluetooth) is ready to be used i.e. pairing has
+                       finished.
+
+                       Parameter should be a dictionary where the keys are the
+                       field names and the values are the actual fields.
+
+                       Possible Errors: org.neard.HandoverAgent.Error.NotSupported
+                                        org.neard.HandoverAgent.Error.NoSuchDevice
+                                        org.neard.HandoverAgent.Error.InProgress
+                                        org.neard.HandoverAgent.Error.Failed
+
+               void Release() [experimental]
+
+                       This method gets called when the service daemon
+                       unregisters the agent. An agent can use it to do
+                       cleanup tasks. There is no need to unregister the
+                       agent, because when this method gets called it has
+                       already been unregistered.
+
+Fields         array{byte} EIR
+
+                       This is EIR blob. Used by SSP capable devices.
+
+               array{byte} nokia.com:bt
+
+                       This is a proprietary extension blob used by some
+                       Nokia Bluetooth 2.0 devices.
+
+
+NDEFAgent hierarchy
+=======================
+
+Service                unique name
+Interface      org.neard.NDEFAgent
+Object path    freely definable
+
+Methods                dict GetNDEF(dict values) [experimental]
+
+                       This method gets called when the service daemon
+                       found an NDEF matching the registered type.
+
+                       The parameter is a dictionary where the keys are the
+                       field names and the values are the actual fields.
+
+               void Release() [experimental]
+
+                       This method gets called when the service daemon
+                       unregisters the agent. An agent can use it to do
+                       cleanup tasks. There is no need to unregister the
+                       agent, because when this method gets called it has
+                       already been unregistered.
+
+Fields         array{byte} NDEF
+
+                       This is the raw NDEF data.
+
+               object Record
+
+                      This is a record object path.
diff --git a/doc/coding-style.txt b/doc/coding-style.txt
new file mode 100644 (file)
index 0000000..47f9c4a
--- /dev/null
@@ -0,0 +1,344 @@
+Every project has its coding style, and neard is not an exception. This
+document describes the preferred coding style for neard code, in order to keep
+some level of consistency among developers so that code can be easily
+understood and maintained, and also to help your code survive under
+maintainer's fastidious eyes so that you can get a passport for your patch
+ASAP.
+
+First of all, neard coding style must follow every rule for Linux kernel
+(http://www.kernel.org/doc/Documentation/CodingStyle). There also exists a tool
+named checkpatch.pl to help you check the compliance with it. Just type
+"checkpatch.pl --no-tree patch_name" to check your patch. In theory, you need
+to clean up all the warnings and errors except this one: "ERROR: Missing
+Signed-off-by: line(s)". neard does not used Signed-Off lines, so including
+them is actually an error.  In certain circumstances one can ignore the 80
+character per line limit.  This is generally only allowed if the alternative
+would make the code even less readable.
+
+Besides the kernel coding style above, neard has special flavors for its own.
+Some of them are mandatory (marked as 'M'), while some others are optional
+(marked as 'O'), but generally preferred.
+
+M1: Blank line before and after an if/while/do/for statement
+============================================================
+There should be a blank line before if statement unless the if is nested and
+not preceded by an expression or variable declaration.
+
+Example:
+1)
+a = 1;
+if (b) {  // wrong
+
+2)
+a = 1
+
+if (b) {
+}
+a = 2; // wrong
+
+3)
+if (a) {
+       if (b)  // correct
+
+4)
+b = 2;
+
+if (a) {       // correct
+
+}
+
+b = 3;
+
+The only exception to this rule applies when a variable is being allocated:
+array = g_try_new0(int, 20);
+if (array == NULL)     // Correct
+       return;
+
+
+M2: Multiple line comment
+=========================
+If your comments have more then one line, please start it from the second line.
+
+Example:
+/*
+ * first line comment  // correct
+ * ...
+ * last line comment
+ */
+
+
+M3: Space before and after operator
+===================================
+There should be a space before and after each operator.
+
+Example:
+a + b;  // correct
+
+
+M4: Wrap long lines
+===================
+If your condition in if, while, for statement or a function declaration is too
+long to fit in one line, the new line needs to be indented not aligned with the
+body.
+
+Example:
+1)
+if (call->status == CALL_STATUS_ACTIVE ||
+       call->status == CALL_STATUS_HELD) {  // wrong
+       neard_dbus_dict_append();
+
+2)
+if (call->status == CALL_STATUS_ACTIVE ||
+               call->status == CALL_STATUS_HELD) {  // correct
+       neard_dbus_dict_append();
+
+3)
+gboolean sim_ust_is_available(unsigned char *service_ust, unsigned char len,
+       num sim_ust_service index) // wrong
+{
+       int a;
+       ...
+}
+
+4)
+gboolean sim_ust_is_available(unsigned char *service_ust, unsigned char len,
+                                       enum sim_ust_service index) // correct
+{
+       int a;
+       ...
+}
+
+If the line being wrapped is a function call or function declaration, the
+preferred style is to indent at least past the opening parenthesis. Indenting
+further is acceptable as well (as long as you don't hit the 80 character
+limit).
+
+If this is not possible due to hitting the 80 character limit, then indenting
+as far as possible to the right without hitting the limit is preferred.
+
+Example:
+
+1)
+gboolean sim_ust_is_available(unsigned char *service_ust, unsigned char len,
+               enum sim_ust_service index); // worse
+
+2)
+gboolean sim_ust_is_available(unsigned char *service_ust, unsigned char len,
+                                               enum sim_ust_service index);
+                                               // better
+
+M5: Git commit message 50/72 formatting
+=======================================
+The commit message header should be within 50 characters. And if you have
+detailed explanatory text, wrap it to 72 character.
+
+
+M6: Space when doing type casting
+=================================
+There should be a space between new type and variable.
+
+Example:
+1)
+a = (int *)b;  // wrong
+2)
+a = (int *) b;  // correct
+
+
+M7: Don't initialize variable unnecessarily
+===========================================
+When declaring a variable, try not to initialize it unless necessary.
+
+Example:
+int i = 1;  // wrong
+
+for (i = 0; i < 3; i++) {
+}
+
+
+M8: Use g_try_malloc instead of g_malloc
+========================================
+When g_malloc fails, the whole program would exit. Most of time, this is not
+the expected behavior, and you may want to use g_try_malloc instead.
+
+Example:
+additional = g_try_malloc(len - 1);  // correct
+if (additional == NULL)
+       return FALSE;
+
+
+M9: Follow the order of include header elements
+===============================================
+When writing an include header the various elements should be in the following
+order:
+       - #includes
+       - forward declarations
+       - #defines
+       - enums
+       - typedefs
+       - function declarations and inline function definitions
+
+
+M10: Internal headers must not use include guards
+=================================================
+Any time when creating a new header file with non-public API, that header
+must not contain include guards.
+
+
+M11: Naming of enums
+====================
+
+Enums must have a descriptive name.  The enum type should be small caps and
+it should not be typedef-ed.  Enum contents should be in CAPITAL letters and
+prefixed by the enum type name.
+
+Example:
+
+enum animal_type {
+       ANIMAL_TYPE_FOUR_LEGS,
+       ANIMAL_TYPE_EIGHT_LEGS,
+       ANIMAL_TYPE_TWO_LEGS,
+};
+
+If the enum contents have values (e.g. from specification) the formatting
+should be as follows:
+
+enum animal_type {
+       ANIMAL_TYPE_FOUR_LEGS =         4,
+       ANIMAL_TYPE_EIGHT_LEGS =        8,
+       ANIMAL_TYPE_TWO_LEGS =          2,
+};
+
+M12: Enum as switch variable
+====================
+
+If the variable of a switch is an enum, you must not include a default in
+switch body. The reason for this is: If later on you modify the enum by adding
+a new type, and forget to change the switch accordingly, the compiler will
+complain the new added type hasn't been handled.
+
+Example:
+
+enum animal_type {
+       ANIMAL_TYPE_FOUR_LEGS =         4,
+       ANIMAL_TYPE_EIGHT_LEGS =        8,
+       ANIMAL_TYPE_TWO_LEGS =          2,
+};
+
+enum animal_type t;
+
+switch (t) {
+case ANIMAL_TYPE_FOUR_LEGS:
+       ...
+       break;
+case ANIMAL_TYPE_EIGHT_LEGS:
+       ...
+       break;
+case ANIMAL_TYPE_TWO_LEGS:
+       ...
+       break;
+default:  // wrong
+       break;
+}
+
+However if the enum comes from an external header file outside neard
+we cannot make any assumption of how the enum is defined and this
+rule might not apply.
+
+M13: Check for pointer being NULL
+=================================
+
+When checking if a pointer or a return value is NULL, explicitly compare to
+NULL rather than use the shorter check with "!" operator.
+
+Example:
+1)
+array = g_try_new0(int, 20);
+if (!array)    // Wrong
+       return;
+
+2)
+if (!g_at_chat_get_slave(chat))        // Wrong
+       return -EINVAL;
+
+3)
+array = g_try_new0(int, 20);
+if (array == NULL)     // Correct
+       return;
+
+
+M14: Always use parenthesis with sizeof
+=======================================
+The expression argument to the sizeof operator should always be in
+parenthesis, too.
+
+Example:
+1)
+memset(stuff, 0, sizeof(*stuff));
+
+2)
+memset(stuff, 0, sizeof *stuff); // Wrong
+
+
+M15: Use void if function has no parameters
+===========================================================
+A function with no parameters must use void in the parameter list.
+
+Example:
+1)
+void foo(void)
+{
+}
+
+2)
+void foo()     // Wrong
+{
+}
+
+M16: Don't use hex value with shift operators
+==============================================
+The expression argument to the shift operators should not be in hex.
+
+Example:
+
+1)
+1 << y
+
+2)
+0x1 << y       // Wrong
+
+O1: Shorten the name
+====================
+Better to use abbreviation, rather than full name, to name a variable,
+function, struct, etc.
+
+Example:
+supplementary_service  // too long
+ss  // better
+
+O2: Try to avoid complex if body
+================================
+It's better not to have a complicated statement for if. You may judge its
+contrary condition and return | break | continue | goto ASAP.
+
+Example:
+1)
+if (a) {  // worse
+       struct voicecall *v;
+       call = synthesize_outgoing_call(vc, vc->pending);
+       v = voicecall_create(vc, call);
+       v->detect_time = time(NULL);
+       DBG("Registering new call: %d", call->id);
+       voicecall_dbus_register(v);
+} else
+       return;
+
+2)
+if (!a)
+       return;
+
+struct voicecall *v;
+call = synthesize_outgoing_call(vc, vc->pending);
+v = voicecall_create(vc, call);
+v->detect_time = time(NULL);
+DBG("Registering new call: %d", call->id);
+voicecall_dbus_register(v);
diff --git a/doc/device-api.txt b/doc/device-api.txt
new file mode 100644 (file)
index 0000000..a098bbb
--- /dev/null
@@ -0,0 +1,46 @@
+Device hierarchy
+================
+
+Service                org.neard
+Interface      org.neard.Device
+Object path    [variable prefix]/{nfc0}/{device0, device1...}
+
+Method         dict GetProperties()
+
+                       Returns all properties for the device. See the
+                       properties section for available properties.
+
+                       Possible Errors: org.neard.Error.DoesNotExist
+
+               void SetProperty(string name, variant value)
+
+                       Changes the value of the specified property. Only
+                       properties that are listed a read-write are changeable.
+                       On success this will emit a PropertyChanged signal.
+
+                       Possible Errors: org.neard.Error.DoesNotExist
+                                        org.neard.Error.InvalidArguments
+
+               void Push(dict attributes)
+
+                       Creates an NDEF record from the attributes dictionary.
+
+                       The attribute argument should at least contain a
+                       record type and is described by the Record properties.
+                       For example, one would add a type, a Language, an
+                       Encoding and a Representation for a text record.
+
+                       Possible Errors: org.neard.Error.PermissionDenied
+                                        org.neard.Error.InvalidArguments
+                                        org.neard.Error.InProgress
+
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+
+Properties     array{object} Records [readonly]
+
+                       List of NDEF records object paths.
diff --git a/doc/features.txt b/doc/features.txt
new file mode 100644 (file)
index 0000000..5745dc0
--- /dev/null
@@ -0,0 +1,45 @@
+neard - Near Field Communication daemon
+=======================================
+
+Purpose
+=======
+
+This document describes all the major functionalities
+supported by neard.
+
+
+Reader Mode
+===========
+Supported tags:
+
+  * Type 1: Supported.
+  * Type 2: Supported.
+    * MIFARE classic 1K and 4K supported.
+  * Type 3: Supported.
+  * Type 4: Supported.
+
+
+Card Emulation Mode
+===================
+Not supported.
+
+
+Writer Mode
+===========
+Supported tags:
+
+  * Type 1: Supported.
+  * Type 2: Supported.
+  * Type 3: Supported.
+  * Type 4: Supported.
+
+
+Peer To Peer Mode
+=================
+neard reads and write NDEFs from p2p devices, through SNEP and NPP.
+Both initiator and target modes are supported.
+
+
+Handover
+========
+WIP.
diff --git a/doc/manager-api.txt b/doc/manager-api.txt
new file mode 100644 (file)
index 0000000..47547ff
--- /dev/null
@@ -0,0 +1,67 @@
+Manager hierarchy
+=================
+
+Service                org.neard
+Interface      org.neard.Manager
+Object path    /
+
+Methods                dict GetProperties()
+
+                       Returns all properties for the device. See the
+                       properties section for available properties.
+
+                       Possible Errors: org.neard.Error.DoesNotExist
+
+               void SetProperty(string name, variant value)
+
+                       Changes the value of the specified property. Only
+                       properties that are listed a read-write are changeable.
+                       On success this will emit a PropertyChanged signal.
+
+                       Possible Errors: org.neard.Error.DoesNotExist
+                                        org.neard.Error.InvalidArguments
+
+               void RegisterHandoverAgent(object path)
+
+                       Register new handover agent.
+
+                       Possible Errors: org.neard.Error.InvalidArguments
+
+               void UnregisterHandoverAgent(object path)
+
+                       Unregister an existing handover agent.
+
+                       Possible Errors: org.neard.Error.InvalidArguments
+
+               void RegisterNDEFAgent(object path, string type)
+
+                       Register new NDEF agent.
+
+                       When a record macthing the registered type is found,
+                       the agent will get the whole NDEF as a raw byte stream.
+
+                       Possible Errors: org.neard.Error.InvalidArguments
+
+               void UnregisterNDEFAgent(object path, string type)
+
+                       Unregister an existing NDEF agent.
+
+                       Possible Errors: org.neard.Error.InvalidArguments
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+               AdapterAdded(object adapter)
+
+                       Parameter is the object path of added adapter.
+
+               AdapterRemoved(object adapter)
+
+                       Parameter is the object path of removed adapter.
+
+Properties     array{object} Adapters [readonly]
+
+                       List of adapter object paths.
diff --git a/doc/tag-api.txt b/doc/tag-api.txt
new file mode 100644 (file)
index 0000000..6fb2a0a
--- /dev/null
@@ -0,0 +1,156 @@
+Tag hierarchy
+================
+
+Service                org.neard
+Interface      org.neard.Tag
+Object path    [variable prefix]/{nfc0}/{tag0, tag1...}
+
+Method         dict GetProperties()
+
+                       Returns all properties for the device. See the
+                       properties section for available properties.
+
+                       Possible Errors: org.neard.Error.DoesNotExist
+
+               void SetProperty(string name, variant value)
+
+                       Changes the value of the specified property. Only
+                       properties that are listed a read-write are changeable.
+                       On success this will emit a PropertyChanged signal.
+
+                       Possible Errors: org.neard.Error.DoesNotExist
+                                        org.neard.Error.InvalidArguments
+
+               void Write(dict attributes)
+
+                       Creates an NDEF record from the attributes dictionary.
+
+                       The attribute argument should at least contain a
+                       record type and is described by the Record properties.
+                       For example, one would add a type, a Language, an
+                       Encoding and a Representation for a text record.
+                       To push raw NDEF, one should use the NDEF key and use
+                       an array of bytes for the NDEF stream.
+
+                       Possible Errors: org.neard.Error.PermissionDenied
+                                        org.neard.Error.InvalidArguments
+                                        org.neard.Error.InProgress
+
+               array{byte} GetRawNDEF()
+
+                       Return the tag's NDEF as a raw bytes stream.
+
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+
+Properties     string Type [readonly]
+
+                       The NFC tag type.
+                       Possible values are "Type 1", "Type 2", "Type 3",
+                       "Type 4" and "NFC-DEP"
+
+               string Protocol [readonly]
+
+                       The tag radio protocol.
+                       Possible values are "Felica", "MIFARE", "Jewel",
+                       and "ISO-DEP".
+
+               array{object} Records [readonly]
+
+                       List of NDEF records object paths.
+
+               boolean ReadOnly [readonly]
+
+                       Give the current status of tag's read mode
+
+
+Record hierarchy
+================
+
+Service                org.neard
+Interface      org.neard.Record
+Object path    [variable prefix]/{nfc0}/{tag0|device}/{record0,record1,...}
+
+Method         dict GetProperties()
+
+                       Returns all properties for the record. Each record
+                       has it's type and properties.
+
+                       If type has "Text", possible properties are "Encoding",
+                       "Language" and "Representation".
+
+                       See the properties section for available properties.
+
+                       Possible Errors: org.neard.Error.DoesNotExist
+
+Properties     string Type [readonly]
+
+                       The NDEF record type name.
+
+                       Possible values are "SmartPoster", "Text", "URI",
+                       "HandoverRequest", "HandoverSelect", "HandoverCarrier".
+
+               string Encoding [readonly]
+
+                       The character encoding.
+
+                       Possible values are "UTF-8" or "UTF-16".
+                       This property is only valid for Text and SmartPoster's
+                       title records.
+
+               string Language [readonly]
+
+                       The ISO/IANA language code (For example "en" or "jp").
+
+                       This property is only valid for Text and SmartPoster's
+                       title records.
+
+               string Representation [readonly]
+
+                       The human readable representation of a text or
+                       title record.
+
+                       This property is only valid for Text and SmartPoster's
+                       title records.
+
+               string URI [readonly]
+
+                       The record URI (for example https://nfc-forum.org).
+
+                       This is the complete URI, including the scheme and
+                       the resource.
+                       This property is only valid for SmartPoster's URI records.
+
+               string MIMEType [readonly]
+
+                       The URI object MIME type.
+
+                       This is a description of the MIME type of the object
+                       the URI points at.
+                       This is not a mandatory field and is only valid for
+                       Smart Posters carrying a URI record.
+
+               uint32 Size [readonly]
+
+                       The URI object size.
+
+                       This is the size of the object the URI points at.
+                       It should be used by applications to decide if they can
+                       afford to fetch the object or not.
+                       This is not a mandatory field and is only valid for
+                       Smart Posters carrying a URI record.
+
+               string Action [readonly]
+
+                       The suggested course of action.
+
+                       This one is only valid for Smart Posters and is a
+                       suggestion only. It can be ignored, and the possible
+                       values are "Do" (for example launch the browser),
+                       "Save" (for example save the URI in the bookmarks folder,
+                       or "Edit" (for example open the URI in an URI editor for
+                       the user to modify it.
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
new file mode 100644 (file)
index 0000000..0a8a27c
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.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.
+ *
+ *  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GDBUS_H
+#define __GDBUS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+typedef void (* GDBusWatchFunction) (DBusConnection *connection,
+                                                       void *user_data);
+
+typedef gboolean (* GDBusSignalFunction) (DBusConnection *connection,
+                                       DBusMessage *message, void *user_data);
+
+DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name,
+                                                       DBusError *error);
+
+DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name,
+                                                       DBusError *error);
+
+gboolean g_dbus_request_name(DBusConnection *connection, const char *name,
+                                                       DBusError *error);
+
+gboolean g_dbus_set_disconnect_function(DBusConnection *connection,
+                               GDBusWatchFunction function,
+                               void *user_data, DBusFreeFunction destroy);
+
+typedef void (* GDBusDestroyFunction) (void *user_data);
+
+typedef DBusMessage * (* GDBusMethodFunction) (DBusConnection *connection,
+                                       DBusMessage *message, void *user_data);
+
+typedef guint32 GDBusPendingReply;
+
+typedef void (* GDBusSecurityFunction) (DBusConnection *connection,
+                                               const char *action,
+                                               gboolean interaction,
+                                               GDBusPendingReply pending);
+
+typedef enum {
+       G_DBUS_METHOD_FLAG_DEPRECATED = (1 << 0),
+       G_DBUS_METHOD_FLAG_NOREPLY    = (1 << 1),
+       G_DBUS_METHOD_FLAG_ASYNC      = (1 << 2),
+} GDBusMethodFlags;
+
+typedef enum {
+       G_DBUS_SIGNAL_FLAG_DEPRECATED = (1 << 0),
+} GDBusSignalFlags;
+
+typedef enum {
+       G_DBUS_PROPERTY_FLAG_DEPRECATED = (1 << 0),
+} GDBusPropertyFlags;
+
+typedef enum {
+       G_DBUS_SECURITY_FLAG_DEPRECATED        = (1 << 0),
+       G_DBUS_SECURITY_FLAG_BUILTIN           = (1 << 1),
+       G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION = (1 << 2),
+} GDBusSecurityFlags;
+
+typedef struct {
+       const char *name;
+       const char *signature;
+} GDBusArgInfo;
+
+typedef struct {
+       const char *name;
+       GDBusMethodFunction function;
+       GDBusMethodFlags flags;
+       unsigned int privilege;
+       const GDBusArgInfo *in_args;
+       const GDBusArgInfo *out_args;
+} GDBusMethodTable;
+
+typedef struct {
+       const char *name;
+       GDBusSignalFlags flags;
+       const GDBusArgInfo *args;
+} GDBusSignalTable;
+
+typedef struct {
+       const char *name;
+       const char *type;
+       GDBusPropertyFlags flags;
+} GDBusPropertyTable;
+
+typedef struct {
+       unsigned int privilege;
+       const char *action;
+       GDBusSecurityFlags flags;
+       GDBusSecurityFunction function;
+} GDBusSecurityTable;
+
+#define GDBUS_ARGS(args...) (const GDBusArgInfo[]) { args, { } }
+
+#define GDBUS_METHOD(_name, _in_args, _out_args, _function) \
+       .name = _name, \
+       .in_args = _in_args, \
+       .out_args = _out_args, \
+       .function = _function
+
+#define GDBUS_ASYNC_METHOD(_name, _in_args, _out_args, _function) \
+       .name = _name, \
+       .in_args = _in_args, \
+       .out_args = _out_args, \
+       .function = _function, \
+       .flags = G_DBUS_METHOD_FLAG_ASYNC
+
+#define GDBUS_DEPRECATED_METHOD(_name, _in_args, _out_args, _function) \
+       .name = _name, \
+       .in_args = _in_args, \
+       .out_args = _out_args, \
+       .function = _function, \
+       .flags = G_DBUS_METHOD_FLAG_DEPRECATED
+
+#define GDBUS_DEPRECATED_ASYNC_METHOD(_name, _in_args, _out_args, _function) \
+       .name = _name, \
+       .in_args = _in_args, \
+       .out_args = _out_args, \
+       .function = _function, \
+       .flags = G_DBUS_METHOD_FLAG_ASYNC | G_DBUS_METHOD_FLAG_DEPRECATED
+
+#define GDBUS_NOREPLY_METHOD(_name, _in_args, _out_args, _function) \
+       .name = _name, \
+       .in_args = _in_args, \
+       .out_args = _out_args, \
+       .function = _function, \
+       .flags = G_DBUS_METHOD_FLAG_NOREPLY
+
+#define GDBUS_SIGNAL(_name, _args) \
+       .name = _name, \
+       .args = _args
+
+#define GDBUS_DEPRECATED_SIGNAL(_name, _args) \
+       .name = _name, \
+       .args = _args, \
+       .flags = G_DBUS_SIGNAL_FLAG_DEPRECATED
+
+gboolean g_dbus_register_interface(DBusConnection *connection,
+                                       const char *path, const char *name,
+                                       const GDBusMethodTable *methods,
+                                       const GDBusSignalTable *signals,
+                                       const GDBusPropertyTable *properties,
+                                       void *user_data,
+                                       GDBusDestroyFunction destroy);
+gboolean g_dbus_unregister_interface(DBusConnection *connection,
+                                       const char *path, const char *name);
+
+gboolean g_dbus_register_security(const GDBusSecurityTable *security);
+gboolean g_dbus_unregister_security(const GDBusSecurityTable *security);
+
+void g_dbus_pending_success(DBusConnection *connection,
+                                       GDBusPendingReply pending);
+void g_dbus_pending_error(DBusConnection *connection,
+                               GDBusPendingReply pending,
+                               const char *name, const char *format, ...)
+                                       __attribute__((format(printf, 4, 5)));
+void g_dbus_pending_error_valist(DBusConnection *connection,
+                               GDBusPendingReply pending, const char *name,
+                                       const char *format, va_list args);
+
+DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name,
+                                               const char *format, ...)
+                                       __attribute__((format(printf, 3, 4)));
+DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name,
+                                       const char *format, va_list args);
+DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...);
+DBusMessage *g_dbus_create_reply_valist(DBusMessage *message,
+                                               int type, va_list args);
+
+gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message);
+gboolean g_dbus_send_reply(DBusConnection *connection,
+                               DBusMessage *message, int type, ...);
+gboolean g_dbus_send_reply_valist(DBusConnection *connection,
+                               DBusMessage *message, int type, va_list args);
+
+gboolean g_dbus_emit_signal(DBusConnection *connection,
+                               const char *path, const char *interface,
+                               const char *name, int type, ...);
+gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
+                               const char *path, const char *interface,
+                               const char *name, int type, va_list args);
+
+guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
+                               GDBusWatchFunction connect,
+                               GDBusWatchFunction disconnect,
+                               void *user_data, GDBusDestroyFunction destroy);
+guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name,
+                               GDBusWatchFunction function,
+                               void *user_data, GDBusDestroyFunction destroy);
+guint g_dbus_add_signal_watch(DBusConnection *connection,
+                               const char *sender, const char *path,
+                               const char *interface, const char *member,
+                               GDBusSignalFunction function, void *user_data,
+                               GDBusDestroyFunction destroy);
+gboolean g_dbus_remove_watch(DBusConnection *connection, guint tag);
+void g_dbus_remove_all_watches(DBusConnection *connection);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GDBUS_H */
diff --git a/gdbus/mainloop.c b/gdbus/mainloop.c
new file mode 100644 (file)
index 0000000..cff326f
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.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.
+ *
+ *  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus.h"
+
+#define DISPATCH_TIMEOUT  0
+
+#define info(fmt...)
+#define error(fmt...)
+#define debug(fmt...)
+
+struct timeout_handler {
+       guint id;
+       DBusTimeout *timeout;
+};
+
+struct watch_info {
+       guint id;
+       DBusWatch *watch;
+       DBusConnection *conn;
+};
+
+struct disconnect_data {
+       GDBusWatchFunction function;
+       void *user_data;
+};
+
+static gboolean disconnected_signal(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct disconnect_data *dc_data = data;
+
+       error("Got disconnected from the system message bus");
+
+       dc_data->function(conn, dc_data->user_data);
+
+       dbus_connection_unref(conn);
+
+       return TRUE;
+}
+
+static gboolean message_dispatch(void *data)
+{
+       DBusConnection *conn = data;
+
+       dbus_connection_ref(conn);
+
+       /* Dispatch messages */
+       while (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS);
+
+       dbus_connection_unref(conn);
+
+       return FALSE;
+}
+
+static inline void queue_dispatch(DBusConnection *conn,
+                                               DBusDispatchStatus status)
+{
+       if (status == DBUS_DISPATCH_DATA_REMAINS)
+               g_timeout_add(DISPATCH_TIMEOUT, message_dispatch, conn);
+}
+
+static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+       struct watch_info *info = data;
+       unsigned int flags = 0;
+       DBusDispatchStatus status;
+
+       dbus_connection_ref(info->conn);
+
+       if (cond & G_IO_IN)  flags |= DBUS_WATCH_READABLE;
+       if (cond & G_IO_OUT) flags |= DBUS_WATCH_WRITABLE;
+       if (cond & G_IO_HUP) flags |= DBUS_WATCH_HANGUP;
+       if (cond & G_IO_ERR) flags |= DBUS_WATCH_ERROR;
+
+       dbus_watch_handle(info->watch, flags);
+
+       status = dbus_connection_get_dispatch_status(info->conn);
+       queue_dispatch(info->conn, status);
+
+       dbus_connection_unref(info->conn);
+
+       return TRUE;
+}
+
+static void watch_info_free(void *data)
+{
+       struct watch_info *info = data;
+
+       if (info->id > 0) {
+               g_source_remove(info->id);
+               info->id = 0;
+       }
+
+       dbus_connection_unref(info->conn);
+
+       g_free(info);
+}
+
+static dbus_bool_t add_watch(DBusWatch *watch, void *data)
+{
+       DBusConnection *conn = data;
+       GIOCondition cond = G_IO_HUP | G_IO_ERR;
+       GIOChannel *chan;
+       struct watch_info *info;
+       unsigned int flags;
+       int fd;
+
+       if (!dbus_watch_get_enabled(watch))
+               return TRUE;
+
+       info = g_new0(struct watch_info, 1);
+
+       fd = dbus_watch_get_unix_fd(watch);
+       chan = g_io_channel_unix_new(fd);
+
+       info->watch = watch;
+       info->conn = dbus_connection_ref(conn);
+
+       dbus_watch_set_data(watch, info, watch_info_free);
+
+       flags = dbus_watch_get_flags(watch);
+
+       if (flags & DBUS_WATCH_READABLE) cond |= G_IO_IN;
+       if (flags & DBUS_WATCH_WRITABLE) cond |= G_IO_OUT;
+
+       info->id = g_io_add_watch(chan, cond, watch_func, info);
+
+       g_io_channel_unref(chan);
+
+       return TRUE;
+}
+
+static void remove_watch(DBusWatch *watch, void *data)
+{
+       if (dbus_watch_get_enabled(watch))
+               return;
+
+       /* will trigger watch_info_free() */
+       dbus_watch_set_data(watch, NULL, NULL);
+}
+
+static void watch_toggled(DBusWatch *watch, void *data)
+{
+       /* Because we just exit on OOM, enable/disable is
+        * no different from add/remove */
+       if (dbus_watch_get_enabled(watch))
+               add_watch(watch, data);
+       else
+               remove_watch(watch, data);
+}
+
+static gboolean timeout_handler_dispatch(gpointer data)
+{
+       struct timeout_handler *handler = data;
+
+       handler->id = 0;
+
+       /* if not enabled should not be polled by the main loop */
+       if (!dbus_timeout_get_enabled(handler->timeout))
+               return FALSE;
+
+       dbus_timeout_handle(handler->timeout);
+
+       return FALSE;
+}
+
+static void timeout_handler_free(void *data)
+{
+       struct timeout_handler *handler = data;
+
+       if (handler->id > 0) {
+               g_source_remove(handler->id);
+               handler->id = 0;
+       }
+
+       g_free(handler);
+}
+
+static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data)
+{
+       int interval = dbus_timeout_get_interval(timeout);
+       struct timeout_handler *handler;
+
+       if (!dbus_timeout_get_enabled(timeout))
+               return TRUE;
+
+       handler = g_new0(struct timeout_handler, 1);
+
+       handler->timeout = timeout;
+
+       dbus_timeout_set_data(timeout, handler, timeout_handler_free);
+
+       handler->id = g_timeout_add(interval, timeout_handler_dispatch,
+                                                               handler);
+
+       return TRUE;
+}
+
+static void remove_timeout(DBusTimeout *timeout, void *data)
+{
+       /* will trigger timeout_handler_free() */
+       dbus_timeout_set_data(timeout, NULL, NULL);
+}
+
+static void timeout_toggled(DBusTimeout *timeout, void *data)
+{
+       if (dbus_timeout_get_enabled(timeout))
+               add_timeout(timeout, data);
+       else
+               remove_timeout(timeout, data);
+}
+
+static void dispatch_status(DBusConnection *conn,
+                                       DBusDispatchStatus status, void *data)
+{
+       if (!dbus_connection_get_is_connected(conn))
+               return;
+
+       queue_dispatch(conn, status);
+}
+
+static inline void setup_dbus_with_main_loop(DBusConnection *conn)
+{
+       dbus_connection_set_watch_functions(conn, add_watch, remove_watch,
+                                               watch_toggled, conn, NULL);
+
+       dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout,
+                                               timeout_toggled, NULL, NULL);
+
+       dbus_connection_set_dispatch_status_function(conn, dispatch_status,
+                                                               NULL, NULL);
+}
+
+static gboolean setup_bus(DBusConnection *conn, const char *name,
+                                               DBusError *error)
+{
+       gboolean result;
+       DBusDispatchStatus status;
+
+       if (name != NULL) {
+               result = g_dbus_request_name(conn, name, error);
+
+               if (error != NULL) {
+                       if (dbus_error_is_set(error) == TRUE)
+                               return FALSE;
+               }
+
+               if (result == FALSE)
+                       return FALSE;
+       }
+
+       setup_dbus_with_main_loop(conn);
+
+       status = dbus_connection_get_dispatch_status(conn);
+       queue_dispatch(conn, status);
+
+       return TRUE;
+}
+
+DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name,
+                                                       DBusError *error)
+{
+       DBusConnection *conn;
+
+       conn = dbus_bus_get(type, error);
+
+       if (error != NULL) {
+               if (dbus_error_is_set(error) == TRUE)
+                       return NULL;
+       }
+
+       if (conn == NULL)
+               return NULL;
+
+       if (setup_bus(conn, name, error) == FALSE) {
+               dbus_connection_unref(conn);
+               return NULL;
+       }
+
+       return conn;
+}
+
+DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name,
+                                                       DBusError *error)
+{
+       DBusConnection *conn;
+
+       conn = dbus_bus_get_private(type, error);
+
+       if (error != NULL) {
+               if (dbus_error_is_set(error) == TRUE)
+                       return NULL;
+       }
+
+       if (conn == NULL)
+               return NULL;
+
+       if (setup_bus(conn, name, error) == FALSE) {
+               dbus_connection_unref(conn);
+               return NULL;
+       }
+
+       return conn;
+}
+
+gboolean g_dbus_request_name(DBusConnection *connection, const char *name,
+                                                       DBusError *error)
+{
+       int result;
+
+       result = dbus_bus_request_name(connection, name,
+                                       DBUS_NAME_FLAG_DO_NOT_QUEUE, error);
+
+       if (error != NULL) {
+               if (dbus_error_is_set(error) == TRUE)
+                       return FALSE;
+       }
+
+       if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+               if (error != NULL)
+                       dbus_set_error(error, name, "Name already in use");
+
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+gboolean g_dbus_set_disconnect_function(DBusConnection *connection,
+                               GDBusWatchFunction function,
+                               void *user_data, DBusFreeFunction destroy)
+{
+       struct disconnect_data *dc_data;
+
+       dc_data = g_new0(struct disconnect_data, 1);
+
+       dc_data->function = function;
+       dc_data->user_data = user_data;
+
+       dbus_connection_set_exit_on_disconnect(connection, FALSE);
+
+       if (g_dbus_add_signal_watch(connection, NULL, NULL,
+                               DBUS_INTERFACE_LOCAL, "Disconnected",
+                               disconnected_signal, dc_data, g_free) == 0) {
+               error("Failed to add watch for D-Bus Disconnected signal");
+               g_free(dc_data);
+               return FALSE;
+       }
+
+       return TRUE;
+}
diff --git a/gdbus/object.c b/gdbus/object.c
new file mode 100644 (file)
index 0000000..9689006
--- /dev/null
@@ -0,0 +1,858 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.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.
+ *
+ *  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus.h"
+
+#define info(fmt...)
+#define error(fmt...)
+#define debug(fmt...)
+
+struct generic_data {
+       unsigned int refcount;
+       GSList *interfaces;
+       char *introspect;
+};
+
+struct interface_data {
+       char *name;
+       const GDBusMethodTable *methods;
+       const GDBusSignalTable *signals;
+       const GDBusPropertyTable *properties;
+       void *user_data;
+       GDBusDestroyFunction destroy;
+};
+
+struct security_data {
+       GDBusPendingReply pending;
+       DBusMessage *message;
+       const GDBusMethodTable *method;
+       void *iface_user_data;
+};
+
+static void print_arguments(GString *gstr, const GDBusArgInfo *args,
+                                               const char *direction)
+{
+       for (; args && args->name; args++) {
+               g_string_append_printf(gstr,
+                                       "\t\t\t<arg name=\"%s\" type=\"%s\"",
+                                       args->name, args->signature);
+
+               if (direction)
+                       g_string_append_printf(gstr,
+                                       " direction=\"%s\"/>\n", direction);
+               else
+                       g_string_append_printf(gstr, "/>\n");
+
+       }
+}
+
+static void generate_interface_xml(GString *gstr, struct interface_data *iface)
+{
+       const GDBusMethodTable *method;
+       const GDBusSignalTable *signal;
+
+       for (method = iface->methods; method && method->name; method++) {
+               gboolean deprecated = method->flags &
+                                               G_DBUS_METHOD_FLAG_DEPRECATED;
+               gboolean noreply = method->flags &
+                                               G_DBUS_METHOD_FLAG_NOREPLY;
+
+               if (!deprecated && !noreply &&
+                               !(method->in_args && method->in_args->name) &&
+                               !(method->out_args && method->out_args->name))
+                       g_string_append_printf(gstr, "\t\t<method name=\"%s\"/>\n",
+                                                               method->name);
+               else {
+                       g_string_append_printf(gstr, "\t\t<method name=\"%s\">\n",
+                                                               method->name);
+                       print_arguments(gstr, method->in_args, "in");
+                       print_arguments(gstr, method->out_args, "out");
+
+                       if (deprecated)
+                               g_string_append_printf(gstr, "\t\t\t<annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n");
+
+                       if (noreply)
+                               g_string_append_printf(gstr, "\t\t\t<annotation name=\"org.freedesktop.DBus.Method.NoReply\" value=\"true\"/>\n");
+
+                       g_string_append_printf(gstr, "\t\t</method>\n");
+               }
+       }
+
+       for (signal = iface->signals; signal && signal->name; signal++) {
+               gboolean deprecated = signal->flags &
+                                               G_DBUS_SIGNAL_FLAG_DEPRECATED;
+
+               if (!deprecated && !(signal->args && signal->args->name))
+                       g_string_append_printf(gstr, "\t\t<signal name=\"%s\"/>\n",
+                                                               signal->name);
+               else {
+                       g_string_append_printf(gstr, "\t\t<signal name=\"%s\">\n",
+                                                               signal->name);
+                       print_arguments(gstr, signal->args, NULL);
+
+                       if (deprecated)
+                               g_string_append_printf(gstr, "\t\t\t<annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n");
+
+                       g_string_append_printf(gstr, "\t\t</signal>\n");
+               }
+       }
+}
+
+static void generate_introspection_xml(DBusConnection *conn,
+                               struct generic_data *data, const char *path)
+{
+       GSList *list;
+       GString *gstr;
+       char **children;
+       int i;
+
+       g_free(data->introspect);
+
+       gstr = g_string_new(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
+
+       g_string_append_printf(gstr, "<node>\n");
+
+       for (list = data->interfaces; list; list = list->next) {
+               struct interface_data *iface = list->data;
+
+               g_string_append_printf(gstr, "\t<interface name=\"%s\">\n",
+                                                               iface->name);
+
+               generate_interface_xml(gstr, iface);
+
+               g_string_append_printf(gstr, "\t</interface>\n");
+       }
+
+       if (!dbus_connection_list_registered(conn, path, &children))
+               goto done;
+
+       for (i = 0; children[i]; i++)
+               g_string_append_printf(gstr, "\t<node name=\"%s\"/>\n",
+                                                               children[i]);
+
+       dbus_free_string_array(children);
+
+done:
+       g_string_append_printf(gstr, "</node>\n");
+
+       data->introspect = g_string_free(gstr, FALSE);
+}
+
+static DBusMessage *introspect(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       struct generic_data *data = user_data;
+       DBusMessage *reply;
+
+       if (data->introspect == NULL)
+               generate_introspection_xml(connection, data,
+                                               dbus_message_get_path(message));
+
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_append_args(reply, DBUS_TYPE_STRING, &data->introspect,
+                                       DBUS_TYPE_INVALID);
+
+       return reply;
+}
+
+static DBusHandlerResult process_message(DBusConnection *connection,
+                       DBusMessage *message, const GDBusMethodTable *method,
+                                                       void *iface_user_data)
+{
+       DBusMessage *reply;
+
+       reply = method->function(connection, message, iface_user_data);
+
+       if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY) {
+               if (reply != NULL)
+                       dbus_message_unref(reply);
+               return DBUS_HANDLER_RESULT_HANDLED;
+       }
+
+       if (method->flags & G_DBUS_METHOD_FLAG_ASYNC) {
+               if (reply == NULL)
+                       return DBUS_HANDLER_RESULT_HANDLED;
+       }
+
+       if (reply == NULL)
+               return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+       dbus_connection_send(connection, reply, NULL);
+       dbus_message_unref(reply);
+
+       return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static GDBusPendingReply next_pending = 1;
+static GSList *pending_security = NULL;
+
+static const GDBusSecurityTable *security_table = NULL;
+
+void g_dbus_pending_success(DBusConnection *connection,
+                                       GDBusPendingReply pending)
+{
+       GSList *list;
+
+        for (list = pending_security; list; list = list->next) {
+               struct security_data *secdata = list->data;
+
+               if (secdata->pending != pending)
+                       continue;
+
+               pending_security = g_slist_remove(pending_security, secdata);
+
+               process_message(connection, secdata->message,
+                               secdata->method, secdata->iface_user_data);
+
+               dbus_message_unref(secdata->message);
+               g_free(secdata);
+               return;
+        }
+}
+
+void g_dbus_pending_error_valist(DBusConnection *connection,
+                               GDBusPendingReply pending, const char *name,
+                                       const char *format, va_list args)
+{
+       GSList *list;
+
+        for (list = pending_security; list; list = list->next) {
+               struct security_data *secdata = list->data;
+               DBusMessage *reply;
+
+               if (secdata->pending != pending)
+                       continue;
+
+               pending_security = g_slist_remove(pending_security, secdata);
+
+               reply = g_dbus_create_error_valist(secdata->message,
+                                                       name, format, args);
+               if (reply != NULL) {
+                       dbus_connection_send(connection, reply, NULL);
+                       dbus_message_unref(reply);
+               }
+
+               dbus_message_unref(secdata->message);
+               g_free(secdata);
+               return;
+        }
+}
+
+void g_dbus_pending_error(DBusConnection *connection,
+                               GDBusPendingReply pending,
+                               const char *name, const char *format, ...)
+{
+       va_list args;
+
+       va_start(args, format);
+
+       g_dbus_pending_error_valist(connection, pending, name, format, args);
+
+       va_end(args);
+}
+
+int polkit_check_authorization(DBusConnection *conn,
+                               const char *action, gboolean interaction,
+                               void (*function) (dbus_bool_t authorized,
+                                                       void *user_data),
+                                               void *user_data, int timeout);
+
+struct builtin_security_data {
+       DBusConnection *conn;
+       GDBusPendingReply pending;
+};
+
+static void builtin_security_result(dbus_bool_t authorized, void *user_data)
+{
+       struct builtin_security_data *data = user_data;
+
+       if (authorized == TRUE)
+               g_dbus_pending_success(data->conn, data->pending);
+       else
+               g_dbus_pending_error(data->conn, data->pending,
+                                               DBUS_ERROR_AUTH_FAILED, NULL);
+
+       g_free(data);
+}
+
+static void builtin_security_function(DBusConnection *conn,
+                                               const char *action,
+                                               gboolean interaction,
+                                               GDBusPendingReply pending)
+{
+       struct builtin_security_data *data;
+
+       data = g_new0(struct builtin_security_data, 1);
+       data->conn = conn;
+       data->pending = pending;
+
+       if (polkit_check_authorization(conn, action, interaction,
+                               builtin_security_result, data, 30000) < 0)
+               g_dbus_pending_error(conn, pending, NULL, NULL);
+}
+
+static gboolean check_privilege(DBusConnection *conn, DBusMessage *msg,
+                       const GDBusMethodTable *method, void *iface_user_data)
+{
+       const GDBusSecurityTable *security;
+
+       for (security = security_table; security && security->privilege;
+                                                               security++) {
+               struct security_data *secdata;
+               gboolean interaction;
+
+               if (security->privilege != method->privilege)
+                       continue;
+
+               secdata = g_new(struct security_data, 1);
+               secdata->pending = next_pending++;
+               secdata->message = dbus_message_ref(msg);
+               secdata->method = method;
+               secdata->iface_user_data = iface_user_data;
+
+               pending_security = g_slist_prepend(pending_security, secdata);
+
+               if (security->flags & G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION)
+                       interaction = TRUE;
+               else
+                       interaction = FALSE;
+
+               if (!(security->flags & G_DBUS_SECURITY_FLAG_BUILTIN) &&
+                                                       security->function)
+                       security->function(conn, security->action,
+                                               interaction, secdata->pending);
+               else
+                       builtin_security_function(conn, security->action,
+                                               interaction, secdata->pending);
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void generic_unregister(DBusConnection *connection, void *user_data)
+{
+       struct generic_data *data = user_data;
+
+       g_free(data->introspect);
+       g_free(data);
+}
+
+static struct interface_data *find_interface(GSList *interfaces,
+                                               const char *name)
+{
+       GSList *list;
+
+       if (name == NULL)
+               return NULL;
+
+       for (list = interfaces; list; list = list->next) {
+               struct interface_data *iface = list->data;
+               if (!strcmp(name, iface->name))
+                       return iface;
+       }
+
+       return NULL;
+}
+
+static gboolean g_dbus_args_have_signature(const GDBusArgInfo *args,
+                                                       DBusMessage *message)
+{
+       const char *sig = dbus_message_get_signature(message);
+       const char *p = NULL;
+
+       for (; args && args->signature && *sig; args++) {
+               p = args->signature;
+
+               for (; *sig && *p; sig++, p++) {
+                       if (*p != *sig)
+                               return FALSE;
+               }
+       }
+
+       if (*sig || (p && *p) || (args && args->signature))
+               return FALSE;
+
+       return TRUE;
+}
+
+static DBusHandlerResult generic_message(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct generic_data *data = user_data;
+       struct interface_data *iface;
+       const GDBusMethodTable *method;
+       const char *interface;
+
+       interface = dbus_message_get_interface(message);
+
+       iface = find_interface(data->interfaces, interface);
+       if (iface == NULL)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       for (method = iface->methods; method &&
+                       method->name && method->function; method++) {
+               if (dbus_message_is_method_call(message, iface->name,
+                                                       method->name) == FALSE)
+                       continue;
+
+               if (g_dbus_args_have_signature(method->in_args,
+                                                       message) == FALSE)
+                       continue;
+
+               if (check_privilege(connection, message, method,
+                                               iface->user_data) == TRUE)
+                       return DBUS_HANDLER_RESULT_HANDLED;
+
+               return process_message(connection, message, method,
+                                                       iface->user_data);
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusObjectPathVTable generic_table = {
+       .unregister_function    = generic_unregister,
+       .message_function       = generic_message,
+};
+
+static void invalidate_parent_data(DBusConnection *conn, const char *child_path)
+{
+       struct generic_data *data = NULL;
+       char *parent_path, *slash;
+
+       parent_path = g_strdup(child_path);
+       slash = strrchr(parent_path, '/');
+       if (slash == NULL)
+               goto done;
+
+       if (slash == parent_path && parent_path[1] != '\0')
+               parent_path[1] = '\0';
+       else
+               *slash = '\0';
+
+       if (!strlen(parent_path))
+               goto done;
+
+       if (dbus_connection_get_object_path_data(conn, parent_path,
+                                                       (void *) &data) == FALSE) {
+               goto done;
+       }
+
+       invalidate_parent_data(conn, parent_path);
+
+       if (data == NULL)
+               goto done;
+
+       g_free(data->introspect);
+       data->introspect = NULL;
+
+done:
+       g_free(parent_path);
+}
+
+static const GDBusMethodTable introspect_methods[] = {
+       { GDBUS_METHOD("Introspect", NULL,
+                       GDBUS_ARGS({ "xml", "s" }), introspect) },
+       { }
+};
+
+static void add_interface(struct generic_data *data, const char *name,
+                               const GDBusMethodTable *methods,
+                               const GDBusSignalTable *signals,
+                               const GDBusPropertyTable *properties,
+                               void *user_data,
+                               GDBusDestroyFunction destroy)
+{
+       struct interface_data *iface;
+
+       iface = g_new0(struct interface_data, 1);
+       iface->name = g_strdup(name);
+       iface->methods = methods;
+       iface->signals = signals;
+       iface->properties = properties;
+       iface->user_data = user_data;
+       iface->destroy = destroy;
+
+       data->interfaces = g_slist_append(data->interfaces, iface);
+}
+
+static struct generic_data *object_path_ref(DBusConnection *connection,
+                                                       const char *path)
+{
+       struct generic_data *data;
+
+       if (dbus_connection_get_object_path_data(connection, path,
+                                               (void *) &data) == TRUE) {
+               if (data != NULL) {
+                       data->refcount++;
+                       return data;
+               }
+       }
+
+       data = g_new0(struct generic_data, 1);
+       data->refcount = 1;
+
+       data->introspect = g_strdup(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "<node></node>");
+
+       if (!dbus_connection_register_object_path(connection, path,
+                                               &generic_table, data)) {
+               g_free(data->introspect);
+               g_free(data);
+               return NULL;
+       }
+
+       invalidate_parent_data(connection, path);
+
+       add_interface(data, DBUS_INTERFACE_INTROSPECTABLE,
+                       introspect_methods, NULL, NULL, data, NULL);
+
+       return data;
+}
+
+static gboolean remove_interface(struct generic_data *data, const char *name)
+{
+       struct interface_data *iface;
+
+       iface = find_interface(data->interfaces, name);
+       if (iface == NULL)
+               return FALSE;
+
+       data->interfaces = g_slist_remove(data->interfaces, iface);
+
+       if (iface->destroy)
+               iface->destroy(iface->user_data);
+
+       g_free(iface->name);
+       g_free(iface);
+
+       return TRUE;
+}
+
+static void object_path_unref(DBusConnection *connection, const char *path)
+{
+       struct generic_data *data = NULL;
+
+       if (dbus_connection_get_object_path_data(connection, path,
+                                               (void *) &data) == FALSE)
+               return;
+
+       if (data == NULL)
+               return;
+
+       data->refcount--;
+
+       if (data->refcount > 0)
+               return;
+
+       remove_interface(data, DBUS_INTERFACE_INTROSPECTABLE);
+
+       invalidate_parent_data(connection, path);
+
+       dbus_connection_unregister_object_path(connection, path);
+}
+
+static gboolean check_signal(DBusConnection *conn, const char *path,
+                               const char *interface, const char *name,
+                               const GDBusArgInfo **args)
+{
+       struct generic_data *data = NULL;
+       struct interface_data *iface;
+       const GDBusSignalTable *signal;
+
+       *args = NULL;
+       if (!dbus_connection_get_object_path_data(conn, path,
+                                       (void *) &data) || data == NULL) {
+               error("dbus_connection_emit_signal: path %s isn't registered",
+                               path);
+               return FALSE;
+       }
+
+       iface = find_interface(data->interfaces, interface);
+       if (iface == NULL) {
+               error("dbus_connection_emit_signal: %s does not implement %s",
+                               path, interface);
+               return FALSE;
+       }
+
+       for (signal = iface->signals; signal && signal->name; signal++) {
+               if (!strcmp(signal->name, name)) {
+                       *args = signal->args;
+                       return TRUE;
+               }
+       }
+
+       error("No signal named %s on interface %s", name, interface);
+       return FALSE;
+}
+
+static dbus_bool_t emit_signal_valist(DBusConnection *conn,
+                                               const char *path,
+                                               const char *interface,
+                                               const char *name,
+                                               int first,
+                                               va_list var_args)
+{
+       DBusMessage *signal;
+       dbus_bool_t ret;
+       const GDBusArgInfo *args;
+
+       if (!check_signal(conn, path, interface, name, &args))
+               return FALSE;
+
+       signal = dbus_message_new_signal(path, interface, name);
+       if (signal == NULL) {
+               error("Unable to allocate new %s.%s signal", interface,  name);
+               return FALSE;
+       }
+
+       ret = dbus_message_append_args_valist(signal, first, var_args);
+       if (!ret)
+               goto fail;
+
+       if (g_dbus_args_have_signature(args, signal) == FALSE) {
+               error("%s.%s: got unexpected signature '%s'", interface, name,
+                                       dbus_message_get_signature(signal));
+               ret = FALSE;
+               goto fail;
+       }
+
+       ret = dbus_connection_send(conn, signal, NULL);
+
+fail:
+       dbus_message_unref(signal);
+
+       return ret;
+}
+
+gboolean g_dbus_register_interface(DBusConnection *connection,
+                                       const char *path, const char *name,
+                                       const GDBusMethodTable *methods,
+                                       const GDBusSignalTable *signals,
+                                       const GDBusPropertyTable *properties,
+                                       void *user_data,
+                                       GDBusDestroyFunction destroy)
+{
+       struct generic_data *data;
+
+       data = object_path_ref(connection, path);
+       if (data == NULL)
+               return FALSE;
+
+       if (find_interface(data->interfaces, name)) {
+               object_path_unref(connection, path);
+               return FALSE;
+       }
+
+       add_interface(data, name, methods, signals,
+                       properties, user_data, destroy);
+
+       g_free(data->introspect);
+       data->introspect = NULL;
+
+       return TRUE;
+}
+
+gboolean g_dbus_unregister_interface(DBusConnection *connection,
+                                       const char *path, const char *name)
+{
+       struct generic_data *data = NULL;
+
+       if (path == NULL)
+               return FALSE;
+
+       if (dbus_connection_get_object_path_data(connection, path,
+                                               (void *) &data) == FALSE)
+               return FALSE;
+
+       if (data == NULL)
+               return FALSE;
+
+       if (remove_interface(data, name) == FALSE)
+               return FALSE;
+
+       g_free(data->introspect);
+       data->introspect = NULL;
+
+       object_path_unref(connection, path);
+
+       return TRUE;
+}
+
+gboolean g_dbus_register_security(const GDBusSecurityTable *security)
+{
+       if (security_table != NULL)
+               return FALSE;
+
+       security_table = security;
+
+       return TRUE;
+}
+
+gboolean g_dbus_unregister_security(const GDBusSecurityTable *security)
+{
+       security_table = NULL;
+
+       return TRUE;
+}
+
+DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name,
+                                       const char *format, va_list args)
+{
+       char str[1024];
+
+       vsnprintf(str, sizeof(str), format, args);
+
+       return dbus_message_new_error(message, name, str);
+}
+
+DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name,
+                                               const char *format, ...)
+{
+       va_list args;
+       DBusMessage *reply;
+
+       va_start(args, format);
+
+       reply = g_dbus_create_error_valist(message, name, format, args);
+
+       va_end(args);
+
+       return reply;
+}
+
+DBusMessage *g_dbus_create_reply_valist(DBusMessage *message,
+                                               int type, va_list args)
+{
+       DBusMessage *reply;
+
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL)
+               return NULL;
+
+       if (dbus_message_append_args_valist(reply, type, args) == FALSE) {
+               dbus_message_unref(reply);
+               return NULL;
+       }
+
+       return reply;
+}
+
+DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...)
+{
+       va_list args;
+       DBusMessage *reply;
+
+       va_start(args, type);
+
+       reply = g_dbus_create_reply_valist(message, type, args);
+
+       va_end(args);
+
+       return reply;
+}
+
+gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message)
+{
+       dbus_bool_t result;
+
+       if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL)
+               dbus_message_set_no_reply(message, TRUE);
+
+       result = dbus_connection_send(connection, message, NULL);
+
+       dbus_message_unref(message);
+
+       return result;
+}
+
+gboolean g_dbus_send_reply_valist(DBusConnection *connection,
+                               DBusMessage *message, int type, va_list args)
+{
+       DBusMessage *reply;
+
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL)
+               return FALSE;
+
+       if (dbus_message_append_args_valist(reply, type, args) == FALSE) {
+               dbus_message_unref(reply);
+               return FALSE;
+       }
+
+       return g_dbus_send_message(connection, reply);
+}
+
+gboolean g_dbus_send_reply(DBusConnection *connection,
+                               DBusMessage *message, int type, ...)
+{
+       va_list args;
+       gboolean result;
+
+       va_start(args, type);
+
+       result = g_dbus_send_reply_valist(connection, message, type, args);
+
+       va_end(args);
+
+       return result;
+}
+
+gboolean g_dbus_emit_signal(DBusConnection *connection,
+                               const char *path, const char *interface,
+                               const char *name, int type, ...)
+{
+       va_list args;
+       gboolean result;
+
+       va_start(args, type);
+
+       result = emit_signal_valist(connection, path, interface,
+                                                       name, type, args);
+
+       va_end(args);
+
+       return result;
+}
+
+gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
+                               const char *path, const char *interface,
+                               const char *name, int type, va_list args)
+{
+       return emit_signal_valist(connection, path, interface,
+                                                       name, type, args);
+}
diff --git a/gdbus/polkit.c b/gdbus/polkit.c
new file mode 100644 (file)
index 0000000..9e95fa3
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.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.
+ *
+ *  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <dbus/dbus.h>
+
+#include <glib.h>
+
+int polkit_check_authorization(DBusConnection *conn,
+                               const char *action, gboolean interaction,
+                               void (*function) (dbus_bool_t authorized,
+                                                       void *user_data),
+                                               void *user_data, int timeout);
+
+static void add_dict_with_string_value(DBusMessageIter *iter,
+                                       const char *key, const char *str)
+{
+       DBusMessageIter dict, entry, value;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+       dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
+                                                               NULL, &entry);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+                                       DBUS_TYPE_STRING_AS_STRING, &value);
+       dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str);
+       dbus_message_iter_close_container(&entry, &value);
+
+       dbus_message_iter_close_container(&dict, &entry);
+       dbus_message_iter_close_container(iter, &dict);
+}
+
+static void add_empty_string_dict(DBusMessageIter *iter)
+{
+       DBusMessageIter dict;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       dbus_message_iter_close_container(iter, &dict);
+}
+
+static void add_arguments(DBusConnection *conn, DBusMessageIter *iter,
+                               const char *action, dbus_uint32_t flags)
+{
+       const char *busname = dbus_bus_get_unique_name(conn);
+       const char *kind = "system-bus-name";
+       const char *cancel = "";
+       DBusMessageIter subject;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
+                                                       NULL, &subject);
+       dbus_message_iter_append_basic(&subject, DBUS_TYPE_STRING, &kind);
+       add_dict_with_string_value(&subject, "name", busname);
+       dbus_message_iter_close_container(iter, &subject);
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &action);
+       add_empty_string_dict(iter);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &flags);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &cancel);
+}
+
+static dbus_bool_t parse_result(DBusMessageIter *iter)
+{
+       DBusMessageIter result;
+       dbus_bool_t authorized, challenge;
+
+       dbus_message_iter_recurse(iter, &result);
+
+       dbus_message_iter_get_basic(&result, &authorized);
+       dbus_message_iter_get_basic(&result, &challenge);
+
+       return authorized;
+}
+
+struct authorization_data {
+       void (*function) (dbus_bool_t authorized, void *user_data);
+       void *user_data;
+};
+
+static void authorization_reply(DBusPendingCall *call, void *user_data)
+{
+       struct authorization_data *data = user_data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       dbus_bool_t authorized = FALSE;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+               goto done;
+
+       if (dbus_message_has_signature(reply, "(bba{ss})") == FALSE)
+               goto done;
+
+       dbus_message_iter_init(reply, &iter);
+
+       authorized = parse_result(&iter);
+
+done:
+       if (data->function != NULL)
+               data->function(authorized, data->user_data);
+
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+#define AUTHORITY_DBUS "org.freedesktop.PolicyKit1"
+#define AUTHORITY_INTF "org.freedesktop.PolicyKit1.Authority"
+#define AUTHORITY_PATH "/org/freedesktop/PolicyKit1/Authority"
+
+int polkit_check_authorization(DBusConnection *conn,
+                               const char *action, gboolean interaction,
+                               void (*function) (dbus_bool_t authorized,
+                                                       void *user_data),
+                                               void *user_data, int timeout)
+{
+       struct authorization_data *data;
+       DBusMessage *msg;
+       DBusMessageIter iter;
+       DBusPendingCall *call;
+       dbus_uint32_t flags = 0x00000000;
+
+       if (conn == NULL)
+               return -EINVAL;
+
+       data = dbus_malloc0(sizeof(*data));
+       if (data == NULL)
+               return -ENOMEM;
+
+       msg = dbus_message_new_method_call(AUTHORITY_DBUS, AUTHORITY_PATH,
+                               AUTHORITY_INTF, "CheckAuthorization");
+       if (msg == NULL) {
+               dbus_free(data);
+               return -ENOMEM;
+       }
+
+       if (interaction == TRUE)
+               flags |= 0x00000001;
+
+       if (action == NULL)
+               action = "org.freedesktop.policykit.exec";
+
+       dbus_message_iter_init_append(msg, &iter);
+       add_arguments(conn, &iter, action, flags);
+
+       if (dbus_connection_send_with_reply(conn, msg,
+                                               &call, timeout) == FALSE) {
+               dbus_message_unref(msg);
+               dbus_free(data);
+               return -EIO;
+       }
+
+       if (call == NULL) {
+               dbus_message_unref(msg);
+               dbus_free(data);
+               return -EIO;
+       }
+
+       data->function = function;
+       data->user_data = user_data;
+
+       dbus_pending_call_set_notify(call, authorization_reply,
+                                                       data, dbus_free);
+
+       dbus_message_unref(msg);
+
+       return 0;
+}
diff --git a/gdbus/watch.c b/gdbus/watch.c
new file mode 100644 (file)
index 0000000..d749176
--- /dev/null
@@ -0,0 +1,747 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.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.
+ *
+ *  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus.h"
+
+#define info(fmt...)
+#define error(fmt...)
+#define debug(fmt...)
+
+static DBusHandlerResult message_filter(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data);
+
+static guint listener_id = 0;
+static GSList *listeners = NULL;
+
+struct service_data {
+       DBusConnection *conn;
+       DBusPendingCall *call;
+       char *name;
+       const char *owner;
+       guint id;
+       struct filter_callback *callback;
+};
+
+struct filter_callback {
+       GDBusWatchFunction conn_func;
+       GDBusWatchFunction disc_func;
+       GDBusSignalFunction signal_func;
+       GDBusDestroyFunction destroy_func;
+       struct service_data *data;
+       void *user_data;
+       guint id;
+};
+
+struct filter_data {
+       DBusConnection *connection;
+       DBusHandleMessageFunction handle_func;
+       char *name;
+       char *owner;
+       char *path;
+       char *interface;
+       char *member;
+       char *argument;
+       GSList *callbacks;
+       GSList *processed;
+       guint name_watch;
+       gboolean lock;
+       gboolean registered;
+};
+
+static struct filter_data *filter_data_find(DBusConnection *connection,
+                                                       const char *name,
+                                                       const char *owner,
+                                                       const char *path,
+                                                       const char *interface,
+                                                       const char *member,
+                                                       const char *argument)
+{
+       GSList *current;
+
+       for (current = listeners;
+                       current != NULL; current = current->next) {
+               struct filter_data *data = current->data;
+
+               if (connection != data->connection)
+                       continue;
+
+               if (name && data->name &&
+                               g_str_equal(name, data->name) == FALSE)
+                       continue;
+
+               if (owner && data->owner &&
+                               g_str_equal(owner, data->owner) == FALSE)
+                       continue;
+
+               if (path && data->path &&
+                               g_str_equal(path, data->path) == FALSE)
+                       continue;
+
+               if (interface && data->interface &&
+                               g_str_equal(interface, data->interface) == FALSE)
+                       continue;
+
+               if (member && data->member &&
+                               g_str_equal(member, data->member) == FALSE)
+                       continue;
+
+               if (argument && data->argument &&
+                               g_str_equal(argument, data->argument) == FALSE)
+                       continue;
+
+               return data;
+       }
+
+       return NULL;
+}
+
+static void format_rule(struct filter_data *data, char *rule, size_t size)
+{
+       const char *sender;
+       int offset;
+
+       offset = snprintf(rule, size, "type='signal'");
+       sender = data->name ? : data->owner;
+
+       if (sender)
+               offset += snprintf(rule + offset, size - offset,
+                               ",sender='%s'", sender);
+       if (data->path)
+               offset += snprintf(rule + offset, size - offset,
+                               ",path='%s'", data->path);
+       if (data->interface)
+               offset += snprintf(rule + offset, size - offset,
+                               ",interface='%s'", data->interface);
+       if (data->member)
+               offset += snprintf(rule + offset, size - offset,
+                               ",member='%s'", data->member);
+       if (data->argument)
+               snprintf(rule + offset, size - offset,
+                               ",arg0='%s'", data->argument);
+}
+
+static gboolean add_match(struct filter_data *data,
+                               DBusHandleMessageFunction filter)
+{
+       DBusError err;
+       char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
+
+       format_rule(data, rule, sizeof(rule));
+       dbus_error_init(&err);
+
+       dbus_bus_add_match(data->connection, rule, &err);
+       if (dbus_error_is_set(&err)) {
+               error("Adding match rule \"%s\" failed: %s", rule,
+                               err.message);
+               dbus_error_free(&err);
+               return FALSE;
+       }
+
+       data->handle_func = filter;
+       data->registered = TRUE;
+
+       return TRUE;
+}
+
+static gboolean remove_match(struct filter_data *data)
+{
+       DBusError err;
+       char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
+
+       format_rule(data, rule, sizeof(rule));
+
+       dbus_error_init(&err);
+
+       dbus_bus_remove_match(data->connection, rule, &err);
+       if (dbus_error_is_set(&err)) {
+               error("Removing owner match rule for %s failed: %s",
+                               rule, err.message);
+               dbus_error_free(&err);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static struct filter_data *filter_data_get(DBusConnection *connection,
+                                       DBusHandleMessageFunction filter,
+                                       const char *sender,
+                                       const char *path,
+                                       const char *interface,
+                                       const char *member,
+                                       const char *argument)
+{
+       struct filter_data *data;
+       const char *name = NULL, *owner = NULL;
+
+       if (filter_data_find(connection, NULL, NULL, NULL, NULL, NULL, NULL) == NULL) {
+               if (!dbus_connection_add_filter(connection,
+                                       message_filter, NULL, NULL)) {
+                       error("dbus_connection_add_filter() failed");
+                       return NULL;
+               }
+       }
+
+       if (sender == NULL)
+               goto proceed;
+
+       if (sender[0] == ':')
+               owner = sender;
+       else
+               name = sender;
+
+proceed:
+       data = filter_data_find(connection, name, owner, path, interface,
+                                       member, argument);
+       if (data)
+               return data;
+
+       data = g_new0(struct filter_data, 1);
+
+       data->connection = dbus_connection_ref(connection);
+       data->name = name ? g_strdup(name) : NULL;
+       data->owner = owner ? g_strdup(owner) : NULL;
+       data->path = g_strdup(path);
+       data->interface = g_strdup(interface);
+       data->member = g_strdup(member);
+       data->argument = g_strdup(argument);
+
+       if (!add_match(data, filter)) {
+               g_free(data);
+               return NULL;
+       }
+
+       listeners = g_slist_append(listeners, data);
+
+       return data;
+}
+
+static struct filter_callback *filter_data_find_callback(
+                                               struct filter_data *data,
+                                               guint id)
+{
+       GSList *l;
+
+       for (l = data->callbacks; l; l = l->next) {
+               struct filter_callback *cb = l->data;
+               if (cb->id == id)
+                       return cb;
+       }
+       for (l = data->processed; l; l = l->next) {
+               struct filter_callback *cb = l->data;
+               if (cb->id == id)
+                       return cb;
+       }
+
+       return NULL;
+}
+
+static void filter_data_free(struct filter_data *data)
+{
+       GSList *l;
+
+       for (l = data->callbacks; l != NULL; l = l->next)
+               g_free(l->data);
+
+       g_slist_free(data->callbacks);
+       g_dbus_remove_watch(data->connection, data->name_watch);
+       g_free(data->name);
+       g_free(data->owner);
+       g_free(data->path);
+       g_free(data->interface);
+       g_free(data->member);
+       g_free(data->argument);
+       dbus_connection_unref(data->connection);
+       g_free(data);
+}
+
+static void filter_data_call_and_free(struct filter_data *data)
+{
+       GSList *l;
+
+       for (l = data->callbacks; l != NULL; l = l->next) {
+               struct filter_callback *cb = l->data;
+               if (cb->disc_func)
+                       cb->disc_func(data->connection, cb->user_data);
+               if (cb->destroy_func)
+                       cb->destroy_func(cb->user_data);
+               g_free(cb);
+       }
+
+       filter_data_free(data);
+}
+
+static struct filter_callback *filter_data_add_callback(
+                                               struct filter_data *data,
+                                               GDBusWatchFunction connect,
+                                               GDBusWatchFunction disconnect,
+                                               GDBusSignalFunction signal,
+                                               GDBusDestroyFunction destroy,
+                                               void *user_data)
+{
+       struct filter_callback *cb = NULL;
+
+       cb = g_new0(struct filter_callback, 1);
+
+       cb->conn_func = connect;
+       cb->disc_func = disconnect;
+       cb->signal_func = signal;
+       cb->destroy_func = destroy;
+       cb->user_data = user_data;
+       cb->id = ++listener_id;
+
+       if (data->lock)
+               data->processed = g_slist_append(data->processed, cb);
+       else
+               data->callbacks = g_slist_append(data->callbacks, cb);
+
+       return cb;
+}
+
+static void service_data_free(struct service_data *data)
+{
+       struct filter_callback *callback = data->callback;
+
+       dbus_connection_unref(data->conn);
+
+       if (data->call)
+               dbus_pending_call_unref(data->call);
+
+       if (data->id)
+               g_source_remove(data->id);
+
+       g_free(data->name);
+       g_free(data);
+
+       callback->data = NULL;
+}
+
+static gboolean filter_data_remove_callback(struct filter_data *data,
+                                               struct filter_callback *cb)
+{
+       DBusConnection *connection;
+
+       data->callbacks = g_slist_remove(data->callbacks, cb);
+       data->processed = g_slist_remove(data->processed, cb);
+
+       /* Cancel pending operations */
+       if (cb->data) {
+               if (cb->data->call)
+                       dbus_pending_call_cancel(cb->data->call);
+               service_data_free(cb->data);
+       }
+
+       if (cb->destroy_func)
+               cb->destroy_func(cb->user_data);
+
+       g_free(cb);
+
+       /* Don't remove the filter if other callbacks exist or data is lock
+        * processing callbacks */
+       if (data->callbacks || data->lock)
+               return TRUE;
+
+       if (data->registered && !remove_match(data))
+               return FALSE;
+
+       connection = dbus_connection_ref(data->connection);
+       listeners = g_slist_remove(listeners, data);
+
+       /* Remove filter if there are no listeners left for the connection */
+       if (filter_data_find(connection, NULL, NULL, NULL, NULL, NULL,
+                                                               NULL) == NULL)
+               dbus_connection_remove_filter(connection, message_filter,
+                                               NULL);
+
+       filter_data_free(data);
+       dbus_connection_unref(connection);
+
+       return TRUE;
+}
+
+static DBusHandlerResult signal_filter(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct filter_data *data = user_data;
+       struct filter_callback *cb;
+
+       while (data->callbacks) {
+               cb = data->callbacks->data;
+
+               if (cb->signal_func && !cb->signal_func(connection, message,
+                                                       cb->user_data)) {
+                       filter_data_remove_callback(data, cb);
+                       continue;
+               }
+
+               /* Check if the watch was removed/freed by the callback
+                * function */
+               if (!g_slist_find(data->callbacks, cb))
+                       continue;
+
+               data->callbacks = g_slist_remove(data->callbacks, cb);
+               data->processed = g_slist_append(data->processed, cb);
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static void update_name_cache(const char *name, const char *owner)
+{
+       GSList *l;
+
+       for (l = listeners; l != NULL; l = l->next) {
+               struct filter_data *data = l->data;
+
+               if (g_strcmp0(data->name, name) != 0)
+                       continue;
+
+               g_free(data->owner);
+               data->owner = g_strdup(owner);
+       }
+}
+
+static const char *check_name_cache(const char *name)
+{
+       GSList *l;
+
+       for (l = listeners; l != NULL; l = l->next) {
+               struct filter_data *data = l->data;
+
+               if (g_strcmp0(data->name, name) != 0)
+                       continue;
+
+               return data->owner;
+       }
+
+       return NULL;
+}
+
+static DBusHandlerResult service_filter(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct filter_data *data = user_data;
+       struct filter_callback *cb;
+       char *name, *old, *new;
+
+       if (!dbus_message_get_args(message, NULL,
+                               DBUS_TYPE_STRING, &name,
+                               DBUS_TYPE_STRING, &old,
+                               DBUS_TYPE_STRING, &new,
+                               DBUS_TYPE_INVALID)) {
+               error("Invalid arguments for NameOwnerChanged signal");
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       }
+
+       update_name_cache(name, new);
+
+       while (data->callbacks) {
+               cb = data->callbacks->data;
+
+               if (*new == '\0') {
+                       if (cb->disc_func)
+                               cb->disc_func(connection, cb->user_data);
+               } else {
+                       if (cb->conn_func)
+                               cb->conn_func(connection, cb->user_data);
+               }
+
+               /* Check if the watch was removed/freed by the callback
+                * function */
+               if (!g_slist_find(data->callbacks, cb))
+                       continue;
+
+               /* Only auto remove if it is a bus name watch */
+               if (data->argument[0] == ':' &&
+                               (cb->conn_func == NULL || cb->disc_func == NULL)) {
+                       filter_data_remove_callback(data, cb);
+                       continue;
+               }
+
+               data->callbacks = g_slist_remove(data->callbacks, cb);
+               data->processed = g_slist_append(data->processed, cb);
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+static DBusHandlerResult message_filter(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct filter_data *data;
+       const char *sender, *path, *iface, *member, *arg = NULL;
+
+       /* Only filter signals */
+       if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       sender = dbus_message_get_sender(message);
+       path = dbus_message_get_path(message);
+       iface = dbus_message_get_interface(message);
+       member = dbus_message_get_member(message);
+       dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID);
+
+       /* Sender is always bus name */
+       data = filter_data_find(connection, NULL, sender, path, iface, member,
+                                       arg);
+       if (data == NULL) {
+               error("Got %s.%s signal which has no listeners", iface, member);
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       }
+
+       if (data->handle_func) {
+               data->lock = TRUE;
+
+               data->handle_func(connection, message, data);
+
+               data->callbacks = data->processed;
+               data->processed = NULL;
+               data->lock = FALSE;
+       }
+
+       if (data->callbacks)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       remove_match(data);
+
+       listeners = g_slist_remove(listeners, data);
+
+       /* Remove filter if there are no listeners left for the connection */
+       if (filter_data_find(connection, NULL, NULL, NULL, NULL, NULL,
+                                                               NULL) == NULL)
+               dbus_connection_remove_filter(connection, message_filter,
+                                               NULL);
+
+       filter_data_free(data);
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static gboolean update_service(void *user_data)
+{
+       struct service_data *data = user_data;
+       struct filter_callback *cb = data->callback;
+
+       update_name_cache(data->name, data->owner);
+       if (cb->conn_func)
+               cb->conn_func(data->conn, cb->user_data);
+
+       service_data_free(data);
+
+       return FALSE;
+}
+
+static void service_reply(DBusPendingCall *call, void *user_data)
+{
+       struct service_data *data = user_data;
+       DBusMessage *reply;
+       DBusError err;
+
+       reply = dbus_pending_call_steal_reply(call);
+       if (reply == NULL)
+               return;
+
+       dbus_error_init(&err);
+
+       if (dbus_set_error_from_message(&err, reply))
+               goto fail;
+
+       if (dbus_message_get_args(reply, &err,
+                                       DBUS_TYPE_STRING, &data->owner,
+                                               DBUS_TYPE_INVALID) == FALSE)
+               goto fail;
+
+       update_service(data);
+
+       goto done;
+
+fail:
+       error("%s", err.message);
+       dbus_error_free(&err);
+       service_data_free(data);
+done:
+       dbus_message_unref(reply);
+}
+
+static void check_service(DBusConnection *connection,
+                                       const char *name,
+                                       struct filter_callback *callback)
+{
+       DBusMessage *message;
+       struct service_data *data;
+
+       data = g_try_malloc0(sizeof(*data));
+       if (data == NULL) {
+               error("Can't allocate data structure");
+               return;
+       }
+
+       data->conn = dbus_connection_ref(connection);
+       data->name = g_strdup(name);
+       data->callback = callback;
+       callback->data = data;
+
+       data->owner = check_name_cache(name);
+       if (data->owner != NULL) {
+               data->id = g_idle_add(update_service, data);
+               return;
+       }
+
+       message = dbus_message_new_method_call(DBUS_SERVICE_DBUS,
+                       DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner");
+       if (message == NULL) {
+               error("Can't allocate new message");
+               g_free(data);
+               return;
+       }
+
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &name,
+                                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                                       &data->call, -1) == FALSE) {
+               error("Failed to execute method call");
+               g_free(data);
+               goto done;
+       }
+
+       if (data->call == NULL) {
+               error("D-Bus connection not available");
+               g_free(data);
+               goto done;
+       }
+
+       dbus_pending_call_set_notify(data->call, service_reply, data, NULL);
+
+done:
+       dbus_message_unref(message);
+}
+
+guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
+                               GDBusWatchFunction connect,
+                               GDBusWatchFunction disconnect,
+                               void *user_data, GDBusDestroyFunction destroy)
+{
+       struct filter_data *data;
+       struct filter_callback *cb;
+
+       if (name == NULL)
+               return 0;
+
+       data = filter_data_get(connection, service_filter, NULL, NULL,
+                               DBUS_INTERFACE_DBUS, "NameOwnerChanged",
+                               name);
+       if (data == NULL)
+               return 0;
+
+       cb = filter_data_add_callback(data, connect, disconnect, NULL, destroy,
+                                       user_data);
+       if (cb == NULL)
+               return 0;
+
+       if (connect)
+               check_service(connection, name, cb);
+
+       return cb->id;
+}
+
+guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name,
+                               GDBusWatchFunction func,
+                               void *user_data, GDBusDestroyFunction destroy)
+{
+       return g_dbus_add_service_watch(connection, name, NULL, func,
+                                                       user_data, destroy);
+}
+
+guint g_dbus_add_signal_watch(DBusConnection *connection,
+                               const char *sender, const char *path,
+                               const char *interface, const char *member,
+                               GDBusSignalFunction function, void *user_data,
+                               GDBusDestroyFunction destroy)
+{
+       struct filter_data *data;
+       struct filter_callback *cb;
+
+       data = filter_data_get(connection, signal_filter, sender, path,
+                               interface, member, NULL);
+       if (data == NULL)
+               return 0;
+
+       cb = filter_data_add_callback(data, NULL, NULL, function, destroy,
+                                       user_data);
+       if (cb == NULL)
+               return 0;
+
+       if (data->name != NULL && data->name_watch == 0)
+               data->name_watch = g_dbus_add_service_watch(connection,
+                                                       data->name, NULL,
+                                                       NULL, NULL, NULL);
+
+       return cb->id;
+}
+
+gboolean g_dbus_remove_watch(DBusConnection *connection, guint id)
+{
+       struct filter_data *data;
+       struct filter_callback *cb;
+       GSList *ldata;
+
+       if (id == 0)
+               return FALSE;
+
+       for (ldata = listeners; ldata; ldata = ldata->next) {
+               data = ldata->data;
+
+               cb = filter_data_find_callback(data, id);
+               if (cb) {
+                       filter_data_remove_callback(data, cb);
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+void g_dbus_remove_all_watches(DBusConnection *connection)
+{
+       struct filter_data *data;
+
+       while ((data = filter_data_find(connection, NULL, NULL, NULL, NULL,
+                                       NULL, NULL))) {
+               listeners = g_slist_remove(listeners, data);
+               filter_data_call_and_free(data);
+       }
+
+       dbus_connection_remove_filter(connection, message_filter, NULL);
+}
diff --git a/include/adapter.h b/include/adapter.h
new file mode 100644 (file)
index 0000000..e547d3d
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __NEAR_ADAPTER_H
+#define __NEAR_ADAPTER_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+typedef int (*near_recv)(uint8_t *resp, int length, void *data);
+typedef int (*near_release)(int err, void *data);
+
+int near_adapter_connect(uint32_t idx, uint32_t target_idx, uint8_t protocol);
+int near_adapter_disconnect(uint32_t idx);
+int near_adapter_send(uint32_t idx, uint8_t *buf, size_t length,
+                       near_recv rx_cb, void *data, near_release data_rel);
+int near_adapter_recv(uint32_t idx, uint8_t *buf, size_t length);
+
+#endif
diff --git a/include/dbus.h b/include/dbus.h
new file mode 100644 (file)
index 0000000..e5c74af
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <dbus/dbus.h>
+
+#define NFC_SERVICE     "org.neard"
+#define NFC_PATH       "/org/neard"
+
+#define NFC_ERROR_INTERFACE     NFC_SERVICE ".Error"
+#define NFC_NDEF_AGENT_INTERFACE NFC_SERVICE ".NDEFAgent"
+
+#define NFC_MANAGER_INTERFACE   NFC_SERVICE ".Manager"
+#define NFC_MANAGER_PATH        "/"
+
+#define NFC_ADAPTER_INTERFACE   NFC_SERVICE ".Adapter"
+#define NFC_DEVICE_INTERFACE    NFC_SERVICE ".Device"
+#define NFC_TAG_INTERFACE       NFC_SERVICE ".Tag"
+#define NFC_RECORD_INTERFACE    NFC_SERVICE ".Record"
+
+typedef void (* near_dbus_append_cb_t) (DBusMessageIter *iter,
+                                                       void *user_data);
+
+DBusConnection *near_dbus_get_connection(void);
+
+void near_dbus_property_append_basic(DBusMessageIter *iter,
+                                       const char *key, int type, void *val);
+void near_dbus_property_append_dict(DBusMessageIter *iter, const char *key,
+                       near_dbus_append_cb_t function, void *user_data);
+void near_dbus_property_append_array(DBusMessageIter *iter,
+                                               const char *key, int type,
+                       near_dbus_append_cb_t function, void *user_data);
+void near_dbus_property_append_fixed_array(DBusMessageIter *iter,
+                               const char *key, int type, void *val, int len);
+
+dbus_bool_t near_dbus_property_changed_basic(const char *path,
+                               const char *interface, const char *key,
+                                                       int type, void *val);
+dbus_bool_t near_dbus_property_changed_dict(const char *path,
+                               const char *interface, const char *key,
+                       near_dbus_append_cb_t function, void *user_data);
+dbus_bool_t near_dbus_property_changed_array(const char *path,
+                       const char *interface, const char *key, int type,
+                       near_dbus_append_cb_t function, void *user_data);
+
+dbus_bool_t near_dbus_setting_changed_basic(const char *owner,
+                               const char *path, const char *key,
+                               int type, void *val);
+dbus_bool_t near_dbus_setting_changed_dict(const char *owner,
+                               const char *path, const char *key,
+                               near_dbus_append_cb_t function,
+                               void *user_data);
+dbus_bool_t near_dbus_setting_changed_array(const char *owner,
+                               const char *path, const char *key, int type,
+                               near_dbus_append_cb_t function,
+                               void *user_data);
+
+static inline void near_dbus_dict_open(DBusMessageIter *iter,
+                                                       DBusMessageIter *dict)
+{
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, dict);
+}
+
+static inline void near_dbus_dict_close(DBusMessageIter *iter,
+                                                       DBusMessageIter *dict)
+{
+       dbus_message_iter_close_container(iter, dict);
+}
+
+static inline void near_dbus_dict_append_basic(DBusMessageIter *dict,
+                                       const char *key, int type, void *val)
+{
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                                               NULL, &entry);
+       near_dbus_property_append_basic(&entry, key, type, val);
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+static inline void near_dbus_dict_append_dict(DBusMessageIter *dict,
+                       const char *key, near_dbus_append_cb_t function,
+                                                       void *user_data)
+{
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                                               NULL, &entry);
+       near_dbus_property_append_dict(&entry, key, function, user_data);
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+static inline void near_dbus_dict_append_array(DBusMessageIter *dict,
+               const char *key, int type, near_dbus_append_cb_t function,
+                                                       void *user_data)
+{
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                                               NULL, &entry);
+       near_dbus_property_append_array(&entry, key,
+                                               type, function, user_data);
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+static inline void near_dbus_dict_append_fixed_array(DBusMessageIter *dict,
+                               const char *key, int type, void *val, int len)
+{
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                                               NULL, &entry);
+       near_dbus_property_append_fixed_array(&entry, key, type, val, len);
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+dbus_bool_t near_dbus_validate_ident(const char *ident);
+char *near_dbus_encode_string(const char *value);
diff --git a/include/device.h b/include/device.h
new file mode 100644 (file)
index 0000000..15df8a9
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __NEAR_DEVICE_H
+#define __NEAR_DEVICE_H
+
+#include <stdint.h>
+
+#include <glib.h>
+
+struct near_device;
+
+typedef void (*near_device_io_cb) (uint32_t adapter_idx, uint32_t target_idx,
+                                                               int status);
+
+struct near_ndef_message;
+
+#define NEAR_DEVICE_PRIORITY_LOW      -100
+#define NEAR_DEVICE_PRIORITY_DEFAULT     0
+#define NEAR_DEVICE_PRIORITY_HIGH      100
+
+#define NEAR_DEVICE_SN_NPP       "com.android.npp"
+#define NEAR_DEVICE_SN_SNEP      "urn:nfc:sn:snep"
+#define NEAR_DEVICE_SN_HANDOVER  "urn:nfc:sn:handover"
+
+struct near_device_driver {
+       int priority;
+
+       int (*listen)(uint32_t adapter_idx, near_device_io_cb cb);
+       int (*push)(uint32_t adapter_idx, uint32_t target_idx,
+                                       struct near_ndef_message *ndef,
+                                       char *service_name,
+                                       near_device_io_cb cb);
+};
+
+struct near_device *near_device_get_device(uint32_t adapter_idx,
+                                               uint32_t target_idx);
+int near_device_add_data(uint32_t adapter_idx, uint32_t target_idx,
+                       uint8_t *data, size_t data_length);
+int near_device_add_records(struct near_device *device, GList *records,
+                               near_device_io_cb cb, int status);
+int near_device_driver_register(struct near_device_driver *driver);
+void near_device_driver_unregister(struct near_device_driver *driver);
+
+#endif
diff --git a/include/log.h b/include/log.h
new file mode 100644 (file)
index 0000000..6183b88
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+void near_info(const char *format, ...)
+                               __attribute__((format(printf, 1, 2)));
+void near_warn(const char *format, ...)
+                               __attribute__((format(printf, 1, 2)));
+void near_error(const char *format, ...)
+                               __attribute__((format(printf, 1, 2)));
+void near_debug(const char *format, ...)
+                               __attribute__((format(printf, 1, 2)));
+
+struct near_debug_desc {
+       const char *name;
+       const char *file;
+#define NEAR_DEBUG_FLAG_DEFAULT (0)
+#define NEAR_DEBUG_FLAG_PRINT   (1 << 0)
+       unsigned int flags;
+} __attribute__((aligned(8)));
+
+#define DBG(fmt, arg...) do { \
+       static struct near_debug_desc __near_debug_desc \
+       __attribute__((used, section("__debug"), aligned(8))) = { \
+               .file = __FILE__, .flags = NEAR_DEBUG_FLAG_DEFAULT, \
+       }; \
+       if (__near_debug_desc.flags & NEAR_DEBUG_FLAG_PRINT) \
+               near_debug("%s:%s() " fmt, \
+                                       __FILE__, __FUNCTION__ , ## arg); \
+} while (0)
diff --git a/include/ndef.h b/include/ndef.h
new file mode 100644 (file)
index 0000000..fdc5006
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __NEAR_NDEF_H
+#define __NEAR_NDEF_H
+
+#include <near/tag.h>
+
+struct near_ndef_record;
+
+struct near_ndef_message {
+       size_t length;
+       size_t offset;
+       uint8_t *data;
+};
+
+/* near_ndef_handover_carrier*/
+#define                NEAR_CARRIER_EMPTY      0x00
+#define                NEAR_CARRIER_BLUETOOTH  0x01    /* bit 0 */
+#define                NEAR_CARRIER_WIFI       0x02    /* bit 1 */
+#define                NEAR_CARRIER_UNKNOWN    0x80    /* Bit 7 */
+
+int near_ndef_count_records(uint8_t *ndef_in, size_t ndef_in_length,
+                                               uint8_t record_type);
+
+int near_ndef_record_length(uint8_t *ndef_in, size_t ndef_in_length);
+
+GList *near_ndef_parse_msg(uint8_t *ndef_data, size_t ndef_length);
+
+void near_ndef_records_free(GList *records);
+
+struct near_ndef_message *near_ndef_prepare_text_record(char *encoding,
+                                       char *language_code, char *text);
+
+struct near_ndef_message *near_ndef_prepare_uri_record(uint8_t identifier,
+                                        uint32_t field_length, uint8_t *field);
+
+struct near_ndef_message *near_ndef_prepare_handover_record(char* type_name,
+                                               struct near_ndef_record *record,
+                                               uint8_t carriers);
+
+struct near_ndef_message *
+near_ndef_prepare_smartposter_record(uint8_t uri_identifier,
+                                       uint32_t uri_field_length,
+                                       uint8_t *uri_field);
+
+#endif
diff --git a/include/nfc.h b/include/nfc.h
new file mode 100644 (file)
index 0000000..6189f27
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ *
+ * Authors:
+ *    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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __LINUX_NFC_H
+#define __LINUX_NFC_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+
+#define NFC_GENL_NAME "nfc"
+#define NFC_GENL_VERSION 1
+
+#define NFC_GENL_MCAST_EVENT_NAME "events"
+
+/**
+ * enum nfc_commands - supported nfc commands
+ *
+ * @NFC_CMD_UNSPEC: unspecified command
+ *
+ * @NFC_CMD_GET_DEVICE: request information about a device (requires
+ *     %NFC_ATTR_DEVICE_INDEX) or dump request to get a list of all nfc devices
+ * @NFC_CMD_DEV_UP: turn on the nfc device
+ *     (requires %NFC_ATTR_DEVICE_INDEX)
+ * @NFC_CMD_DEV_DOWN: turn off the nfc device
+ *     (requires %NFC_ATTR_DEVICE_INDEX)
+ * @NFC_CMD_START_POLL: start polling for targets using the given protocols
+ *     (requires %NFC_ATTR_DEVICE_INDEX and %NFC_ATTR_PROTOCOLS)
+ * @NFC_CMD_STOP_POLL: stop polling for targets (requires
+ *     %NFC_ATTR_DEVICE_INDEX)
+ * @NFC_CMD_GET_TARGET: dump all targets found by the previous poll (requires
+ *     %NFC_ATTR_DEVICE_INDEX)
+ * @NFC_EVENT_TARGETS_FOUND: event emitted when a new target is found
+ *     (it sends %NFC_ATTR_DEVICE_INDEX)
+ * @NFC_EVENT_DEVICE_ADDED: event emitted when a new device is registred
+ *     (it sends %NFC_ATTR_DEVICE_NAME, %NFC_ATTR_DEVICE_INDEX and
+ *     %NFC_ATTR_PROTOCOLS)
+ * @NFC_EVENT_DEVICE_REMOVED: event emitted when a device is removed
+ *     (it sends %NFC_ATTR_DEVICE_INDEX)
+ * @NFC_EVENT_TM_ACTIVATED: event emitted when the adapter is activated in
+ *      target mode.
+ * @NFC_EVENT_DEVICE_DEACTIVATED: event emitted when the adapter is deactivated
+ *      from target mode.
+ */
+enum nfc_commands {
+       NFC_CMD_UNSPEC,
+       NFC_CMD_GET_DEVICE,
+       NFC_CMD_DEV_UP,
+       NFC_CMD_DEV_DOWN,
+       NFC_CMD_DEP_LINK_UP,
+       NFC_CMD_DEP_LINK_DOWN,
+       NFC_CMD_START_POLL,
+       NFC_CMD_STOP_POLL,
+       NFC_CMD_GET_TARGET,
+       NFC_EVENT_TARGETS_FOUND,
+       NFC_EVENT_DEVICE_ADDED,
+       NFC_EVENT_DEVICE_REMOVED,
+       NFC_EVENT_TARGET_LOST,
+       NFC_EVENT_TM_ACTIVATED,
+       NFC_EVENT_TM_DEACTIVATED,
+/* private: internal use only */
+       __NFC_CMD_AFTER_LAST
+};
+#define NFC_CMD_MAX (__NFC_CMD_AFTER_LAST - 1)
+
+/**
+ * enum nfc_attrs - supported nfc attributes
+ *
+ * @NFC_ATTR_UNSPEC: unspecified attribute
+ *
+ * @NFC_ATTR_DEVICE_INDEX: index of nfc device
+ * @NFC_ATTR_DEVICE_NAME: device name, max 8 chars
+ * @NFC_ATTR_PROTOCOLS: nfc protocols - bitwise or-ed combination from
+ *     NFC_PROTO_*_MASK constants
+ * @NFC_ATTR_TARGET_INDEX: index of the nfc target
+ * @NFC_ATTR_TARGET_SENS_RES: NFC-A targets extra information such as NFCID
+ * @NFC_ATTR_TARGET_SEL_RES: NFC-A targets extra information (useful if the
+ *     target is not NFC-Forum compliant)
+ * @NFC_ATTR_TARGET_NFCID1: NFC-A targets identifier, max 10 bytes
+ * @NFC_ATTR_TARGET_SENSB_RES: NFC-B targets extra information, max 12 bytes
+ * @NFC_ATTR_TARGET_SENSF_RES: NFC-F targets extra information, max 18 bytes
+ * @NFC_ATTR_COMM_MODE: Passive or active mode
+ * @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
+ */
+enum nfc_attrs {
+       NFC_ATTR_UNSPEC,
+       NFC_ATTR_DEVICE_INDEX,
+       NFC_ATTR_DEVICE_NAME,
+       NFC_ATTR_PROTOCOLS,
+       NFC_ATTR_TARGET_INDEX,
+       NFC_ATTR_TARGET_SENS_RES,
+       NFC_ATTR_TARGET_SEL_RES,
+       NFC_ATTR_TARGET_NFCID1,
+       NFC_ATTR_TARGET_SENSB_RES,
+       NFC_ATTR_TARGET_SENSF_RES,
+       NFC_ATTR_COMM_MODE,
+       NFC_ATTR_RF_MODE,
+       NFC_ATTR_DEVICE_POWERED,
+       NFC_ATTR_IM_PROTOCOLS,
+       NFC_ATTR_TM_PROTOCOLS,
+/* private: internal use only */
+       __NFC_ATTR_AFTER_LAST
+};
+#define NFC_ATTR_MAX (__NFC_ATTR_AFTER_LAST - 1)
+
+#define NFC_DEVICE_NAME_MAXSIZE 8
+#define NFC_NFCID1_MAXSIZE 10
+#define NFC_SENSB_RES_MAXSIZE 12
+#define NFC_SENSF_RES_MAXSIZE 18
+#define NFC_GB_MAXSIZE        48
+
+/* NFC protocols */
+#define NFC_PROTO_JEWEL                1
+#define NFC_PROTO_MIFARE       2
+#define NFC_PROTO_FELICA       3
+#define NFC_PROTO_ISO14443     4
+#define NFC_PROTO_NFC_DEP      5
+#define NFC_PROTO_ISO14443_B   6
+
+#define NFC_PROTO_MAX          7
+
+/* NFC communication modes */
+#define NFC_COMM_ACTIVE  0
+#define NFC_COMM_PASSIVE 1
+
+/* NFC RF modes */
+#define NFC_RF_INITIATOR 0
+#define NFC_RF_TARGET    1
+#define NFC_RF_NONE      2
+
+/* NFC protocols masks used in bitsets */
+#define NFC_PROTO_JEWEL_MASK      (1 << NFC_PROTO_JEWEL)
+#define NFC_PROTO_MIFARE_MASK     (1 << NFC_PROTO_MIFARE)
+#define NFC_PROTO_FELICA_MASK    (1 << NFC_PROTO_FELICA)
+#define NFC_PROTO_ISO14443_MASK          (1 << NFC_PROTO_ISO14443)
+#define NFC_PROTO_NFC_DEP_MASK   (1 << NFC_PROTO_NFC_DEP)
+#define NFC_PROTO_ISO14443_B_MASK (1 << NFC_PROTO_ISO14443_B)
+
+struct sockaddr_nfc {
+       sa_family_t sa_family;
+       __u32 dev_idx;
+       __u32 target_idx;
+       __u32 nfc_protocol;
+};
+
+#define NFC_LLCP_MAX_SERVICE_NAME 63
+struct sockaddr_nfc_llcp {
+       sa_family_t sa_family;
+       __u32 dev_idx;
+       __u32 target_idx;
+       __u32 nfc_protocol;
+       __u8 dsap; /* Destination SAP, if known */
+       __u8 ssap; /* Source SAP to be bound to */
+       char service_name[NFC_LLCP_MAX_SERVICE_NAME]; /* Service name URI */;
+       size_t service_name_len;
+};
+
+/* NFC socket protocols */
+#define NFC_SOCKPROTO_RAW      0
+#define NFC_SOCKPROTO_LLCP     1
+#define NFC_SOCKPROTO_MAX      2
+
+#define NFC_HEADER_SIZE 1
+
+#endif /*__LINUX_NFC_H */
diff --git a/include/plugin.h b/include/plugin.h
new file mode 100644 (file)
index 0000000..609775d
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef __NEAR_PLUGIN_H
+#define __NEAR_PLUGIN_H
+
+#include <near/version.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NEAR_PLUGIN_PRIORITY_LOW      -100
+#define NEAR_PLUGIN_PRIORITY_DEFAULT     0
+#define NEAR_PLUGIN_PRIORITY_HIGH      100
+
+/**
+ * SECTION:plugin
+ * @title: Plugin premitives
+ * @short_description: Functions for declaring plugins
+ */
+
+struct near_plugin_desc {
+       const char *name;
+       const char *description;
+       const char *version;
+       int priority;
+       int (*init) (void);
+       void (*exit) (void);
+};
+
+/**
+ * NEAR_PLUGIN_DEFINE:
+ * @name: plugin name
+ * @description: plugin description
+ * @version: plugin version string
+ * @init: init function called on plugin loading
+ * @exit: exit function called on plugin removal
+ *
+ * Macro for defining a plugin descriptor
+ *
+ * |[
+ * #include <near/plugin.h>
+ *
+ * static int example_init(void)
+ * {
+ *     return 0;
+ * }
+ *
+ * static void example_exit(void)
+ * {
+ * }
+ *
+ * NEAR_PLUGIN_DEFINE(example, "Example plugin", NEAR_VERSION,
+ *                                     example_init, example_exit)
+ * ]|
+ */
+#ifdef NEAR_PLUGIN_BUILTIN
+#define NEAR_PLUGIN_DEFINE(name, description, version, priority, init, exit) \
+               struct near_plugin_desc __near_builtin_ ## name = { \
+                       #name, description, version, priority, init, exit \
+               };
+#else
+#define NEAR_PLUGIN_DEFINE(name, description, version, priority, init, exit) \
+               extern struct near_plugin_desc near_plugin_desc \
+                               __attribute__ ((visibility("default"))); \
+               struct near_plugin_desc near_plugin_desc = { \
+                       #name, description, version, priority, init, exit \
+               };
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __NEAR_PLUGIN_H */
diff --git a/include/setting.h b/include/setting.h
new file mode 100644 (file)
index 0000000..8f7a26a
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __NEAR_SETTING_H
+#define __NEAR_SETTING_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+near_bool_t near_setting_get_bool(const char *key);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __NEAR_SETTING_H */
diff --git a/include/tag.h b/include/tag.h
new file mode 100644 (file)
index 0000000..7a2ca6e
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __NEAR_TAG_H
+#define __NEAR_TAG_H
+
+#include <stdint.h>
+
+#include <glib.h>
+
+#define NFC_HEADER_SIZE 1
+
+#define NFC_MAX_NFCID1_LEN 10
+
+enum near_tag_sub_type {
+       NEAR_TAG_NFC_T2_MIFARE_ULTRALIGHT = 0,  // SAK 0x00
+       NEAR_TAG_NFC_T2_MIFARE_CLASSIC_1K,      // SAK:0x08
+       NEAR_TAG_NFC_T2_MIFARE_MINI,            // SAK 0x09
+       NEAR_TAG_NFC_T2_MIFARE_CLASSIC_4K,      // SAK:0x18
+       NEAR_TAG_NFC_T2_MIFARE_DESFIRE,         // SAK:0x20
+       NEAR_TAG_NFC_T2_JCOP30,                 // SAK:0x28
+       NEAR_TAG_NFC_T2_MIFARE_4K_EMUL,         // SAK:0x38
+       NEAR_TAG_NFC_T2_MIFARE_1K_INFINEON,     // SAK:0x88
+       NEAR_TAG_NFC_T2_MPCOS,                  // SAK:0x98
+       NEAR_TAG_NFC_SUBTYPE_UNKNOWN = 0xFF
+};
+
+enum near_tag_memory_layout {
+       NEAR_TAG_MEMORY_STATIC = 0,
+       NEAR_TAG_MEMORY_DYNAMIC,
+       NEAR_TAG_MEMORY_OTHER,
+       NEAR_TAG_MEMORY_UNKNOWN = 0xFF
+};
+
+typedef void (*near_tag_io_cb) (uint32_t adapter_idx, uint32_t target_idx,
+                                                               int status);
+
+struct near_ndef_message;
+
+#define NEAR_TAG_PRIORITY_LOW      -100
+#define NEAR_TAG_PRIORITY_DEFAULT     0
+#define NEAR_TAG_PRIORITY_HIGH      100
+
+struct near_tag_driver {
+       uint16_t type;
+       int priority;
+
+       int (*read)(uint32_t adapter_idx, uint32_t target_idx,
+                                               near_tag_io_cb cb);
+       int (*write)(uint32_t adapter_idx, uint32_t target_idx,
+                                       struct near_ndef_message *ndef,
+                                       near_tag_io_cb cb);
+       int (*check_presence)(uint32_t adapter_idx, uint32_t target_idx,
+                                               near_tag_io_cb cb);
+       int (*format)(uint32_t adapter_idx, uint32_t target_idx,
+                                       near_tag_io_cb cb);
+};
+
+struct near_tag;
+
+struct near_tag *near_tag_get_tag(uint32_t adapter_idx, uint32_t target_idx);
+void near_tag_set_ro(struct near_tag *tag, near_bool_t readonly);
+void near_tag_set_blank(struct near_tag *tag, near_bool_t blank);
+near_bool_t near_tag_get_blank(struct near_tag *tag);
+int near_tag_add_data(uint32_t adapter_idx, uint32_t target_idx,
+                       uint8_t *data, size_t data_length);
+int near_tag_add_records(struct near_tag *tag, GList *records,
+                               near_tag_io_cb cb, int status);
+enum near_tag_sub_type near_tag_get_subtype(uint32_t adapter_idx,
+                                       uint32_t target_idx);
+uint8_t *near_tag_get_nfcid(uint32_t adapter_idx, uint32_t target_idx,
+                                       uint8_t *nfcid_len);
+int near_tag_set_nfcid(uint32_t adapter_idx, uint32_t target_idx,
+                                       uint8_t *nfcid, size_t nfcid_len);
+uint8_t *near_tag_get_data(struct near_tag *tag, size_t *data_length);
+uint32_t near_tag_get_adapter_idx(struct near_tag *tag);
+uint32_t near_tag_get_target_idx(struct near_tag *tag);
+int near_tag_add_ndef(struct near_tag *tag, uint8_t *ndef_data, size_t ndef_length);
+int near_tag_driver_register(struct near_tag_driver *driver);
+void near_tag_driver_unregister(struct near_tag_driver *driver);
+void near_tag_set_memory_layout(struct near_tag *tag,
+                                       enum near_tag_memory_layout);
+enum near_tag_memory_layout near_tag_get_memory_layout(struct near_tag *tag);
+void near_tag_set_max_ndef_size(struct near_tag *tag, uint16_t size);
+uint16_t near_tag_get_max_ndef_size(struct near_tag *tag);
+void near_tag_set_c_apdu_max_size(struct near_tag *tag, uint16_t size);
+uint16_t near_tag_get_c_apdu_max_size(struct near_tag *tag);
+void near_tag_set_idm(struct near_tag *tag, uint8_t *idm, uint8_t len);
+uint8_t *near_tag_get_idm(struct near_tag *tag, uint8_t *len);
+void near_tag_set_attr_block(struct near_tag *tag, uint8_t *attr, uint8_t len);
+uint8_t *near_tag_get_attr_block(struct near_tag *tag, uint8_t *len);
+void near_tag_set_ic_type(struct near_tag *tag, uint8_t ic_type);
+uint8_t near_tag_get_ic_type(struct near_tag *tag);
+
+#endif
diff --git a/include/tlv.h b/include/tlv.h
new file mode 100644 (file)
index 0000000..2142512
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __NEAR_TLV_H
+#define __NEAR_TLV_H
+
+#define TLV_NULL 0x00
+#define TLV_LOCK 0x01
+#define TLV_MEM  0x02
+#define TLV_NDEF 0x03
+#define TLV_PROP 0xfd
+#define TLV_END  0xfe
+
+uint16_t near_tlv_length(uint8_t *tlv);
+uint8_t *near_tlv_next(uint8_t *tlv);
+uint8_t *near_tlv_data(uint8_t *tlv);
+GList *near_tlv_parse(uint8_t *tlv, size_t tlv_length);
+
+#endif
diff --git a/include/types.h b/include/types.h
new file mode 100644 (file)
index 0000000..bd5b53d
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __NEAR_TYPES_H
+#define __NEAR_TYPES_H
+
+#ifndef        FALSE
+#define        FALSE   (0)
+#endif
+
+#ifndef        TRUE
+#define        TRUE    (!FALSE)
+#endif
+
+typedef int    near_bool_t;
+
+#endif
diff --git a/include/version.h.in b/include/version.h.in
new file mode 100644 (file)
index 0000000..2119dcc
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __NEAR_VERSION_H
+#define __NEAR_VERSION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NEAR_VERSION   "@VERSION@"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __NEAR_VERSION_H */
diff --git a/neard.pc.in b/neard.pc.in
new file mode 100644 (file)
index 0000000..3a46466
--- /dev/null
@@ -0,0 +1,13 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+plugindir=${libdir}/neard/plugins
+
+Name: neard
+Description: NFC daemon
+Requires: glib-2.0 dbus-1 libnl
+Version: @VERSION@
+Libs: -module -avoid-version -export-symbols-regex neard_plugin_desc
+Cflags: -I${includedir}
\ No newline at end of file
diff --git a/packaging/init b/packaging/init
new file mode 100644 (file)
index 0000000..759a352
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+/usr/libexec/neard
diff --git a/packaging/neard.changes b/packaging/neard.changes
new file mode 100644 (file)
index 0000000..e3af7b5
--- /dev/null
@@ -0,0 +1,24 @@
+* Tue Sep 11 2012 arron.wang <arron.wang@intel.com> submit/2.0_beta/20120831.083207@3ecb862
+- Update License Info
+
+* Tue Aug 28 2012 Arron < arron.wang@intel.com> - 0.6
+- Upgrade to version 0.6
+
+* Mon Aug  6 10:39:42 CST 2012 Arron <arron.wang@intel.com>
+- Add systemd support
+
+* Fri Aug 03 2012 Anas Nashif <anas.nashif@intel.com> a7864dd
+- fix runtime requirements
+
+* Tue Jul 31 09:22:14 CST 2012 Arron <arron.wang@intel.com> - 0.5
+- Upgrade to version 0.5
+
+* Tue Apr 24 2012 Arron <arron.wang@intel.com> - 0.2.26
+- Upgrade to latest version
+
+* Fri Apr 20 2012 Arron <arron.wang@intel.com> - 0.1.64
+- Add building require for kernel-adaptation-bb-devel to fix the building error
+
+* Fri Mar 30 2012 Arron <arron.wang@intel.com> - 0.1.64
+- Init package for neard
+
diff --git a/packaging/neard.service b/packaging/neard.service
new file mode 100644 (file)
index 0000000..5ea1e81
--- /dev/null
@@ -0,0 +1,11 @@
+[Unit]
+Description=NFC Manager Daemon
+Before=network.target
+
+[Service]
+Type=dbus
+BusName=org.neard
+ExecStart=/usr/libexec/neard -n
+
+[Install]
+WantedBy=multi-user.target
diff --git a/packaging/neard.spec b/packaging/neard.spec
new file mode 100644 (file)
index 0000000..fcdd501
--- /dev/null
@@ -0,0 +1,99 @@
+Name:       neard-tizen
+Summary:    Near Field Communication Manager
+Version:    0.8
+Release:    1
+Group:      System/Networking
+License:    GPLv2
+Source0:    http://www.kernel.org/pub/linux/network/nfc/neard-%{version}.tar.bz2
+Source1:    init
+Source2:    neard.service
+Requires(post): /bin/ln
+BuildRequires:  pkgconfig(glib-2.0)
+BuildRequires:  pkgconfig(dbus-1)
+BuildRequires:  pkgconfig(libnl-2.0)
+
+Requires:   systemd
+Requires(post):   systemd
+Requires(preun):  systemd
+Requires(postun): systemd
+
+%description
+Near Field Communication Manager
+
+
+
+%package devel
+Summary:    Development files for NFC Manager
+Group:      Development/Libraries
+Requires:   %{name} = %{version}-%{release}
+
+%description devel
+neard-devel contains development files for use with neard.
+
+%package test
+Summary:    Test Scripts for NFC Manager
+Group:      Development/Tools
+Requires:   %{name} = %{version}-%{release}
+Requires:   dbus-python
+Requires:   pygobject
+
+%description test
+Scripts for testing neard and its functionality
+
+%prep
+%setup -q
+
+%build
+./bootstrap
+%configure \
+               --enable-debug \
+               --prefix=/usr \
+               --sysconfdir=/etc \
+               --enable-nfctype1=builtin \
+               --enable-nfctype2=builtin \
+               --enable-nfctype3=builtin \
+               --enable-nfctype4=builtin \
+               --disable-p2p \
+               --enable-test
+
+make %{?jobs:-j%jobs}
+
+%install
+%make_install
+
+mkdir -p %{buildroot}/etc/rc.d/init.d
+cp %{SOURCE1} %{buildroot}/etc/rc.d/init.d/neard
+chmod +x %{buildroot}/etc/rc.d/init.d/neard
+
+# Systemd service file
+install -d %{buildroot}%{_libdir}/systemd/system/
+install -m 644 %{S:2} %{buildroot}%{_libdir}/systemd/system/neard.service
+install -d %{buildroot}%{_libdir}/systemd/system/network.target.wants/
+ln -s ../neard.service %{buildroot}%{_libdir}/systemd/system/network.target.wants/neard.service
+
+%post
+ln -sf ../init.d/neard /etc/rc.d/rc3.d/S64neard
+systemctl daemon-reload
+systemctl restart neard.service
+
+%preun
+systemctl stop neard.service
+
+%postun
+systemctl daemon-reload
+
+%files
+%doc COPYING
+/usr/libexec/neard
+/etc/dbus-1/system.d/org.neard.conf
+/etc/rc.d/init.d/*
+%{_libdir}/systemd/system/neard.service
+%{_libdir}/systemd/system/network.target.wants/neard.service
+
+%files devel
+%{_includedir}/near/*.h
+%{_libdir}/pkgconfig/*.pc
+
+%files test
+%defattr(-,root,root,-)
+%{_libdir}/neard/test/*
diff --git a/plugins/handover.c b/plugins/handover.c
new file mode 100644 (file)
index 0000000..0e148bd
--- /dev/null
@@ -0,0 +1,509 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <linux/socket.h>
+
+#include <near/nfc.h>
+#include <near/types.h>
+#include <near/log.h>
+#include <near/adapter.h>
+#include <near/device.h>
+#include <near/tag.h>
+#include <near/ndef.h>
+#include <near/tlv.h>
+
+#include "p2p.h"
+
+#define NDEF_HR_MSG_MIN_LENGTH 0x06
+#define HR_HEADER_SIZE 6               /* header (1) + type len (1) +
+                                       *  payload len (1) + rec type (2) 'Hx'
+                                       *  + version (1)
+                                       */
+
+#define RECORD_TYPE_WKT_ALTERNATIVE_CARRIER 0x0a
+#define FRAME_TYPE_OFFSET      3
+
+enum loop_stage_flag {
+       STATE_MAIN_NDEF         = 0x00,
+       STATE_CFG_RECORD        = 0x01,
+};
+
+static GHashTable *hr_ndef_hash = NULL;
+
+struct extra_ndef {
+       uint8_t *ndef;
+       uint8_t length;
+};
+
+struct hr_ndef {
+       uint8_t *ndef;
+       uint16_t cur_ptr;
+       int cur_record_len;
+       int missing_bytes;
+       uint32_t adapter_idx;
+       uint32_t target_idx;
+       near_tag_io_cb cb;
+       int extra_ndef_count;
+       int block_free_size;
+       near_bool_t cfg_record_state;
+       near_bool_t in_extra_read;
+};
+
+struct hr_push_client {
+       uint8_t fd;
+       uint32_t adapter_idx;
+       uint32_t target_idx;
+       near_device_io_cb cb;
+       guint watch;
+};
+
+static void free_hr_ndef(gpointer data)
+{
+       struct hr_ndef *ndef = data;
+
+       if (ndef != NULL)
+               g_free(ndef->ndef);
+
+       g_free(ndef);
+}
+
+static void handover_close(int client_fd, int err)
+{
+       struct hr_ndef *ndef;
+
+       DBG("");
+
+       ndef = g_hash_table_lookup(hr_ndef_hash, GINT_TO_POINTER(client_fd));
+       if (ndef == NULL)
+               return;
+
+       g_hash_table_remove(hr_ndef_hash, GINT_TO_POINTER(client_fd));
+}
+
+/* Parse an incoming handover buffer*/
+static int handover_ndef_parse(int client_fd, struct hr_ndef *ndef)
+{
+       int err;
+       GList *records;
+       struct near_ndef_message *msg;
+
+       DBG("");
+
+       if ((ndef->ndef == NULL) ||
+                       (ndef->cur_ptr < NDEF_HR_MSG_MIN_LENGTH)) {
+               err = -EINVAL;
+               goto fail;
+       }
+
+       /* call the global parse function */
+       records = near_ndef_parse_msg(ndef->ndef, ndef->cur_ptr);
+       if (records == NULL) {
+               err = -ENOMEM;
+               goto fail;
+       }
+
+       /*
+        * If we receive a request, we should reply with a Hs but
+        * if the initial frame is Hs (it means we initiated the
+        * exchange with a Hr), so we have to do some actions (e.g.:
+        * pairing with bluetooth)
+        */
+       if (strncmp((char *) (ndef->ndef + FRAME_TYPE_OFFSET), "Hr", 2) == 0) {
+               /*
+                * The first entry on the record list is the Hr record.
+                * We build the Hs based on it.
+                */
+               msg = near_ndef_prepare_handover_record("Hs", records->data,
+                                                       NEAR_CARRIER_UNKNOWN);
+               if (msg == NULL) {
+                       err = -EINVAL;
+                       goto fail;
+               }
+
+               near_info("Send Hs frame");
+               err = send(client_fd, msg->data, msg->length, MSG_DONTWAIT);
+
+               g_free(msg->data);
+               g_free(msg);
+       } else {
+               err = 0;
+       }
+
+       near_ndef_records_free(records);
+
+       return err;
+
+fail:
+       near_error("ndef parsing failed %d", err);
+
+       handover_close(client_fd, 0);
+
+       return err;
+}
+
+static near_bool_t handover_recv_error(void)
+{
+       near_error("%s", strerror(errno));
+
+       if (errno == EAGAIN)
+               return TRUE;
+
+       return FALSE;
+}
+
+/* Add extra records right after the end of the "Hr" ndef record */
+static near_bool_t handover_read_cfg_records(int client_fd,
+                               uint32_t adapter_idx, uint32_t target_idx,
+                               near_tag_io_cb cb)
+{
+       struct hr_ndef *ndef;
+       int bytes_recv;
+       int ndef_size;
+       int err;
+
+       ndef = g_hash_table_lookup(hr_ndef_hash, GINT_TO_POINTER(client_fd));
+       if (ndef == NULL) {
+               near_error("hr_ndef should exist");
+               return FALSE;
+       }
+
+       if (ndef->in_extra_read == TRUE) {
+               /* Next prepare read to complete the Hr */
+               ndef->ndef = g_try_realloc(ndef->ndef, ndef->cur_record_len +
+                               NDEF_HR_MSG_MIN_LENGTH);
+               if (ndef == NULL)
+                       return FALSE;
+
+               /* Read header bytes */
+               bytes_recv = recv(client_fd, ndef->ndef + ndef->cur_ptr,
+                               NDEF_HR_MSG_MIN_LENGTH, MSG_DONTWAIT);
+               if (bytes_recv < 0)
+                       return handover_recv_error();
+
+               /* Now, check the ndef payload size plus header bytes */
+               ndef_size = near_ndef_record_length(ndef->ndef + ndef->cur_ptr,
+                                                               bytes_recv);
+               if (ndef_size < 0)
+                       goto fail;
+
+               ndef->cur_ptr += bytes_recv;
+               ndef->missing_bytes = ndef_size - bytes_recv;
+
+               /* Next prepare read to complete the NDEF */
+               ndef->ndef = g_try_realloc(ndef->ndef, ndef->cur_record_len
+                                                               + ndef_size);
+               if (ndef->ndef == NULL) {
+                       g_free(ndef);
+                       return FALSE;
+               }
+               ndef->cur_record_len += ndef_size;
+               ndef->in_extra_read = FALSE;
+
+               return TRUE;
+       }
+
+       /* Read remaining bytes */
+       bytes_recv = recv(client_fd, ndef->ndef + ndef->cur_ptr,
+                                       ndef->missing_bytes, MSG_DONTWAIT);
+       if (bytes_recv < 0)
+               return handover_recv_error();
+
+       ndef->cur_ptr += bytes_recv;
+       ndef->missing_bytes -= bytes_recv;
+
+       /* Is the NDEF read complete ? */
+       if (ndef->missing_bytes)
+               return TRUE;    /* more bytes to come... */
+
+       ndef->extra_ndef_count--;
+       ndef->in_extra_read = TRUE;
+
+       if (ndef->extra_ndef_count == 0) {
+               /* All the bytes are read so now, parse the frame */
+               err = handover_ndef_parse(client_fd, ndef);
+               if (err > 0) {
+                       /* clean memory */
+                       handover_close(client_fd, 0);
+                       return TRUE;
+               }
+
+               return FALSE;
+       }
+
+       /* Process the next NDEF */
+       return TRUE;
+
+fail:
+       near_error("Handover read NDEFs failed");
+       return FALSE;
+}
+
+static near_bool_t handover_read_hr(int client_fd,
+               uint32_t adapter_idx, uint32_t target_idx, near_tag_io_cb cb)
+{
+       int bytes_recv;
+       int extra_ndefs;
+       struct hr_ndef *ndef;
+
+       DBG("");
+
+       ndef = g_hash_table_lookup(hr_ndef_hash, GINT_TO_POINTER(client_fd));
+       if (ndef == NULL)
+               return FALSE;
+
+       /* Read remaining bytes */
+       bytes_recv = recv(client_fd, ndef->ndef + ndef->cur_ptr,
+                       ndef->missing_bytes, MSG_DONTWAIT);
+       if (bytes_recv < 0)
+               return handover_recv_error();
+
+       ndef->cur_ptr += bytes_recv;
+       ndef->missing_bytes -= bytes_recv;
+
+       /* Is the ndef "Hr" read complete or should we loop */
+       if (ndef->missing_bytes)
+               return TRUE;
+
+       /*
+        * The first NDEF frame is read. We now should determine how many
+        * extra records follow the NDEF frame.
+        * We skip the first 6 bytes (Hr header) to jump on the first record
+        */
+       extra_ndefs = near_ndef_count_records(ndef->ndef + HR_HEADER_SIZE,
+                       ndef->cur_record_len - HR_HEADER_SIZE,
+                       RECORD_TYPE_WKT_ALTERNATIVE_CARRIER);
+       if (extra_ndefs < 0)
+               goto fail;
+
+       /* There's still some extra ndefs to read */
+       ndef->extra_ndef_count = extra_ndefs;
+
+       /* End of Handover message - now process extra records */
+       ndef->in_extra_read = TRUE;
+       ndef->cfg_record_state = TRUE;
+
+       /* But, if there's no ac record, we jump to the parsing */
+       if (ndef->extra_ndef_count == 0) {
+               handover_ndef_parse(client_fd, ndef);
+               return FALSE;
+       }
+
+       return TRUE;
+
+fail:
+       near_error("Handover read failed");
+       return FALSE;
+}
+
+static near_bool_t handover_read_initialize(int client_fd,
+               uint32_t adapter_idx, uint32_t target_idx, near_tag_io_cb cb)
+{
+       int bytes_recv;
+       struct hr_ndef *ndef;
+
+       DBG("");
+
+       /* Allocate the ndef structure */
+       ndef = g_try_malloc0(sizeof(struct hr_ndef));
+       if (ndef == NULL)
+               goto fail;
+
+       /* Allocate and read frame header (6 bytes) */
+       ndef->ndef = g_try_malloc0(NDEF_HR_MSG_MIN_LENGTH);
+       if (ndef->ndef == NULL)
+               goto fail;
+
+       /* Initialize default values */
+       ndef->cur_ptr = 0;
+       ndef->cur_record_len = -1;
+       ndef->adapter_idx = adapter_idx;
+       ndef->target_idx = target_idx;
+       ndef->cb = cb;
+       ndef->cfg_record_state = FALSE;
+
+       g_hash_table_insert(hr_ndef_hash, GINT_TO_POINTER(client_fd), ndef);
+
+       /* Read header bytes (6) */
+       bytes_recv = recv(client_fd, ndef->ndef,
+                               NDEF_HR_MSG_MIN_LENGTH, MSG_DONTWAIT);
+       if (bytes_recv < 0)
+               return handover_recv_error();
+
+       /* Now, check the ndef payload size plus header bytes */
+       ndef->cur_record_len = near_ndef_record_length(ndef->ndef, bytes_recv);
+       if (ndef->cur_record_len < 0)
+               goto fail;
+
+       ndef->cur_ptr += bytes_recv;
+       ndef->missing_bytes = ndef->cur_record_len - bytes_recv;
+
+       DBG("Handover frame size is %d", ndef->cur_ptr);
+
+       /* Next prepare read to complete the read */
+       ndef->ndef = g_try_realloc(ndef->ndef, ndef->cur_record_len);
+       if (ndef->ndef == NULL)
+               goto fail;
+
+       return TRUE;
+
+fail:
+       free_hr_ndef(ndef);
+
+       return FALSE;
+}
+
+/*
+ * This function is a "dispatcher", to read Hr/Hs messages,
+ * and/or additional NDEF messages
+ */
+static near_bool_t handover_read(int client_fd,
+               uint32_t adapter_idx, uint32_t target_idx,
+               near_tag_io_cb cb)
+{
+       struct hr_ndef *ndef;
+
+       ndef = g_hash_table_lookup(hr_ndef_hash, GINT_TO_POINTER(client_fd));
+       if (ndef == NULL) {
+               /* First call: allocate and read header bytes */
+               return handover_read_initialize(client_fd, adapter_idx,
+                                               target_idx, cb);
+       }
+
+       if (ndef->cfg_record_state == TRUE) {
+               return handover_read_cfg_records(client_fd, adapter_idx,
+                                                       target_idx, cb);
+       }
+
+       return handover_read_hr(client_fd, adapter_idx, target_idx, cb);
+}
+
+static void free_hr_push_client(struct hr_push_client *client, int status)
+{
+       DBG("");
+
+       handover_close(client->fd, 0);
+
+       if (client->cb)
+               client->cb(client->adapter_idx, client->target_idx, status);
+
+       if (client->watch > 0)
+               g_source_remove(client->watch);
+
+       g_free(client);
+}
+
+static gboolean handover_push_event(GIOChannel *channel,
+                               GIOCondition condition, gpointer data)
+{
+       near_bool_t ret;
+       struct hr_push_client *client = (struct hr_push_client *) data;
+
+       DBG("condition 0x%x", condition);
+
+       if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               near_error("Error with Handover client");
+
+               free_hr_push_client(client, -EIO);
+
+               return FALSE;
+       }
+
+       ret = handover_read(client->fd,
+                       client->adapter_idx, client->target_idx,
+                       client->cb);
+
+       if (ret == FALSE)
+               free_hr_push_client(client, 0);
+
+       return ret;
+}
+
+static int handover_push(int client_fd,
+                       uint32_t adapter_idx, uint32_t target_idx,
+                       struct near_ndef_message *ndef,
+                       near_device_io_cb cb)
+{
+       int err;
+       struct hr_push_client *client;
+       GIOChannel *channel;
+
+       DBG("");
+
+       client = g_try_malloc0(sizeof(struct hr_push_client));
+       if (client == NULL)
+               return -ENOMEM;
+
+       channel = g_io_channel_unix_new(client_fd);
+       g_io_channel_set_close_on_unref(channel, TRUE);
+
+       client->fd = client_fd;
+       client->adapter_idx = adapter_idx;
+       client->target_idx = target_idx;
+       client->cb = cb;
+       client->watch = g_io_add_watch(channel,
+                                       G_IO_IN | G_IO_HUP | G_IO_NVAL |
+                                       G_IO_ERR, handover_push_event,
+                                       (gpointer) client);
+
+       g_io_channel_unref(channel);
+
+       err = send(client_fd, ndef->data, ndef->length, MSG_DONTWAIT);
+       if (err < 0) {
+               free_hr_push_client(client, err);
+               g_io_channel_unref(channel);
+       }
+
+       return err;
+}
+
+struct near_p2p_driver handover_driver = {
+       .name = "Handover",
+       .service_name = NEAR_DEVICE_SN_HANDOVER,
+       .fallback_service_name = NEAR_DEVICE_SN_SNEP,
+       .read = handover_read,
+       .push = handover_push,
+       .close = handover_close,
+};
+
+int handover_init(void)
+{
+       hr_ndef_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                                       NULL, free_hr_ndef);
+
+       return near_p2p_register(&handover_driver);
+}
+
+void handover_exit(void)
+{
+       near_p2p_unregister(&handover_driver);
+
+       g_hash_table_destroy(hr_ndef_hash);
+       hr_ndef_hash = NULL;
+}
diff --git a/plugins/mifare.c b/plugins/mifare.c
new file mode 100644 (file)
index 0000000..368d470
--- /dev/null
@@ -0,0 +1,1343 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <linux/socket.h>
+
+#include <near/nfc.h>
+#include <near/plugin.h>
+#include <near/log.h>
+#include <near/types.h>
+#include <near/adapter.h>
+#include <near/tag.h>
+#include <near/ndef.h>
+#include <near/tlv.h>
+
+/*
+ * NXP Application Notes:
+ * AN1304, AN1305, ...
+ * http://www.nxp.com/technical-support-portal/53420/71108/application-notes
+ */
+
+/* Prototypes */
+int mifare_read(uint32_t adapter_idx, uint32_t target_idx,
+               near_tag_io_cb cb, enum near_tag_sub_type tgt_subtype);
+
+int mifare_check_presence(uint32_t adapter_idx, uint32_t target_idx,
+               near_tag_io_cb cb, enum near_tag_sub_type tgt_subtype);
+
+int mifare_write(uint32_t adapter_idx, uint32_t target_idx,
+               struct near_ndef_message *ndef,
+               near_tag_io_cb cb, enum near_tag_sub_type tgt_subtype);
+
+/* MIFARE command set */
+#define MF_CMD_WRITE           0xA0
+#define MF_CMD_READ            0x30
+#define MF_CMD_AUTH_KEY_A      0x60
+
+#define NFC_AID_TAG            0xE103
+
+/*
+ * Define boundaries for 1K / 2K / 4K
+ * 1K:   sector 0 to 15 (3 blocks each + trailer block )
+ * 2K:   sector 0 to 31 (3 blocks each + trailer block )
+ * 4K:   sector 0 to 31 (3 blocks each + trailer block )
+ *     and sector 32 to 39 (15 blocks each + trailer block )
+ */
+#define DEFAULT_BLOCK_SIZE     16      /* MF_CMD_READ */
+
+#define STD_BLK_SECT_TRAILER   4       /* bl per sect with trailer 1K/2K */
+#define EXT_BLK_SECT_TRAILER   16      /* bl per sect with trailer 4K */
+
+#define STD_BLK_PER_SECT       3       /* 1 sect == 3blocks */
+#define EXT_BLK_PER_SECT       15      /* for 4K tags */
+
+/* Usual sector size, including trailer */
+#define STD_SECTOR_SIZE                (4 * DEFAULT_BLOCK_SIZE)        /* 00-31 */
+#define EXT_SECTOR_SIZE                (16 * DEFAULT_BLOCK_SIZE)       /* 32-39 */
+
+/* Usual sector size, without trailer */
+#define SECTOR_SIZE            (3 * DEFAULT_BLOCK_SIZE)
+#define BIG_SECTOR_SIZE                (15 * DEFAULT_BLOCK_SIZE)
+
+#define T4K_BOUNDARY           32
+#define T4K_BLK_OFF            0x80    /* blocks count before sector 32 */
+
+#define NO_TRAILER     0
+#define WITH_TRAILER   1
+#define SECT_IS_NFC    1
+
+/* Default MAD keys. Key length = 6 bytes */
+#define MAD_KEY_LEN    6
+static uint8_t MAD_public_key[] = {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5};
+static uint8_t MAD_NFC_key[] = {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7};
+
+#define MAD1_SECTOR            0x00    /* Sector 0 is for MAD1 */
+#define MAD1_1ST_BLOCK         0x00    /* 1st block of sector 0 */
+#define        MAD2_GPB_BITS           0x02    /* MAD v2 flag */
+
+#define MAD2_SECTOR            0x10    /* Sector 16 is for MAD2 */
+#define MAD2_1ST_BLOCK         0x40    /* 1st block of MAD2 */
+
+#define MAD_V1_AIDS_LEN                15      /* 1 to 0x0F */
+#define MAD_V2_AIDS_LEN                23      /*0x11 to 0x27 */
+
+#define NFC_1ST_BLOCK          0x04    /* Sectors from 1 are for NFC */
+
+#define ACC_BITS_LEN            3
+
+/* Access bits for data blocks mask */
+static uint8_t DATA_access_mask[] = {0x77, 0x77, 0x77};
+
+/* Write with key A access bits configuration */
+static uint8_t WRITE_with_key_A[] = {0x77, 0x07, 0x00};
+
+/* MAD1 sector structure. Start at block 0x00 */
+struct MAD_1 {
+       uint8_t man_info[16];
+       uint16_t crc_dir;
+       uint16_t aids[MAD_V1_AIDS_LEN];
+       /* Trailer */
+       uint8_t key_A[MAD_KEY_LEN];
+       uint8_t access_cond[3];
+       uint8_t GPB;
+       uint8_t key_B[MAD_KEY_LEN];
+} __attribute__((packed));
+
+/* MAD2 sector structure. Start at block 0x40 */
+struct MAD_2 {
+       uint16_t crc_dir;
+       uint16_t aids[MAD_V2_AIDS_LEN];
+       /* Trailer */
+       uint8_t key_A[MAD_KEY_LEN];
+       uint8_t access_cond[3];
+       uint8_t GPB;
+       uint8_t key_B[MAD_KEY_LEN];
+} __attribute__((packed));
+
+struct mifare_cookie {
+       uint32_t adapter_idx;
+       uint32_t target_idx;
+       uint8_t *nfcid1;
+       uint8_t nfcid1_len;
+
+       struct near_tag *tag;
+       near_tag_io_cb cb;
+       near_recv next_far_func;
+
+       /* For MAD access */
+       struct MAD_1 *mad_1;
+       struct MAD_2 *mad_2;
+       GSList *g_sect_list;            /* Global sectors list */
+
+       /* For read and write functions */
+       near_recv rws_next_fct;         /* next function */
+       int rws_block_start;            /* first block */
+       int rws_block_end;              /* last block */
+       int rws_completed;              /* read blocks */
+
+
+       /* For read only */
+       int rs_length;                  /* read length */
+       uint8_t *rs_pmem;               /* Stored read sector */
+       int rs_max_length;              /* available size */
+       uint8_t *nfc_data;
+       size_t nfc_data_length;
+
+       /* For write only */
+       struct near_ndef_message *ndef; /* message to write */
+       size_t ndef_length;             /* message length */
+
+       /* For access check */
+       int (*acc_check_function)(void *data);  /* acc check fnc */
+       uint8_t *acc_bits_mask;                 /* blocks to check */
+       uint8_t *acc_rights;                    /* condition */
+       int (*acc_denied_fct)(void *data);/* fnc to call on access denial */
+       GSList *acc_sect;                 /* sector from g_sect_list to check */
+};
+
+struct type2_cmd {
+       uint8_t cmd;
+       uint8_t block;
+       uint8_t data[];
+} __attribute__((packed));
+
+struct mf_write_cmd {
+       uint8_t cmd;
+       uint8_t block;
+       uint8_t data[DEFAULT_BLOCK_SIZE];
+} __attribute__((packed));
+
+struct mifare_cmd {
+       uint8_t cmd;
+       uint8_t block;
+       uint8_t key[MAD_KEY_LEN];
+       uint8_t nfcid[NFC_NFCID1_MAXSIZE];
+} __attribute__((packed));
+
+static int mifare_release(int err, void *data)
+{
+       struct mifare_cookie *cookie = data;
+
+       DBG("%p", cookie);
+
+       if (cookie == NULL)
+               return err;
+
+       if (err < 0 && cookie->cb) {
+               cookie->cb(cookie->adapter_idx, cookie->target_idx, err);
+               near_adapter_disconnect(cookie->adapter_idx);
+       }
+
+       /* Now free allocs */
+       g_free(cookie->nfcid1);
+       g_slist_free(cookie->g_sect_list);
+       g_free(cookie->mad_1);
+       g_free(cookie->mad_2);
+
+       if (cookie->ndef)
+               g_free(cookie->ndef->data);
+
+       g_free(cookie->ndef);
+       g_free(cookie);
+       cookie = NULL;
+
+       return err;
+}
+
+/*
+ * Mifare_generic MAD unlock block function
+ * This function send unlock code to the tag, and so, allow access
+ * to the complete related sector.
+ */
+static int mifare_unlock_sector(int block_id,
+                               near_recv next_far_fct,
+                               void *data)
+{
+       struct mifare_cmd cmd;
+       struct mifare_cookie *cookie = data;
+       uint8_t *key_ref;
+
+       /*
+        * For MADs sectors we use public key A (a0a1a2a3a4a5) but
+-       * for NFC sectors we use NFC_KEY_A (d3f7d3f7d3f7)
+        */
+       if ((block_id == MAD1_1ST_BLOCK) || (block_id == MAD2_1ST_BLOCK))
+               key_ref = MAD_public_key;
+       else
+               key_ref = MAD_NFC_key;
+
+        /* CMD AUTHENTICATION */
+       cmd.cmd = MF_CMD_AUTH_KEY_A;
+
+       /* Authenticate will be on the 1st block of the sector */
+       cmd.block = block_id;
+
+       /* Store the AUTH KEY */
+       memcpy(&cmd.key, key_ref, MAD_KEY_LEN);
+
+       /* add the UID */
+       memcpy(&cmd.nfcid, cookie->nfcid1, cookie->nfcid1_len);
+
+       return near_adapter_send(cookie->adapter_idx, (uint8_t *)&cmd,
+               sizeof(cmd) - NFC_NFCID1_MAXSIZE + cookie->nfcid1_len,
+               next_far_fct, cookie, mifare_release);
+}
+
+/*
+ * Common MIFARE Block read:
+ * Each call will read 16 bytes from tag... so to read 1 sector,
+ * it has to be called it 4 times or 16 times
+ * (minus 1 or not for the trailer block)
+ *
+ * data: mifare_cookie *mf_ck
+ * mf_ck->read_block: block number to read
+ */
+static int mifare_read_block(uint8_t block_id,
+                               void *data,
+                               near_recv far_func)
+{
+       struct type2_cmd cmd;
+       struct mifare_cookie *mf_ck = data;
+
+       cmd.cmd = MF_CMD_READ; /* MIFARE READ */
+       cmd.block = block_id;
+
+       return near_adapter_send(mf_ck->adapter_idx, (uint8_t *) &cmd, 2,
+                                       far_func, mf_ck, mifare_release);
+}
+
+/*
+ * Check access rights
+ * Function processes sector trailer received from tag and checks access rights.
+ * In case specified access isn't granted it calls appropriate
+ * access denial function.
+ * If access is granted, previous action (e.g. read, write) is continued.
+ */
+static int mifare_check_rights_cb(uint8_t *resp, int length, void *data)
+{
+       struct mifare_cookie *mf_ck = data;
+       int err;
+       uint8_t *c;
+       int i;
+
+       if (length < 0) {
+               err = length;
+               goto out_err;
+       }
+
+       /* skip reader byte and key A */
+        c = resp + 1 + MAD_KEY_LEN;
+
+       for (i = 0; i < ACC_BITS_LEN; i++) {
+               if ((c[i] & mf_ck->acc_bits_mask[i]) != mf_ck->acc_rights[i]) {
+                       (*mf_ck->acc_denied_fct)(data);
+                       return 0;
+               }
+       }
+
+       /* Continue previous action (read/write) */
+       err = (*mf_ck->rws_next_fct)(resp, length, data);
+
+       if (err < 0)
+               goto out_err;
+
+       return err;
+
+out_err:
+       return mifare_release(err, mf_ck);
+}
+
+/* Calls to mifare_read_block to get sector trailer */
+static int mifare_check_rights(uint8_t *resp, int length, void *data)
+{
+       struct mifare_cookie *mf_ck = data;
+       int err;
+
+       err = mifare_read_block(mf_ck->rws_block_start, mf_ck,
+                       mifare_check_rights_cb);
+
+       if (err < 0)
+               return mifare_release(err, mf_ck);
+
+       return err;
+}
+
+static int mifare_read_sector_cb(uint8_t *resp, int length, void *data)
+{
+       struct mifare_cookie *mf_ck = data;
+       int err = -1;
+
+       if (length < 0) {
+               err = length;
+               goto out_err;
+       }
+
+       /* Save the data */
+       length = length - 1; /* ignore first byte - Reader byte */
+
+       /* save the length: */
+       mf_ck->rs_length = mf_ck->rs_length + length;
+
+       memcpy(mf_ck->rs_pmem + mf_ck->rws_completed * DEFAULT_BLOCK_SIZE,
+                       resp + 1,/* ignore reader byte */
+                       length);
+
+       /* Next block */
+       mf_ck->rws_completed = mf_ck->rws_completed + 1;
+
+       if ((mf_ck->rws_block_start + mf_ck->rws_completed)
+                                               < mf_ck->rws_block_end)
+               err = mifare_read_block(
+                               (mf_ck->rws_block_start + mf_ck->rws_completed),
+                               data,
+                               mifare_read_sector_cb);
+       else {
+               /* Now Process the callback ! */
+               err = (*mf_ck->rws_next_fct)(mf_ck->rs_pmem,
+                                               mf_ck->rs_length, data);
+       }
+
+       if (err < 0)
+               goto out_err;
+       return err;
+
+out_err:
+       return mifare_release(err, mf_ck);
+}
+
+static int mifare_read_sector_unlocked(uint8_t *resp, int length, void *data)
+{
+       struct mifare_cookie *mf_ck = data;
+       int err;
+
+       if (length < 0) {
+               err = length;
+               goto out_err;
+       }
+       /* And run the read process on the first block of the sector */
+       err = mifare_read_block(mf_ck->rws_block_start, data,
+                               mifare_read_sector_cb);
+
+       if (err < 0)
+               goto out_err;
+       return err;
+
+out_err:
+       return mifare_release(err, mf_ck);
+}
+
+/*
+ * This function reads a complete sector, using block per block function.
+ * sector sizes can be:
+ * Sectors 0 to 31:
+ *     48 bytes: 3*16 no trailer
+ *     64 bytes: 4*16 with trailer
+ * Sectors 32 to 39:
+ *     240 bytes: 15*16 no trailer
+ *     256 bytes: 16*16 with trailer
+ *
+ * Unlock is done at the beginning of first sector.
+ */
+static int mifare_read_sector(void *cookie,
+                       uint8_t *pmem,          /* memory to fill */
+                       uint16_t memsize,       /* remaining free size */
+                       uint8_t sector_id,      /* sector to read */
+                       near_bool_t trailer,    /* Add trailer or not */
+                       near_recv next_func)
+{
+       struct mifare_cookie *mf_ck = cookie;
+       int err;
+       int blocks_count;
+
+       DBG("");
+
+       /* Prepare call values */
+       mf_ck->rs_pmem = pmem;                  /* where to store */
+       mf_ck->rs_max_length = memsize;         /* max size to store */
+       mf_ck->rs_length = 0;                   /* no bytes yet */
+       mf_ck->rws_completed = 0;               /* blocks read */
+
+       /* According to tag size, compute the correct block offset */
+       if (sector_id < T4K_BOUNDARY)
+               mf_ck->rws_block_start = sector_id * 4;  /* 1st block to read */
+       else
+               mf_ck->rws_block_start =
+                               (sector_id - T4K_BOUNDARY) * 16 + T4K_BLK_OFF;
+
+       /* Find blocks_per_sect, according to position and trailer or not */
+       if (sector_id < T4K_BOUNDARY)
+               blocks_count = (STD_BLK_PER_SECT + trailer);
+       else
+               blocks_count = (EXT_BLK_PER_SECT + trailer);
+
+       mf_ck->rws_block_end = mf_ck->rws_block_start + blocks_count;
+
+       mf_ck->rws_next_fct = next_func;                /* leaving function */
+
+       /* Being on the first block of a sector, unlock it */
+       err = mifare_unlock_sector(mf_ck->rws_block_start,
+                       mifare_read_sector_unlocked, mf_ck);
+
+       return err;
+}
+
+static int mifare_read_NFC_loop(uint8_t *resp, int length, void *data)
+{
+       struct mifare_cookie *mf_ck = data;
+       int err = 0;
+
+       DBG("");
+
+       if (length < 0) {
+               err = length;
+               goto out_err;
+       }
+
+       /* ptr to the next read ptr */
+       mf_ck->nfc_data = mf_ck->nfc_data + length;
+
+       /* remaining free mem */
+       mf_ck->nfc_data_length = mf_ck->nfc_data_length - length;
+
+
+       /* Additional sectors to read ? */;
+       if (mf_ck->g_sect_list != NULL && mf_ck->g_sect_list->next != NULL) {
+
+               err = mifare_read_sector(data,  /* cookie */
+                       mf_ck->nfc_data,                /* where to store */
+                       (int) mf_ck->nfc_data_length,   /* global length */
+                       GPOINTER_TO_INT(mf_ck->g_sect_list->data), /* id */
+                       NO_TRAILER,                     /* Trailer ? */
+                       mifare_read_NFC_loop);          /* next function */
+
+               mf_ck->g_sect_list = g_slist_remove(mf_ck->g_sect_list,
+                                               mf_ck->g_sect_list->data);
+
+               if (err < 0)
+                       goto out_err;
+               return err;
+       } else {
+               GList *records;
+               uint8_t *nfc_data;
+               size_t nfc_data_length;
+
+               DBG("Done reading");
+
+               nfc_data = near_tag_get_data(mf_ck->tag, &nfc_data_length);
+               if (nfc_data == NULL) {
+                       err = -ENOMEM;
+                       goto out_err;
+               }
+
+               records = near_tlv_parse(nfc_data, nfc_data_length);
+               near_tag_add_records(mf_ck->tag, records, mf_ck->cb, 0);
+
+               err = 0;
+       }
+
+out_err:
+       return mifare_release(err, mf_ck);
+}
+
+/* Prepare read NFC loop */
+static int mifare_read_NFC(uint8_t *resp, int length, void *data)
+{
+       struct mifare_cookie *mf_ck = data;
+       int err;
+
+       /* save tag memory pointer to data_block */
+       mf_ck->nfc_data = near_tag_get_data(mf_ck->tag,
+                                       &mf_ck->nfc_data_length);
+
+       /* First read here: */
+       err = mifare_read_sector(data,          /* cookie */
+               mf_ck->nfc_data,                /* where to store */
+               mf_ck->nfc_data_length,         /* global length */
+               GPOINTER_TO_INT(mf_ck->g_sect_list->data), /* sector id */
+               NO_TRAILER,                     /* Don't want Trailer */
+               mifare_read_NFC_loop);          /* next function */
+
+       mf_ck->g_sect_list = g_slist_remove(mf_ck->g_sect_list,
+                                               mf_ck->g_sect_list->data);
+       if (err < 0)
+               goto out_err;
+       return err;
+
+out_err:
+       return mifare_release(err, mf_ck);
+}
+
+static int mifare_process_MADs(void *data)
+{
+       struct mifare_cookie *mf_ck = data;
+       int err;
+       int i;
+       int global_tag_size = 0;
+       int ioffset;
+       uint8_t *tag_data;
+       size_t data_size;
+
+       DBG("");
+
+       /* Parse MAD entries to get the global size and fill the array */
+       if (mf_ck->mad_1 == NULL) {
+               err = -EINVAL;
+               goto out_err;
+       }
+
+       /* Skip non-NFC sectors at the beginning of the tag, if any */
+       for (i = 0 ; i < MAD_V1_AIDS_LEN; i++) {
+               if (mf_ck->mad_1->aids[i] == NFC_AID_TAG)
+                       break;
+       }
+
+       /*
+        * NFC sectors have to be continuous,
+        * so only some sectors at the beginning and at the end of tag
+        * can be non-NFC.
+        */
+       for (; i < MAD_V1_AIDS_LEN; i++) {
+               if (mf_ck->mad_1->aids[i] != NFC_AID_TAG)
+                       goto done_mad;
+
+               /* Save in the global list */
+               mf_ck->g_sect_list = g_slist_append(mf_ck->g_sect_list,
+                                               GINT_TO_POINTER(i + 1));
+               global_tag_size += SECTOR_SIZE;
+       }
+
+       /* Now MAD 2 */
+       ioffset = MAD_V1_AIDS_LEN + 1 + 1; /* skip 0x10 */
+       if (mf_ck->mad_2 == NULL)
+               goto done_mad;
+
+       /*
+        * If all sectors from MAD1 were non-NFC,
+        * skip initial non-NFC sectors from MAD2
+        */
+       i = 0;
+
+       if (global_tag_size == 0)
+               for (; i < MAD_V2_AIDS_LEN; i++)
+                       if (mf_ck->mad_2->aids[i] == NFC_AID_TAG)
+                               break;
+
+       for (; i < MAD_V2_AIDS_LEN; i++) {
+               if (mf_ck->mad_2->aids[i] != NFC_AID_TAG)
+                       goto done_mad;
+
+               mf_ck->g_sect_list = g_slist_append( mf_ck->g_sect_list,
+                                               GINT_TO_POINTER(ioffset + i));
+               if (i < EXT_BLK_PER_SECT)
+                       global_tag_size += SECTOR_SIZE;
+               else
+                       global_tag_size += BIG_SECTOR_SIZE;
+       }
+
+done_mad:
+       if (global_tag_size == 0) {
+
+               /* no NFC sectors - mark tag as blank */
+               near_error("TAG Global size: [%d], not valid NFC tag.",
+                               global_tag_size);
+               return -ENODEV;
+       }
+
+       /* n sectors, each sector is 3 blocks, each block is 16 bytes */
+       DBG("TAG Global size: [%d]", global_tag_size);
+
+       mf_ck->tag = near_tag_get_tag(mf_ck->adapter_idx, mf_ck->target_idx);
+       if (mf_ck->tag == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       /* don't allocate new data before writing */
+       tag_data = near_tag_get_data(mf_ck->tag, &data_size);
+       if (tag_data == NULL) {
+               err = near_tag_add_data(mf_ck->adapter_idx,
+                                               mf_ck->target_idx,
+                                               NULL, /* Empty */
+                                               global_tag_size);
+
+               if (err < 0)
+                       goto out_err;
+       }
+
+       /* Check access rights */
+       err = mf_ck->acc_check_function(data);
+
+       if (err < 0)
+               goto out_err;
+
+       return err;
+
+out_err:
+       return mifare_release(err, mf_ck);
+}
+
+/* Transitional function - async */
+static int read_MAD2_complete(uint8_t *empty, int iempty, void *data)
+{
+       return mifare_process_MADs(data);
+}
+
+/* This function reads the MAD2 sector */
+static int mifare_read_MAD2(void *data)
+{
+       struct mifare_cookie *mf_ck = data;
+       int err = 0;
+
+       DBG("");
+
+       /* As auth is ok, allocate Mifare Access Directory v1 */
+       mf_ck->mad_2 = g_try_malloc0(STD_SECTOR_SIZE);
+       if (mf_ck->mad_2 == NULL) {
+               near_error("Memory allocation failed (MAD2)");
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       err = mifare_read_sector(data,
+                       (uint8_t *) mf_ck->mad_2,
+                       (int) STD_SECTOR_SIZE,
+                       MAD2_SECTOR,                    /* sector 0x10 */
+                       WITH_TRAILER,                   /* Want Trailer */
+                       read_MAD2_complete);
+
+       if (err < 0)
+               goto out_err;
+       return err;
+
+out_err:
+       return mifare_release(err, mf_ck);
+}
+
+/*
+ * This function checks, in MAD1, if there's a MAD2 directory
+ * available. This is is the case for 2K and 4K tag
+ * If MAD2 exists, read it, elsewhere process the current MAD
+ */
+static int read_MAD1_complete(uint8_t *empty, int iempty, void *data)
+{
+       struct mifare_cookie *mf_ck = data;
+       int err;
+
+       DBG("");
+
+       /* Check if there's a need to get MAD2 sector */
+       if ((mf_ck->mad_1->GPB & 0x03) == MAD2_GPB_BITS)
+               err = mifare_read_MAD2(mf_ck);
+       else
+               err = mifare_process_MADs(data);
+
+       return err;
+}
+
+/*
+ * Function called to read the first MAD sector
+ * MAD is mandatory
+ */
+static int mifare_read_MAD1(uint8_t *resp, int length, void *data)
+{
+       struct mifare_cookie *mf_ck = data;
+       int err = 0;
+
+       DBG("%p %d", data, length);
+
+       if (length < 0) {
+               err = length;
+               return err;
+       }
+
+       /*
+        * As auth is ok, allocate Mifare Access Directory v1
+        * allocated size is also STD_SECTOR_SIZE
+        */
+       mf_ck->mad_1 = g_try_malloc0(STD_SECTOR_SIZE);
+       if (mf_ck->mad_1 == NULL) {
+               near_error("Memory allocation failed (MAD1)");
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       /* Call to mifare_read_sector */
+       err = mifare_read_sector(data,
+                       (uint8_t *)mf_ck->mad_1,        /* where to store */
+                       (int) STD_SECTOR_SIZE,          /* allocated size */
+                       MAD1_SECTOR,                    /* sector 0 */
+                       WITH_TRAILER,                   /* Want Trailer */
+                       read_MAD1_complete);
+
+       if (err < 0)
+               goto out_err;
+       return err;
+
+out_err:
+       return mifare_release(err, mf_ck);
+}
+
+/* If first NFC sector isn't writable, mark whole tag as read only */
+static int is_read_only(void *data)
+{
+       struct mifare_cookie *mf_ck = data;
+
+       DBG("Tag is read only");
+
+       near_tag_set_ro(mf_ck->tag, TRUE);
+
+       /* Continue previous action (read) */
+       (*mf_ck->rws_next_fct)(NULL, 0, data);
+
+       return 0;
+}
+
+
+static int mifare_check_read_only(void *data)
+{
+       struct mifare_cookie *mf_ck = data;
+       int err;
+
+       DBG("");
+
+       /*
+        * As authorisation with key B is not supported,
+        * in case writing with key A is not permitted, tag is read-only
+        */
+       mf_ck->acc_bits_mask = DATA_access_mask;
+       mf_ck->acc_rights = WRITE_with_key_A;
+
+       /* Check acces rights of first NFC sector */
+       mf_ck->rws_block_start = NFC_1ST_BLOCK + STD_BLK_PER_SECT;
+       /* Afterwards read tag */
+       mf_ck->rws_next_fct = mifare_read_NFC;
+       /* In case of writing access denial, set read only */
+       mf_ck->acc_denied_fct = is_read_only;
+
+       err = mifare_unlock_sector(mf_ck->rws_block_start,
+                       mifare_check_rights, mf_ck);
+
+       if (err < 0)
+               return mifare_release(err, mf_ck);
+
+       return err;
+}
+
+/*
+ * MIFARE: entry point:
+ * Read all the MAD sectors (0x00, 0x10) to get the Application Directory
+ * entries.
+ * On sector 0x00, App. directory is on block 0x01 & block 0x02
+ * On sector 0x10, App. directory is on block 0x40, 0x41 & 0x42
+ * On reading, CRC is ignored.
+ */
+int mifare_read(uint32_t adapter_idx, uint32_t target_idx,
+               near_tag_io_cb cb, enum near_tag_sub_type tgt_subtype)
+{
+       struct mifare_cookie *cookie;
+       int err;
+
+       DBG("");
+
+       /*Check supported and tested Mifare type */
+       switch (tgt_subtype) {
+       case NEAR_TAG_NFC_T2_MIFARE_CLASSIC_1K:
+       case NEAR_TAG_NFC_T2_MIFARE_CLASSIC_4K:
+               break;
+       default:
+               near_error("Mifare tag type [%d] not supported.", tgt_subtype);
+               return -1;
+       }
+
+       /* Alloc global cookie */
+       cookie = g_try_malloc0(sizeof(struct mifare_cookie));
+       if (cookie == NULL)
+               return -ENOMEM;
+
+       /* Get the nfcid1 */
+       cookie->nfcid1 = near_tag_get_nfcid(adapter_idx, target_idx,
+                               &cookie->nfcid1_len);
+       cookie->adapter_idx = adapter_idx;
+       cookie->target_idx = target_idx;
+       cookie->cb = cb;
+
+       /* check access rights - while reading just check read only */
+       cookie->acc_check_function = mifare_check_read_only;
+
+       /*
+        * Need to unlock before reading
+        * This will check if public keys are allowed (and, so, NDEF could
+        * be "readable"...
+        */
+       err = mifare_unlock_sector(MAD1_1ST_BLOCK,      /* related block */
+                               mifare_read_MAD1,       /* callback function */
+                               cookie);                /* target data */
+       if (err < 0)
+               return mifare_release(err, cookie);
+
+       return 0;
+}
+
+static int check_presence(uint8_t *resp, int length, void *data)
+{
+       struct mifare_cookie *cookie = data;
+       int err = 0;
+
+       DBG("%d", length);
+
+       if (length < 0) {
+               err = -EIO;
+               goto out;
+       }
+
+       if (cookie->cb)
+               cookie->cb(cookie->adapter_idx, cookie->target_idx, err);
+
+out:
+       return mifare_release(err, cookie);
+}
+
+int mifare_check_presence(uint32_t adapter_idx, uint32_t target_idx,
+                       near_tag_io_cb cb, enum near_tag_sub_type tgt_subtype)
+{
+       struct mifare_cmd cmd;
+       struct mifare_cookie *cookie;
+       uint8_t *key_ref = MAD_public_key;
+
+       DBG("");
+
+       /* Check supported and tested Mifare type */
+       switch (tgt_subtype) {
+       case NEAR_TAG_NFC_T2_MIFARE_CLASSIC_1K:
+       case NEAR_TAG_NFC_T2_MIFARE_CLASSIC_4K:
+               break;
+       default:
+               near_error("Mifare tag type %d not supported.", tgt_subtype);
+               return -1;
+       }
+
+       /* Alloc global cookie */
+       cookie = g_try_malloc0(sizeof(struct mifare_cookie));
+       if (cookie == NULL)
+               return -ENOMEM;
+
+       /* Get the nfcid1 */
+       cookie->nfcid1 = near_tag_get_nfcid(adapter_idx, target_idx,
+                                       &cookie->nfcid1_len);
+       cookie->adapter_idx = adapter_idx;
+       cookie->target_idx = target_idx;
+       cookie->cb = cb;
+
+       /*
+        * To check presence of Mifare Classic Tag,
+        * send authentication command instead of read one
+        */
+       cmd.cmd = MF_CMD_AUTH_KEY_A;
+
+       /* Authenticate the 1st block of the MAD sector */
+       cmd.block = MAD1_1ST_BLOCK;
+
+       /* Store the AUTH KEY */
+       memcpy(&cmd.key, key_ref, MAD_KEY_LEN);
+
+       /* add the UID */
+       memcpy(&cmd.nfcid, cookie->nfcid1, cookie->nfcid1_len);
+
+       return near_adapter_send(cookie->adapter_idx,
+                       (uint8_t *) &cmd,
+                       sizeof(cmd) - NFC_NFCID1_MAXSIZE + cookie->nfcid1_len,
+                       check_presence,
+                       cookie, mifare_release);
+}
+
+/*
+ * Common MIFARE Block write:
+ * Each call will write 16 bytes to tag... so to write 1 sector,
+ * it has to be called it 4 or 16 times (minus 1 for the trailer block)
+ */
+static int mifare_write_block(uint8_t block_id, void *data,
+                               near_recv far_func)
+{
+       struct mf_write_cmd cmd;
+       struct mifare_cookie *mf_ck = data;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.cmd = MF_CMD_WRITE; /* MIFARE WRITE */
+       cmd.block = block_id;
+
+       if ((mf_ck->ndef->offset + DEFAULT_BLOCK_SIZE) <
+                       mf_ck->ndef->length) {
+               memcpy(cmd.data, mf_ck->ndef->data +
+                               mf_ck->ndef->offset, DEFAULT_BLOCK_SIZE);
+               mf_ck->ndef->offset += DEFAULT_BLOCK_SIZE;
+       } else {
+               memcpy(cmd.data, mf_ck->ndef->data + mf_ck->ndef->offset,
+                               mf_ck->ndef->length - mf_ck->ndef->offset);
+               mf_ck->ndef->offset = mf_ck->ndef->length + 1;
+       }
+
+       return near_adapter_send(mf_ck->adapter_idx,
+                               (uint8_t *) &cmd, sizeof(cmd),
+                               far_func, data, NULL);
+}
+
+static int mifare_correct_length_cb(uint8_t *resp, int length, void *data)
+{
+       struct mifare_cookie *mf_ck = data;
+
+       DBG("Done writing");
+
+       if (mf_ck->cb)
+               mf_ck->cb(mf_ck->adapter_idx, mf_ck->target_idx, 0);
+
+       return mifare_release(0, mf_ck);
+}
+
+/* After writing ndef message, its length has to be updated */
+static int mifare_correct_length(uint8_t *resp, int length, void *data)
+{
+       struct mifare_cookie *mf_ck = data;
+
+       DBG("");
+
+       /* Correct length field */
+       mf_ck->ndef->data[1] = mf_ck->ndef_length;
+       /* and ndef offset so it points to the beginning */
+       mf_ck->ndef->offset = 0;
+
+       /* Run the write process only on the first block of the sector */
+       return mifare_write_block(NFC_1ST_BLOCK, mf_ck,
+                                       mifare_correct_length_cb);
+}
+
+static int mifare_write_sector_cb(uint8_t *resp, int length, void *data)
+{
+       struct mifare_cookie *mf_ck = data;
+       int err;
+
+       /* Next block */
+       mf_ck->rws_completed = mf_ck->rws_completed + 1;
+
+       /* Check if it's the last block */
+       if ((mf_ck->rws_block_start + mf_ck->rws_completed)
+                       < mf_ck->rws_block_end) {
+               /* then check if there's still data to write */
+               if (mf_ck->ndef->offset < mf_ck->ndef->length)
+                       err = mifare_write_block(
+                               mf_ck->rws_block_start + mf_ck->rws_completed,
+                               data, mifare_write_sector_cb);
+               else
+                       /* No more Data to write */
+                       /* Correct length of the ndef message */
+                       err = mifare_unlock_sector(NFC_1ST_BLOCK,
+                                               mifare_correct_length, mf_ck);
+       } else {
+               /* Process the callback */
+               err = (*mf_ck->rws_next_fct)(resp, length, data);
+       }
+
+       if (err < 0)
+               return mifare_release(err, mf_ck);
+
+       return err;
+
+}
+
+static int mifare_write_sector_unlocked(uint8_t *resp, int length, void *data)
+{
+       struct mifare_cookie *mf_ck = data;
+       int err;
+
+       if (length < 0) {
+               err = length;
+               goto out_err;
+       }
+
+       /* Run the write process on the first block of the sector */
+       err = mifare_write_block(mf_ck->rws_block_start, data,
+                       mifare_write_sector_cb);
+
+       if (err < 0)
+               goto out_err;
+       return err;
+
+out_err:
+       return mifare_release(err, mf_ck);
+}
+
+/*
+ * This function writes a complete sector, using block per block function.
+ * sector sizes can be:
+ * Sectors 0 to 31:
+ *     48 bytes: 3*16 (no trailer)
+ * Sectors 32 to 39:
+ *     240 bytes: 15*16 (no trailer)
+ *
+ * Unlock is done at the beginning of each sector.
+ */
+static int mifare_write_sector(void *cookie,
+                               uint8_t sector_id,      /* sector to write */
+                               near_recv next_func)
+{
+       struct mifare_cookie *mf_ck = cookie;
+       int blocks_count;
+
+       DBG("");
+
+       /* Prepare call values */
+
+       /* According to tag size, compute the correct block offset */
+       if (sector_id < T4K_BOUNDARY)
+               mf_ck->rws_block_start = sector_id * STD_BLK_SECT_TRAILER;
+       else
+               mf_ck->rws_block_start = T4K_BLK_OFF +
+                       (sector_id - T4K_BOUNDARY) * EXT_BLK_SECT_TRAILER;
+
+       /* Find blocks_per_sect, according to position, no trailer */
+       if (sector_id < T4K_BOUNDARY)
+               blocks_count = STD_BLK_PER_SECT;
+       else
+               blocks_count = EXT_BLK_PER_SECT;
+
+       mf_ck->rws_block_end = mf_ck->rws_block_start + blocks_count;
+       mf_ck->rws_completed = 0;
+       mf_ck->rws_next_fct = next_func;
+
+       /* Being on the first block of the sector, unlock it */
+       return mifare_unlock_sector(mf_ck->rws_block_start,
+                                       mifare_write_sector_unlocked, mf_ck);
+}
+
+static int mifare_write_NFC_loop(uint8_t *resp, int length, void *data)
+{
+       struct mifare_cookie *mf_ck = data;
+       int err = 0;
+
+       if (length < 0 || resp[0] != 0) {
+               err = -EIO;
+               goto out_err;
+       }
+
+       /* Something more to write? */;
+       if (mf_ck->ndef->offset < mf_ck->ndef->length) {
+               err = mifare_write_sector(data,         /* cookie */
+                               GPOINTER_TO_INT(mf_ck->g_sect_list->data),
+                               mifare_write_NFC_loop); /* next function */
+
+               mf_ck->g_sect_list = g_slist_remove(mf_ck->g_sect_list,
+                                               mf_ck->g_sect_list->data);
+
+               if (err < 0)
+                       goto out_err;
+
+       } else {
+               /* Correct length of an NDEF message */
+               err = mifare_unlock_sector(NFC_1ST_BLOCK,
+                                       mifare_correct_length, mf_ck);
+
+               if (err < 0)
+                       goto out_err;
+       }
+
+       return err;
+out_err:
+       return mifare_release(err, mf_ck);
+}
+
+static int mifare_write_NFC(void *data)
+{
+       struct mifare_cookie *mf_ck = data;
+       int err;
+
+       DBG("");
+
+       mf_ck->rws_completed = 0;       /* written blocks */
+
+       /* First write here: */
+       err = mifare_write_sector(data,         /* cookie */
+               GPOINTER_TO_INT(mf_ck->g_sect_list->data), /* sector id */
+               mifare_write_NFC_loop);         /* next function */
+
+       mf_ck->g_sect_list = g_slist_remove(mf_ck->g_sect_list,
+                                               mf_ck->g_sect_list->data);
+
+       if (err < 0)
+               return mifare_release(err, mf_ck);
+
+       return err;
+}
+
+static int mifare_check_rights_loop(uint8_t *resp, int length, void *data)
+{
+       struct mifare_cookie *mf_ck = data;
+       int err;
+       int sector_id;
+
+       if (mf_ck->acc_sect->next != NULL) {
+
+               mf_ck->acc_sect = mf_ck->acc_sect->next;
+               sector_id = GPOINTER_TO_INT(mf_ck->acc_sect->data);
+
+               if (sector_id < T4K_BOUNDARY)
+                       mf_ck->rws_block_start = sector_id * 4
+                                                       + STD_BLK_PER_SECT;
+               else
+                       mf_ck->rws_block_start = T4K_BLK_OFF + EXT_BLK_PER_SECT
+                               + (sector_id - T4K_BOUNDARY) * 16;
+
+               err = mifare_unlock_sector(mf_ck->rws_block_start,
+                               mifare_check_rights, mf_ck);
+       } else {
+               /* Full access granted, start writing */
+               err = mifare_write_NFC(data);
+       }
+
+       if (err < 0)
+               return mifare_release(err, mf_ck);
+
+       return err;
+}
+
+
+/*
+ * If one of NFC sectors isn't writable,
+ * tag size for writing is smaller than actual memory size,
+ * so calculate it and check if it is enough for ndef message.
+ */
+static int writing_not_permitted(void *data)
+{
+       struct mifare_cookie *mf_ck = data;
+       unsigned int new_tag_size = 0;
+       int sector_id;
+       int i;
+
+       sector_id = GPOINTER_TO_INT(mf_ck->acc_sect->data);
+       DBG("Writing sector %i not permitted", sector_id);
+
+       /* Read only sector found, calculate new tag size */
+       if (sector_id <= MAD_V1_AIDS_LEN) {
+               for (i = GPOINTER_TO_INT(mf_ck->g_sect_list->data);
+                               i < sector_id; i++)
+                       new_tag_size += SECTOR_SIZE;
+       } else {
+               /* Start from first NFC sector */
+               for (i = GPOINTER_TO_INT(mf_ck->g_sect_list->data);
+                               i <= MAD_V1_AIDS_LEN; i++)
+                       new_tag_size += SECTOR_SIZE;
+
+               /*
+                * If any of previous sector was NFC, skip MAD2
+                * If not, leave "i" as it was
+                */
+               if (i < MAD2_SECTOR)
+                       i = MAD2_SECTOR + 1;
+
+               for (; i < sector_id; i++) {
+                       if (i < T4K_BOUNDARY)
+                               new_tag_size += SECTOR_SIZE;
+                       else
+                               new_tag_size += BIG_SECTOR_SIZE;
+               }
+       }
+
+       DBG("TAG writable sectors' size: [%d].", new_tag_size);
+
+       /* Check if there's enough space on tag */
+       if (new_tag_size < mf_ck->ndef->length) {
+               near_error("Not enough space on tag");
+
+               if (mf_ck->cb)
+                       mf_ck->cb(mf_ck->adapter_idx,
+                                       mf_ck->target_idx, -ENOSPC);
+
+               mifare_release(0, data);
+               return -ENOSPC;
+       }
+
+       /* Enough space on tag, continue writing */
+       mifare_write_NFC(data);
+
+       return 0;
+}
+
+static int mifare_check_rights_NFC(void *data)
+{
+       struct mifare_cookie *mf_ck = data;
+       int err;
+
+       DBG("");
+
+       /*
+        * As authorisation with key B is not supported,
+        * in case writing with key A is not permitted, tag is read-only
+        */
+       mf_ck->acc_bits_mask = DATA_access_mask;
+       mf_ck->acc_rights = WRITE_with_key_A;
+
+       mf_ck->acc_sect = mf_ck->g_sect_list;
+       mf_ck->rws_block_start = NFC_1ST_BLOCK + STD_BLK_PER_SECT;
+       mf_ck->rws_next_fct = mifare_check_rights_loop;
+
+       mf_ck->acc_denied_fct = writing_not_permitted;
+       err = mifare_unlock_sector(mf_ck->rws_block_start,
+                       mifare_check_rights, mf_ck);
+
+       if (err < 0)
+               return mifare_release(err, mf_ck);
+
+       return err;
+}
+
+int mifare_write(uint32_t adapter_idx, uint32_t target_idx,
+                       struct near_ndef_message *ndef,
+                       near_tag_io_cb cb, enum near_tag_sub_type tgt_subtype)
+{
+       struct mifare_cookie *cookie;
+       struct near_tag *tag;
+       size_t tag_size;
+       int err;
+
+       DBG("");
+
+       /* Check supported and tested Mifare type */
+       switch (tgt_subtype) {
+       case NEAR_TAG_NFC_T2_MIFARE_CLASSIC_1K:
+       case NEAR_TAG_NFC_T2_MIFARE_CLASSIC_4K:
+               break;
+       default:
+               near_error("Mifare tag type %d not supported.", tgt_subtype);
+               return -1;
+       }
+
+       /* Check if there's enough space on tag */
+       tag = near_tag_get_tag(adapter_idx, target_idx);
+       near_tag_get_data(tag, &tag_size);
+
+       if (tag_size < ndef->length) {
+               near_error("Not enough space on tag");
+               return -ENOSPC;
+       }
+
+       /* Alloc global cookie */
+       cookie = g_try_malloc0(sizeof(struct mifare_cookie));
+       if (cookie == NULL)
+               return -ENOMEM;
+
+       /* Get the nfcid1 */
+       cookie->nfcid1 = near_tag_get_nfcid(adapter_idx, target_idx,
+                       &cookie->nfcid1_len);
+       cookie->adapter_idx = adapter_idx;
+       cookie->target_idx = target_idx;
+       cookie->cb = cb;
+
+       cookie->ndef = ndef;
+       /* Save ndef length */
+       cookie->ndef_length = cookie->ndef->data[1];
+       cookie->ndef->data[1] = 0;
+
+       /*
+        * Check if all sectors are writable
+        * if not, message may be too long to be written
+        */
+       cookie->acc_check_function = mifare_check_rights_NFC;
+
+       /*
+        * Mifare Classic Tag needs to be unlocked before writing
+        * This will check if public keys are allowed (NDEF could be "readable")
+        */
+       err = mifare_unlock_sector(MAD1_1ST_BLOCK,      /* related block */
+                                       mifare_read_MAD1,       /* callback */
+                                       cookie);        /* target data */
+
+       if (err < 0)
+               return mifare_release(err, cookie);
+
+       return 0;
+}
diff --git a/plugins/nfctype1.c b/plugins/nfctype1.c
new file mode 100644 (file)
index 0000000..5cfc3a0
--- /dev/null
@@ -0,0 +1,825 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <linux/socket.h>
+
+#include <near/nfc.h>
+#include <near/plugin.h>
+#include <near/log.h>
+#include <near/types.h>
+#include <near/adapter.h>
+#include <near/tag.h>
+#include <near/ndef.h>
+#include <near/tlv.h>
+
+#define CMD_READ_ALL           0x00    /* Read seg 0 (incl: HR) */
+#define CMD_READ_SEGS          0x10    /* Read 16 blocks (128 bytes) */
+#define CMD_RID                        0x78    /* Read tag UID */
+
+#define CMD_WRITE_E            0x53    /* Write with erase */
+#define CMD_WRITE_NE           0x1A    /* Write no erase */
+
+#define OFFSET_STATUS_CMD      0x00
+#define OFFSET_HEADER_ROM      0x01
+
+#define HR0_TYPE1_STATIC       0x11
+#define HR0_TYPE2_HIGH         0x10
+#define HR0_TYPE2_LOW          0x0F
+
+#define BLOCK_SIZE             8
+#define LEN_STATUS_BYTE                0x01    /* Status byte */
+#define LEN_SPEC_BYTES         (LEN_STATUS_BYTE + 0x02)        /* HRx */
+#define LEN_UID_BYTES          (LEN_STATUS_BYTE + 0x07)        /* UID bytes */
+#define LEN_CC_BYTES           0x04    /* Capab. container */
+#define LEN_DYN_BYTES          0x0A    /* Bytes CtrlIT and TLV - Dyn. only */
+
+#define TYPE1_MAGIC 0xe1
+
+#define TAG_T1_DATA_UID(data) ((data) + LEN_SPEC_BYTES)
+#define TAG_T1_DATA_CC(data) ((data) + LEN_SPEC_BYTES + LEN_UID_BYTES)
+#define TAG_T1_DATA_LENGTH(cc) ((cc[2] + 1) * 8 - LEN_CC_BYTES)
+
+#define TAG_T1_DATA_NFC(cc) ((cc)[0] & TYPE1_MAGIC)
+
+#define TYPE1_NOWRITE_ACCESS   0x0F
+#define TAG_T1_WRITE_FLAG(cc) ((cc)[3] & TYPE1_NOWRITE_ACCESS)
+#define TAG_T1_SEGMENT_SIZE    128
+
+#define TYPE1_STATIC_MAX_DATA_SIZE     0x60
+
+#define UID_LENGTH 4
+
+#define TYPE1_TAG_VER_1_1 0x11
+#define TYPE1_TAG_STATIC_SIZE_120 0x0E
+#define TYPE1_READ_WRITE_ACCESS 0x00
+#define TYPE1_STATIC_TAG_DATA_LENGTH 116
+
+struct type1_cmd {
+       uint8_t cmd;
+       uint8_t addr;
+       uint8_t data[1];
+       uint8_t uid[UID_LENGTH];
+} __attribute__((packed));
+
+struct type1_tag {
+       uint32_t adapter_idx;
+       uint16_t current_block;
+       uint16_t current_seg;
+       uint16_t last_seg;
+       uint16_t data_read;
+       uint8_t uid[UID_LENGTH];
+
+       near_tag_io_cb cb;
+       struct near_tag *tag;
+};
+
+struct t1_cookie {
+       uint32_t adapter_idx;
+       uint32_t target_idx;
+       uint8_t uid[UID_LENGTH];
+       uint32_t current_block; /* Static tag */
+       uint32_t current_byte;  /* Static tag */
+       struct near_ndef_message *ndef;
+       near_tag_io_cb cb;
+       uint8_t cc[LEN_CC_BYTES];
+};
+
+static void t1_init_cmd(struct type1_tag *tag, struct type1_cmd *cmd)
+{
+       if (tag == NULL || cmd == NULL)
+               return;
+
+       memcpy(cmd->uid, tag->uid, UID_LENGTH);
+}
+
+static int t1_cookie_release(int err, void *data)
+{
+       struct t1_cookie *cookie = data;
+
+       DBG("%p", cookie);
+
+       if (cookie == NULL)
+               return err;
+
+       if (cookie->cb != NULL)
+               cookie->cb(cookie->adapter_idx, cookie->target_idx, err);
+
+       if (cookie->ndef)
+               g_free(cookie->ndef->data);
+
+       g_free(cookie->ndef);
+       g_free(cookie);
+       cookie = NULL;
+
+       return err;
+}
+
+/* Read segments (128 bytes) and store them to the tag data block */
+static int data_recv(uint8_t *resp, int length, void *data)
+{
+       struct type1_tag *t1_tag = data;
+       struct type1_cmd t1_cmd;
+       uint8_t *tagdata;
+       size_t data_length;
+
+       DBG("%d", length);
+
+       if (length < 0)
+               return length;
+
+       length = length - LEN_STATUS_BYTE;  /* ignore first byte */
+
+       /* Add data to tag mem */
+       tagdata = near_tag_get_data(t1_tag->tag, &data_length);
+       memcpy(tagdata + t1_tag->data_read, resp + 1, length);
+
+       /* Next segment */
+       t1_tag->data_read =  t1_tag->data_read + length;
+       t1_tag->current_seg = t1_tag->current_seg + 1;
+
+       if (t1_tag->current_seg <= t1_tag->last_seg) {
+               /* RSEG cmd */
+               t1_init_cmd(t1_tag, &t1_cmd);
+
+               t1_cmd.cmd = CMD_READ_SEGS;
+               t1_cmd.addr = (t1_tag->current_seg << 4) & 0xFF;
+
+               return near_adapter_send(t1_tag->adapter_idx,
+                               (uint8_t *) &t1_cmd, sizeof(t1_cmd),
+                               data_recv, t1_tag, NULL);
+       } else { /* This is the end */
+               GList *records;
+
+               DBG("READ complete");
+
+               records = near_tlv_parse(tagdata, data_length);
+               near_tag_add_records(t1_tag->tag, records, t1_tag->cb, 0);
+
+               /* free memory */
+               g_free(t1_tag);
+
+               return 0;
+       }
+}
+
+/*
+ * The dynamic read function:
+ * Bytes [0..3] : CC
+ * [4..8]: TLV Lock ControlIT (0x01, 0x03, v1, V2, V3)
+ * [9..13]: TLV Reserved Memory Control        (0x02, 0x03, V1, V2, V3)
+ * [14..]: TLV NDEF (0x03, L0, L1, L2, V1,V2 ...)
+ */
+static int read_dynamic_tag(uint8_t *cc, int length, void *data)
+{
+       struct type1_tag *t1_tag = data;
+       struct type1_cmd t1_cmd;
+
+       uint8_t *tagdata;
+       uint8_t *pndef;
+       size_t data_length;
+
+       DBG("Dynamic Mode");
+
+       tagdata = near_tag_get_data(t1_tag->tag, &data_length);
+
+       /* Skip un-needed bytes */
+       pndef = cc + 4;         /* right after CC bytes */
+       pndef = pndef + 5;      /* skip TLV Lock bits bytes */
+       pndef = pndef + 5;      /* skip TLV ControlIT bytes */
+
+       /*
+        * Save first NFC bytes to tag memory
+        * 10 blocks[0x3..0xC] of 8 bytes + 2 bytes from block 2
+        */
+       memcpy(tagdata, pndef, 10 * BLOCK_SIZE + 2);
+
+       /* Read the next one, up to the end of the data area */
+       t1_tag->current_seg = 1;
+       t1_tag->last_seg = ((cc[2] * BLOCK_SIZE) / TAG_T1_SEGMENT_SIZE);
+       t1_tag->data_read = 10 * BLOCK_SIZE + 2;
+
+       t1_init_cmd(t1_tag, &t1_cmd);
+
+       /* T1 read segment */
+       t1_cmd.cmd = CMD_READ_SEGS;
+       /* 5.3.3 ADDS operand is [b8..b5] */
+       t1_cmd.addr = (t1_tag->current_seg << 4) & 0xFF;
+
+       return near_adapter_send(t1_tag->adapter_idx,
+                       (uint8_t *)&t1_cmd, sizeof(t1_cmd),
+                       data_recv, t1_tag, NULL);
+}
+
+static int meta_recv(uint8_t *resp, int length, void *data)
+{
+       struct t1_cookie *cookie = data;
+       struct near_tag *tag;
+       struct type1_tag *t1_tag;
+
+       uint8_t *cc;
+       int err = -EOPNOTSUPP;
+
+       DBG("%d", length);
+
+       if (length < 0) {
+               err = length;
+               goto out_err;
+       }
+
+       /* First byte is cmd status */
+       if (resp[OFFSET_STATUS_CMD] != 0) {
+               DBG("Command failed: 0x%x", resp[OFFSET_STATUS_CMD]);
+               err = -EIO;
+               goto out_err;
+       }
+
+       /* Check Magic NFC tag */
+       cc = TAG_T1_DATA_CC(resp);
+       if (TAG_T1_DATA_NFC(cc) == 0) {
+               if (resp[OFFSET_HEADER_ROM] == HR0_TYPE1_STATIC) {
+                       err = near_tag_add_data(cookie->adapter_idx,
+                                               cookie->target_idx,
+                                               NULL,
+                                               TYPE1_STATIC_TAG_DATA_LENGTH);
+               } else {
+                       near_error("Not a valid NFC magic tag 0x%x", cc[0]);
+                       err = -EINVAL;
+                       goto out_err;
+               }
+       } else {
+               /* Add data to the tag */
+               err = near_tag_add_data(cookie->adapter_idx, cookie->target_idx,
+                                               NULL, TAG_T1_DATA_LENGTH(cc));
+       }
+
+       if (err < 0)
+               goto out_err;
+
+       tag = near_tag_get_tag(cookie->adapter_idx, cookie->target_idx);
+       if (tag == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       t1_tag = g_try_malloc0(sizeof(struct type1_tag));
+       if (t1_tag == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       t1_tag->adapter_idx = cookie->adapter_idx;
+       t1_tag->cb = cookie->cb;
+       t1_tag->tag = tag;
+       memcpy(t1_tag->uid, cookie->uid, UID_LENGTH);
+
+       /* Set the ReadWrite flag */
+       if (TAG_T1_WRITE_FLAG(cc) == TYPE1_NOWRITE_ACCESS)
+               near_tag_set_ro(tag, TRUE);
+       else
+               near_tag_set_ro(tag, FALSE);
+
+       /* Check Static or Dynamic memory model */
+       if (resp[OFFSET_HEADER_ROM] == HR0_TYPE1_STATIC) {
+               uint8_t *tagdata;
+               size_t data_length;
+               GList *records;
+
+               if (TAG_T1_DATA_NFC(cc) == 0)
+                       near_tag_set_blank(tag, TRUE);
+               else
+                       near_tag_set_blank(tag, FALSE);
+
+               DBG("READ Static complete");
+
+               tagdata = near_tag_get_data(t1_tag->tag, &data_length);
+               memcpy(tagdata, cc + LEN_CC_BYTES, TAG_T1_DATA_LENGTH(cc));
+
+               near_tag_set_memory_layout(tag, NEAR_TAG_MEMORY_STATIC);
+
+               records = near_tlv_parse(tagdata, data_length);
+               near_tag_add_records(t1_tag->tag, records, t1_tag->cb, 0);
+
+               g_free(t1_tag);
+
+               return 0;
+       } else if ((resp[OFFSET_HEADER_ROM] & 0xF0) == HR0_TYPE2_HIGH) {
+               near_tag_set_memory_layout(tag, NEAR_TAG_MEMORY_DYNAMIC);
+               err = read_dynamic_tag(cc, length, t1_tag);
+               /*
+                * As reading isn't complete,
+                * callback shouldn't be called while freeing the cookie
+                */
+               cookie->cb = NULL;
+       } else {
+               err = -EOPNOTSUPP;
+       }
+
+       if (err < 0)
+               g_free(t1_tag);
+
+out_err:
+       DBG("err %d", err);
+
+       return t1_cookie_release(err, cookie);
+}
+
+/*
+ * READALL to read a maximum of 124 bytes.
+ * This cmd is common to static and dynamic targets
+ * This should allow to get the HR0 byte.
+ */
+static int rid_resp(uint8_t *resp, int length, void *data)
+{
+       struct t1_cookie *cookie = data;
+       struct type1_cmd t1_cmd;
+       uint8_t *uid;
+       int err;
+
+       DBG("");
+
+       /* First byte is cmd status */
+       if (resp[OFFSET_STATUS_CMD] != 0) {
+               DBG("Command failed: 0x%x", resp[OFFSET_STATUS_CMD]);
+               err = -EIO;
+               goto out_err;
+       }
+
+       uid = TAG_T1_DATA_UID(resp);
+
+       DBG("UID 0x%x 0x%x 0x%x 0x%x", uid[0], uid[1], uid[2], uid[3]);
+
+       near_tag_set_nfcid(cookie->adapter_idx, cookie->target_idx,
+                                                       uid, UID_LENGTH);
+
+       t1_cmd.cmd = CMD_READ_ALL;     /* Read ALL cmd give 124 bytes */
+       t1_cmd.addr = 0;               /* NA */
+       t1_cmd.data[0] = 0;
+       memcpy(t1_cmd.uid, uid, UID_LENGTH);
+
+       memcpy(cookie->uid, uid, UID_LENGTH);
+
+       return near_adapter_send(cookie->adapter_idx,
+                               (uint8_t *)&t1_cmd, sizeof(t1_cmd),
+                               meta_recv, cookie, t1_cookie_release);
+
+out_err:
+       DBG("err %d", err);
+
+       return t1_cookie_release(err, cookie);
+}
+
+static int nfctype1_read_meta(uint32_t adapter_idx, uint32_t target_idx,
+                                               near_tag_io_cb cb, uint8_t *uid)
+{
+       struct type1_cmd cmd;
+       struct t1_cookie *cookie;
+
+       DBG("");
+
+       memset(&cmd, 0, sizeof(cmd));
+
+       cookie = g_try_malloc0(sizeof(struct t1_cookie));
+       if (cookie == NULL)
+               return -ENOMEM;
+
+       cookie->adapter_idx = adapter_idx;
+       cookie->target_idx = target_idx;
+       cookie->cb = cb;
+
+       if (uid != NULL) {
+               cmd.cmd = CMD_READ_ALL; /* Read ALL cmd give 124 bytes */
+               memcpy(cmd.uid, uid, UID_LENGTH);
+               memcpy(cookie->uid, uid, UID_LENGTH);
+
+               return near_adapter_send(adapter_idx, (uint8_t *) &cmd,
+                                               sizeof(cmd), meta_recv, cookie,
+                                               t1_cookie_release);
+       } else {
+               cmd.cmd = CMD_RID;
+
+               return near_adapter_send(adapter_idx, (uint8_t *) &cmd,
+                                               sizeof(cmd), rid_resp, cookie,
+                                               t1_cookie_release);
+       }
+}
+
+/* First step: RID to get the tag UID */
+static int nfctype1_read(uint32_t adapter_idx,
+                               uint32_t target_idx, near_tag_io_cb cb)
+{
+       uint8_t *uid;
+       uint8_t  uid_length;
+       int err;
+
+       DBG("");
+
+       uid = near_tag_get_nfcid(adapter_idx, target_idx, &uid_length);
+       if (uid == NULL || uid_length != UID_LENGTH) {
+               if (uid != NULL) {
+                       near_error("Invalid UID");
+
+                       g_free(uid);
+                       return -EINVAL;
+               }
+
+               return nfctype1_read_meta(adapter_idx, target_idx, cb, NULL);
+       }
+
+       err = nfctype1_read_meta(adapter_idx, target_idx, cb, uid);
+
+       g_free(uid);
+
+       return err;
+}
+
+static int write_nmn_e1_resp(uint8_t *resp, int length, void *data)
+{
+       int err = 0;
+       struct t1_cookie *cookie = data;
+
+       DBG("");
+
+       if (length < 0)
+               err = length;
+
+       if (resp[OFFSET_STATUS_CMD] != 0)
+               err = -EIO;
+
+       DBG("Done writing");
+
+       return t1_cookie_release(err, cookie);
+}
+
+static int write_nmn_e1(struct t1_cookie *cookie)
+{
+       struct type1_cmd cmd;
+
+       DBG("");
+
+       cmd.cmd = CMD_WRITE_E;
+       cmd.addr = 0x08;
+       cmd.data[0] = TYPE1_MAGIC;
+       memcpy(cmd.uid, cookie->uid, UID_LENGTH);
+
+       return near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd,
+                                       sizeof(cmd), write_nmn_e1_resp, cookie,
+                                       t1_cookie_release);
+}
+
+static int data_write_resp(uint8_t *resp, int length, void *data)
+{
+       struct t1_cookie *cookie = data;
+       uint8_t addr = 0;
+       struct type1_cmd cmd;
+       int err;
+
+       DBG("");
+
+       if (length < 0) {
+               err = length;
+               goto out_err;
+       }
+
+       if (resp[OFFSET_STATUS_CMD] != 0) {
+               err = -EIO;
+               goto out_err;
+       }
+
+       if (cookie->ndef->offset > cookie->ndef->length)
+               return write_nmn_e1(cookie);
+
+       if (cookie->current_byte >= BLOCK_SIZE) {
+               cookie->current_byte = 0;
+               cookie->current_block++;
+       }
+
+       cmd.cmd = CMD_WRITE_E;
+       addr = cookie->current_block << 3;
+       cmd.addr = addr | (cookie->current_byte & 0x7);
+       cmd.data[0] = cookie->ndef->data[cookie->ndef->offset];
+       memcpy(cmd.uid, cookie->uid, UID_LENGTH);
+       cookie->ndef->offset++;
+       cookie->current_byte++;
+
+       err = near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd,
+                                       sizeof(cmd), data_write_resp, cookie,
+                                       NULL);
+       if (err < 0)
+               goto out_err;
+
+       return 0;
+
+out_err:
+       return t1_cookie_release(err, cookie);
+}
+
+static int data_write(uint32_t adapter_idx, uint32_t target_idx,
+                       struct near_ndef_message *ndef, near_tag_io_cb cb)
+{
+       int err;
+       struct type1_cmd cmd;
+       struct t1_cookie *cookie;
+       uint8_t *uid, uid_length;
+
+       DBG("");
+
+       uid = near_tag_get_nfcid(adapter_idx, target_idx, &uid_length);
+       if (uid == NULL || uid_length != UID_LENGTH) {
+               near_error("Invalid type 1 UID");
+               err = -EINVAL;
+               goto out_err;
+       }
+
+       cmd.cmd  = CMD_WRITE_E;
+       cmd.addr = 0x08;
+       cmd.data[0] = 0x00;
+       memcpy(cmd.uid, uid, UID_LENGTH);
+
+       cookie = g_try_malloc0(sizeof(struct t1_cookie));
+       if (cookie == NULL) {
+               g_free(uid);
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       cookie->adapter_idx = adapter_idx;
+       cookie->target_idx = target_idx;
+       memcpy(cookie->uid, uid, UID_LENGTH);
+       cookie->current_block = 1;
+       cookie->current_byte = LEN_CC_BYTES;
+       cookie->ndef = ndef;
+       cookie->cb = cb;
+
+       g_free(uid);
+
+       return near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd,
+                                       sizeof(cmd), data_write_resp, cookie,
+                                       t1_cookie_release);
+out_err:
+       if (cb != NULL)
+               cb(adapter_idx, target_idx, err);
+
+       return err;
+}
+
+/*
+ * The writing of a new NDEF message SHALL occur as follows:
+ * Write NMN = 00h to indicate that no valid NDEF message is present
+ * during writing to allow error detection in the event that the tag
+ * is removed from the field prior to completion of operation.
+ * Write VNo and RWA if required
+ * Write NDEF Message TLV
+ * Write NDEF Message data
+ * Write NMN = E1h as the last byte to be written
+ */
+static int nfctype1_write(uint32_t adapter_idx, uint32_t target_idx,
+                               struct near_ndef_message *ndef,
+                               near_tag_io_cb cb)
+{
+       struct near_tag *tag;
+       int err;
+
+       DBG("");
+
+       if (ndef == NULL || cb == NULL) {
+               err = -EINVAL;
+               goto out_err;
+       }
+
+       tag = near_tag_get_tag(adapter_idx, target_idx);
+       if (tag == NULL) {
+               err = -EINVAL;
+               goto out_err;
+       }
+
+       /* This check is valid for only static tags.
+        * Max data length on Type 1 Tag including TLV's
+        * is TYPE1_STATIC_MAX_DATA_SIZE */
+       if (near_tag_get_memory_layout(tag) == NEAR_TAG_MEMORY_STATIC) {
+               if ((ndef->length + 3) > TYPE1_STATIC_MAX_DATA_SIZE) {
+                       near_error("not enough space on tag");
+                       err = -ENOSPC;
+                       goto out_err;
+               }
+       }
+
+       return data_write(adapter_idx, target_idx, ndef, cb);
+
+       if (err < 0)
+               goto out_err;
+
+       return 0;
+
+out_err:
+       if (cb != NULL)
+               cb(adapter_idx, target_idx, err);
+
+       return err;
+}
+
+static int check_presence(uint8_t *resp, int length, void *data)
+{
+       struct t1_cookie *cookie = data;
+       int err = 0;
+
+       DBG("%d", length);
+
+       if (length < 0)
+               err = -EIO;
+
+       return t1_cookie_release(err, cookie);
+}
+
+static int nfctype1_check_presence(uint32_t adapter_idx,
+                               uint32_t target_idx, near_tag_io_cb cb)
+{
+       struct type1_cmd t1_cmd;
+       struct t1_cookie *cookie;
+       uint8_t *uid, uid_length;
+
+       DBG("");
+
+       uid = near_tag_get_nfcid(adapter_idx, target_idx, &uid_length);
+       if (uid == NULL || uid_length != UID_LENGTH) {
+               near_error("Invalid type 1 UID");
+               return -EINVAL;
+       }
+
+       t1_cmd.cmd = CMD_READ_ALL;     /* Read ALL cmd give 124 bytes */
+       t1_cmd.addr = 0;               /* NA */
+       t1_cmd.data[0] = 0;
+       memcpy(t1_cmd.uid, uid, UID_LENGTH);
+
+       g_free(uid);
+
+       cookie = g_try_malloc0(sizeof(struct t1_cookie));
+       if (cookie == NULL)
+               return -ENOMEM;
+
+       cookie->adapter_idx = adapter_idx;
+       cookie->target_idx = target_idx;
+       cookie->cb = cb;
+
+       return near_adapter_send(adapter_idx, (uint8_t *) &t1_cmd,
+                               sizeof(t1_cmd), check_presence, cookie,
+                               t1_cookie_release);
+}
+
+static int format_resp(uint8_t *resp, int length, void *data)
+{
+       int err = 0;
+       struct t1_cookie *cookie = data;
+       struct near_tag *tag;
+       struct type1_cmd cmd;
+       uint8_t addr;
+
+       DBG("");
+
+       if (length < 0 || resp[0] != 0) {
+               err = -EIO;
+               goto out_err;
+       }
+
+       if (cookie->current_byte < LEN_CC_BYTES) {
+               cmd.cmd  = CMD_WRITE_E;
+               addr = cookie->current_block << 3;
+               cmd.addr = addr | (cookie->current_byte & 0x7);
+               cmd.data[0] = cookie->cc[cookie->current_byte];
+               cookie->current_byte++;
+               memcpy(cmd.uid, cookie->uid, UID_LENGTH);
+
+               return near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd,
+                                       sizeof(cmd), format_resp, cookie,
+                                       t1_cookie_release);
+       } else {
+               tag = near_tag_get_tag(cookie->adapter_idx, cookie->target_idx);
+               if (tag == NULL) {
+                       err = -EINVAL;
+                       goto out_err;
+               }
+
+               DBG("Done formatting");
+               near_tag_set_blank(tag, FALSE);
+       }
+
+out_err:
+       return t1_cookie_release(err, cookie);
+}
+
+static int nfctype1_format(uint32_t adapter_idx, uint32_t target_idx,
+                               near_tag_io_cb cb)
+{
+       int err;
+       struct near_tag *tag;
+       struct type1_cmd cmd;
+       struct t1_cookie *cookie;
+       uint8_t *uid, uid_length, addr;
+
+       DBG("");
+
+       tag = near_tag_get_tag(adapter_idx, target_idx);
+       if (tag == NULL)
+               return -EINVAL;
+
+       /* TODO: Dynamic tag format */
+       if (near_tag_get_memory_layout(tag) != NEAR_TAG_MEMORY_STATIC)
+               return -EOPNOTSUPP;
+
+       uid = near_tag_get_nfcid(adapter_idx, target_idx, &uid_length);
+       if (uid == NULL || uid_length != UID_LENGTH) {
+               near_error("Invalid type 1 UID");
+               return -EINVAL;
+       }
+
+       cookie = g_try_malloc0(sizeof(struct t1_cookie));
+       if (cookie == NULL) {
+               err = -EINVAL;
+               goto out_err;
+       }
+
+       cookie->adapter_idx = adapter_idx;
+       cookie->target_idx = target_idx;
+       cookie->cb = cb;
+       memcpy(cookie->uid, uid, UID_LENGTH);
+       cookie->cc[0] = TYPE1_MAGIC;
+       cookie->cc[1] = TYPE1_TAG_VER_1_1;
+       cookie->cc[2] = TYPE1_TAG_STATIC_SIZE_120;
+       cookie->cc[3] = TYPE1_READ_WRITE_ACCESS;
+       cookie->current_block = 1;
+       cookie->current_byte = 0;
+
+       cmd.cmd  = CMD_WRITE_E;
+       addr = cookie->current_block << 3;
+       cmd.addr = addr | (cookie->current_byte & 0x7);
+       cmd.data[0] = cookie->cc[cookie->current_byte];
+       cookie->current_byte++;
+       memcpy(cmd.uid, cookie->uid, UID_LENGTH);
+       g_free(uid);
+
+       err = near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd,
+                               sizeof(cmd), format_resp, cookie,
+                               t1_cookie_release);
+       if (err < 0)
+               goto out_err;
+
+       return 0;
+
+out_err:
+       g_free(cookie);
+       g_free(uid);
+
+       return err;
+}
+
+static struct near_tag_driver type1_driver = {
+       .type           = NFC_PROTO_JEWEL,
+       .priority       = NEAR_TAG_PRIORITY_DEFAULT,
+       .read           = nfctype1_read,
+       .write          = nfctype1_write,
+       .check_presence = nfctype1_check_presence,
+       .format         = nfctype1_format,
+};
+
+static int nfctype1_init(void)
+{
+       DBG("");
+
+       return near_tag_driver_register(&type1_driver);
+}
+
+static void nfctype1_exit(void)
+{
+       DBG("");
+
+       near_tag_driver_unregister(&type1_driver);
+}
+
+NEAR_PLUGIN_DEFINE(nfctype1, "NFC Forum Type 1 tags support", VERSION,
+                       NEAR_PLUGIN_PRIORITY_HIGH, nfctype1_init, nfctype1_exit)
diff --git a/plugins/nfctype2.c b/plugins/nfctype2.c
new file mode 100644 (file)
index 0000000..3dc7856
--- /dev/null
@@ -0,0 +1,635 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <linux/socket.h>
+
+#include <near/nfc.h>
+#include <near/plugin.h>
+#include <near/log.h>
+#include <near/types.h>
+#include <near/adapter.h>
+#include <near/tag.h>
+#include <near/ndef.h>
+#include <near/tlv.h>
+
+extern int mifare_read(uint32_t adapter_idx, uint32_t target_idx,
+               near_tag_io_cb cb, enum near_tag_sub_type tgt_subtype);
+
+extern int mifare_check_presence(uint32_t adapter_idx, uint32_t target_idx,
+                       near_tag_io_cb cb, enum near_tag_sub_type tgt_subtype);
+
+extern int mifare_write(uint32_t adapter_idx, uint32_t target_idx,
+                       struct near_ndef_message *ndef,
+                       near_tag_io_cb cb, enum near_tag_sub_type tgt_subtype);
+
+#define CMD_READ         0x30
+#define CMD_READ_SIZE    0x02
+
+#define CMD_WRITE        0xA2
+
+#define READ_SIZE  16
+#define BLOCK_SIZE 4
+
+#define META_BLOCK_START 0
+#define DATA_BLOCK_START 4
+#define TYPE2_MAGIC 0xe1
+
+#define TAG_DATA_CC(data) ((data) + 12)
+#define TAG_DATA_LENGTH(cc) ((cc)[2] * 8)
+#define TAG_DATA_NFC(cc) ((cc)[0] & TYPE2_MAGIC)
+
+#define TYPE2_NOWRITE_ACCESS   0x0F
+#define TYPE2_READWRITE_ACCESS 0x00
+#define TAG_T2_WRITE_FLAG(cc) ((cc)[3] & TYPE2_NOWRITE_ACCESS)
+
+#define NDEF_MAX_SIZE  0x30
+
+#define CC_BLOCK_START 3
+#define TYPE2_TAG_VER_1_0  0x10
+#define TYPE2_DATA_SIZE_48 0x6
+
+struct type2_cmd {
+       uint8_t cmd;
+       uint8_t block;
+       uint8_t data[BLOCK_SIZE];
+} __attribute__((packed));
+
+struct type2_tag {
+       uint32_t adapter_idx;
+       uint16_t current_block;
+
+       near_tag_io_cb cb;
+       struct near_tag *tag;
+};
+
+struct t2_cookie {
+       uint32_t adapter_idx;
+       uint32_t target_idx;
+       uint8_t current_block;
+       struct near_ndef_message *ndef;
+       near_tag_io_cb cb;
+};
+
+struct type2_cc {
+       uint8_t magic;
+       uint8_t version;
+       uint8_t mem_size;
+       uint8_t read_write;
+};
+
+static int t2_cookie_release(int err, void *data)
+{
+       struct t2_cookie *cookie = data;
+
+       DBG("%p", cookie);
+
+       if (cookie == NULL)
+               return err;
+
+       if (cookie->cb != NULL)
+               cookie->cb(cookie->adapter_idx, cookie->target_idx, err);
+
+       if (cookie->ndef)
+               g_free(cookie->ndef->data);
+
+       g_free(cookie->ndef);
+       g_free(cookie);
+       cookie = NULL;
+
+       return err;
+}
+
+static int data_recv(uint8_t *resp, int length, void *data)
+{
+       struct type2_tag *tag = data;
+       struct type2_cmd cmd;
+       uint8_t *nfc_data;
+       size_t current_length, length_read, data_length;
+       uint32_t adapter_idx;
+       int read_blocks;
+
+       DBG("%d", length);
+
+       if (length < 0) {
+               g_free(tag);
+
+               return  length;
+       }
+
+       nfc_data = near_tag_get_data(tag->tag, &data_length);
+       adapter_idx = near_tag_get_adapter_idx(tag->tag);
+
+       length_read = length - NFC_HEADER_SIZE;
+       current_length = tag->current_block * BLOCK_SIZE;
+       if (current_length + length - NFC_HEADER_SIZE > data_length)
+               length_read = data_length - current_length;
+
+       memcpy(nfc_data + current_length, resp + NFC_HEADER_SIZE, length_read);
+
+       if (current_length + length_read == data_length) {
+               GList *records;
+
+               /* TODO parse tag->data for NDEFS, and notify target.c */
+               tag->current_block = 0;
+
+               DBG("Done reading");
+
+               records = near_tlv_parse(nfc_data, data_length);
+               near_tag_add_records(tag->tag, records, tag->cb, 0);
+
+               g_free(tag);
+
+               return 0;
+       }
+
+       read_blocks = length / BLOCK_SIZE;
+       tag->current_block += read_blocks;
+
+       cmd.cmd = CMD_READ;
+       cmd.block = DATA_BLOCK_START + tag->current_block;
+
+       DBG("adapter %d", adapter_idx);
+
+       return near_adapter_send(adapter_idx,
+                               (uint8_t *) &cmd, CMD_READ_SIZE,
+                                       data_recv, tag, NULL);
+}
+
+static int data_read(struct type2_tag *tag)
+{
+       struct type2_cmd cmd;
+       uint32_t adapter_idx;
+
+       DBG("");
+
+       tag->current_block = 0;
+
+       cmd.cmd = CMD_READ;
+       cmd.block = DATA_BLOCK_START;
+
+       adapter_idx = near_tag_get_adapter_idx(tag->tag);
+
+       return near_adapter_send(adapter_idx,
+                                       (uint8_t *) &cmd, CMD_READ_SIZE,
+                                       data_recv, tag, NULL);
+}
+
+static int meta_recv(uint8_t *resp, int length, void *data)
+{
+       struct t2_cookie *cookie = data;
+       struct near_tag *tag;
+       struct type2_tag *t2_tag;
+       uint8_t *cc;
+       int err;
+
+       DBG("%d", length);
+
+       if (length < 0) {
+               err = length;
+               goto out_err;
+       }
+
+       if (resp[0] != 0) {
+               err = -EIO;
+               goto out_err;
+       }
+
+       cc = TAG_DATA_CC(resp + NFC_HEADER_SIZE);
+
+       /* Default to 48 bytes data size in case of blank tag */
+       err = near_tag_add_data(cookie->adapter_idx, cookie->target_idx,
+                       NULL, (TAG_DATA_LENGTH(cc) ? TAG_DATA_LENGTH(cc) :
+                       TYPE2_DATA_SIZE_48 << 3));
+
+       if (err < 0)
+               goto out_err;
+
+       tag = near_tag_get_tag(cookie->adapter_idx, cookie->target_idx);
+       if (tag == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       t2_tag = g_try_malloc0(sizeof(struct type2_tag));
+       if (t2_tag == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       t2_tag->adapter_idx = cookie->adapter_idx;
+       t2_tag->cb = cookie->cb;
+       t2_tag->tag = tag;
+
+       /* Set the ReadWrite flag */
+       if (TAG_T2_WRITE_FLAG(cc) == TYPE2_NOWRITE_ACCESS)
+               near_tag_set_ro(tag, TRUE);
+       else
+               near_tag_set_ro(tag, FALSE);
+
+       near_tag_set_memory_layout(tag, NEAR_TAG_MEMORY_STATIC);
+
+       if (TAG_DATA_NFC(cc) == 0) {
+               DBG("Mark as blank tag");
+               near_tag_set_blank(tag, TRUE);
+       } else {
+               near_tag_set_blank(tag, FALSE);
+       }
+
+       err = data_read(t2_tag);
+       if (err < 0)
+               goto out_tag;
+
+       /*
+        * As reading isn't complete,
+        * callback shouldn't be called while freeing the cookie
+        */
+       cookie->cb = NULL;
+       return t2_cookie_release(err, cookie);
+
+out_tag:
+       g_free(t2_tag);
+
+out_err:
+       return t2_cookie_release(err, cookie);
+}
+
+static int nfctype2_read_meta(uint32_t adapter_idx, uint32_t target_idx,
+                                                       near_tag_io_cb cb)
+{
+       struct type2_cmd cmd;
+       struct t2_cookie *cookie;
+
+       DBG("");
+
+       cmd.cmd = CMD_READ;
+       cmd.block = META_BLOCK_START;
+
+       cookie = g_try_malloc0(sizeof(struct t2_cookie));
+       if (cookie == NULL)
+               return -ENOMEM;
+
+       cookie->adapter_idx = adapter_idx;
+       cookie->target_idx = target_idx;
+       cookie->cb = cb;
+
+       return near_adapter_send(adapter_idx, (uint8_t *) &cmd, CMD_READ_SIZE,
+                                       meta_recv, cookie, t2_cookie_release);
+}
+
+static int nfctype2_read(uint32_t adapter_idx,
+                               uint32_t target_idx, near_tag_io_cb cb)
+{
+       enum near_tag_sub_type tgt_subtype;
+
+       DBG("");
+
+       tgt_subtype = near_tag_get_subtype(adapter_idx, target_idx);
+
+       switch (tgt_subtype) {
+       case NEAR_TAG_NFC_T2_MIFARE_ULTRALIGHT:
+               return nfctype2_read_meta(adapter_idx, target_idx, cb);
+
+       /* Specific Mifare read access */
+       case NEAR_TAG_NFC_T2_MIFARE_CLASSIC_1K:
+       case NEAR_TAG_NFC_T2_MIFARE_CLASSIC_4K:
+               return mifare_read(adapter_idx, target_idx,
+                       cb, tgt_subtype);
+
+       default:
+               DBG("Unknown Tag Type 2 subtype %d", tgt_subtype);
+               return -1;
+       }
+}
+
+static int data_write_resp(uint8_t *resp, int length, void *data)
+{
+       int err;
+       struct t2_cookie *cookie = data;
+       struct type2_cmd cmd;
+
+       DBG("");
+
+       if (length < 0 || resp[0] != 0) {
+               err = -EIO;
+               goto out_err;
+       }
+
+       if (cookie->ndef->offset > cookie->ndef->length) {
+               DBG("Done writing");
+
+               return t2_cookie_release(0, cookie);
+       }
+
+       cmd.cmd = CMD_WRITE;
+       cmd.block = cookie->current_block;
+       cookie->current_block++;
+
+       if ((cookie->ndef->offset + BLOCK_SIZE) <
+                       cookie->ndef->length) {
+               memcpy(cmd.data, cookie->ndef->data +
+                                       cookie->ndef->offset, BLOCK_SIZE);
+               cookie->ndef->offset += BLOCK_SIZE;
+       } else {
+               memcpy(cmd.data, cookie->ndef->data + cookie->ndef->offset,
+                               cookie->ndef->length - cookie->ndef->offset);
+               cookie->ndef->offset = cookie->ndef->length + 1;
+       }
+
+       return near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd,
+                                       sizeof(cmd), data_write_resp, cookie,
+                                       t2_cookie_release);
+
+out_err:
+       return t2_cookie_release(err, cookie);
+}
+
+static int data_write(uint32_t adapter_idx, uint32_t target_idx,
+                               struct near_ndef_message *ndef,
+                               near_tag_io_cb cb)
+{
+       struct type2_cmd cmd;
+       struct t2_cookie *cookie;
+       int err;
+
+       DBG("");
+
+       cookie = g_try_malloc0(sizeof(struct t2_cookie));
+       if (cookie == NULL) {
+               err = -ENOMEM;
+               if (cb != NULL)
+                       cb(adapter_idx, target_idx, err);
+               return err;
+       }
+
+       cookie->adapter_idx = adapter_idx;
+       cookie->target_idx = target_idx;
+       cookie->current_block = DATA_BLOCK_START;
+       cookie->ndef = ndef;
+       cookie->cb = cb;
+
+       cmd.cmd = CMD_WRITE;
+       cmd.block = cookie->current_block;
+       memcpy(cmd.data, cookie->ndef->data, BLOCK_SIZE);
+       cookie->ndef->offset += BLOCK_SIZE;
+       cookie->current_block++;
+
+       return near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd,
+                                       sizeof(cmd), data_write_resp, cookie,
+                                       t2_cookie_release);
+}
+
+static int nfctype2_write(uint32_t adapter_idx, uint32_t target_idx,
+                               struct near_ndef_message *ndef,
+                               near_tag_io_cb cb)
+{
+       struct near_tag *tag;
+       enum near_tag_sub_type tgt_subtype;
+       int err;
+
+       DBG("");
+
+       if (ndef == NULL || cb == NULL) {
+               err = -EINVAL;
+               goto out_err;
+       }
+
+       tag = near_tag_get_tag(adapter_idx, target_idx);
+       if (tag == NULL) {
+               err = -EINVAL;
+               goto out_err;
+       }
+
+       tgt_subtype = near_tag_get_subtype(adapter_idx, target_idx);
+
+       switch (tgt_subtype) {
+       case NEAR_TAG_NFC_T2_MIFARE_ULTRALIGHT:
+               /*
+                * This check is valid for only static tags.
+                * Max data length on Type 2 Tag
+                * including TLV's is NDEF_MAX_SIZE
+                */
+               if (near_tag_get_memory_layout(tag) == NEAR_TAG_MEMORY_STATIC) {
+                       if ((ndef->length + 3) > NDEF_MAX_SIZE) {
+                               near_error("not enough space on tag");
+                               err = -ENOSPC;
+                               goto out_err;
+                       }
+               }
+
+               return data_write(adapter_idx, target_idx, ndef, cb);
+
+       /* Specific Mifare write access */
+       case NEAR_TAG_NFC_T2_MIFARE_CLASSIC_1K:
+       case NEAR_TAG_NFC_T2_MIFARE_CLASSIC_4K:
+               return mifare_write(adapter_idx, target_idx, ndef,
+                               cb, tgt_subtype);
+       default:
+               DBG("Unknown TAG Type 2 subtype %d", tgt_subtype);
+               err = -EINVAL;
+               goto out_err;
+       }
+
+       return 0;
+
+out_err:
+       if (cb != NULL)
+               cb(adapter_idx, target_idx, err);
+
+       return err;
+}
+
+static int check_presence(uint8_t *resp, int length, void *data)
+{
+       struct t2_cookie *cookie = data;
+       int err = 0;
+
+       DBG("%d", length);
+
+       if (length < 0)
+               err = -EIO;
+
+       return t2_cookie_release(err, cookie);
+}
+
+static int nfctype2_check_presence(uint32_t adapter_idx, uint32_t target_idx,
+                                                       near_tag_io_cb cb)
+{
+       struct type2_cmd cmd;
+       struct t2_cookie *cookie;
+       enum near_tag_sub_type tgt_subtype;
+
+       DBG("");
+
+       tgt_subtype = near_tag_get_subtype(adapter_idx, target_idx);
+
+       switch (tgt_subtype) {
+       case NEAR_TAG_NFC_T2_MIFARE_ULTRALIGHT:
+               cmd.cmd = CMD_READ;
+               cmd.block = META_BLOCK_START;
+
+               cookie = g_try_malloc0(sizeof(struct t2_cookie));
+               if (cookie == NULL)
+                       return -ENOMEM;
+
+               cookie->adapter_idx = adapter_idx;
+               cookie->target_idx = target_idx;
+               cookie->cb = cb;
+
+               return near_adapter_send(adapter_idx, (uint8_t *) &cmd,
+                                       CMD_READ_SIZE, check_presence, cookie,
+                                       t2_cookie_release);
+
+       /* Specific Mifare check presence */
+       case NEAR_TAG_NFC_T2_MIFARE_CLASSIC_1K:
+       case NEAR_TAG_NFC_T2_MIFARE_CLASSIC_4K:
+               return mifare_check_presence(adapter_idx, target_idx,
+                                                       cb, tgt_subtype);
+
+       default:
+               DBG("Unknown TAG Type 2 subtype %d", tgt_subtype);
+
+               return -1;
+       }
+}
+
+static int format_resp(uint8_t *resp, int length, void *data)
+{
+       int err = 0;
+       struct t2_cookie *cookie = data;
+       struct near_tag *tag;
+
+       DBG("");
+
+       if (length < 0 || resp[0] != 0) {
+               err = -EIO;
+               goto out_err;
+       }
+
+       tag = near_tag_get_tag(cookie->adapter_idx, cookie->target_idx);
+       if (tag == NULL) {
+               err = -EINVAL;
+               goto out_err;
+       }
+
+       DBG("Done formatting");
+       near_tag_set_blank(tag, FALSE);
+
+out_err:
+       return t2_cookie_release(err, cookie);
+}
+
+static int nfctype2_format(uint32_t adapter_idx, uint32_t target_idx,
+                               near_tag_io_cb cb)
+{
+       struct type2_cmd cmd;
+       struct t2_cookie *cookie;
+       struct near_ndef_message *cc_ndef;
+       struct type2_cc *t2_cc;
+       struct near_tag *tag;
+       enum near_tag_sub_type tgt_subtype;
+       int err;
+
+       DBG("");
+
+       tag = near_tag_get_tag(adapter_idx, target_idx);
+       if (tag == NULL)
+               return -EINVAL;
+
+
+       tgt_subtype = near_tag_get_subtype(adapter_idx, target_idx);
+
+       if (tgt_subtype != NEAR_TAG_NFC_T2_MIFARE_ULTRALIGHT) {
+               DBG("Unknown Tag Type 2 subtype %d", tgt_subtype);
+               return -1;
+       }
+
+       t2_cc = g_try_malloc0(sizeof(struct type2_cc));
+       cc_ndef = g_try_malloc0(sizeof(struct near_ndef_message));
+       cookie = g_try_malloc0(sizeof(struct t2_cookie));
+
+       if (t2_cc == NULL || cc_ndef == NULL || cookie == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       t2_cc->magic = TYPE2_MAGIC;
+       t2_cc->version = TYPE2_TAG_VER_1_0;
+       t2_cc->mem_size = TYPE2_DATA_SIZE_48;
+       t2_cc->read_write = TYPE2_READWRITE_ACCESS;
+
+       cookie->adapter_idx = adapter_idx;
+       cookie->target_idx = target_idx;
+       cookie->current_block = CC_BLOCK_START;
+       cookie->ndef = cc_ndef;
+       cookie->ndef->data = (uint8_t *) t2_cc;
+       cookie->cb = cb;
+
+       cmd.cmd = CMD_WRITE;
+       cmd.block = CC_BLOCK_START;
+       memcpy(cmd.data, (uint8_t *) t2_cc, BLOCK_SIZE);
+
+       err = near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd,
+                               sizeof(cmd), format_resp, cookie, NULL);
+
+out_err:
+       if (err < 0) {
+               g_free(t2_cc);
+               g_free(cc_ndef);
+               g_free(cookie);
+       }
+
+       return err;
+}
+
+static struct near_tag_driver type2_driver = {
+       .type           = NFC_PROTO_MIFARE,
+       .priority       = NEAR_TAG_PRIORITY_DEFAULT,
+       .read           = nfctype2_read,
+       .write          = nfctype2_write,
+       .check_presence = nfctype2_check_presence,
+       .format         = nfctype2_format,
+};
+
+static int nfctype2_init(void)
+{
+       DBG("");
+
+       return near_tag_driver_register(&type2_driver);
+}
+
+static void nfctype2_exit(void)
+{
+       DBG("");
+
+       near_tag_driver_unregister(&type2_driver);
+}
+
+NEAR_PLUGIN_DEFINE(nfctype2, "NFC Forum Type 2 tags support", VERSION,
+                       NEAR_PLUGIN_PRIORITY_HIGH, nfctype2_init, nfctype2_exit)
diff --git a/plugins/nfctype3.c b/plugins/nfctype3.c
new file mode 100644 (file)
index 0000000..f45e1a7
--- /dev/null
@@ -0,0 +1,998 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <linux/socket.h>
+
+#include <near/nfc.h>
+#include <near/plugin.h>
+#include <near/log.h>
+#include <near/types.h>
+#include <near/adapter.h>
+#include <near/tag.h>
+#include <near/ndef.h>
+#include <near/tlv.h>
+
+#define CMD_POLL               0x00
+#define RESP_POLL              0x01
+
+#define        CMD_REQUEST_SERVICE     0x02
+#define        RESP_REQUEST_SERVICE    0x03
+
+#define CMD_REQUEST_RESPONSE   0x04
+#define RESP_REQUEST_RESPONSE  0x05
+
+#define CMD_READ_WO_ENCRYPT    0x06
+#define RESP_READ_WO_ENCRYPT   0x07
+
+#define CMD_WRITE_WO_ENCRYPT   0x08
+#define RESP_WRITE_WO_ENCRYPT  0x09
+
+#define CMD_REQUEST_SYS_CODE   0x0C
+#define RESP_REQUEST_SYS_CODE  0x0D
+
+#define CMD_AUTHENTICATION_1   0x10
+#define RESP_AUTHENTICATION_1  0x11
+
+#define CMD_AUTHENTICATION_2   0x12
+#define RESP_AUTHENTICATION_2  0x13
+
+#define CMD_READ               0x14
+#define RESP_READ              0x15
+
+#define CMD_WRITE              0x16
+#define RESP_WRITE             0x17
+
+#define NFC_SERVICE_CODE       0x000B
+#define BLOCK_SIZE             16
+#define META_BLOCK_START       0
+#define DATA_BLOCK_START       1
+
+#define LEN_ID                 0x08
+#define LEN_CMD                        0x01
+#define LEN_REPLY_CMD          0x02
+#define LEN_CMD_LEN            0x01
+
+/* offsets */
+#define OFS_NFC_STATUS 0
+#define OFS_NFC_LEN    1
+#define OFS_CMD_RESP   2
+#define OFS_IDM                3
+#define OFS_CMD_DATA   (LEN_CMD_LEN + LEN_CMD + LEN_ID)
+#define OFS_READ_FLAG  12
+#define OFS_READ_DATA  14
+#define BLOCK_SIZE     16
+#define CHECKSUM_LEN   2
+
+#define MAX_DATA_SIZE  254
+
+#define NDEF_MAPPING_VERSION   0x10
+#define MAX_READ_BLOCKS_PER_CHECK      0x04
+#define MAX_WRITE_BLOCKS_PER_UPDATE    0x01
+#define MAX_BLOCKS_FOR_NDEF_DATA       0x000D
+#define ATTR_BLOCK_WRITE_FLAG  0x00
+#define ATTR_BLOCK_RW_FLAG     0x01
+
+#define IC_TYPE_OFFSET 12
+#define SYSTEM_OPTION_OFFSET   17
+#define FELICA_LITE_MC_BLOCK   0x88
+#define FELICA_LITE_IC_TYPE    0xF0
+#define FELICA_LITE_S_IC_TYPE  0xF1
+#define FELICA_PLUG_IC_TYPE    0xE0
+
+#define FELICA_LITE_AND_LITE_S_SYS_CODE        0x88B4
+
+struct type3_cmd {
+       uint8_t len;
+       uint8_t cmd;
+       uint8_t data[MAX_DATA_SIZE];
+} __attribute__((packed));
+
+struct type3_tag {
+       uint32_t adapter_idx;
+       uint16_t current_block;
+       uint8_t IDm[LEN_ID];
+
+       near_tag_io_cb cb;
+       struct near_tag *tag;
+};
+
+struct t3_cookie {
+       uint32_t adapter_idx;
+       uint32_t target_idx;
+       near_tag_io_cb cb;
+       uint8_t IDm[LEN_ID];
+       uint8_t current_block;
+       uint8_t attr[BLOCK_SIZE];
+       struct near_ndef_message *ndef;
+       uint8_t ic_type;
+       uint8_t mc_block[BLOCK_SIZE];
+};
+
+static int t3_cookie_release(int err, void *data)
+{
+       struct t3_cookie *cookie = data;
+
+       DBG("%p", cookie);
+
+       if (cookie == NULL)
+               return err;
+
+       if (cookie->cb != NULL)
+               cookie->cb(cookie->adapter_idx, cookie->target_idx, err);
+
+       if (cookie->ndef != NULL)
+               g_free(cookie->ndef->data);
+
+       g_free(cookie->ndef);
+       g_free(cookie);
+       cookie = NULL;
+
+       return err;
+}
+
+/* common: Initialize structure to write block */
+static void prepare_write_block(uint8_t *UID, struct type3_cmd *cmd,
+                                       uint8_t block, uint8_t *data)
+{
+       cmd->cmd = CMD_WRITE_WO_ENCRYPT;        /* command */
+       memcpy(cmd->data, UID, LEN_ID);         /* IDm */
+
+       cmd->data[LEN_ID] = 1;                  /* number of services */
+       cmd->data[LEN_ID + 1] = 0x09;           /* service 0x0009 */
+       cmd->data[LEN_ID + 2] = 0x00;
+
+       cmd->data[LEN_ID + 3] = 0x01;           /* number of blocks */
+       cmd->data[LEN_ID + 4] = 0x80;           /* 2 byte block number format */
+       cmd->data[LEN_ID + 5] = block;          /* block number */
+       memcpy(cmd->data + LEN_ID + 6, data, BLOCK_SIZE); /* data to write */
+
+       cmd->len = LEN_ID + LEN_CMD + LEN_CMD_LEN + 6 + BLOCK_SIZE;
+}
+
+/* common: Initialize structure to read block */
+static void prepare_read_block(uint8_t cur_block,
+                               uint8_t *UID,
+                               struct type3_cmd *cmd)
+{
+       cmd->cmd = CMD_READ_WO_ENCRYPT;                 /* command */
+       memcpy(cmd->data, UID, LEN_ID);                 /* IDm */
+
+       cmd->data[LEN_ID] = 1;                          /* number of service */
+       cmd->data[LEN_ID + 1] = 0x0B;                   /* service x000B */
+       cmd->data[LEN_ID + 2] = 0x00;
+
+       cmd->data[LEN_ID + 3] = 0x01;                   /* number of block */
+       cmd->data[LEN_ID + 4] = 0x80;                   /* 2 bytes block id*/
+       cmd->data[LEN_ID + 5] = cur_block;              /* block number */
+
+       cmd->len = LEN_ID + LEN_CMD + LEN_CMD_LEN + 6;
+}
+
+/* common: Simple checks on received frame */
+static int check_recv_frame(uint8_t *resp, uint8_t reply_code)
+{
+       if (resp[OFS_NFC_STATUS] != 0) {
+               DBG("NFC Command failed: 0x%x", resp[OFS_NFC_STATUS]);
+               return -EIO;
+       }
+
+       if (resp[OFS_CMD_RESP] != reply_code) {
+               DBG("Felica cmd failed: 0x%x", resp[OFS_CMD_RESP]);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int data_recv(uint8_t *resp, int length, void *data)
+{
+       struct type3_tag *tag = data;
+       struct type3_cmd cmd;
+       uint8_t *nfc_data;
+       size_t current_length, length_read, data_length;
+       uint32_t adapter_idx;
+       uint32_t target_idx;
+       int read_blocks;
+       int err;
+
+       DBG("%d", length);
+
+       adapter_idx = near_tag_get_adapter_idx(tag->tag);
+       target_idx = near_tag_get_target_idx(tag->tag);
+
+       if (length < 0) {
+               err = -EIO;
+               goto out_err;
+       }
+
+       nfc_data = near_tag_get_data(tag->tag, &data_length);
+       length_read = length - OFS_READ_DATA;
+       current_length = tag->current_block * BLOCK_SIZE;
+       if (current_length + (length - OFS_READ_DATA) > data_length)
+               length_read = data_length - current_length;
+
+       memcpy(nfc_data + current_length, resp + OFS_READ_DATA, length_read);
+
+       if (current_length + length_read >= data_length) {
+               GList *records;
+
+               tag->current_block = 0;
+
+               DBG("Done reading %zd bytes at %p", data_length, nfc_data);
+               records = near_ndef_parse_msg(nfc_data, data_length);
+               near_tag_add_records(tag->tag, records, tag->cb, 0);
+
+               g_free(tag);
+
+               return 0;
+       }
+
+       /* Read the next block */
+       read_blocks = length / BLOCK_SIZE;
+       tag->current_block += read_blocks;
+
+       prepare_read_block(DATA_BLOCK_START + tag->current_block,
+                               tag->IDm, &cmd);
+
+       err = near_adapter_send(adapter_idx, (uint8_t *) &cmd, cmd.len,
+                               data_recv, tag, NULL);
+
+       if (err < 0)
+               goto out_err;
+
+       return 0;
+
+out_err:
+       if (err < 0 && tag->cb)
+               tag->cb(adapter_idx, target_idx, err);
+
+       g_free(tag);
+
+       return err;
+}
+
+static int data_read(struct type3_tag *tag)
+{
+       struct type3_cmd cmd;
+       uint32_t adapter_idx;
+
+       DBG("");
+
+       tag->current_block = 0;
+
+       prepare_read_block(DATA_BLOCK_START + tag->current_block,
+                                                       tag->IDm, &cmd );
+
+       adapter_idx = near_tag_get_adapter_idx(tag->tag);
+
+       return near_adapter_send(adapter_idx,
+                                       (uint8_t *) &cmd, cmd.len,
+                                       data_recv, tag, NULL);
+}
+
+/* Read block 0 to retrieve the data length */
+static int nfctype3_recv_block_0(uint8_t *resp, int length, void *data)
+{
+       struct t3_cookie *cookie = data;
+       int err = 0;
+       struct near_tag *tag;
+       struct type3_tag *t3_tag = NULL;
+       uint32_t ndef_data_length;
+
+       DBG("%d", length);
+
+       if (length < 0) {
+               err = -EIO;
+               goto out_err;
+       }
+
+       err = check_recv_frame(resp, RESP_READ_WO_ENCRYPT);
+       if (err < 0)
+               goto out_err;
+
+       if (resp[OFS_READ_FLAG] != 0) {
+               DBG("Status 0x%x", resp[OFS_READ_FLAG]);
+               err = -EIO;
+               goto out_err;
+       }
+
+       /* Block 0:[11 - 13]: length is a 3 bytes value */
+       ndef_data_length =  resp[OFS_READ_DATA + 11] * 0x100;
+       ndef_data_length += resp[OFS_READ_DATA + 12];
+       ndef_data_length *= 0x100;
+       ndef_data_length += resp[OFS_READ_DATA + 13];
+
+       /* Add data to the tag */
+       err = near_tag_add_data(cookie->adapter_idx, cookie->target_idx,
+                                       NULL, ndef_data_length);
+       if (err < 0)
+               goto out_err;
+
+       tag = near_tag_get_tag(cookie->adapter_idx, cookie->target_idx);
+       if (tag == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       /* Block 0:[10]: RW Flag. 1 for RW */
+       if (resp[OFS_READ_DATA + 10] == 0)
+               near_tag_set_ro(tag, TRUE);
+       else
+               near_tag_set_ro(tag, FALSE);
+
+       t3_tag = g_try_malloc0(sizeof(struct type3_tag));
+       if (t3_tag == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       memcpy(t3_tag->IDm, cookie->IDm, LEN_ID);
+
+       near_tag_set_idm(tag, cookie->IDm, LEN_ID);
+       near_tag_set_attr_block(tag, resp + OFS_READ_DATA, BLOCK_SIZE);
+       near_tag_set_blank(tag, FALSE);
+       near_tag_set_ic_type(tag, cookie->ic_type);
+
+       t3_tag->adapter_idx = cookie->adapter_idx;
+       t3_tag->cb = cookie->cb;
+       t3_tag->tag = tag;
+
+       err = data_read(t3_tag);
+
+       /*
+        * As reading isn't complete,
+        * callback shouldn't be called while freeing the cookie
+        */
+       if (err == 0)
+               cookie->cb = NULL;
+
+out_err:
+       if (err < 0)
+               g_free(t3_tag);
+
+       return t3_cookie_release(err, cookie);
+}
+
+static int poll_ndef_system_code(uint8_t *resp, int length, void *data)
+{
+       struct t3_cookie *cookie = data;
+       int err = 0;
+       struct type3_cmd cmd;
+
+       DBG("length: %d", length);
+
+       if (length < 0) {
+               err = -EIO;
+               goto out_err;
+       }
+
+       err = check_recv_frame(resp, RESP_POLL);
+       if (err < 0)
+               goto out_err;
+
+       prepare_read_block(META_BLOCK_START, cookie->IDm, &cmd);
+
+       err = near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd,
+                               cmd.len, nfctype3_recv_block_0, cookie, NULL);
+       if (err < 0)
+               goto out_err;
+
+       return 0;
+
+out_err:
+       return t3_cookie_release(err, cookie);
+}
+
+static int check_sys_op_in_mc_block(uint8_t *resp, int length, void *data)
+{
+       struct type3_cmd cmd;
+       struct near_tag *tag;
+       struct t3_cookie *cookie = data;
+       int err = 0;
+
+       DBG("length %d", length);
+
+       if (length < 0) {
+               err = -EIO;
+               goto out_err;
+       }
+
+       err = check_recv_frame(resp, RESP_READ_WO_ENCRYPT);
+       if (err < 0)
+               goto out_err;
+
+       if (resp[SYSTEM_OPTION_OFFSET] == 0x00) {
+               DBG("Blank tag detected");
+
+               err = near_tag_add_data(cookie->adapter_idx,
+                                       cookie->target_idx,
+                                       NULL, 1 /* dummy length */);
+               if (err < 0)
+                       goto out_err;
+
+               tag = near_tag_get_tag(cookie->adapter_idx,
+                                       cookie->target_idx);
+               if (tag == NULL) {
+                       err = -ENOMEM;
+               goto out_err;
+               }
+
+               near_tag_set_idm(tag, cookie->IDm, LEN_ID);
+               near_tag_set_ic_type(tag, cookie->ic_type);
+               near_tag_set_blank(tag, TRUE);
+
+               return t3_cookie_release(0, cookie);
+       } else {
+               /* CMD POLL */
+               cmd.cmd  = CMD_POLL;    /* POLL command */
+               cmd.data[0] = 0x12;     /* System code (NFC SC) */
+               cmd.data[1] = 0xFC;
+               cmd.data[2] = 01;       /* request code */
+               cmd.data[3] = 0x00;     /* time slot */
+               /* data len + 2 bytes */
+               cmd.len = LEN_CMD + LEN_CMD_LEN + 4 ;
+
+               err = near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd,
+                       cmd.len , poll_ndef_system_code, cookie, NULL);
+       }
+
+       if (err < 0)
+               goto out_err;
+
+       return 0;
+
+out_err:
+       return t3_cookie_release(err, cookie);
+}
+
+static int receive_system_code(uint8_t *resp, int length, void *data)
+{
+       struct t3_cookie *cookie = data;
+       int err = 0;
+       struct type3_cmd cmd;
+       uint16_t system_code;
+
+       DBG("length: %d", length);
+
+       if (length < 0) {
+               err = -EIO;
+               goto out_err;
+       }
+
+       err = check_recv_frame(resp, RESP_POLL);
+       if (err < 0)
+               goto out_err;
+
+       cookie->ic_type = resp[IC_TYPE_OFFSET];
+       memcpy(cookie->IDm, resp + OFS_IDM, LEN_ID);
+       system_code = ((uint16_t) (resp[length - 2])) << 8;
+       system_code |= resp[length - 1];
+
+       switch (resp[IC_TYPE_OFFSET]) {
+       case FELICA_LITE_IC_TYPE:
+               prepare_read_block(FELICA_LITE_MC_BLOCK, cookie->IDm, &cmd);
+               err = near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd,
+                                       cmd.len, check_sys_op_in_mc_block,
+                                       cookie, NULL);
+               break;
+
+       case FELICA_LITE_S_IC_TYPE:
+       case FELICA_PLUG_IC_TYPE:
+               /* CMD POLL */
+               cmd.cmd  = CMD_POLL;    /* POLL command */
+               cmd.data[0] = 0x12;     /* System code (NFC SC) */
+               cmd.data[1] = 0xFC;
+               cmd.data[2] = 01;       /* request code */
+               cmd.data[3] = 0x00;     /* time slot */
+               /* data len + 2 bytes */
+               cmd.len = LEN_CMD + LEN_CMD_LEN + 4 ;
+
+               err = near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd,
+                                       cmd.len, poll_ndef_system_code, cookie,
+                                       NULL);
+       }
+
+       if (err < 0)
+               goto out_err;
+
+       return 0;
+
+out_err:
+       return t3_cookie_release(err, cookie);
+}
+
+static int nfctype3_read(uint32_t adapter_idx,
+                               uint32_t target_idx, near_tag_io_cb cb)
+{
+       struct type3_cmd cmd;
+       struct t3_cookie *cookie;
+
+       DBG("");
+
+       /* CMD POLL */
+       cmd.cmd  = CMD_POLL;    /* POLL command */
+       cmd.data[0] = 0xFF;     /* System code */
+       cmd.data[1] = 0xFF;
+       cmd.data[2] = 01;       /* request code */
+       cmd.data[3] = 0x00;     /* time slot */
+
+       /* data len + 2 bytes */
+       cmd.len = LEN_CMD + LEN_CMD_LEN + 4;
+
+       cookie = g_try_malloc0(sizeof(struct t3_cookie));
+       if (cookie == NULL)
+               return -ENOMEM;
+
+       cookie->adapter_idx = adapter_idx;
+       cookie->target_idx = target_idx;
+       cookie->cb = cb;
+
+       return near_adapter_send(adapter_idx, (uint8_t *) &cmd, cmd.len,
+                                       receive_system_code, cookie,
+                                       t3_cookie_release);
+}
+
+static int update_attr_block_cb(uint8_t *resp, int length, void *data)
+{
+       struct t3_cookie *cookie = data;
+       int err;
+
+       DBG("");
+
+       if (length < 0) {
+               err = -EIO;
+               goto out_err;
+       }
+
+       err = check_recv_frame(resp, RESP_WRITE_WO_ENCRYPT);
+       if (err < 0)
+               goto out_err;
+
+       DBG("Done writing");
+
+out_err:
+       return t3_cookie_release(err, cookie);
+}
+
+static int update_attr_block(struct t3_cookie *cookie)
+{
+       struct type3_cmd cmd;
+       uint16_t checksum;
+       uint8_t i;
+
+       DBG("");
+
+       cookie->attr[9] = 0x00; /* writing data completed */
+       cookie->attr[11] = (uint8_t) (cookie->ndef->length >> 16);
+       cookie->attr[12] = (uint8_t) (cookie->ndef->length >> 8);
+       cookie->attr[13] = (uint8_t) cookie->ndef->length;
+       checksum = 0;
+
+       for (i = 0; i < (BLOCK_SIZE - CHECKSUM_LEN); i++)
+               checksum += cookie->attr[i];
+
+       cookie->attr[14] = (uint8_t) (checksum >> 8);
+       cookie->attr[15] = (uint8_t) checksum;
+
+       prepare_write_block(cookie->IDm, &cmd, 0, cookie->attr);
+
+       return near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd, cmd.len,
+                                       update_attr_block_cb, cookie,
+                                       t3_cookie_release);
+}
+
+static int data_write_resp(uint8_t *resp, int length, void *data)
+{
+       struct t3_cookie *cookie = data;
+       struct type3_cmd cmd;
+       uint8_t padding[BLOCK_SIZE] = {0};
+       int err;
+
+       DBG("");
+
+       if (length < 0) {
+               err = -EIO;
+               goto out_err;
+       }
+
+       err = check_recv_frame(resp, RESP_WRITE_WO_ENCRYPT);
+       if (err < 0)
+               goto out_err;
+
+       if (cookie->ndef->offset >= cookie->ndef->length) {
+               err = update_attr_block(cookie);
+               if (err < 0)
+                       goto out_err;
+
+               return 0;
+       }
+
+       if ((cookie->ndef->length - cookie->ndef->offset) <
+                       BLOCK_SIZE) {
+               memcpy(padding, cookie->ndef->data + cookie->ndef->offset,
+                               cookie->ndef->length - cookie->ndef->offset);
+               prepare_write_block(cookie->IDm, &cmd,
+                                       cookie->current_block, padding);
+       } else {
+               prepare_write_block(cookie->IDm, &cmd, cookie->current_block,
+                               cookie->ndef->data + cookie->ndef->offset);
+       }
+
+       cookie->current_block++;
+       cookie->ndef->offset += BLOCK_SIZE;
+
+       err = near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd, cmd.len,
+                                               data_write_resp, cookie, NULL);
+       if (err < 0)
+               goto out_err;
+
+       return 0;
+
+out_err:
+       return t3_cookie_release(err, cookie);
+}
+
+static int data_write(uint32_t adapter_idx, uint32_t target_idx,
+                               struct near_ndef_message *ndef,
+                               struct near_tag *tag,
+                               near_tag_io_cb cb)
+{
+       struct t3_cookie *cookie;
+       struct type3_cmd cmd;
+       uint16_t checksum, nmaxb;
+       uint8_t i, len = 0;
+       uint8_t *idm, *attr;
+       int err;
+
+       DBG("");
+
+       cookie = g_try_malloc0(sizeof(struct t3_cookie));
+
+       if (cookie == NULL) {
+               err = -ENOMEM;
+
+               if (cb != NULL)
+                       cb(adapter_idx, target_idx, err);
+
+               return err;
+       }
+
+       cookie->adapter_idx = adapter_idx;
+       cookie->target_idx = target_idx;
+       cookie->ndef = ndef;
+       cookie->cb = cb;
+       cookie->current_block = 0;
+
+       idm = near_tag_get_idm(tag, &len);
+       if (idm == NULL)
+               return t3_cookie_release(-EINVAL, cookie);
+
+       memcpy(cookie->IDm, idm, len);
+
+       attr = near_tag_get_attr_block(tag, &len);
+       if (attr == NULL)
+               return t3_cookie_release(-EINVAL, cookie);
+
+       memcpy(cookie->attr, attr, len);
+       nmaxb = (((uint16_t) (cookie->attr[3])) << 8) | cookie->attr[4];
+
+       if (cookie->ndef->length > (nmaxb * BLOCK_SIZE)) {
+               near_error("not enough space on tag");
+
+               return t3_cookie_release(-ENOSPC, cookie);
+       }
+
+       cookie->attr[9] = 0x0F; /* writing data in progress */
+       checksum = 0;
+
+       for (i = 0; i < 14; i++)
+               checksum += cookie->attr[i];
+
+       cookie->attr[14] = (uint8_t) (checksum >> 8);
+       cookie->attr[15] = (uint8_t) checksum;
+
+       prepare_write_block(cookie->IDm, &cmd, cookie->current_block,
+                                                       cookie->attr);
+       cookie->current_block++;
+
+       return near_adapter_send(adapter_idx, (uint8_t *) &cmd, cmd.len,
+                                       data_write_resp, cookie,
+                                       t3_cookie_release);
+}
+
+static int nfctype3_write(uint32_t adapter_idx, uint32_t target_idx,
+                               struct near_ndef_message *ndef,
+                               near_tag_io_cb cb)
+{
+       struct near_tag *tag;
+       int err;
+
+       DBG("");
+
+       if (ndef == NULL || cb == NULL) {
+               err = -EINVAL;
+               goto out_err;
+       }
+
+       tag = near_tag_get_tag(adapter_idx, target_idx);
+       if (tag == NULL) {
+               err = -EINVAL;
+               goto out_err;
+       }
+
+       err = data_write(adapter_idx, target_idx, ndef, tag, cb);
+
+out_err:
+       if (cb != NULL)
+               cb(adapter_idx, target_idx, err);
+
+       return err;
+}
+
+static int check_presence(uint8_t *resp, int length, void *data)
+{
+       struct t3_cookie *cookie = data;
+       int err = 0;
+
+       DBG("length %d", length);
+
+       if (length < 0)
+               err = -EIO;
+
+       return t3_cookie_release(err, cookie);
+}
+
+static int nfctype3_check_presence(uint32_t adapter_idx,
+                               uint32_t target_idx, near_tag_io_cb cb)
+{
+       struct type3_cmd cmd;
+       struct t3_cookie *cookie;
+
+       DBG("");
+
+       /* CMD POLL */
+       cmd.cmd  = CMD_POLL;    /* POLL command */
+       cmd.data[0] = 0xFF;     /* System code */
+       cmd.data[1] = 0xFF;
+       cmd.data[2] = 01;       /* request code */
+       cmd.data[3] = 0x00;     /* time slot */
+
+       /* data len + 2 bytes */
+       cmd.len = LEN_CMD + LEN_CMD_LEN + 4 ;
+
+       cookie = g_try_malloc0(sizeof(struct t3_cookie));
+       if (cookie == NULL)
+               return -ENOMEM;
+
+       cookie->adapter_idx = adapter_idx;
+       cookie->target_idx = target_idx;
+       cookie->cb = cb;
+
+       return near_adapter_send(adapter_idx, (uint8_t *) &cmd,
+                               cmd.len, check_presence, cookie,
+                               t3_cookie_release);
+}
+
+static int format_resp(uint8_t *resp, int length, void *data)
+{
+       struct near_tag *tag;
+       struct t3_cookie *cookie = data;
+       int err;
+
+       DBG("");
+
+       if (length < 0) {
+               err = -EIO;
+               goto out_err;
+       }
+
+       err = check_recv_frame(resp, RESP_WRITE_WO_ENCRYPT);
+       if (err < 0)
+               goto out_err;
+
+       tag = near_tag_get_tag(cookie->adapter_idx, cookie->target_idx);
+       if (tag == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       near_tag_set_ro(tag, FALSE);
+       near_tag_set_idm(tag, cookie->IDm, LEN_ID);
+       near_tag_set_attr_block(tag, cookie->attr, BLOCK_SIZE);
+       near_tag_set_blank(tag, FALSE);
+
+       DBG("Formatting is done");
+
+out_err:
+       return t3_cookie_release(err, cookie);
+}
+
+static int write_attr_block(uint8_t *resp, int length , void *data)
+{
+       struct type3_cmd cmd;
+       struct t3_cookie *cookie = data;
+       int err, i;
+       uint16_t checksum = 0;
+
+       DBG("length %d", length);
+
+       if (length < 0) {
+               err = -EIO;
+               goto out_err;
+       }
+
+       err = check_recv_frame(resp, RESP_WRITE_WO_ENCRYPT);
+       if (err < 0)
+               goto out_err;
+
+       cookie->attr[0] = NDEF_MAPPING_VERSION;
+       cookie->attr[1] = MAX_READ_BLOCKS_PER_CHECK;
+       cookie->attr[2] = MAX_WRITE_BLOCKS_PER_UPDATE;
+       cookie->attr[3] = (uint8_t) (MAX_BLOCKS_FOR_NDEF_DATA >> 8);
+       cookie->attr[4] = (uint8_t) (MAX_BLOCKS_FOR_NDEF_DATA);
+       cookie->attr[5] = 0;
+       cookie->attr[6] = 0;
+       cookie->attr[7] = 0;
+       cookie->attr[8] = 0;
+       cookie->attr[9] = ATTR_BLOCK_WRITE_FLAG;
+       cookie->attr[10] = ATTR_BLOCK_RW_FLAG;
+       cookie->attr[11] = 0;
+       cookie->attr[12] = 0;
+       cookie->attr[13] = 0;
+
+       for (i = 0; i < (BLOCK_SIZE - CHECKSUM_LEN); i++)
+               checksum += cookie->attr[i];
+
+       cookie->attr[14] = (uint8_t) (checksum >> 8);
+       cookie->attr[15] = (uint8_t) checksum;
+
+       prepare_write_block(cookie->IDm, &cmd, META_BLOCK_START,
+                                               cookie->attr);
+
+       err = near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd,
+                               cmd.len, format_resp, cookie, NULL);
+       if (err < 0)
+               goto out_err;
+
+       return 0;
+
+out_err:
+       return t3_cookie_release(err, cookie);
+}
+
+static int write_mc_block(uint8_t *resp, int length, void *data)
+{
+       struct type3_cmd cmd;
+       struct t3_cookie *cookie = data;
+       int err;
+
+       DBG("length %d", length);
+
+       if (length < 0) {
+               err = -EIO;
+               goto out_err;
+       }
+
+       err = check_recv_frame(resp, RESP_READ_WO_ENCRYPT);
+       if (err < 0)
+               goto out_err;
+
+       if (resp[OFS_READ_FLAG] != 0) {
+               DBG("Status 0x%x", resp[OFS_READ_FLAG]);
+               err = -EIO;
+               goto out_err;
+       }
+
+       memcpy(cookie->mc_block, resp + 14, BLOCK_SIZE);
+       /*
+        * By updating Byte3 to 01h means making Felica Lite
+        * compatible with NDEF.
+        */
+       cookie->mc_block[3] = 1;
+       prepare_write_block(cookie->IDm, &cmd, FELICA_LITE_MC_BLOCK,
+                               cookie->mc_block);
+       err = near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd,
+                               cmd.len, write_attr_block, cookie, NULL);
+       if (err < 0)
+               goto out_err;
+
+       return 0;
+
+out_err:
+       return t3_cookie_release(err, cookie);
+}
+
+static int nfctype3_format(uint32_t adapter_idx,
+                               uint32_t target_idx, near_tag_io_cb cb)
+{
+       struct type3_cmd cmd;
+       struct near_tag *tag;
+       struct t3_cookie *cookie;
+       uint8_t ic_type;
+       uint8_t *idm, len;
+
+       DBG("");
+
+       tag = near_tag_get_tag(adapter_idx, target_idx);
+       if (tag == NULL)
+               return -ENOMEM;
+
+       ic_type = near_tag_get_ic_type(tag);
+       if (ic_type != FELICA_LITE_IC_TYPE)
+               return -EOPNOTSUPP;
+
+       cookie = g_try_malloc0(sizeof(struct t3_cookie));
+       if (cookie == NULL)
+               return -ENOMEM;
+
+       cookie->adapter_idx = adapter_idx;
+       cookie->target_idx = target_idx;
+       cookie->cb = cb;
+       cookie->ic_type = ic_type;
+
+       idm = near_tag_get_idm(tag, &len);
+       if (idm == NULL)
+               return t3_cookie_release(-EINVAL, cookie);
+
+       memcpy(cookie->IDm, idm, len);
+
+       prepare_read_block(FELICA_LITE_MC_BLOCK, cookie->IDm, &cmd);
+
+       return near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd, cmd.len,
+                                       write_mc_block, cookie,
+                                       t3_cookie_release);
+
+}
+
+static struct near_tag_driver type1_driver = {
+       .type           = NFC_PROTO_FELICA,
+       .priority       = NEAR_TAG_PRIORITY_DEFAULT,
+       .read           = nfctype3_read,
+       .write          = nfctype3_write,
+       .format         = nfctype3_format,
+       .check_presence = nfctype3_check_presence,
+};
+
+static int nfctype3_init(void)
+{
+       DBG("");
+
+       return near_tag_driver_register(&type1_driver);
+}
+
+static void nfctype3_exit(void)
+{
+       DBG("");
+
+       near_tag_driver_unregister(&type1_driver);
+}
+
+NEAR_PLUGIN_DEFINE(nfctype3, "NFC Forum Type 3 tags support", VERSION,
+                       NEAR_PLUGIN_PRIORITY_HIGH, nfctype3_init, nfctype3_exit)
diff --git a/plugins/nfctype4.c b/plugins/nfctype4.c
new file mode 100644 (file)
index 0000000..694d613
--- /dev/null
@@ -0,0 +1,1481 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <linux/socket.h>
+
+#include <near/nfc.h>
+#include <near/plugin.h>
+#include <near/log.h>
+#include <near/types.h>
+#include <near/adapter.h>
+#include <near/tag.h>
+#include <near/ndef.h>
+#include <near/tlv.h>
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+
+#define NFC_STATUS             0
+#define NFC_STATUS_BYTE_LEN    1
+
+#define STATUS_WORD_1          1
+#define STATUS_WORD_2          2
+#define APDU_HEADER_LEN                5
+#define APDU_OK                        0x9000
+#define APDU_NOT_FOUND         0x6A82
+
+/* PICC Level Commands */
+#define PICC_CLASS             0x90
+#define GET_VERSION            0x60
+#define CREATE_APPLICATION     0xCA
+#define SELECT_APPLICATION     0x5A
+#define CREATE_STD_DATA_FILE   0xCD
+#define WRITE_DATA_TO_FILE     0x3D
+
+#define DESFire_EV1_MAJOR_VERSION      0x01
+#define PICC_LEVEL_APDU_OK             0x9100
+#define GET_VERSION_FRAME_RESPONSE_BYTE 0xAF
+#define DESFIRE_KEY_SETTINGS   0x0F
+#define        DESFIRE_NUM_OF_KEYS     0x21
+#define DESFIRE_CC_FILE_NUM    0x01
+#define DESFIRE_NDEF_FILE_NUM  0x02
+#define DESFIRE_COMMSET                0x00
+#define MAPPING_VERSION                0x20
+#define        FREE_READ_ACCESS        0x00
+#define        FREE_WRITE_ACCESS       0x00
+
+#define T4_ALL_ACCESS          0x00
+#define T4_READ_ONLY           0xFF
+
+#define APDU_STATUS(a) (g_ntohs(*((uint16_t *)(a))))
+
+/* Tag Type 4 version ID */
+static uint8_t iso_appname_v1[] = { 0xd2, 0x76, 0x0, 0x0, 0x85, 0x01, 0x0 };
+static uint8_t iso_appname_v2[] = { 0xd2, 0x76, 0x0, 0x0, 0x85, 0x01, 0x1 };
+
+/* Tag T4 File ID */
+static uint8_t iso_cc_fileid[] = { 0xe1, 0x03 };
+#define LEN_ISO_CC_FILEID      2
+
+#define LEN_ISO_CC_READ_SIZE   0x0F
+
+#define CMD_HEADER_SIZE                5
+struct type4_cmd {                     /* iso 7816 */
+       uint8_t class;
+       uint8_t instruction;
+       uint8_t param1;
+       uint8_t param2;
+       uint8_t data_length;
+       uint8_t data[];
+} __attribute__((packed));
+
+struct type4_NDEF_file_control_tlv {
+       uint8_t tag     ;               /* should be 4 */
+       uint8_t len     ;               /* should be 6 */
+       uint16_t file_id ;
+       uint16_t max_ndef_size ;
+       uint8_t read_access ;
+       uint8_t write_access ;
+} __attribute__((packed));
+
+struct type4_cc {                      /* Capability Container */
+       uint16_t CCLEN;
+       uint8_t mapping_version;
+       uint16_t max_R_apdu_data_size;
+       uint16_t max_C_apdu_data_size;
+       struct type4_NDEF_file_control_tlv tlv_fc ;
+       uint8_t tlv_blocks[];
+} __attribute__((packed));
+
+struct desfire_app {
+       uint8_t aid[3];
+       uint8_t key_settings;
+       uint8_t number_of_keys;
+       uint8_t file_id[2];
+       uint8_t iso_appname[7];
+} __attribute__((packed));
+
+struct desfire_std_file {
+       uint8_t file_num;
+       uint8_t file_id[2];
+       uint8_t comm_set;
+       uint8_t access_rights[2];
+       uint8_t size[3];
+} __attribute__((packed));
+
+struct desfire_cc_file {
+       uint8_t file_num;
+       uint8_t offset[3];
+       uint8_t max_len[3];
+       uint8_t cc_len[2];
+       uint8_t version;
+       uint8_t mle[2];
+       uint8_t mlc[2];
+       uint8_t ndef_tlv[4];
+       uint8_t ndef_size[2];
+       uint8_t read_access;
+       uint8_t write_access;
+} __attribute__((packed));
+
+struct t4_cookie {
+       uint32_t adapter_idx;
+       uint32_t target_idx;
+       near_tag_io_cb cb;
+       struct near_tag *tag;
+       uint16_t read_data;
+       uint16_t r_apdu_max_size;
+       uint16_t c_apdu_max_size;
+       uint16_t max_ndef_size;
+       uint8_t write_access;
+       struct near_ndef_message *ndef;
+       uint16_t memory_size;
+};
+
+static int t4_cookie_release(int err, void *data)
+{
+       struct t4_cookie *cookie = data;
+
+       DBG("%p", cookie);
+
+       if (cookie == NULL)
+               return err;
+
+       if (err < 0 && cookie->cb)
+               cookie->cb(cookie->adapter_idx, cookie->target_idx, err);
+
+       if (cookie->ndef)
+               g_free(cookie->ndef->data);
+
+       g_free(cookie->ndef);
+       g_free(cookie);
+
+       return err;
+}
+
+/* ISO functions: This code prepares APDU */
+static int ISO_send_cmd(uint8_t class,
+                       uint8_t instruction,
+                       uint8_t param1,
+                       uint8_t param2,
+                       uint8_t *cmd_data,
+                       uint8_t cmd_data_length,
+                       near_bool_t le,
+                       near_recv cb,
+                       void *in_data)
+{
+       struct type4_cmd *cmd;
+       struct t4_cookie *in_rcv = in_data;
+       uint8_t total_cmd_length;
+       int err;
+
+       DBG("CLA-%02x INS-%02x P1-%02x P2-%02x",
+                       class, instruction, param1, param2);
+
+       if (le == FALSE) {
+               if (cmd_data)
+                       total_cmd_length = APDU_HEADER_LEN + cmd_data_length;
+               else
+                       total_cmd_length = APDU_HEADER_LEN;
+       } else {  /* Extra byte for Le */
+               total_cmd_length = APDU_HEADER_LEN + cmd_data_length + 1;
+       }
+
+       cmd = g_try_malloc0(total_cmd_length);
+       if (cmd == NULL) {
+               DBG("Mem alloc failed");
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       cmd->class      =       class;
+       cmd->instruction =      instruction ;
+       cmd->param1     =       param1 ;
+       cmd->param2     =       param2 ;
+       cmd->data_length =      cmd_data_length;
+
+       if (cmd_data) {
+               memcpy(cmd->data, cmd_data, cmd_data_length);
+               /* The Le byte set to 0x00 defined that any length
+                * of PICC response is allowed */
+               if (le == TRUE)
+                       cmd->data[cmd_data_length] = 0;
+       }
+
+       return near_adapter_send(in_rcv->adapter_idx, (uint8_t *) cmd,
+                                       total_cmd_length, cb, in_rcv,
+                                       t4_cookie_release);
+
+out_err:
+       /* On exit, clean memory */
+       g_free(cmd);
+
+       return err;
+}
+
+/* ISO 7816 command: Select applications or files
+ * p1=0 select by "file id"
+ * P1=4 select by "DF name"
+ *  */
+static int ISO_Select(uint8_t *filename, uint8_t fnamelen, uint8_t P1,
+               near_recv cb, void *cookie)
+{
+       DBG("");
+
+       return ISO_send_cmd(
+                       0x00,           /* CLA */
+                       0xA4,           /* INS: Select file */
+                       P1,             /* P1: select by name */
+                       0x00,           /* P2: First or only occurrence */
+                       filename,       /* cmd_data */
+                       fnamelen,       /* uint8_t cmd_data_length*/
+                       FALSE,
+                       cb,
+                       cookie);
+}
+
+/* ISO 7816 command: Read binary data from files */
+static int ISO_ReadBinary(uint16_t offset, uint8_t readsize,
+                       near_recv cb, void *cookie)
+{
+       DBG("");
+       return ISO_send_cmd(
+                       0x00,           /* CLA */
+                       0xB0,           /* INS: Select file */
+                       (uint8_t) ((offset & 0xFF00) >> 8),
+                       (uint8_t) (offset & 0xFF),
+                       0,              /* no data send */
+                       readsize,       /* bytes to read */
+                       FALSE,
+                       cb,
+                       cookie);
+}
+
+/* ISO 7816 command: Update data */
+static int ISO_Update(uint16_t offset, uint8_t nlen,
+                       uint8_t *data, near_recv cb, void *cookie)
+{
+       DBG("");
+       return ISO_send_cmd(
+                       0x00,                   /* CLA */
+                       0xD6,                   /* INS: Select file */
+                       (uint8_t) ((offset & 0xFF00) >> 8),
+                       (uint8_t) (offset & 0xFF),
+                       data,                   /* length of NDEF data */
+                       nlen,                   /* NLEN + NDEF data */
+                       FALSE,
+                       cb,
+                       cookie);
+}
+
+static int data_read_cb(uint8_t *resp, int length, void *data)
+{
+       struct t4_cookie *cookie = data ;
+       uint8_t *nfc_data;
+       size_t data_length, length_read, current_length;
+       uint16_t remain_bytes;
+
+       DBG("%d", length);
+
+       if (length < 0)
+               return t4_cookie_release(length, cookie);
+
+       if (APDU_STATUS(resp + length - 2) != APDU_OK) {
+               DBG("Fail read_cb SW:x%04x", APDU_STATUS(resp + length - 2));
+
+               return t4_cookie_release(-EIO, cookie);
+       }
+
+       nfc_data = near_tag_get_data(cookie->tag, &data_length);
+
+       /* Remove SW1 / SW2  and NFC header */
+       length_read = length - NFC_HEADER_SIZE - 2 ;
+       length = length_read;
+
+       current_length = cookie->read_data;
+
+       if (current_length + (length_read) > data_length)
+               length_read = data_length - current_length;
+
+       memcpy(nfc_data + current_length, resp + NFC_HEADER_SIZE, length_read);
+       if (current_length + length_read == data_length) {
+               GList *records;
+
+               DBG("Done reading");
+
+               records = near_ndef_parse_msg(nfc_data, data_length);
+               near_tag_add_records(cookie->tag, records, cookie->cb, 0);
+
+               return t4_cookie_release(0, cookie);
+       }
+
+       cookie->read_data += length ;
+       remain_bytes = (data_length - cookie->read_data);
+
+       if (remain_bytes >= cookie->r_apdu_max_size)
+               return ISO_ReadBinary(cookie->read_data + 2,
+                               cookie->r_apdu_max_size, data_read_cb, cookie);
+       else
+               return ISO_ReadBinary(cookie->read_data + 2,
+                               (uint8_t) remain_bytes, data_read_cb, cookie);
+}
+
+static int t4_readbin_NDEF_ID(uint8_t *resp, int length, void *data)
+{
+       struct t4_cookie *cookie = data;
+       struct near_tag *tag;
+       int err;
+
+       DBG("%d", length);
+
+       if (length < 0)
+               return t4_cookie_release(length, cookie);
+
+       if (APDU_STATUS(resp + length - 2) != APDU_OK) {
+               DBG("Fail SW:x%04x", APDU_STATUS(resp + length - 2));
+
+               return t4_cookie_release(-EIO, cookie);
+       }
+
+       /* Add data to the tag */
+       err = near_tag_add_data(cookie->adapter_idx, cookie->target_idx, NULL,
+                               g_ntohs(*((uint16_t *)(resp + NFC_STATUS_BYTE_LEN))));
+       if (err < 0)
+               return t4_cookie_release(err, cookie);
+
+       tag = near_tag_get_tag(cookie->adapter_idx, cookie->target_idx);
+       if (tag == NULL)
+               return t4_cookie_release(-ENOMEM, cookie);
+
+       near_tag_set_max_ndef_size(tag, cookie->max_ndef_size);
+       near_tag_set_c_apdu_max_size(tag, cookie->c_apdu_max_size);
+       near_tag_set_blank(tag, FALSE);
+
+       /* save the tag */
+       cookie->tag = tag;
+
+       /* Set write conditions */
+       if (cookie->write_access == T4_READ_ONLY)
+               near_tag_set_ro(tag, TRUE);
+       else
+               near_tag_set_ro(tag, FALSE);
+
+       /*
+        * TODO: see how we can get the UID value:
+        * near_tag_set_uid(tag, resp + NFC_HEADER_SIZE, 8);
+        */
+
+       /* Read 1st block */
+       return ISO_ReadBinary(2, cookie->r_apdu_max_size - 2,
+                       data_read_cb, cookie);
+}
+
+static int t4_select_NDEF_ID(uint8_t *resp, int length, void *data)
+{
+       struct t4_cookie *cookie = data;
+
+       DBG("%d", length);
+
+       if (length < 0)
+               return t4_cookie_release(length, cookie);
+
+       /* Check for APDU error */
+       if (APDU_STATUS(resp + STATUS_WORD_1) != APDU_OK) {
+               DBG("Fail SW:x%04x", APDU_STATUS(resp + STATUS_WORD_1));
+
+               return t4_cookie_release(-EIO, cookie);
+       }
+
+       /* Read 0x0f bytes, to grab the NDEF msg length */
+       return ISO_ReadBinary(0, LEN_ISO_CC_READ_SIZE,
+                                       t4_readbin_NDEF_ID, cookie);
+}
+
+static int t4_readbin_cc(uint8_t *resp, int length, void *data)
+{
+       struct t4_cookie *cookie = data;
+       struct type4_cc *read_cc;
+
+       DBG("%d", length);
+
+       if (length < 0)
+               return t4_cookie_release(length, cookie);
+
+       /* Check APDU error ( the two last bytes of the resp) */
+       if (APDU_STATUS(resp + length - 2) != APDU_OK) {
+               DBG("Fail SW:x%04x", APDU_STATUS(resp + length - 2));
+
+               return t4_cookie_release(-EIO, cookie);
+       }
+
+       /* -2 for status word and -1 is for NFC first byte... */
+       read_cc = g_try_malloc0(length - 2 - NFC_STATUS_BYTE_LEN);
+       if (read_cc == NULL) {
+               DBG("Mem alloc failed");
+
+               return t4_cookie_release(-ENOMEM, cookie);
+       }
+
+       memcpy(read_cc, &resp[1], length - 2 - NFC_STATUS_BYTE_LEN) ;
+
+       cookie->r_apdu_max_size = g_ntohs(read_cc->max_R_apdu_data_size) -
+                       APDU_HEADER_LEN;
+       cookie->c_apdu_max_size = g_ntohs(read_cc->max_C_apdu_data_size);
+       cookie->max_ndef_size = g_ntohs(read_cc->tlv_fc.max_ndef_size);
+
+       /* TODO 5.1.1: TLV blocks can be zero, one or more... */
+       /* TODO 5.1.2: Must ignore proprietary blocks (x05)... */
+       if (read_cc->tlv_fc.tag != 0x4) {
+               DBG("NDEF File Control tag not found");
+
+               return t4_cookie_release(-EINVAL, cookie);
+       }
+
+       /* save rw conditions */
+       cookie->write_access = read_cc->tlv_fc.write_access;
+
+       return ISO_Select((uint8_t *) &read_cc->tlv_fc.file_id,
+                       LEN_ISO_CC_FILEID, 0, t4_select_NDEF_ID, cookie);
+}
+
+static int t4_select_cc(uint8_t *resp, int length, void *data)
+{
+       struct t4_cookie *cookie = data;
+       int err;
+
+       DBG("%d", length);
+
+       if (length < 0)
+               return t4_cookie_release(length, cookie);
+
+       /* Check for APDU error */
+       if (APDU_STATUS(resp + STATUS_WORD_1) != APDU_OK) {
+
+               DBG(" Found empty tag");
+               /* Add data to the tag */
+               err = near_tag_add_data(cookie->adapter_idx, cookie->target_idx,
+                                       NULL, 1 /* dummy length */);
+               if (err < 0)
+                       return t4_cookie_release(err, cookie);
+
+               cookie->tag = near_tag_get_tag(cookie->adapter_idx,
+                                               cookie->target_idx);
+               if (cookie->tag == NULL)
+                       return t4_cookie_release(-ENOMEM, cookie);
+
+               near_tag_set_blank(cookie->tag, TRUE);
+
+               if (cookie->cb)
+                       cookie->cb(cookie->adapter_idx, cookie->target_idx, 0);
+
+               return t4_cookie_release(0, cookie);
+       }
+
+       return ISO_ReadBinary(0, LEN_ISO_CC_READ_SIZE, t4_readbin_cc, cookie);
+}
+
+static int t4_select_file_by_name_v1(uint8_t *resp, int length, void *data)
+{
+       struct t4_cookie *cookie = data;
+
+       DBG("%d", length);
+
+       if (length < 0)
+               return t4_cookie_release(length, cookie);
+
+       /* Check for APDU error */
+       if (APDU_STATUS(resp + STATUS_WORD_1) != APDU_OK) {
+               DBG("V1 Fail SW:x%04x", APDU_STATUS(resp + STATUS_WORD_1));
+
+               return t4_cookie_release(-EIO, cookie);
+       }
+
+       if (resp[NFC_STATUS] != 0)
+               return t4_cookie_release(-EIO, cookie);
+
+       /* Jump to select phase */
+       return ISO_Select(iso_cc_fileid, LEN_ISO_CC_FILEID, 0,
+                               t4_select_cc, cookie);
+}
+
+static int t4_select_file_by_name_v2(uint8_t *resp, int length, void *data)
+{
+       struct t4_cookie *cookie = data;
+
+       DBG("%d", length);
+
+       if (length < 0)
+               t4_cookie_release(length, cookie);
+
+       /* Check for APDU error - Not found */
+       if (APDU_STATUS(resp + STATUS_WORD_1) == APDU_NOT_FOUND) {
+               DBG("Fallback to V1");
+
+               return ISO_Select(iso_appname_v1, ARRAY_SIZE(iso_appname_v1),
+                                       0x4, t4_select_file_by_name_v1, cookie);
+       }
+
+       if (APDU_STATUS(resp + STATUS_WORD_1) != APDU_OK) {
+               DBG("V2 Fail SW:x%04x", APDU_STATUS(resp + STATUS_WORD_1));
+
+               return t4_cookie_release(-EIO, cookie);
+       }
+
+       if (resp[NFC_STATUS] != 0)
+               return t4_cookie_release(-EIO, cookie);
+
+       /* Jump to select phase */
+       return ISO_Select(iso_cc_fileid, LEN_ISO_CC_FILEID, 0, t4_select_cc,
+                                                                       cookie);
+}
+
+static int nfctype4_read(uint32_t adapter_idx,
+               uint32_t target_idx, near_tag_io_cb cb)
+{
+       struct t4_cookie *cookie;
+
+       DBG("");
+
+       cookie = g_try_malloc0(sizeof(struct t4_cookie));
+       if (cookie == NULL)
+               return -ENOMEM;
+
+       cookie->adapter_idx = adapter_idx;
+       cookie->target_idx = target_idx;
+       cookie->cb = cb;
+       cookie->tag = NULL;
+       cookie->read_data = 0;
+
+       /* Check for V2 type 4 tag */
+       return ISO_Select(iso_appname_v2, ARRAY_SIZE(iso_appname_v2),
+                               0x4, t4_select_file_by_name_v2, cookie);
+}
+
+static int data_write_cb(uint8_t *resp, int length, void *data)
+{
+       struct t4_cookie *cookie = data;
+       int err = 0;
+
+       DBG("%d", length);
+
+       if (length < 0)
+               return t4_cookie_release(length, cookie);
+
+       if (APDU_STATUS(resp + length - 2) != APDU_OK) {
+               near_error("write failed SWx%04x",
+                               APDU_STATUS(resp + length - 2));
+
+               return t4_cookie_release(-EIO, cookie);
+       }
+
+       if (cookie->ndef->offset >= cookie->ndef->length) {
+               DBG("Done writing");
+               near_adapter_disconnect(cookie->adapter_idx);
+
+               if (cookie->cb)
+                       cookie->cb(cookie->adapter_idx, cookie->target_idx, 0);
+
+               return t4_cookie_release(0, cookie);
+       }
+
+       if ((cookie->ndef->length - cookie->ndef->offset) >
+                       cookie->c_apdu_max_size) {
+               err = ISO_Update(cookie->ndef->offset,
+                               cookie->c_apdu_max_size,
+                               cookie->ndef->data + cookie->ndef->offset,
+                               data_write_cb, cookie);
+               cookie->ndef->offset += cookie->c_apdu_max_size;
+       } else {
+               err = ISO_Update(cookie->ndef->offset,
+                               cookie->ndef->length - cookie->ndef->offset,
+                               cookie->ndef->data + cookie->ndef->offset,
+                               data_write_cb, cookie);
+               cookie->ndef->offset = cookie->ndef->length;
+       }
+
+       if (err < 0)
+               return t4_cookie_release(err, cookie);
+
+       return err;
+}
+
+static int data_write(uint32_t adapter_idx, uint32_t target_idx,
+                               struct near_ndef_message *ndef,
+                               struct near_tag *tag, near_tag_io_cb cb)
+{
+       struct t4_cookie *cookie;
+       int err;
+
+       cookie = g_try_malloc0(sizeof(struct t4_cookie));
+
+       if (cookie == NULL) {
+               err = -ENOMEM;
+
+               if (cb != NULL)
+                       cb(adapter_idx, target_idx, err);
+
+               return err;
+       }
+
+       cookie->adapter_idx = adapter_idx;
+       cookie->target_idx = target_idx;
+       cookie->cb = cb;
+       cookie->tag = NULL;
+       cookie->read_data = 0;
+       cookie->max_ndef_size = near_tag_get_max_ndef_size(tag);
+       cookie->c_apdu_max_size = near_tag_get_c_apdu_max_size(tag);
+       cookie->ndef = ndef;
+
+       if (cookie->max_ndef_size < cookie->ndef->length) {
+               near_error("not enough space on tag to write data");
+
+               return t4_cookie_release(-ENOMEM, cookie);
+       }
+
+       if ((cookie->ndef->length - cookie->ndef->offset) >
+                       cookie->c_apdu_max_size) {
+               err = ISO_Update(cookie->ndef->offset,
+                               cookie->c_apdu_max_size,
+                               cookie->ndef->data,
+                               data_write_cb, cookie);
+               cookie->ndef->offset += cookie->c_apdu_max_size;
+       } else {
+               err = ISO_Update(cookie->ndef->offset,
+                               cookie->ndef->length,
+                               cookie->ndef->data,
+                               data_write_cb, cookie);
+               cookie->ndef->offset = cookie->ndef->length;
+       }
+
+       if (err < 0)
+               goto out_err;
+
+       return 0;
+
+out_err:
+       return t4_cookie_release(err, cookie);
+}
+
+static int nfctype4_write(uint32_t adapter_idx, uint32_t target_idx,
+                       struct near_ndef_message *ndef, near_tag_io_cb cb)
+{
+       struct near_tag *tag;
+       int err;
+
+       DBG("");
+
+       if (ndef == NULL || cb == NULL) {
+               err = -EINVAL;
+               goto out_err;
+       }
+
+       tag = near_tag_get_tag(adapter_idx, target_idx);
+       if (tag == NULL) {
+               err = -EINVAL;
+               goto out_err;
+       }
+
+       err = data_write(adapter_idx, target_idx, ndef, tag, cb);
+
+out_err:
+       if (cb != NULL)
+               cb(adapter_idx, target_idx, err);
+
+       return err;
+}
+
+static int check_presence(uint8_t *resp, int length, void *data)
+{
+       struct t4_cookie *cookie = data;
+       int err = 0;
+
+       DBG("%d", length);
+
+       if (length < 0)
+               err = -EIO;
+
+       if (cookie->cb)
+               cookie->cb(cookie->adapter_idx,
+                               cookie->target_idx, err);
+
+       return t4_cookie_release(err, cookie);
+}
+
+static int nfctype4_check_presence(uint32_t adapter_idx,
+               uint32_t target_idx, near_tag_io_cb cb)
+{
+       struct t4_cookie *cookie;
+
+       DBG("");
+
+       cookie = g_try_malloc0(sizeof(struct t4_cookie));
+       if (cookie == NULL)
+               return -ENOMEM;
+
+       cookie->adapter_idx = adapter_idx;
+       cookie->target_idx = target_idx;
+       cookie->cb = cb;
+       cookie->tag = NULL;
+       cookie->read_data = 0;
+
+       /* Check for V2 type 4 tag */
+       return ISO_Select(iso_appname_v2, ARRAY_SIZE(iso_appname_v2),
+                               0x4, check_presence, cookie);
+}
+
+static int select_ndef_file(uint8_t *resp, int length, void *data)
+{
+       struct t4_cookie *cookie = data;
+
+       DBG("%d", length);
+
+       if (length < 0)
+               return t4_cookie_release(length, cookie);
+
+       if (APDU_STATUS(resp + STATUS_WORD_1) != APDU_OK) {
+               near_error("select ndef file resp failed %02X",
+                                       resp[length - 1]);
+
+               return t4_cookie_release(-EIO, cookie);
+       }
+
+       DBG("ndef file selected");
+
+       if (cookie->cb)
+               cookie->cb(cookie->adapter_idx, cookie->target_idx, 0);
+
+       return t4_cookie_release(0, cookie);
+}
+
+static int read_cc_file(uint8_t *resp, int length, void *data)
+{
+       struct t4_cookie *cookie = data;
+       struct near_tag *tag;
+       struct type4_cc *read_cc = NULL;
+       int err = 0;
+
+       DBG("%d", length);
+
+       if (length < 0) {
+               err = length;
+               goto out_err;
+       }
+
+       /* Check APDU error ( the two last bytes of the resp) */
+       if (APDU_STATUS(resp + length - 2) != APDU_OK) {
+               near_error("read cc failed SWx%04x",
+                                       APDU_STATUS(resp + length - 2));
+               err = -EIO;
+               goto out_err;
+       }
+
+       /* -2 for status word and -1 is for NFC first byte... */
+       read_cc = g_try_malloc0(length - 2 - NFC_STATUS_BYTE_LEN);
+       if (read_cc == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       memcpy(read_cc, &resp[1], length - 2 - NFC_STATUS_BYTE_LEN) ;
+       cookie->c_apdu_max_size = g_ntohs(read_cc->max_C_apdu_data_size);
+       cookie->max_ndef_size = g_ntohs(read_cc->tlv_fc.max_ndef_size);
+
+       tag = near_tag_get_tag(cookie->adapter_idx, cookie->target_idx);
+       if (tag == NULL) {
+               err = -EINVAL;
+               goto out_err;
+       }
+
+       near_tag_set_max_ndef_size(tag, cookie->memory_size);
+       near_tag_set_c_apdu_max_size(tag, cookie->c_apdu_max_size);
+
+       if (read_cc->tlv_fc.tag  != 0x4) {
+               near_error("NDEF File not found") ;
+               err = -EINVAL ;
+               goto out_err;
+       }
+
+       err = ISO_Select((uint8_t *)&read_cc->tlv_fc.file_id,
+                       LEN_ISO_CC_FILEID, 0, select_ndef_file, cookie);
+       if (err < 0) {
+               near_error("select ndef file req failed %d", err);
+               goto out_err;
+       }
+
+       g_free(read_cc);
+       return 0;
+
+out_err:
+       g_free(read_cc);
+       return t4_cookie_release(err, cookie);
+}
+
+static int select_cc_file(uint8_t *resp, int length, void *data)
+{
+       int err = 0;
+       struct t4_cookie *cookie = data;
+
+       if (length < 0) {
+               near_error("CC file select resp failed %d", length);
+
+               return t4_cookie_release(length, cookie);
+       }
+
+       if (APDU_STATUS(resp + STATUS_WORD_1) != APDU_OK) {
+               near_error("CC file select response %02X",
+                                               resp[length - 1]);
+
+               return t4_cookie_release(-EIO, cookie);
+       }
+
+       err = ISO_ReadBinary(0, LEN_ISO_CC_READ_SIZE, read_cc_file, cookie);
+       if (err < 0) {
+               near_error("read cc file req failed %d", err);
+               goto out_err;
+       }
+
+       return 0;
+
+out_err:
+       return t4_cookie_release(err, cookie);
+}
+
+static int select_iso_appname_v2(uint8_t *resp, int length, void *data)
+{
+       int err = 0;
+       struct t4_cookie *cookie = data;
+
+       if (length < 0) {
+               near_error("iso app select resp failed %d", length);
+
+               return t4_cookie_release(length, cookie);
+       }
+
+       if (resp[NFC_STATUS] != 0x00) {
+               near_error("iso app select response %02X",
+                                       resp[length - 1]);
+
+               return t4_cookie_release(-EIO, cookie);
+       }
+
+       err = ISO_Select(iso_cc_fileid, LEN_ISO_CC_FILEID, 0,
+                       select_cc_file, cookie);
+       if (err < 0) {
+               near_error("select cc req failed %d", err);
+               goto out_err;
+       }
+
+       return 0;
+
+out_err:
+       return t4_cookie_release(err, cookie);
+}
+
+static int format_resp(uint8_t *resp, int length, void *data)
+{
+       int err = 0;
+       struct t4_cookie *cookie = data;
+       struct near_tag *tag;
+
+       DBG("");
+
+       if (length < 0) {
+               near_error("write data to ndef file resp failed %d", length);
+
+               return t4_cookie_release(length, cookie);
+       }
+
+       if (APDU_STATUS(resp + 1) != PICC_LEVEL_APDU_OK) {
+               near_error("wrtie data to ndef file response %02X",
+                                               resp[length - 1]);
+
+               return t4_cookie_release(-EIO, cookie);
+       }
+
+       tag = near_tag_get_tag(cookie->adapter_idx, cookie->target_idx);
+       if (tag == NULL)
+               return t4_cookie_release(-EINVAL, cookie);
+
+       DBG("Formatting is done");
+       near_tag_set_blank(tag, FALSE);
+
+       /*
+        * 1) Till now all commands which are used for formatting are
+        *    at mifare desfire level. Now select iso appname_v2,
+        *    cc file and ndef file with ISO 7816-4 commands.
+        * 2) Selecting ndef file means making sure that read write
+        *    operations will perform on NDEF file.
+        */
+       err = ISO_Select(iso_appname_v2, ARRAY_SIZE(iso_appname_v2),
+                       0x4, select_iso_appname_v2, cookie);
+       if (err < 0) {
+               near_error("iso_select appnamev2 req failed %d", err);
+               goto out_err;
+       }
+
+       return err;
+
+out_err:
+       return t4_cookie_release(err, cookie);
+}
+
+static int write_data_to_ndef_file(uint8_t *resp, int length, void *data)
+{
+       int err = 0;
+       struct t4_cookie *cookie = data;
+       uint8_t *cmd_data = NULL;
+       uint8_t cmd_data_length;
+       uint8_t ndef_file_offset[] = {0x00, 0x00, 0x00};
+       uint8_t empty_ndef_file_len[] = {0x02, 0x00, 0x00}; /* 000002h */
+       uint8_t ndef_nlen[] = {0x00, 0x00}; /* 0000h */
+
+       DBG("");
+
+       if (length < 0) {
+               near_error("create ndef file resp failed %d", length);
+               err = length;
+               goto out_err;
+       }
+
+       if (APDU_STATUS(resp + 1) != PICC_LEVEL_APDU_OK) {
+               near_error("create ndef file response %02X",
+                                               resp[length - 1]);
+               err = -EIO;
+               goto out_err;
+       }
+
+       /* Step8 : Write data to NDEF file ( no NDEF message) */
+       cmd_data_length = 1 /* File num */
+                               + ARRAY_SIZE(ndef_file_offset)
+                               + ARRAY_SIZE(empty_ndef_file_len)
+                               + ARRAY_SIZE(ndef_nlen);
+
+       cmd_data = g_try_malloc0(cmd_data_length);
+       if (cmd_data == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       cmd_data[0] = DESFIRE_NDEF_FILE_NUM;
+       memcpy(cmd_data + 1, ndef_file_offset, ARRAY_SIZE(ndef_file_offset));
+       memcpy(cmd_data + 4, empty_ndef_file_len,
+                       ARRAY_SIZE(empty_ndef_file_len));
+       memcpy(cmd_data + 7, ndef_nlen, ARRAY_SIZE(ndef_nlen));
+
+       err = ISO_send_cmd(PICC_CLASS, WRITE_DATA_TO_FILE,
+                               0x00, 0x00, cmd_data, cmd_data_length,
+                               TRUE, format_resp, cookie);
+       if (err < 0) {
+               near_error("wrtie data to ndef file req failed %d", err);
+               goto out_err;
+       }
+
+       g_free(cmd_data);
+       return 0;
+
+out_err:
+       g_free(cmd_data);
+       return t4_cookie_release(err, cookie);
+}
+
+static int create_ndef_file(uint8_t *resp, int length, void *data)
+{
+       int err = 0;
+       struct t4_cookie *cookie = data;
+       struct desfire_std_file *ndef = NULL;
+       uint8_t iso_ndef_file_id[] = {0x04, 0xE1}; /* E104h */
+       uint8_t ndef_file_access_rights[] = {0xE0, 0xEE}; /* EEE0h */
+
+       DBG("");
+
+       if (length < 0) {
+               near_error("write data to cc file resp failed %d", length);
+               err = length;
+               goto out_err;
+       }
+
+       if (APDU_STATUS(resp + 1) != PICC_LEVEL_APDU_OK) {
+               near_error("write data to cc file response %02X",
+                                               resp[length - 1]);
+               err = -EIO;
+               goto out_err;
+       }
+
+       ndef = g_try_malloc0(sizeof(struct desfire_std_file));
+       if (ndef == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       ndef->file_num = DESFIRE_NDEF_FILE_NUM;
+       memcpy(ndef->file_id, iso_ndef_file_id, ARRAY_SIZE(iso_ndef_file_id));
+       ndef->comm_set = DESFIRE_COMMSET;
+       memcpy(ndef->access_rights, ndef_file_access_rights,
+                               ARRAY_SIZE(ndef_file_access_rights));
+       ndef->size[0] = 0;
+       ndef->size[1] = (uint8_t) (cookie->memory_size >> 8);
+       ndef->size[2] = (uint8_t) cookie->memory_size;
+
+       err = ISO_send_cmd(PICC_CLASS, CREATE_STD_DATA_FILE,
+                               0x00, 0x00, (uint8_t *)ndef,
+                               sizeof(struct desfire_std_file),
+                               TRUE, write_data_to_ndef_file, cookie);
+       if (err < 0) {
+               near_error("create ndef file req failed %d", err);
+               goto out_err;
+       }
+
+       g_free(ndef);
+       return 0;
+
+out_err:
+       g_free(ndef);
+       return t4_cookie_release(err, cookie);
+}
+
+static int write_data_to_cc_file(uint8_t *resp, int length, void *data)
+{
+       int err = 0;
+       struct t4_cookie *cookie = data;
+       struct desfire_cc_file *cc = NULL;
+       uint8_t cc_file_offset[] = {0x00, 0x00, 0x00};
+       uint8_t cc_file_max_len[] = {0x0F, 0x00, 0x00}; /* 00000Fh */
+       uint8_t cc_len[] = {0x00, 0x0F}; /* 000Fh*/
+       uint8_t mle_r_apdu[] = {0x00, 0x3B}; /* 003Bh */
+       uint8_t mlc_c_apdu[] = {0x00, 0x34}; /* 0034h */
+       /* T: 04, L: 06: V: E104h (NDEF ISO FID = E104h)*/
+       uint8_t ndef_tlv[] = {0x04, 0x06, 0xE1, 0x04};
+
+
+       DBG("");
+
+       if (length < 0) {
+               near_error("create cc file resp failed %d", length);
+               err = length;
+               goto out_err;
+       }
+
+       if (APDU_STATUS(resp + 1) != PICC_LEVEL_APDU_OK) {
+               near_error("create cc file response %02X",
+                                               resp[length - 1]);
+               err = -EIO;
+               goto out_err;
+       }
+
+       cc = g_try_malloc0(sizeof(struct desfire_cc_file));
+       if (cc == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       cc->file_num = DESFIRE_CC_FILE_NUM;
+       memcpy(cc->offset, cc_file_offset, ARRAY_SIZE(cc_file_offset));
+       memcpy(cc->max_len, cc_file_max_len, ARRAY_SIZE(cc_file_max_len));
+       memcpy(cc->cc_len, cc_len, ARRAY_SIZE(cc_len));
+       cc->version = MAPPING_VERSION;
+       memcpy(cc->mle, mle_r_apdu, ARRAY_SIZE(mle_r_apdu));
+       memcpy(cc->mlc, mlc_c_apdu, ARRAY_SIZE(mlc_c_apdu));
+       memcpy(cc->ndef_tlv, ndef_tlv, ARRAY_SIZE(ndef_tlv));
+       cc->ndef_size[0] = (uint8_t) (cookie->memory_size >> 8);
+       cc->ndef_size[1] = (uint8_t) cookie->memory_size;
+       cc->read_access = FREE_READ_ACCESS;
+       cc->write_access = FREE_WRITE_ACCESS;
+
+       err = ISO_send_cmd(PICC_CLASS, WRITE_DATA_TO_FILE,
+                               0x00, 0x00, (uint8_t *)cc,
+                               sizeof(struct desfire_cc_file),
+                               TRUE, create_ndef_file, cookie);
+       if (err < 0) {
+               near_error("write data to cc file req failed %d", err);
+               goto out_err;
+       }
+
+       g_free(cc);
+       return 0;
+
+out_err:
+       g_free(cc);
+       return t4_cookie_release(err, cookie);
+}
+
+static int create_cc_file(uint8_t *resp, int length, void *data)
+{
+       int err = 0;
+       struct t4_cookie *cookie = data;
+       struct desfire_std_file *cc = NULL;
+       uint8_t iso_cc_file_id[] = {0x03, 0xe1}; /* E103h */
+       uint8_t cc_file_access_rights[] = {0xE0, 0xEE}; /* EEE0h */
+       uint8_t cc_file_max_len[] = {0x0F, 0x00, 0x00}; /* 00000Fh */
+
+       DBG("");
+
+       if (length < 0) {
+               near_error("select application1 resp failed %d", length);
+               err = length;
+               goto out_err;
+       }
+
+       if (APDU_STATUS(resp + 1) != PICC_LEVEL_APDU_OK) {
+               near_error("select application1 response %02X",
+                                               resp[length - 1]);
+               err = -EIO;
+               goto out_err;
+       }
+
+       cc = g_try_malloc0(sizeof(struct desfire_std_file));
+       if (cc == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       cc->file_num = DESFIRE_CC_FILE_NUM;
+       memcpy(cc->file_id, iso_cc_file_id, ARRAY_SIZE(iso_cc_file_id));
+       cc->comm_set = DESFIRE_COMMSET;
+       memcpy(cc->access_rights, cc_file_access_rights,
+                               ARRAY_SIZE(cc_file_access_rights));
+       memcpy(cc->size, cc_file_max_len, ARRAY_SIZE(cc_file_max_len));
+
+       err = ISO_send_cmd(PICC_CLASS,
+                               CREATE_STD_DATA_FILE,
+                               0x00, 0x00, (uint8_t *)cc,
+                               sizeof(struct desfire_std_file),
+                               TRUE, write_data_to_cc_file, cookie);
+       if (err < 0) {
+               near_error("create cc file req failed %d", err);
+               goto out_err;
+       }
+
+       g_free(cc);
+       return 0;
+
+out_err:
+       g_free(cc);
+       return t4_cookie_release(err, cookie);
+}
+
+static int select_application_1(uint8_t *resp, int length, void *data)
+{
+       int err = 0;
+       struct t4_cookie *cookie = data;
+       uint8_t *cmd_data = NULL;
+       uint8_t cmd_data_length;
+       uint8_t desfire_aid_1[] = {0x01, 0x00, 0x00}; /* 000001h */
+
+       DBG("");
+
+       if (length < 0) {
+               near_error("create application resp failed %d", length);
+               err = length;
+               goto out_err;
+       }
+
+       if (APDU_STATUS(resp + 1) != PICC_LEVEL_APDU_OK) {
+               near_error("create application response %02X",
+                                               resp[length - 1]);
+               err = -EIO;
+               goto out_err;
+       }
+
+       /* Step4 : Select application (which is created just now) */
+       cmd_data_length = ARRAY_SIZE(desfire_aid_1);
+       cmd_data = g_try_malloc0(cmd_data_length);
+       if (cmd_data == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       memcpy(cmd_data, desfire_aid_1, cmd_data_length);
+       err = ISO_send_cmd(PICC_CLASS, SELECT_APPLICATION,
+                               0x00, 0x00, cmd_data, cmd_data_length,
+                               TRUE, create_cc_file, cookie);
+       if (err < 0) {
+               near_error("select application1 req failed %d", err);
+               goto out_err;
+       }
+
+       g_free(cmd_data);
+       return 0;
+
+out_err:
+       g_free(cmd_data);
+       return t4_cookie_release(err, cookie);
+}
+
+static int create_application(uint8_t *resp, int length, void *data)
+{
+       int err = 0;
+       struct t4_cookie *cookie = data;
+       uint8_t desfire_aid_1[] = {0x01, 0x00, 0x00}; /* 000001h */
+       uint8_t desfire_file_id[] = {0x10, 0xE1}; /* E110h */
+       struct desfire_app *app = NULL;
+
+       DBG("");
+
+       if (length < 0) {
+               near_error("select application resp failed %d", length);
+               err = length;
+               goto out_err;
+       }
+
+       if (APDU_STATUS(resp + 1) != PICC_LEVEL_APDU_OK) {
+               near_error("select application response %02X",
+                                               resp[length - 1]);
+               err = -EIO;
+               goto out_err;
+       }
+
+       app = g_try_malloc0(sizeof(struct desfire_app));
+       if (app == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       memcpy(app->aid, desfire_aid_1, ARRAY_SIZE(desfire_aid_1));
+       app->key_settings = DESFIRE_KEY_SETTINGS;
+       app->number_of_keys = DESFIRE_NUM_OF_KEYS;
+       memcpy(app->file_id, desfire_file_id, ARRAY_SIZE(desfire_file_id));
+       memcpy(app->iso_appname, iso_appname_v2, ARRAY_SIZE(iso_appname_v2));
+
+       /* Step3 : Create Application */
+       err = ISO_send_cmd(PICC_CLASS, CREATE_APPLICATION,
+                               0x00, 0x00, (uint8_t *)app,
+                               sizeof(struct desfire_app),
+                               TRUE, select_application_1, cookie);
+       if (err < 0) {
+               near_error("create application req failed %d", err);
+               goto out_err;
+       }
+
+       g_free(app);
+       return 0;
+
+out_err:
+       g_free(app);
+       return t4_cookie_release(err, cookie);
+}
+
+static int select_application(uint8_t *resp, int length, void *data)
+{
+       int err;
+       struct t4_cookie *cookie = data;
+       uint8_t *cmd_data = NULL;
+       uint8_t cmd_data_length;
+       uint8_t desfire_aid[] = {0x00, 0x00, 0x00}; /* 000000h */
+
+       DBG("");
+
+       if (length < 0) {
+               near_error("get version3 resp failed %d", length);
+               err = length;
+               goto out_err;
+       }
+
+       if (resp[length - 1] != 0x00) {
+               near_error("get version3 response %02X",
+                                               resp[length - 1]);
+               err = -EIO;
+               goto out_err;
+       }
+
+       /* AID : 000000h */
+       cmd_data_length = ARRAY_SIZE(desfire_aid);
+       cmd_data = g_try_malloc0(cmd_data_length);
+       if (cmd_data == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       memcpy(cmd_data, desfire_aid, cmd_data_length);
+       /* Step2 : Select Application */
+       err = ISO_send_cmd(PICC_CLASS,
+                               SELECT_APPLICATION,
+                               0x00, 0x00, cmd_data, cmd_data_length,
+                               TRUE, create_application, cookie);
+       if (err < 0) {
+               near_error("select application req failed %d", err);
+               goto out_err;
+       }
+
+       g_free(cmd_data);
+       return 0;
+
+out_err:
+       g_free(cmd_data);
+       return t4_cookie_release(err, cookie);
+}
+
+static int get_version_frame3(uint8_t *resp, int length, void *data)
+{
+       int err;
+       struct t4_cookie *cookie = data;
+
+       DBG("");
+
+       if (length < 0) {
+               near_error("get version2 resp failed %d", length);
+
+               return t4_cookie_release(length, cookie);
+       }
+
+       if (resp[4] == 0x01 /* Major Version */
+               && resp[length - 1] == GET_VERSION_FRAME_RESPONSE_BYTE) {
+
+               err = ISO_send_cmd(PICC_CLASS,
+                                       GET_VERSION_FRAME_RESPONSE_BYTE,
+                                       0x00, 0x00, NULL, 0, FALSE,
+                                       select_application, cookie);
+               if (err < 0) {
+                       near_error("get version3 req failed %d", err);
+
+                       return t4_cookie_release(err, cookie);
+               }
+       } else {
+               near_error("get version2 response %02X", resp[length - 1]);
+
+               return t4_cookie_release(-EIO, cookie);
+       }
+
+       return 0;
+}
+
+static int get_version_frame2(uint8_t *resp, int length, void *data)
+{
+       int err;
+       struct t4_cookie *cookie = data;
+
+       DBG("");
+
+       if (length < 0) {
+               near_error(" get version resp failed %d", length);
+
+               return t4_cookie_release(length, cookie);
+       }
+
+       if (resp[4] == 0x01 /* Major Version */
+               && resp[length - 1] == GET_VERSION_FRAME_RESPONSE_BYTE) {
+
+               /*
+                * When N is the GET_VERSION response 6th byte,
+                * the DESFire tag memory size is 2 ^ (N /2).
+                */
+               cookie->memory_size = (1 << (resp[6] / 2));
+               err = ISO_send_cmd(PICC_CLASS,
+                                       GET_VERSION_FRAME_RESPONSE_BYTE,
+                                       0x00, 0x00, NULL, 0, FALSE,
+                                       get_version_frame3, cookie);
+               if (err < 0) {
+                       near_error("get version2 req failed %d", err);
+
+                       return t4_cookie_release(err, cookie);
+               }
+       } else {
+               near_error("get version response %02X", resp[length - 1]);
+
+               return t4_cookie_release(-EIO, cookie);
+       }
+
+       return 0;
+}
+
+/* Steps to format Type 4 (MIFARE DESFire EV1) tag as per AN1104.pdf from nxp.
+ * 1) Get version to determine memory size of tag
+ * 2) Select applciation with AID equal to 000000h (PICC level)
+ * 3) Create application with AID equal to 000001h
+ * 4) Select application (Select previously created application in step3)
+ * 5) Create std data file with File number equal to 01h (CC file), ISOFileID
+ *    equal to E103h, ComSet equal to 00h, AccesRights to EEEEh, FileSize bigger
+ *    equal to 00000Fh
+ * 6) Write data to CC file with CCLEN equal to 000Fh, Mapping version equal to
+ *    20h, MLe equal to 003Bh, MLc equal to 0034h, and NDEF File control TLV
+ *    equal to: T=04h, L=06h, V=E1 04 (NDEF ISO FID = E104h), 08 00 (NDEF File
+ *    size = 2048 Bytes) 00 (free read access) 00 (free write access)
+ * 7) Create std data file with File number equal to 02h (NDEF File DESFireFId),
+ *    ISO FileID equal to E104h, ComSet equal to 00h, ComSet equal to 00h,
+ *    AccessRights equal to EEE0h, FileSize equal to 000800h (2048 bytes)
+ * 8) Write data to write content of the NDEF File with NLEN equal to 0000h, and
+ *    no NDEF messsage.
+ * 9) Now Formatting is done, then select ISO appname2, select CC file and read.
+ * 10) Select NDEF file (by doing last two steps means, making sure that read
+ *    write operations perform on NDEF file).
+ * */
+
+static int nfctype4_format(uint32_t adapter_idx, uint32_t target_idx,
+                                               near_tag_io_cb cb)
+{
+       int err;
+       struct t4_cookie *cookie;
+
+       DBG("");
+
+       cookie = g_try_malloc0(sizeof(struct t4_cookie));
+       if (cookie == NULL)
+               return -ENOMEM;
+
+       cookie->adapter_idx = adapter_idx;
+       cookie->target_idx = target_idx;
+       cookie->cb = cb;
+
+       /* Step1 : Get Version */
+       err = ISO_send_cmd(PICC_CLASS, GET_VERSION,
+                               0x00, 0x00, NULL, 0, FALSE,
+                               get_version_frame2, cookie);
+
+       if (err < 0) {
+               near_error("get version req failed %d", err);
+               g_free(cookie);
+       }
+
+       return err;
+}
+
+static struct near_tag_driver type4_driver = {
+       .type           = NFC_PROTO_ISO14443,
+       .priority       = NEAR_TAG_PRIORITY_DEFAULT,
+       .read           = nfctype4_read,
+       .write          = nfctype4_write,
+       .check_presence = nfctype4_check_presence,
+       .format         = nfctype4_format,
+};
+
+static int nfctype4_init(void)
+{
+       DBG("");
+
+       return near_tag_driver_register(&type4_driver);
+}
+
+static void nfctype4_exit(void)
+{
+       DBG("");
+
+       near_tag_driver_unregister(&type4_driver);
+}
+
+NEAR_PLUGIN_DEFINE(nfctype4, "NFC Forum Type 4 tags support", VERSION,
+               NEAR_PLUGIN_PRIORITY_HIGH, nfctype4_init, nfctype4_exit)
diff --git a/plugins/npp.c b/plugins/npp.c
new file mode 100644 (file)
index 0000000..90ebc1c
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <linux/socket.h>
+
+#include <near/nfc.h>
+#include <near/plugin.h>
+#include <near/log.h>
+#include <near/types.h>
+#include <near/adapter.h>
+#include <near/device.h>
+#include <near/ndef.h>
+#include <near/tlv.h>
+
+#include "p2p.h"
+
+struct p2p_npp_ndef_entry {
+       uint8_t action;
+       uint32_t ndef_length;
+       uint8_t ndef[];
+} __attribute__((packed));
+
+struct p2p_npp_frame {
+       uint8_t version;
+       uint32_t n_ndef;
+       struct p2p_npp_ndef_entry ndefs[];
+} __attribute__((packed));
+
+static near_bool_t npp_read(int client_fd,
+                       uint32_t adapter_idx, uint32_t target_idx,
+                       near_tag_io_cb cb)
+{
+       struct near_device *device;
+       struct p2p_npp_frame frame;
+       struct p2p_npp_ndef_entry entry;
+       int bytes_recv, n_ndef, i, ndef_length, total_ndef_length, err;
+       uint8_t *ndefs, *current_ndef;
+       GList *records;
+
+       ndefs = NULL;
+       total_ndef_length = 0;
+       err = 0;
+
+       bytes_recv = recv(client_fd, &frame, sizeof(frame), 0);
+       if (bytes_recv < 0) {
+               near_error("Could not read NPP frame %d", bytes_recv);
+               return bytes_recv;
+       }
+
+       n_ndef = GINT_FROM_BE(frame.n_ndef);
+
+       DBG("version %d %d NDEFs", frame.version, n_ndef);
+
+       for (i = 0; i < n_ndef; i++) {
+               bytes_recv = recv(client_fd, &entry, sizeof(entry), 0);
+               if (bytes_recv < 0) {
+                       near_error("Could not read NPP NDEF entry %d",
+                                                               bytes_recv);
+                       err = bytes_recv;
+                       break;
+               }
+
+               ndef_length = GINT_FROM_BE(entry.ndef_length);
+               total_ndef_length += ndef_length + TLV_SIZE;
+               DBG("NDEF %d length %d", i, ndef_length);
+
+               ndefs = g_try_realloc(ndefs, total_ndef_length);
+               if (ndefs == NULL) {
+                       near_error("Could not allocate NDEF buffer %d",
+                                                               bytes_recv);
+                       err = -ENOMEM;
+                       break;
+               }
+
+               current_ndef = ndefs + total_ndef_length
+                                       - (ndef_length + TLV_SIZE);
+               current_ndef[0] = TLV_NDEF;
+               current_ndef[1] = ndef_length;
+
+               bytes_recv = recv(client_fd, current_ndef + TLV_SIZE,
+                                       ndef_length, 0);
+               if (bytes_recv < 0) {
+                       near_error("Could not read NDEF entry %d",
+                                                       bytes_recv);
+                       err = bytes_recv;
+                       break;
+               }
+       }
+
+       if (total_ndef_length == 0)
+               return err;
+
+       DBG("Total NDEF length %d", total_ndef_length);
+
+       err = near_tag_add_data(adapter_idx, target_idx,
+                                       ndefs, total_ndef_length);
+       if (err < 0)
+               return FALSE;
+
+       device = near_device_get_device(adapter_idx, target_idx);
+       if (device == NULL) {
+               g_free(ndefs);
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < total_ndef_length; i++)
+               DBG("NDEF[%d] 0x%x", i, ndefs[i]);
+
+       records = near_tlv_parse(ndefs, total_ndef_length);
+       near_device_add_records(device, records, cb, 0);
+
+       g_free(ndefs);
+
+       return FALSE;
+}
+
+struct near_p2p_driver npp_driver = {
+       .name = "NPP",
+       .service_name = NEAR_DEVICE_SN_NPP,
+       .fallback_service_name = NULL,
+       .read = npp_read,
+};
+
+int npp_init(void)
+{
+       return near_p2p_register(&npp_driver);
+}
+
+void npp_exit(void)
+{
+       near_p2p_unregister(&npp_driver);
+}
diff --git a/plugins/p2p.c b/plugins/p2p.c
new file mode 100644 (file)
index 0000000..be5c6e1
--- /dev/null
@@ -0,0 +1,396 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <linux/socket.h>
+
+#include <near/nfc.h>
+#include <near/plugin.h>
+#include <near/log.h>
+#include <near/types.h>
+#include <near/tag.h>
+#include <near/device.h>
+#include <near/adapter.h>
+#include <near/tlv.h>
+#include <near/ndef.h>
+
+#include "p2p.h"
+
+static GSList *driver_list = NULL;
+static GList *server_list = NULL;
+
+struct p2p_data {
+       struct near_p2p_driver *driver;
+       uint32_t adapter_idx;
+       uint32_t target_idx;
+       near_device_io_cb cb;
+       int fd;
+       guint watch;
+
+       GList *client_list;
+};
+
+static gboolean p2p_client_event(GIOChannel *channel, GIOCondition condition,
+                                                       gpointer user_data)
+{
+       struct p2p_data *client_data = user_data;
+       near_bool_t more;
+
+       DBG("condition 0x%x", condition);
+
+       if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               int err;
+
+               if (client_data->watch > 0)
+                       g_source_remove(client_data->watch);
+               client_data->watch = 0;
+
+               if (condition & (G_IO_NVAL | G_IO_ERR))
+                       err = -EIO;
+               else
+                       err = 0;
+
+               if (client_data->driver->close != NULL)
+                       client_data->driver->close(client_data->fd, err);
+
+               near_error("%s client channel closed",
+                                       client_data->driver->name);
+
+               return FALSE;
+       }
+
+       more = client_data->driver->read(client_data->fd,
+                                               client_data->adapter_idx,
+                                               client_data->target_idx,
+                                               client_data->cb);
+
+       return more;
+}
+
+static void free_client_data(gpointer data)
+{
+       struct p2p_data *client_data;
+
+       DBG("");
+
+       client_data = (struct p2p_data *) data;
+
+       if (client_data->driver->close != NULL)
+               client_data->driver->close(client_data->fd, 0);
+
+       if (client_data->watch > 0)
+               g_source_remove(client_data->watch);
+
+       g_free(client_data);
+}
+
+static void free_server_data(gpointer data)
+{
+       struct p2p_data *server_data;
+
+       DBG("");
+
+       server_data = (struct p2p_data *)data;
+
+       if (server_data->watch > 0)
+               g_source_remove(server_data->watch);
+       server_data->watch = 0;
+       g_list_free_full(server_data->client_list, free_client_data);
+       server_data->client_list = NULL;
+
+       DBG("Closing server socket");
+
+       close(server_data->fd);
+
+       g_free(server_data);
+}
+
+static gboolean p2p_listener_event(GIOChannel *channel, GIOCondition condition,
+                                                       gpointer user_data)
+{
+       struct sockaddr_nfc_llcp client_addr;
+       socklen_t client_addr_len;
+       int server_fd, client_fd;
+       struct p2p_data *client_data, *server_data = user_data;
+       GIOChannel *client_channel;
+       struct near_p2p_driver *driver = server_data->driver;
+
+       DBG("condition 0x%x", condition);
+
+       server_fd = g_io_channel_unix_get_fd(channel);
+
+       if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               if (server_data->watch > 0)
+                       g_source_remove(server_data->watch);
+               server_data->watch = 0;
+
+               g_list_free_full(server_data->client_list, free_client_data);
+               server_data->client_list = NULL;
+
+               near_error("Error with %s server channel", driver->name);
+
+               return FALSE;
+       }
+
+       client_addr_len = sizeof(client_addr);
+       client_fd = accept(server_fd, (struct sockaddr *) &client_addr,
+                                                       &client_addr_len);
+       if (client_fd < 0) {
+               near_error("accept failed %d", client_fd);
+
+               return FALSE;
+       }
+
+       DBG("client dsap %d ssap %d",
+               client_addr.dsap, client_addr.ssap);
+       DBG("target idx %d", client_addr.target_idx);
+
+       client_data = g_try_malloc0(sizeof(struct p2p_data));
+       if (client_data == NULL) {
+               close(client_fd);
+               return FALSE;
+       }
+
+       client_data->driver = server_data->driver;
+       client_data->adapter_idx = server_data->adapter_idx;
+       client_data->target_idx = client_addr.target_idx;
+       client_data->fd = client_fd;
+       client_data->cb = server_data->cb;
+
+       client_channel = g_io_channel_unix_new(client_fd);
+       g_io_channel_set_close_on_unref(client_channel, TRUE);
+
+       client_data->watch = g_io_add_watch(client_channel,
+                               G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
+                               p2p_client_event,
+                               client_data);
+
+       server_data->client_list = g_list_append(server_data->client_list, client_data);
+
+       return TRUE;
+}
+
+static int p2p_bind(struct near_p2p_driver *driver, uint32_t adapter_idx,
+                                                       near_device_io_cb cb)
+{
+       int err, fd;
+       struct sockaddr_nfc_llcp addr;
+       GIOChannel *channel;
+       struct p2p_data *server_data;
+
+       DBG("Binding %s", driver->name);
+
+       fd = socket(AF_NFC, SOCK_STREAM, NFC_SOCKPROTO_LLCP);
+       if (fd < 0)
+               return -errno;
+
+       memset(&addr, 0, sizeof(struct sockaddr_nfc_llcp));
+       addr.sa_family = AF_NFC;
+       addr.dev_idx = adapter_idx;
+       addr.nfc_protocol = NFC_PROTO_NFC_DEP;
+       addr.service_name_len = strlen(driver->service_name);
+       strcpy(addr.service_name, driver->service_name);
+
+       err = bind(fd, (struct sockaddr *) &addr,
+                       sizeof(struct sockaddr_nfc_llcp));
+       if (err < 0) {
+               if (errno == EADDRINUSE) {
+                       DBG("%s is already bound", driver->name);
+                       close(fd);
+                       return 0;
+               }
+
+               near_error("%s bind failed %d %d", driver->name, err, errno);
+
+               close(fd);
+               return err;
+       }
+
+       err = listen(fd, 10);
+       if (err < 0) {
+               near_error("%s listen failed %d", driver->name, err);
+
+               close(fd);
+               return err;
+       }
+
+       server_data = g_try_malloc0(sizeof(struct p2p_data));
+       if (server_data == NULL) {
+               close(fd);
+               return -ENOMEM;
+       }
+
+       server_data->driver = driver;
+       server_data->adapter_idx = adapter_idx;
+       server_data->fd = fd;
+       server_data->cb = cb;
+
+       channel = g_io_channel_unix_new(fd);
+       g_io_channel_set_close_on_unref(channel, TRUE);
+
+       server_data->watch = g_io_add_watch(channel,
+                               G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
+                               p2p_listener_event,
+                               (gpointer) server_data);
+       g_io_channel_unref(channel);
+
+       server_list = g_list_append(server_list, server_data);
+
+       return 0;
+}
+
+static int p2p_listen(uint32_t adapter_idx, near_device_io_cb cb)
+{
+       int err = -1;
+       GSList *list;
+
+       for (list = driver_list; list != NULL; list = list->next) {
+               struct near_p2p_driver *driver = list->data;
+
+               if (p2p_bind(driver, adapter_idx, cb) == 0)
+                       err = 0;
+       }
+
+       return err;
+}
+
+static int p2p_connect(uint32_t adapter_idx, uint32_t target_idx,
+                       struct near_ndef_message *ndef,
+                       near_device_io_cb cb,  struct near_p2p_driver *driver)
+{
+       int fd, err = 0;
+       struct sockaddr_nfc_llcp addr;
+
+       DBG("");
+
+       fd = socket(AF_NFC, SOCK_STREAM, NFC_SOCKPROTO_LLCP);
+       if (fd < 0)
+               return -errno;
+
+       memset(&addr, 0, sizeof(struct sockaddr_nfc_llcp));
+       addr.sa_family = AF_NFC;
+       addr.dev_idx = adapter_idx;
+       addr.target_idx = target_idx;
+       addr.nfc_protocol = NFC_PROTO_NFC_DEP;
+       addr.service_name_len = strlen(driver->service_name);
+       strcpy(addr.service_name, driver->service_name);
+
+       err = connect(fd, (struct sockaddr *) &addr,
+                       sizeof(struct sockaddr_nfc_llcp));
+       if (err < 0) {
+               near_error("Connect failed  %d", err);
+               close(fd);
+
+               return err;
+       }
+
+       return fd;
+}
+
+static int p2p_push(uint32_t adapter_idx, uint32_t target_idx,
+                       struct near_ndef_message *ndef, char *service_name,
+                       near_device_io_cb cb)
+{
+       int fd;
+       GSList *list;
+
+       DBG("");
+
+       for (list = driver_list; list != NULL; list = list->next) {
+               struct near_p2p_driver *driver = list->data;
+
+               if (strcmp(driver->service_name, service_name) != 0)
+                       continue;
+               /*
+                * Because of Android's implementation, we have use SNEP for
+                * Handover. So, on Handover session, we try to connect to
+                * the handover service and fallback to SNEP on connect fail.
+                */
+               fd = p2p_connect(adapter_idx, target_idx, ndef, cb, driver);
+               if (fd < 0)
+                       return  p2p_push(adapter_idx, target_idx, ndef,
+                               (char *) driver->fallback_service_name, cb);
+               else
+                       return driver->push(fd, adapter_idx, target_idx,
+                                       ndef, cb);
+       }
+
+       return -1;
+}
+
+static struct near_device_driver p2p_driver = {
+       .priority       = NEAR_DEVICE_PRIORITY_HIGH,
+       .listen         = p2p_listen,
+       .push           = p2p_push,
+};
+
+
+int near_p2p_register(struct near_p2p_driver *driver)
+{
+       DBG("driver %p name %s", driver, driver->name);
+
+       driver_list = g_slist_prepend(driver_list, driver);
+
+       return 0;
+}
+
+void near_p2p_unregister(struct near_p2p_driver *driver)
+{
+       DBG("driver %p name %s", driver, driver->name);
+
+       driver_list = g_slist_remove(driver_list, driver);
+}
+
+static int p2p_init(void)
+{
+       DBG("");
+
+       npp_init();
+       snep_init();
+       handover_init();
+
+       return near_device_driver_register(&p2p_driver);
+}
+
+static void p2p_exit(void)
+{
+       DBG("");
+
+       g_list_free_full(server_list, free_server_data);
+
+       snep_exit();
+       npp_exit();
+       handover_exit();
+
+       near_device_driver_unregister(&p2p_driver);
+}
+
+NEAR_PLUGIN_DEFINE(p2p, "NFC Forum peer to peer mode support", VERSION,
+               NEAR_PLUGIN_PRIORITY_HIGH, p2p_init, p2p_exit)
diff --git a/plugins/p2p.h b/plugins/p2p.h
new file mode 100644 (file)
index 0000000..033ecd2
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef AF_NFC
+#define AF_NFC 39
+#endif
+
+struct near_p2p_driver {
+       const char *name;
+       const char *service_name;
+       const char *fallback_service_name;
+       near_bool_t (*read)(int client_fd,
+                               uint32_t adapter_idx, uint32_t target_idx,
+                               near_device_io_cb cb);
+       int (*push)(int client_fd, uint32_t adapter_idx, uint32_t target_idx,
+                       struct near_ndef_message *ndef, near_device_io_cb cb);
+       void (*close)(int client_fd, int err);
+};
+
+#define TLV_SIZE 2
+
+int npp_init(void);
+void npp_exit(void);
+
+int snep_init(void);
+void snep_exit(void);
+
+int handover_init(void);
+void handover_exit(void);
+
+int near_p2p_register(struct near_p2p_driver *driver);
+void near_p2p_unregister(struct near_p2p_driver *driver);
diff --git a/plugins/snep.c b/plugins/snep.c
new file mode 100644 (file)
index 0000000..262cf4d
--- /dev/null
@@ -0,0 +1,691 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <linux/socket.h>
+
+#include <near/nfc.h>
+#include <near/plugin.h>
+#include <near/log.h>
+#include <near/types.h>
+#include <near/adapter.h>
+#include <near/device.h>
+#include <near/ndef.h>
+#include <near/tlv.h>
+
+#include "p2p.h"
+
+#define SNEP_VERSION     0x10
+
+/* Request codes */
+#define SNEP_REQ_CONTINUE 0x00
+#define SNEP_REQ_GET      0x01
+#define SNEP_REQ_PUT      0x02
+#define SNEP_REQ_REJECT   0x7f
+
+/* Response codes */
+#define SNEP_RESP_CONTINUE  0x80
+#define SNEP_RESP_SUCCESS   0x81
+#define SNEP_RESP_NOT_FOUND 0xc0
+#define SNEP_RESP_EXCESS    0xc1
+#define SNEP_RESP_BAD_REQ   0xc2
+#define SNEP_RESP_NOT_IMPL  0xe0
+#define SNEP_RESP_VERSION   0xe1
+#define SNEP_RESP_REJECT    0xff
+
+#define SNEP_REQ_PUT_HEADER_LENGTH 6
+#define SNEP_REQ_GET_HEADER_LENGTH 10
+/* TODO: Right now it is dummy, need to get correct value
+ * from lower layers */
+#define SNEP_REQ_MAX_FRAGMENT_LENGTH 128
+
+struct p2p_snep_data {
+       uint8_t request_code;
+       uint8_t *nfc_data;
+       uint32_t nfc_data_length;
+       uint32_t nfc_data_current_length;
+       uint8_t *nfc_data_ptr;
+       uint32_t adapter_idx;
+       uint32_t target_idx;
+       gboolean respond_continue;
+       near_tag_io_cb cb;
+};
+
+struct snep_fragment {
+       uint32_t len;
+       uint8_t *data;
+};
+
+struct p2p_snep_put_req_data {
+       uint8_t fd;
+       uint32_t adapter_idx;
+       uint32_t target_idx;
+       near_device_io_cb cb;
+       guint watch;
+
+       GSList *fragments;
+};
+
+struct p2p_snep_req_frame {
+       uint8_t version;
+       uint8_t request;
+       uint32_t length;
+       uint8_t ndef[];
+} __attribute__((packed));
+
+struct p2p_snep_resp_frame {
+       uint8_t version;
+       uint8_t response;
+       uint32_t length;
+       uint8_t info[];
+} __attribute__((packed));
+
+static GHashTable *snep_client_hash = NULL;
+
+static void free_snep_client(gpointer data)
+{
+       struct p2p_snep_data *snep_data = data;
+
+       g_free(snep_data->nfc_data);
+       g_free(snep_data);
+}
+
+static void snep_response_noinfo(int client_fd, uint8_t response)
+{
+       struct p2p_snep_resp_frame resp;
+
+       DBG("Response 0x%x", response);
+
+       resp.version = SNEP_VERSION;
+       resp.response = response;
+       resp.length = 0;
+
+       send(client_fd, &resp, sizeof(resp), 0);
+}
+
+static void snep_close(int client_fd, int err)
+{
+       struct p2p_snep_data *snep_data;
+
+       DBG("");
+
+       snep_data = g_hash_table_lookup(snep_client_hash,
+                                       GINT_TO_POINTER(client_fd));
+       if (snep_data == NULL)
+               return;
+
+       snep_data->cb(snep_data->adapter_idx, snep_data->target_idx, err);
+
+       g_hash_table_remove(snep_client_hash, GINT_TO_POINTER(client_fd));
+}
+
+static void snep_response_with_info(int client_fd, uint8_t response,
+               uint8_t *data, int length)
+{
+       struct p2p_snep_resp_frame *resp;
+
+       DBG("Response with info 0x%x (len:%d)", response, length);
+
+       resp = g_try_malloc0(sizeof(struct p2p_snep_resp_frame) + length);
+       if (resp == NULL) {
+               DBG("Memory allocation error");
+               return;
+       }
+
+       /* Fill */
+       resp->version = SNEP_VERSION;
+       resp->response = response;
+       resp->length = GUINT32_TO_BE(length);
+       memcpy(resp->info, data, length);
+
+       send(client_fd, resp, sizeof(struct p2p_snep_resp_frame) + length, 0);
+
+       g_free(resp);
+}
+
+/*
+ * snep_parse_handover_record
+ *
+ * The hr frame should be here BUT:
+ *     The first 4 bytes are the Max Allowed Length
+ *
+ * - Because of an Android's BUGs:
+ *     - the Hr frame is not correct; a Hr record
+ *     is embedded in a ... Hr record !!! The author
+ *     used 'Hr' instead of 'cr'
+ *     - The OOB block is badly written:
+ *     - the payload ID should be the same in the 'ac' record
+ *             and the OOB record.
+ *     - The OOB data length bytes must be swapped (Big endian to Little E.)
+ *
+ * The hack fixes the first issue (bluetooth.c fixes the second) !
+ * */
+static void snep_parse_handover_record(int client_fd, uint8_t *ndef,
+               uint32_t nfc_data_length)
+{
+       GList *records;
+       struct near_ndef_message *msg;
+
+       if (ndef == NULL)
+               return;
+
+       /*
+        * Bugfix Android: Fix 'cr' instead of 'Hr'
+        * Bug is in Google:HandoverManager.java:645
+        */
+       if (strncmp((char *)(ndef + 9), "Hr", 2) == 0)
+               *(ndef + 9) = 'c';
+
+       /* Parse the incoming frame */
+       records = near_ndef_parse_msg(ndef, nfc_data_length);
+
+       /*
+        * If we received a Hr, we must build a Hs and send it.
+        * If the frame is a Hs, nothing more to do (SNEP REPLY is SUCCESS and
+        * the pairing is done in near_ndef_parse_msg()
+        * */
+       if (strncmp((char *)(ndef + 3), "Hr", 2) == 0) {
+               msg = near_ndef_prepare_handover_record("Hs", records->data,
+                                               NEAR_CARRIER_BLUETOOTH);
+
+               near_info("Send SNEP / Hs frame");
+               snep_response_with_info(client_fd, SNEP_RESP_SUCCESS,
+                                       msg->data, msg->length);
+               g_free(msg->data);
+               g_free(msg);
+       }
+
+       near_ndef_records_free(records);
+
+       return;
+}
+
+static near_bool_t snep_read_ndef(int client_fd,
+                                       struct p2p_snep_data *snep_data)
+{
+       int bytes_recv, remaining_bytes;
+       struct near_device *device;
+       GList *records;
+
+       DBG("");
+
+       remaining_bytes = snep_data->nfc_data_length -
+                               snep_data->nfc_data_current_length;
+
+       DBG("Remaining bytes %d", remaining_bytes);
+
+       bytes_recv = recv(client_fd, snep_data->nfc_data_ptr, remaining_bytes,
+                                                               MSG_DONTWAIT);
+       if (bytes_recv < 0) {
+               near_error("%d %s", bytes_recv, strerror(errno));
+
+               /* Some more data should show up */
+               if (errno == EAGAIN)
+                       return TRUE;
+
+               goto out;
+       }
+
+       DBG("Received %d bytes", bytes_recv);
+
+       snep_data->nfc_data_current_length += bytes_recv;
+       snep_data->nfc_data_ptr += bytes_recv;
+
+       if (snep_data->nfc_data_length != snep_data->nfc_data_current_length) {
+               if (snep_data->respond_continue == FALSE) {
+                       DBG("Continue");
+                       snep_data->respond_continue = TRUE;
+                       snep_response_noinfo(client_fd, SNEP_RESP_CONTINUE);
+               }
+
+               return TRUE;
+       }
+
+       if (snep_data->request_code == SNEP_REQ_GET) {
+               /*
+                * This goes against the SNEP specification:
+                * "The default server SHALL NOT accept Get requests." but
+                * the first Android Handover implementation (Jelly Bean)
+                * does Handover through SNEP via GET frames...Since Android
+                * seems popular these days, we'd better support that spec
+                * violation.
+                *
+                * Parse the Hr and send a Hs
+                * Max allowed size in the first 4 bytes
+                */
+               snep_parse_handover_record(client_fd, snep_data->nfc_data + 4,
+                               snep_data->nfc_data_length - 4);
+       } else {
+               snep_response_noinfo(client_fd, SNEP_RESP_SUCCESS);
+               if (near_device_add_data(snep_data->adapter_idx,
+                               snep_data->target_idx,
+                               snep_data->nfc_data,
+                               snep_data->nfc_data_length) < 0)
+                       goto out;
+
+               device = near_device_get_device(snep_data->adapter_idx,
+                               snep_data->target_idx);
+               if (device == NULL)
+                       goto out;
+
+               records = near_ndef_parse_msg(snep_data->nfc_data,
+                               snep_data->nfc_data_length);
+               near_device_add_records(device, records, snep_data->cb, 0);
+       }
+
+out:
+       g_hash_table_remove(snep_client_hash, GINT_TO_POINTER(client_fd));
+
+       return FALSE;
+}
+
+static near_bool_t snep_read(int client_fd,
+                               uint32_t adapter_idx, uint32_t target_idx,
+                               near_tag_io_cb cb)
+{
+       struct p2p_snep_data *snep_data;
+       struct p2p_snep_req_frame frame;
+       int bytes_recv;
+       uint32_t ndef_length;
+
+       DBG("");
+
+       snep_data = g_hash_table_lookup(snep_client_hash,
+                                       GINT_TO_POINTER(client_fd));
+
+       /*
+        * We already got something from this client, we should try
+        * to continue reading.
+        */
+       if (snep_data != NULL)
+               return snep_read_ndef(client_fd, snep_data);
+
+       /* TODO Try with PEEK */
+       bytes_recv = recv(client_fd, &frame, sizeof(frame), 0);
+       if (bytes_recv < 0) {
+               near_error("Could not read SNEP frame %d", bytes_recv);
+               return bytes_recv;
+       }
+
+       ndef_length = GINT_FROM_BE(frame.length);
+
+       DBG("Allocating SNEP data %d", ndef_length);
+
+       snep_data = g_try_malloc0(sizeof(struct p2p_snep_data));
+       if (snep_data == NULL)
+               return FALSE;
+
+       snep_data->nfc_data = g_try_malloc0(ndef_length + TLV_SIZE);
+       if (snep_data->nfc_data == NULL) {
+               g_free(snep_data);
+               return FALSE;
+       }
+
+       snep_data->nfc_data_length = ndef_length;
+       snep_data->nfc_data_ptr = snep_data->nfc_data;
+       snep_data->adapter_idx = adapter_idx;
+       snep_data->target_idx = target_idx;
+       snep_data->respond_continue = FALSE;
+       snep_data->cb = cb;
+
+       g_hash_table_insert(snep_client_hash,
+                                       GINT_TO_POINTER(client_fd), snep_data);
+
+       snep_data->request_code = frame.request;
+
+       DBG("Request 0x%x", frame.request);
+
+       switch (frame.request) {
+       case SNEP_REQ_CONTINUE:
+               near_error("Unsupported SNEP request code");
+               snep_response_noinfo(client_fd, SNEP_RESP_NOT_IMPL);
+               return FALSE;
+       case SNEP_REQ_GET:
+       case SNEP_REQ_PUT:
+               return snep_read_ndef(client_fd, snep_data);
+       }
+
+       return FALSE;
+}
+
+static void free_snep_fragment(gpointer data)
+{
+       struct snep_fragment *fragment = data;
+
+       if (fragment != NULL)
+               g_free(fragment->data);
+
+       g_free(fragment);
+       fragment = NULL;
+}
+
+static void free_snep_push_data(gpointer userdata, int status)
+{
+       struct p2p_snep_put_req_data *data;
+
+       DBG("");
+
+       data = (struct p2p_snep_put_req_data *) userdata;
+
+       close(data->fd);
+
+       if (data->cb)
+               data->cb(data->adapter_idx, data->target_idx, status);
+
+       if (data->watch > 0)
+               g_source_remove(data->watch);
+
+       g_slist_free_full(data->fragments, free_snep_fragment);
+       g_free(data);
+}
+
+static int snep_send_fragment(struct p2p_snep_put_req_data *req)
+{
+       struct snep_fragment *fragment;
+       int err;
+
+       DBG("");
+
+       if (req == NULL || req->fragments == NULL ||
+               g_slist_length(req->fragments) == 0)
+               return -EINVAL;
+
+       fragment = req->fragments->data;
+
+       err = send(req->fd, fragment->data, fragment->len, 0);
+
+       req->fragments = g_slist_remove(req->fragments, fragment);
+       g_free(fragment->data);
+       g_free(fragment);
+
+       return err;
+}
+
+static int snep_push_response(struct p2p_snep_put_req_data *req)
+{
+       struct p2p_snep_resp_frame frame;
+       uint8_t *ndef;
+       uint32_t ndef_len;
+       int bytes_recv, err;
+
+       DBG("");
+
+       bytes_recv = recv(req->fd, &frame, sizeof(frame), 0);
+       if (bytes_recv < 0) {
+               near_error("Could not read SNEP frame %d", bytes_recv);
+               return bytes_recv;
+       }
+
+       /* Check frame length */
+       frame.length = g_ntohl(frame.length);
+
+       DBG("Response 0x%x", frame.response);
+
+       switch (frame.response) {
+       case SNEP_RESP_CONTINUE:
+               while (g_slist_length(req->fragments) != 0) {
+                       err = snep_send_fragment(req);
+                       if (err < 0)
+                               return err;
+               }
+
+               return frame.response;
+
+       case SNEP_RESP_SUCCESS:
+               if (frame.length == 0)
+                       return 0;
+
+               /* Get the incoming data */
+               ndef_len = frame.length;
+               ndef = g_try_malloc0(ndef_len);
+               if (ndef == NULL)
+                       return -ENOMEM;
+
+               bytes_recv = recv(req->fd, ndef, ndef_len, 0);
+               if (bytes_recv < 0) {
+                       near_error("Could not read SNEP frame %d", bytes_recv);
+                       return bytes_recv;
+               }
+
+               /* Not enough bytes */
+               if (bytes_recv < 6)
+                       return -EINVAL;
+
+               if (strncmp((char *)(ndef + 3), "Hs", 2) == 0)
+                       snep_parse_handover_record(req->fd, ndef, ndef_len);
+
+               g_free(ndef);
+
+               return 0;
+       }
+
+       return -1;
+}
+
+static gboolean snep_push_event(GIOChannel *channel,
+                               GIOCondition condition, gpointer data)
+{
+       int err;
+
+       DBG("condition 0x%x", condition);
+
+       if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+
+               near_error("Error with SNEP channel");
+
+               free_snep_push_data(data, -1);
+
+               return FALSE;
+       }
+
+       err = snep_push_response(data);
+       if (err <= 0) {
+               free_snep_push_data(data, err);
+
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static int snep_push_prepare_fragments(struct p2p_snep_put_req_data *req,
+                                               struct near_ndef_message *ndef)
+{
+       struct snep_fragment *fragment;
+       uint32_t max_fragment_len;
+
+       DBG("");
+
+       max_fragment_len = SNEP_REQ_MAX_FRAGMENT_LENGTH;
+
+       while (ndef->offset < ndef->length) {
+
+               fragment = g_try_malloc0(sizeof(struct snep_fragment));
+               if (fragment == NULL)
+                       return -ENOMEM;
+
+               if (max_fragment_len <= (ndef->length - ndef->offset))
+                       fragment->len = max_fragment_len;
+               else
+                       fragment->len = ndef->length - ndef->offset;
+
+               fragment->data = g_try_malloc0(fragment->len);
+               if (fragment->data == NULL) {
+                       g_free(fragment);
+                       return -ENOMEM;
+               }
+
+               memcpy(fragment->data, ndef->data + ndef->offset,
+                                       fragment->len);
+               ndef->offset += fragment->len;
+               req->fragments = g_slist_append(req->fragments, fragment);
+       }
+
+       return 0;
+}
+
+static int snep_push(int fd, uint32_t adapter_idx, uint32_t target_idx,
+                       struct near_ndef_message *ndef,
+                       near_device_io_cb cb)
+{
+       struct p2p_snep_put_req_data *req;
+       struct p2p_snep_req_frame header;
+       struct snep_fragment *fragment;
+       uint32_t max_fragment_len;
+       GIOChannel *channel;
+       gboolean fragmenting;
+       int err;
+       int snep_req_header_length, snep_additional_length;
+
+       DBG("");
+
+       req = g_try_malloc0(sizeof(struct p2p_snep_put_req_data));
+       if (req == NULL) {
+               err = -ENOMEM;
+               goto error;
+       }
+
+       channel = g_io_channel_unix_new(fd);
+       g_io_channel_set_close_on_unref(channel, TRUE);
+
+       req->fd = fd;
+       req->adapter_idx = adapter_idx;
+       req->target_idx = target_idx;
+       req->cb = cb;
+       ndef->offset = 0;
+       req->watch = g_io_add_watch(channel, G_IO_IN | G_IO_HUP | G_IO_NVAL |
+                                       G_IO_ERR, snep_push_event,
+                                       (gpointer) req);
+
+       max_fragment_len = SNEP_REQ_MAX_FRAGMENT_LENGTH;
+       header.version = SNEP_VERSION;
+
+       /* Check if Hr or Hs for Handover over SNEP */
+       if (*(char *)(ndef->data + 3) == 'H') {
+               header.request = SNEP_REQ_GET;          /* Get for android */
+               snep_req_header_length = SNEP_REQ_GET_HEADER_LENGTH;
+               snep_additional_length = 4;  /* 4 Acceptable Length */
+       } else {
+               header.request = SNEP_REQ_PUT;
+               snep_req_header_length = SNEP_REQ_PUT_HEADER_LENGTH;
+               snep_additional_length = 0;
+       }
+
+       header.length = GUINT32_TO_BE(ndef->length + snep_additional_length);
+
+       fragment = g_try_malloc0(sizeof(struct snep_fragment));
+       if (fragment == NULL) {
+               err = -ENOMEM;
+               goto error;
+       }
+
+       if (max_fragment_len >= (ndef->length + snep_req_header_length)) {
+               fragment->len = ndef->length + snep_req_header_length;
+               fragmenting = FALSE;
+       } else {
+               fragment->len = max_fragment_len;
+               fragmenting = TRUE;
+       }
+
+       fragment->data = g_try_malloc0(fragment->len);
+       if (fragment->data == NULL) {
+               g_free(fragment);
+               err = ENOMEM;
+               goto error;
+       }
+
+       /* Header to data - common header */
+       memcpy(fragment->data, (uint8_t *)&header, SNEP_REQ_PUT_HEADER_LENGTH);
+
+       /* if GET, we add the Acceptable length */
+       if (header.request == SNEP_REQ_GET)
+               *(uint32_t *)(fragment->data + SNEP_REQ_PUT_HEADER_LENGTH)  =
+                               GUINT32_TO_BE(snep_req_header_length);
+
+       if (fragmenting == TRUE) {
+               memcpy(fragment->data + snep_req_header_length, ndef->data,
+                               max_fragment_len - snep_req_header_length);
+               ndef->offset = max_fragment_len - snep_req_header_length;
+
+               err = snep_push_prepare_fragments(req, ndef);
+               if (err < 0) {
+                       g_free(fragment->data);
+                       g_free(fragment);
+                       goto error;
+               }
+
+       } else {
+               memcpy(fragment->data + snep_req_header_length,
+                                       ndef->data, ndef->length);
+       }
+
+       err = send(fd, fragment->data, fragment->len, 0);
+       if (err < 0) {
+               near_error("Sending failed %d", err);
+               g_free(fragment->data);
+               g_free(fragment);
+
+               goto error;
+       }
+
+       g_free(fragment->data);
+       g_free(fragment);
+
+       return 0;
+
+error:
+       free_snep_push_data(req, err);
+
+       return err;
+}
+
+struct near_p2p_driver snep_driver = {
+       .name = "SNEP",
+       .service_name = NEAR_DEVICE_SN_SNEP,
+       .fallback_service_name = NEAR_DEVICE_SN_NPP,
+       .read = snep_read,
+       .push = snep_push,
+       .close = snep_close,
+};
+
+int snep_init(void)
+{
+       snep_client_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                                       NULL, free_snep_client);
+
+       return near_p2p_register(&snep_driver);
+}
+
+void snep_exit(void)
+{
+       near_p2p_unregister(&snep_driver);
+
+       g_hash_table_destroy(snep_client_hash);
+       snep_client_hash = NULL;
+}
diff --git a/src/adapter.c b/src/adapter.c
new file mode 100644 (file)
index 0000000..25cf956
--- /dev/null
@@ -0,0 +1,1193 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <gdbus.h>
+
+#include "near.h"
+
+/* We check for the tag being present every 2 seconds */
+#define CHECK_PRESENCE_PERIOD 2
+
+static DBusConnection *connection = NULL;
+
+static GHashTable *adapter_hash;
+
+enum near_adapter_rf_mode {
+       NEAR_ADAPTER_RF_MODE_IDLE      = 0,
+       NEAR_ADAPTER_RF_MODE_INITIATOR = 1,
+       NEAR_ADAPTER_RF_MODE_TARGET    = 2
+};
+
+#define NEAR_ADAPTER_MODE_INITIATOR 0x1
+#define NEAR_ADAPTER_MODE_TARGET    0x2
+#define NEAR_ADAPTER_MODE_DUAL      0x3
+
+struct near_adapter {
+       char *path;
+
+       char *name;
+       uint32_t idx;
+       uint32_t protocols;
+       uint32_t poll_mode;
+       enum near_adapter_rf_mode rf_mode;
+
+       near_bool_t powered;
+       near_bool_t polling;
+       near_bool_t constant_poll;
+       near_bool_t dep_up;
+
+       GHashTable *tags;
+       struct near_tag *tag_link;
+       int tag_sock;
+
+       GHashTable *devices;
+       struct near_device *device_link;
+       int device_sock;
+
+       GIOChannel *channel;
+       guint watch;
+       GList *ioreq_list;
+
+       guint presence_timeout;
+};
+
+struct near_adapter_ioreq {
+       uint32_t target_idx;
+       near_recv cb;
+       unsigned char buf[1024];
+       size_t len;
+       void *data;
+};
+
+/* HACK HACK */
+#ifndef AF_NFC
+#define AF_NFC 39
+#endif
+
+static void free_adapter(gpointer data)
+{
+       struct near_adapter *adapter = data;
+
+       if (adapter->presence_timeout > 0)
+               g_source_remove(adapter->presence_timeout);
+
+       g_free(adapter->name);
+       g_free(adapter->path);
+       g_free(adapter);
+}
+
+static void free_tag(gpointer data)
+{
+       struct near_tag *tag = data;
+
+       __near_tag_remove(tag);
+}
+
+static void free_device(gpointer data)
+{
+       struct near_device *device = data;
+
+       __near_device_remove(device);
+}
+
+static char *rf_mode_to_string(struct near_adapter *adapter)
+{
+       switch (adapter->rf_mode) {
+       case NEAR_ADAPTER_RF_MODE_IDLE:
+               return "Idle";
+       case NEAR_ADAPTER_RF_MODE_INITIATOR:
+               return "Initiator";
+       case NEAR_ADAPTER_RF_MODE_TARGET:
+               return "Target";
+       }
+
+       return NULL;
+}
+
+static void polling_changed(struct near_adapter *adapter)
+{
+
+       near_dbus_property_changed_basic(adapter->path,
+                                       NFC_ADAPTER_INTERFACE, "Polling",
+                                       DBUS_TYPE_BOOLEAN, &adapter->polling);
+}
+
+static void rf_mode_changed(struct near_adapter *adapter)
+{
+       const char *rf_mode = rf_mode_to_string(adapter);
+
+       if (rf_mode == NULL)
+               return;
+
+       near_dbus_property_changed_basic(adapter->path,
+                                       NFC_ADAPTER_INTERFACE, "Mode",
+                                       DBUS_TYPE_STRING, &rf_mode);
+}
+
+static int adapter_start_poll(struct near_adapter *adapter)
+{
+       int err;
+       uint32_t im_protos, tm_protos;
+
+       if (g_hash_table_size(adapter->tags) > 0) {
+               DBG("Clearing tags");
+
+               g_hash_table_remove_all(adapter->tags);
+               __near_adapter_tags_changed(adapter->idx);
+       }
+
+       if (g_hash_table_size(adapter->devices) > 0) {
+               DBG("Clearing devices");
+
+               g_hash_table_remove_all(adapter->devices);
+               __near_adapter_devices_changed(adapter->idx);
+       }
+
+       DBG("Poll mode 0x%x", adapter->poll_mode);
+
+       im_protos = tm_protos = 0;
+
+       if (adapter->poll_mode & NEAR_ADAPTER_MODE_INITIATOR)
+               im_protos = adapter->protocols;
+
+       if (adapter->poll_mode & NEAR_ADAPTER_MODE_TARGET)
+               tm_protos = adapter->protocols;
+
+       err = __near_netlink_start_poll(adapter->idx, im_protos, tm_protos);
+       if (err < 0)
+               return err;
+
+       adapter->polling = TRUE;
+
+       polling_changed(adapter);
+
+       return 0;
+}
+
+static void append_path(gpointer key, gpointer value, gpointer user_data)
+{
+       struct near_adapter *adapter = value;
+       DBusMessageIter *iter = user_data;
+
+       DBG("%s", adapter->path);
+
+       if (adapter->path == NULL)
+               return;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+                                                       &adapter->path);
+}
+
+void __near_adapter_list(DBusMessageIter *iter, void *user_data)
+{
+       g_hash_table_foreach(adapter_hash, append_path, iter);
+}
+
+static void append_protocols(DBusMessageIter *iter, void *user_data)
+{
+       struct near_adapter *adapter = user_data;
+       const char *str;
+
+       DBG("protocols 0x%x", adapter->protocols);
+
+       if (adapter->protocols & NFC_PROTO_FELICA_MASK) {
+               str = "Felica";
+
+               dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+       }
+
+       if (adapter->protocols & NFC_PROTO_MIFARE_MASK) {
+               str = "MIFARE";
+
+               dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+       }
+
+       if (adapter->protocols & NFC_PROTO_JEWEL_MASK) {
+               str = "Jewel";
+
+               dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+       }
+
+       if (adapter->protocols & NFC_PROTO_ISO14443_MASK) {
+               str = "ISO-DEP";
+
+               dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+       }
+
+       if (adapter->protocols & NFC_PROTO_NFC_DEP_MASK) {
+               str = "NFC-DEP";
+
+               dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+       }
+}
+
+static void append_tag_path(gpointer key, gpointer value, gpointer user_data)
+{
+       struct near_tag *tag = value;
+       DBusMessageIter *iter = user_data;
+       const char *tag_path;
+
+       tag_path = __near_tag_get_path(tag);
+       if (tag_path == NULL)
+               return;
+
+       DBG("%s", tag_path);
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &tag_path);
+}
+
+static void append_tags(DBusMessageIter *iter, void *user_data)
+{
+       struct near_adapter *adapter = user_data;
+
+       DBG("");
+
+       g_hash_table_foreach(adapter->tags, append_tag_path, iter);
+}
+
+static void append_device_path(gpointer key, gpointer value, gpointer user_data)
+{
+       struct near_device *device = value;
+       DBusMessageIter *iter = user_data;
+       const char *device_path;
+
+       device_path = __near_device_get_path(device);
+       if (device_path == NULL)
+               return;
+
+       DBG("%s", device_path);
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+                                                       &device_path);
+}
+
+static void append_devices(DBusMessageIter *iter, void *user_data)
+{
+       struct near_adapter *adapter = user_data;
+
+       DBG("");
+
+       g_hash_table_foreach(adapter->devices, append_device_path, iter);
+}
+
+void __near_adapter_tags_changed(uint32_t adapter_idx)
+{
+       struct near_adapter *adapter;
+
+       DBG("");
+
+       adapter = g_hash_table_lookup(adapter_hash,
+                                       GINT_TO_POINTER(adapter_idx));
+       if (adapter == NULL)
+               return;
+
+       near_dbus_property_changed_array(adapter->path,
+                                       NFC_ADAPTER_INTERFACE, "Tags",
+                                       DBUS_TYPE_OBJECT_PATH, append_tags,
+                                       adapter);
+}
+
+void __near_adapter_devices_changed(uint32_t adapter_idx)
+{
+       struct near_adapter *adapter;
+
+       DBG("");
+
+       adapter = g_hash_table_lookup(adapter_hash,
+                                       GINT_TO_POINTER(adapter_idx));
+       if (adapter == NULL)
+               return;
+
+       near_dbus_property_changed_array(adapter->path,
+                                       NFC_ADAPTER_INTERFACE, "Devices",
+                                       DBUS_TYPE_OBJECT_PATH, append_devices,
+                                       adapter);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct near_adapter *adapter = data;
+       const char *rf_mode;
+       DBusMessage *reply;
+       DBusMessageIter array, dict;
+
+       DBG("conn %p", conn);
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &array);
+
+       near_dbus_dict_open(&array, &dict);
+
+       near_dbus_dict_append_basic(&dict, "Powered",
+                                   DBUS_TYPE_BOOLEAN, &adapter->powered);
+
+       near_dbus_dict_append_basic(&dict, "Polling",
+                                   DBUS_TYPE_BOOLEAN, &adapter->polling);
+
+       rf_mode = rf_mode_to_string(adapter);
+       if (rf_mode != NULL)
+               near_dbus_dict_append_basic(&dict, "Mode",
+                                               DBUS_TYPE_STRING, &rf_mode);
+
+       near_dbus_dict_append_array(&dict, "Protocols",
+                               DBUS_TYPE_STRING, append_protocols, adapter);
+
+       near_dbus_dict_append_array(&dict, "Tags",
+                               DBUS_TYPE_OBJECT_PATH, append_tags, adapter);
+
+       near_dbus_dict_append_array(&dict, "Devices",
+                               DBUS_TYPE_OBJECT_PATH, append_devices, adapter);
+
+       near_dbus_dict_close(&array, &dict);
+
+       return reply;
+}
+
+static DBusMessage *set_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct near_adapter *adapter = data;
+       DBusMessageIter iter, value;
+       const char *name;
+       int type, err;
+
+       DBG("conn %p", conn);
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return __near_error_invalid_arguments(msg);
+
+       dbus_message_iter_get_basic(&iter, &name);
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       type = dbus_message_iter_get_arg_type(&value);
+
+       if (g_str_equal(name, "Powered") == TRUE) {
+               near_bool_t powered;
+
+               if (type != DBUS_TYPE_BOOLEAN)
+                       return __near_error_invalid_arguments(msg);
+
+               dbus_message_iter_get_basic(&value, &powered);
+
+               err = __near_netlink_adapter_enable(adapter->idx, powered);
+               if (err < 0) {
+                       if (err == -EALREADY) {
+                               if (powered == TRUE)
+                                       return __near_error_already_enabled(msg);
+                               else
+                                       return __near_error_already_disabled(msg);
+                       }
+
+                       return __near_error_failed(msg, -err);
+               }
+
+               adapter->powered = powered;
+       } else {
+               return __near_error_invalid_property(msg);
+       }
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *start_poll_loop(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct near_adapter *adapter = data;
+       const char *dbus_mode;
+       int err;
+
+       DBG("conn %p", conn);
+
+       dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &dbus_mode,
+                                                       DBUS_TYPE_INVALID);
+
+       DBG("Mode %s", dbus_mode);
+
+       if (g_strcmp0(dbus_mode, "Initiator") == 0)
+               adapter->poll_mode = NEAR_ADAPTER_MODE_INITIATOR;
+       else if (g_strcmp0(dbus_mode, "Target") == 0)
+               adapter->poll_mode = NEAR_ADAPTER_MODE_TARGET;
+       else if (g_strcmp0(dbus_mode, "Dual") == 0)
+               adapter->poll_mode = NEAR_ADAPTER_MODE_DUAL;
+       else
+               adapter->poll_mode = NEAR_ADAPTER_MODE_INITIATOR;
+
+       err = adapter_start_poll(adapter);
+       if (err < 0)
+               return __near_error_failed(msg, -err);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *stop_poll_loop(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct near_adapter *adapter = data;
+       int err;
+
+       DBG("conn %p", conn);
+
+       if (adapter->polling == FALSE)
+               return __near_error_not_polling(msg);
+
+       err = __near_netlink_stop_poll(adapter->idx);
+       if (err < 0)
+               return __near_error_failed(msg, -err);
+
+       adapter->polling = FALSE;
+
+       polling_changed(adapter);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static void tag_present_cb(uint32_t adapter_idx, uint32_t target_idx,
+                                                               int status);
+
+static gboolean check_presence(gpointer user_data)
+{
+       struct near_adapter *adapter = user_data;
+       struct near_tag *tag;
+       int err;
+
+       DBG("");
+
+       if (adapter == NULL)
+               return FALSE;
+
+       tag = adapter->tag_link;
+       if (tag == NULL)
+               goto out_err;
+
+       err = __near_tag_check_presence(tag, tag_present_cb);
+       if (err < 0) {
+               DBG("Could not check target presence");
+               goto out_err;
+       }
+
+       return FALSE;
+
+out_err:
+       near_adapter_disconnect(adapter->idx);
+       if (adapter->constant_poll == TRUE)
+               adapter_start_poll(adapter);
+
+       return FALSE;
+}
+
+static void tag_present_cb(uint32_t adapter_idx, uint32_t target_idx,
+                                                               int status)
+{
+       struct near_adapter *adapter;
+
+       DBG("");
+
+       adapter = g_hash_table_lookup(adapter_hash,
+                                       GINT_TO_POINTER(adapter_idx));
+       if (adapter == NULL)
+               return;
+
+       if (status < 0) {
+               DBG("Tag is gone");
+
+               near_adapter_disconnect(adapter->idx);
+               if (adapter->constant_poll == TRUE)
+                       adapter_start_poll(adapter);
+
+               return;
+       }
+
+       adapter->presence_timeout =
+               g_timeout_add_seconds(CHECK_PRESENCE_PERIOD,
+                                       check_presence, adapter);
+}
+
+void __near_adapter_start_check_presence(uint32_t adapter_idx,
+                                               uint32_t target_idx)
+{
+       struct near_adapter *adapter;
+
+       DBG("");
+
+       adapter = g_hash_table_lookup(adapter_hash,
+                       GINT_TO_POINTER(adapter_idx));
+       if (adapter == NULL)
+               return;
+
+       adapter->presence_timeout =
+                       g_timeout_add_seconds(CHECK_PRESENCE_PERIOD,
+                                       check_presence, adapter);
+}
+
+void __near_adapter_stop_check_presence(uint32_t adapter_idx,
+                                               uint32_t target_idx)
+{
+       struct near_adapter *adapter;
+
+       DBG("");
+
+       adapter = g_hash_table_lookup(adapter_hash,
+                       GINT_TO_POINTER(adapter_idx));
+       if (adapter == NULL)
+               return;
+
+       if (adapter->presence_timeout > 0)
+               g_source_remove(adapter->presence_timeout);
+}
+
+static const GDBusMethodTable adapter_methods[] = {
+       { GDBUS_METHOD("GetProperties",
+                               NULL, GDBUS_ARGS({"properties", "a{sv}"}),
+                               get_properties) },
+       { GDBUS_METHOD("SetProperty",
+                               GDBUS_ARGS({"name", "s"}, {"value", "v"}),
+                               NULL, set_property) },
+       { GDBUS_METHOD("StartPollLoop", GDBUS_ARGS({"name", "s"}), NULL,
+                                                       start_poll_loop) },
+       { GDBUS_METHOD("StopPollLoop", NULL, NULL, stop_poll_loop) },
+       { },
+};
+
+static const GDBusSignalTable adapter_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                               GDBUS_ARGS({"name", "s"}, {"value", "v"})) },
+       { GDBUS_SIGNAL("TagFound", GDBUS_ARGS({"address", "o"})) },
+       { GDBUS_SIGNAL("TagLost", GDBUS_ARGS({"address", "o"})) },
+       { }
+};
+
+struct near_adapter * __near_adapter_create(uint32_t idx,
+               const char *name, uint32_t protocols, near_bool_t powered)
+{
+       struct near_adapter *adapter;
+
+       adapter = g_try_malloc0(sizeof(struct near_adapter));
+       if (adapter == NULL)
+               return NULL;
+
+       adapter->name = g_strdup(name);
+       if (adapter->name == NULL) {
+               g_free(adapter);
+               return NULL;
+       }
+       adapter->idx = idx;
+       adapter->protocols = protocols;
+       adapter->powered = powered;
+       adapter->constant_poll = near_setting_get_bool("ConstantPoll");
+       adapter->dep_up = FALSE;
+       adapter->tags = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                                       NULL, free_tag);
+       adapter->tag_sock = -1;
+
+       adapter->devices = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                                       NULL, free_device);
+       adapter->device_sock = -1;
+
+       adapter->path = g_strdup_printf("%s/nfc%d", NFC_PATH, idx);
+
+       return adapter;
+}
+
+void __near_adapter_destroy(struct near_adapter *adapter)
+{
+       DBG("");
+
+       free_adapter(adapter);
+}
+
+const char *__near_adapter_get_path(struct near_adapter *adapter)
+{
+       return adapter->path;
+}
+
+struct near_adapter *__near_adapter_get(uint32_t idx)
+{
+       return g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx));
+}
+
+int __near_adapter_set_dep_state(uint32_t idx, near_bool_t dep)
+{
+       struct near_adapter *adapter;
+
+       DBG("idx %d", idx);
+
+       adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx));
+       if (adapter == NULL)
+               return -ENODEV;
+
+       adapter->dep_up = dep;
+
+       if (dep == FALSE && adapter->constant_poll == TRUE)
+               adapter_start_poll(adapter);
+
+       if (dep == FALSE) {
+               uint32_t target_idx;
+
+               target_idx =  __neard_device_get_idx(adapter->device_link);
+               __near_adapter_remove_target(idx, target_idx);
+       } else {
+               __near_adapter_devices_changed(idx);
+       }
+
+       return 0;
+}
+
+near_bool_t __near_adapter_get_dep_state(uint32_t idx)
+{
+       struct near_adapter *adapter;
+
+       DBG("idx %d", idx);
+
+       adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx));
+       if (adapter == NULL)
+               return FALSE;
+
+       return adapter->dep_up;
+}
+
+int __near_adapter_add(struct near_adapter *adapter)
+{
+       uint32_t idx = adapter->idx;
+
+       DBG("%s", adapter->path);
+
+       if (g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx)) != NULL)
+               return -EEXIST;
+
+       g_hash_table_insert(adapter_hash, GINT_TO_POINTER(idx), adapter);
+
+       DBG("connection %p", connection);
+
+       g_dbus_register_interface(connection, adapter->path,
+                                       NFC_ADAPTER_INTERFACE,
+                                       adapter_methods, adapter_signals,
+                                       NULL, adapter, NULL);
+
+       return 0;
+}
+
+void __near_adapter_remove(struct near_adapter *adapter)
+{
+       DBG("%s", adapter->path);
+
+       g_dbus_unregister_interface(connection, adapter->path,
+                                               NFC_ADAPTER_INTERFACE);
+
+       g_hash_table_remove(adapter_hash, GINT_TO_POINTER(adapter->idx));
+}
+
+static void tag_read_cb(uint32_t adapter_idx, uint32_t target_idx, int status)
+{
+       struct near_adapter *adapter;
+
+       DBG("status %d", status);
+
+       adapter = g_hash_table_lookup(adapter_hash,
+                                       GINT_TO_POINTER(adapter_idx));
+       if (adapter == NULL)
+               return;
+
+       if (status < 0) {
+               near_adapter_disconnect(adapter->idx);
+               if (adapter->constant_poll == TRUE)
+                       adapter_start_poll(adapter);
+
+               return;
+       }
+
+       __near_adapter_tags_changed(adapter_idx);
+
+       adapter->presence_timeout =
+               g_timeout_add_seconds(CHECK_PRESENCE_PERIOD,
+                                       check_presence, adapter);
+}
+
+static void device_read_cb(uint32_t adapter_idx, uint32_t target_idx,
+                                                               int status)
+{
+       struct near_adapter *adapter;
+
+       DBG("status %d", status);
+
+       adapter = g_hash_table_lookup(adapter_hash,
+                                       GINT_TO_POINTER(adapter_idx));
+       if (adapter == NULL)
+               return;
+
+       if (status < 0) {
+               if (adapter->device_link != NULL) {
+                       __near_netlink_dep_link_down(adapter->idx);
+                       adapter->device_link = NULL;
+               }
+
+               if (adapter->constant_poll == TRUE)
+                       adapter_start_poll(adapter);
+
+               return;
+       }
+}
+
+static int adapter_add_tag(struct near_adapter *adapter, uint32_t target_idx,
+                       uint32_t protocols,
+                       uint16_t sens_res, uint8_t sel_res,
+                       uint8_t *nfcid, uint8_t nfcid_len)
+{
+       struct near_tag *tag;
+       uint32_t tag_type;
+       int err;
+
+       tag = __near_tag_add(adapter->idx, target_idx, protocols,
+                               sens_res, sel_res,
+                               nfcid, nfcid_len);
+       if (tag == NULL)
+               return -ENODEV;
+
+       g_hash_table_insert(adapter->tags, GINT_TO_POINTER(target_idx), tag);
+
+       tag_type = __near_tag_get_type(tag);
+
+       err = near_adapter_connect(adapter->idx, target_idx, tag_type);
+       if (err < 0) {
+               near_error("Could not connect");
+               return err;
+       }
+
+       return __near_tag_read(tag, tag_read_cb);
+}
+
+static int adapter_add_device(struct near_adapter *adapter,
+                               uint32_t target_idx,
+                               uint8_t *nfcid, uint8_t nfcid_len)
+{
+       struct near_device *device;
+       int err;
+
+       device = __near_device_add(adapter->idx, target_idx, nfcid, nfcid_len);
+       if (device == NULL)
+               return -ENODEV;
+
+       g_hash_table_insert(adapter->devices, GINT_TO_POINTER(target_idx),
+                                                               device);
+
+       /* For p2p, reading is listening for an incoming connection */
+       err = __near_device_listen(device, device_read_cb);
+       if (err < 0) {
+               near_error("Could not read device");
+               return err;
+       }
+
+       adapter->device_link = device;
+
+       if (adapter->dep_up == TRUE)
+               return 0;
+
+       err = __near_netlink_dep_link_up(adapter->idx, target_idx,
+                                       NFC_COMM_ACTIVE, NFC_RF_INITIATOR);
+
+       if (err < 0)
+               adapter->device_link = NULL;
+
+       return err;
+}
+
+int __near_adapter_add_target(uint32_t idx, uint32_t target_idx,
+                       uint32_t protocols, uint16_t sens_res, uint8_t sel_res,
+                       uint8_t *nfcid, uint8_t nfcid_len)
+{
+       struct near_adapter *adapter;
+
+       DBG("idx %d", idx);
+
+       adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx));
+       if (adapter == NULL)
+               return -ENODEV;
+
+       adapter->polling = FALSE;
+       polling_changed(adapter);
+
+       adapter->rf_mode = NEAR_ADAPTER_RF_MODE_INITIATOR;
+       rf_mode_changed(adapter);
+
+       if (protocols & NFC_PROTO_NFC_DEP_MASK)
+               return adapter_add_device(adapter, target_idx,
+                                               nfcid, nfcid_len);
+       else
+               return adapter_add_tag(adapter, target_idx, protocols,
+                                       sens_res, sel_res, nfcid, nfcid_len);
+}
+
+int __near_adapter_remove_target(uint32_t idx, uint32_t target_idx)
+{
+       struct near_adapter *adapter;
+
+       DBG("idx %d", idx);
+
+       adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx));
+       if (adapter == NULL)
+               return -ENODEV;
+
+       adapter->rf_mode = NEAR_ADAPTER_RF_MODE_IDLE;
+       rf_mode_changed(adapter);
+
+       if (g_hash_table_remove(adapter->tags,
+                       GINT_TO_POINTER(target_idx)) == TRUE) {
+               __near_adapter_tags_changed(idx);
+
+               return 0;
+       }
+
+       if (g_hash_table_remove(adapter->devices,
+                       GINT_TO_POINTER(target_idx)) == TRUE) {
+               __near_adapter_devices_changed(idx);
+
+               return 0;
+       }
+
+       return 0;
+}
+
+int __near_adapter_add_device(uint32_t idx, uint8_t *nfcid, uint8_t nfcid_len)
+{
+       struct near_adapter *adapter;
+       int ret;
+
+       DBG("idx %d", idx);
+
+       adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx));
+       if (adapter == NULL)
+               return -ENODEV;
+
+       adapter->polling = FALSE;
+       adapter->dep_up = TRUE;
+       adapter->rf_mode = NEAR_ADAPTER_RF_MODE_TARGET;
+       polling_changed(adapter);
+       rf_mode_changed(adapter);
+
+       ret = adapter_add_device(adapter, 0, nfcid, nfcid_len);
+       if (ret < 0)
+               return ret;
+
+       __near_adapter_devices_changed(idx);
+
+       return 0;
+}
+
+int __near_adapter_remove_device(uint32_t idx)
+{
+       struct near_adapter *adapter;
+       uint32_t device_idx = 0;
+
+       DBG("idx %d", idx);
+
+       adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx));
+       if (adapter == NULL)
+               return -ENODEV;
+
+       if (g_hash_table_remove(adapter->devices,
+                       GINT_TO_POINTER(device_idx)) == FALSE)
+               return 0;
+
+       adapter->rf_mode = NEAR_ADAPTER_RF_MODE_IDLE;
+       rf_mode_changed(adapter);
+       __near_adapter_devices_changed(idx);
+
+       adapter->dep_up = FALSE;
+
+       if (adapter->constant_poll == TRUE)
+               adapter_start_poll(adapter);
+
+       return 0;
+}
+
+static void adapter_flush_rx(struct near_adapter *adapter, int error)
+{
+       GList *list;
+
+       for (list = adapter->ioreq_list; list; list = list->next) {
+               struct near_adapter_ioreq *req = list->data;
+
+               if (req == NULL)
+                       continue;
+
+               req->cb(NULL, error, req->data);
+               g_free(req);
+       }
+
+       g_list_free(adapter->ioreq_list);
+       adapter->ioreq_list = NULL;
+}
+
+static gboolean execute_recv_cb(gpointer user_data)
+{
+       struct near_adapter_ioreq *req = user_data;
+
+       DBG("data %p", req->data);
+
+       req->cb(req->buf, req->len, req->data);
+
+       g_free(req);
+
+       return FALSE;
+}
+
+static gboolean adapter_recv_event(GIOChannel *channel, GIOCondition condition,
+                                                       gpointer user_data)
+{
+       struct near_adapter *adapter = user_data;
+       struct near_adapter_ioreq *req;
+       GList *first;
+       int sk;
+
+       DBG("condition 0x%x", condition);
+
+       if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               near_error("Error while reading NFC bytes");
+
+               adapter_flush_rx(adapter, -EIO);
+
+               near_adapter_disconnect(adapter->idx);
+
+               adapter->presence_timeout =
+                       g_timeout_add_seconds(2 * CHECK_PRESENCE_PERIOD,
+                                             check_presence, adapter);
+               return FALSE;
+       }
+
+       sk = g_io_channel_unix_get_fd(channel);
+       first = g_list_first(adapter->ioreq_list);
+       if (first == NULL)
+               return TRUE;
+
+       req = first->data;
+       req->len = recv(sk, req->buf, sizeof(req->buf), 0);
+
+       adapter->ioreq_list = g_list_remove(adapter->ioreq_list, req);
+
+       g_idle_add(execute_recv_cb, req);
+
+       return TRUE;
+}
+
+int near_adapter_connect(uint32_t idx, uint32_t target_idx, uint8_t protocol)
+{
+       struct near_adapter *adapter;
+       struct near_tag *tag;
+       struct sockaddr_nfc addr;
+       int err, sock;
+
+       DBG("idx %d", idx);
+
+       adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx));
+       if (adapter == NULL)
+               return -ENODEV;
+
+       if (adapter->tag_sock != -1)
+               return -EALREADY;
+
+       tag = g_hash_table_lookup(adapter->tags,
+                               GINT_TO_POINTER(target_idx));
+       if (tag == NULL)
+               return -ENOLINK;
+
+       sock = socket(AF_NFC, SOCK_SEQPACKET, NFC_SOCKPROTO_RAW);
+       if (sock == -1)
+               return sock;
+
+       addr.sa_family = AF_NFC;
+       addr.dev_idx = idx;
+       addr.target_idx = target_idx;
+       addr.nfc_protocol = protocol;
+
+       err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+       if (err) {
+               close(sock);
+               return err;
+       }
+
+       adapter->tag_sock = sock;
+       adapter->tag_link = tag;
+
+       if (adapter->channel == NULL)
+               adapter->channel = g_io_channel_unix_new(adapter->tag_sock);
+
+       g_io_channel_set_flags(adapter->channel, G_IO_FLAG_NONBLOCK, NULL);
+       g_io_channel_set_close_on_unref(adapter->channel, TRUE);
+
+       if (adapter->watch == 0)
+               adapter->watch = g_io_add_watch(adapter->channel,
+                               G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
+                                               adapter_recv_event, adapter);
+
+       return 0;
+}
+
+int near_adapter_disconnect(uint32_t idx)
+{
+       struct near_adapter *adapter;
+       uint32_t target_idx;
+       uint16_t tag_type;
+
+       DBG("idx %d", idx);
+
+       adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx));
+       if (adapter == NULL)
+               return -ENODEV;
+
+       DBG("link %p", adapter->tag_link);
+
+       if (adapter->tag_link == NULL)
+               return -ENOLINK;
+
+       tag_type = __near_tag_get_type(adapter->tag_link);
+       target_idx = near_tag_get_target_idx(adapter->tag_link);
+
+       DBG("tag type %d", tag_type);
+
+       __near_adapter_remove_target(adapter->idx, target_idx);
+
+       if (adapter->tag_sock == -1)
+               return -ENOLINK;
+
+       if (adapter->watch > 0) {
+               g_source_remove(adapter->watch);
+               adapter->watch = 0;
+       }
+
+       g_io_channel_unref(adapter->channel);
+       adapter->channel = NULL;
+       adapter->tag_sock = -1;
+       adapter->tag_link = NULL;
+
+       return 0;
+}
+
+int near_adapter_send(uint32_t idx, uint8_t *buf, size_t length,
+                       near_recv cb, void *data, near_release data_rel)
+{
+       struct near_adapter *adapter;
+       struct near_adapter_ioreq *req = NULL;
+       int err;
+
+       DBG("idx %d", idx);
+
+       adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx));
+       if (adapter == NULL) {
+               err = -ENODEV;
+               goto out_err;
+       }
+
+       if (adapter->tag_sock == -1 || adapter->tag_link == NULL) {
+               err = -ENOLINK;
+               goto out_err;
+       }
+
+       if (cb != NULL && adapter->watch != 0) {
+               req = g_try_malloc0(sizeof(*req));
+               if (req == NULL) {
+                       err = -ENOMEM;
+                       goto out_err;
+               }
+
+               DBG("req %p cb %p data %p", req, cb, data);
+
+               req->target_idx = near_tag_get_target_idx(adapter->tag_link);
+               req->cb = cb;
+               req->data = data;
+
+               adapter->ioreq_list =
+                       g_list_append(adapter->ioreq_list, req);
+       }
+
+       err = send(adapter->tag_sock, buf, length, 0);
+       if (err < 0)
+               goto out_err;
+
+       return err;
+
+out_err:
+       if (req != NULL) {
+               GList *last = g_list_last(adapter->ioreq_list);
+
+               g_free(req);
+               adapter->ioreq_list =
+                               g_list_delete_link(adapter->ioreq_list, last);
+       }
+
+       if (data_rel != NULL)
+               return (*data_rel)(err, data);
+
+       return err;
+}
+
+static void adapter_listen(gpointer key, gpointer value, gpointer user_data)
+{
+       struct near_adapter *adapter = value;
+       struct near_device_driver *driver = user_data;
+
+       DBG("%s", adapter->path);
+
+       if (adapter->path == NULL)
+               return;
+
+       driver->listen(adapter->idx, device_read_cb);
+}
+
+void __near_adapter_listen(struct near_device_driver *driver)
+{
+       g_hash_table_foreach(adapter_hash, adapter_listen, driver);
+}
+
+int __near_adapter_init(void)
+{
+       DBG("");
+
+       connection = near_dbus_get_connection();
+
+       adapter_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                                       NULL, free_adapter);
+
+       return 0;
+}
+
+void __near_adapter_cleanup(void)
+{
+       DBG("");
+
+       g_hash_table_destroy(adapter_hash);
+       adapter_hash = NULL;
+}
diff --git a/src/agent.c b/src/agent.c
new file mode 100644 (file)
index 0000000..5fe34cf
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <gdbus.h>
+
+#include "near.h"
+
+static DBusConnection *connection = NULL;
+static GHashTable *ndef_app_hash;
+
+struct near_ndef_agent {
+       char *sender;
+       char *path;
+       char *record_type;
+       guint watch;
+};
+
+static void ndef_agent_free(gpointer data)
+{
+       DBusMessage *message;
+       struct near_ndef_agent *agent = data;
+
+       DBG("");
+
+       if (agent == NULL)
+               return;
+
+       DBG("%s %s %s", agent->sender, agent->path, NFC_NDEF_AGENT_INTERFACE);
+
+       message = dbus_message_new_method_call(agent->sender, agent->path,
+                                       NFC_NDEF_AGENT_INTERFACE, "Release");
+       if (message == NULL)
+               return;
+
+       dbus_message_set_no_reply(message, TRUE);
+
+       g_dbus_send_message(connection, message);
+
+       g_dbus_remove_watch(connection, agent->watch);
+
+       g_free(agent->sender);
+       g_free(agent->path);
+}
+
+static void ndef_agent_disconnect(DBusConnection *conn, void *user_data)
+{
+       struct near_ndef_agent *agent = user_data;
+
+       DBG("agent %s disconnected", agent->path);
+
+       g_hash_table_remove(ndef_app_hash, agent->record_type);
+}
+
+static void append_record_path(DBusMessageIter *iter, void *user_data)
+{
+       GList *records = user_data, *list;
+       struct near_ndef_record *record;
+       char *path;
+
+       for (list = records; list; list = list->next) {
+               record = list->data;
+
+               path = __near_ndef_record_get_path(record);
+               if (path == NULL)
+                       continue;
+
+               dbus_message_iter_append_basic(iter,
+                                       DBUS_TYPE_STRING, &path);
+       }
+}
+
+static void append_ndef(DBusMessageIter *iter, void *user_data)
+{
+       GList *records = user_data;
+
+       __near_ndef_append_records(iter, records);
+}
+
+static void ndef_agent_push_records(struct near_ndef_agent *agent,
+                                                       GList *records)
+{
+       DBusMessageIter iter, dict;
+       DBusMessage *message;
+
+       DBG("");
+
+       if (agent->sender == NULL || agent->path == NULL)
+               return;
+
+       DBG("Sending NDEF to %s %s", agent->path, agent->sender);
+
+       message = dbus_message_new_method_call(agent->sender, agent->path,
+                                       NFC_NDEF_AGENT_INTERFACE,
+                                       "GetNDEF");
+       if (message == NULL)
+               return;
+
+       dbus_message_iter_init_append(message, &iter);
+
+       near_dbus_dict_open(&iter, &dict);
+       near_dbus_dict_append_array(&dict, "Records",
+                               DBUS_TYPE_STRING, append_record_path, records);
+       near_dbus_dict_append_array(&dict, "NDEF",
+                               DBUS_TYPE_BYTE, append_ndef, records);
+       near_dbus_dict_close(&iter, &dict);
+
+       DBG("sending...");
+
+       dbus_message_set_no_reply(message, TRUE);
+
+       g_dbus_send_message(connection, message);
+}
+
+void __near_agent_ndef_parse_records(GList *records)
+{
+       GList *list;
+       struct near_ndef_record *record;
+       struct near_ndef_agent *agent;
+       char *type;
+
+       DBG("");
+
+       for (list = records, agent = NULL; list; list = list->next) {
+               record = list->data;
+               type  = __near_ndef_record_get_type(record);
+
+               if (type == NULL)
+                       continue;
+
+               DBG("Looking for type %s", type);
+
+               agent = g_hash_table_lookup(ndef_app_hash, type);
+               if (agent != NULL)
+                       break;
+       }
+
+       if (agent == NULL)
+               return;
+
+       ndef_agent_push_records(agent, records);
+
+       return;
+}
+
+int __near_agent_ndef_register(const char *sender, const char *path,
+                                               const char *record_type)
+{
+       struct near_ndef_agent *agent;
+
+       DBG("%s registers path %s for %s", sender, path, record_type);
+
+       if (g_hash_table_lookup(ndef_app_hash, record_type) != NULL)
+               return -EEXIST;
+
+       agent = g_try_malloc0(sizeof(struct near_ndef_agent));
+       if (agent == NULL)
+               return -ENOMEM;
+
+       agent->sender = g_strdup(sender);
+       agent->path = g_strdup(path);
+       agent->record_type = g_strdup(record_type);
+       
+       if (agent->sender == NULL || agent->path == NULL ||
+           agent->record_type == NULL) {
+               g_free(agent);
+               return -ENOMEM;
+       }
+
+       agent->watch = g_dbus_add_disconnect_watch(connection, sender,
+                                               ndef_agent_disconnect, agent, NULL);
+       g_hash_table_insert(ndef_app_hash, agent->record_type, agent);
+
+       return 0;
+}
+
+int __near_agent_ndef_unregister(const char *sender, const char *path,
+                                               const char *record_type)
+{
+       struct near_ndef_agent *agent;
+
+       DBG("sender %s path %s type %s", sender, path, record_type);
+
+       agent = g_hash_table_lookup(ndef_app_hash, record_type);
+       if (agent == NULL)
+               return -EINVAL;
+
+       if (strcmp(agent->path, path) != 0 || strcmp(agent->sender, sender) != 0)
+               return -EINVAL;
+
+       g_hash_table_remove(ndef_app_hash, record_type);
+
+       return 0;
+}
+
+static guint handover_agent_watch = 0;
+static gchar *handover_agent_path = NULL;
+static gchar *handover_agent_sender = NULL;
+
+static void handover_agent_free(void)
+{
+       if (handover_agent_watch > 0) {
+               g_dbus_remove_watch(connection, handover_agent_watch);
+               handover_agent_watch = 0;
+       }
+
+       g_free(handover_agent_sender);
+       handover_agent_sender = NULL;
+
+       g_free(handover_agent_path);
+       handover_agent_path = NULL;
+}
+
+static void handover_agent_disconnect(DBusConnection *conn, void *data)
+{
+       DBG("data %p", data);
+
+       handover_agent_watch = 0;
+
+       handover_agent_free();
+}
+
+static void handover_agent_release(void)
+{
+       DBusMessage *message;
+
+       if (handover_agent_watch == 0)
+               return;
+
+       message = dbus_message_new_method_call(handover_agent_sender,
+                                               handover_agent_path,
+                                               "org.neard.HandoverAgent",
+                                               "Release");
+       if (message != NULL)
+               g_dbus_send_message(connection, message);
+
+       handover_agent_free();
+}
+
+int __near_agent_handover_register(const char *sender, const char *path)
+{
+       DBG("sender %s path %s", sender, path);
+
+       if (handover_agent_path != NULL)
+               return -EEXIST;
+
+       handover_agent_watch = g_dbus_add_disconnect_watch(connection, sender,
+                                       handover_agent_disconnect, NULL, NULL);
+       if (handover_agent_watch == 0)
+               return -ENOMEM;
+
+       handover_agent_sender = g_strdup(sender);
+       handover_agent_path = g_strdup(path);
+
+       return 0;
+}
+
+int __near_agent_handover_unregister(const char *sender, const char *path)
+{
+       DBG("sender %s path %s", sender, path);
+
+       if (handover_agent_path == NULL)
+               return -ESRCH;
+
+       handover_agent_free();
+
+       return 0;
+}
+
+int __near_agent_init(void)
+{
+       DBG("");
+
+       connection = near_dbus_get_connection();
+       if (connection == NULL)
+               return -1;
+
+       ndef_app_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, ndef_agent_free);
+
+       return 0;
+}
+
+void __near_agent_cleanup(void)
+{
+       DBG("");
+
+       g_hash_table_destroy(ndef_app_hash);
+       ndef_app_hash = NULL;
+
+       handover_agent_release();
+
+       dbus_connection_unref(connection);
+}
diff --git a/src/bluetooth.c b/src/bluetooth.c
new file mode 100644 (file)
index 0000000..63e76da
--- /dev/null
@@ -0,0 +1,1062 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gdbus.h>
+
+#include "near.h"
+
+#define BLUEZ_SERVICE                  "org.bluez"
+#define MANAGER_INTF                   BLUEZ_SERVICE ".Manager"
+#define ADAPTER_INTF                   BLUEZ_SERVICE ".Adapter"
+#define OOB_INTF                       BLUEZ_SERVICE ".OutOfBand"
+#define DEFAULT_ADAPTER                        "DefaultAdapter"
+#define ADAPTER_REMOVED                        "AdapterRemoved"
+#define DEFAULT_ADAPTER_CHANGED                "DefaultAdapterChanged"
+#define MANAGER_PATH                   "/"
+#define OOB_AGENT                      "/org/neard/agent/neard_oob"
+
+#define BT_NOINPUTOUTPUT               "NoInputNoOutput"
+#define BT_DISPLAY_YESNO               "DisplayYesNo"
+
+/* BT EIR list */
+#define EIR_UUID128_ALL                0x07 /* 128-bit UUID, all listed */
+#define EIR_NAME_SHORT         0x08 /* shortened local name */
+#define EIR_NAME_COMPLETE      0x09 /* complete local name */
+
+/* Specific OOB EIRs */
+#define EIR_CLASS_OF_DEVICE    0x0D  /* class of device */
+#define EIR_SP_HASH            0x0E  /* simple pairing hash C */
+#define EIR_SP_RANDOMIZER      0x0F  /* simple pairing randomizer R */
+/* Optional EIRs */
+#define EIR_DEVICE_ID          0x10  /* device ID */
+#define EIR_SECURITY_MGR_FLAGS 0x11  /* security manager flags */
+
+#define EIR_SIZE_LEN           1
+#define EIR_HEADER_LEN         (EIR_SIZE_LEN + 1)
+#define BT_ADDRESS_SIZE                6
+#define COD_SIZE               3
+#define OOB_SP_SIZE            16
+
+#define get_unaligned(ptr)                     \
+({                                             \
+       struct __attribute__((packed)) {        \
+               typeof(*(ptr)) __v;             \
+       } *__p = (typeof(__p)) (ptr);           \
+       __p->__v;                               \
+})
+
+struct near_oob_data {
+       char *def_adapter;
+
+       char *bd_addr;          /* oob mandatory */
+
+       /* optional */
+       char *bt_name;                  /* short or long name */
+       uint8_t bt_name_len;
+       int class_of_device;            /* Class of device */
+       near_bool_t powered;
+       near_bool_t pairable;
+       near_bool_t discoverable;
+       uint8_t *uuids;
+       int uuids_len;
+
+       uint8_t *spair_hash;                    /* OOB hash Key */
+       uint8_t *spair_randomizer;              /* OOB randomizer key */
+       uint8_t authentication[OOB_SP_SIZE];    /* On BT 2.0 */
+       uint8_t security_manager_oob_flags;     /* see BT Core 4.0 */
+};
+
+static DBusConnection *bt_conn;
+static struct near_oob_data bt_def_oob_data;
+
+static int bt_do_pairing(struct near_oob_data *oob);
+
+static void __bt_eir_free(struct near_oob_data *oob)
+{
+       DBG("");
+
+       if (oob->def_adapter != NULL) {
+               g_free(oob->def_adapter);
+               oob->def_adapter = NULL;
+       }
+
+       if (oob->bd_addr != NULL) {
+               g_free(oob->bd_addr);
+               oob->bd_addr = NULL;
+       }
+
+       if (oob->bt_name != NULL) {
+               g_free(oob->bt_name);
+               oob->bt_name = NULL;
+       }
+
+       if (oob->spair_hash != NULL) {
+               g_free(oob->spair_hash);
+               oob->spair_hash = NULL;
+       }
+
+       if (oob->spair_randomizer != NULL) {
+               g_free(oob->spair_randomizer);
+               oob->spair_randomizer = NULL;
+       }
+
+
+}
+
+static void bt_eir_free(struct near_oob_data *oob)
+{
+       __bt_eir_free(oob);
+
+       g_free(oob);
+}
+
+/* D-Bus helper functions */
+static int bt_generic_call(DBusConnection *conn,
+               struct near_oob_data *oob,              /* user data */
+               const char *dest,                       /* method call */
+               const char *path,
+               const char *interface,
+               const char *method,
+               DBusPendingCallNotifyFunction bt_cb,    /* callback */
+               int type, ...)                          /* params */
+{
+       DBusMessage *msg;
+       DBusPendingCall *pending;
+       va_list args;
+       int err;
+
+       DBG("%s", method);
+
+       msg = dbus_message_new_method_call(dest, path, interface, method);
+
+       if (msg == NULL) {
+               near_error("Unable to allocate new D-Bus %s message", method);
+               err = -ENOMEM;
+       }
+
+       va_start(args, type);
+
+       if (!dbus_message_append_args_valist(msg, type, args)) {
+               va_end(args);
+               err = -EIO;
+               goto error_done;
+       }
+       va_end(args);
+
+       if (!dbus_connection_send_with_reply(conn, msg, &pending, -1)) {
+               near_error("Sending %s failed", method);
+               err = -EIO;
+               goto error_done;
+       }
+
+       if (pending == NULL) {
+               near_error("D-Bus connection not available");
+               err = -EIO;
+               goto error_done;
+       }
+
+       /* Prepare for notification */
+       dbus_pending_call_set_notify(pending, bt_cb, oob, NULL);
+       err = 0 ;
+
+error_done:
+       dbus_message_unref(msg);
+       return err;
+}
+
+static void bt_create_paired_device_cb(DBusPendingCall *pending,
+                                       void *user_data)
+{
+       DBusMessage *reply;
+       DBusError   error;
+       struct near_oob_data *oob = user_data;
+
+       DBG("");
+
+       reply = dbus_pending_call_steal_reply(pending);
+       if (reply == NULL)
+               return;
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply)) {
+               near_error("%s", error.message);
+               dbus_error_free(&error);
+               goto cb_done;
+       }
+
+       DBG("Successful pairing");
+
+cb_done:
+       /* task completed - clean memory*/
+       bt_eir_free(oob);
+
+       dbus_message_unref(reply);
+       dbus_pending_call_unref(pending);
+
+       return;
+}
+
+static int bt_create_paired_device(DBusConnection *conn,
+                               struct near_oob_data *oob,
+                               const char *capabilities)
+{
+       const char *agent_path = OOB_AGENT;
+
+       return bt_generic_call(bt_conn, oob, BLUEZ_SERVICE,
+                       oob->def_adapter, ADAPTER_INTF, "CreatePairedDevice",
+                       bt_create_paired_device_cb,
+                       /* params */
+                       DBUS_TYPE_STRING, &oob->bd_addr,
+                       DBUS_TYPE_OBJECT_PATH, &agent_path,
+                       DBUS_TYPE_STRING, &capabilities,
+                       DBUS_TYPE_INVALID);
+
+}
+
+static void bt_oob_add_remote_data_cb(DBusPendingCall *pending, void *user_data)
+{
+       DBusMessage *reply;
+       DBusError   error;
+       struct near_oob_data *oob = user_data;
+
+       DBG("");
+
+       reply = dbus_pending_call_steal_reply(pending);
+       if (reply == NULL)
+               return;
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply))
+               goto cb_fail;
+
+       near_info("OOB data added");
+
+       dbus_message_unref(reply);
+       dbus_pending_call_unref(pending);
+
+       /* Jump to the next: Pairing !!!*/
+       DBG("Try to pair devices...");
+       bt_create_paired_device(bt_conn, oob, BT_DISPLAY_YESNO);
+       return;
+
+cb_fail:
+       near_error("%s", error.message);
+       dbus_error_free(&error);
+
+       bt_eir_free(oob);
+
+       dbus_message_unref(reply);
+       dbus_pending_call_unref(pending);
+
+       return;
+}
+
+static int bt_oob_add_remote_data(DBusConnection *conn,
+                               struct near_oob_data *oob)
+{
+       int16_t hash_len = 16;
+       int16_t rdm_len = 16;
+
+       return bt_generic_call(bt_conn, oob, BLUEZ_SERVICE,
+                       oob->def_adapter, OOB_INTF, "AddRemoteData",
+                       bt_oob_add_remote_data_cb,
+                       /* params */
+                       DBUS_TYPE_STRING, &oob->bd_addr,
+                       DBUS_TYPE_ARRAY,
+                               DBUS_TYPE_BYTE, &oob->spair_hash, hash_len,
+                       DBUS_TYPE_ARRAY,
+                               DBUS_TYPE_BYTE, &oob->spair_randomizer, rdm_len,
+                       DBUS_TYPE_INVALID);
+}
+
+/* Pairing: JustWorks or OOB  */
+static int bt_do_pairing(struct near_oob_data *oob)
+{
+       int err = 0;
+
+       DBG("%s", oob->bd_addr);
+
+       /* Is this a *real* oob pairing or a "JustWork" */
+       if ((oob->spair_hash) && (oob->spair_randomizer))
+               err = bt_oob_add_remote_data(bt_conn, oob);
+       else
+               err = bt_create_paired_device(bt_conn, oob,
+                               BT_NOINPUTOUTPUT);
+
+       if (err < 0)
+               near_error("Pairing failed. Err[%d]", err);
+
+       return err;
+}
+
+/*
+ */
+static int extract_properties(DBusMessage *reply,
+               struct near_oob_data *oob)
+{
+       char *data = NULL;
+       int idata;
+       int i, j;
+
+       DBusMessageIter array, dict;
+
+       if (dbus_message_iter_init(reply, &array) == FALSE)
+               return -1;
+
+       if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
+               return -1;
+
+       dbus_message_iter_recurse(&array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (g_str_equal(key, "Address") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &data);
+
+                       /* Now, fill the local struct */
+                       oob->bd_addr = g_try_malloc0(BT_ADDRESS_SIZE);
+                       if (oob->bd_addr == NULL)
+                               return -ENOMEM;
+
+                       /* Address is like: "ff:ee:dd:cc:bb:aa" */
+                       for (i = 5, j = 0 ; i >= 0; i--, j += 3)
+                               oob->bd_addr[i] = strtol(data + j, NULL, 16);
+                       DBG("local address: %s", data);
+
+               } else if (g_str_equal(key, "Name") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &data);
+                       oob->bt_name = g_strdup(data);
+                       if (oob->bt_name != NULL) {
+                               oob->bt_name_len = strlen(oob->bt_name);
+                               DBG("local name: %s", oob->bt_name);
+                       }
+
+               } else if (g_str_equal(key, "Class") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &idata);
+                       oob->class_of_device = idata;
+
+               } else if (g_str_equal(key, "Powered") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &idata);
+                       oob->powered = idata;
+
+               } else if (g_str_equal(key, "Discoverable") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &idata);
+                       oob->discoverable = idata;
+
+               } else if (g_str_equal(key, "Pairable") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &idata);
+                       oob->pairable = idata;
+
+               } else if (g_str_equal(key, "UUIDs") == TRUE) {
+                       oob->uuids_len = sizeof(value);
+                       oob->uuids = g_try_malloc0(oob->uuids_len);
+                       if (oob->uuids == NULL)
+                               return -ENOMEM;
+                       memcpy(oob->uuids, &value, oob->uuids_len);
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+
+       return 0;
+}
+
+static int bt_parse_properties(DBusMessage *reply, void *user_data)
+{
+       struct near_oob_data *bt_props = user_data;
+
+       DBG("");
+
+       /* Free datas */
+       g_free(bt_props->bd_addr);
+       g_free(bt_props->bt_name);
+
+       /* Grab properties from dbus */
+       if (extract_properties(reply, bt_props) < 0)
+               goto fail;
+
+       return 0;
+
+fail:
+       g_free(bt_props->bd_addr);
+       bt_props->bd_addr = NULL;
+
+       g_free(bt_props->bt_name);
+       bt_props->bt_name = NULL;
+
+       return -ENOMEM;
+}
+
+
+/* Get default local adapter properties */
+static void bt_get_properties_cb(DBusPendingCall *pending, void *user_data)
+{
+       struct near_oob_data *bt_props = user_data;
+       DBusMessage *reply;
+       DBusError   error;
+       int err;
+
+       DBG("");
+
+       reply = dbus_pending_call_steal_reply(pending);
+       if (reply == NULL)
+               return;
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply))
+               goto cb_fail;
+
+       err = bt_parse_properties(reply, bt_props);
+       if (err < 0)
+               near_error("Problem parsing local properties %d", err);
+       else
+               DBG("Get Properties complete: %s", bt_props->def_adapter);
+
+       /* clean */
+       dbus_message_unref(reply);
+       dbus_pending_call_unref(pending);
+       return;
+
+cb_fail:
+       near_error("%s", error.message);
+       dbus_error_free(&error);
+
+       dbus_message_unref(reply);
+       dbus_pending_call_unref(pending);
+
+       return;
+}
+
+static void bt_get_default_adapter_cb(DBusPendingCall *pending, void *user_data)
+{
+       struct near_oob_data *bt_props = user_data;
+       DBusMessage *reply;
+       DBusError   error;
+       gchar *path;
+
+       DBG("");
+
+       reply = dbus_pending_call_steal_reply(pending);
+       if (reply == NULL)
+               return;
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply))
+               goto cb_fail;
+
+       if (dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH,
+                                       &path, DBUS_TYPE_INVALID) == FALSE)
+               goto cb_fail;
+
+       /* Save the default adapter */
+       bt_props->def_adapter = g_strdup(path);
+       DBG("Using default adapter %s", bt_props->def_adapter);
+
+       /* clean */
+       dbus_message_unref(reply);
+       dbus_pending_call_unref(pending);
+
+       /* Jump on getAdapterProperties */
+       bt_generic_call(bt_conn, bt_props,
+                       BLUEZ_SERVICE,
+                       bt_props->def_adapter,
+                       ADAPTER_INTF, "GetProperties",
+                       bt_get_properties_cb,
+                       DBUS_TYPE_INVALID);
+       return;
+
+cb_fail:
+       near_error("%s", error.message);
+       dbus_error_free(&error);
+
+       dbus_message_unref(reply);
+       dbus_pending_call_unref(pending);
+
+       return;
+}
+
+static int bt_refresh_adapter_props(DBusConnection *conn, void *user_data)
+{
+       DBG("%p %p", conn, user_data);
+
+       return bt_generic_call(conn, user_data,
+                       BLUEZ_SERVICE,
+                       MANAGER_PATH, MANAGER_INTF,
+                       DEFAULT_ADAPTER,
+                       bt_get_default_adapter_cb,
+                       DBUS_TYPE_INVALID);
+}
+
+/* Parse and fill the bluetooth oob information block */
+static void bt_parse_eir(uint8_t *eir_data, uint16_t eir_data_len,
+                               struct near_oob_data *oob, uint16_t *props)
+{
+       char *tmp;
+       uint16_t len = 0;
+
+       DBG("total len: %u", eir_data_len);
+
+       while (len < eir_data_len - 1) {
+               uint8_t eir_len = eir_data[0];  /* EIR field length */
+               uint8_t eir_code;               /* EIR field type*/
+               uint8_t data_len;               /* EIR data length */
+               uint8_t *data;
+
+               /* check for early termination */
+               if (eir_len == 0)
+                       break;
+
+               len += eir_len + 1;
+
+               /* Do not continue EIR Data parsing if got incorrect length */
+               if (len > eir_data_len)
+                       break;
+
+               data_len = eir_len - 1;
+
+               eir_code = eir_data[1]; /* EIR code */
+               data = &eir_data[2];
+
+               DBG("type 0x%.2X data_len %u", eir_code, data_len);
+
+               switch (eir_code) {
+               case EIR_NAME_SHORT:
+               case EIR_NAME_COMPLETE:
+                       oob->bt_name = g_try_malloc0(data_len + 1); /* eos */
+                       if (oob->bt_name) {
+                               oob->bt_name_len = data_len;
+                               memcpy(oob->bt_name, data, oob->bt_name_len);
+                               oob->bt_name[data_len] = 0;     /* end str*/
+                       }
+                       break;
+
+               case EIR_CLASS_OF_DEVICE:
+                       tmp = g_strdup_printf("%02X%02X%02X",
+                                       *data, *(data + 1), *(data + 2));
+                       if (tmp != NULL) {
+                               oob->class_of_device = strtol(tmp, NULL, 16);
+                               *props |= OOB_PROPS_COD;
+                       }
+                       g_free(tmp);
+                       break;
+
+               case EIR_SP_HASH:
+                       oob->spair_hash = g_try_malloc0(OOB_SP_SIZE);
+                       if (oob->spair_hash) {
+                               memcpy(oob->spair_hash, data, OOB_SP_SIZE);
+                               *props |= OOB_PROPS_SP_HASH;
+                       }
+                       break;
+
+               case EIR_SP_RANDOMIZER:
+                       oob->spair_randomizer = g_try_malloc0(OOB_SP_SIZE);
+                       if (oob->spair_randomizer) {
+                               memcpy(oob->spair_randomizer,
+                                               data, OOB_SP_SIZE);
+                               *props |= OOB_PROPS_SP_RANDOM;
+                       }
+                       break;
+
+               case EIR_SECURITY_MGR_FLAGS:
+                       oob->security_manager_oob_flags = *data;
+                       break;
+
+               case EIR_UUID128_ALL:
+                       /* TODO: Process uuids128
+                        * */
+                       break;
+
+               default:        /* ignore and skip */
+                       near_error("Unknown EIR x%02x (len: %d)", eir_code,
+                                                               eir_len);
+                       break;
+               }
+               /* Next eir */
+               eir_data += eir_len + 1;
+       }
+}
+
+/*
+ * Because of some "old" implementation, "version" will help
+ * to determine the record data structure.
+ * Some specifications are proprietary (eg. "short mode")
+ * and are not fully documented.
+ * mime_properties is a bitmask and should reflect the fields found in
+ * the incoming oob.
+ */
+int __near_bluetooth_parse_oob_record(uint8_t version, uint8_t *bt_data,
+                               uint16_t *mime_properties, near_bool_t pair)
+{
+       struct near_oob_data *oob;
+       uint16_t bt_oob_data_size;
+       uint8_t *ptr = bt_data;
+       uint8_t marker;
+       char *tmp;
+
+       DBG("");
+
+       oob = g_try_malloc0(sizeof(struct near_oob_data));
+
+       if (version == BT_MIME_V2_1) {
+               /*
+                * Total OOB data size (including size bytes)
+                * Some implementations (e.g. Android 4.1) stores
+                * the data_size in big endian but NDEF forum spec (BT Secure
+                * Simple Pairing) requires a little endian. At the same time,
+                * the NDEF forum NDEF spec define a payload length as single
+                * byte (and the payload size IS the oob data size).
+                */
+               bt_oob_data_size =
+                       GUINT16_FROM_LE(get_unaligned((uint16_t *) bt_data));
+               if (bt_oob_data_size > 0xFF)    /* Big Endian */
+                       bt_oob_data_size = GUINT16_FROM_BE(bt_oob_data_size);
+
+               bt_oob_data_size -= 2 ; /* remove oob datas size len */
+
+               /* First item: BD_ADDR (mandatory) */
+               ptr = &bt_data[2];
+               oob->bd_addr = g_strdup_printf("%02X:%02X:%02X:%02X:%02X:%02X",
+                               ptr[5], ptr[4], ptr[3], ptr[2], ptr[1], ptr[0]);
+
+               /* Skip to the next element (optional) */
+               ptr += BT_ADDRESS_SIZE;
+               bt_oob_data_size -= BT_ADDRESS_SIZE ;
+
+               if (bt_oob_data_size)
+                       bt_parse_eir(ptr, bt_oob_data_size, oob,
+                                                       mime_properties);
+       } else if (version == BT_MIME_V2_0) {
+               marker = *ptr++;        /* could be '$' */
+
+               oob->bd_addr = g_strdup_printf(
+                               "%02X:%02X:%02X:%02X:%02X:%02X",
+                               ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]);
+               ptr = ptr + BT_ADDRESS_SIZE;
+
+               /* Class of device */
+               tmp = g_strdup_printf("%02X%02X%02X",
+                               *ptr, *(ptr + 1), *(ptr + 2));
+               if (tmp != NULL)
+                       oob->class_of_device = strtol(tmp, NULL, 16);
+               g_free(tmp);
+
+               ptr = ptr + 3;
+
+               /* "Short mode" seems to use a 4 bytes code
+                * instead of 16 bytes...
+                */
+               if (marker == '$') {   /* Short NFC */
+                       memcpy(oob->authentication, ptr, 4);
+                       ptr = ptr + 4;
+               } else {
+                       memcpy(oob->authentication, ptr, 16);
+                       ptr = ptr + 16;
+               }
+
+               /* get the device name */
+               oob->bt_name_len = *ptr++;
+               oob->bt_name = g_try_malloc0(oob->bt_name_len+1);
+               if (oob->bt_name) {
+                       memcpy(oob->bt_name, ptr, oob->bt_name_len);
+                       oob->bt_name[oob->bt_name_len+1] = 0;
+               }
+               ptr = ptr + oob->bt_name_len;
+       } else {
+               return -EINVAL;
+       }
+
+       if (pair == FALSE)
+               return 0;
+
+       /* check and get the default adapter */
+       oob->def_adapter = g_strdup(bt_def_oob_data.def_adapter);
+       if (oob->def_adapter == NULL) {
+               near_error("bt_get_default_adapter failed");
+               bt_eir_free(oob);
+               return -EIO;
+       }
+
+       return  bt_do_pairing(oob);
+}
+
+int __near_bluetooth_pair(void *data)
+{
+       struct near_oob_data *oob = data;
+
+       /* check and get the default adapter */
+       oob->def_adapter = g_strdup(bt_def_oob_data.def_adapter);
+       if (oob->bt_name == NULL) {
+               near_error("bt_get_default_adapter failed: %d", -EIO);
+               bt_eir_free(oob);
+               return -EIO;
+       }
+
+       return bt_do_pairing(oob);
+}
+
+/* This function is synchronous as oob datas change on each session */
+static int bt_sync_oob_readlocaldata(DBusConnection *conn, char *adapter_path,
+               char *spair_hash,
+               char *spair_randomizer)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+       int hash_len, rndm_len;
+
+       message = dbus_message_new_method_call(BLUEZ_SERVICE, adapter_path,
+                       OOB_INTF, "ReadLocalData");
+       if (!message)
+               return 0;
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(conn,
+                       message, -1, &error);
+
+       dbus_message_unref(message);
+
+       if (!reply) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       near_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else {
+                       near_error("Failed to set property");
+               }
+               return 0;
+       }
+
+       if (dbus_message_get_args(reply, NULL,
+                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, spair_hash, &hash_len,
+                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+                                               spair_randomizer, &rndm_len,
+                       DBUS_TYPE_INVALID) == FALSE)
+               goto done;
+
+       if ((hash_len != OOB_SP_SIZE) || (rndm_len != OOB_SP_SIZE)) {
+               DBG("no OOB data found !");
+               goto done;
+       }
+
+       dbus_message_unref(reply);
+       DBG("OOB data found");
+       return hash_len;
+
+done:
+       dbus_message_unref(reply);
+       return 0;
+}
+
+/*
+ * External API to get bt properties
+ * Prepare a "real" oob datas block
+ * mime_props is a bitmask we use to add or not specific fields in the
+ * oob frame (e.g.: OOB keys)
+ * */
+uint8_t *__near_bluetooth_local_get_properties(int *bt_data_len,
+                                               uint16_t mime_props)
+{
+       uint8_t *bt_oob_block = NULL;
+       uint16_t bt_oob_block_size = 0;
+       int max_block_size;
+       uint8_t offset;
+
+       char hash[OOB_SP_SIZE];
+       char random[OOB_SP_SIZE];
+
+       /* Check adapter datas */
+       if (bt_def_oob_data.def_adapter == NULL) {
+               near_error("No bt adapter info");
+               goto fail;
+       }
+
+       /* Prepare the BT block */
+       max_block_size = sizeof(uint16_t) +             /* stored oob size */
+                       BT_ADDRESS_SIZE +
+                       EIR_HEADER_LEN + bt_def_oob_data.bt_name_len +
+                       EIR_HEADER_LEN + COD_SIZE;      /* class */
+
+       /* Should we add oob pairing keys ?*/
+       if (mime_props & OOB_PROPS_SP) {
+               max_block_size += (EIR_HEADER_LEN + OOB_SP_SIZE + /* oob hash */
+                               EIR_HEADER_LEN + OOB_SP_SIZE); /* oob random */
+       }
+
+       bt_oob_block_size = sizeof(uint16_t)    /* stored oob size */
+                       + BT_ADDRESS_SIZE;      /* device address */
+
+       bt_oob_block = g_try_malloc0(max_block_size);
+       if (bt_oob_block == NULL)
+               goto fail;
+       offset = sizeof(uint16_t); /* Skip size...will be filled later */
+
+       /* Now prepare data frame */
+       memcpy(bt_oob_block + offset, bt_def_oob_data.bd_addr, BT_ADDRESS_SIZE);
+       offset += BT_ADDRESS_SIZE;
+
+       /* bt name */
+       if (bt_def_oob_data.bt_name != NULL) {
+               bt_oob_block_size += (bt_def_oob_data.bt_name_len +
+                                                               EIR_HEADER_LEN);
+
+               bt_oob_block[offset++] = bt_def_oob_data.bt_name_len +
+                                                               EIR_SIZE_LEN;
+               bt_oob_block[offset++] = EIR_NAME_COMPLETE; /* EIR data type */
+               memcpy(bt_oob_block + offset, bt_def_oob_data.bt_name,
+                                       bt_def_oob_data.bt_name_len);
+               offset += bt_def_oob_data.bt_name_len;
+       }
+
+       /* CoD */
+       bt_oob_block_size += COD_SIZE +  EIR_HEADER_LEN;
+
+       bt_oob_block[offset++] = COD_SIZE + EIR_SIZE_LEN;
+       bt_oob_block[offset++] = EIR_CLASS_OF_DEVICE;
+
+       memcpy(bt_oob_block + offset,
+                       (uint8_t *)&bt_def_oob_data.class_of_device, COD_SIZE);
+       offset += COD_SIZE;
+
+       /* The following data are generated dynamically
+        * so we have to read the local oob data
+        * */
+       /* Should we add oob pairing keys */
+       if ((mime_props & OOB_PROPS_SP) == 0)
+               goto out;
+
+       if (bt_sync_oob_readlocaldata(bt_conn, bt_def_oob_data.def_adapter,
+                                       hash, random) == OOB_SP_SIZE) {
+               bt_oob_block_size += 2 * (OOB_SP_SIZE + EIR_HEADER_LEN);
+
+               /* OOB datas */
+               if (hash != NULL) {
+                       bt_oob_block[offset++] = OOB_SP_SIZE + EIR_SIZE_LEN;
+                       bt_oob_block[offset++] = EIR_SP_HASH;
+                       memcpy(bt_oob_block + offset, hash, OOB_SP_SIZE);
+                       offset += OOB_SP_SIZE;
+               }
+
+               if (random != NULL) {
+                       bt_oob_block[offset++] = OOB_SP_SIZE + EIR_SIZE_LEN;
+                       bt_oob_block[offset++] = EIR_SP_RANDOMIZER;
+                       memcpy(bt_oob_block + offset, random, OOB_SP_SIZE);
+                       offset += OOB_SP_SIZE;
+               }
+       }
+
+out:
+       *(uint16_t *)bt_oob_block = bt_oob_block_size ;
+       *bt_data_len = bt_oob_block_size;
+
+       return bt_oob_block;
+
+fail:
+       g_free(bt_oob_block);
+       return NULL;
+}
+
+static void bt_connect(DBusConnection *conn, void *user_data)
+{
+
+       DBG("connection %p with %p", conn, user_data);
+       if (bt_refresh_adapter_props(conn, user_data) < 0)
+               near_error("bt_get_default_adapter failed");
+
+       return;
+}
+
+static void bt_disconnect(DBusConnection *conn, void *user_data)
+{
+       DBG("%p", conn);
+
+       __bt_eir_free(user_data);
+}
+
+/* BT adapter removed handler */
+static gboolean bt_adapter_removed(DBusConnection *conn,
+                                       DBusMessage *message,
+                                       void *user_data)
+{
+       DBusMessageIter iter;
+       struct near_oob_data *bt_props = user_data;
+       const char *adapter_path;
+
+       DBG("");
+
+       if (bt_props->def_adapter == NULL)
+               return TRUE;
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &adapter_path);
+
+       if (g_strcmp0(adapter_path, bt_props->def_adapter) == 0) {
+               near_info("Remove the default adapter [%s]", adapter_path);
+
+               __bt_eir_free(bt_props);
+               bt_props->def_adapter = NULL;
+       }
+
+       return TRUE;
+}
+
+/* BT default adapter changed handler */
+static gboolean bt_default_adapter_changed(DBusConnection *conn,
+                                       DBusMessage *message,
+                                       void *user_data)
+{
+       struct near_oob_data *bt_props = user_data;
+       DBusMessageIter iter;
+       const char *adapter_path;
+
+       DBG("");
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &adapter_path);
+       DBG("New default adapter [%s]", adapter_path);
+
+       /* Disable the old one */
+       __bt_eir_free(bt_props);
+       bt_props->def_adapter = NULL;
+
+       /* Refresh */
+       bt_refresh_adapter_props(conn, user_data);
+
+       return TRUE;
+}
+
+static void bt_dbus_disconnect_cb(DBusConnection *conn, void *user_data)
+{
+       near_error("D-Bus disconnect (BT)");
+       bt_conn = NULL;
+}
+
+static guint watch;
+static guint removed_watch;
+static guint adapter_watch;
+
+static int bt_prepare_handlers(DBusConnection *conn)
+{
+       DBG("%p", conn);
+
+       if (conn == NULL)
+               return -EIO;
+
+       watch = g_dbus_add_service_watch(conn, BLUEZ_SERVICE,
+                                               bt_connect,
+                                               bt_disconnect,
+                                               &bt_def_oob_data, NULL);
+
+       removed_watch = g_dbus_add_signal_watch(conn, NULL, NULL, MANAGER_INTF,
+                                               ADAPTER_REMOVED,
+                                               bt_adapter_removed,
+                                               &bt_def_oob_data, NULL);
+
+
+       adapter_watch = g_dbus_add_signal_watch(conn, NULL, NULL, MANAGER_INTF,
+                                               DEFAULT_ADAPTER_CHANGED,
+                                               bt_default_adapter_changed,
+                                               &bt_def_oob_data, NULL);
+
+       if (watch == 0 ||  removed_watch == 0 || adapter_watch == 0) {
+               near_error("Bluez event handlers failed to register.");
+               g_dbus_remove_watch(conn, watch);
+               g_dbus_remove_watch(conn, removed_watch);
+               g_dbus_remove_watch(conn, adapter_watch);
+
+               return -EIO;
+       }
+
+       return 0;
+}
+
+/* Bluetooth exiting function */
+void __near_bluetooth_cleanup(void)
+{
+       DBG("");
+
+       if (bt_conn == NULL)
+               return;
+
+       g_dbus_remove_watch(bt_conn, watch);
+       g_dbus_remove_watch(bt_conn, removed_watch);
+       g_dbus_remove_watch(bt_conn, adapter_watch);
+
+       dbus_connection_unref(bt_conn);
+
+       __bt_eir_free(&bt_def_oob_data);
+
+       return;
+}
+
+/*
+ * Bluetooth initialization function.
+ *     Allocate bt local settings storage
+ *     and setup event handlers
+ */
+int __near_bluetooth_init(void)
+{
+       DBusError err;
+
+       DBG("");
+
+       dbus_error_init(&err);
+
+       /* save the dbus connection */
+       bt_conn = near_dbus_get_connection();
+       if (bt_conn == NULL) {
+               if (dbus_error_is_set(&err) == TRUE) {
+                       near_error("%s", err.message);
+                       dbus_error_free(&err);
+               } else
+                       near_error("Can't register with system bus\n");
+               return -EIO;
+       }
+
+       /* dbus disconnect callback */
+       g_dbus_set_disconnect_function(bt_conn, bt_dbus_disconnect_cb,
+                                               NULL, NULL);
+
+       /* Set bluez event handlers */
+       return bt_prepare_handlers(bt_conn);
+}
diff --git a/src/dbus.c b/src/dbus.c
new file mode 100644 (file)
index 0000000..ad65544
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <gdbus.h>
+
+#include "near.h"
+
+static DBusConnection *connection;
+
+dbus_bool_t near_dbus_validate_ident(const char *ident)
+{
+       unsigned int i;
+
+       if (ident == NULL)
+               return FALSE;
+
+       for (i = 0; i < strlen(ident); i++) {
+               if (ident[i] >= '0' && ident[i] <= '9')
+                       continue;
+               if (ident[i] >= 'a' && ident[i] <= 'z')
+                       continue;
+               if (ident[i] >= 'A' && ident[i] <= 'Z')
+                       continue;
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+char *near_dbus_encode_string(const char *value)
+{
+       GString *str;
+       unsigned int i, size;
+
+       if (value == NULL)
+               return NULL;
+
+       size = strlen(value);
+
+       str = g_string_new(NULL);
+       if (str == NULL)
+               return NULL;
+
+       for (i = 0; i < size; i++) {
+               const char tmp = value[i];
+               if ((tmp < '0' || tmp > '9') && (tmp < 'A' || tmp > 'Z') &&
+                                               (tmp < 'a' || tmp > 'z'))
+                       g_string_append_printf(str, "_%02x", tmp);
+               else
+                       str = g_string_append_c(str, tmp);
+       }
+
+       return g_string_free(str, FALSE);
+}
+
+void near_dbus_property_append_basic(DBusMessageIter *iter,
+                                       const char *key, int type, void *val)
+{
+       DBusMessageIter value;
+       const char *signature;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key);
+
+       switch (type) {
+       case DBUS_TYPE_BOOLEAN:
+               signature = DBUS_TYPE_BOOLEAN_AS_STRING;
+               break;
+       case DBUS_TYPE_STRING:
+               signature = DBUS_TYPE_STRING_AS_STRING;
+               break;
+       case DBUS_TYPE_BYTE:
+               signature = DBUS_TYPE_BYTE_AS_STRING;
+               break;
+       case DBUS_TYPE_UINT16:
+               signature = DBUS_TYPE_UINT16_AS_STRING;
+               break;
+       case DBUS_TYPE_INT16:
+               signature = DBUS_TYPE_INT16_AS_STRING;
+               break;
+       case DBUS_TYPE_UINT32:
+               signature = DBUS_TYPE_UINT32_AS_STRING;
+               break;
+       case DBUS_TYPE_INT32:
+               signature = DBUS_TYPE_INT32_AS_STRING;
+               break;
+       case DBUS_TYPE_OBJECT_PATH:
+               signature = DBUS_TYPE_OBJECT_PATH_AS_STRING;
+               break;
+       default:
+               signature = DBUS_TYPE_VARIANT_AS_STRING;
+               break;
+       }
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                                       signature, &value);
+       dbus_message_iter_append_basic(&value, type, val);
+       dbus_message_iter_close_container(iter, &value);
+}
+
+void near_dbus_property_append_dict(DBusMessageIter *iter, const char *key,
+                       near_dbus_append_cb_t function, void *user_data)
+{
+       DBusMessageIter value, dict;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                       DBUS_TYPE_ARRAY_AS_STRING
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &value);
+
+       near_dbus_dict_open(&value, &dict);
+       if (function)
+               function(&dict, user_data);
+       near_dbus_dict_close(&value, &dict);
+
+       dbus_message_iter_close_container(iter, &value);
+}
+
+void near_dbus_property_append_fixed_array(DBusMessageIter *iter,
+                               const char *key, int type, void *val, int len)
+{
+       DBusMessageIter value, array;
+       const char *variant_sig, *array_sig;
+
+       switch (type) {
+       case DBUS_TYPE_BYTE:
+               variant_sig = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING;
+               array_sig = DBUS_TYPE_BYTE_AS_STRING;
+               break;
+       default:
+               return;
+       }
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                                       variant_sig, &value);
+
+       dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
+                                                       array_sig, &array);
+       dbus_message_iter_append_fixed_array(&array, type, val, len);
+       dbus_message_iter_close_container(&value, &array);
+
+       dbus_message_iter_close_container(iter, &value);
+}
+
+void near_dbus_property_append_array(DBusMessageIter *iter,
+                                               const char *key, int type,
+                       near_dbus_append_cb_t function, void *user_data)
+{
+       DBusMessageIter value, array;
+       const char *variant_sig, *array_sig;
+
+       switch (type) {
+       case DBUS_TYPE_STRING:
+               variant_sig = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING;
+               array_sig = DBUS_TYPE_STRING_AS_STRING;
+               break;
+       case DBUS_TYPE_OBJECT_PATH:
+               variant_sig = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING;
+               array_sig = DBUS_TYPE_OBJECT_PATH_AS_STRING;
+               break;
+       case DBUS_TYPE_BYTE:
+               variant_sig = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING;
+               array_sig = DBUS_TYPE_BYTE_AS_STRING;
+               break;
+       default:
+               return;
+       }
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                                       variant_sig, &value);
+
+       dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
+                                                       array_sig, &array);
+       if (function)
+               function(&array, user_data);
+       dbus_message_iter_close_container(&value, &array);
+
+       dbus_message_iter_close_container(iter, &value);
+}
+
+static DBusConnection *connection = NULL;
+
+dbus_bool_t near_dbus_property_changed_basic(const char *path,
+                               const char *interface, const char *key,
+                                                       int type, void *val)
+{
+       DBusMessage *signal;
+       DBusMessageIter iter;
+
+       if (path == NULL)
+               return FALSE;
+
+       signal = dbus_message_new_signal(path, interface, "PropertyChanged");
+       if (signal == NULL)
+               return FALSE;
+
+       dbus_message_iter_init_append(signal, &iter);
+       near_dbus_property_append_basic(&iter, key, type, val);
+
+       g_dbus_send_message(connection, signal);
+
+       return TRUE;
+}
+
+dbus_bool_t near_dbus_property_changed_dict(const char *path,
+                               const char *interface, const char *key,
+                       near_dbus_append_cb_t function, void *user_data)
+{
+       DBusMessage *signal;
+       DBusMessageIter iter;
+
+       if (path == NULL)
+               return FALSE;
+
+       signal = dbus_message_new_signal(path, interface, "PropertyChanged");
+       if (signal == NULL)
+               return FALSE;
+
+       dbus_message_iter_init_append(signal, &iter);
+       near_dbus_property_append_dict(&iter, key, function, user_data);
+
+       g_dbus_send_message(connection, signal);
+
+       return TRUE;
+}
+
+dbus_bool_t near_dbus_property_changed_array(const char *path,
+                       const char *interface, const char *key, int type,
+                       near_dbus_append_cb_t function, void *user_data)
+{
+       DBusMessage *signal;
+       DBusMessageIter iter;
+
+       if (path == NULL)
+               return FALSE;
+
+       signal = dbus_message_new_signal(path, interface, "PropertyChanged");
+       if (signal == NULL)
+               return FALSE;
+
+       dbus_message_iter_init_append(signal, &iter);
+       near_dbus_property_append_array(&iter, key, type,
+                                               function, user_data);
+
+       g_dbus_send_message(connection, signal);
+
+       return TRUE;
+}
+
+DBusConnection *near_dbus_get_connection(void)
+{
+       return connection;
+}
+
+int __near_dbus_init(DBusConnection *conn)
+{
+       DBG("");
+
+       connection = conn;
+
+       return 0;
+}
+
+void __near_dbus_cleanup(void)
+{
+       DBG("");
+
+       connection = NULL;
+}
diff --git a/src/device.c b/src/device.c
new file mode 100644 (file)
index 0000000..de9cbe8
--- /dev/null
@@ -0,0 +1,503 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <gdbus.h>
+
+#include "near.h"
+
+struct near_device {
+       char *path;
+
+       uint32_t adapter_idx;
+       uint32_t target_idx;
+
+       uint8_t nfcid[NFC_MAX_NFCID1_LEN];
+       uint8_t nfcid_len;
+
+       size_t data_length;
+       uint8_t *data;
+
+       uint32_t n_records;
+       GList *records;
+
+       DBusMessage *push_msg; /* Push pending message */
+};
+
+static DBusConnection *connection = NULL;
+
+static GHashTable *device_hash;
+
+static GSList *driver_list = NULL;
+
+static void free_device(gpointer data)
+{
+       struct near_device *device = data;
+       GList *list;
+
+       DBG("device %p", device);
+
+       for (list = device->records; list; list = list->next) {
+               struct near_ndef_record *record = list->data;
+
+               __near_ndef_record_free(record);
+       }
+
+       g_list_free(device->records);
+       g_free(device->path);
+       g_free(device->data);
+       g_free(device);
+}
+
+struct near_device *near_device_get_device(uint32_t adapter_idx,
+                                               uint32_t target_idx)
+{
+       struct near_device *device;
+       char *path;
+
+       DBG("");
+
+       path = g_strdup_printf("%s/nfc%d/device%d", NFC_PATH,
+                                       adapter_idx, target_idx);
+       if (path == NULL)
+               return NULL;
+
+       device = g_hash_table_lookup(device_hash, path);
+       g_free(path);
+
+       /* TODO refcount */
+       return device;
+}
+
+void __near_device_remove(struct near_device *device)
+{
+       char *path = device->path;
+
+       DBG("path %s", device->path);
+
+       if (g_hash_table_lookup(device_hash, device->path) == NULL)
+               return;
+
+       g_dbus_unregister_interface(connection, device->path,
+                                               NFC_DEVICE_INTERFACE);
+
+       g_hash_table_remove(device_hash, path);
+}
+
+const char *__near_device_get_path(struct near_device *device)
+{
+       return device->path;
+}
+
+uint32_t __neard_device_get_idx(struct near_device *device)
+{
+       return device->target_idx;
+}
+
+static void append_records(DBusMessageIter *iter, void *user_data)
+{
+       struct near_device *device = user_data;
+       GList *list;
+
+       DBG("");
+
+       for (list = device->records; list; list = list->next) {
+               struct near_ndef_record *record = list->data;
+               char *path;
+
+               path = __near_ndef_record_get_path(record);
+               if (path == NULL)
+                       continue;
+
+               dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+                                                       &path);
+       }
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct near_device *device = data;
+       DBusMessage *reply;
+       DBusMessageIter array, dict;
+
+       DBG("conn %p", conn);
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &array);
+
+       near_dbus_dict_open(&array, &dict);
+
+       near_dbus_dict_append_array(&dict, "Records",
+                               DBUS_TYPE_OBJECT_PATH, append_records, device);
+
+       near_dbus_dict_close(&array, &dict);
+
+       return reply;
+}
+
+static DBusMessage *set_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBG("conn %p", conn);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static void push_cb(uint32_t adapter_idx, uint32_t target_idx, int status)
+{
+       struct near_device *device;
+       DBusConnection *conn;
+       DBusMessage *reply;
+
+       DBG("Push status %d", status);
+
+       conn = near_dbus_get_connection();
+       device = near_device_get_device(adapter_idx, target_idx);
+
+       if (conn == NULL || device == NULL)
+               return;
+
+       if (status != 0) {
+               reply = __near_error_failed(device->push_msg, EINVAL);
+               if (reply != NULL)
+                       g_dbus_send_message(conn, reply);
+       } else {
+               g_dbus_send_reply(conn, device->push_msg, DBUS_TYPE_INVALID);
+       }
+
+       dbus_message_unref(device->push_msg);
+       device->push_msg = NULL;
+}
+
+static char *sn_from_message(DBusMessage *msg)
+{
+       DBusMessageIter iter;
+       DBusMessageIter arr_iter;
+
+       DBG("");
+
+       dbus_message_iter_init(msg, &iter);
+       dbus_message_iter_recurse(&iter, &arr_iter);
+
+       while (dbus_message_iter_get_arg_type(&arr_iter) !=
+                                               DBUS_TYPE_INVALID) {
+               const char *key, *value;
+               DBusMessageIter ent_iter;
+               DBusMessageIter var_iter;
+
+               dbus_message_iter_recurse(&arr_iter, &ent_iter);
+               dbus_message_iter_get_basic(&ent_iter, &key);
+
+               if (g_strcmp0(key, "Type") != 0) {
+                       dbus_message_iter_next(&arr_iter);
+                       continue;
+               }
+
+               dbus_message_iter_next(&ent_iter);
+               dbus_message_iter_recurse(&ent_iter, &var_iter);
+
+               switch (dbus_message_iter_get_arg_type(&var_iter)) {
+               case DBUS_TYPE_STRING:
+                       dbus_message_iter_get_basic(&var_iter, &value);
+
+                       if (g_strcmp0(value, "Text") == 0)
+                               return NEAR_DEVICE_SN_SNEP;
+                       else if (g_strcmp0(value, "URI") == 0)
+                               return NEAR_DEVICE_SN_SNEP;
+                       else if (g_strcmp0(value, "SmartPoster") == 0)
+                               return NEAR_DEVICE_SN_SNEP;
+                       else if (g_strcmp0(value, "Handover") == 0)
+                               return NEAR_DEVICE_SN_HANDOVER;
+                       else
+                               return NULL;
+
+                       break;
+               }
+
+               dbus_message_iter_next(&arr_iter);
+       }
+
+       return NULL;
+}
+
+static DBusMessage *push_ndef(DBusConnection *conn,
+                               DBusMessage *msg, void *data)
+{
+       struct near_device *device = data;
+       struct near_ndef_message *ndef;
+       char *service_name;
+       int err;
+
+       DBG("conn %p", conn);
+
+       if (device->push_msg)
+               return __near_error_in_progress(msg);
+
+       device->push_msg = dbus_message_ref(msg);
+
+       service_name = sn_from_message(msg);
+       if (service_name == NULL) {
+               err = -EINVAL;
+               goto error;
+       }
+
+       ndef = __ndef_build_from_message(msg);
+       if (ndef == NULL) {
+               err = -EINVAL;
+               goto error;
+       }
+
+       err = __near_device_push(device, ndef, service_name, push_cb);
+       if (err < 0)
+               goto error;
+
+       g_free(ndef);
+       g_free(ndef->data);
+
+       return NULL;
+
+error:
+       dbus_message_unref(device->push_msg);
+       device->push_msg = NULL;
+
+       return __near_error_failed(msg, -err);
+}
+
+static const GDBusMethodTable device_methods[] = {
+       { GDBUS_METHOD("GetProperties",
+                               NULL, GDBUS_ARGS({"properties", "a{sv}"}),
+                               get_properties) },
+       { GDBUS_METHOD("SetProperty",
+                               GDBUS_ARGS({"name", "s"}, {"value", "v"}),
+                               NULL, set_property) },
+       { GDBUS_ASYNC_METHOD("Push", GDBUS_ARGS({"attributes", "a{sv}"}),
+                                                       NULL, push_ndef) },
+       { },
+};
+
+static const GDBusSignalTable device_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                               GDBUS_ARGS({"name", "s"}, {"value", "v"})) },
+       { }
+};
+
+int near_device_add_data(uint32_t adapter_idx, uint32_t target_idx,
+                       uint8_t *data, size_t data_length)
+{
+       struct near_device *device;
+
+       device = near_device_get_device(adapter_idx, target_idx);
+       if (device == NULL)
+               return -ENODEV;
+
+       device->data_length = data_length;
+       device->data = g_try_malloc0(data_length);
+       if (device->data == NULL)
+               return -ENOMEM;
+
+       if (data != NULL)
+               memcpy(device->data, data, data_length);
+
+       return 0;
+}
+
+int near_device_add_records(struct near_device *device, GList *records,
+                               near_device_io_cb cb, int status)
+{
+       GList *list;
+       struct near_ndef_record *record;
+       char *path;
+
+       DBG("records %p", records);
+
+       for (list = records; list; list = list->next) {
+               record = list->data;
+
+               path = g_strdup_printf("%s/nfc%d/device%d/record%d",
+                                       NFC_PATH, device->adapter_idx,
+                                       device->target_idx, device->n_records);
+
+               if (path == NULL)
+                       continue;
+
+               __near_ndef_record_register(record, path);
+
+               device->n_records++;
+               device->records = g_list_append(device->records, record);
+       }
+
+       __near_agent_ndef_parse_records(device->records);
+
+       if (cb != NULL)
+               cb(device->adapter_idx, device->target_idx, status);
+
+       g_list_free(records);
+
+       return 0;
+}
+
+struct near_device *__near_device_add(uint32_t adapter_idx, uint32_t target_idx,
+                                       uint8_t *nfcid, uint8_t nfcid_len)
+{
+       struct near_device *device;
+       char *path;
+
+       device = near_device_get_device(adapter_idx, target_idx);
+       if (device != NULL)
+               return NULL;
+
+       device = g_try_malloc0(sizeof(struct near_device));
+       if (device == NULL)
+               return NULL;
+
+       device->path = g_strdup_printf("%s/nfc%d/device%d", NFC_PATH,
+                                       adapter_idx, target_idx);
+       if (device->path == NULL) {
+               g_free(device);
+               return NULL;
+       }
+       device->adapter_idx = adapter_idx;
+       device->target_idx = target_idx;
+       device->n_records = 0;
+
+       if (nfcid_len <= NFC_MAX_NFCID1_LEN && nfcid_len > 0) {
+               device->nfcid_len = nfcid_len;
+               memcpy(device->nfcid, nfcid, nfcid_len);
+       }
+
+       path = g_strdup(device->path);
+       if (path == NULL) {
+               g_free(device);
+               return NULL;
+       }
+
+       g_hash_table_insert(device_hash, path, device);
+
+       DBG("connection %p", connection);
+
+       g_dbus_register_interface(connection, device->path,
+                                       NFC_DEVICE_INTERFACE,
+                                       device_methods, device_signals,
+                                                       NULL, device, NULL);
+
+       return device;
+}
+
+int __near_device_listen(struct near_device *device, near_device_io_cb cb)
+{
+       GSList *list;
+
+       DBG("");
+
+       for (list = driver_list; list; list = list->next) {
+               struct near_device_driver *driver = list->data;
+
+               return driver->listen(device->adapter_idx, cb);
+       }
+
+       return 0;
+}
+
+int __near_device_push(struct near_device *device,
+                       struct near_ndef_message *ndef, char *service_name,
+                       near_device_io_cb cb)
+{
+       GSList *list;
+
+       DBG("");
+
+       if (__near_adapter_get_dep_state(device->adapter_idx) == FALSE) {
+               near_error("DEP link is not established");
+               return -ENOLINK;
+       }
+
+       for (list = driver_list; list; list = list->next) {
+               struct near_device_driver *driver = list->data;
+
+               return driver->push(device->adapter_idx, device->target_idx,
+                                       ndef, service_name, cb);
+       }
+
+       return 0;
+}
+
+static gint cmp_prio(gconstpointer a, gconstpointer b)
+{
+       const struct near_tag_driver *driver1 = a;
+       const struct near_tag_driver *driver2 = b;
+
+       return driver2->priority - driver1->priority;
+}
+
+int near_device_driver_register(struct near_device_driver *driver)
+{
+       DBG("");
+
+       if (driver->listen == NULL)
+               return -EINVAL;
+
+       driver_list = g_slist_insert_sorted(driver_list, driver, cmp_prio);
+
+       __near_adapter_listen(driver);
+
+       return 0;
+}
+
+void near_device_driver_unregister(struct near_device_driver *driver)
+{
+       DBG("");
+
+       driver_list = g_slist_remove(driver_list, driver);
+}
+
+int __near_device_init(void)
+{
+       DBG("");
+
+       connection = near_dbus_get_connection();
+
+       device_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, free_device);
+
+       return 0;
+}
+
+void __near_device_cleanup(void)
+{
+       DBG("");
+
+       g_hash_table_destroy(device_hash);
+       device_hash = NULL;
+}
diff --git a/src/error.c b/src/error.c
new file mode 100644 (file)
index 0000000..4012b5b
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+
+#include <gdbus.h>
+
+#include "near.h"
+
+DBusMessage *__near_error_failed(DBusMessage *msg, int errnum)
+{
+       const char *str = strerror(errnum);
+
+       switch (errnum) {
+       case ESRCH:
+               return __near_error_not_registered(msg);
+       case ENXIO:
+               return __near_error_not_found(msg);
+       case EACCES:
+               return __near_error_permission_denied(msg);
+       case EEXIST:
+               return __near_error_already_exists(msg);
+       case EINVAL:
+               return __near_error_invalid_arguments(msg);
+       case ENOSYS:
+               return __near_error_not_implemented(msg);
+       case ENOLINK:
+               return __near_error_no_carrier(msg);
+       case ENOTUNIQ:
+               return __near_error_not_unique(msg);
+       case EOPNOTSUPP:
+               return __near_error_not_supported(msg);
+       case ECONNABORTED:
+               return __near_error_operation_aborted(msg);
+       case EISCONN:
+               return __near_error_already_connected(msg);
+       case ENOTCONN:
+               return __near_error_not_connected(msg);
+       case ETIMEDOUT:
+               return __near_error_operation_timeout(msg);
+       case EALREADY:
+               return __near_error_in_progress(msg);
+       case ENOKEY:
+               return __near_error_passphrase_required(msg);
+       }
+
+       return g_dbus_create_error(msg, NFC_ERROR_INTERFACE
+                                       ".Failed", "%s", str);
+}
+
+DBusMessage *__near_error_invalid_arguments(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, NFC_ERROR_INTERFACE
+                               ".InvalidArguments", "Invalid arguments");
+}
+
+DBusMessage *__near_error_permission_denied(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, NFC_ERROR_INTERFACE
+                               ".PermissionDenied", "Permission denied");
+}
+
+DBusMessage *__near_error_passphrase_required(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, NFC_ERROR_INTERFACE
+                               ".PassphraseRequired", "Passphrase required");
+}
+
+DBusMessage *__near_error_not_registered(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, NFC_ERROR_INTERFACE
+                                       ".NotRegistered", "Not registered");
+}
+
+DBusMessage *__near_error_not_unique(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, NFC_ERROR_INTERFACE
+                                       ".NotUnique", "Not unique");
+}
+
+DBusMessage *__near_error_not_supported(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, NFC_ERROR_INTERFACE
+                                       ".NotSupported", "Not supported");
+}
+
+DBusMessage *__near_error_not_implemented(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, NFC_ERROR_INTERFACE
+                                       ".NotImplemented", "Not implemented");
+}
+
+DBusMessage *__near_error_not_found(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, NFC_ERROR_INTERFACE
+                                               ".NotFound", "Not found");
+}
+
+DBusMessage *__near_error_not_polling(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, NFC_ERROR_INTERFACE
+                                               ".Failed", "Not polling");
+}
+
+DBusMessage *__near_error_no_carrier(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, NFC_ERROR_INTERFACE
+                                               ".NoCarrier", "No carrier");
+}
+
+DBusMessage *__near_error_in_progress(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, NFC_ERROR_INTERFACE
+                                               ".InProgress", "In progress");
+}
+
+DBusMessage *__near_error_already_exists(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, NFC_ERROR_INTERFACE
+                               ".AlreadyExists", "Already exists");
+}
+
+DBusMessage *__near_error_already_enabled(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, NFC_ERROR_INTERFACE
+                               ".AlreadyEnabled", "Already enabled");
+}
+
+DBusMessage *__near_error_already_disabled(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, NFC_ERROR_INTERFACE
+                               ".AlreadyDisabled", "Already disabled");
+}
+
+DBusMessage *__near_error_already_connected(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, NFC_ERROR_INTERFACE
+                               ".AlreadyConnected", "Already connected");
+}
+
+DBusMessage *__near_error_not_connected(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, NFC_ERROR_INTERFACE
+                                       ".NotConnected", "Not connected");
+}
+DBusMessage *__near_error_operation_aborted(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, NFC_ERROR_INTERFACE
+                               ".OperationAborted", "Operation aborted");
+}
+
+DBusMessage *__near_error_operation_timeout(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, NFC_ERROR_INTERFACE
+                               ".OperationTimeout", "Operation timeout");
+}
+
+DBusMessage *__near_error_invalid_service(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, NFC_ERROR_INTERFACE
+                               ".InvalidService", "Invalid service");
+}
+
+DBusMessage *__near_error_invalid_property(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, NFC_ERROR_INTERFACE
+                               ".InvalidProperty", "Invalid property");
+}
diff --git a/src/genbuiltin b/src/genbuiltin
new file mode 100755 (executable)
index 0000000..9c2ba15
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+for i in $*
+do
+       echo "extern struct near_plugin_desc __near_builtin_$i;"
+done
+
+echo
+echo "static struct near_plugin_desc *__near_builtin[] = {"
+
+for i in $*
+do
+       echo "  &__near_builtin_$i,"
+done
+
+echo "  NULL"
+echo "};"
diff --git a/src/log.c b/src/log.c
new file mode 100644 (file)
index 0000000..99fb984
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,138 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdarg.h>
+#include <syslog.h>
+
+#include "near.h"
+
+void near_info(const char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+
+       vsyslog(LOG_INFO, format, ap);
+
+       va_end(ap);
+}
+
+void near_warn(const char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+
+       vsyslog(LOG_WARNING, format, ap);
+
+       va_end(ap);
+}
+
+void near_error(const char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+
+       vsyslog(LOG_ERR, format, ap);
+
+       va_end(ap);
+}
+
+void near_debug(const char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+
+       vsyslog(LOG_DEBUG, format, ap);
+
+       va_end(ap);
+}
+
+extern struct near_debug_desc __start___debug[];
+extern struct near_debug_desc __stop___debug[];
+
+static gchar **enabled = NULL;
+
+static gboolean is_enabled(struct near_debug_desc *desc)
+{
+       int i;
+
+       if (enabled == NULL)
+               return FALSE;
+
+       for (i = 0; enabled[i] != NULL; i++) {
+               if (desc->name != NULL && g_pattern_match_simple(enabled[i],
+                                                       desc->name) == TRUE)
+                       return TRUE;
+               if (desc->file != NULL && g_pattern_match_simple(enabled[i],
+                                                       desc->file) == TRUE)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+int __near_log_init(const char *debug, gboolean detach)
+{
+       int option = LOG_NDELAY | LOG_PID;
+       struct near_debug_desc *desc;
+       const char *name = NULL, *file = NULL;
+
+       if (debug != NULL)
+               enabled = g_strsplit_set(debug, ":, ", 0);
+
+       for (desc = __start___debug; desc < __stop___debug; desc++) {
+               if (file != NULL || name != NULL) {
+                       if (g_strcmp0(desc->file, file) == 0) {
+                               if (desc->name == NULL)
+                                       desc->name = name;
+                       } else
+                               file = NULL;
+               }
+
+               if (is_enabled(desc) == TRUE)
+                       desc->flags |= NEAR_DEBUG_FLAG_PRINT;
+       }
+
+       if (detach == FALSE)
+               option |= LOG_PERROR;
+
+       openlog("neard", option, LOG_DAEMON);
+
+       syslog(LOG_INFO, "NEAR daemon version %s", VERSION);
+
+       return 0;
+}
+
+void __near_log_cleanup(void)
+{
+       syslog(LOG_INFO, "Exit");
+
+       closelog();
+
+       g_strfreev(enabled);
+}
diff --git a/src/main.c b/src/main.c
new file mode 100644 (file)
index 0000000..dea9995
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+
+#include <gdbus.h>
+
+#include "near.h"
+
+static struct {
+       near_bool_t constant_poll;
+} near_settings  = {
+       .constant_poll = FALSE,
+};
+
+static GKeyFile *load_config(const char *file)
+{
+       GError *err = NULL;
+       GKeyFile *keyfile;
+
+       keyfile = g_key_file_new();
+
+       g_key_file_set_list_separator(keyfile, ',');
+
+       if (!g_key_file_load_from_file(keyfile, file, 0, &err)) {
+               if (err->code != G_FILE_ERROR_NOENT) {
+                       near_error("Parsing %s failed: %s", file,
+                                                               err->message);
+               }
+
+               g_error_free(err);
+               g_key_file_free(keyfile);
+               return NULL;
+       }
+
+       return keyfile;
+}
+
+static void parse_config(GKeyFile *config)
+{
+       GError *error = NULL;
+       gboolean boolean;
+
+       if (config == NULL)
+               return;
+
+       DBG("parsing main.conf");
+
+       boolean = g_key_file_get_boolean(config, "General",
+                                               "ConstantPoll", &error);
+       if (error == NULL)
+               near_settings.constant_poll = boolean;
+
+       g_clear_error(&error);
+}
+
+static GMainLoop *main_loop = NULL;
+
+static volatile sig_atomic_t __terminated = 0;
+
+static void sig_term(int sig)
+{
+       if (__terminated > 0)
+               return;
+
+       __terminated = 1;
+
+       near_info("Terminating");
+
+       g_main_loop_quit(main_loop);
+}
+
+static void disconnect_callback(DBusConnection *conn, void *user_data)
+{
+       near_error("D-Bus disconnect");
+
+       g_main_loop_quit(main_loop);
+}
+
+static gchar *option_debug = NULL;
+static gchar *option_plugin = NULL;
+static gchar *option_noplugin = NULL;
+static gboolean option_detach = TRUE;
+static gboolean option_version = FALSE;
+
+static gboolean parse_debug(const char *key, const char *value,
+                                       gpointer user_data, GError **error)
+{
+       if (value)
+               option_debug = g_strdup(value);
+       else
+               option_debug = g_strdup("*");
+
+       return TRUE;
+}
+
+static GOptionEntry options[] = {
+       { "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG,
+                               G_OPTION_ARG_CALLBACK, parse_debug,
+                               "Specify debug options to enable", "DEBUG" },
+       { "nodaemon", 'n', G_OPTION_FLAG_REVERSE,
+                               G_OPTION_ARG_NONE, &option_detach,
+                               "Don't fork daemon to background" },
+       { "plugin", 'p', 0, G_OPTION_ARG_STRING, &option_plugin,
+                               "Specify plugins to load", "NAME,..." },
+       { "noplugin", 'P', 0, G_OPTION_ARG_STRING, &option_noplugin,
+                               "Specify plugins not to load", "NAME,..." },
+       { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
+                               "Show version information and exit" },
+       { NULL },
+};
+
+near_bool_t near_setting_get_bool(const char *key)
+{
+       if (g_str_equal(key, "ConstantPoll") == TRUE)
+               return near_settings.constant_poll;
+
+       return FALSE;
+}
+
+int main(int argc, char *argv[])
+{
+       GOptionContext *context;
+       GError *error = NULL;
+       DBusConnection *conn;
+       DBusError err;
+       GKeyFile *config;
+       struct sigaction sa;
+
+       context = g_option_context_new(NULL);
+       g_option_context_add_main_entries(context, options, NULL);
+
+       if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
+               if (error != NULL) {
+                       g_printerr("%s\n", error->message);
+                       g_error_free(error);
+               } else
+                       g_printerr("An unknown error occurred\n");
+               exit(1);
+       }
+
+       g_option_context_free(context);
+
+       if (option_version == TRUE) {
+               printf("%s\n", VERSION);
+               exit(0);
+       }
+
+       if (option_detach == TRUE) {
+               if (daemon(0, 0)) {
+                       perror("Can't start daemon");
+                       exit(1);
+               }
+       }
+
+       main_loop = g_main_loop_new(NULL, FALSE);
+
+       dbus_error_init(&err);
+
+       conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NFC_SERVICE, &err);
+       if (conn == NULL) {
+               if (dbus_error_is_set(&err) == TRUE) {
+                       fprintf(stderr, "%s\n", err.message);
+                       dbus_error_free(&err);
+               } else
+                       fprintf(stderr, "Can't register with system bus\n");
+               exit(1);
+       }
+
+       g_dbus_set_disconnect_function(conn, disconnect_callback, NULL, NULL);
+
+       __near_log_init(option_debug, option_detach);
+       __near_dbus_init(conn);
+
+       config = load_config(CONFIGDIR "/main.conf");
+
+       parse_config(config);
+
+       __near_netlink_init();
+       __near_tag_init();
+       __near_device_init();
+       __near_adapter_init();
+       __near_ndef_init();
+       __near_manager_init(conn);
+       __near_bluetooth_init();
+       __near_agent_init();
+
+       __near_plugin_init(option_plugin, option_noplugin);
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = sig_term;
+       sigaction(SIGINT, &sa, NULL);
+       sigaction(SIGTERM, &sa, NULL);
+
+       g_main_loop_run(main_loop);
+
+       __near_plugin_cleanup();
+
+       __near_agent_cleanup();
+       __near_bluetooth_cleanup();
+       __near_manager_cleanup();
+       __near_ndef_cleanup();
+       __near_adapter_cleanup();
+       __near_device_cleanup();
+       __near_tag_cleanup();
+       __near_netlink_cleanup();
+
+       __near_dbus_cleanup();
+       __near_log_cleanup();
+
+       dbus_connection_unref(conn);
+
+       g_main_loop_unref(main_loop);
+
+       if (config)
+               g_key_file_free(config);
+
+       return 0;
+}
diff --git a/src/main.conf b/src/main.conf
new file mode 100644 (file)
index 0000000..65b6ac3
--- /dev/null
@@ -0,0 +1,7 @@
+[General]
+
+# Enable constant polling. Default value is false.
+# Constant polling will automatically trigger a new
+# polling loop whenever a tag or a device is no longer
+# in the RF field.
+ConstantPoll = true
diff --git a/src/manager.c b/src/manager.c
new file mode 100644 (file)
index 0000000..2002411
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <gdbus.h>
+
+#include "near.h"
+
+static DBusConnection *connection;
+
+static DBusMessage *get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+       DBusMessageIter array, dict;
+
+       DBG("conn %p", conn);
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &array);
+
+       near_dbus_dict_open(&array, &dict);
+
+       near_dbus_dict_append_array(&dict, "Adapters",
+                       DBUS_TYPE_OBJECT_PATH, __near_adapter_list, NULL);
+
+       near_dbus_dict_close(&array, &dict);
+
+       return reply;
+}
+
+static DBusMessage *set_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBG("conn %p", conn);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+int __near_manager_adapter_add(uint32_t idx, const char *name,
+                               uint32_t protocols, near_bool_t powered)
+{
+       struct near_adapter *adapter;
+       const char *path;
+       int err;
+
+       DBG("idx %d", idx);
+
+       adapter = __near_adapter_create(idx, name, protocols, powered);
+       if (adapter == NULL)
+               return -ENOMEM;
+
+       path = __near_adapter_get_path(adapter);
+       if (path == NULL) {
+               __near_adapter_destroy(adapter);
+               return -EINVAL;
+       }
+
+       err = __near_adapter_add(adapter);
+       if (err < 0) {
+               __near_adapter_destroy(adapter);
+       } else {
+               near_dbus_property_changed_array(NFC_MANAGER_PATH,
+                               NFC_MANAGER_INTERFACE, "Adapters",
+                               DBUS_TYPE_OBJECT_PATH, __near_adapter_list,
+                               NULL);
+
+               g_dbus_emit_signal(connection, "/",
+                       NFC_MANAGER_INTERFACE, "AdapterAdded",
+                       DBUS_TYPE_OBJECT_PATH, &path,
+                       DBUS_TYPE_INVALID);
+       }
+
+       return err;
+}
+
+void __near_manager_adapter_remove(uint32_t idx)
+{
+       struct near_adapter *adapter;
+       const char *path;
+
+       DBG("idx %d", idx);
+
+       adapter = __near_adapter_get(idx);
+       if (adapter == NULL)
+               return;
+
+       path = __near_adapter_get_path(adapter);
+       if (path == NULL)
+               return;
+
+
+       g_dbus_emit_signal(connection, "/",
+                       NFC_MANAGER_INTERFACE, "AdapterRemoved",
+                       DBUS_TYPE_OBJECT_PATH, &path,
+                       DBUS_TYPE_INVALID);
+
+       __near_adapter_remove(adapter);
+
+       near_dbus_property_changed_array(NFC_MANAGER_PATH,
+                               NFC_MANAGER_INTERFACE, "Adapters",
+                               DBUS_TYPE_OBJECT_PATH, __near_adapter_list,
+                               NULL);
+}
+
+static DBusMessage *register_handover_agent(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       const char *sender, *path;
+       int err;
+
+       DBG("conn %p", conn);
+
+       sender = dbus_message_get_sender(msg);
+
+       dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID);
+
+       err = __near_agent_handover_register(sender, path);
+       if (err < 0)
+               return __near_error_failed(msg, -err);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *unregister_handover_agent(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       const char *sender, *path;
+       int err;
+
+       DBG("conn %p", conn);
+
+       sender = dbus_message_get_sender(msg);
+
+       dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID);
+
+       err = __near_agent_handover_unregister(sender, path);
+       if (err < 0)
+               return __near_error_failed(msg, -err);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *register_ndef_agent(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessageIter iter;
+       const char *sender, *path, *type;
+       int err;
+
+       DBG("conn %p", conn);
+
+       sender = dbus_message_get_sender(msg);
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return __near_error_invalid_arguments(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH)
+               return __near_error_invalid_arguments(msg);
+
+       dbus_message_iter_get_basic(&iter, &path);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __near_error_invalid_arguments(msg);
+
+       dbus_message_iter_get_basic(&iter, &type);
+
+       err = __near_agent_ndef_register(sender, path, type);
+       if (err < 0)
+               return __near_error_failed(msg, -err);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *unregister_ndef_agent(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessageIter iter;
+       const char *sender, *path, *type;
+       int err;
+
+       DBG("conn %p", conn);
+
+       sender = dbus_message_get_sender(msg);
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return __near_error_invalid_arguments(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH)
+               return __near_error_invalid_arguments(msg);
+
+       dbus_message_iter_get_basic(&iter, &path);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __near_error_invalid_arguments(msg);
+
+       dbus_message_iter_get_basic(&iter, &type);
+
+       err = __near_agent_ndef_unregister(sender, path, type);
+       if (err < 0)
+               return __near_error_failed(msg, -err);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable manager_methods[] = {
+       { GDBUS_METHOD("GetProperties",
+                               NULL, GDBUS_ARGS({"properties", "a{sv}"}),
+                               get_properties) },
+       { GDBUS_METHOD("SetProperty",
+                               GDBUS_ARGS({"name", "s"}, {"value", "v"}),
+                               NULL, set_property) },
+       { GDBUS_METHOD("RegisterHandoverAgent",
+                       GDBUS_ARGS({ "path", "o" }), NULL,
+                       register_handover_agent) },
+       { GDBUS_METHOD("UnregisterHandoverAgent",
+                       GDBUS_ARGS({ "path", "o" }), NULL,
+                       unregister_handover_agent) },
+       { GDBUS_METHOD("RegisterNDEFAgent",
+                       GDBUS_ARGS({"path", "o"}, {"type", "s"}),
+                       NULL, register_ndef_agent) },
+       { GDBUS_METHOD("UnregisterNDEFAgent",
+                       GDBUS_ARGS({"path", "o"}, {"type", "s"}),
+                       NULL, unregister_ndef_agent) },
+       { },
+};
+
+static const GDBusSignalTable manager_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                               GDBUS_ARGS({"name", "s"}, {"value", "v"})) },
+       { GDBUS_SIGNAL("AdapterAdded", GDBUS_ARGS({"adapter", "o" })) },
+       { GDBUS_SIGNAL("AdapterRemoved", GDBUS_ARGS({"adapter", "o" })) },
+       { }
+};
+
+int __near_manager_init(DBusConnection *conn)
+{
+       DBG("");
+
+       connection = dbus_connection_ref(conn);
+
+       DBG("connection %p", connection);
+
+       g_dbus_register_interface(connection, NFC_MANAGER_PATH,
+                                               NFC_MANAGER_INTERFACE,
+                                               manager_methods,
+                                               manager_signals, NULL, NULL, NULL);
+
+       return __near_netlink_get_adapters();
+}
+
+void __near_manager_cleanup(void)
+{
+       DBG("");
+
+       g_dbus_unregister_interface(connection, NFC_MANAGER_PATH,
+                                               NFC_MANAGER_INTERFACE);
+
+       dbus_connection_unref(connection);
+}
diff --git a/src/ndef.c b/src/ndef.c
new file mode 100644 (file)
index 0000000..f123d62
--- /dev/null
@@ -0,0 +1,3049 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <gdbus.h>
+
+#include "near.h"
+
+enum record_tnf {
+       RECORD_TNF_EMPTY     = 0x00,
+       RECORD_TNF_WELLKNOWN = 0x01,
+       RECORD_TNF_MIME      = 0x02,
+       RECORD_TNF_URI       = 0x03,
+       RECORD_TNF_EXTERNAL  = 0x04,
+       RECORD_TNF_UNKNOWN   = 0x05,
+       RECORD_TNF_UNCHANGED = 0x06,
+};
+
+#define RECORD_ACTION_DO   0x00
+#define RECORD_ACTION_SAVE 0x01
+#define RECORD_ACTION_EDIT 0x02
+
+#define RECORD_MB_BIT(val)  ((val & 0x80) >> 7)
+#define RECORD_ME_BIT(val)  ((val & 0x40) >> 6)
+#define RECORD_CF_BIT(val)  ((val & 0x20) >> 5)
+#define RECORD_SR_BIT(val)  ((val & 0x10) >> 4)
+#define RECORD_IL_BIT(val)  ((val & 0x8)  >> 3)
+#define RECORD_TNF_BIT(val) (val & 0x7)
+
+#define NDEF_MSG_MIN_LENGTH 0x03
+#define NDEF_PAYLOAD_LENGTH_OFFSET 0x02
+
+#define RECORD_MB    0x80
+#define RECORD_ME    0x40
+#define RECORD_CF    0x20
+#define RECORD_SR    0x10
+#define RECORD_IL    0x08
+#define RECORD_TNF_EMPTY_SET(val)     ((val & ~0x7) | RECORD_TNF_EMPTY)
+#define RECORD_TNF_WKT_SET(val)       ((val & ~0x7) | RECORD_TNF_WELLKNOWN)
+#define RECORD_TNF_MIME_SET(val)      ((val & ~0x7) | RECORD_TNF_MIME)
+#define RECORD_TNF_URI_SET(val)       ((val & ~0x7) | RECORD_TNF_URI)
+#define RECORD_TNF_EXTERNAL_SET(val)  ((val & ~0x7) | RECORD_TNF_EXTERNAL)
+#define RECORD_TNF_UKNOWN_SET(val)    ((val & ~0x7) | RECORD_TNF_UNKNOWN)
+#define RECORD_TNF_UNCHANGED_SET(val) ((val & ~0x7) | RECORD_TNF_UNCHANGED)
+
+#define NDEF_MSG_SHORT_RECORD_MAX_LENGTH 0xFF
+#define NDEF_TEXT_RECORD_TYPE_NAME_HEX_VALUE 0x54
+#define NDEF_TEXT_RECORD_UTF16_STATUS 0x80
+
+enum record_type {
+       RECORD_TYPE_WKT_SMART_POSTER          =   0x01,
+       RECORD_TYPE_WKT_URI                   =   0x02,
+       RECORD_TYPE_WKT_TEXT                  =   0x03,
+       RECORD_TYPE_WKT_SIZE                  =   0x04,
+       RECORD_TYPE_WKT_TYPE                  =   0x05,
+       RECORD_TYPE_WKT_ACTION                =   0x06,
+       RECORD_TYPE_WKT_HANDOVER_REQUEST      =   0x07,
+       RECORD_TYPE_WKT_HANDOVER_SELECT       =   0x08,
+       RECORD_TYPE_WKT_HANDOVER_CARRIER      =   0x09,
+       RECORD_TYPE_WKT_ALTERNATIVE_CARRIER   =   0x0a,
+       RECORD_TYPE_WKT_COLLISION_RESOLUTION  =   0x0b,
+       RECORD_TYPE_WKT_ERROR                 =   0x0c,
+       RECORD_TYPE_MIME_TYPE                 =   0x0d,
+       RECORD_TYPE_UNKNOWN                   =   0xfe,
+       RECORD_TYPE_ERROR                     =   0xff
+};
+
+#define RECORD_TYPE_WKT "urn:nfc:wkt:"
+#define RECORD_TYPE_EXTERNAL "urn:nfc:ext:"
+
+struct near_ndef_record_header {
+       uint8_t mb;
+       uint8_t me;
+       uint8_t cf;
+       uint8_t sr;
+       uint8_t il;
+       uint8_t tnf;
+       uint8_t il_length;
+       uint8_t *il_field;
+       uint32_t payload_len;
+       uint32_t offset;
+       uint8_t type_len;
+       enum record_type rec_type;
+       char *type_name;
+       uint32_t header_len;
+};
+
+struct near_ndef_text_payload {
+       char *encoding;
+       char *language_code;
+       char *data;
+};
+
+struct near_ndef_uri_payload {
+       uint8_t identifier;
+
+       uint32_t  field_length;
+       uint8_t  *field;
+};
+
+struct near_ndef_sp_payload {
+       struct near_ndef_uri_payload *uri;
+
+       uint8_t number_of_title_records;
+       struct near_ndef_text_payload **title_records;
+
+       uint32_t size; /* from Size record*/
+       char *type;    /* from Type record*/
+       char *action;
+       /* TODO add icon and other records fields*/
+};
+
+struct near_ndef_mime_payload {
+       char *type;
+
+       struct {
+               uint8_t carrier_type;
+               uint16_t properties;    /* e.g.: NO_PAIRING_KEY */
+       } handover;
+};
+
+enum carrier_power_state {
+       CPS_INACTIVE                    =   0x00,
+       CPS_ACTIVE                      =   0x01,
+       CPS_ACTIVATING                  =   0x02,
+       CPS_UNKNOWN                     =   0x03,
+};
+
+/* Handover record definitions */
+
+/* alternative record (AC) */
+#define AC_RECORD_PAYLOAD_LEN  4
+
+struct near_ndef_ac_payload {
+       enum carrier_power_state cps;   /* carrier power state */
+
+       uint8_t cdr_len;        /* carrier data reference length: 0x01 */
+       uint8_t cdr;            /* carrier data reference */
+       uint8_t adata_refcount; /* auxiliary data reference count */
+
+       /* !: if adata_refcount == 0, then there's no data reference */
+       uint16_t **adata;       /* auxiliary data reference */
+};
+
+/*
+ * carrier data (see cdr in near_ndef_ac_payload )
+ * These settings can be retrieved from mime, carrier records, etc...
+ */
+struct near_ndef_carrier_data {
+       uint8_t cdr;            /* carrier data reference */
+       uint8_t *data;
+       size_t data_len;
+};
+
+/* Default Handover version */
+#define HANDOVER_VERSION       0x12
+#define HANDOVER_MAJOR(version) (((version) >> 4) & 0xf)
+#define HANDOVER_MINOR(version) ((version) & 0xf)
+
+
+/* General Handover Request/Select record */
+struct near_ndef_ho_payload {
+       uint8_t version;                /* version id */
+       uint16_t collision_record;      /* collision record */
+
+       uint8_t number_of_ac_payloads;  /* At least 1 ac is needed */
+       struct near_ndef_ac_payload **ac_payloads;
+
+       /* Optional records */
+       uint16_t *err_record;   /* not NULL if present */
+
+       uint8_t number_of_cfg_payloads; /* extra NDEF records */
+       struct near_ndef_mime_payload **cfg_payloads;
+};
+
+struct near_ndef_record {
+       char *path;
+
+       struct near_ndef_record_header *header;
+
+       /* specific payloads */
+       struct near_ndef_text_payload *text;
+       struct near_ndef_uri_payload  *uri;
+       struct near_ndef_sp_payload   *sp;
+       struct near_ndef_mime_payload *mime;
+       struct near_ndef_ho_payload   *ho;      /* handover payload */
+
+       char *type;
+
+       uint8_t *data;
+       size_t data_len;
+};
+
+static DBusConnection *connection = NULL;
+
+static inline void fillb8(uint8_t *ptr, uint32_t len)
+{
+       (*(uint8_t *)(ptr)) = ((uint8_t)(len));
+}
+
+static inline void fillb16(uint8_t *ptr, uint32_t len)
+{
+       fillb8((ptr), (uint16_t)(len) >> 8);
+       fillb8((uint8_t *)(ptr) + 1, len);
+}
+
+static inline void fillb32(uint8_t *ptr, uint32_t len)
+{
+       fillb16((ptr), (uint32_t)(len) >> 16);
+       fillb16((uint8_t *)(ptr) + 2, (uint32_t)(len));
+}
+
+char *__near_ndef_record_get_path(struct near_ndef_record *record)
+{
+       return record->path;
+}
+
+char *__near_ndef_record_get_type(struct near_ndef_record *record)
+{
+       return record->type;
+}
+
+uint8_t *__near_ndef_record_get_data(struct near_ndef_record *record,
+                                                               size_t *len)
+{
+       *len = record->data_len;
+
+       return record->data;
+}
+
+void __near_ndef_append_records(DBusMessageIter *iter, GList *records)
+{
+       GList *list;
+
+       DBG("");
+
+       for (list = records; list; list = list->next) {
+               struct near_ndef_record *record = list->data;
+               uint8_t *data;
+               size_t data_len;
+
+               data = __near_ndef_record_get_data(record, &data_len);
+               if (data == NULL)
+                       continue;
+
+               dbus_message_iter_append_fixed_array(iter, DBUS_TYPE_BYTE,
+                                                       &data, data_len);
+       }
+}
+
+static void append_text_payload(struct near_ndef_text_payload *text,
+                                       DBusMessageIter *dict)
+{
+       DBG("");
+
+       if (text == NULL || dict == NULL)
+               return;
+
+       if (text->encoding != NULL)
+               near_dbus_dict_append_basic(dict, "Encoding",
+                                               DBUS_TYPE_STRING,
+                                               &(text->encoding));
+
+       if (text->language_code != NULL)
+               near_dbus_dict_append_basic(dict, "Language",
+                                               DBUS_TYPE_STRING,
+                                               &(text->language_code));
+
+       if (text->data != NULL)
+               near_dbus_dict_append_basic(dict, "Representation",
+                                               DBUS_TYPE_STRING,
+                                               &(text->data));
+}
+
+static const char *uri_prefixes[NFC_MAX_URI_ID + 1] = {
+       "",
+       "http://www.",
+       "https://www.",
+       "http://",
+       "https://",
+       "tel:",
+       "mailto:",
+       "ftp://anonymous:anonymous@",
+       "ftp://ftp.",
+       "ftps://",
+       "sftp://",
+       "smb://",
+       "nfs://",
+       "ftp://",
+       "dav://",
+       "news:",
+       "telnet://",
+       "imap:",
+       "rstp://",
+       "urn:",
+       "pop:",
+       "sip:",
+       "sips:",
+       "tftp:",
+       "btspp://",
+       "btl2cap://",
+       "btgoep://",
+       "tcpobex://",
+       "irdaobex://",
+       "file://",
+       "urn:epc:id:",
+       "urn:epc:tag:",
+       "urn:epc:pat:",
+       "urn:epc:raw:",
+       "urn:epc:",
+       "urn:nfc:",
+};
+
+const char *__near_ndef_get_uri_prefix(uint8_t id)
+{
+       if (id > NFC_MAX_URI_ID)
+               return NULL;
+
+       return uri_prefixes[id];
+}
+
+static void append_uri_payload(struct near_ndef_uri_payload *uri,
+                                       DBusMessageIter *dict)
+{
+       char *value;
+       const char *prefix = NULL;
+
+       DBG("");
+
+       if (uri == NULL || dict == NULL)
+               return;
+
+       if (uri->identifier > NFC_MAX_URI_ID) {
+               near_error("Invalid URI identifier 0x%x", uri->identifier);
+               return;
+       }
+
+       prefix = uri_prefixes[uri->identifier];
+
+       DBG("URI prefix %s", prefix);
+
+       value = g_strdup_printf("%s%.*s", prefix, uri->field_length,
+                                                        uri->field);
+
+       near_dbus_dict_append_basic(dict, "URI", DBUS_TYPE_STRING, &value);
+
+       g_free(value);
+}
+
+static void append_sp_payload(struct near_ndef_sp_payload *sp,
+                                               DBusMessageIter *dict)
+{
+       uint8_t i;
+
+       DBG("");
+
+       if (sp == NULL || dict == NULL)
+               return;
+
+       if (sp->action != NULL)
+               near_dbus_dict_append_basic(dict, "Action", DBUS_TYPE_STRING,
+                                                       &(sp->action));
+
+       if (sp->uri != NULL)
+               append_uri_payload(sp->uri, dict);
+
+       if (sp->title_records != NULL &&
+                       sp->number_of_title_records > 0) {
+               for (i = 0; i < sp->number_of_title_records; i++)
+                       append_text_payload(sp->title_records[i], dict);
+       }
+
+       if (sp->type != NULL)
+               near_dbus_dict_append_basic(dict, "MIMEType", DBUS_TYPE_STRING,
+                                                               &(sp->type));
+
+       if (sp->size > 0)
+               near_dbus_dict_append_basic(dict, "Size", DBUS_TYPE_UINT32,
+                                                       &(sp->size));
+}
+
+static void append_mime_payload(struct near_ndef_mime_payload *mime,
+                                       DBusMessageIter *dict)
+{
+       DBG("");
+
+       if (mime == NULL || dict == NULL)
+               return;
+
+       if (mime->type != NULL)
+               near_dbus_dict_append_basic(dict, "MIME",
+                                               DBUS_TYPE_STRING,
+                                               &(mime->type));
+}
+
+static void append_record(struct near_ndef_record *record,
+                                       DBusMessageIter *dict)
+{
+       char *type;
+
+       DBG("");
+
+       if (record == NULL || dict == NULL)
+               return;
+
+       switch (record->header->rec_type) {
+       case RECORD_TYPE_WKT_SIZE:
+       case RECORD_TYPE_WKT_TYPE:
+       case RECORD_TYPE_WKT_ACTION:
+       case RECORD_TYPE_WKT_ALTERNATIVE_CARRIER:
+       case RECORD_TYPE_WKT_COLLISION_RESOLUTION:
+       case RECORD_TYPE_WKT_ERROR:
+       case RECORD_TYPE_UNKNOWN:
+       case RECORD_TYPE_ERROR:
+               break;
+
+       case RECORD_TYPE_WKT_TEXT:
+               type = "Text";
+               near_dbus_dict_append_basic(dict, "Type",
+                                       DBUS_TYPE_STRING, &type);
+               append_text_payload(record->text, dict);
+               break;
+
+       case RECORD_TYPE_WKT_URI:
+               type = "URI";
+               near_dbus_dict_append_basic(dict, "Type",
+                                       DBUS_TYPE_STRING, &type);
+               append_uri_payload(record->uri, dict);
+               break;
+
+       case RECORD_TYPE_WKT_SMART_POSTER:
+               type = "SmartPoster";
+               near_dbus_dict_append_basic(dict, "Type",
+                                       DBUS_TYPE_STRING, &type);
+               append_sp_payload(record->sp, dict);
+               break;
+
+       case RECORD_TYPE_WKT_HANDOVER_REQUEST:
+               type = "HandoverRequest";
+               near_dbus_dict_append_basic(dict, "Type",
+                                       DBUS_TYPE_STRING, &type);
+               break;
+
+       case RECORD_TYPE_WKT_HANDOVER_SELECT:
+               type = "HandoverSelect";
+               near_dbus_dict_append_basic(dict, "Type",
+                                       DBUS_TYPE_STRING, &type);
+               break;
+
+       case RECORD_TYPE_WKT_HANDOVER_CARRIER:
+               type = "HandoverCarrier";
+               near_dbus_dict_append_basic(dict, "Type",
+                                       DBUS_TYPE_STRING, &type);
+               break;
+
+       case RECORD_TYPE_MIME_TYPE:
+               type = "MIME Type (RFC 2046)";
+               near_dbus_dict_append_basic(dict, "Type",
+                                       DBUS_TYPE_STRING, &type);
+               append_mime_payload(record->mime, dict);
+               break;
+       }
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct near_ndef_record *record = data;
+       DBusMessage *reply;
+       DBusMessageIter array, dict;
+
+       DBG("conn %p", conn);
+
+       if (conn == NULL || msg == NULL ||
+               data == NULL)
+               return NULL;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &array);
+
+       near_dbus_dict_open(&array, &dict);
+
+       append_record(record, &dict);
+
+       near_dbus_dict_close(&array, &dict);
+
+       return reply;
+}
+
+static const GDBusMethodTable record_methods[] = {
+       { GDBUS_METHOD("GetProperties",
+                               NULL, GDBUS_ARGS({"properties", "a{sv}"}),
+                               get_properties) },
+       { },
+};
+
+static void free_text_payload(struct near_ndef_text_payload *text)
+{
+       if (text == NULL)
+               return;
+
+       g_free(text->encoding);
+       g_free(text->language_code);
+       g_free(text->data);
+       g_free(text);
+
+       text = NULL;
+}
+
+static void free_uri_payload(struct near_ndef_uri_payload *uri)
+{
+       if (uri == NULL)
+               return;
+
+       g_free(uri->field);
+       g_free(uri);
+
+       uri = NULL;
+}
+
+static void free_sp_payload(struct near_ndef_sp_payload *sp)
+{
+       uint8_t i;
+
+       if (sp == NULL)
+               return;
+
+       free_uri_payload(sp->uri);
+
+       if (sp->title_records != NULL) {
+               for (i = 0; i < sp->number_of_title_records; i++)
+                       free_text_payload(sp->title_records[i]);
+       }
+
+       g_free(sp->title_records);
+       g_free(sp->type);
+       g_free(sp->action);
+       g_free(sp);
+
+       sp = NULL;
+}
+
+static void free_mime_payload(struct near_ndef_mime_payload *mime)
+{
+       if (mime == NULL)
+               return;
+
+       g_free(mime->type);
+       g_free(mime);
+
+       mime = NULL;
+}
+
+static void free_ac_payload(struct near_ndef_ac_payload *ac)
+{
+       if (ac == NULL)
+               return;
+
+       g_free(ac->adata);
+       g_free(ac);
+       ac = NULL;
+}
+
+static void free_ho_payload(struct near_ndef_ho_payload *ho)
+{
+       int i;
+
+       if (ho == NULL)
+               return;
+
+       if (ho->ac_payloads != NULL) {
+               for (i = 0; i < ho->number_of_ac_payloads; i++)
+                       free_ac_payload(ho->ac_payloads[i]);
+       }
+
+       g_free(ho->ac_payloads);
+       g_free(ho);
+
+       ho = NULL;
+}
+
+static void free_ndef_record(struct near_ndef_record *record)
+{
+       if (record == NULL)
+               return;
+
+       g_free(record->path);
+
+       if (record->header != NULL) {
+
+               switch (record->header->rec_type) {
+               case RECORD_TYPE_WKT_SIZE:
+               case RECORD_TYPE_WKT_TYPE:
+               case RECORD_TYPE_WKT_ACTION:
+               case RECORD_TYPE_WKT_HANDOVER_CARRIER:
+               case RECORD_TYPE_WKT_ALTERNATIVE_CARRIER:
+               case RECORD_TYPE_WKT_COLLISION_RESOLUTION:
+               case RECORD_TYPE_WKT_ERROR:
+               case RECORD_TYPE_UNKNOWN:
+               case RECORD_TYPE_ERROR:
+                       break;
+
+               case RECORD_TYPE_WKT_HANDOVER_REQUEST:
+               case RECORD_TYPE_WKT_HANDOVER_SELECT:
+                       free_ho_payload(record->ho);
+                       break;
+
+               case RECORD_TYPE_WKT_TEXT:
+                       free_text_payload(record->text);
+                       break;
+
+               case RECORD_TYPE_WKT_URI:
+                       free_uri_payload(record->uri);
+                       break;
+
+               case RECORD_TYPE_WKT_SMART_POSTER:
+                       free_sp_payload(record->sp);
+                       break;
+
+               case RECORD_TYPE_MIME_TYPE:
+                       free_mime_payload(record->mime);
+               }
+
+               g_free(record->header->il_field);
+               g_free(record->header->type_name);
+       }
+
+       g_free(record->header);
+       g_free(record->type);
+       g_free(record->data);
+       g_free(record);
+       record = NULL;
+}
+
+void __near_ndef_record_free(struct near_ndef_record *record)
+{
+       g_dbus_unregister_interface(connection, record->path,
+                                               NFC_RECORD_INTERFACE);
+
+       free_ndef_record(record);
+}
+
+static char *action_to_string(uint8_t action)
+{
+       switch (action) {
+       case RECORD_ACTION_DO:
+               return "Do";
+       case RECORD_ACTION_SAVE:
+               return "Save";
+       case RECORD_ACTION_EDIT:
+               return "Edit";
+       default:
+               near_error("Unknown action 0x%x", action);
+               return NULL;
+       }
+}
+
+/**
+ * @brief returns record type for external type
+ * Validate type and type length and returns
+ * type.
+ *
+ * @param type    Type name in hex format
+ * @param type_lenth Type name length
+ *
+ * @return enum record type
+ */
+
+static enum record_type get_external_record_type(uint8_t *type,
+                                               size_t type_length)
+{
+       DBG("");
+
+       if (strncmp((char *) type, BT_MIME_STRING_2_0,
+                                       strlen(BT_MIME_STRING_2_0)) == 0)
+               return RECORD_TYPE_MIME_TYPE;
+       else
+               return RECORD_TYPE_UNKNOWN;
+}
+
+/**
+ * @brief returns record type
+ * Validate type name format, type and type length and returns
+ * type.
+ *
+ * @param tnf     TypeNameFormat value
+ * @param type    Type name in hex format
+ * @param type_lenth Type name length
+ *
+ * @return enum record type
+ */
+
+static enum record_type get_record_type(enum record_tnf tnf,
+                               uint8_t *type, size_t type_length)
+{
+       DBG("");
+
+       switch (tnf) {
+       case RECORD_TNF_EMPTY:
+       case RECORD_TNF_URI:
+       case RECORD_TNF_UNKNOWN:
+       case RECORD_TNF_UNCHANGED:
+               break;
+
+       case RECORD_TNF_WELLKNOWN:
+               if (type_length == 1) {
+                       if (type[0] == 'T')
+                               return RECORD_TYPE_WKT_TEXT;
+                       else if (type[0] == 'U')
+                               return RECORD_TYPE_WKT_URI;
+                       else if (type[0] == 's')
+                               return RECORD_TYPE_WKT_SIZE;
+                       else if (type[0] == 't')
+                               return RECORD_TYPE_WKT_TYPE;
+                       else
+                               return RECORD_TYPE_UNKNOWN;
+
+               } else if (type_length == 2) {
+                       if (strncmp((char *)type, "Sp", 2) == 0)
+                               return RECORD_TYPE_WKT_SMART_POSTER;
+                       else if (strncmp((char *) type, "Hr", 2) == 0)
+                               return RECORD_TYPE_WKT_HANDOVER_REQUEST;
+                       else if (strncmp((char *) type, "Hs", 2) == 0)
+                               return RECORD_TYPE_WKT_HANDOVER_SELECT;
+                       else if (strncmp((char *) type, "Hc", 2) == 0)
+                               return RECORD_TYPE_WKT_HANDOVER_CARRIER;
+                       else if (strncmp((char *) type, "ac", 2) == 0)
+                               return RECORD_TYPE_WKT_ALTERNATIVE_CARRIER;
+                       else if (strncmp((char *) type, "cr", 2) == 0)
+                               return RECORD_TYPE_WKT_COLLISION_RESOLUTION;
+                       else
+                               return RECORD_TYPE_UNKNOWN;
+
+               } else if (type_length == 3) {
+                       if (strncmp((char *)type, "act", 3) == 0)
+                               return RECORD_TYPE_WKT_ACTION;
+                       else if (strncmp((char *)type, "err", 3) == 0)
+                               return RECORD_TYPE_WKT_ERROR;
+                       else
+                               return RECORD_TYPE_UNKNOWN;
+
+               }
+
+       case RECORD_TNF_MIME:
+               return RECORD_TYPE_MIME_TYPE;
+
+       case RECORD_TNF_EXTERNAL:
+               return get_external_record_type(type, type_length);
+
+       }
+
+       return RECORD_TYPE_UNKNOWN;
+}
+
+static int build_record_type_string(struct near_ndef_record *rec)
+{
+       uint8_t tnf;
+
+       DBG("");
+
+       if (rec == NULL || rec->header == NULL)
+               return -EINVAL;
+
+       tnf = rec->header->tnf;
+
+       if (rec->header->rec_type == RECORD_TYPE_WKT_SMART_POSTER) {
+               rec->type = g_strdup_printf(RECORD_TYPE_WKT "U");
+               return 0;
+       }
+
+       switch (tnf) {
+       case RECORD_TNF_EMPTY:
+       case RECORD_TNF_UNKNOWN:
+       case RECORD_TNF_UNCHANGED:
+               return -EINVAL;
+
+       case RECORD_TNF_URI:
+       case RECORD_TNF_MIME:
+               rec->type = g_strndup(rec->header->type_name,
+                                     rec->header->type_len);
+               break;
+
+       case RECORD_TNF_WELLKNOWN:
+               rec->type = g_strdup_printf(RECORD_TYPE_WKT "%s",
+                                     rec->header->type_name);
+               break;
+
+       case RECORD_TNF_EXTERNAL:
+               rec->type = g_strdup_printf(RECORD_TYPE_EXTERNAL "%s",
+                                     rec->header->type_name);
+               break;
+       }
+
+       return 0;
+}
+
+static uint8_t validate_record_begin_and_end_bits(uint8_t *msg_mb,
+                                       uint8_t *msg_me, uint8_t rec_mb,
+                                       uint8_t rec_me)
+{
+       DBG("");
+
+       if (msg_mb == NULL || msg_me == NULL)
+               return 0;
+
+       /* Validating record header begin and end bits
+        * eg: Single record: [mb:1,me:1]
+        *     Two records:   [mb:1,me:0 - mb:0,me:1]
+        *     Three or more records [mb:1,me:0 - mb:0,me:0 .. mb:0,me:1]
+        **/
+
+       if (rec_mb == 1) {
+               if (*msg_mb != 1)
+                       *msg_mb = rec_mb;
+               else
+                       return -EINVAL;
+
+       }
+
+       if (rec_me == 1) {
+               if (*msg_me != 1) {
+                       if (*msg_mb == 1)
+                               *msg_me = rec_me;
+                       else
+                               return -EINVAL;
+
+               } else
+                       return -EINVAL;
+
+       }
+
+       return 0;
+}
+
+/**
+ * @brief Parse the ndef record header.
+ *
+ * Parse the ndef record header and cache the begin, end, chunkflag,
+ * short-record and type-name-format bits. ID length and field, record
+ * type, payload length and offset (where payload byte starts in input
+ * parameter). Validate offset for every step forward against total
+ * available length.
+ *
+ * @note : Caller responsibility to free the memory.
+ *
+ * @param[in] rec      ndef byte stream
+ * @param[in] offset   record header offset
+ * @param[in] length   total length in byte stream
+ *
+ * @return struct near_ndef_record_header * RecordHeader on Success
+ *                                          NULL   on Failure
+ */
+static struct near_ndef_record_header *parse_record_header(uint8_t *rec,
+                                       uint32_t offset, uint32_t length)
+{
+       struct near_ndef_record_header *rec_header = NULL;
+       uint8_t *type = NULL;
+       uint32_t header_len = 0;
+
+       DBG("length %d", length);
+
+       if (rec == NULL || offset >= length)
+               return NULL;
+
+       /* This check is for empty record. */
+       if ((length - offset) < NDEF_MSG_MIN_LENGTH)
+               return NULL;
+
+       rec_header = g_try_malloc0(sizeof(struct near_ndef_record_header));
+       if (rec_header == NULL)
+               return NULL;
+
+       rec_header->mb = RECORD_MB_BIT(rec[offset]);
+       rec_header->me = RECORD_ME_BIT(rec[offset]);
+       rec_header->sr = RECORD_SR_BIT(rec[offset]);
+       rec_header->il = RECORD_IL_BIT(rec[offset]);
+       rec_header->tnf = RECORD_TNF_BIT(rec[offset]);
+
+       DBG("mb %d me %d sr %d il %d tnf %d",
+               rec_header->mb, rec_header->me, rec_header->sr,
+               rec_header->il, rec_header->tnf);
+
+       offset++;
+       rec_header->type_len = rec[offset++];
+       header_len = 2; /* type length + header bits */
+
+       if (rec_header->sr == 1) {
+               rec_header->payload_len = rec[offset++];
+               header_len++;
+       } else {
+               rec_header->payload_len =
+                       g_ntohl(*((uint32_t *)(rec + offset)));
+               offset += 4;
+               header_len += 4;
+
+               if ((offset + rec_header->payload_len) > length)
+                       goto fail;
+       }
+
+       DBG("payload length %d", rec_header->payload_len);
+
+       if (rec_header->il == 1) {
+               rec_header->il_length = rec[offset++];
+               header_len++;
+
+               if ((offset + rec_header->payload_len) > length)
+                       goto fail;
+       }
+
+       if (rec_header->type_len > 0) {
+               if ((offset + rec_header->type_len) > length)
+                       goto fail;
+
+               type = g_try_malloc0(rec_header->type_len);
+               if (type == NULL)
+                       goto fail;
+
+               memcpy(type, rec + offset, rec_header->type_len);
+               offset += rec_header->type_len;
+               header_len += rec_header->type_len;
+
+               if ((offset + rec_header->payload_len) > length)
+                       goto fail;
+       }
+
+       if (rec_header->il_length > 0) {
+               if ((offset + rec_header->il_length) > length)
+                       goto fail;
+
+               rec_header->il_field = g_try_malloc0(rec_header->il_length);
+               if (rec_header->il_field == NULL)
+                       goto fail;
+
+               memcpy(rec_header->il_field, rec + offset,
+                                       rec_header->il_length);
+               offset += rec_header->il_length;
+               header_len += rec_header->il_length;
+
+               if ((offset + rec_header->payload_len) > length)
+                       goto fail;
+       }
+
+       rec_header->rec_type = get_record_type(rec_header->tnf, type,
+                                                       rec_header->type_len);
+       rec_header->offset = offset;
+       rec_header->header_len = header_len;
+       rec_header->type_name = g_strndup((char *) type, rec_header->type_len);
+
+       g_free(type);
+
+       return rec_header;
+
+fail:
+       near_error("parsing record header failed");
+
+       g_free(type);
+       g_free(rec_header->il_field);
+       g_free(rec_header->type_name);
+       g_free(rec_header);
+
+       return NULL;
+}
+
+/**
+ * @brief Parse the Text record payload
+ *
+ * Parse the Text payload.
+ *
+ * @param[in] payload NDEF pointer set to record payload first byte
+ * @param[in] length  payload_len
+ *
+ * @return struct near_ndef_text_payload * Payload on Success
+ *                                       NULL   on Failure
+ */
+
+static struct near_ndef_text_payload *
+parse_text_payload(uint8_t *payload, uint32_t length)
+{
+       struct near_ndef_text_payload *text_payload = NULL;
+       uint8_t status, lang_length;
+       uint32_t offset;
+
+       DBG("");
+
+       if (payload == NULL)
+               return NULL;
+
+       offset = 0;
+       text_payload = g_try_malloc0(sizeof(struct near_ndef_text_payload));
+       if (text_payload == NULL)
+               return NULL;
+
+       /* 0x80 is used to get 7th bit value (0th bit is LSB) */
+       status = ((payload[offset] & 0x80) >> 7);
+
+       text_payload->encoding = (status == 0) ?
+                                       g_strdup("UTF-8") : g_strdup("UTF-16");
+
+       /* 0x3F is used to get 5th-0th bits value (0th bit is LSB) */
+       lang_length = (payload[offset] & 0x3F);
+       offset++;
+
+       if (lang_length > 0) {
+               if ((offset + lang_length) >= length)
+                       goto fail;
+
+               text_payload->language_code = g_strndup(
+                                               (char *)(payload + offset),
+                                               lang_length);
+       } else {
+               text_payload->language_code = NULL;
+       }
+
+       offset += lang_length;
+
+       if ((length - lang_length - 1) > 0) {
+               text_payload->data = g_strndup((char *)(payload + offset),
+                                       length - lang_length - 1);
+       } else {
+               text_payload->data = NULL;
+       }
+
+       if (offset >= length)
+               goto fail;
+
+       DBG("Encoding  '%s'", text_payload->encoding);
+       DBG("Language Code  '%s'", text_payload->language_code);
+       DBG("Data  '%s'", text_payload->data);
+
+       return text_payload;
+
+fail:
+       near_error("text payload parsing failed");
+       free_text_payload(text_payload);
+
+       return NULL;
+}
+
+/**
+ * @brief Parse the URI record payload
+ *
+ * Parse the URI payload.
+ *
+ * @param[in] payload NDEF pointer set to record payload first byte
+ * @param[in] length  Payload length
+ *
+ * @return struct near_ndef_uri_payload * payload on Success
+ *                                       NULL   on Failure
+ */
+
+static struct near_ndef_uri_payload *
+parse_uri_payload(uint8_t *payload, uint32_t length)
+{
+       struct near_ndef_uri_payload *uri_payload = NULL;
+       uint32_t index, offset;
+
+       DBG("");
+
+       if (payload == NULL)
+               return NULL;
+
+       offset = 0;
+       uri_payload = g_try_malloc0(sizeof(struct near_ndef_uri_payload));
+       if (uri_payload == NULL)
+               return NULL;
+
+       uri_payload->identifier = payload[offset];
+       offset++;
+
+       uri_payload->field_length = length - 1;
+
+       if (uri_payload->field_length > 0) {
+               uri_payload->field = g_try_malloc0(uri_payload->field_length);
+               if (uri_payload->field == NULL)
+                       goto fail;
+
+               memcpy(uri_payload->field, payload + offset,
+                               uri_payload->field_length);
+
+               for (index = 0; index < uri_payload->field_length; index++) {
+                       /* URI Record Type Definition 1.0 [3.2.3]
+                        * Any character value within the URI between
+                        * (and including) 0 and 31 SHALL be recorded as
+                        * an error, and the URI record to be discarded */
+                       if (uri_payload->field[index] <= 31)
+                               goto fail;
+               }
+
+       }
+
+       DBG("Identifier  '0X%X'", uri_payload->identifier);
+       DBG("Field  '%.*s'", uri_payload->field_length, uri_payload->field);
+
+       return uri_payload;
+
+fail:
+       near_error("uri payload parsing failed");
+       free_uri_payload(uri_payload);
+
+       return NULL;
+}
+
+/**
+ * @brief Validate titles records language code in Smartposter.
+ * There must not be two or more records with the same language identifier.
+ *
+ * @param[in] GSList *  list of title records (struct near_ndef_text_payload *)
+ *
+ * @return Zero on success
+ *         Negative value on failure
+ */
+
+static int8_t validate_language_code_in_sp_record(GSList *titles)
+{
+       uint8_t i, j, length;
+       struct near_ndef_text_payload *title1, *title2;
+
+       DBG("");
+
+       if (titles == NULL)
+               return -EINVAL;
+
+       length = g_slist_length(titles);
+
+       for (i = 0; i < length; i++) {
+               title1 = g_slist_nth_data(titles, i);
+
+               for (j = i + 1; j < length; j++) {
+                       title2 = g_slist_nth_data(titles, j);
+
+                       if ((title1->language_code == NULL) &&
+                                       (title2->language_code == NULL))
+                               continue;
+
+                       if (g_strcmp0(title1->language_code,
+                                       title2->language_code) == 0)
+                               return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * @brief Parse the smart poster record payload.
+ *
+ * Parse the smart poster payload and cache the
+ * data in respective fields of smart poster structure.
+ *
+ * @note Caller responsibility to free the memory.
+ *
+ * @param[in] payload NDEF pointer set to record payload first byte
+ * @param[in] length Record payload length
+ *
+ * @return struct near_ndef_sp_payload * Record on Success
+ *                                      NULL   on Failure
+ */
+
+static struct near_ndef_sp_payload *
+parse_sp_payload(uint8_t *payload, uint32_t length)
+{
+       struct near_ndef_sp_payload *sp_payload = NULL;
+       struct near_ndef_record_header *rec_header = NULL;
+       uint8_t mb = 0, me = 0, i;
+       uint32_t offset;
+       GSList *titles = NULL, *temp;
+
+       DBG("");
+
+       if (payload == NULL)
+               return NULL;
+
+       offset = 0;
+       sp_payload = g_try_malloc0(sizeof(struct near_ndef_sp_payload));
+       if (sp_payload == NULL)
+               return NULL;
+
+       while (offset < length) {
+
+               DBG("Record header : 0x%x", payload[offset]);
+
+               rec_header = parse_record_header(payload, offset, length);
+               if (rec_header == NULL)
+                       goto fail;
+
+               if (validate_record_begin_and_end_bits(&mb, &me,
+                                       rec_header->mb, rec_header->me) != 0) {
+                       DBG("validate mb me failed");
+                       goto fail;
+               }
+
+               offset = rec_header->offset;
+
+               switch (rec_header->rec_type) {
+               case RECORD_TYPE_WKT_SMART_POSTER:
+               case RECORD_TYPE_WKT_HANDOVER_REQUEST:
+               case RECORD_TYPE_WKT_HANDOVER_SELECT:
+               case RECORD_TYPE_WKT_HANDOVER_CARRIER:
+               case RECORD_TYPE_WKT_ALTERNATIVE_CARRIER:
+               case RECORD_TYPE_WKT_COLLISION_RESOLUTION:
+               case RECORD_TYPE_MIME_TYPE:
+               case RECORD_TYPE_WKT_ERROR:
+               case RECORD_TYPE_UNKNOWN:
+               case RECORD_TYPE_ERROR:
+                       break;
+
+               case RECORD_TYPE_WKT_URI:
+                       /* URI record should be only one. */
+                       if (sp_payload->uri != NULL)
+                               goto fail;
+
+                       sp_payload->uri = parse_uri_payload(payload + offset,
+                                               rec_header->payload_len);
+                       if (sp_payload->uri == NULL)
+                               goto fail;
+
+                       break;
+
+               case RECORD_TYPE_WKT_TEXT:
+                       /*
+                        * Title records can zero or more. First fill the
+                        * records in list and validate language identifier
+                        * and then cache them into sp record structure.
+                        */
+                       {
+                       struct near_ndef_text_payload *title;
+                       title = parse_text_payload(payload + offset,
+                                               rec_header->payload_len);
+                       if (title == NULL)
+                               goto fail;
+
+                       titles = g_slist_append(titles, title);
+                       }
+                       break;
+
+               case RECORD_TYPE_WKT_SIZE:
+                       /*
+                        * If payload length is not exactly 4 bytes
+                        * then record is wrong.
+                        */
+                       if (rec_header->payload_len != 4)
+                               goto fail;
+
+                       sp_payload->size =
+                               g_ntohl(*((uint32_t *)(payload + offset)));
+                       break;
+
+               case RECORD_TYPE_WKT_TYPE:
+
+                       if (rec_header->payload_len > 0) {
+                               sp_payload->type = g_try_malloc0(
+                                               rec_header->payload_len);
+                               if (sp_payload->type == NULL)
+                                       goto fail;
+
+                               sp_payload->type = g_strndup(
+                                               (char *) payload + offset,
+                                               rec_header->payload_len);
+                       }
+
+                       break;
+
+               case RECORD_TYPE_WKT_ACTION:
+                       /*
+                        * If the action record exists, payload should be
+                        * single byte, otherwise consider it as error.
+                        */
+                       if (rec_header->payload_len != 1)
+                               goto fail;
+
+                       sp_payload->action =
+                               g_strdup(action_to_string(payload[offset]));
+
+                       break;
+               }
+
+               offset += rec_header->payload_len;
+               g_free(rec_header->il_field);
+               g_free(rec_header->type_name);
+               g_free(rec_header);
+               rec_header = NULL;
+       }
+
+       /*
+        * Code to fill smart poster record structure from
+        * 'titles' list.
+        */
+       if (titles == NULL)
+               return sp_payload;
+
+       if (validate_language_code_in_sp_record(titles) != 0) {
+               DBG("language code validation failed");
+               goto fail;
+       }
+
+       temp = titles;
+       sp_payload->number_of_title_records = g_slist_length(temp);
+       sp_payload->title_records = g_try_malloc0(
+                               sp_payload->number_of_title_records *
+                                sizeof(struct near_ndef_text_payload *));
+       if (sp_payload->title_records == NULL)
+               goto fail;
+
+       for (i = 0; i < sp_payload->number_of_title_records; i++) {
+               sp_payload->title_records[i] = temp->data;
+               temp = temp->next;
+       }
+
+       g_slist_free(titles);
+       titles = NULL;
+
+       return sp_payload;
+
+fail:
+       near_error("smart poster payload parsing failed");
+
+       if (rec_header != NULL) {
+               g_free(rec_header->type_name);
+               g_free(rec_header->il_field);
+               g_free(rec_header);
+       }
+
+       free_sp_payload(sp_payload);
+       g_slist_free(titles);
+
+       return NULL;
+}
+
+static struct near_ndef_mime_payload *
+parse_mime_type(struct near_ndef_record *record,
+                       uint8_t *ndef_data, size_t ndef_length, size_t offset,
+                       uint32_t payload_length, near_bool_t action)
+{
+       struct near_ndef_mime_payload *mime = NULL;
+       int err = 0;
+
+       DBG("");
+
+       if ((ndef_data == NULL) || ((offset + payload_length) > ndef_length))
+               return NULL;
+
+       mime = g_try_malloc0(sizeof(struct near_ndef_mime_payload));
+       if (mime == NULL)
+               return NULL;
+
+       mime->type = g_strdup(record->header->type_name);
+
+       DBG("MIME Type  '%s' action: %d", mime->type, action);
+       if (strcmp(mime->type, BT_MIME_STRING_2_1) == 0) {
+               mime->handover.carrier_type = NEAR_CARRIER_BLUETOOTH;
+               err = __near_bluetooth_parse_oob_record(BT_MIME_V2_1,
+                               &ndef_data[offset], &mime->handover.properties,
+                               action);
+       } else if (strcmp(mime->type, BT_MIME_STRING_2_0) == 0) {
+               mime->handover.carrier_type = NEAR_CARRIER_BLUETOOTH;
+               err = __near_bluetooth_parse_oob_record(BT_MIME_V2_0,
+                               &ndef_data[offset], &mime->handover.properties,
+                               action);
+       }
+
+       if (err < 0) {
+               DBG("Parsing mime error %d", err);
+               g_free(mime->type);
+               g_free(mime);
+               return NULL;
+       }
+
+       return mime;
+}
+
+/* Set the MB bit in message header */
+static uint8_t near_ndef_set_mb(uint8_t *hdr, near_bool_t first_rec)
+{
+       /* Reset bits 0x40 */
+       *hdr &= (0xFF & (~RECORD_MB));
+
+       /* Set if needed */
+       if (first_rec == TRUE)
+               *hdr |= RECORD_MB;
+
+       return *hdr;
+}
+
+/* Set the MB/ME bit in message header */
+static uint8_t near_ndef_set_me(uint8_t *hdr, near_bool_t last_rec)
+{
+       /* Reset bits 0x80 */
+       *hdr &= (0xFF & (~RECORD_ME));
+
+       /* Set if needed */
+       if (last_rec == TRUE)
+               *hdr |= RECORD_ME;
+
+       return *hdr;
+}
+
+/* Set the MB/ME bit in message header */
+static uint8_t near_ndef_set_mb_me(uint8_t *hdr, near_bool_t first_rec,
+                                               near_bool_t last_rec)
+{
+       near_ndef_set_mb(hdr, first_rec);
+       return near_ndef_set_me(hdr, last_rec);
+}
+
+/**
+ * @brief Allocates ndef message structure
+ *
+ * Allocates ndef message structure and fill message header byte,
+ * type length byte, payload length and type name. Offset is payload
+ * first byte (caller of this API can start filling their payload
+ * from offset value).
+ *
+ * @note : caller responsibility to free the input and output
+ *         parameters memory.
+ *
+ * @param[in] type_name    Record type name
+ * @param[in] payload_len  Record payload length
+ * @param[in] payload_id   Record payload id string
+ * @param[in] payload_id_len  Record payload id string length
+ * @param[in] tnf          Type name format to set
+ * @param[in] first_rec    Message begin (MB) flag
+ * @param[in] last_rec     Message end (ME) flag
+ *
+ * @return struct near_ndef_message * - Success
+ *         NULL - Failure
+ */
+static struct near_ndef_message *ndef_message_alloc_complete(char *type_name,
+               uint32_t payload_len,
+               char *payload_id,
+               uint8_t payload_id_len,
+               enum record_tnf tnf,
+               near_bool_t first_rec,
+               near_bool_t last_rec)
+{
+       struct near_ndef_message *msg;
+       uint8_t hdr = 0, type_len, sr_bit, il_bit, id_len;
+
+       msg = g_try_malloc0(sizeof(struct near_ndef_message));
+       if (msg == NULL)
+               return NULL;
+
+       msg->length = 0;
+       msg->offset = 0;
+       msg->length++; /* record header*/
+       msg->length++; /* type name length byte*/
+
+       type_len = (type_name != NULL) ? strlen(type_name) : 0;
+       id_len = (payload_id != NULL) ? payload_id_len : 0;
+       sr_bit =  (payload_len <= NDEF_MSG_SHORT_RECORD_MAX_LENGTH)
+                                       ? TRUE : FALSE;
+
+       il_bit = (payload_id != NULL) ? TRUE : FALSE;
+
+       msg->length += (sr_bit == TRUE) ? 1 : 4;
+       msg->length += (il_bit == TRUE) ? 1 : 0;
+       msg->length += type_len;
+       msg->length += payload_len;
+       msg->length += id_len;
+
+       msg->data = g_try_malloc0(msg->length);
+       if (msg->data == NULL)
+               goto fail;
+
+       /* Set MB ME bits */
+       hdr = near_ndef_set_mb_me(&hdr, first_rec, last_rec);
+
+       if (sr_bit == TRUE)
+               hdr |= RECORD_SR;
+
+       hdr = RECORD_TNF_WKT_SET(hdr);
+       if (il_bit == TRUE)
+               hdr |= RECORD_IL;
+
+       switch (tnf) {
+       case RECORD_TNF_EMPTY:
+               hdr = RECORD_TNF_EMPTY_SET(hdr);
+               break;
+
+       case RECORD_TNF_URI:
+               hdr = RECORD_TNF_URI_SET(hdr);
+               break;
+
+       case RECORD_TNF_EXTERNAL:
+               hdr = RECORD_TNF_EXTERNAL_SET(hdr);
+               break;
+       case RECORD_TNF_UNKNOWN:
+               hdr = RECORD_TNF_UKNOWN_SET(hdr);
+               break;
+
+       case RECORD_TNF_UNCHANGED:
+               hdr = RECORD_TNF_UNCHANGED_SET(hdr);
+               break;
+
+       case RECORD_TNF_WELLKNOWN:
+               hdr = RECORD_TNF_WKT_SET(hdr);
+               break;
+
+       case RECORD_TNF_MIME:
+               hdr = RECORD_TNF_MIME_SET(hdr);
+               break;
+       }
+
+       msg->data[msg->offset++] = hdr;
+       msg->data[msg->offset++] = type_len;
+
+       if (sr_bit == TRUE) {
+               msg->data[msg->offset++] = payload_len;
+       } else {
+               fillb32((msg->data + msg->offset), payload_len);
+               msg->offset += 4;
+       }
+
+       if (il_bit == TRUE)
+               msg->data[msg->offset++] = payload_id_len;
+
+       if (type_name != NULL) {
+               memcpy(msg->data + msg->offset, type_name, type_len);
+               msg->offset += type_len;
+       }
+
+       if (il_bit == TRUE) {
+               memcpy(msg->data + msg->offset, payload_id, payload_id_len);
+               msg->offset += payload_id_len;
+       }
+
+       return msg;
+
+fail:
+       near_error("ndef message struct allocation failed");
+       g_free(msg->data);
+       g_free(msg);
+
+       return NULL;
+}
+
+/*
+ *  @brief Allocates ndef message structure
+ *
+ *  This is a wrapper to ndef_message_alloc, as, in most cases,
+ *  there's no payload id, and MB=TRUE and ME=TRUE. Default type name format
+ *  is also set to RECORD_TNF_WELLKNOWN
+ *
+ */
+static struct near_ndef_message *ndef_message_alloc(char* type_name,
+                                                       uint32_t payload_len)
+{
+       return ndef_message_alloc_complete(type_name, payload_len,
+                       NULL, 0,
+                       RECORD_TNF_WELLKNOWN,
+                       TRUE, TRUE);
+}
+
+static struct near_ndef_ac_payload *parse_ac_payload(uint8_t *payload,
+                                               uint32_t length)
+{
+       struct near_ndef_ac_payload *ac_payload = NULL;
+       uint32_t offset;
+
+       DBG("");
+
+       if (payload == NULL)
+               return NULL;
+
+       offset = 0;
+       ac_payload = g_try_malloc0(sizeof(struct near_ndef_ac_payload));
+       if (ac_payload == NULL)
+               goto fail;
+
+       /* Carrier flag */
+       ac_payload->cps = payload[offset];    /* TODO Check enum */
+       offset++;
+
+       /* Carrier data reference length */
+       ac_payload->cdr_len = payload[offset];
+       offset++;
+
+       /* Carrier data reference */
+       ac_payload->cdr = payload[offset];
+       offset = offset + ac_payload->cdr_len;
+
+       /* Auxiliary data reference count */
+       ac_payload->adata_refcount = payload[offset];
+       offset++;
+
+       if (ac_payload->adata_refcount == 0)
+               return ac_payload;
+
+       /* save the auxiliary data reference */
+       ac_payload->adata = g_try_malloc0(
+                       ac_payload->adata_refcount * sizeof(uint16_t));
+       if (ac_payload->adata == NULL)
+               goto fail;
+
+       memcpy(ac_payload->adata, payload + offset,
+                       ac_payload->adata_refcount * sizeof(uint16_t));
+
+       /* and leave */
+       return ac_payload;
+
+fail:
+       near_error("ac payload parsing failed");
+       free_ac_payload(ac_payload);
+
+       return NULL;
+}
+
+/* carrier power state & carrier reference */
+static struct near_ndef_message *near_ndef_prepare_ac_message(uint8_t cps,
+                                                               char cdr)
+{
+       struct near_ndef_message *ac_msg;
+
+       /* alloc "ac" message minus adata*/
+       ac_msg = ndef_message_alloc_complete("ac", AC_RECORD_PAYLOAD_LEN,
+                                       NULL, 0,
+                                       RECORD_TNF_WELLKNOWN,
+                                       TRUE, TRUE);
+       if (ac_msg == NULL)
+               return NULL;
+
+       /* Prepare ac message */
+       ac_msg->data[ac_msg->offset++] = cps;
+       ac_msg->data[ac_msg->offset++] = 1;     /* cdr_len def size */
+       ac_msg->data[ac_msg->offset++] = cdr;   /* cdr */
+       ac_msg->data[ac_msg->offset] = 0;       /* adata ref count */
+
+       /* Check if we want an empty record */
+       if (cdr == 0x00)
+               ac_msg->length = 0;
+
+       return ac_msg;
+}
+
+/* Collision Record message */
+static struct near_ndef_message *near_ndef_prepare_cr_message(uint16_t cr_id)
+{
+       struct near_ndef_message *cr_msg;
+
+       cr_msg = ndef_message_alloc_complete("cr", sizeof(uint16_t),
+                                               NULL, 0,
+                                               RECORD_TNF_WELLKNOWN,
+                                               TRUE, TRUE);
+       if (cr_msg == NULL)
+               return NULL;
+
+       /* Prepare ac message */
+       *(uint16_t *)(cr_msg->data + cr_msg->offset) = g_htons(cr_id);
+
+       return cr_msg;
+}
+
+/* Prepare the bluetooth data record */
+static struct near_ndef_message *near_ndef_prepare_bt_message(uint8_t *bt_data,
+                       int bt_data_len, char cdr, uint8_t cdr_len)
+{
+       struct near_ndef_message *bt_msg = NULL;
+
+       if (bt_data == NULL)
+               goto fail;
+
+       bt_msg = ndef_message_alloc_complete(BT_MIME_STRING_2_1, bt_data_len,
+                                       &cdr, cdr_len, RECORD_TNF_MIME,
+                                       TRUE, TRUE);
+       if (bt_msg == NULL)
+               goto fail;
+
+       /* store data */
+       memcpy(bt_msg->data + bt_msg->offset, bt_data, bt_data_len);
+
+       return bt_msg;
+
+fail:
+       if (bt_msg != NULL)
+               g_free(bt_msg->data);
+
+       g_free(bt_msg);
+
+       return NULL;
+}
+
+/*
+ * Walk thru the cfgs list and set the carriers bitfield
+ */
+static uint8_t near_get_carriers_list(struct near_ndef_record *record)
+{
+       struct near_ndef_ho_payload *ho = record->ho;
+       uint8_t carriers;
+       int i;
+
+       carriers = 0;
+
+       for (i = 0; i < ho->number_of_cfg_payloads; i++) {
+               struct near_ndef_mime_payload *rec = ho->cfg_payloads[i];
+
+               carriers |= rec->handover.carrier_type;
+       }
+
+       return carriers;
+}
+
+/*
+ * Walk thru the cfgs list and get the properties corresponding
+ * to the carrier bit.
+ */
+static uint16_t near_get_carrier_properties(struct near_ndef_record *record,
+                                               uint8_t carrier_bit)
+{
+       struct near_ndef_ho_payload *ho = record->ho;
+       int i;
+
+       for (i = 0; i < ho->number_of_cfg_payloads; i++) {
+               struct near_ndef_mime_payload *rec = ho->cfg_payloads[i];
+
+               if ((rec->handover.carrier_type & carrier_bit) != 0)
+                       return rec->handover.properties;
+       }
+
+       return OOB_PROPS_EMPTY;
+}
+
+/*
+ * @brief Prepare Handover select record with mandatory fields.
+ *
+ * TODO: only mime (BT) are supported now... Wifi will come soon...
+ * Only 1 ac record + 1 collision record+ Hr header
+ */
+struct near_ndef_message *near_ndef_prepare_handover_record(char* type_name,
+                                       struct near_ndef_record *record,
+                                       uint8_t carriers)
+
+{
+       uint8_t *oob_data = NULL;
+       int oob_size;
+       struct near_ndef_message *hs_msg = NULL;
+       struct near_ndef_message *ac_msg = NULL;
+       struct near_ndef_message *cr_msg = NULL;
+       struct near_ndef_message *bt_msg = NULL;
+       uint16_t props;
+       uint16_t collision;
+       uint8_t hs_length;
+       near_bool_t mb, me;
+       char cdr = '0';                 /* Carrier data reference */
+
+       if (record->ho == NULL)
+               goto fail;
+
+       collision = record->ho->collision_record;
+
+       /* no cr on Hs */
+       if (strncmp((char *) type_name, "Hs", 2) == 0)
+               collision = 0;
+
+       /* Walk the cfg list to get the carriers */
+       if (carriers == NEAR_CARRIER_UNKNOWN)
+               carriers = near_get_carriers_list(record);
+
+       /*
+        * Prepare records to be added
+        * now prepare the cr message: MB=1 ME=0
+        */
+       if (collision != 0) {
+               cr_msg = near_ndef_prepare_cr_message(collision);
+               if (cr_msg == NULL)
+                       goto fail;
+       }
+
+       /* If there's no carrier, we create en empty ac record */
+       if (carriers == NEAR_CARRIER_EMPTY)
+               cdr = 0x00;
+
+       /*
+        * ac record: if only one: MB=0 ME=1
+        * cps should be active
+        */
+       ac_msg = near_ndef_prepare_ac_message(CPS_ACTIVE, cdr);
+       if (ac_msg == NULL)
+               goto fail;
+
+       if (carriers & NEAR_CARRIER_BLUETOOTH) {
+               /* Retrieve the bluetooth settings */
+               props = near_get_carrier_properties(record,
+                                                       NEAR_CARRIER_BLUETOOTH);
+
+               oob_data = __near_bluetooth_local_get_properties(&oob_size,
+                                                                       props);
+               if (oob_data == NULL) {
+                       near_error("Getting Bluetooth OOB data failed");
+                       goto fail;
+               }
+
+               bt_msg = near_ndef_prepare_bt_message(oob_data, oob_size,
+                                                               cdr, 1);
+               if (bt_msg == NULL)
+                       goto fail;
+
+               near_ndef_set_mb_me(bt_msg->data, FALSE, TRUE);
+       }
+
+       if (carriers & NEAR_CARRIER_WIFI) {
+               /* TODO LATER */
+               goto fail;
+       }
+
+       /*
+        * Build the complete handover frame
+        * Prepare Hs or Hr message (1 for version)
+        */
+       hs_length = 1 + ac_msg->length;
+       if (cr_msg != NULL)
+               hs_length += cr_msg->length;
+
+       if (bt_msg != NULL)
+               hs_msg = ndef_message_alloc(type_name, hs_length +
+                                                               bt_msg->length);
+       else
+               hs_msg = ndef_message_alloc(type_name, hs_length);
+       if (hs_msg == NULL)
+               goto fail;
+
+       /*
+        * The handover payload length is not the *real* length.
+        * The PL refers to the NDEF record, not the extra ones.
+        * So, we have to fix the payload length in the header.
+        */
+       hs_msg->data[NDEF_PAYLOAD_LENGTH_OFFSET] = hs_length;
+
+       near_ndef_set_mb_me(hs_msg->data, TRUE, TRUE);
+
+       if ((carriers != NEAR_CARRIER_EMPTY) || (cr_msg != NULL))
+               near_ndef_set_me(hs_msg->data, FALSE);
+
+       /* Add version */
+       hs_msg->data[hs_msg->offset++] = HANDOVER_VERSION;
+
+       /* Prepare MB / ME flags */
+       /* cr */
+       mb = TRUE;
+       me = TRUE;
+       if (cr_msg != NULL) {
+               near_ndef_set_mb_me(cr_msg->data, mb, me);
+               if (ac_msg->length != 0)
+                       near_ndef_set_me(cr_msg->data, FALSE);
+               mb = FALSE;
+       }
+
+       /* ac */
+       if (ac_msg->length != 0)
+               near_ndef_set_mb_me(ac_msg->data, mb, TRUE); /* xxx, TRUE */
+
+       /* Now, copy the datas */
+       /* copy cr */
+       if (cr_msg != NULL) {
+               memcpy(hs_msg->data + hs_msg->offset, cr_msg->data,
+                               cr_msg->length);
+               hs_msg->offset += cr_msg->length;
+       }
+
+       /* copy ac */
+       memcpy(hs_msg->data + hs_msg->offset, ac_msg->data, ac_msg->length);
+       hs_msg->offset += ac_msg->length;
+
+       if (hs_msg->offset > hs_msg->length)
+               goto fail;
+
+       /*
+        * Additional NDEF (associated to the ac records)
+        * Add the BT record which is not part in hs initial size
+        */
+       if (bt_msg != NULL)
+               memcpy(hs_msg->data + hs_msg->offset, bt_msg->data,
+                                                       bt_msg->length);
+
+       if (ac_msg != NULL) {
+               g_free(ac_msg->data);
+               g_free(ac_msg);
+       }
+
+       if (cr_msg != NULL) {
+               g_free(cr_msg->data);
+               g_free(cr_msg);
+       }
+
+       if (bt_msg != NULL) {
+               g_free(bt_msg->data);
+               g_free(bt_msg);
+       }
+
+       g_free(oob_data);
+
+       DBG("Hs NDEF done");
+
+       return hs_msg;
+
+fail:
+       near_error("handover select record preparation failed");
+
+       if (ac_msg != NULL) {
+               g_free(ac_msg->data);
+               g_free(ac_msg);
+       }
+
+       if (cr_msg != NULL) {
+               g_free(cr_msg->data);
+               g_free(cr_msg);
+       }
+
+       if (hs_msg != NULL) {
+               g_free(hs_msg->data);
+               g_free(hs_msg);
+       }
+
+       if (bt_msg != NULL) {
+               g_free(bt_msg->data);
+               g_free(bt_msg);
+       }
+
+       g_free(oob_data);
+
+       return NULL;
+}
+
+/* Code to fill hr record structure from acs and mimes lists */
+static int near_fill_ho_payload(struct near_ndef_ho_payload *ho,
+                                       GSList *acs, GSList *mimes)
+{
+       int rec_count;
+       int i;
+       GSList *temp;
+
+       rec_count = g_slist_length(acs);
+       ho->ac_payloads = g_try_malloc0(rec_count *
+                       sizeof(struct near_ndef_ac_payload *));
+       if (ho->ac_payloads == NULL)
+               goto fail;
+       temp = acs;
+       for (i = 0; i < rec_count; i++) {
+               ho->ac_payloads[i] = temp->data;
+               temp = temp->next;
+       }
+       ho->number_of_ac_payloads = rec_count;
+       g_slist_free(acs);
+
+       /* Same process for cfg mimes */
+       rec_count = g_slist_length(mimes);
+       ho->cfg_payloads = g_try_malloc0(rec_count *
+                       sizeof(struct near_ndef_mime_payload *));
+       if (ho->cfg_payloads == NULL)
+               goto fail;
+       temp = mimes;
+       for (i = 0; i < rec_count; i++) {
+               ho->cfg_payloads[i] = temp->data;
+               temp = temp->next;
+       }
+
+       ho->number_of_cfg_payloads = rec_count;
+       g_slist_free(mimes);
+
+       return 0;
+fail:
+       g_free(ho->ac_payloads);
+       g_free(ho->cfg_payloads);
+       ho->ac_payloads = NULL;
+       ho->cfg_payloads = NULL;
+       return -ENOMEM;
+}
+
+/*
+ * @brief Parse the Handover request record payload
+ * This function will parse an Hr record payload, retrieving sub records
+ * like (ac, cr, er) but it  will also get the associated
+ * ndefs (eg: handover carrier record, mime type for BT)
+ * In a handover frame, only the following types are expected:
+ *     RECORD_TYPE_WKT_HANDOVER_CARRIER:
+ *     RECORD_TYPE_WKT_COLLISION_RESOLUTION
+ *     RECORD_TYPE_MIME_TYPE
+ *     RECORD_TYPE_WKT_ALTERNATIVE_CARRIER
+ */
+static struct near_ndef_ho_payload *parse_ho_payload(enum record_type rec_type,
+               uint8_t *payload, uint32_t ho_length, size_t frame_length,
+               uint8_t ho_mb, uint8_t ho_me)
+{
+       struct near_ndef_ho_payload *ho_payload = NULL;
+       struct near_ndef_ac_payload *ac = NULL;
+       struct near_ndef_mime_payload *mime = NULL;
+       struct near_ndef_record *trec = NULL;
+       GSList *acs = NULL, *mimes = NULL;
+       uint8_t mb = 0, me = 0;
+       uint32_t offset;
+       int16_t count_ac = 0;
+       near_bool_t bt_pair;
+
+       DBG("");
+
+       if (payload == NULL)
+               return NULL;
+       offset = 0;
+
+       /* Create the handover record payload */
+       ho_payload = g_try_malloc0(sizeof(struct near_ndef_ho_payload));
+       if (ho_payload == NULL)
+               return NULL;
+
+       /* Version is the first mandatory field of hr payload */
+       ho_payload->version = payload[offset];
+
+       /* If major is different, reply with an empty Hs */
+       if (HANDOVER_MAJOR(ho_payload->version) !=
+           HANDOVER_MAJOR(HANDOVER_VERSION)) {
+               near_error("Unsupported version (%d)", ho_payload->version);
+               /* Skip parsing and return an empty record */
+               return ho_payload;
+       }
+
+       offset = offset + 1;
+
+       /* We should work on the whole frame */
+       ho_length = frame_length;
+
+       while (offset < ho_length) {
+               /* Create local record for mime parsing */
+               trec = g_try_malloc0(sizeof(struct near_ndef_record));
+               if (trec == NULL)
+                       return NULL;
+
+               trec->header = parse_record_header(payload, offset, ho_length);
+
+               if (trec->header == NULL)
+                       goto fail;
+
+               offset = trec->header->offset;
+
+               switch (trec->header->rec_type) {
+               case RECORD_TYPE_WKT_SMART_POSTER:
+               case RECORD_TYPE_WKT_SIZE:
+               case RECORD_TYPE_WKT_TEXT:
+               case RECORD_TYPE_WKT_TYPE:
+               case RECORD_TYPE_WKT_ACTION:
+               case RECORD_TYPE_WKT_URI:
+               case RECORD_TYPE_WKT_HANDOVER_REQUEST:
+               case RECORD_TYPE_WKT_HANDOVER_SELECT:
+               case RECORD_TYPE_WKT_ERROR:
+               case RECORD_TYPE_UNKNOWN:
+               case RECORD_TYPE_ERROR:
+                       break;
+
+               case RECORD_TYPE_WKT_HANDOVER_CARRIER:
+                       DBG("HANDOVER_CARRIER");
+                       /*
+                        * TODO process Hc record too !!!
+                        * Used for Wifi session
+                        */
+                       break;
+
+               case RECORD_TYPE_MIME_TYPE:
+                       DBG("TYPE_MIME_TYPE");
+
+                       /* check mb/me bits */
+                       if (validate_record_begin_and_end_bits(&ho_mb, &ho_me,
+                               trec->header->mb, trec->header->me) != 0) {
+                               DBG("validate mb me failed");
+                               goto fail;
+                       }
+
+                       /*
+                        * In Handover, the mime type gives bluetooth handover
+                        * configuration datas.
+                        * If we initiated the session, the received Hs frame
+                        * is the signal to launch the pairing.
+                        */
+                       if (rec_type == RECORD_TYPE_WKT_HANDOVER_SELECT)
+                               bt_pair = TRUE;
+                       else
+                               bt_pair = FALSE;
+
+                       mime = parse_mime_type(trec, payload, frame_length,
+                                       offset, trec->header->payload_len,
+                                       bt_pair);
+                       if (mime == NULL)
+                               goto fail;
+
+                       /* add the mime to the list */
+                       mimes = g_slist_append(mimes, mime);
+                       count_ac--;
+                       if (count_ac == 0)
+                               offset = ho_length;
+                       break;
+
+               case RECORD_TYPE_WKT_COLLISION_RESOLUTION:
+                       DBG("COLLISION_RESOLUTION");
+
+                       /* check nested mb/me bits */
+                       if (validate_record_begin_and_end_bits(&mb, &me,
+                               trec->header->mb, trec->header->me) != 0) {
+                               DBG("validate mb me failed");
+                               goto fail;
+                       }
+
+                       ho_payload->collision_record =
+                               g_ntohs(*((uint16_t *)(payload + offset)));
+                       break;
+
+               case RECORD_TYPE_WKT_ALTERNATIVE_CARRIER:
+                       DBG("ALTERNATIVE_CARRIER");
+
+                       /* check nested mb/me bits */
+                       if (validate_record_begin_and_end_bits(&mb, &me,
+                               trec->header->mb, trec->header->me) != 0) {
+                               DBG("validate mb me failed");
+                               goto fail;
+                       }
+
+                       ac = parse_ac_payload(payload + offset,
+                                       trec->header->payload_len);
+                       if (ac == NULL)
+                               goto fail;
+
+                       acs = g_slist_append(acs, ac);
+
+                       /* TODO check if adata are present */
+                       count_ac++;
+                       break;
+               }
+
+               offset += trec->header->payload_len;
+               g_free(trec->header->il_field);
+               g_free(trec->header->type_name);
+               g_free(trec->header);
+               trec->header = NULL;
+
+               g_free(trec);
+       }
+
+       if ((acs == NULL) || (mimes == NULL))
+               return ho_payload;
+
+       /* Save the records */
+       if (near_fill_ho_payload(ho_payload, acs, mimes) < 0)
+               goto fail;
+
+       DBG("handover payload parsing complete");
+
+       return ho_payload;
+
+fail:
+       near_error("handover payload parsing failed");
+
+       if (trec != NULL) {
+               if (trec->header != NULL) {
+                       g_free(trec->header->type_name);
+                       g_free(trec->header->il_field);
+                       g_free(trec->header);
+               }
+               g_free(trec);
+       }
+
+       free_ho_payload(ho_payload);
+
+       return NULL;
+}
+
+int __near_ndef_record_register(struct near_ndef_record *record, char *path)
+{
+       record->path = path;
+
+       g_dbus_register_interface(connection, record->path,
+                                               NFC_RECORD_INTERFACE,
+                                               record_methods,
+                                               NULL, NULL,
+                                               record, NULL);
+
+       return 0;
+}
+
+/**
+ * @brief Parse message represented by bytes block
+ *
+ * @param[in] ndef_data   pointer on data representing ndef message
+ * @param[in] ndef_length size of ndef_data
+ * @param[out]           records list, contains all the records
+ *                                     from parsed message
+ */
+GList *near_ndef_parse_msg(uint8_t *ndef_data, size_t ndef_length)
+{
+       GList *records;
+       uint8_t p_mb = 0, p_me = 0, *record_start;
+       size_t offset = 0;
+       struct near_ndef_record *record = NULL;
+
+       DBG("");
+
+       records = NULL;
+
+       if (ndef_data == NULL ||
+               ndef_length < NDEF_MSG_MIN_LENGTH)
+                       goto fail;
+
+       while (offset < ndef_length) {
+
+               DBG("Record Header : 0x%X", ndef_data[offset]);
+
+               record = g_try_malloc0(sizeof(struct near_ndef_record));
+               if (record == NULL)
+                       goto fail;
+
+               record->header = parse_record_header(ndef_data, offset,
+                                                       ndef_length);
+               if (record->header == NULL)
+                       goto fail;
+
+               if (validate_record_begin_and_end_bits(&p_mb, &p_me,
+                                       record->header->mb,
+                                       record->header->me) != 0) {
+                       DBG("validate mb me failed");
+                       goto fail;
+               }
+
+               record_start = ndef_data + offset;
+               offset = record->header->offset;
+
+               switch (record->header->rec_type) {
+               case RECORD_TYPE_WKT_SIZE:
+               case RECORD_TYPE_WKT_TYPE:
+               case RECORD_TYPE_WKT_ACTION:
+               case RECORD_TYPE_WKT_HANDOVER_CARRIER:
+               case RECORD_TYPE_WKT_ALTERNATIVE_CARRIER:
+               case RECORD_TYPE_WKT_COLLISION_RESOLUTION:
+               case RECORD_TYPE_WKT_ERROR:
+               case RECORD_TYPE_UNKNOWN:
+               case RECORD_TYPE_ERROR:
+                       break;
+
+               case RECORD_TYPE_WKT_HANDOVER_REQUEST:
+               case RECORD_TYPE_WKT_HANDOVER_SELECT:
+                       /*
+                        * Handover frame are a little bit special as the NDEF
+                        * length (specified in the header) is not the real
+                        * frame size. The complete frame includes extra NDEF
+                        * following the initial handover NDEF
+                        */
+                       record->ho = parse_ho_payload(record->header->rec_type,
+                                       ndef_data + offset,
+                                       record->header->payload_len,
+                                       ndef_length - offset,
+                                       record->header->mb, record->header->me);
+                       if (record->ho == NULL)
+                               goto fail;
+
+                       /* the complete frame is processed, break the loop */
+                       record->header->payload_len = ndef_length;
+                       break;
+
+               case RECORD_TYPE_WKT_TEXT:
+                       record->text = parse_text_payload(ndef_data + offset,
+                                               record->header->payload_len);
+
+                       if (record->text == NULL)
+                               goto fail;
+
+                       break;
+
+               case RECORD_TYPE_WKT_URI:
+                       record->uri = parse_uri_payload(ndef_data + offset,
+                                               record->header->payload_len);
+
+                       if (record->uri == NULL)
+                               goto fail;
+
+                       break;
+
+               case RECORD_TYPE_WKT_SMART_POSTER:
+                       record->sp = parse_sp_payload(
+                                               ndef_data + offset,
+                                               record->header->payload_len);
+
+                       if (record->sp == NULL)
+                               goto fail;
+
+                       break;
+
+               case RECORD_TYPE_MIME_TYPE:
+                       record->mime = parse_mime_type(record, ndef_data,
+                                               ndef_length, offset,
+                                               record->header->payload_len,
+                                               TRUE);
+
+
+                       if (record->mime == NULL)
+                               goto fail;
+
+                       break;
+               }
+
+               record->data_len = record->header->header_len +
+                                       record->header->payload_len;
+
+               record->data = g_try_malloc0(record->data_len);
+               if (record->data == NULL)
+                       goto fail;
+
+               memcpy(record->data, record_start, record->data_len);
+
+               records = g_list_append(records, record);
+
+               build_record_type_string(record);
+
+               offset += record->header->payload_len;
+       }
+
+       return records;
+
+fail:
+       near_error("ndef parsing failed");
+       free_ndef_record(record);
+
+       return records;
+}
+
+void near_ndef_records_free(GList *records)
+{
+       GList *list;
+
+       for (list = records; list; list = list->next) {
+               struct near_ndef_record *record = list->data;
+
+               __near_ndef_record_free(record);
+       }
+
+       g_list_free(records);
+}
+
+/*
+ * @brief Compute an NDEF record length
+ *
+ * Would compute ndef records length, even though the submitted frame
+ * is incomplete. This code is used in the handover read function, as
+ * we have to "guess" the final frame size.
+ *
+ * Message size for SR=1 is:
+ *  1 : ndef rec header (offset 0)
+ *  x : record type length (offset 1)
+ *  y : payload length (offset 2) 1 byte ONLY if SR=1
+ *     if SR=0: (4bytes) 32 bits
+ *  z : payload id length (offset 3) ONLY if il_length=1
+ * */
+int near_ndef_record_length(uint8_t *ndef_in, size_t ndef_in_length)
+{
+       int ndef_size;   /* default size for NDEF hdr + rec typ len + payl */
+       size_t offset;
+       uint8_t hdr;
+
+       DBG("");
+
+       if (ndef_in_length < 3)
+               return -EINVAL;
+
+       ndef_size = 3;
+       offset = 0;
+
+       /* save header byte */
+       hdr = ndef_in[offset];
+       offset++;
+
+       /* header->type_len */
+       ndef_size += ndef_in[offset++];
+
+       /* header->payload_len */
+       if (RECORD_SR_BIT(hdr) == 1) {
+               ndef_size += ndef_in[offset++];
+       } else {
+               ndef_size += g_ntohl(*((uint32_t *)(ndef_in + offset)));
+               offset += 4;
+
+               if (offset >= ndef_in_length)
+                       return -ERANGE;
+       }
+
+       /* header->il */
+       ndef_size += RECORD_IL_BIT(hdr);
+
+       /* header->il_length */
+       if (RECORD_IL_BIT(hdr) == 1)
+               ndef_size += ndef_in[offset++];
+
+       DBG("near_ndef_message_length is %d", ndef_size);
+
+       return ndef_size;
+}
+
+int near_ndef_count_records(uint8_t *ndef_in, size_t ndef_in_length,
+                       uint8_t record_type)
+{
+       uint8_t p_mb = 0, p_me = 0;
+       int err;
+       size_t offset;
+       struct near_ndef_record *record = NULL;
+       int counted_records = 0 ;
+
+       DBG("");
+
+       offset = 0;
+
+       if (ndef_in == NULL ||  ndef_in_length < NDEF_MSG_MIN_LENGTH) {
+               err = -EINVAL;
+               goto fail;
+       }
+
+       while (offset < ndef_in_length) {
+               record = g_try_malloc0(sizeof(struct near_ndef_record));
+               if (record == NULL) {
+                       err = -ENOMEM;
+                       goto fail;
+               }
+
+               /* Create a record */
+               record->header = parse_record_header(ndef_in, offset,
+                                                       ndef_in_length);
+               if (record->header == NULL) {
+                       err = -EINVAL;
+                       goto fail;
+               }
+
+               /* Validate MB ME */
+               if (validate_record_begin_and_end_bits(&p_mb, &p_me,
+                                       record->header->mb,
+                                       record->header->me) != 0) {
+                       DBG("validate mb me failed");
+                       err = -EINVAL;
+                       goto fail;
+               }
+
+               /* Is this what we want ? */
+               if (record->header->rec_type == record_type)
+                       counted_records++;
+
+               /* Jump to the next record */
+               offset = record->header->offset + record->header->payload_len;
+
+               free_ndef_record(record);
+       }
+
+       DBG("Type %d Records found: %d", record_type, counted_records);
+
+       return counted_records;
+
+fail:
+       near_error("ndef counting failed");
+       free_ndef_record(record);
+       return err;
+}
+
+/**
+ * @brief Prepare Text ndef record
+ *
+ * Prepare text ndef record with provided input data and return
+ * ndef message structure (length and byte stream) in success or
+ * NULL in failure case.
+ *
+ * @note : caller responsibility to free the input and output
+ *         parameters memory.
+ *
+ * @param[in] encoding      Encoding (UTF-8 | UTF-16)
+ * @param[in] language_code Language Code
+ * @param[in] text          Actual text
+ *
+ * @return struct near_ndef_message * - Success
+ *         NULL - Failure
+ */
+struct near_ndef_message *near_ndef_prepare_text_record(char *encoding,
+                                               char *language_code, char *text)
+{
+       struct near_ndef_message *msg;
+       uint32_t text_len, payload_length;
+       uint8_t  code_len, status = 0;
+
+       DBG("");
+
+       /* Validate input parameters*/
+       if (((g_strcmp0(encoding, "UTF-8") != 0) &&
+                (g_strcmp0(encoding, "UTF-16") != 0)) ||
+                (language_code == NULL) ||
+                (text == NULL)) {
+               return NULL;
+       }
+
+       code_len = strlen(language_code);
+       text_len = strlen(text);
+       payload_length = 1 + code_len + text_len;
+
+       msg = ndef_message_alloc("T", payload_length);
+       if (msg == NULL)
+               return NULL;
+
+       if (g_strcmp0(encoding, "UTF-16") == 0)
+               status |= NDEF_TEXT_RECORD_UTF16_STATUS;
+
+       status = status | code_len;
+       msg->data[msg->offset++] = status;
+
+       if (code_len > 0)
+               memcpy(msg->data + msg->offset, language_code, code_len);
+
+       msg->offset += code_len;
+
+       if (text_len > 0)
+               memcpy(msg->data + msg->offset, text, text_len);
+
+       msg->offset += text_len;
+
+       if (msg->offset > msg->length)
+               goto fail;
+
+       return msg;
+
+fail:
+       near_error("text record preparation failed");
+       g_free(msg->data);
+       g_free(msg);
+
+       return NULL;
+}
+
+/**
+ * @brief Prepare URI ndef record
+ *
+ * Prepare uri ndef record with provided input data and return
+ * ndef message structure (length and byte stream) in success or
+ * NULL in failure case.
+ *
+ * @note : caller responsibility to free the input and output
+ *         parameters memory.
+ *
+ * @param[in] identifier    URI Identifier
+ * @param[in] field_length  URI field length
+ * @param[in] field         URI field
+ *
+ * @return struct near_ndef_message * - Success
+ *         NULL - Failure
+ */
+struct near_ndef_message *near_ndef_prepare_uri_record(uint8_t identifier,
+                                       uint32_t field_length, uint8_t *field)
+{
+       struct near_ndef_message *msg = NULL;
+       uint32_t payload_length;
+
+       DBG("");
+
+       /* Validate input parameters*/
+       if ((field_length == 0 && field != NULL) ||
+               (field_length != 0 && field == NULL)) {
+               return NULL;
+       }
+
+       payload_length = field_length + 1;
+
+       msg = ndef_message_alloc("U", payload_length);
+       if (msg == NULL)
+               return NULL;
+
+       msg->data[msg->offset++] = identifier;
+
+       if (field_length > 0) {
+               memcpy(msg->data + msg->offset, field, field_length);
+               msg->offset += field_length;
+       }
+
+       if (msg->offset > msg->length)
+               goto fail;
+
+       return msg;
+
+fail:
+       near_error("uri record preparation failed");
+       g_free(msg->data);
+       g_free(msg);
+
+       return NULL;
+}
+
+/**
+ * @brief Prepare Smartposter ndef record with mandatory URI fields.
+ *
+ * Prepare smartposter ndef record with provided input data and
+ * return ndef message structure (length and byte stream) in success or
+ * NULL in failure case.
+ *
+ * @note : caller responsibility to free the input and output
+ *         parameters memory.
+ *
+ * @param[in] uri_identfier
+ * @param[in] uri_field_length
+ * @param[in] uri_field
+ *
+ * @return struct near_ndef_message * - Success
+ *         NULL - Failure
+ */
+struct near_ndef_message *
+near_ndef_prepare_smartposter_record(uint8_t uri_identifier,
+                                       uint32_t uri_field_length,
+                                       uint8_t *uri_field)
+{
+       struct near_ndef_message *msg = NULL, *uri = NULL;
+
+       /* URI is mandatory in Smartposter */
+       uri = near_ndef_prepare_uri_record(uri_identifier, uri_field_length,
+                                                               uri_field);
+       if (uri == NULL)
+               goto fail;
+
+       /* URI record length is equal to payload length of Sp record */
+       msg = ndef_message_alloc("Sp", uri->length);
+       if (msg == NULL)
+               goto fail;
+
+       memcpy(msg->data + msg->offset, uri->data, uri->length);
+       msg->offset += uri->length;
+
+       if (msg->offset > msg->length)
+               goto fail;
+
+       if (uri != NULL) {
+               g_free(uri->data);
+               g_free(uri);
+       }
+
+       return msg;
+
+fail:
+       near_error("smartposter record preparation failed");
+
+       if (uri != NULL) {
+               g_free(uri->data);
+               g_free(uri);
+       }
+
+       if (msg != NULL) {
+               g_free(msg->data);
+               g_free(msg);
+       }
+
+       return NULL;
+}
+
+static char *get_text_field(DBusMessage *msg, char *text)
+{
+       DBusMessageIter iter, arr_iter;
+       char *uri = NULL;
+
+       DBG("");
+
+       if (text == NULL)
+               return NULL;
+
+       dbus_message_iter_init(msg, &iter);
+       dbus_message_iter_recurse(&iter, &arr_iter);
+
+       while (dbus_message_iter_get_arg_type(&arr_iter) !=
+                                       DBUS_TYPE_INVALID) {
+               const char *key;
+               DBusMessageIter ent_iter;
+               DBusMessageIter var_iter;
+
+               dbus_message_iter_recurse(&arr_iter, &ent_iter);
+               dbus_message_iter_get_basic(&ent_iter, &key);
+               dbus_message_iter_next(&ent_iter);
+               dbus_message_iter_recurse(&ent_iter, &var_iter);
+
+               switch (dbus_message_iter_get_arg_type(&var_iter)) {
+               case DBUS_TYPE_STRING:
+                       if (g_strcmp0(key, text) == 0)
+                               dbus_message_iter_get_basic(&var_iter, &uri);
+
+                       break;
+               }
+
+               dbus_message_iter_next(&arr_iter);
+       }
+
+       return uri;
+}
+
+static inline char *get_uri_field(DBusMessage *msg)
+{
+       return get_text_field(msg, "URI");
+}
+
+static inline char *get_carrier_field(DBusMessage *msg)
+{
+       return get_text_field(msg, "Carrier");
+}
+
+static struct near_ndef_message *build_text_record(DBusMessage *msg)
+{
+       DBusMessageIter iter, arr_iter;
+       char *cod = NULL, *lang = NULL, *rep = NULL;
+
+       DBG("");
+
+       dbus_message_iter_init(msg, &iter);
+       dbus_message_iter_recurse(&iter, &arr_iter);
+
+       while (dbus_message_iter_get_arg_type(&arr_iter) !=
+                                       DBUS_TYPE_INVALID) {
+               const char *key;
+               DBusMessageIter ent_iter;
+               DBusMessageIter var_iter;
+
+               dbus_message_iter_recurse(&arr_iter, &ent_iter);
+               dbus_message_iter_get_basic(&ent_iter, &key);
+               dbus_message_iter_next(&ent_iter);
+               dbus_message_iter_recurse(&ent_iter, &var_iter);
+
+               switch (dbus_message_iter_get_arg_type(&var_iter)) {
+               case DBUS_TYPE_STRING:
+                       if (g_strcmp0(key, "Encoding") == 0)
+                               dbus_message_iter_get_basic(&var_iter, &cod);
+                       else if (g_strcmp0(key, "Language") == 0)
+                               dbus_message_iter_get_basic(&var_iter, &lang);
+                       else if (g_strcmp0(key, "Representation") == 0)
+                               dbus_message_iter_get_basic(&var_iter, &rep);
+
+                       break;
+               }
+
+               dbus_message_iter_next(&arr_iter);
+       }
+
+       return near_ndef_prepare_text_record(cod, lang, rep);
+}
+
+static struct near_ndef_message *build_uri_record(DBusMessage *msg)
+{
+       char *uri = NULL;
+       const char *uri_prefix = NULL;
+       uint8_t id_len, i, id;
+       uint32_t uri_len;
+
+       DBG("");
+
+       uri = get_uri_field(msg);
+       if (uri == NULL)
+               return NULL;
+
+       id = 0;
+       id_len = 0;
+
+       for (i = 1; i <= NFC_MAX_URI_ID; i++) {
+               uri_prefix = __near_ndef_get_uri_prefix(i);
+
+               if (uri_prefix != NULL &&
+                   g_str_has_prefix(uri, uri_prefix) == TRUE) {
+                       id = i;
+                       id_len = strlen(uri_prefix);
+                       break;
+               }
+       }
+
+       DBG("%d %d\n", i, id_len);
+
+       uri_len = strlen(uri) - id_len;
+       return near_ndef_prepare_uri_record(id, uri_len,
+                                               (uint8_t *)(uri + id_len));
+}
+
+static struct near_ndef_message *build_sp_record(DBusMessage *msg)
+{
+       char *uri = NULL;
+       const char *uri_prefix;
+       uint8_t id_len, i;
+       uint32_t uri_len;
+
+       DBG("");
+
+       /*
+        * Currently this function supports only mandatory URI record,
+        * TODO: Other records support.
+        */
+       uri = get_uri_field(msg);
+       if (uri == NULL)
+               return NULL;
+
+       for (i = 1; i <= NFC_MAX_URI_ID; i++) {
+               uri_prefix = __near_ndef_get_uri_prefix(i);
+
+               if (uri_prefix != NULL &&
+                               g_str_has_prefix(uri, uri_prefix) == TRUE)
+                       break;
+       }
+
+       if (uri_prefix == NULL) {
+               i = 0;
+               id_len = 0;
+       } else
+               id_len = strlen(uri_prefix);
+
+       uri_len = strlen(uri) - id_len;
+       return near_ndef_prepare_smartposter_record(i, uri_len,
+                                               (uint8_t *)(uri + id_len));
+}
+
+static struct near_ndef_ac_payload *build_ho_local_ac_record(void)
+{
+       struct near_ndef_ac_payload *ac_payload = NULL;
+
+       DBG("");
+
+       /* Allocate ac record */
+       ac_payload = g_try_malloc0(sizeof(struct near_ndef_ac_payload));
+       if (ac_payload == NULL)
+               return NULL;
+
+       /* Carrier flag */
+       ac_payload->cps = CPS_ACTIVE;    /* TODO Should reflect BT state */
+
+       /* Carrier data reference length */
+       ac_payload->cdr_len = 1;
+
+       /* Carrier data reference */
+       ac_payload->cdr = '0';
+
+       /* Auxiliary data reference count */
+       ac_payload->adata_refcount = 0;
+
+       return ac_payload;
+}
+
+static struct near_ndef_message *build_ho_record(DBusMessage *msg)
+{
+       char *carrier_type = NULL;
+       uint8_t carrier;
+       struct near_ndef_record *record = NULL;
+
+       DBG("");
+
+       carrier_type = get_carrier_field(msg);
+       if (carrier_type == NULL) {
+               DBG("Empty carrier name");
+               return NULL;
+       }
+
+       carrier = NEAR_CARRIER_EMPTY;
+       if (g_strcmp0(carrier_type, "bluetooth") == 0)
+               carrier |= NEAR_CARRIER_BLUETOOTH;
+       if (g_strcmp0(carrier_type, "wifi") == 0)
+               carrier |= NEAR_CARRIER_WIFI;
+
+       if (carrier == NEAR_CARRIER_EMPTY) {
+               DBG("Invalid carrier name");
+               return NULL;
+       }
+
+       /* Build local record */
+       record = g_try_malloc0(sizeof(struct near_ndef_record));
+       if (record == NULL)
+               return NULL;
+
+       /* Do a Ho */
+       record->ho = g_try_malloc0(sizeof(struct near_ndef_ho_payload));
+       if (record->ho == NULL)
+               goto fail;
+
+       /* fill the Ho */
+       record->ho->version = HANDOVER_VERSION;
+
+       /* Generate random number for Collision Resolution Record */
+       record->ho->collision_record = GUINT16_TO_BE(
+                               g_random_int_range(0, G_MAXUINT16 + 1));
+       record->ho->err_record = NULL;
+
+       record->ho->number_of_ac_payloads = 1;
+       record->ho->ac_payloads = g_try_malloc0(
+                                       sizeof(struct near_ndef_ac_payload *));
+       if (record->ho->ac_payloads == NULL)
+               goto fail;
+       record->ho->ac_payloads[0] = build_ho_local_ac_record();
+
+       return near_ndef_prepare_handover_record("Hr", record, carrier);
+
+fail:
+       free_ho_payload(record->ho);
+       g_free(record);
+
+       return NULL;
+}
+
+static struct near_ndef_message * build_raw_ndef(DBusMessage *msg)
+{
+       DBusMessageIter iter, arr_iter;
+       struct near_ndef_message *ndef;
+
+       DBG("");
+
+       ndef = NULL;
+
+       dbus_message_iter_init(msg, &iter);
+       dbus_message_iter_recurse(&iter, &arr_iter);
+
+       while (dbus_message_iter_get_arg_type(&arr_iter) !=
+                                       DBUS_TYPE_INVALID) {
+               const char *key;
+               DBusMessageIter ent_iter;
+               DBusMessageIter var_iter;
+
+               dbus_message_iter_recurse(&arr_iter, &ent_iter);
+               dbus_message_iter_get_basic(&ent_iter, &key);
+               dbus_message_iter_next(&ent_iter);
+               dbus_message_iter_recurse(&ent_iter, &var_iter);
+
+               switch (dbus_message_iter_get_arg_type(&var_iter)) {
+               case DBUS_TYPE_ARRAY:
+                       if (g_strcmp0(key, "NDEF") == 0) {
+                               DBusMessageIter array;
+                               uint8_t *data;
+                               int data_size;
+
+                               dbus_message_iter_recurse(&var_iter, &array);
+                               dbus_message_iter_get_fixed_array(&array,
+                                                               &data,
+                                                               &data_size);
+
+                               ndef = g_try_malloc0(data_size);
+                               if (ndef == NULL)
+                                       break;
+
+                               ndef->data = g_try_malloc0(data_size);
+                               if (ndef->data == NULL) {
+                                       g_free(ndef);
+                                       break;
+                               }
+
+                               ndef->length = data_size;
+                               memcpy(ndef->data, data, data_size);
+                       }
+
+                       break;
+               }
+
+               dbus_message_iter_next(&arr_iter);
+       }
+
+       return ndef;
+}
+
+struct near_ndef_message *__ndef_build_from_message(DBusMessage *msg)
+{
+       DBusMessageIter iter;
+       DBusMessageIter arr_iter;
+       struct near_ndef_message *ndef;
+
+       DBG("");
+
+       dbus_message_iter_init(msg, &iter);
+       dbus_message_iter_recurse(&iter, &arr_iter);
+
+       ndef = NULL;
+
+       while (dbus_message_iter_get_arg_type(&arr_iter) !=
+                                               DBUS_TYPE_INVALID) {
+               const char *key, *value;
+               DBusMessageIter ent_iter;
+               DBusMessageIter var_iter;
+
+               dbus_message_iter_recurse(&arr_iter, &ent_iter);
+               dbus_message_iter_get_basic(&ent_iter, &key);
+
+               if (g_strcmp0(key, "Type") != 0) {
+                       dbus_message_iter_next(&arr_iter);
+                       continue;
+               }
+
+               dbus_message_iter_next(&ent_iter);
+               dbus_message_iter_recurse(&ent_iter, &var_iter);
+
+               switch (dbus_message_iter_get_arg_type(&var_iter)) {
+               case DBUS_TYPE_STRING:
+                       dbus_message_iter_get_basic(&var_iter, &value);
+
+                       if (g_strcmp0(value, "Text") == 0) {
+                               ndef = build_text_record(msg);
+                               break;
+                       } else if (g_strcmp0(value, "URI") == 0) {
+                               ndef = build_uri_record(msg);
+                               break;
+                       } else if (g_strcmp0(value, "SmartPoster") == 0) {
+                               ndef = build_sp_record(msg);
+                               break;
+                       } else if (g_strcmp0(value, "Handover") == 0) {
+                               ndef = build_ho_record(msg);
+                               break;
+                       } else if (g_strcmp0(value, "Raw") == 0) {
+                               ndef = build_raw_ndef(msg);
+                               break;
+                       } else {
+                               near_error("%s not supported", value);
+                               ndef = NULL;
+                               break;
+                       }
+
+                       break;
+               }
+
+               dbus_message_iter_next(&arr_iter);
+       }
+
+       return ndef;
+}
+
+int __near_ndef_init(void)
+{
+       DBG("");
+
+       connection = near_dbus_get_connection();
+
+       return 0;
+}
+
+void __near_ndef_cleanup(void)
+{
+}
diff --git a/src/near.h b/src/near.h
new file mode 100644 (file)
index 0000000..e47d1fb
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdint.h>
+#include <sys/socket.h>
+
+#include <linux/socket.h>
+
+
+#include <glib.h>
+
+#include <near/nfc.h>
+#include <near/types.h>
+
+struct near_adapter;
+struct near_device_driver;
+
+#include <near/log.h>
+
+int __near_log_init(const char *debug, gboolean detach);
+void __near_log_cleanup(void);
+
+#include <near/dbus.h>
+
+int __near_dbus_init(DBusConnection *conn);
+void __near_dbus_cleanup(void);
+
+DBusMessage *__near_error_failed(DBusMessage *msg, int errnum);
+DBusMessage *__near_error_invalid_arguments(DBusMessage *msg);
+DBusMessage *__near_error_permission_denied(DBusMessage *msg);
+DBusMessage *__near_error_passphrase_required(DBusMessage *msg);
+DBusMessage *__near_error_not_registered(DBusMessage *msg);
+DBusMessage *__near_error_not_unique(DBusMessage *msg);
+DBusMessage *__near_error_not_supported(DBusMessage *msg);
+DBusMessage *__near_error_not_implemented(DBusMessage *msg);
+DBusMessage *__near_error_not_found(DBusMessage *msg);
+DBusMessage *__near_error_not_polling(DBusMessage *msg);
+DBusMessage *__near_error_no_carrier(DBusMessage *msg);
+DBusMessage *__near_error_in_progress(DBusMessage *msg);
+DBusMessage *__near_error_already_exists(DBusMessage *msg);
+DBusMessage *__near_error_already_enabled(DBusMessage *msg);
+DBusMessage *__near_error_already_disabled(DBusMessage *msg);
+DBusMessage *__near_error_already_connected(DBusMessage *msg);
+DBusMessage *__near_error_not_connected(DBusMessage *msg);
+DBusMessage *__near_error_operation_aborted(DBusMessage *msg);
+DBusMessage *__near_error_operation_timeout(DBusMessage *msg);
+DBusMessage *__near_error_invalid_service(DBusMessage *msg);
+DBusMessage *__near_error_invalid_property(DBusMessage *msg);
+
+int __near_manager_adapter_add(uint32_t idx, const char *name,
+                       uint32_t protocols, near_bool_t powered);
+void __near_manager_adapter_remove(uint32_t idx);
+int __near_manager_init(DBusConnection *conn);
+void __near_manager_cleanup(void);
+
+#include <near/adapter.h>
+
+struct near_adapter * __near_adapter_create(uint32_t idx,
+               const char *name, uint32_t protocols, near_bool_t powered);
+void __near_adapter_destroy(struct near_adapter *adapter);
+const char *__near_adapter_get_path(struct near_adapter *adapter);
+struct near_adapter *__near_adapter_get(uint32_t idx);
+int __near_adapter_add(struct near_adapter *adapter);
+void __near_adapter_remove(struct near_adapter *adapter);
+int __near_adapter_add_target(uint32_t idx, uint32_t target_idx,
+                       uint32_t protocols, uint16_t sens_res, uint8_t sel_res,
+                       uint8_t *nfcid, uint8_t nfcid_len);
+int __near_adapter_remove_target(uint32_t idx, uint32_t target_idx);
+int __near_adapter_add_device(uint32_t idx, uint8_t *nfcid, uint8_t nfcid_len);
+int __near_adapter_remove_device(uint32_t idx);
+int __near_adapter_set_dep_state(uint32_t idx, near_bool_t dep);
+near_bool_t __near_adapter_get_dep_state(uint32_t idx);
+void __near_adapter_tags_changed(uint32_t adapter_idx);
+void __near_adapter_devices_changed(uint32_t adapter_idx);
+void __near_adapter_listen(struct near_device_driver *driver);
+void __near_adapter_list(DBusMessageIter *iter, void *user_data);
+void __near_adapter_start_check_presence(uint32_t adapter_idx, uint32_t target_idx);
+void __near_adapter_stop_check_presence(uint32_t adapter_idx, uint32_t target_idx);
+int __near_adapter_init(void);
+void __near_adapter_cleanup(void);
+
+#include <near/ndef.h>
+
+#define NFC_MAX_URI_ID 0x23
+
+int __near_ndef_init(void);
+void __near_ndef_cleanup(void);
+int __near_ndef_record_register(struct near_ndef_record *record, char *path);
+void __near_ndef_record_free(struct near_ndef_record *record);
+char *__near_ndef_record_get_path(struct near_ndef_record *record);
+char *__near_ndef_record_get_type(struct near_ndef_record *record);
+uint8_t *__near_ndef_record_get_data(struct near_ndef_record *record, size_t *len);
+void __near_ndef_append_records(DBusMessageIter *iter, GList *record);
+const char *__near_ndef_get_uri_prefix(uint8_t id);
+struct near_ndef_message *__ndef_build_from_message(DBusMessage *msg);
+
+#include <near/tag.h>
+
+int __near_tag_init(void);
+void __near_tag_cleanup(void);
+struct near_tag *__near_tag_add(uint32_t idx, uint32_t target_idx,
+                               uint32_t protocols,
+                               uint16_t sens_res, uint8_t sel_res,
+                               uint8_t *nfcid, uint8_t nfcid_len);
+void __near_tag_remove(struct near_tag *tag);
+const char *__near_tag_get_path(struct near_tag *tag);
+uint32_t __near_tag_get_type(struct near_tag *tag);
+void __near_tag_append_records(struct near_tag *tag, DBusMessageIter *iter);
+int __near_tag_read(struct near_tag *tag, near_tag_io_cb cb);
+int __near_tag_write(struct near_tag *tag,
+                               struct near_ndef_message *ndef,
+                               near_tag_io_cb cb);
+int __near_tag_check_presence(struct near_tag *tag, near_tag_io_cb cb);
+
+#include <near/device.h>
+
+int __near_device_init(void);
+void __near_device_cleanup(void);
+const char *__near_device_get_path(struct near_device *device);
+uint32_t __neard_device_get_idx(struct near_device *device);
+struct near_device *__near_device_add(uint32_t idx, uint32_t target_idx,
+                                       uint8_t *nfcid, uint8_t nfcid_len);
+void __near_device_remove(struct near_device *device);
+int __near_device_listen(struct near_device *device, near_device_io_cb cb);
+int __near_device_push(struct near_device *device,
+                       struct near_ndef_message *ndef, char *service_name,
+                       near_device_io_cb cb);
+
+#include <near/tlv.h>
+
+int __near_netlink_get_adapters(void);
+int __near_netlink_start_poll(int idx,
+                       uint32_t im_protocols, uint32_t tm_protocols);
+int __near_netlink_stop_poll(int idx);
+int __near_netlink_dep_link_up(uint32_t idx, uint32_t target_idx,
+                               uint8_t comm_mode, uint8_t rf_mode);
+int __near_netlink_dep_link_down(uint32_t idx);
+int __near_netlink_adapter_enable(int idx, near_bool_t enable);
+int __near_netlink_init(void);
+void __near_netlink_cleanup(void);
+
+#include <near/setting.h>
+
+#include <near/plugin.h>
+
+int __near_plugin_init(const char *pattern, const char *exclude);
+void __near_plugin_cleanup(void);
+
+/* NFC Bluetooth Secure Simple Pairing */
+#define BT_MIME_V2_0           0
+#define BT_MIME_V2_1           1
+#define BT_MIME_STRING_2_0     "nokia.com:bt"
+#define BT_MIME_STRING_2_1     "application/vnd.bluetooth.ep.oob"
+#define WIFI_MIME_STRING       "application/vnd.wfa.wsc"
+
+/* Mime specific properties */
+#define OOB_PROPS_EMPTY                0x00
+#define OOB_PROPS_SP_HASH      0x01
+#define OOB_PROPS_SP_RANDOM    0x02
+#define OOB_PROPS_SHORT_NAME   0x04
+#define OOB_PROPS_COD          0x08
+#define OOB_PROPS_SP           (OOB_PROPS_SP_HASH | OOB_PROPS_SP_RANDOM)
+
+int __near_bluetooth_init(void);
+void __near_bluetooth_cleanup(void);
+int __near_bluetooth_parse_oob_record(uint8_t version, uint8_t *bt_data,
+                                       uint16_t *properties, near_bool_t pair);
+int __near_bluetooth_pair(void *data);
+uint8_t *__near_bluetooth_local_get_properties(int *bt_data_len,
+                                                       uint16_t mime_props);
+
+void __near_agent_ndef_parse_records(GList *records);
+int __near_agent_ndef_register(const char *sender, const char *path,
+                                               const char *record_type);
+int __near_agent_ndef_unregister(const char *sender, const char *path,
+                                               const char *record_type);
+int __near_agent_handover_register(const char *sender, const char *path);
+int __near_agent_handover_unregister(const char *sender, const char *path);
+
+int __near_agent_init(void);
+void __near_agent_cleanup(void);
diff --git a/src/netlink.c b/src/netlink.c
new file mode 100644 (file)
index 0000000..fa83723
--- /dev/null
@@ -0,0 +1,910 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright 2007, 2008       Johannes Berg <johannes@sipsolutions.net>
+ *  Copyright (C) 2011 Instituto Nokia de Tecnologia
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+
+#include "near.h"
+
+#ifdef NEED_LIBNL_COMPAT
+#define nl_sock nl_handle
+
+static inline struct nl_handle *nl_socket_alloc(void)
+{
+       return nl_handle_alloc();
+}
+
+static inline void nl_socket_free(struct nl_sock *h)
+{
+       nl_handle_destroy(h);
+}
+
+#define NLE_MISSING_ATTR       14
+
+static inline void __nl_perror(int error, const char *s)
+{
+       nl_perror(s);
+}
+#define nl_perror __nl_perror
+#endif
+
+struct nlnfc_state {
+       struct nl_sock *cmd_sock;
+       struct nl_sock *event_sock;
+       int nfc_id;
+       int mcid;
+};
+
+static struct nlnfc_state *nfc_state;
+static GIOChannel *netlink_channel = NULL;
+
+static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
+                        void *arg)
+{
+       int *ret = arg;
+
+       DBG("");
+
+       *ret = err->error;
+
+       return NL_STOP;
+}
+
+static int finish_handler(struct nl_msg *msg, void *arg)
+{
+       int *ret = arg;
+
+       DBG("");
+
+       *ret = 1;
+
+       return NL_SKIP;
+}
+
+static int ack_handler(struct nl_msg *msg, void *arg)
+{
+       int *ret = arg;
+
+       DBG("");
+
+       *ret = 1;
+
+       return NL_STOP;
+}
+
+static int nl_send_msg(struct nl_sock *sock, struct nl_msg *msg,
+                       int (*rx_handler)(struct nl_msg *, void *),
+                       void *data)
+{
+       struct nl_cb *cb;
+       int err, done;
+
+       DBG("");
+
+       cb = nl_cb_alloc(NL_CB_DEFAULT);
+       if (cb == NULL)
+               return -ENOMEM;
+
+       err = nl_send_auto_complete(sock, msg);
+       if (err < 0) {
+               nl_cb_put(cb);
+               near_error("%s", strerror(err));
+
+               return err;
+       }
+
+       err = done = 0;
+
+       nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
+       nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &done);
+       nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &done);
+
+       if (rx_handler != NULL)
+               nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, rx_handler, data);
+
+       while (err == 0 && done == 0)
+               nl_recvmsgs(sock, cb);
+
+       nl_cb_put(cb);
+
+       return err;
+}
+
+
+static int get_devices_handler(struct nl_msg *n, void *arg)
+{
+       struct nlmsghdr *nlh = nlmsg_hdr(n);
+       struct nlattr *attrs[NFC_ATTR_MAX + 1];
+       char *name;
+       uint32_t idx, protocols;
+       near_bool_t powered;
+
+       DBG("");
+
+       genlmsg_parse(nlh, 0, attrs, NFC_ATTR_MAX, NULL);
+
+       if (attrs[NFC_ATTR_DEVICE_INDEX] == NULL ||
+           attrs[NFC_ATTR_DEVICE_NAME] == NULL ||
+           attrs[NFC_ATTR_PROTOCOLS] == NULL) {
+               nl_perror(NLE_MISSING_ATTR, "NFC_CMD_GET_DEVICE");
+               return NL_STOP;
+       }
+
+
+       idx = nla_get_u32(attrs[NFC_ATTR_DEVICE_INDEX]);
+       name = nla_get_string(attrs[NFC_ATTR_DEVICE_NAME]);
+       protocols = nla_get_u32(attrs[NFC_ATTR_PROTOCOLS]);
+
+       if (attrs[NFC_ATTR_DEVICE_POWERED] == NULL)
+               powered = FALSE;
+       else
+               powered = nla_get_u8(attrs[NFC_ATTR_DEVICE_POWERED]);
+
+       __near_manager_adapter_add(idx, name, protocols, powered);
+
+       return NL_SKIP;
+}
+
+int __near_netlink_get_adapters(void)
+{
+       struct nl_msg *msg;
+       void *hdr;
+       int err;
+
+       DBG("");
+
+       if (nfc_state == NULL || nfc_state->nfc_id < 0)
+               return -ENODEV;
+
+       msg = nlmsg_alloc();
+       if (msg == NULL)
+               return -ENOMEM;
+
+       hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nfc_state->nfc_id, 0,
+                         NLM_F_DUMP, NFC_CMD_GET_DEVICE, NFC_GENL_VERSION);
+       if (hdr == NULL) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       err = nl_send_msg(nfc_state->cmd_sock, msg, get_devices_handler, NULL);
+
+out:
+       nlmsg_free(msg);
+
+       return err;
+}
+
+int __near_netlink_start_poll(int idx,
+                               uint32_t im_protocols, uint32_t tm_protocols)
+{
+       struct nl_msg *msg;
+       void *hdr;
+       int err;
+
+       DBG("IM protos 0x%x TM protos 0x%x", im_protocols, tm_protocols);
+
+       msg = nlmsg_alloc();
+       if (msg == NULL)
+               return -ENOMEM;
+
+       hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nfc_state->nfc_id, 0,
+                       NLM_F_REQUEST, NFC_CMD_START_POLL, NFC_GENL_VERSION);
+       if (hdr == NULL) {
+               err = -EINVAL;
+               goto nla_put_failure;
+       }
+
+       err = -EMSGSIZE;
+
+       NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, idx);
+       if (im_protocols != 0) {
+               NLA_PUT_U32(msg, NFC_ATTR_IM_PROTOCOLS, im_protocols);
+               NLA_PUT_U32(msg, NFC_ATTR_PROTOCOLS, im_protocols);
+       }
+       if (tm_protocols != 0)
+               NLA_PUT_U32(msg, NFC_ATTR_TM_PROTOCOLS, tm_protocols);
+
+       err = nl_send_msg(nfc_state->cmd_sock, msg, NULL, NULL);
+
+nla_put_failure:
+       nlmsg_free(msg);
+
+       return err;
+}
+
+
+int __near_netlink_stop_poll(int idx)
+{
+       struct nl_msg *msg;
+       void *hdr;
+       int err;
+
+       DBG("");
+
+       msg = nlmsg_alloc();
+       if (msg == NULL)
+               return -ENOMEM;
+
+       hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nfc_state->nfc_id, 0,
+                       NLM_F_REQUEST, NFC_CMD_STOP_POLL, NFC_GENL_VERSION);
+       if (hdr == NULL) {
+               err = -EINVAL;
+               goto nla_put_failure;
+       }
+
+       err = -EMSGSIZE;
+
+       NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, idx);
+
+       err = nl_send_msg(nfc_state->cmd_sock, msg, NULL, NULL);
+
+nla_put_failure:
+       nlmsg_free(msg);
+
+       return err;
+}
+
+int __near_netlink_dep_link_up(uint32_t idx, uint32_t target_idx,
+                               uint8_t comm_mode, uint8_t rf_mode)
+{
+       struct nl_msg *msg;
+       void *hdr;
+       int err;
+
+       DBG("");
+
+       msg = nlmsg_alloc();
+       if (msg == NULL)
+               return -ENOMEM;
+
+       hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nfc_state->nfc_id, 0,
+                       NLM_F_REQUEST, NFC_CMD_DEP_LINK_UP, NFC_GENL_VERSION);
+       if (hdr == NULL) {
+               err = -EINVAL;
+               goto nla_put_failure;
+       }
+
+       err = -EMSGSIZE;
+
+       NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, idx);
+       NLA_PUT_U32(msg, NFC_ATTR_TARGET_INDEX, target_idx);
+       NLA_PUT_U8(msg, NFC_ATTR_COMM_MODE, comm_mode);
+       NLA_PUT_U8(msg, NFC_ATTR_RF_MODE, rf_mode);
+
+       err = nl_send_msg(nfc_state->cmd_sock, msg, NULL, NULL);
+
+nla_put_failure:
+       nlmsg_free(msg);
+
+       return err;
+}
+
+int __near_netlink_dep_link_down(uint32_t idx)
+{
+       struct nl_msg *msg;
+       void *hdr;
+       int err;
+
+       DBG("");
+
+       msg = nlmsg_alloc();
+       if (msg == NULL)
+               return -ENOMEM;
+
+       hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nfc_state->nfc_id, 0,
+                       NLM_F_REQUEST, NFC_CMD_DEP_LINK_DOWN, NFC_GENL_VERSION);
+       if (hdr == NULL) {
+               err = -EINVAL;
+               goto nla_put_failure;
+       }
+
+       err = -EMSGSIZE;
+
+       NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, idx);
+
+       err = nl_send_msg(nfc_state->cmd_sock, msg, NULL, NULL);
+
+nla_put_failure:
+       nlmsg_free(msg);
+
+       return err;
+}
+
+int __near_netlink_adapter_enable(int idx, near_bool_t enable)
+{
+       struct nl_msg *msg;
+       void *hdr;
+       int err;
+       uint8_t cmd;
+
+       DBG("");
+
+       msg = nlmsg_alloc();
+       if (msg == NULL)
+               return -ENOMEM;
+
+       if (enable == TRUE)
+               cmd = NFC_CMD_DEV_UP;
+       else
+               cmd = NFC_CMD_DEV_DOWN;
+
+       hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nfc_state->nfc_id, 0,
+                       NLM_F_REQUEST, cmd, NFC_GENL_VERSION);
+       if (hdr == NULL) {
+               err = -EINVAL;
+               goto nla_put_failure;
+       }
+
+       err = -EMSGSIZE;
+
+       NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, idx);
+
+       err = nl_send_msg(nfc_state->cmd_sock, msg, NULL, NULL);
+
+nla_put_failure:
+       nlmsg_free(msg);
+
+       return err;
+}
+
+
+static int no_seq_check(struct nl_msg *n, void *arg)
+{
+       DBG("");
+
+       return NL_OK;
+}
+
+static int nfc_netlink_event_adapter(struct genlmsghdr *gnlh, near_bool_t add)
+{
+       struct nlattr *attrs[NFC_ATTR_MAX + 1];
+       uint32_t idx;
+
+       DBG("");
+
+       nla_parse(attrs, NFC_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+       if (attrs[NFC_ATTR_DEVICE_INDEX] == NULL) {
+               near_error("Missing device index");
+               return -ENODEV;
+       }
+
+       idx = nla_get_u32(attrs[NFC_ATTR_DEVICE_INDEX]);
+
+       if (add == TRUE &&
+               (attrs[NFC_ATTR_DEVICE_NAME] == NULL ||
+                       attrs[NFC_ATTR_PROTOCOLS] == NULL)) {
+               near_error("Missing attributes");
+               return -EINVAL;
+       }
+
+       if (add == TRUE) {
+               char *name;
+               uint32_t protocols;
+               near_bool_t powered;
+
+               name = nla_get_string(attrs[NFC_ATTR_DEVICE_NAME]);
+               protocols = nla_get_u32(attrs[NFC_ATTR_PROTOCOLS]);
+               if (attrs[NFC_ATTR_DEVICE_POWERED] == NULL)
+                       powered = FALSE;
+               else
+                       powered = nla_get_u8(attrs[NFC_ATTR_DEVICE_POWERED]);
+
+               return __near_manager_adapter_add(idx, name,
+                                               protocols, powered);
+       } else {
+               __near_manager_adapter_remove(idx);
+       }
+
+       return 0;
+}
+
+static int get_targets_handler(struct nl_msg *n, void *arg)
+{
+       struct nlmsghdr *nlh = nlmsg_hdr(n);
+       struct nlattr *attrs[NFC_ATTR_MAX + 1];
+       uint32_t adapter_idx, target_idx, protocols;
+       uint16_t sens_res = 0;
+       uint8_t sel_res = 0;
+       uint8_t nfcid[NFC_MAX_NFCID1_LEN], nfcid_len;
+
+       DBG("");
+
+       genlmsg_parse(nlh, 0, attrs, NFC_ATTR_MAX, NULL);
+
+       adapter_idx = *((uint32_t *)arg);
+       target_idx = nla_get_u32(attrs[NFC_ATTR_TARGET_INDEX]);
+       protocols = nla_get_u32(attrs[NFC_ATTR_PROTOCOLS]);
+
+       if (attrs[NFC_ATTR_TARGET_SENS_RES] != NULL)
+               sens_res =
+                       nla_get_u16(attrs[NFC_ATTR_TARGET_SENS_RES]);
+
+       if (attrs[NFC_ATTR_TARGET_SEL_RES] != NULL)
+               sel_res =
+                       nla_get_u16(attrs[NFC_ATTR_TARGET_SEL_RES]);
+
+       if (attrs[NFC_ATTR_TARGET_NFCID1] != NULL) {
+               nfcid_len = nla_len(attrs[NFC_ATTR_TARGET_NFCID1]);
+               if (nfcid_len <= NFC_MAX_NFCID1_LEN)
+                       memcpy(nfcid, nla_data(attrs[NFC_ATTR_TARGET_NFCID1]),
+                                                               nfcid_len);
+       } else {
+               nfcid_len = 0;
+       }
+
+       DBG("target idx %d proto 0x%x sens_res 0x%x sel_res 0x%x NFCID len %d",
+           target_idx, protocols, sens_res, sel_res, nfcid_len);
+
+       __near_adapter_add_target(adapter_idx, target_idx, protocols,
+                                 sens_res, sel_res, nfcid, nfcid_len);
+
+       return 0;
+}
+
+static int nfc_netlink_event_targets_found(struct genlmsghdr *gnlh)
+{
+       struct nlattr *attr[NFC_ATTR_MAX + 1];
+       struct nl_msg *msg;
+       void *hdr;
+       int err;
+       uint32_t adapter_idx;
+
+       DBG("");
+
+       nla_parse(attr, NFC_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                       genlmsg_attrlen(gnlh, 0), NULL);
+       if (attr[NFC_ATTR_DEVICE_INDEX] == NULL)
+               return -ENODEV;
+
+       adapter_idx = nla_get_u32(attr[NFC_ATTR_DEVICE_INDEX]);
+
+       DBG("adapter %d", adapter_idx);
+
+       msg = nlmsg_alloc();
+       if (msg == NULL)
+               return -ENOMEM;
+
+       hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nfc_state->nfc_id, 0,
+                       NLM_F_DUMP, NFC_CMD_GET_TARGET, NFC_GENL_VERSION);
+       if (hdr == NULL) {
+               err = -EINVAL;
+               goto nla_put_failure;
+       }
+
+       err = -EMSGSIZE;
+
+       NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, adapter_idx);
+
+       err = nl_send_msg(nfc_state->cmd_sock, msg,
+                               get_targets_handler, &adapter_idx);
+
+nla_put_failure:
+       nlmsg_free(msg);
+
+       return err;
+}
+
+static int nfc_netlink_event_target_lost(struct genlmsghdr *gnlh)
+{
+       struct nlattr *attr[NFC_ATTR_MAX + 1];
+       uint32_t adapter_idx, target_idx;
+
+       DBG("");
+
+       nla_parse(attr, NFC_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                       genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (attr[NFC_ATTR_DEVICE_INDEX] == NULL)
+               return -ENODEV;
+
+       if (attr[NFC_ATTR_TARGET_INDEX] == NULL)
+               return -ENODEV;
+
+       adapter_idx = nla_get_u32(attr[NFC_ATTR_DEVICE_INDEX]);
+       target_idx = nla_get_u32(attr[NFC_ATTR_TARGET_INDEX]);
+
+       DBG("adapter %d target %d", adapter_idx, target_idx);
+
+       return __near_adapter_remove_target(adapter_idx, target_idx);
+}
+
+static int nfc_netlink_event_dep_up(struct genlmsghdr *gnlh)
+{
+       struct nlattr *attrs[NFC_ATTR_MAX + 1];
+       uint32_t idx, target_idx = 0;
+       uint8_t rf_mode;
+
+       DBG("");
+
+       nla_parse(attrs, NFC_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+       if (attrs[NFC_ATTR_DEVICE_INDEX] == NULL) {
+               near_error("Missing device index");
+               return -ENODEV;
+       }
+
+       if (attrs[NFC_ATTR_COMM_MODE] == NULL ||
+                       attrs[NFC_ATTR_RF_MODE] == NULL) {
+               near_error("Missing rf or comm modes");
+               return -ENODEV;
+       }
+
+       idx = nla_get_u32(attrs[NFC_ATTR_DEVICE_INDEX]);
+       rf_mode = nla_get_u8(attrs[NFC_ATTR_RF_MODE]);
+
+       if (rf_mode == NFC_RF_INITIATOR) {
+               if (attrs[NFC_ATTR_TARGET_INDEX] == NULL) {
+                       near_error("Missing target index");
+                       return -ENODEV;
+               };
+
+               target_idx = nla_get_u32(attrs[NFC_ATTR_TARGET_INDEX]);
+
+               DBG("%d %d", idx, target_idx);
+
+               return __near_adapter_set_dep_state(idx, TRUE);
+       } else {
+               return -EOPNOTSUPP;
+       }
+}
+
+static int nfc_netlink_event_dep_down(struct genlmsghdr *gnlh)
+{
+       struct nlattr *attrs[NFC_ATTR_MAX + 1];
+       uint32_t idx;
+
+       DBG("");
+
+       nla_parse(attrs, NFC_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+       if (attrs[NFC_ATTR_DEVICE_INDEX] == NULL) {
+               near_error("Missing device index");
+               return -ENODEV;
+       }
+
+       idx = nla_get_u32(attrs[NFC_ATTR_DEVICE_INDEX]);
+
+       __near_adapter_set_dep_state(idx, FALSE);
+
+       return 0;
+}
+
+static int nfc_netlink_event_tm_activated(struct genlmsghdr *gnlh)
+{
+       struct nlattr *attrs[NFC_ATTR_MAX + 1];
+       uint32_t idx;
+
+       DBG("");
+
+       nla_parse(attrs, NFC_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+       if (attrs[NFC_ATTR_DEVICE_INDEX] == NULL) {
+               near_error("Missing device index");
+               return -ENODEV;
+       }
+
+       idx = nla_get_u32(attrs[NFC_ATTR_DEVICE_INDEX]);
+
+       DBG("%d", idx);
+
+       return __near_adapter_add_device(idx, NULL, 0);
+}
+
+static int nfc_netlink_event_tm_deactivated(struct genlmsghdr *gnlh)
+{
+       struct nlattr *attrs[NFC_ATTR_MAX + 1];
+       uint32_t idx;
+
+       DBG("");
+
+       nla_parse(attrs, NFC_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+       if (attrs[NFC_ATTR_DEVICE_INDEX] == NULL) {
+               near_error("Missing device index");
+               return -ENODEV;
+       }
+
+       idx = nla_get_u32(attrs[NFC_ATTR_DEVICE_INDEX]);
+
+       DBG("%d", idx);
+
+       return __near_adapter_remove_device(idx);
+}
+
+static int nfc_netlink_event(struct nl_msg *n, void *arg)
+{
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(n));
+
+       DBG("event 0x%x", gnlh->cmd);
+
+       switch (gnlh->cmd) {
+       case NFC_EVENT_TARGETS_FOUND:
+               DBG("Targets found");
+               nfc_netlink_event_targets_found(gnlh);
+               break;
+       case NFC_EVENT_TARGET_LOST:
+               DBG("Target lost");
+               nfc_netlink_event_target_lost(gnlh);
+               break;
+       case NFC_EVENT_DEVICE_ADDED:
+               DBG("Adapter added");
+               nfc_netlink_event_adapter(gnlh, TRUE);
+
+               break;
+       case NFC_EVENT_DEVICE_REMOVED:
+               DBG("Adapter removed");
+               nfc_netlink_event_adapter(gnlh, FALSE);
+
+               break;
+       case NFC_CMD_DEP_LINK_UP:
+               DBG("DEP link is up");
+               nfc_netlink_event_dep_up(gnlh);
+
+               break;
+       case NFC_CMD_DEP_LINK_DOWN:
+               DBG("DEP link is down");
+               nfc_netlink_event_dep_down(gnlh);
+
+               break;
+       case NFC_EVENT_TM_ACTIVATED:
+               DBG("Target mode activated");
+               nfc_netlink_event_tm_activated(gnlh);
+
+               break;
+       case NFC_EVENT_TM_DEACTIVATED:
+               DBG("Target mode deactivated");
+               nfc_netlink_event_tm_deactivated(gnlh);
+
+               break;
+       }
+
+       return NL_SKIP;
+}
+
+static gboolean __nfc_netlink_event(GIOChannel *channel,
+                               GIOCondition cond, gpointer data)
+{
+       struct nl_cb *cb;
+       struct nlnfc_state *state = data;
+
+       if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
+               return FALSE;
+
+       cb = nl_cb_alloc(NL_CB_VERBOSE);
+       if (cb == NULL)
+               return TRUE;
+
+       nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
+       nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, nfc_netlink_event, data);
+
+       nl_recvmsgs(state->event_sock, cb);
+
+       nl_cb_put(cb);
+
+       return TRUE;
+}
+
+static int nfc_event_listener(struct nlnfc_state *state)
+{
+       int sock;
+
+       sock = nl_socket_get_fd(state->event_sock);
+       netlink_channel = g_io_channel_unix_new(sock);
+       g_io_channel_set_close_on_unref(netlink_channel, TRUE);
+
+       g_io_channel_set_encoding(netlink_channel, NULL, NULL);
+       g_io_channel_set_buffered(netlink_channel, FALSE);
+
+       g_io_add_watch(netlink_channel,
+                               G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+                                               __nfc_netlink_event, state);
+
+       return 0;
+}
+
+struct handler_args {
+       const char *group;
+       int id;
+};
+
+static int family_handler(struct nl_msg *msg, void *arg)
+{
+       struct handler_args *grp = arg;
+       struct nlattr *tb[CTRL_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *mcgrp;
+       int rem_mcgrp;
+
+       DBG("");
+
+       nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+        if (!tb[CTRL_ATTR_MCAST_GROUPS])
+               return NL_SKIP;
+
+       nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mcgrp) {
+               struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1];
+
+               nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX,
+                         nla_data(mcgrp), nla_len(mcgrp), NULL);
+
+               if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME] ||
+                   !tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID])
+                       continue;
+               if (strncmp(nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]),
+                           grp->group, nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME])))
+                       continue;
+               grp->id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]);
+               break;
+       }
+       
+       return NL_SKIP;
+}
+
+static int nl_get_multicast_id(struct nl_sock *sock, const char *family,
+                               const char *group)
+{
+       struct nl_msg *msg;
+       int err, ctrlid;
+       struct handler_args grp = {
+               .group = group,
+               .id = -ENOENT,
+       };
+
+       DBG("");
+
+       msg = nlmsg_alloc();
+       if (msg == NULL)
+               return -ENOMEM;
+
+       ctrlid = genl_ctrl_resolve(sock, "nlctrl");
+
+        genlmsg_put(msg, 0, 0, ctrlid, 0,
+                   0, CTRL_CMD_GETFAMILY, 0);
+
+       err = -EMSGSIZE;
+
+       NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family);
+
+       err = nl_send_msg(sock, msg, family_handler, &grp);
+       if (err)
+               goto nla_put_failure;
+
+       DBG("multicast id %d", grp.id);
+
+       err = grp.id;
+
+nla_put_failure:
+       nlmsg_free(msg);
+
+       return err;
+}
+
+int __near_netlink_init(void)
+{
+       int err;
+
+       DBG("");
+
+       nfc_state = g_try_malloc0(sizeof(struct nlnfc_state));
+       if (nfc_state == NULL)
+               return -ENOMEM;
+
+       nfc_state->cmd_sock = nl_socket_alloc();
+       if (nfc_state->cmd_sock == NULL) {
+               near_error("Failed to allocate NFC command netlink socket");
+               err = -ENOMEM;
+               goto state_free;
+       }
+
+       nfc_state->event_sock = nl_socket_alloc();
+       if (nfc_state->event_sock == NULL) {
+               near_error("Failed to allocate NFC event netlink socket");
+               err = -ENOMEM;
+               goto handle_cmd_destroy;
+       }
+
+       if (genl_connect(nfc_state->cmd_sock)) {
+               near_error("Failed to connect to generic netlink");
+               err = -ENOLINK;
+               goto handle_event_destroy;
+       }
+
+       if (genl_connect(nfc_state->event_sock)) {
+               near_error("Failed to connect to generic netlink");
+               err = -ENOLINK;
+               goto handle_event_destroy;
+       }
+
+       nfc_state->nfc_id = genl_ctrl_resolve(nfc_state->cmd_sock, "nfc");
+       if (nfc_state->nfc_id < 0) {
+               near_error("Unable to find NFC netlink family");
+               err = -ENOENT;
+               goto handle_event_destroy;
+       }
+
+       nfc_state->mcid = nl_get_multicast_id(nfc_state->cmd_sock, NFC_GENL_NAME,
+                                               NFC_GENL_MCAST_EVENT_NAME);
+       if (nfc_state->mcid <= 0) {
+               near_error("Wrong mcast id %d", nfc_state->mcid);
+               err = nfc_state->mcid;
+               goto handle_event_destroy;
+       }
+
+       err = nl_socket_add_membership(nfc_state->event_sock, nfc_state->mcid);
+       if (err) {
+               near_error("Error adding nl event socket to membership");
+               goto handle_event_destroy;
+       }
+
+       return nfc_event_listener(nfc_state);
+
+handle_event_destroy:
+       nl_socket_free(nfc_state->event_sock);
+
+handle_cmd_destroy:
+       nl_socket_free(nfc_state->cmd_sock);
+
+state_free:
+       g_free(nfc_state);
+
+       nfc_state = NULL;
+
+       near_error("netlink init failed");
+
+       return err;
+}
+
+void __near_netlink_cleanup(void)
+{
+       if (netlink_channel != NULL) {
+               g_io_channel_shutdown(netlink_channel, TRUE, NULL);
+               g_io_channel_unref(netlink_channel);
+
+               netlink_channel = NULL;
+       }
+
+       if (nfc_state == NULL)
+               return;
+
+       nl_socket_free(nfc_state->cmd_sock);
+       nl_socket_free(nfc_state->event_sock);
+
+       g_free(nfc_state);
+
+       DBG("");
+}
+
diff --git a/src/org.neard.conf b/src/org.neard.conf
new file mode 100644 (file)
index 0000000..12c0ab9
--- /dev/null
@@ -0,0 +1,15 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+    <policy user="root">
+        <allow own="org.neard"/>
+        <allow send_destination="org.neard"/>
+        <allow send_interface="org.neard.NDEFAgent"/>
+    </policy>
+    <policy at_console="true">
+        <allow send_destination="org.neard"/>
+    </policy>
+    <policy context="default">
+        <deny send_destination="org.neard"/>
+    </policy>
+</busconfig>
diff --git a/src/plugin.c b/src/plugin.c
new file mode 100644 (file)
index 0000000..6c5b676
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dlfcn.h>
+
+#include <glib.h>
+
+#ifdef NEAR_PLUGIN_BUILTIN
+#undef NEAR_PLUGIN_BUILTIN
+#endif
+
+#include "near.h"
+
+static GSList *plugins = NULL;
+
+struct near_plugin {
+       void *handle;
+       gboolean active;
+       struct near_plugin_desc *desc;
+};
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct near_plugin *plugin1 = a;
+       const struct near_plugin *plugin2 = b;
+
+       return plugin2->desc->priority - plugin1->desc->priority;
+}
+
+static gboolean add_plugin(void *handle, struct near_plugin_desc *desc)
+{
+       struct near_plugin *plugin;
+
+       if (desc->init == NULL)
+               return FALSE;
+
+       if (g_str_equal(desc->version, NEAR_VERSION) == FALSE) {
+               near_error("Version mismatch for %s", desc->description);
+               return FALSE;
+       }
+
+       plugin = g_try_new0(struct near_plugin, 1);
+       if (plugin == NULL)
+               return FALSE;
+
+       plugin->handle = handle;
+       plugin->active = FALSE;
+       plugin->desc = desc;
+
+       plugins = g_slist_insert_sorted(plugins, plugin, compare_priority);
+
+       return TRUE;
+}
+
+static gboolean check_plugin(struct near_plugin_desc *desc,
+                               char **patterns, char **excludes)
+{
+       if (excludes) {
+               for (; *excludes; excludes++)
+                       if (g_pattern_match_simple(*excludes, desc->name))
+                               break;
+               if (*excludes) {
+                       near_info("Excluding %s", desc->description);
+                       return FALSE;
+               }
+       }
+
+       if (patterns) {
+               for (; *patterns; patterns++)
+                       if (g_pattern_match_simple(*patterns, desc->name))
+                               break;
+               if (!*patterns) {
+                       near_info("Ignoring %s", desc->description);
+                       return FALSE;
+               }
+       }
+
+       return TRUE;
+}
+
+#include "builtin.h"
+
+int __near_plugin_init(const char *pattern, const char *exclude)
+{
+       gchar **patterns = NULL;
+       gchar **excludes = NULL;
+       GSList *list;
+       GDir *dir;
+       const gchar *file;
+       gchar *filename;
+       unsigned int i;
+
+       DBG("");
+
+       if (pattern)
+               patterns = g_strsplit_set(pattern, ":, ", -1);
+
+       if (exclude)
+               excludes = g_strsplit_set(exclude, ":, ", -1);
+
+       for (i = 0; __near_builtin[i]; i++) {
+               if (check_plugin(__near_builtin[i],
+                                               patterns, excludes) == FALSE)
+                       continue;
+
+               add_plugin(NULL, __near_builtin[i]);
+       }
+
+       dir = g_dir_open(PLUGINDIR, 0, NULL);
+       if (dir != NULL) {
+               while ((file = g_dir_read_name(dir)) != NULL) {
+                       void *handle;
+                       struct near_plugin_desc *desc;
+
+                       if (g_str_has_prefix(file, "lib") == TRUE ||
+                                       g_str_has_suffix(file, ".so") == FALSE)
+                               continue;
+
+                       filename = g_build_filename(PLUGINDIR, file, NULL);
+
+                       handle = dlopen(filename, RTLD_NOW);
+                       if (handle == NULL) {
+                               near_error("Can't load %s: %s",
+                                                       filename, dlerror());
+                               g_free(filename);
+                               continue;
+                       }
+
+                       g_free(filename);
+
+                       desc = dlsym(handle, "near_plugin_desc");
+                       if (desc == NULL) {
+                               near_error("Can't load symbol: %s",
+                                                               dlerror());
+                               dlclose(handle);
+                               continue;
+                       }
+
+                       if (check_plugin(desc, patterns, excludes) == FALSE) {
+                               dlclose(handle);
+                               continue;
+                       }
+
+                       if (add_plugin(handle, desc) == FALSE)
+                               dlclose(handle);
+               }
+
+               g_dir_close(dir);
+       }
+
+       for (list = plugins; list; list = list->next) {
+               struct near_plugin *plugin = list->data;
+
+               if (plugin->desc->init() < 0)
+                       continue;
+
+               plugin->active = TRUE;
+       }
+
+       g_strfreev(patterns);
+       g_strfreev(excludes);
+
+       return 0;
+}
+
+void __near_plugin_cleanup(void)
+{
+       GSList *list;
+
+       DBG("");
+
+       for (list = plugins; list; list = list->next) {
+               struct near_plugin *plugin = list->data;
+
+               if (plugin->active == TRUE && plugin->desc->exit)
+                       plugin->desc->exit();
+
+               if (plugin->handle != NULL)
+                       dlclose(plugin->handle);
+
+               g_free(plugin);
+       }
+
+       g_slist_free(plugins);
+}
diff --git a/src/tag.c b/src/tag.c
new file mode 100644 (file)
index 0000000..68b04a4
--- /dev/null
+++ b/src/tag.c
@@ -0,0 +1,1097 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <gdbus.h>
+
+#include "near.h"
+
+#define TYPE3_IDM_LEN 8
+#define TYPE3_ATTR_BLOCK_SIZE 16
+
+struct near_tag {
+       char *path;
+
+       uint32_t adapter_idx;
+       uint32_t target_idx;
+
+       uint32_t protocol;
+       uint32_t type;
+       enum near_tag_sub_type sub_type;
+       enum near_tag_memory_layout layout;
+       near_bool_t readonly;
+
+       uint8_t nfcid[NFC_MAX_NFCID1_LEN];
+       uint8_t nfcid_len;
+
+       size_t data_length;
+       uint8_t *data;
+
+       uint32_t n_records;
+       GList *records;
+       near_bool_t blank;
+
+       /* Tag specific structures */
+       struct {
+               uint8_t IDm[TYPE3_IDM_LEN];
+               uint8_t attr[TYPE3_ATTR_BLOCK_SIZE];
+               uint8_t ic_type;
+       } t3;
+
+       struct {
+               uint16_t max_ndef_size;
+               uint16_t c_apdu_max_size;
+       } t4;
+
+       DBusMessage *write_msg; /* Pending write message */
+       struct near_ndef_message *write_ndef;
+};
+
+static DBusConnection *connection = NULL;
+
+static GHashTable *tag_hash;
+
+static GSList *driver_list = NULL;
+
+struct near_tag *near_tag_get_tag(uint32_t adapter_idx, uint32_t target_idx)
+{
+       struct near_tag *tag;
+       char *path;
+
+       path = g_strdup_printf("%s/nfc%d/tag%d", NFC_PATH,
+                                       adapter_idx, target_idx);
+       if (path == NULL)
+               return NULL;
+
+       tag = g_hash_table_lookup(tag_hash, path);
+       g_free(path);
+
+       /* TODO refcount */
+       return tag;
+}
+
+static void append_records(DBusMessageIter *iter, void *user_data)
+{
+       struct near_tag *tag = user_data;
+       GList *list;
+
+       DBG("");
+
+       for (list = tag->records; list; list = list->next) {
+               struct near_ndef_record *record = list->data;
+               char *path;
+
+               path = __near_ndef_record_get_path(record);
+               if (path == NULL)
+                       continue;
+
+               dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+                                                       &path);
+       }
+}
+
+static const char *type_string(struct near_tag *tag)
+{
+       const char *type;
+
+       DBG("type 0x%x", tag->type);
+
+       switch (tag->type) {
+       case NFC_PROTO_JEWEL:
+               type = "Type 1";
+               break;
+
+       case NFC_PROTO_MIFARE:
+               type = "Type 2";
+               break;
+
+       case NFC_PROTO_FELICA:
+               type = "Type 3";
+               break;
+
+       case NFC_PROTO_ISO14443:
+               type = "Type 4";
+               break;
+
+       default:
+               type = NULL;
+               near_error("Unknown tag type 0x%x", tag->type);
+               break;
+       }
+
+       return type;
+}
+
+static const char *protocol_string(struct near_tag *tag)
+{
+       const char *protocol;
+
+       DBG("protocol 0x%x", tag->protocol);
+
+       switch (tag->protocol) {
+       case NFC_PROTO_FELICA_MASK:
+               protocol = "Felica";
+               break;
+
+       case NFC_PROTO_MIFARE_MASK:
+               protocol = "MIFARE";
+               break;
+
+       case NFC_PROTO_JEWEL_MASK:
+               protocol = "Jewel";
+               break;
+
+       case NFC_PROTO_ISO14443_MASK:
+               protocol = "ISO-DEP";
+               break;
+
+       default:
+               near_error("Unknown tag protocol 0x%x", tag->protocol);
+               protocol = NULL;
+       }
+
+       return protocol;
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct near_tag *tag = data;
+       const char *protocol, *type;
+       DBusMessage *reply;
+       DBusMessageIter array, dict;
+
+       DBG("conn %p", conn);
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &array);
+
+       near_dbus_dict_open(&array, &dict);
+
+       type = type_string(tag);
+       if (type != NULL)
+               near_dbus_dict_append_basic(&dict, "Type",
+                                       DBUS_TYPE_STRING, &type);
+
+       protocol = protocol_string(tag);
+       if (protocol != NULL)
+               near_dbus_dict_append_basic(&dict, "Protocol",
+                                       DBUS_TYPE_STRING, &protocol);
+
+       near_dbus_dict_append_basic(&dict, "ReadOnly",
+                                       DBUS_TYPE_BOOLEAN, &tag->readonly);
+
+       near_dbus_dict_append_array(&dict, "Records",
+                               DBUS_TYPE_OBJECT_PATH, append_records, tag);
+
+       near_dbus_dict_close(&array, &dict);
+
+       return reply;
+}
+
+static DBusMessage *set_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBG("conn %p", conn);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static void tag_read_cb(uint32_t adapter_idx, uint32_t target_idx, int status)
+{
+       struct near_tag *tag;
+
+       tag = near_tag_get_tag(adapter_idx, target_idx);
+
+       if (tag == NULL)
+               return;
+
+       dbus_message_unref(tag->write_msg);
+       tag->write_msg = NULL;
+
+       __near_adapter_start_check_presence(adapter_idx, target_idx);
+
+       __near_adapter_tags_changed(adapter_idx);
+}
+
+static void write_cb(uint32_t adapter_idx, uint32_t target_idx, int status)
+{
+       struct near_tag *tag;
+       DBusConnection *conn;
+       DBusMessage *reply;
+
+       DBG("Write status %d", status);
+
+       tag = near_tag_get_tag(adapter_idx, target_idx);
+       if (tag == NULL)
+               return;
+
+       conn = near_dbus_get_connection();
+       if (conn == NULL)
+               goto out;
+
+       if (status != 0) {
+               reply = __near_error_failed(tag->write_msg, EINVAL);
+               if (reply != NULL)
+                       g_dbus_send_message(conn, reply);
+       } else {
+               g_dbus_send_reply(conn, tag->write_msg, DBUS_TYPE_INVALID);
+       }
+
+       near_ndef_records_free(tag->records);
+       tag->n_records = 0;
+       tag->records = NULL;
+       g_free(tag->data);
+       tag->data = NULL;
+
+       if (status == 0) {
+               /*
+                * If writing succeeded,
+                * check presence will be restored after reading
+                */
+               __near_tag_read(tag, tag_read_cb);
+               return;
+       }
+
+out:
+       dbus_message_unref(tag->write_msg);
+       tag->write_msg = NULL;
+
+       __near_adapter_start_check_presence(tag->adapter_idx, tag->target_idx);
+}
+
+static void format_cb(uint32_t adapter_idx, uint32_t target_idx, int status)
+{
+       struct near_tag *tag;
+       int err;
+
+       DBG("format status %d", status);
+
+       tag = near_tag_get_tag(adapter_idx, target_idx);
+       if (tag == NULL)
+               return;
+
+       if (tag->write_msg == NULL)
+               return;
+
+       if (status == 0) {
+               err = __near_tag_write(tag, tag->write_ndef,
+                                               write_cb);
+               if (err < 0)
+                       goto error;
+       } else {
+               err = status;
+               goto error;
+       }
+
+       return;
+
+error:
+       write_cb(tag->adapter_idx, tag->target_idx, err);
+}
+
+static DBusMessage *write_ndef(DBusConnection *conn,
+                               DBusMessage *msg, void *data)
+{
+       struct near_tag *tag = data;
+       struct near_ndef_message *ndef, *ndef_with_header = NULL;
+       int tlv_len_size, err;
+
+       DBG("conn %p", conn);
+
+       if (tag->readonly == TRUE) {
+               DBG("Read only tag");
+               return __near_error_permission_denied(msg);
+       }
+
+       if (tag->write_msg)
+               return __near_error_in_progress(msg);
+
+       ndef = __ndef_build_from_message(msg);
+       if (ndef == NULL)
+               return __near_error_failed(msg, EINVAL);
+
+       tag->write_msg = dbus_message_ref(msg);
+
+       /* Add NDEF header information depends upon tag type */
+       switch (tag->type) {
+       case NFC_PROTO_JEWEL:
+       case NFC_PROTO_MIFARE:
+               if (ndef->length < 0xff)
+                       tlv_len_size = 3;
+               else
+                       tlv_len_size = 5;
+
+               ndef_with_header = g_try_malloc0(sizeof(
+                                       struct near_ndef_message));
+               if (ndef_with_header == NULL)
+                       goto fail;
+
+               ndef_with_header->offset = 0;
+               ndef_with_header->length = ndef->length + tlv_len_size;
+               ndef_with_header->data =
+                               g_try_malloc0(ndef->length + tlv_len_size);
+               if (ndef_with_header->data == NULL)
+                       goto fail;
+
+               ndef_with_header->data[0] = TLV_NDEF;
+
+               if (ndef->length < 0xff) {
+                       ndef_with_header->data[1] = ndef->length;
+               } else {
+                       ndef_with_header->data[1] = 0xff;
+                       ndef_with_header->data[2] =
+                                       (uint8_t)(ndef->length >> 8);
+                       ndef_with_header->data[3] = (uint8_t)(ndef->length);
+               }
+
+               memcpy(ndef_with_header->data + tlv_len_size - 1, ndef->data,
+                               ndef->length);
+               ndef_with_header->data[ndef->length + tlv_len_size - 1] =
+                                                                       TLV_END;
+               break;
+
+       case NFC_PROTO_FELICA:
+               ndef_with_header = g_try_malloc0(sizeof(
+                                       struct near_ndef_message));
+               if (ndef_with_header == NULL)
+                       goto fail;
+
+               ndef_with_header->offset = 0;
+               ndef_with_header->length = ndef->length;
+               ndef_with_header->data = g_try_malloc0(
+                                               ndef_with_header->length);
+               if (ndef_with_header->data == NULL)
+                       goto fail;
+
+               memcpy(ndef_with_header->data, ndef->data, ndef->length);
+
+               break;
+
+       case NFC_PROTO_ISO14443:
+               ndef_with_header = g_try_malloc0(sizeof(
+                                       struct near_ndef_message));
+               if (ndef_with_header == NULL)
+                       goto fail;
+
+               ndef_with_header->offset = 0;
+               ndef_with_header->length = ndef->length + 2;
+               ndef_with_header->data = g_try_malloc0(ndef->length + 2);
+               if (ndef_with_header->data == NULL)
+                       goto fail;
+
+               ndef_with_header->data[0] = (uint8_t)(ndef->length >> 8);
+               ndef_with_header->data[1] = (uint8_t)(ndef->length);
+               memcpy(ndef_with_header->data + 2, ndef->data, ndef->length);
+
+               break;
+
+       default:
+               g_free(ndef->data);
+               g_free(ndef);
+
+               return __near_error_failed(msg, EOPNOTSUPP);
+       }
+
+       g_free(ndef->data);
+       g_free(ndef);
+
+       tag->write_ndef = ndef_with_header;
+       err = __near_tag_write(tag, ndef_with_header, write_cb);
+       if (err < 0) {
+               g_free(ndef_with_header->data);
+               g_free(ndef_with_header);
+
+               return __near_error_failed(msg, -err);
+       }
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+fail:
+       dbus_message_unref(tag->write_msg);
+       tag->write_msg = NULL;
+
+       return __near_error_failed(msg, ENOMEM);
+}
+
+static DBusMessage *get_raw_ndef(DBusConnection *conn,
+                               DBusMessage *msg, void *data)
+{
+       struct near_tag *tag = data;
+       DBusMessage *reply;
+       DBusMessageIter iter, array;
+
+       DBG("");
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                               DBUS_TYPE_BYTE_AS_STRING,
+                                               &array);
+
+       __near_ndef_append_records(&array, tag->records);
+
+       dbus_message_iter_close_container(&iter, &array);
+
+       return reply;
+}
+
+static const GDBusMethodTable tag_methods[] = {
+       { GDBUS_METHOD("GetProperties",
+                               NULL, GDBUS_ARGS({"properties", "a{sv}"}),
+                               get_properties) },
+       { GDBUS_METHOD("SetProperty",
+                               GDBUS_ARGS({"name", "s"}, {"value", "v"}),
+                               NULL, set_property) },
+       { GDBUS_ASYNC_METHOD("Write", GDBUS_ARGS({"attributes", "a{sv}"}),
+                                                       NULL, write_ndef) },
+       { GDBUS_METHOD("GetRawNDEF",
+                               NULL, GDBUS_ARGS({"NDEF", "ay"}),
+                               get_raw_ndef) },
+       { },
+};
+
+static const GDBusSignalTable tag_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                               GDBUS_ARGS({"name", "s"}, {"value", "v"})) },
+       { }
+};
+
+
+void __near_tag_append_records(struct near_tag *tag, DBusMessageIter *iter)
+{
+       GList *list;
+
+       for (list = tag->records; list; list = list->next) {
+               struct near_ndef_record *record = list->data;
+               char *path;
+
+               path = __near_ndef_record_get_path(record);
+               if (path == NULL)
+                       continue;
+
+               dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+                                                       &path);
+       }
+}
+
+#define NFC_TAG_A (NFC_PROTO_ISO14443_MASK | NFC_PROTO_NFC_DEP_MASK | \
+                               NFC_PROTO_JEWEL_MASK | NFC_PROTO_MIFARE_MASK)
+#define NFC_TAG_A_TYPE2      0x00
+#define NFC_TAG_A_TYPE4      0x01
+#define NFC_TAG_A_NFC_DEP    0x02
+#define NFC_TAG_A_TYPE4_DEP  0x03
+
+#define NFC_TAG_A_SENS_RES_SSD_JEWEL      0x00
+#define NFC_TAG_A_SENS_RES_PLATCONF_JEWEL 0x0c
+
+#define NFC_TAG_A_SEL_PROT(sel_res) (((sel_res) & 0x60) >> 5)
+#define NFC_TAG_A_SEL_CASCADE(sel_res) (((sel_res) & 0x04) >> 2)
+#define NFC_TAG_A_SENS_RES_SSD(sens_res) ((sens_res) & 0x001f)
+#define NFC_TAG_A_SENS_RES_PLATCONF(sens_res) (((sens_res) & 0x0f00) >> 8)
+
+static enum near_tag_sub_type get_tag_type2_sub_type(uint8_t sel_res)
+{
+       switch(sel_res) {
+       case 0x00 :
+               return NEAR_TAG_NFC_T2_MIFARE_ULTRALIGHT;
+       case 0x08:
+               return NEAR_TAG_NFC_T2_MIFARE_CLASSIC_1K;
+       case 0x09:
+               return NEAR_TAG_NFC_T2_MIFARE_MINI;
+       case 0x18:
+               return NEAR_TAG_NFC_T2_MIFARE_CLASSIC_4K;
+       case 0x20:
+               return NEAR_TAG_NFC_T2_MIFARE_DESFIRE;
+       case 0x28 :
+               return NEAR_TAG_NFC_T2_JCOP30;
+       case 0x38:
+               return NEAR_TAG_NFC_T2_MIFARE_4K_EMUL;
+       case 0x88:
+               return NEAR_TAG_NFC_T2_MIFARE_1K_INFINEON;
+       case 0x98:
+               return NEAR_TAG_NFC_T2_MPCOS;
+       }
+
+       return NEAR_TAG_NFC_SUBTYPE_UNKNOWN;
+}
+
+static void set_tag_type(struct near_tag *tag,
+                               uint16_t sens_res, uint8_t sel_res)
+{
+       uint8_t platconf, ssd, proto;
+
+       DBG("protocol 0x%x sens_res 0x%x sel_res 0x%x", tag->protocol,
+                                                       sens_res, sel_res);
+
+       switch (tag->protocol) {
+       case NFC_PROTO_JEWEL_MASK:
+               platconf = NFC_TAG_A_SENS_RES_PLATCONF(sens_res);
+               ssd = NFC_TAG_A_SENS_RES_SSD(sens_res);
+
+               DBG("Jewel");
+
+               if ((ssd == NFC_TAG_A_SENS_RES_SSD_JEWEL) &&
+                               (platconf == NFC_TAG_A_SENS_RES_PLATCONF_JEWEL))
+                       tag->type = NFC_PROTO_JEWEL;
+               break;
+
+       case NFC_PROTO_MIFARE_MASK:
+       case NFC_PROTO_ISO14443_MASK:
+               proto = NFC_TAG_A_SEL_PROT(sel_res);
+
+               DBG("proto 0x%x", proto);
+
+               switch(proto) {
+               case NFC_TAG_A_TYPE2:
+                       tag->type = NFC_PROTO_MIFARE;
+                       tag->sub_type = get_tag_type2_sub_type(sel_res);
+                       break;
+               case NFC_TAG_A_TYPE4:
+                       tag->type = NFC_PROTO_ISO14443;
+                       break;
+               case NFC_TAG_A_TYPE4_DEP:
+                       tag->type = NFC_PROTO_NFC_DEP;
+                       break;
+               }
+               break;
+
+       case NFC_PROTO_FELICA_MASK:
+               tag->type = NFC_PROTO_FELICA;
+               break;
+
+       default:
+               tag->type = NFC_PROTO_MAX;
+               break;
+       }
+
+       DBG("tag type 0x%x", tag->type);
+}
+
+static int tag_initialize(struct near_tag *tag,
+                       uint32_t adapter_idx, uint32_t target_idx,
+                       uint32_t protocols,
+                       uint16_t sens_res, uint8_t sel_res,
+                       uint8_t *nfcid, uint8_t nfcid_len)
+{
+       DBG("");
+
+       tag->path = g_strdup_printf("%s/nfc%d/tag%d", NFC_PATH,
+                                       adapter_idx, target_idx);
+       if (tag->path == NULL)
+               return -ENOMEM;
+       tag->adapter_idx = adapter_idx;
+       tag->target_idx = target_idx;
+       tag->protocol = protocols;
+       tag->n_records = 0;
+       tag->readonly = FALSE;
+
+       if (nfcid_len <= NFC_MAX_NFCID1_LEN) {
+               tag->nfcid_len = nfcid_len;
+               memcpy(tag->nfcid, nfcid, nfcid_len);
+       }
+
+       set_tag_type(tag, sens_res, sel_res);
+
+       return 0;
+}
+
+struct near_tag *__near_tag_add(uint32_t adapter_idx, uint32_t target_idx,
+                               uint32_t protocols,
+                               uint16_t sens_res, uint8_t sel_res,
+                               uint8_t *nfcid, uint8_t nfcid_len)
+{
+       struct near_tag *tag;
+       char *path;
+
+       tag = near_tag_get_tag(adapter_idx, target_idx);
+       if (tag != NULL)
+               return NULL;
+
+       tag = g_try_malloc0(sizeof(struct near_tag));
+       if (tag == NULL)
+               return NULL;
+
+       if (tag_initialize(tag, adapter_idx, target_idx,
+                               protocols,
+                               sens_res, sel_res,
+                               nfcid, nfcid_len) < 0) {
+               g_free(tag);
+               return NULL;
+       }
+
+       path = g_strdup(tag->path);
+       if (path == NULL) {
+               g_free(tag);
+               return NULL;
+       }
+
+       g_hash_table_insert(tag_hash, path, tag);
+
+       DBG("connection %p", connection);
+
+       g_dbus_register_interface(connection, tag->path,
+                                       NFC_TAG_INTERFACE,
+                                       tag_methods, tag_signals,
+                                                       NULL, tag, NULL);
+
+       return tag;
+}
+
+void __near_tag_remove(struct near_tag *tag)
+{
+       char *path = tag->path;
+
+       DBG("path %s", tag->path);
+
+       if (g_hash_table_lookup(tag_hash, tag->path) == NULL)
+               return;
+
+       g_dbus_unregister_interface(connection, tag->path,
+                                               NFC_TAG_INTERFACE);
+
+       g_hash_table_remove(tag_hash, path);
+}
+
+const char *__near_tag_get_path(struct near_tag *tag)
+{
+       return tag->path;
+}
+
+uint32_t __near_tag_get_type(struct near_tag *tag)
+{
+       return tag->type;
+}
+
+enum near_tag_sub_type near_tag_get_subtype(uint32_t adapter_idx,
+                               uint32_t target_idx)
+
+{
+       struct near_tag *tag;
+
+       tag = near_tag_get_tag(adapter_idx, target_idx);
+       if (tag == NULL)
+               return NEAR_TAG_NFC_SUBTYPE_UNKNOWN;
+
+       return tag->sub_type;
+}
+
+uint8_t *near_tag_get_nfcid(uint32_t adapter_idx, uint32_t target_idx,
+                               uint8_t *nfcid_len)
+{
+       struct near_tag *tag;
+       uint8_t *nfcid;
+
+       tag = near_tag_get_tag(adapter_idx, target_idx);
+       if (tag == NULL)
+               goto fail;
+
+       nfcid = g_try_malloc0(tag->nfcid_len);
+       if (nfcid == NULL)
+               goto fail;
+
+       memcpy(nfcid, tag->nfcid, tag->nfcid_len);
+       *nfcid_len = tag->nfcid_len;
+
+       return nfcid;
+
+fail:
+       *nfcid_len = 0;
+       return NULL;
+}
+
+int near_tag_set_nfcid(uint32_t adapter_idx, uint32_t target_idx,
+                                       uint8_t *nfcid, size_t nfcid_len)
+{
+       struct near_tag *tag;
+
+       DBG("NFCID len %zd", nfcid_len);
+
+       tag = near_tag_get_tag(adapter_idx, target_idx);
+       if (tag == NULL)
+               return -ENODEV;
+
+       if (tag->nfcid_len > 0)
+               return -EALREADY;
+
+       if (nfcid_len > NFC_MAX_NFCID1_LEN)
+               return -EINVAL;
+
+       memcpy(tag->nfcid, nfcid, nfcid_len);
+       tag->nfcid_len = nfcid_len;
+
+       return 0;
+}
+
+int near_tag_add_data(uint32_t adapter_idx, uint32_t target_idx,
+                       uint8_t *data, size_t data_length)
+{
+       struct near_tag *tag;
+
+       tag = near_tag_get_tag(adapter_idx, target_idx);
+       if (tag == NULL)
+               return -ENODEV;
+
+       tag->data_length = data_length;
+       tag->data = g_try_malloc0(data_length);
+       if (tag->data == NULL)
+               return -ENOMEM;
+
+       if (data != NULL)
+               memcpy(tag->data, data, data_length);
+
+       return 0;
+}
+
+int near_tag_add_records(struct near_tag *tag, GList *records,
+                               near_tag_io_cb cb, int status)
+{
+       GList *list;
+       struct near_ndef_record *record;
+       char *path;
+
+       DBG("records %p", records);
+
+       for (list = records; list; list = list->next) {
+               record = list->data;
+
+               path = g_strdup_printf("%s/nfc%d/tag%d/record%d",
+                                       NFC_PATH, tag->adapter_idx,
+                                       tag->target_idx, tag->n_records);
+
+               if (path == NULL)
+                       continue;
+
+               __near_ndef_record_register(record, path);
+
+               tag->n_records++;
+               tag->records = g_list_append(tag->records, record);
+       }
+
+       __near_agent_ndef_parse_records(tag->records);
+
+       if (cb != NULL)
+               cb(tag->adapter_idx, tag->target_idx, status);
+
+       g_list_free(records);
+
+       return 0;
+}
+
+void near_tag_set_ro(struct near_tag *tag, near_bool_t readonly)
+{
+       tag->readonly = readonly;
+}
+
+void near_tag_set_blank(struct near_tag *tag, near_bool_t blank)
+{
+       tag->blank = blank;
+}
+
+near_bool_t near_tag_get_blank(struct near_tag *tag)
+{
+       return tag->blank;
+}
+
+uint8_t *near_tag_get_data(struct near_tag *tag, size_t *data_length)
+{
+       if (data_length == NULL)
+               return NULL;
+
+       *data_length = tag->data_length;
+
+       return tag->data;
+}
+
+uint32_t near_tag_get_adapter_idx(struct near_tag *tag)
+{
+       return tag->adapter_idx;
+}
+
+uint32_t near_tag_get_target_idx(struct near_tag *tag)
+{
+       return tag->target_idx;
+}
+
+enum near_tag_memory_layout near_tag_get_memory_layout(struct near_tag *tag)
+{
+       if (tag == NULL)
+               return NEAR_TAG_MEMORY_UNKNOWN;
+
+       return tag->layout;
+}
+
+void near_tag_set_memory_layout(struct near_tag *tag,
+                                       enum near_tag_memory_layout layout)
+{
+       if (tag == NULL)
+               return;
+
+       tag->layout = layout;
+}
+
+void near_tag_set_max_ndef_size(struct near_tag *tag, uint16_t size)
+{
+       if (tag == NULL)
+               return;
+
+       tag->t4.max_ndef_size = size;
+}
+
+uint16_t near_tag_get_max_ndef_size(struct near_tag *tag)
+{
+       if (tag == NULL)
+               return 0;
+
+       return tag->t4.max_ndef_size;
+}
+
+void near_tag_set_c_apdu_max_size(struct near_tag *tag, uint16_t size)
+{
+       if (tag == NULL)
+               return;
+
+       tag->t4.c_apdu_max_size = size;
+}
+
+uint16_t near_tag_get_c_apdu_max_size(struct near_tag *tag)
+{
+       if (tag == NULL)
+               return 0;
+
+       return tag->t4.c_apdu_max_size;
+}
+
+void near_tag_set_idm(struct near_tag *tag, uint8_t *idm, uint8_t len)
+{
+       if (tag == NULL || len > TYPE3_IDM_LEN)
+               return;
+
+       memset(tag->t3.IDm, 0, TYPE3_IDM_LEN);
+       memcpy(tag->t3.IDm, idm, len);
+}
+
+uint8_t *near_tag_get_idm(struct near_tag *tag, uint8_t *len)
+{
+       if (tag == NULL || len == NULL)
+               return NULL;
+
+       *len = TYPE3_IDM_LEN;
+       return tag->t3.IDm;
+}
+
+void near_tag_set_attr_block(struct near_tag *tag, uint8_t *attr, uint8_t len)
+{
+       if (tag == NULL || len > TYPE3_ATTR_BLOCK_SIZE)
+               return;
+
+       memset(tag->t3.attr, 0, TYPE3_ATTR_BLOCK_SIZE);
+       memcpy(tag->t3.attr, attr, len);
+}
+
+uint8_t *near_tag_get_attr_block(struct near_tag *tag, uint8_t *len)
+{
+       if (tag == NULL || len == NULL)
+               return NULL;
+
+       *len = TYPE3_ATTR_BLOCK_SIZE;
+       return tag->t3.attr;
+}
+
+void near_tag_set_ic_type(struct near_tag *tag, uint8_t ic_type)
+{
+       if (tag == NULL)
+               return;
+
+       tag->t3.ic_type = ic_type;
+}
+
+uint8_t near_tag_get_ic_type(struct near_tag *tag)
+{
+       if (tag == NULL)
+               return 0;
+
+       return tag->t3.ic_type;
+}
+
+static gint cmp_prio(gconstpointer a, gconstpointer b)
+{
+       const struct near_tag_driver *driver1 = a;
+       const struct near_tag_driver *driver2 = b;
+
+       return driver2->priority - driver1->priority;
+}
+
+int near_tag_driver_register(struct near_tag_driver *driver)
+{
+       DBG("");
+
+       if (driver->read == NULL)
+               return -EINVAL;
+
+       driver_list = g_slist_insert_sorted(driver_list, driver, cmp_prio);
+
+       return 0;
+}
+
+void near_tag_driver_unregister(struct near_tag_driver *driver)
+{
+       DBG("");
+
+       driver_list = g_slist_remove(driver_list, driver);
+}
+
+int __near_tag_read(struct near_tag *tag, near_tag_io_cb cb)
+{
+       GSList *list;
+
+       DBG("type 0x%x", tag->type);
+
+       /* Stop check presence while reading */
+       __near_adapter_stop_check_presence(tag->adapter_idx, tag->target_idx);
+
+       for (list = driver_list; list; list = list->next) {
+               struct near_tag_driver *driver = list->data;
+
+               DBG("driver type 0x%x", driver->type);
+
+               if (driver->type == tag->type)
+                       return driver->read(tag->adapter_idx, tag->target_idx,
+                                                                       cb);
+       }
+
+       return 0;
+}
+
+int __near_tag_write(struct near_tag *tag,
+                               struct near_ndef_message *ndef,
+                               near_tag_io_cb cb)
+{
+       GSList *list;
+       int err;
+
+       DBG("type 0x%x", tag->type);
+
+       for (list = driver_list; list; list = list->next) {
+               struct near_tag_driver *driver = list->data;
+
+               DBG("driver type 0x%x", driver->type);
+
+               if (driver->type == tag->type) {
+                       /* Stop check presence while writing */
+                       __near_adapter_stop_check_presence(tag->adapter_idx,
+                                                               tag->target_idx);
+
+                       if (tag->blank == TRUE && driver->format != NULL) {
+                               DBG("Blank tag detected, formatting");
+                               err = driver->format(tag->adapter_idx,
+                                               tag->target_idx, format_cb);
+                       } else {
+                               err = driver->write(tag->adapter_idx,
+                                                       tag->target_idx, ndef,
+                                                       cb);
+                       }
+
+                       break;
+               }
+       }
+
+       if (list == NULL)
+               err = -EOPNOTSUPP;
+
+       if (err < 0)
+               __near_adapter_start_check_presence(tag->adapter_idx,
+                                                       tag->target_idx);
+
+       return err;
+}
+
+int __near_tag_check_presence(struct near_tag *tag, near_tag_io_cb cb)
+{
+       GSList *list;
+
+       DBG("type 0x%x", tag->type);
+
+       for (list = driver_list; list; list = list->next) {
+               struct near_tag_driver *driver = list->data;
+
+               DBG("driver type 0x%x", driver->type);
+
+               if (driver->type == tag->type) {
+                       if (driver->check_presence == NULL)
+                               continue;
+
+                       return driver->check_presence(tag->adapter_idx, tag->target_idx, cb);
+               }
+       }
+
+       return -EOPNOTSUPP;
+}
+
+static void free_tag(gpointer data)
+{
+       struct near_tag *tag = data;
+
+       DBG("tag %p", tag);
+
+       near_ndef_records_free(tag->records);
+
+       g_free(tag->path);
+       g_free(tag->data);
+       g_free(tag);
+}
+
+int __near_tag_init(void)
+{
+       DBG("");
+
+       connection = near_dbus_get_connection();
+
+       tag_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, free_tag);
+
+       return 0;
+}
+
+void __near_tag_cleanup(void)
+{
+       DBG("");
+
+       g_hash_table_destroy(tag_hash);
+       tag_hash = NULL;
+}
diff --git a/src/tlv.c b/src/tlv.c
new file mode 100644 (file)
index 0000000..d16ddb0
--- /dev/null
+++ b/src/tlv.c
@@ -0,0 +1,115 @@
+/*
+ *
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2011  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "near.h"
+
+uint16_t near_tlv_length(uint8_t *tlv)
+{
+       uint16_t length;
+
+       if (tlv[0] == TLV_NULL || tlv[0] == TLV_END)
+               length = 0;
+       else if (tlv[1] == 0xff)
+               length = g_ntohs(*(uint16_t *)(tlv + 2));
+       else
+               length = tlv[1];
+
+       return length;
+}
+
+uint8_t *near_tlv_next(uint8_t *tlv)
+{
+       uint16_t length;
+       uint8_t l_length;
+
+       length = near_tlv_length(tlv);
+       if (length > 0xfe)
+               l_length = 3;
+       else if (length == 0)
+               l_length = 0;
+       else
+               l_length = 1;
+
+       /* T (1 byte) + L (1 or 3 bytes) + V */
+       return tlv + 1 + l_length + length;
+}
+
+uint8_t *near_tlv_data(uint8_t *tlv)
+{
+       uint16_t length;
+       uint8_t l_length;
+
+       length = near_tlv_length(tlv);
+       if (length > 0xfe)
+               l_length = 3;
+       else if (length == 0)
+               l_length = 0;
+       else
+               l_length = 1;
+
+       /* T (1 byte) + L (1 or 3 bytes) */
+       return tlv + 1 + l_length;
+}
+
+GList *near_tlv_parse(uint8_t *tlv, size_t tlv_length)
+{
+       GList *records;
+       uint8_t *data, t;
+
+       DBG("");
+
+       records = NULL;
+       data = tlv;
+
+       while(1) {
+               t = tlv[0];
+
+               DBG("tlv 0x%x", tlv[0]);
+
+               switch (t) {
+               case TLV_NDEF:
+                       DBG("NDEF found %d bytes long", near_tlv_length(tlv));
+
+                       records = near_ndef_parse_msg(near_tlv_data(tlv),
+                                               near_tlv_length(tlv));
+
+                       break;
+               case TLV_END:
+                       break;
+               }
+
+               if (t == TLV_END)
+                       break;
+
+               tlv = near_tlv_next(tlv);
+
+               if (tlv - data >= (uint16_t) tlv_length)
+                       break;
+       }
+
+       DBG("Done");
+
+       return records;
+}
diff --git a/test/bt-handover b/test/bt-handover
new file mode 100755 (executable)
index 0000000..95b6dbf
--- /dev/null
@@ -0,0 +1,69 @@
+#!/usr/bin/python
+
+import os
+import sys
+import gobject
+
+import dbus
+import dbus.mainloop.glib
+
+from dbus.lowlevel import MethodCallMessage, HANDLER_RESULT_NOT_YET_HANDLED
+
+mainloop = gobject.MainLoop()
+
+def property_changed_adapter(name, value, path):
+    if name in ["Devices"]:
+        if (len(value) == 0):
+            print "Lost device, exiting"
+            mainloop.quit()
+        else:
+            device_path = value[0]
+
+            print "Pairing with %s" % (device_path)
+
+            device = dbus.Interface(bus.get_object("org.neard", device_path), "org.neard.Device")
+            device.Push(({ "Type" : "Handover", "Carrier" : "bluetooth"}))
+
+if __name__ == '__main__':
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+        bluez_manager = dbus.Interface(bus.get_object("org.bluez", "/"), "org.bluez.Manager")
+       
+       bluez_adapter_path = bluez_manager.DefaultAdapter()
+
+       bluez_adapter = dbus.Interface(bus.get_object("org.bluez", bluez_adapter_path),
+                                                       "org.bluez.Adapter")
+
+       for bluez_path in bluez_adapter.ListDevices():
+               print("Removing %s" % (bluez_path))
+               bluez_adapter.RemoveDevice(bluez_path)
+
+
+       manager = dbus.Interface(bus.get_object("org.neard", "/"),
+                                       "org.neard.Manager")
+
+       properties = manager.GetProperties()
+       device_path = properties["Adapters"][0]
+       adapter = dbus.Interface(bus.get_object("org.neard", device_path),
+                                       "org.neard.Adapter")
+
+        adapter_properties = adapter.GetProperties()
+
+        for key in adapter_properties.keys():
+            if key in ["Polling"]:
+                if adapter_properties[key] == dbus.Boolean(1):
+                    print "Stoping poll on %s" % (device_path)
+                    adapter.StopPollLoop()
+
+        print "Starting poll on %s" % (device_path)
+       adapter.StartPollLoop("Initiator")
+
+       bus.add_signal_receiver(property_changed_adapter,
+                               bus_name="org.neard",
+                               dbus_interface="org.neard.Adapter",
+                               signal_name = "PropertyChanged",
+                               path_keyword="path")
+
+       mainloop.run()
diff --git a/test/disable-adapter b/test/disable-adapter
new file mode 100755 (executable)
index 0000000..fe514ff
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if len(sys.argv) < 2:
+       print "Usage: %s <nfc device>" % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.neard", "/"),
+                                       "org.neard.Manager")
+
+
+path = "/org/neard/" + sys.argv[1]
+adapter = dbus.Interface(bus.get_object("org.neard", path),
+                                       "org.neard.Adapter")
+
+try:
+       adapter.SetProperty("Powered", dbus.Boolean(0), timeout = 10)
+except dbus.DBusException, error:
+       print "%s: %s" % (error._dbus_error_name, error.message)
\ No newline at end of file
diff --git a/test/dump-device b/test/dump-device
new file mode 100755 (executable)
index 0000000..58b090c
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+def extract_record(key, list):
+       for i in list:
+               record = dbus.Interface(bus.get_object("org.neard", i),
+                                               "org.neard.Record")
+
+               properties = record.GetProperties()
+               print "        Record = [ %s ]" % (str(i))
+
+               for key in properties.keys():
+                       val = str(properties[key])
+                       print "              %s = %s" % (key, val)
+
+device = dbus.Interface(bus.get_object("org.neard", sys.argv[1]),
+                                               "org.neard.Device")
+
+properties = device.GetProperties()
+
+print "[ %s ]" % (sys.argv[1])
+
+for key in properties.keys():
+    if key in ["Records"]:
+        extract_record(key, properties[key])
+
diff --git a/test/dump-record b/test/dump-record
new file mode 100755 (executable)
index 0000000..8cf3cb1
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+def extract_list(list):
+       val = "["
+       for i in list:
+               val += " " + str(i)
+       val += " ]"
+       return val
+
+bus = dbus.SystemBus()
+
+record = dbus.Interface(bus.get_object("org.neard", sys.argv[1]),
+                                               "org.neard.Record")
+
+properties = record.GetProperties()
+
+print "[ %s ]" % (sys.argv[1])
+
+for key in properties.keys():
+    val = str(properties[key])
+    print "      %s = %s" % (key, val)
diff --git a/test/dump-tag b/test/dump-tag
new file mode 100755 (executable)
index 0000000..4e987fc
--- /dev/null
@@ -0,0 +1,48 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+def extract_ndef(list):
+       val = "["
+       for i in list:
+               val += " 0x%x" % i
+       val += " ]"
+       
+       return val
+
+def extract_record(key, list):
+       for i in list:
+               record = dbus.Interface(bus.get_object("org.neard", i),
+                                               "org.neard.Record")
+
+               properties = record.GetProperties()
+               print "        Record = [ %s ]" % (str(i))
+
+               for key in properties.keys():
+                       val = str(properties[key])
+                       print "              %s = %s" % (key, val)
+
+tag = dbus.Interface(bus.get_object("org.neard", sys.argv[1]),
+                                               "org.neard.Tag")
+
+properties = tag.GetProperties()
+raw_ndef = tag.GetRawNDEF()
+
+print "[ %s ]" % (sys.argv[1])
+
+print "        Raw NDEF = %s" % (extract_ndef(raw_ndef))
+
+for key in properties.keys():
+    if key in ["Type"]:
+        val = str(properties[key])
+       print "        %s = %s" % (key, val)
+    elif key in ["Protocol"]:
+       val = str(properties[key])
+       print "        %s = %s" % (key, val)
+
+    if key in ["Records"]:
+        extract_record(key, properties[key])
+
diff --git a/test/enable-adapter b/test/enable-adapter
new file mode 100755 (executable)
index 0000000..bd6f03a
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if len(sys.argv) < 2:
+       print "Usage: %s <nfc device>" % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.neard", "/"),
+                                       "org.neard.Manager")
+
+
+path = "/org/neard/" + sys.argv[1]
+adapter = dbus.Interface(bus.get_object("org.neard", path),
+                                       "org.neard.Adapter")
+
+try:
+       adapter.SetProperty("Powered", dbus.Boolean(1), timeout = 10)
+except dbus.DBusException, error:
+       print "%s: %s" % (error._dbus_error_name, error.message)
diff --git a/test/list-adapters b/test/list-adapters
new file mode 100755 (executable)
index 0000000..9e11322
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/python
+
+import dbus
+
+
+def extract_list(list):
+       val = "["
+       for i in list:
+               val += " " + str(i)
+       val += " ]"
+       return val
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.neard", "/"),
+                                       "org.neard.Manager")
+
+
+properties = manager.GetProperties()
+
+for path in properties["Adapters"]:
+    print "[ %s ]" % (path)
+
+    adapter = dbus.Interface(bus.get_object("org.neard", path),
+                                               "org.neard.Adapter")
+
+    properties = adapter.GetProperties()
+
+    for key in properties.keys():
+        if key in ["Powered", "Polling"]:
+            if properties[key] == dbus.Boolean(1):
+                val = "true"
+            else:
+                val = "false"
+        elif key in ["Protocols", "Tags", "Devices"]:
+            val = extract_list(properties[key])
+        else:
+            val = str(properties[key])
+            
+        print "        %s = %s" % (key, val)
diff --git a/test/monitor-near b/test/monitor-near
new file mode 100755 (executable)
index 0000000..fef307e
--- /dev/null
@@ -0,0 +1,62 @@
+#!/usr/bin/python
+
+import gobject
+
+import dbus
+import dbus.mainloop.glib
+
+from dbus.lowlevel import MethodCallMessage, HANDLER_RESULT_NOT_YET_HANDLED
+
+def extract_list(list):
+       val = "["
+       for i in list:
+               val += " " + str(i)
+       val += " ]"
+       return val
+
+def extract_bool(b):
+        if b == dbus.Boolean(1):
+          val = "true"
+       else:
+               val = "false"
+       return val      
+
+
+def property_changed_adapter(name, value, path):
+    adapter = path[path.rfind("/") + 1:]
+    if name in ["Polling"]:
+           val = extract_bool(value)
+    elif name in ["Tags", "Devices"]:
+          val = extract_list(value)
+    else:
+          val = str(value)
+
+    print "[Adapter] [%s] %s = %s" % (adapter, name, val)
+
+def property_changed_manager(name, value, path):
+    manager = path[path.rfind("/") + 1:]
+    if name in ["Adapters"]:
+           val = extract_list(value)
+
+    print "[Manager] %s = %s" % (name, val)
+
+
+if __name__ == '__main__':
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       bus.add_signal_receiver(property_changed_manager,
+                               bus_name="org.neard",
+                               dbus_interface="org.neard.Manager",
+                               signal_name = "PropertyChanged",
+                               path_keyword="path")
+
+       bus.add_signal_receiver(property_changed_adapter,
+                               bus_name="org.neard",
+                               dbus_interface="org.neard.Adapter",
+                               signal_name = "PropertyChanged",
+                               path_keyword="path")
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/neard-ui.py b/test/neard-ui.py
new file mode 100755 (executable)
index 0000000..1113ca8
--- /dev/null
@@ -0,0 +1,586 @@
+#!/usr/bin/env python
+
+import pdb
+import sys
+
+import gobject
+import gtk
+
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+
+import traceback
+
+##=================================================================
+class Neard:
+
+    #signal handler #1 for Manager
+    def manager_Added(self, name):
+        print (" Added %s") % name
+        self.manager_getDetails()
+
+    #signal handler #2 for Manager
+    def manager_Removed(self, name):
+        print (" Removed %s") % name
+        if self.adapters_remove is not None:
+            self.adapter_update(name)
+            return
+
+    #connect to the manager in order to be notified on
+    #add/remove adapter
+    def manager_Connect(self):
+        try:
+            manager_obj = self.systemBus.get_object('org.neard', "/")
+            # Add 2 handlers to follow Adapters
+            manager_obj.connect_to_signal('AdapterAdded',
+                                           self.manager_Added,
+                                          'org.neard.Manager')
+            manager_obj.connect_to_signal('AdapterRemoved',
+                                          self.manager_Removed,
+                                          'org.neard.Manager')
+            self.iface_manager = dbus.Interface(manager_obj, 'org.neard.Manager')
+
+        except:
+            print ("Can't connect to org.neard.Manager");
+            self.iface_manager = None
+        #Retrieve the manager informations
+        self.manager_getDetails()
+
+    def manager_getDetails(self):
+        #No iface_manager means adapter removed
+        if self.iface_manager is None:
+            if self.adapters_remove is not None:
+                self.adapters_remove()
+            return
+        #Need to update adapter's details
+        self.adapter_updateDetails(self.iface_manager.GetProperties())
+
+    def adapter_PropertyChanged(self, prop, value, adapt_path = None):
+        print("Prop changed: %s") % prop
+        adapt_properties = {}
+        adapt_properties[prop] = value
+        if prop == "Tags":
+            self.tag_updateDetails(adapt_properties)
+        else:
+            self.adapter_update(adapt_path, adapt_properties)
+
+        #Update the records UI
+    def record_updateDetails(self, tag_properties):
+         for record_path in tag_properties["Records"]:
+            print ("REC %s ") % record_path
+            record_obj = self.systemBus.get_object('org.neard',
+                                                     record_path)
+            record_iface = dbus.Interface(record_obj,'org.neard.Record')
+            record_properties = record_iface.GetProperties()
+
+            self.recordregistered[record_path] = True
+
+            # call UI update
+            self.records_update(record_path, record_properties)
+
+    #Update the tags UI
+    def tag_updateDetails(self, adapt_properties):
+        if adapt_properties["Tags"]:
+            for tag_path in adapt_properties["Tags"]:
+                print ("TAG %s ") % tag_path
+                tag_obj = self.systemBus.get_object('org.neard', tag_path)
+
+                tag_iface = dbus.Interface(tag_obj,'org.neard.Tag')
+                tag_properties = tag_iface.GetProperties()
+
+                self.tagregistered[tag_path] = True
+                # call UI update
+                self.tags_update(tag_path, tag_properties)
+                #Process the records
+                self.record_updateDetails(tag_properties)
+        else:
+            print ("remove tags and records")
+            self.tags_update()
+            self.records_update()
+
+
+    #Something changed, must update the UI
+    def adapter_updateDetails(self, properties):
+        if self.adapter_update is not None and "Adapters" in properties:
+            for adapt_path in properties["Adapters"]:
+                if adapt_path in self.adaptregistered:
+                    print (" already registered %s") % adapt_path
+                else:
+                    #Get valuable informations from the object
+                    adapter_obj = self.systemBus.get_object('org.neard',
+                                                         adapt_path)
+                    adapter_obj.connect_to_signal('PropertyChanged',
+                                               self.adapter_PropertyChanged,
+                                               'org.neard.Adapter',
+                                               path_keyword='adapt_path')
+
+                    adapt_iface = dbus.Interface(adapter_obj,'org.neard.Adapter')
+                    adapt_properties = adapt_iface.GetProperties()
+
+                    self.adaptregistered[adapt_path] = True
+
+                    #Update display info
+                    self.adapter_update(adapt_path, adapt_properties)
+
+                    #udpate UI for tags
+                    self.tag_updateDetails(adapt_properties)
+
+
+    #Search DBUS to find any neard instance
+    def neardNameOwnerChanged(self, proxy):
+        if proxy:
+            print("Neard is connected to System bus")
+            self.manager_Connect()
+        else:
+            print("Neard is disconnected from System bus")
+            self.iface_manager = None
+            self.adaptregistered = {}
+            self.manager_getDetails()
+
+    #Main init function
+    def __init__(self, adapter_update = None, adapters_remove = None,
+                  tags_update = None, records_update = None):
+        self.test = False
+        self.adapter_update = adapter_update
+        self.adapters_remove = adapters_remove
+        self.tags_update = tags_update
+        self.records_update = records_update
+
+        self.adaptregistered = {}
+        self.tagregistered = {}
+        self.recordregistered = {}
+
+        self.systemBus = dbus.SystemBus()
+
+        #Prepare the first handler
+        self.systemBus.watch_name_owner('org.neard',
+                                         self.neardNameOwnerChanged)
+
+##=================================================================
+class NeardUI(Neard):
+
+    # return the current selection
+    def adapters_actionToggle(self, i, col):
+        if i:
+            return self.adapters_list.get_value(i, col)
+        return True
+
+    # Action: activate or not the polling mode
+    def adapter_pollingToggled(self, poolingRendererToggle, path, user):
+        if path:
+            i = self.adapters_list.get_iter(path)
+            objpath = self.adapters_list.get_value(i, 0)
+            adapter_obj = self.systemBus.get_object('org.neard', objpath)
+            adapt_iface = dbus.Interface(adapter_obj,'org.neard.Adapter')
+
+            if self.adapters_actionToggle(i, 3):
+                print ("Stop Polling %s") % objpath
+                adapt_iface.StopPollLoop()
+            else:
+                print ("Start Polling %s") % objpath
+                adapt_iface.StartPollLoop("Initiator")
+
+
+    #------------------------------
+    #Set the field values
+    def adapters_setUIList(self, adapt_properties, i, col, name, path = None):
+        if name in adapt_properties:
+            value = adapt_properties[name]
+
+            if name == "Tags":
+                value = "{"
+                for tgts in adapt_properties[name]:
+                    #For each tag, add it to the tag lbox:
+                    #Trim path....
+                    short_tgt = tgts[len(path)+1:]
+
+                    if value == "{":
+                        value = "{" + short_tgt
+                    else:
+                        value = value + "," + short_tgt
+                value = value + "}"
+
+            if name == "Protocols":
+                value = None
+                for protos in adapt_properties[name]:
+                    if value is None:
+                        value = protos
+                    else:
+                        value = value + " " + protos
+
+            if value is not None:
+                self.adapters_list.set_value(i, col, value)
+            print ("  property %s, value %s") % (name, value)
+
+    # Clear one or all the adapters present in list
+    def adapter_RemoveUI(self):
+        self.adapters_list.clear()
+
+    #Add, Update or delete a list entry
+    def adapter_UpdateUI(self, path = None, adapt_properties = None):
+        i = self.adapters_list.get_iter_first()
+        while i is not None:
+            if self.adapters_list.get_value(i, 0) == path:
+                break
+            i = self.adapters_list.iter_next(i)
+
+        if adapt_properties is None:
+            if i:
+                print ("Delete adapter %s") % path
+                self.adapters_list.remove(i)
+            else:
+                print ("Already deleted adapter %s") % path
+            return
+
+        if i is None:
+            i = self.adapters_list.append()
+            self.adapters_list.set_value(i, 0, path)
+            print ("Add adapter %s") % (path)
+        else:
+            print ("Update adapter %s") % (path)
+
+
+        self.adapters_setUIList(adapt_properties, i, 2, "Powered")
+        self.adapters_setUIList(adapt_properties, i, 3, "Polling")
+        self.adapters_setUIList(adapt_properties, i, 4, "Protocols")
+        self.adapters_setUIList(adapt_properties, i, 5, "Tags", path)
+
+    #--------------------------------------------------
+    def tags_setUIList(self, tag_properties, i, col, name):
+        if name in tag_properties:
+            value = tag_properties[name]
+
+            if name == "Records":
+                value = None
+                for tags in tag_properties[name]:
+                    #For each tag, add it to the tag lbox:
+                    if value is None:
+                        value = tags
+                    else:
+                        value = value + "-" + tags
+
+            if name == "Type":
+                value = None
+                for tags in tag_properties[name]:
+                    #For each tag, add it to the tag lbox:
+                    if value is None:
+                        value = tags
+                    else:
+                        value = value + "-" + tags
+
+            if value is not None:
+                self.tags_list.set_value(i, col, value)
+            print ("  property %s, value %s") % (name, value)
+
+    #Add, Update or delete a list entry
+    def tag_UpdateUI(self, path = None, tag_properties = None):
+        print("Tag Update %s") % path
+        i = self.tags_list.get_iter_first()
+        while i is not None:
+            if self.tags_list.get_value(i, 0) == path:
+                break
+            i = self.tags_list.iter_next(i)
+
+        #Delete mode: Remove all
+        if tag_properties is None:
+            i = self.tags_list.get_iter_first()
+            while i is not None:
+                path_name = self.tags_list.get_value(i, 0)
+                print ("Deleted tag %s") % path_name
+                self.tags_list.remove(i)
+                if self.tags_list.iter_is_valid(i):
+                    i = self.tags_list.iter_next(i)
+                else:
+                    i = None
+            return
+
+        if i is None:
+            i = self.tags_list.append()
+            self.tags_list.set_value(i, 0, path)
+            print ("Add tag %s") % (path)
+        else:
+            print ("Update tag %s") % (path)
+        self.tags_setUIList(tag_properties, i, 2, "ReadOnly")
+        self.tags_setUIList(tag_properties, i, 3, "Type")
+        self.tags_setUIList(tag_properties, i, 4, "Records")
+
+    #--------------------------------------------------
+    def records_setUIList(self, record_properties, i, col, name):
+        if name in record_properties:
+            value = record_properties[name]
+        else:
+            value = ""
+            for rec_data in record_properties:
+                if rec_data != "Type":
+                     if value != "":
+                         value = value + "\n"
+                     value = value + rec_data + " :   " +record_properties[rec_data]
+
+        if value is not None:
+            self.records_list.set_value(i, col, value)
+        print ("  property %s, value %s") % (name, value)
+
+    #Add, Update or delete a list entry
+    def record_UpdateUI(self, path = None, record_properties = None):
+        print("Record Update %s") % path
+        i = self.records_list.get_iter_first()
+        while i is not None:
+            if self.records_list.get_value(i, 0) == path:
+                break
+            i = self.records_list.iter_next(i)
+
+         #Delete mode: Remove all records
+        if record_properties is None:
+            i = self.records_list.get_iter_first()
+            while i is not None:
+                path_name = self.records_list.get_value(i, 0)
+                print ("Delete record %s") % path_name
+                self.records_list.remove(i)
+                if self.records_list.iter_is_valid(i):
+                    i = self.records_list.iter_next(i)
+                else:
+                    i = None
+            return
+
+        if i is None:
+            i = self.records_list.append()
+            self.records_list.set_value(i, 0, path)
+            print ("Add record %s") % (path)
+        else:
+            print ("Update record %s") % (path)
+
+        self.records_setUIList(record_properties, i, 2, "Type")
+        self.records_setUIList(record_properties, i, 3, "Data")
+
+
+    def tag_RemoveUI(self):
+        printf("Tag Remove")
+
+    #Adapter selected on lbox
+    def on_adapter_sel_changed(self, selection = None):
+        model, iter = selection.get_selected()
+        if iter:
+             value = self.adapters_list.get_value(iter, 0)
+             print ("value %s") % value
+             value = self.adapters_list.get_value(iter, 5)
+             print ("tag: %s") % value
+
+
+    #-----------------------------------------------------
+    # Prepare TreeView  for Adapters
+    def createAdaptersWidgets(self, adaptlist):
+
+        #treeview adapters
+        tv_adapters = gtk.TreeView(adaptlist)
+
+        column = gtk.TreeViewColumn("Path", gtk.CellRendererText(), text=0)
+        tv_adapters.append_column(column)
+
+        toggle = gtk.CellRendererToggle()
+        column = gtk.TreeViewColumn("Powered", toggle, active=2)
+        tv_adapters.append_column(column)
+
+        toggle = gtk.CellRendererToggle()
+        column = gtk.TreeViewColumn("Polling", toggle, active=3)
+        toggle.connect('toggled', self.adapter_pollingToggled, None)
+        tv_adapters.append_column(column)
+
+        column = gtk.TreeViewColumn("Protocols",gtk.CellRendererText(), text=4)
+        tv_adapters.append_column(column)
+
+        column = gtk.TreeViewColumn("Tags", gtk.CellRendererText(), text=5)
+        tv_adapters.append_column(column)
+
+        tv_adapters.get_selection().connect("changed",
+                                            self.on_adapter_sel_changed)
+
+        return tv_adapters;
+
+    # Prepare TreeView  for Tags
+    def createTagsWidgets(self, tags_list):
+
+
+        tv_tags = gtk.TreeView(tags_list)
+
+        column = gtk.TreeViewColumn("Path", gtk.CellRendererText(), text=0)
+        tv_tags.append_column(column)
+        toggle = gtk.CellRendererToggle()
+        column = gtk.TreeViewColumn("ReadOnly", toggle, active=2)
+        tv_tags.append_column(column)
+
+        column = gtk.TreeViewColumn("Type", gtk.CellRendererText(), text=3)
+        tv_tags.append_column(column)
+
+        column = gtk.TreeViewColumn("Record", gtk.CellRendererText(), text=4)
+        tv_tags.append_column(column)
+
+        return tv_tags;#
+
+    # Prepare TreeView  for Records
+    def createRecordsWidgets(self, records_list):
+        #treeview Records
+        tv_records = gtk.TreeView(records_list)
+        tv_records.connect("row-activated", self.on_record_activated)
+
+        column = gtk.TreeViewColumn("Path", gtk.CellRendererText(), text=0)
+        tv_records.append_column(column)
+
+        column = gtk.TreeViewColumn("Type", gtk.CellRendererText(), text=2)
+        tv_records.append_column(column)
+
+        column = gtk.TreeViewColumn("Data", gtk.CellRendererText(), text=3)
+        tv_records.append_column(column)
+        return tv_records;
+
+    def on_record_activated(self, widget, row, col):
+        model = widget.get_model()
+        recordUI = RecordUI(self.neardDialog, model[row][0], model[row][2])
+        recordUI.show()
+
+    def dlg_onResponse(self, dialog, response):
+        self.neardDialog.destroy()
+        self.neardDialog = None
+        if self.response_callback is not None:
+            self.response_callback(response)
+
+    #------------------------------
+    #Prepare the dialog
+    def createDialog(self, title = None):
+        if self.title is not None:
+            title = self.title
+        dialog = gtk.Dialog(title, None,
+                            gtk.DIALOG_MODAL |
+                            gtk.DIALOG_DESTROY_WITH_PARENT,
+                            (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
+        dialog.set_property("resizable", True)
+        dialog.set_default_size(800, 300)
+
+        notebook = gtk.Notebook()
+        dialog.child.add(notebook)
+
+        # Create the first tab...an adapters's list
+        scrolledwindow = gtk.ScrolledWindow()
+        widget = self.createAdaptersWidgets(self.adapters_list)
+        scrolledwindow.add(widget)
+#        notebook.append_page(widget, gtk.Label("Adapters"))
+        notebook.append_page(scrolledwindow, gtk.Label("Adapters"))
+
+        scrolledwindow = gtk.ScrolledWindow()
+        widget = self.createTagsWidgets(self.tags_list)
+        scrolledwindow.add(widget)
+
+        notebook.append_page(scrolledwindow, gtk.Label("Tags"))
+
+        scrolledwindow = gtk.ScrolledWindow()
+        widget = self.createRecordsWidgets(self.records_list)
+        scrolledwindow.add(widget)
+        notebook.append_page(scrolledwindow, gtk.Label("Records"))
+
+        dialog.connect('response', self.dlg_onResponse)
+#        dialog.vbox.pack_end(vbox, True, True, 0)
+        return dialog
+
+    def show(self):
+        if self.neardDialog is None:
+            self.neardDialog = self.createDialog()
+        self.neardDialog.show_all()
+
+    def __init__(self, title = None, response_callback = None):
+        self.title = title
+        self.response_callback = response_callback
+        self.neardDialog = None
+
+        self.adapters_list = gtk.ListStore(gobject.TYPE_STRING,  # path =0
+                                      gobject.TYPE_STRING,  # Name = 1
+                                      gobject.TYPE_BOOLEAN, # powered = 2
+                                      gobject.TYPE_BOOLEAN, # polling = 3
+                                      gobject.TYPE_STRING,  # protocols = 4
+                                      gobject.TYPE_STRING)  # tags = 5
+
+        self.tags_list = gtk.ListStore(gobject.TYPE_STRING,  # path =0
+                                      gobject.TYPE_STRING,      # Name = 1
+                                      gobject.TYPE_BOOLEAN,     # Read Only 2
+                                      gobject.TYPE_STRING,      # Type = 3
+                                      gobject.TYPE_STRING)     # Record = 4
+
+        self.records_list = gtk.ListStore(gobject.TYPE_STRING,  # path =0
+                                      gobject.TYPE_STRING,      # Name = 1
+                                      gobject.TYPE_STRING,      # Type = 2
+                                      gobject.TYPE_STRING)        # content = 3
+
+        Neard.__init__(self, self.adapter_UpdateUI, self.adapter_RemoveUI
+                       , self.tag_UpdateUI, self.record_UpdateUI)
+
+class RecordUI():
+    def wr_onResponse(self, dialog, response):
+        if response != gtk.RESPONSE_ACCEPT:
+            return
+        content = self.content_entry.get_text()
+        type_name = self.type_combo.get_active_text()
+        bus = dbus.SystemBus()
+        record_path = self.path
+        tag_path = record_path[:record_path.rfind("/")]
+        tag = dbus.Interface(bus.get_object("org.neard", tag_path), "org.neard.Tag")
+        if type_name in ["Text"]:
+            content1 = content.split()
+            tag.Write({"Type" : type_name,
+                      "Encoding" : content1[0],
+                      "Language" : content1[1],
+                      "Representation" : ' '.join(content1[2:])})
+        else:
+            tag.Write({"Type" : type_name,
+                       "URI" : content})
+        dialog.destroy()
+
+    def __init__(self, parent = None, path = None, type_name = None):
+        self.path = path
+        type_combo = gtk.combo_box_new_text()
+        type_combo.append_text('Text')
+        type_combo.append_text('URI')
+        type_combo.append_text('SmartPoster')
+        if type_name == 'Text':
+            type_combo.set_active(0)
+        elif type_name == 'URI':
+            type_combo.set_active(1)
+        elif type_name == 'SmartPoster':
+            type_combo.set_active(2)
+        fixed = gtk.Fixed()
+        content_entry = gtk.Entry()
+        fixed.put(type_combo, 20, 40)
+        fixed.put(content_entry, 150, 40)
+        type_label = gtk.Label("Type")
+        content_label = gtk.Label("Content")
+        fixed.put(type_label, 20, 15)
+        fixed.put(content_label, 150, 15)
+
+        record_dialog = gtk.Dialog("Write Record", parent,
+                        gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+                        (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
+        self.record_dialog = record_dialog
+        record_dialog.set_default_size(280, 120)
+        record_dialog.set_position(gtk.WIN_POS_CENTER)
+        record_dialog.connect('response', self.wr_onResponse)
+        hbox = record_dialog.get_content_area()
+        hbox.pack_start(fixed)
+        self.type_combo = type_combo
+        self.content_entry = content_entry
+        fixed.show_all()
+
+    def show(self):
+        self.record_dialog.run()
+        self.record_dialog.destroy()
+
+##=================================================================
+if __name__ == "__main__":
+
+    def endmainloop(response):
+        mainloop.quit()
+
+    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+    mainloop = gobject.MainLoop()
+
+    m = NeardUI("Neard", endmainloop)
+    m.show()
+
+    mainloop.run()
diff --git a/test/push-device b/test/push-device
new file mode 100755 (executable)
index 0000000..614f4fa
--- /dev/null
@@ -0,0 +1,48 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+def help_text():
+       print "Usage: %s <device-path> <record-type> <...>" % (sys.argv[0])
+       print "         If type is Text, parameters are <encoding> <language> <representation>"
+       print "         If type is URI, parameters are <uri>"
+       print "         If type is SmartPoster, parameters are <uri>"
+       print "         If type is Handover, parameters are <carrier>"
+       print "e.g. < %s /org/neard/nfc0/device0 Text UTF-8 en-US hello,Type2! >" % (sys.argv[0])
+       print "e.g. < %s /org/neard/nfc0/device0 URI http://www.nfc-forum.com >" % (sys.argv[0])
+       print "e.g. < %s /org/neard/nfc0/device0 SmartPoster http://www.nfc-forum.com >" % (sys.argv[0])
+       print "e.g. < %s /org/neard/nfc0/device0 Handover bluetooth >" % (sys.argv[0])
+       sys.exit(1)
+
+if len(sys.argv) < 2:
+       help_text()
+
+bus = dbus.SystemBus()
+
+device = dbus.Interface(bus.get_object("org.neard", sys.argv[1]),
+                                               "org.neard.Device")
+
+if len(sys.argv) == 6:
+       if sys.argv[2] in ["Text"]:
+               device.Push(({ "Type" : "Text",
+                               "Encoding" : sys.argv[3],
+                               "Language" : sys.argv[4],
+                               "Representation" : sys.argv[5] }))
+       else:
+               help_text()
+
+elif len(sys.argv) == 4:
+       if sys.argv[2] in ["URI"]:
+               device.Push(({ "Type" : "URI",
+                               "URI" : sys.argv[3] }))
+       elif sys.argv[2] in ["SmartPoster"]:
+               device.Push(({ "Type" : "SmartPoster",
+                               "URI" : sys.argv[3] }))
+       elif sys.argv[2] in ["Handover"]:
+               device.Push(({ "Type" : "Handover",
+                               "Carrier" : sys.argv[3] }))
+       else:
+               help_text()
+else:
+       help_text()
diff --git a/test/simple-agent b/test/simple-agent
new file mode 100755 (executable)
index 0000000..1c7fb4d
--- /dev/null
@@ -0,0 +1,68 @@
+#!/usr/bin/python
+
+import gobject
+
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+import sys
+
+class NDEFAgent(dbus.service.Object):
+
+       @dbus.service.method("org.neard.NDEFAgent",
+                                       in_signature='', out_signature='')
+       def Release(self):
+               print "Release"
+               mainloop.quit()
+
+       @dbus.service.method("org.neard.NDEFAgent",
+                                       in_signature='a{sv}',
+                                       out_signature='')
+       def GetNDEF(self, fields):
+               print "GetNDEF %s"
+
+               if fields.has_key("Records"):
+                       for path in fields["Records"]:
+                               print "Record path %s" % (path)
+
+               if fields.has_key("NDEF"):
+                       val = "["
+                       for i in fields["NDEF"]:
+                               val += " 0x%x" % i
+                       val += " ]"
+                       print "NDEF %s" % val
+
+               return
+
+       @dbus.service.method("org.neard.NDEFAgent",
+                                       in_signature='', out_signature='')
+       def Cancel(self):
+               print "Cancel"
+
+def print_usage():
+       print "Usage:"
+       print "For NDEF agent:"
+       print "%s NDEF Type=<record type>" % (sys.argv[0])
+       print "Help: %s help" % (sys.argv[0])
+       sys.exit(1)
+
+if __name__ == '__main__':
+       if len(sys.argv) == 2 and sys.argv[1] == "help":
+               print_usage()
+
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+       manager = dbus.Interface(bus.get_object('org.neard', "/"),
+                                       'org.neard.Manager')
+
+       if len(sys.argv) > 2:   
+               if sys.argv[1] == "NDEF":
+                       path = "/test/ndef/agent"
+                       object = NDEFAgent(bus, path)
+                       rec_type = sys.argv[2].replace("Type=", "", 1)                  
+
+                       manager.RegisterNDEFAgent(path, rec_type)
+
+                       mainloop = gobject.MainLoop()
+                       mainloop.run()
diff --git a/test/start-poll b/test/start-poll
new file mode 100755 (executable)
index 0000000..858597b
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if len(sys.argv) < 2:
+       print "Usage: %s [nfc device] <polling mode>" % (sys.argv[0])
+       sys.exit(1)
+
+if len(sys.argv) < 3:
+       mode = "Initiator"
+else:
+       mode = sys.argv[2]
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.neard", "/"),
+                                       "org.neard.Manager")
+
+
+print "Polling Mode %s" % (mode)
+
+path = "/org/neard/" + sys.argv[1]
+adapter = dbus.Interface(bus.get_object("org.neard", path),
+                                       "org.neard.Adapter")
+
+try:
+       adapter.StartPollLoop(mode)
+except dbus.DBusException, error:
+       print "%s: %s" % (error._dbus_error_name, error.message)
diff --git a/test/stop-poll b/test/stop-poll
new file mode 100755 (executable)
index 0000000..5ef2ef9
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if len(sys.argv) < 2:
+       print "Usage: %s <nfc device>" % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.neard", "/"),
+                                       "org.neard.Manager")
+
+
+path = "/org/neard/" + sys.argv[1]
+adapter = dbus.Interface(bus.get_object("org.neard", path),
+                                       "org.neard.Adapter")
+
+try:
+       adapter.StopPollLoop()
+except dbus.DBusException, error:
+       print "%s: %s" % (error._dbus_error_name, error.message)
diff --git a/test/write-tag b/test/write-tag
new file mode 100755 (executable)
index 0000000..482239c
--- /dev/null
@@ -0,0 +1,53 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+def help_text():
+       print "Usage: %s <tag-path> <record-type> <...>" % (sys.argv[0])
+       print "         If type is Text, parameters are <encoding> <language> <representation>"
+       print "         If type is URI, parameters are <uri>"
+       print "         If type is SmartPoster, parameters are <uri>"
+       print "e.g. < %s /org/neard/nfc0/tag0 Text UTF-8 en-US hello,Type2! >" % (sys.argv[0])
+       print "e.g. < %s /org/neard/nfc0/tag0 URI http://www.nfc-forum.com >" % (sys.argv[0])
+       print "e.g. < %s /org/neard/nfc0/tag0 SmartPoster http://www.nfc-forum.com >" % (sys.argv[0])
+       print "e.g. < %s /org/neard/nfc0/tag0 Raw <NDEF_file> >" % (sys.argv[0])
+       sys.exit(1)
+
+if len(sys.argv) < 2:
+       help_text()
+
+bus = dbus.SystemBus()
+
+tag = dbus.Interface(bus.get_object("org.neard", sys.argv[1]),
+                                               "org.neard.Tag")
+
+if len(sys.argv) == 6:
+       if sys.argv[2] in ["Text"]:
+               tag.Write(({ "Type" : "Text",
+                               "Encoding" : sys.argv[3],
+                               "Language" : sys.argv[4],
+                               "Representation" : sys.argv[5] }))
+       else:
+               help_text()
+
+elif len(sys.argv) == 4:
+       if sys.argv[2] in ["URI"]:
+               tag.Write(({ "Type" : "URI",
+                               "URI" : sys.argv[3] }))
+       elif sys.argv[2] in ["SmartPoster"]:
+               tag.Write(({ "Type" : "SmartPoster",
+                               "URI" : sys.argv[3] }))
+       elif sys.argv[2] in ["Raw"]:
+               ndef = file(sys.argv[3]).read().rsplit(' ')
+               ndef_stream = bytearray()
+
+               for b in ndef:
+                       ndef_stream.append(int(b, 16))
+
+               tag.Write(({ "Type" : "Raw",
+                               "NDEF" : dbus.ByteArray(ndef_stream) }))
+       else:
+               help_text()
+else:
+       help_text()
diff --git a/tools/snep-send.c b/tools/snep-send.c
new file mode 100644 (file)
index 0000000..a78d9f9
--- /dev/null
@@ -0,0 +1,120 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <linux/socket.h>
+
+#include <near/nfc.h>
+#include <near/types.h>
+#include <near/ndef.h>
+
+#include "../src/near.h"
+
+/* HACK HACK */
+#ifndef AF_NFC
+#define AF_NFC 39
+#endif
+
+#define SNEP_VERSION     0x10
+
+/* Request codes */
+#define SNEP_REQ_CONTINUE 0x00
+#define SNEP_REQ_GET      0x01
+#define SNEP_REQ_PUT      0x02
+#define SNEP_REQ_REJECT   0x7f
+
+/* Response codes */
+#define SNEP_RESP_CONTINUE  0x80
+#define SNEP_RESP_SUCCESS   0x81
+#define SNEP_RESP_NOT_FOUND 0xc0
+#define SNEP_RESP_EXCESS    0xc1
+#define SNEP_RESP_BAD_REQ   0xc2
+#define SNEP_RESP_NOT_IMPL  0xe0
+#define SNEP_RESP_VERSION   0xe1
+#define SNEP_RESP_REJECT    0xff
+
+struct p2p_snep_req_frame {
+       uint8_t version;
+       uint8_t request;
+       uint32_t length;
+       uint8_t ndef[];
+} __attribute__((packed));
+
+int main(int argc, char *argv[])
+{
+       int fd, len;
+       struct near_ndef_message *ndef;
+       int adapter_idx, target_idx;
+       struct sockaddr_nfc_llcp addr;
+       struct p2p_snep_req_frame *frame;
+       size_t frame_length;
+
+       if (argc < 3) {
+               printf("Usage: %s <adapter index> <target index>\n", argv[0]);
+               exit(0);
+       }
+
+       adapter_idx = atoi(argv[1]);
+       target_idx = atoi(argv[2]);
+
+       fd =  socket(AF_NFC, SOCK_STREAM, NFC_SOCKPROTO_LLCP);
+       if (fd < 0)
+               return -1;
+
+       memset(&addr, 0, sizeof(struct sockaddr_nfc_llcp));
+       addr.sa_family = AF_NFC;
+       addr.dev_idx = adapter_idx;
+       addr.target_idx = target_idx;
+       addr.nfc_protocol = NFC_PROTO_NFC_DEP;
+       addr.service_name_len = strlen("urn:nfc:sn:snep");
+       strcpy(addr.service_name, "urn:nfc:sn:snep");
+
+       if (connect(fd, (struct sockaddr *)&addr,
+                   sizeof(struct sockaddr_nfc_llcp)) < 0) {
+               near_error("Connect error %s\n", strerror(errno));
+               return -1;
+       }
+       
+       ndef = near_ndef_prepare_text_record("UTF-8", "en", "Hello world");
+       if (ndef == NULL) {
+               close(fd);
+               near_error("Could not build NDEF");
+               return -1;
+       }
+
+       frame_length = sizeof(struct p2p_snep_req_frame) + ndef->length;
+       frame = g_try_malloc0(frame_length);
+       if (frame == NULL) {
+               close(fd);
+               near_error("Could not allocate SNEP frame");
+               return -1;
+       }
+
+       frame->version = SNEP_VERSION;
+       frame->request = SNEP_REQ_PUT;
+       frame->length = GUINT_TO_BE(ndef->length);
+
+       memcpy(frame->ndef, ndef->data, ndef->length);
+
+       len = send(fd, (uint8_t *)frame, frame_length, 0);
+       if (len < 0) {
+               near_error("Could not send text NDEF %s\n", strerror(errno));
+
+               g_free(frame);
+               close(fd);
+
+               return -1;
+       }
+
+       DBG("Sent %d bytes", len);
+
+       g_free(frame);
+       close(fd);
+
+       return 0;
+}