summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS11
-rw-r--r--COPYING340
-rw-r--r--ChangeLog73
-rw-r--r--HACKING87
-rw-r--r--INSTALL236
-rw-r--r--Makefile.am128
-rw-r--r--Makefile.plugins30
-rw-r--r--NEWS0
-rw-r--r--README52
-rw-r--r--TODO82
-rw-r--r--acinclude.m427
-rwxr-xr-xbootstrap7
-rwxr-xr-xbootstrap-configure12
-rw-r--r--configure.ac126
-rw-r--r--doc/adapter-api.txt126
-rw-r--r--doc/agent-api.txt105
-rw-r--r--doc/coding-style.txt344
-rw-r--r--doc/device-api.txt46
-rw-r--r--doc/features.txt45
-rw-r--r--doc/manager-api.txt67
-rw-r--r--doc/tag-api.txt156
-rw-r--r--gdbus/gdbus.h227
-rw-r--r--gdbus/mainloop.c379
-rw-r--r--gdbus/object.c858
-rw-r--r--gdbus/polkit.c202
-rw-r--r--gdbus/watch.c747
-rw-r--r--include/adapter.h38
-rw-r--r--include/dbus.h138
-rw-r--r--include/device.h63
-rw-r--r--include/log.h47
-rw-r--r--include/ndef.h65
-rw-r--r--include/nfc.h186
-rw-r--r--include/plugin.h95
-rw-r--r--include/setting.h35
-rw-r--r--include/tag.h113
-rw-r--r--include/tlv.h37
-rw-r--r--include/types.h35
-rw-r--r--include/version.h.in35
-rw-r--r--neard.pc.in13
-rw-r--r--packaging/init3
-rw-r--r--packaging/neard.changes24
-rw-r--r--packaging/neard.service11
-rw-r--r--packaging/neard.spec99
-rw-r--r--plugins/handover.c509
-rw-r--r--plugins/mifare.c1343
-rw-r--r--plugins/nfctype1.c825
-rw-r--r--plugins/nfctype2.c635
-rw-r--r--plugins/nfctype3.c998
-rw-r--r--plugins/nfctype4.c1481
-rw-r--r--plugins/npp.c159
-rw-r--r--plugins/p2p.c396
-rw-r--r--plugins/p2p.h50
-rw-r--r--plugins/snep.c691
-rw-r--r--src/adapter.c1193
-rw-r--r--src/agent.c322
-rw-r--r--src/bluetooth.c1062
-rw-r--r--src/dbus.c298
-rw-r--r--src/device.c503
-rw-r--r--src/error.c191
-rwxr-xr-xsrc/genbuiltin17
-rw-r--r--src/log.c138
-rw-r--r--src/main.c245
-rw-r--r--src/main.conf7
-rw-r--r--src/manager.c294
-rw-r--r--src/ndef.c3049
-rw-r--r--src/near.h199
-rw-r--r--src/netlink.c910
-rw-r--r--src/org.neard.conf15
-rw-r--r--src/plugin.c211
-rw-r--r--src/tag.c1097
-rw-r--r--src/tlv.c115
-rwxr-xr-xtest/bt-handover69
-rwxr-xr-xtest/disable-adapter23
-rwxr-xr-xtest/dump-device30
-rwxr-xr-xtest/dump-record24
-rwxr-xr-xtest/dump-tag48
-rwxr-xr-xtest/enable-adapter23
-rwxr-xr-xtest/list-adapters40
-rwxr-xr-xtest/monitor-near62
-rwxr-xr-xtest/neard-ui.py586
-rwxr-xr-xtest/push-device48
-rwxr-xr-xtest/simple-agent68
-rwxr-xr-xtest/start-poll30
-rwxr-xr-xtest/stop-poll23
-rwxr-xr-xtest/write-tag53
-rw-r--r--tools/snep-send.c120
86 files changed, 23750 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
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
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
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
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
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
index 0000000..07e1437
--- /dev/null
+++ b/Makefile.am
@@ -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
index 0000000..e561f8f
--- /dev/null
+++ b/Makefile.plugins
@@ -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
index 0000000..e69de29
--- /dev/null
+++ b/NEWS
diff --git a/README b/README
new file mode 100644
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
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
index 0000000..329c6a9
--- /dev/null
+++ b/acinclude.m4
@@ -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
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
index 0000000..4e705bc
--- /dev/null
+++ b/bootstrap-configure
@@ -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
index 0000000..59d9a0a
--- /dev/null
+++ b/configure.ac
@@ -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
index 0000000..8b6906c
--- /dev/null
+++ b/doc/adapter-api.txt
@@ -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
index 0000000..e1a5330
--- /dev/null
+++ b/doc/agent-api.txt
@@ -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
index 0000000..47f9c4a
--- /dev/null
+++ b/doc/coding-style.txt
@@ -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
index 0000000..a098bbb
--- /dev/null
+++ b/doc/device-api.txt
@@ -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
index 0000000..5745dc0
--- /dev/null
+++ b/doc/features.txt
@@ -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
index 0000000..47547ff
--- /dev/null
+++ b/doc/manager-api.txt
@@ -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
index 0000000..6fb2a0a
--- /dev/null
+++ b/doc/tag-api.txt
@@ -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
index 0000000..0a8a27c
--- /dev/null
+++ b/gdbus/gdbus.h
@@ -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
index 0000000..cff326f
--- /dev/null
+++ b/gdbus/mainloop.c
@@ -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
index 0000000..9689006
--- /dev/null
+++ b/gdbus/object.c
@@ -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
index 0000000..9e95fa3
--- /dev/null
+++ b/gdbus/polkit.c
@@ -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
index 0000000..d749176
--- /dev/null
+++ b/gdbus/watch.c
@@ -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
index 0000000..e547d3d
--- /dev/null
+++ b/include/adapter.h
@@ -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
index 0000000..e5c74af
--- /dev/null
+++ b/include/dbus.h
@@ -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
index 0000000..15df8a9
--- /dev/null
+++ b/include/device.h
@@ -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
index 0000000..6183b88
--- /dev/null
+++ b/include/log.h
@@ -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
index 0000000..fdc5006
--- /dev/null
+++ b/include/ndef.h
@@ -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
index 0000000..6189f27
--- /dev/null
+++ b/include/nfc.h
@@ -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
index 0000000..609775d
--- /dev/null
+++ b/include/plugin.h
@@ -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
index 0000000..8f7a26a
--- /dev/null
+++ b/include/setting.h
@@ -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
index 0000000..7a2ca6e
--- /dev/null
+++ b/include/tag.h
@@ -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
index 0000000..2142512
--- /dev/null
+++ b/include/tlv.h
@@ -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
index 0000000..bd5b53d
--- /dev/null
+++ b/include/types.h
@@ -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
index 0000000..2119dcc
--- /dev/null
+++ b/include/version.h.in
@@ -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
index 0000000..3a46466
--- /dev/null
+++ b/neard.pc.in
@@ -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
index 0000000..759a352
--- /dev/null
+++ b/packaging/init
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+/usr/libexec/neard
diff --git a/packaging/neard.changes b/packaging/neard.changes
new file mode 100644
index 0000000..e3af7b5
--- /dev/null
+++ b/packaging/neard.changes
@@ -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
index 0000000..5ea1e81
--- /dev/null
+++ b/packaging/neard.service
@@ -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
index 0000000..fcdd501
--- /dev/null
+++ b/packaging/neard.spec
@@ -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
index 0000000..0e148bd
--- /dev/null
+++ b/plugins/handover.c
@@ -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
index 0000000..368d470
--- /dev/null
+++ b/plugins/mifare.c
@@ -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
index 0000000..5cfc3a0
--- /dev/null
+++ b/plugins/nfctype1.c
@@ -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
index 0000000..3dc7856
--- /dev/null
+++ b/plugins/nfctype2.c
@@ -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
index 0000000..f45e1a7
--- /dev/null
+++ b/plugins/nfctype3.c
@@ -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
index 0000000..694d613
--- /dev/null
+++ b/plugins/nfctype4.c
@@ -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
index 0000000..90ebc1c
--- /dev/null
+++ b/plugins/npp.c
@@ -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
index 0000000..be5c6e1
--- /dev/null
+++ b/plugins/p2p.c
@@ -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
index 0000000..033ecd2
--- /dev/null
+++ b/plugins/p2p.h
@@ -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
index 0000000..262cf4d
--- /dev/null
+++ b/plugins/snep.c
@@ -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
index 0000000..25cf956
--- /dev/null
+++ b/src/adapter.c
@@ -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
index 0000000..5fe34cf
--- /dev/null
+++ b/src/agent.c
@@ -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
index 0000000..63e76da
--- /dev/null
+++ b/src/bluetooth.c
@@ -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
index 0000000..ad65544
--- /dev/null
+++ b/src/dbus.c
@@ -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
index 0000000..de9cbe8
--- /dev/null
+++ b/src/device.c
@@ -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
index 0000000..4012b5b
--- /dev/null
+++ b/src/error.c
@@ -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
index 0000000..9c2ba15
--- /dev/null
+++ b/src/genbuiltin
@@ -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
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
index 0000000..dea9995
--- /dev/null
+++ b/src/main.c
@@ -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
index 0000000..65b6ac3
--- /dev/null
+++ b/src/main.conf
@@ -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
index 0000000..2002411
--- /dev/null
+++ b/src/manager.c
@@ -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
index 0000000..f123d62
--- /dev/null
+++ b/src/ndef.c
@@ -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
index 0000000..e47d1fb
--- /dev/null
+++ b/src/near.h
@@ -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
index 0000000..fa83723
--- /dev/null
+++ b/src/netlink.c
@@ -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
index 0000000..12c0ab9
--- /dev/null
+++ b/src/org.neard.conf
@@ -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
index 0000000..6c5b676
--- /dev/null
+++ b/src/plugin.c
@@ -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
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
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
index 0000000..95b6dbf
--- /dev/null
+++ b/test/bt-handover
@@ -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
index 0000000..fe514ff
--- /dev/null
+++ b/test/disable-adapter
@@ -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
index 0000000..58b090c
--- /dev/null
+++ b/test/dump-device
@@ -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
index 0000000..8cf3cb1
--- /dev/null
+++ b/test/dump-record
@@ -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
index 0000000..4e987fc
--- /dev/null
+++ b/test/dump-tag
@@ -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
index 0000000..bd6f03a
--- /dev/null
+++ b/test/enable-adapter
@@ -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
index 0000000..9e11322
--- /dev/null
+++ b/test/list-adapters
@@ -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
index 0000000..fef307e
--- /dev/null
+++ b/test/monitor-near
@@ -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
index 0000000..1113ca8
--- /dev/null
+++ b/test/neard-ui.py
@@ -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
index 0000000..614f4fa
--- /dev/null
+++ b/test/push-device
@@ -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
index 0000000..1c7fb4d
--- /dev/null
+++ b/test/simple-agent
@@ -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
index 0000000..858597b
--- /dev/null
+++ b/test/start-poll
@@ -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
index 0000000..5ef2ef9
--- /dev/null
+++ b/test/stop-poll
@@ -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
index 0000000..482239c
--- /dev/null
+++ b/test/write-tag
@@ -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
index 0000000..a78d9f9
--- /dev/null
+++ b/tools/snep-send.c
@@ -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;
+}