From 33b7ab7b4554c02c628cea423fa6fd3a49b5655c Mon Sep 17 00:00:00 2001 From: HyungKyu Song Date: Sat, 16 Feb 2013 00:54:17 +0900 Subject: [PATCH] Tizen 2.0 Release --- AUTHORS | 11 + COPYING | 340 ++++++ ChangeLog | 73 ++ HACKING | 87 ++ INSTALL | 236 ++++ Makefile.am | 128 ++ Makefile.plugins | 30 + NEWS | 0 README | 52 + TODO | 82 ++ acinclude.m4 | 27 + bootstrap | 7 + bootstrap-configure | 12 + configure.ac | 126 ++ doc/adapter-api.txt | 126 ++ doc/agent-api.txt | 105 ++ doc/coding-style.txt | 344 ++++++ doc/device-api.txt | 46 + doc/features.txt | 45 + doc/manager-api.txt | 67 ++ doc/tag-api.txt | 156 +++ gdbus/gdbus.h | 227 ++++ gdbus/mainloop.c | 379 ++++++ gdbus/object.c | 858 +++++++++++++ gdbus/polkit.c | 202 ++++ gdbus/watch.c | 747 ++++++++++++ include/adapter.h | 38 + include/dbus.h | 138 +++ include/device.h | 63 + include/log.h | 47 + include/ndef.h | 65 + include/nfc.h | 186 +++ include/plugin.h | 95 ++ include/setting.h | 35 + include/tag.h | 113 ++ include/tlv.h | 37 + include/types.h | 35 + include/version.h.in | 35 + neard.pc.in | 13 + packaging/init | 3 + packaging/neard.changes | 24 + packaging/neard.service | 11 + packaging/neard.spec | 99 ++ plugins/handover.c | 509 ++++++++ plugins/mifare.c | 1343 +++++++++++++++++++++ plugins/nfctype1.c | 825 +++++++++++++ plugins/nfctype2.c | 635 ++++++++++ plugins/nfctype3.c | 998 ++++++++++++++++ plugins/nfctype4.c | 1481 +++++++++++++++++++++++ plugins/npp.c | 159 +++ plugins/p2p.c | 396 ++++++ plugins/p2p.h | 50 + plugins/snep.c | 691 +++++++++++ src/adapter.c | 1193 ++++++++++++++++++ src/agent.c | 322 +++++ src/bluetooth.c | 1062 +++++++++++++++++ src/dbus.c | 298 +++++ src/device.c | 503 ++++++++ src/error.c | 191 +++ src/genbuiltin | 17 + src/log.c | 138 +++ src/main.c | 245 ++++ src/main.conf | 7 + src/manager.c | 294 +++++ src/ndef.c | 3049 +++++++++++++++++++++++++++++++++++++++++++++++ src/near.h | 199 ++++ src/netlink.c | 910 ++++++++++++++ src/org.neard.conf | 15 + src/plugin.c | 211 ++++ src/tag.c | 1097 +++++++++++++++++ src/tlv.c | 115 ++ test/bt-handover | 69 ++ test/disable-adapter | 23 + test/dump-device | 30 + test/dump-record | 24 + test/dump-tag | 48 + test/enable-adapter | 23 + test/list-adapters | 40 + test/monitor-near | 62 + test/neard-ui.py | 586 +++++++++ test/push-device | 48 + test/simple-agent | 68 ++ test/start-poll | 30 + test/stop-poll | 23 + test/write-tag | 53 + tools/snep-send.c | 120 ++ 86 files changed, 23750 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 HACKING create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 Makefile.plugins create mode 100644 NEWS create mode 100644 README create mode 100644 TODO create mode 100644 acinclude.m4 create mode 100755 bootstrap create mode 100755 bootstrap-configure create mode 100644 configure.ac create mode 100644 doc/adapter-api.txt create mode 100644 doc/agent-api.txt create mode 100644 doc/coding-style.txt create mode 100644 doc/device-api.txt create mode 100644 doc/features.txt create mode 100644 doc/manager-api.txt create mode 100644 doc/tag-api.txt create mode 100644 gdbus/gdbus.h create mode 100644 gdbus/mainloop.c create mode 100644 gdbus/object.c create mode 100644 gdbus/polkit.c create mode 100644 gdbus/watch.c create mode 100644 include/adapter.h create mode 100644 include/dbus.h create mode 100644 include/device.h create mode 100644 include/log.h create mode 100644 include/ndef.h create mode 100644 include/nfc.h create mode 100644 include/plugin.h create mode 100644 include/setting.h create mode 100644 include/tag.h create mode 100644 include/tlv.h create mode 100644 include/types.h create mode 100644 include/version.h.in create mode 100644 neard.pc.in create mode 100644 packaging/init create mode 100644 packaging/neard.changes create mode 100644 packaging/neard.service create mode 100644 packaging/neard.spec create mode 100644 plugins/handover.c create mode 100644 plugins/mifare.c create mode 100644 plugins/nfctype1.c create mode 100644 plugins/nfctype2.c create mode 100644 plugins/nfctype3.c create mode 100644 plugins/nfctype4.c create mode 100644 plugins/npp.c create mode 100644 plugins/p2p.c create mode 100644 plugins/p2p.h create mode 100644 plugins/snep.c create mode 100644 src/adapter.c create mode 100644 src/agent.c create mode 100644 src/bluetooth.c create mode 100644 src/dbus.c create mode 100644 src/device.c create mode 100644 src/error.c create mode 100755 src/genbuiltin create mode 100644 src/log.c create mode 100644 src/main.c create mode 100644 src/main.conf create mode 100644 src/manager.c create mode 100644 src/ndef.c create mode 100644 src/near.h create mode 100644 src/netlink.c create mode 100644 src/org.neard.conf create mode 100644 src/plugin.c create mode 100644 src/tag.c create mode 100644 src/tlv.c create mode 100755 test/bt-handover create mode 100755 test/disable-adapter create mode 100755 test/dump-device create mode 100755 test/dump-record create mode 100755 test/dump-tag create mode 100755 test/enable-adapter create mode 100755 test/list-adapters create mode 100755 test/monitor-near create mode 100755 test/neard-ui.py create mode 100755 test/push-device create mode 100755 test/simple-agent create mode 100755 test/start-poll create mode 100755 test/stop-poll create mode 100755 test/write-tag create mode 100644 tools/snep-send.c diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..446d2ed --- /dev/null +++ b/AUTHORS @@ -0,0 +1,11 @@ +Samuel Ortiz +Marcel Holtmann +Olivier Guiter +Ravikumar Veeramally +Vinicius Costa Gomes +Eyal Reizer +Jeff Zheng +Szymon Janc +Wiktor Lawski +Dorota Moskal +Krzysztof Lyczkowski 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. + + + Copyright (C) + + 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. + + , 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 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 + + 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 + + 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 + + +p2p mode +======== + +- Bluetooth Handover integration + + Priority: Medium + Complexity: C4 + Dependencies: Core:Handover Agent API + Owner: Olivier Guiter + + 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 + + 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 + * + * + * 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 +#include + +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 + * + * + * 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 +#endif + +#include +#include + +#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 + * + * + * 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 +#endif + +#include +#include + +#include +#include + +#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\tname, 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\n", + method->name); + else { + g_string_append_printf(gstr, "\t\t\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\n"); + + if (noreply) + g_string_append_printf(gstr, "\t\t\t\n"); + + g_string_append_printf(gstr, "\t\t\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\n", + signal->name); + else { + g_string_append_printf(gstr, "\t\t\n", + signal->name); + print_arguments(gstr, signal->args, NULL); + + if (deprecated) + g_string_append_printf(gstr, "\t\t\t\n"); + + g_string_append_printf(gstr, "\t\t\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, "\n"); + + for (list = data->interfaces; list; list = list->next) { + struct interface_data *iface = list->data; + + g_string_append_printf(gstr, "\t\n", + iface->name); + + generate_interface_xml(gstr, iface); + + g_string_append_printf(gstr, "\t\n"); + } + + if (!dbus_connection_list_registered(conn, path, &children)) + goto done; + + for (i = 0; children[i]; i++) + g_string_append_printf(gstr, "\t\n", + children[i]); + + dbus_free_string_array(children); + +done: + g_string_append_printf(gstr, "\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 ""); + + 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 + * + * + * 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 +#endif + +#include + +#include + +#include + +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 + * + * + * 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 +#endif + +#include +#include + +#include +#include + +#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 +#include +#include + +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 + +#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 + +#include + +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 + +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 + * Aloisio Almeida Jr + * + * 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 +#include + +#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 + +#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 + * + * 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 + +#include + +#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 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 +- Add systemd support + +* Fri Aug 03 2012 Anas Nashif a7864dd +- fix runtime requirements + +* Tue Jul 31 09:22:14 CST 2012 Arron - 0.5 +- Upgrade to version 0.5 + +* Tue Apr 24 2012 Arron - 0.2.26 +- Upgrade to latest version + +* Fri Apr 20 2012 Arron - 0.1.64 +- Add building require for kernel-adaptation-bb-devel to fix the building error + +* Fri Mar 30 2012 Arron - 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 +#endif + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#endif + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 +#endif + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#endif + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +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 +#endif + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#endif + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#endif + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#endif + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#endif + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#endif + +#include +#include +#include +#include +#include + +#include + +#include + +#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 +#endif + +#include +#include +#include +#include + +#include + +#include + +#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 +#endif + +#include +#include +#include +#include +#include + +#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 +#endif + +#include +#include + +#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 +#endif + +#include +#include +#include +#include + +#include + +#include + +#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 +#endif + +#include +#include + +#include + +#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 +#endif + +#include +#include + +#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 +#endif + +#include +#include +#include +#include +#include + +#include + +#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 +#endif + +#include +#include +#include + +#include + +#include + +#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 +#endif + +#include +#include +#include +#include + +#include + +#include + +#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 +#include + +#include + + +#include + +#include +#include + +struct near_adapter; +struct near_device_driver; + +#include + +int __near_log_init(const char *debug, gboolean detach); +void __near_log_cleanup(void); + +#include + +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 + +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 + +#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 + +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 + +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 + +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 + +#include + +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 + * 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 +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#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 @@ + + + + + + + + + + + + + + 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 +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#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 +#endif + +#include +#include +#include +#include + +#include + +#include + +#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 +#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 " % (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 " % (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 <...>" % (sys.argv[0]) + print " If type is Text, parameters are " + print " If type is URI, parameters are " + print " If type is SmartPoster, parameters are " + print " If type is Handover, parameters are " + 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=" % (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] " % (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 " % (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 <...>" % (sys.argv[0]) + print " If type is Text, parameters are " + print " If type is URI, parameters are " + print " If type is SmartPoster, parameters are " + 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 >" % (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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#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 \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; +} -- 2.7.4