diff options
86 files changed, 23750 insertions, 0 deletions
@@ -0,0 +1,11 @@ +Samuel Ortiz <sameo@linux.intel.com> +Marcel Holtmann <marcel@holtmann.org> +Olivier Guiter <olivier.guiter@linux.intel.com> +Ravikumar Veeramally <ravikumar.veeramally@linux.intel.com> +Vinicius Costa Gomes <vcgomes@gmail.com> +Eyal Reizer <eyalr@ti.com> +Jeff Zheng <jeff.zheng@linux.intel.com> +Szymon Janc <szymon.janc@tieto.com> +Wiktor Lawski <wiktor.lawski@tieto.com> +Dorota Moskal <dorota.moskal@tieto.com> +Krzysztof Lyczkowski <krzysztof.lyczkowski@tieto.com> @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..343ec98 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,73 @@ +ver 0.8 + Added raw NDEF push through the NDEF agent API. + Fixed a couple of p2p file descriptor leaks. + Fixed CR handover record handling in handover Select frames. + Fixed handover version check support. + Fixed handover code to support AC less frames. + Fixed p2p client event routine to support multiple incoming requests. + Fixed netlink support for nested events. + +ver 0.7 + Added support for Android Jelly Bean handover. + Added a Bluetooth handover automated script. + Added MIFARE Classic writing support. + Added MIFARE read only check support. + Added MIFARE check presence support. + Fixed a tag writing related double free. + Fixed tag plugins memory leaks. + Fixed tag plugins callback calls code paths. + Fixed URI NDEF writing. + +ver 0.6 + Added tag formatting support for Type 1, 3 an 4. + Added blank tag detection for Type 1, 3 and 4. + Added an NDEF agent API implementation. + Fixed the handover agent API. + Fixed potential NULL pointer dereferences. + Fixed a few plugin memory leaks. + Fixed p2p NDEF push while p2p link is down. + +ver 0.5: + Added Handover Initiator support. + Added Handover selector support. + Added support for Type 2 tags formatting. + Added early binding support. + Fixed handover and p2p memory leaks. + Fixed Type 2 tag read command. + Fixed NDEF push return value. + Removed plugins enablement configure options. + +ver 0.4: + Added target mode support for peer to peer. + Added a handover agent API. + Fixed SNEP read fragmentation. + Fixed SNEP response frames version field. + Fixed build dependencies. + Fixed netlink and adapter memory leaks. + +ver 0.3: + Added a Tag D-Bus API. + Added an SNEP testing tool. + Added SNEP fragmentation support. + Added BlueZ signal handlers for adapter tracking. + Added BlueZ adapter properties handling. + Added a main.conf template. + Removed the Target D-Bus API. + Fixed Type 1 commands handling. + +ver 0.2: + Added SNEP support. + Added tag presence check implementation. + Added initial publishing API implementation. + Added MIFARE reader support. + Added NFC type 3 writer mode support. + Added netlink Powered property handling implementation. + Fixed p2p network conditions errors. + Fixed valgrind memory leak reports. + +ver 0.1: + Added reader mode for NFC types 1,2,3 and 4. + Added writer mode for NFC types 1,2, and 4. + Added peer to peer reader mode (NPP). + Added initial SNEP support. + Added initial Bluetooth OOB support. @@ -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. @@ -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 @@ -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. @@ -0,0 +1,82 @@ +Background +========== + +- Priority scale: High, Medium and Low + +- Complexity scale: C1, C2, C4 and C8. + The complexity scale is exponential, with complexity 1 being the + lowest complexity. Complexity is a function of both task 'complexity' + and task 'scope'. + +Core +==== + +- Handover Agent API + + Priority: Medium + Complexity: C4 + Owner: Szymon Janc <szymon.janc@tieto.com> + + neard's handover agent API will allow an eventual BlueZ neard plugin to + pass OOB data to neard. From this OOB data, neard will build an Hs record + and reply to the handover requester. + +- Card Emulation Mode + + Priority: Low + Complexity: C8 + Dependencies: Core:NDEF building library + Owner: Samuel Ortiz <sameo@linux.intel.com> + + In card emulation mode, the NFC adapter is in passive mode, and gets + activated by an initiator mode device. Then the initiator sends command + that the target is supposed to interpret and respond to. Most of the + commands are tag specific. + For a proper card emulation mode support, the NFC adapter must enter the + polling loop and not only initiate the RF field, but then wait for intiator + to activate it. Once activated, the NFC socket should enter the accept + state, and dispatch the various commands to the right plugins (one per + protocol, a.k.a. tags). + + +Reader mode +=========== + + +Writer mode +=========== + +- MIFARE writer mode support + + Priority: Low + Complexity: C2 + Owner: Dorota Moskal <dorota.moskal@tieto.com> + + +p2p mode +======== + +- Bluetooth Handover integration + + Priority: Medium + Complexity: C4 + Dependencies: Core:Handover Agent API + Owner: Olivier Guiter <olivier.guiter@linux.intel.com> + + The handover integration has to be done between bluetoothd, obexd and + neard. Obexd or BlueZ should be able to call a handover requester + org.neard.Manager method to send an Hr to a remote peer. This asynchronous + method will return upon reception of an Hs record. This is when BlueZ + will be able to initiate the pairing. + On the other hand, neard should be able to get the appropriate OOB data + from BlueZ through the handover agent API, and build an Hs to send to + the handover requester. The latter will then initiate the pairing. + +- SNEP and LLCP validation tests + + Priority: Medium + Complexity: C4 + Owner: Ravi kumar Veeramally <ravikumar.veeramally@linux.intel.com> + + Implement SNEP validation test cases as an optional neard plugin, and + LLCP ones as a set of tools/ binaries. diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 0000000..329c6a9 --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,27 @@ +AC_DEFUN([AC_PROG_CC_PIE], [ + AC_CACHE_CHECK([whether ${CC-cc} accepts -fPIE], ac_cv_prog_cc_pie, [ + echo 'void f(){}' > conftest.c + if test -z "`${CC-cc} -fPIE -pie -c conftest.c 2>&1`"; then + ac_cv_prog_cc_pie=yes + else + ac_cv_prog_cc_pie=no + fi + rm -rf conftest* + ]) +]) + +AC_DEFUN([COMPILER_FLAGS], [ + if (test "${CFLAGS}" = ""); then + CFLAGS="-Wall -O2 -D_FORTIFY_SOURCE=2" + fi + if (test "$USE_MAINTAINER_MODE" = "yes"); then + CFLAGS+=" -Werror -Wextra" + CFLAGS+=" -Wno-unused-parameter" + CFLAGS+=" -Wno-missing-field-initializers" + CFLAGS+=" -Wdeclaration-after-statement" + CFLAGS+=" -Wmissing-declarations" + CFLAGS+=" -Wredundant-decls" + CFLAGS+=" -Wcast-align" + CFLAGS+=" -DG_DISABLE_DEPRECATED" + fi +]) diff --git a/bootstrap b/bootstrap new file mode 100755 index 0000000..91756f9 --- /dev/null +++ b/bootstrap @@ -0,0 +1,7 @@ +#!/bin/sh + +aclocal && \ + autoheader && \ + libtoolize --automake --copy --force && \ + automake --add-missing --copy && \ + autoconf diff --git a/bootstrap-configure b/bootstrap-configure new file mode 100755 index 0000000..4e705bc --- /dev/null +++ b/bootstrap-configure @@ -0,0 +1,12 @@ +#!/bin/sh + +if [ -f config.status ]; then + make maintainer-clean +fi + +./bootstrap && \ + ./configure --enable-maintainer-mode \ + --enable-debug \ + --prefix=/usr \ + --sysconfdir=/etc \ + --enable-tools $* diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..59d9a0a --- /dev/null +++ b/configure.ac @@ -0,0 +1,126 @@ +AC_PREREQ(2.60) +AC_INIT(neard, 0.8) + +AM_INIT_AUTOMAKE([foreign subdir-objects color-tests]) +AM_CONFIG_HEADER(config.h) + +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +AM_MAINTAINER_MODE + +AC_PREFIX_DEFAULT(/usr/local) + +if (test "${libdir}" = '${exec_prefix}/lib'); then + libdir="${prefix}/lib" +fi + +plugindir="${libdir}/near/plugins" + +PKG_PROG_PKG_CONFIG + +COMPILER_FLAGS + +AC_LANG_C + +AC_PROG_CC +AC_PROG_CC_PIE +AC_PROG_INSTALL + +m4_define([_LT_AC_TAGCONFIG], []) +m4_ifdef([AC_LIBTOOL_TAGS], [AC_LIBTOOL_TAGS([])]) + +AC_DISABLE_STATIC +AC_PROG_LIBTOOL + +AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization], + [disable code optimization through compiler]), [ + if (test "${enableval}" = "no"); then + CFLAGS="$CFLAGS -O0" + fi +]) + +AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug], + [enable compiling with debugging information]), [ + if (test "${enableval}" = "yes" && + test "${ac_cv_prog_cc_g}" = "yes"); then + CFLAGS="$CFLAGS -g" + fi +]) + +AC_ARG_ENABLE(pie, AC_HELP_STRING([--enable-pie], + [enable position independent executables flag]), [ + if (test "${enableval}" = "yes" && + test "${ac_cv_prog_cc_pie}" = "yes"); then + CFLAGS="$CFLAGS -fPIE" + LDFLAGS="$LDFLAGS -pie" + fi +]) + +AC_CHECK_LIB(dl, dlopen, dummy=yes, + AC_MSG_ERROR(dynamic linking loader is required)) + +PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.28, dummy=yes, + AC_MSG_ERROR(GLib >= 2.28 is required)) +AC_SUBST(GLIB_CFLAGS) +AC_SUBST(GLIB_LIBS) + +PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.2, dummy=yes, + AC_MSG_ERROR(D-Bus >= 1.2 is required)) +AC_SUBST(DBUS_CFLAGS) +AC_SUBST(DBUS_LIBS) + +PKG_CHECK_MODULES(LIBNL3, libnl-3.0 libnl-genl-3.0, [ + NETLINK_CFLAGS=${LIBNL3_CFLAGS} + NETLINK_LIBS=${LIBNL3_LIBS} +], [ + PKG_CHECK_MODULES(LIBNL2, libnl-2.0, [ + NETLINK_CFLAGS=${LIBNL2_CFLAGS} + NETLINK_LIBS=${LIBNL2_LIBS} + ], [ + PKG_CHECK_MODULES(LIBNL1, libnl-1, dummy=yes, + AC_MSG_ERROR(Netlink library is required)) + AC_DEFINE(NEED_LIBNL_COMPAT, 1, + [Define to 1 if you need libnl-1 compat functions.]) + NETLINK_CFLAGS=${LIBNL1_CFLAGS} + NETLINK_LIBS=${LIBNL1_LIBS} + ]) +]) +AC_SUBST(NETLINK_CFLAGS) +AC_SUBST(NETLINK_LIBS) + +AC_ARG_ENABLE(test, AC_HELP_STRING([--enable-test], + [enable test/example scripts]), + [enable_test=${enableval}]) +AM_CONDITIONAL(TEST, test "${enable_test}" = "yes") + +AC_ARG_ENABLE(tools, AC_HELP_STRING([--enable-tools], + [enable testing tools]), + [enable_tools=${enableval}]) +AM_CONDITIONAL(TOOLS, test "${enable_tools}" = "yes") + +AC_ARG_ENABLE(nfctype1, AC_HELP_STRING([--disable-nfctype1], + [disable NFC forum type 1 tags support]), + [enable_nfctype1=${enableval}]) +AM_CONDITIONAL(NFCTYPE1, test "${enable_nfctype1}" != "no") + +AC_ARG_ENABLE(nfctype2, AC_HELP_STRING([--disable-nfctype2], + [disable NFC forum type 2 tags support]), + [enable_nfctype2=${enableval}]) +AM_CONDITIONAL(NFCTYPE2, test "${enable_nfctype2}" != "no") + +AC_ARG_ENABLE(nfctype3, AC_HELP_STRING([--disable-nfctype3], + [disable NFC forum type 3 tags support]), + [enable_nfctype3=${enableval}]) +AM_CONDITIONAL(NFCTYPE3, test "${enable_nfctype3}" != "no") + +AC_ARG_ENABLE(nfctype4, AC_HELP_STRING([--disable-nfctype4], + [disable NFC forum type 4 tags support]), + [enable_nfctype4=${enableval}]) +AM_CONDITIONAL(NFCTYPE4, test "${enable_nfctype4}" != "no") + +AC_ARG_ENABLE(p2p, AC_HELP_STRING([--disable-p2p], + [disable NFC peer to peer support]), + [enable_p2p=${enableval}]) +AM_CONDITIONAL(P2P, test "${enable_p2p}" != "no") + +AC_OUTPUT(Makefile include/version.h neard.pc) diff --git a/doc/adapter-api.txt b/doc/adapter-api.txt new file mode 100644 index 0000000..8b6906c --- /dev/null +++ b/doc/adapter-api.txt @@ -0,0 +1,126 @@ +Adapter hierarchy +================= + +Service org.neard +Interface org.neard.Adapter +Object path [variable prefix]/{nfc0,nfc1,...} + +Methods: dict GetProperties() + + Returns all properties for the device. See the + properties section for available properties. + + Possible Errors: org.neard.Error.DoesNotExist + + void SetProperty(string name, variant value) + + Changes the value of the specified property. Only + properties that are listed a read-write are changeable. + On success this will emit a PropertyChanged signal. + + Possible Errors: org.neard.Error.DoesNotExist + org.neard.Error.InvalidArguments + + void StartPollLoop(string mode) + + Starts the adapter polling loop. Depending on the mode, + the adapter will start polling for targets, listening + for NFC devices or both. + The mode parameter can have the following values: + "Initiator", "Target" or "Dual". For any other value, the + adapter will fall back to initiator mode. + Dual mode will have the adapter alternate between target + and initiator modes during the polling loop. + + This process will start emitting TagFound and + PropertyChanged "Polling" signals. + + Possible errors: org.neard.Error.NotReady + org.neard.Error.Failed + org.neard.Error.NotSupported + + void StopPollLoop() + + The adapter polling loop will stop. + + Possible errors: org.neard.Error.NotReady + org.neard.Error.Failed + org.neard.Error.NotSupported + + object StartEmulation(dict attributes) + + Starts tag emulation mode. + + Adapters can only emulate one target at a time, so + subsequent calls to this method will always return + the same object path. + + The attributes dictionary is described by the + Record properties. + For example, one would add a type, a Language, an + Encoding and a Representation for emulating a text + NDEF record. + + Returns the object path for the emulated target. + + Possible errors: org.neard.Error.NotReady + org.neard.Error.Failed + org.neard.Error.NotSupported + + void StopEmulation() + + Stops tag emulation mode. + + Possible errors: org.neard.Error.NotReady + org.neard.Error.Failed + org.neard.Error.NotSupported + + +Signals PropertyChanged(string name, variant value) + + This signal indicates a changed value of the given + property. + + TagFound(string address, dict values) + + This signal is sent whenever an NFC tag is found, + as a result of a probe response reception. + + The dictionary contains basically the same values + that are returned by the GetProperties method + from the org.neard.Tag interface. + + TagLost(string address) + + This signal is sent whenever the NFC tag is no longer + in sight, or when it's been de-activated. + + +Properties: string Mode [readonly] + + The adapter NFC radio mode. + + Valid types are "Initiator", "Target" and "Idle" + + boolean Powered [readwrite] + + Switch an adapter on or off. + + boolean Polling [readonly] + + Indicates that the adapter is currently polling for targets. + This is only valid when the adapter is in initiator mode. + + array{string} Protocols [readonly] + + The adapter supported protocols. + Possible values are "Felica", "MIFARE", "Jewel", + "ISO-DEP" and "NFC-DEP". + + array{object} Tags [readonly] + + The tags object paths. + + array{object} Devices [readonly] + + The devices object paths. diff --git a/doc/agent-api.txt b/doc/agent-api.txt new file mode 100644 index 0000000..e1a5330 --- /dev/null +++ b/doc/agent-api.txt @@ -0,0 +1,105 @@ +HandoverAgent hierarchy +======================= + +Service unique name +Interface org.neard.HandoverAgent +Object path freely definable + +Methods dict RequestOOB(dict values) [experimental] + + This method gets called when the service daemon + needs to get Out Of Band data from the handover + agent, typically the BlueZ daemon. + + The service daemon will use this OOB data to build + a Handover Request or Select message and send it to + remote device. + + Values parameter is optional. It should be a dictionary + where the keys are the field names and the values are + the actual fields. If provided it should contain remote + Out Of Band data received in Handover Request message. + Those data will be stored for future use (i.e. when + remote initialize pairing) and providing those will not + initialize pairing. + + The return value should be a dictionary where the + keys are the field names and the values are the + actual fields. + + Possible Errors: org.neard.HandoverAgent.Error.NotSupported + org.neard.HandoverAgent.Error.NoSuchDevice + org.neard.HandoverAgent.Error.InProgress + org.neard.HandoverAgent.Error.Failed + + void PushOOB(dict values) [experimental] + + This method gets called when service daemon received + Handover Select message from selector and needs to pass + remote Out Of Band data to agent to start handover. + + If there is no Bluetooth adapter or if it doesn't + support simple pairing the agent will return an error. + + Agent shall implicitly initialize pairing if needed. + + This function returns when alternative carrier + (Bluetooth) is ready to be used i.e. pairing has + finished. + + Parameter should be a dictionary where the keys are the + field names and the values are the actual fields. + + Possible Errors: org.neard.HandoverAgent.Error.NotSupported + org.neard.HandoverAgent.Error.NoSuchDevice + org.neard.HandoverAgent.Error.InProgress + org.neard.HandoverAgent.Error.Failed + + void Release() [experimental] + + This method gets called when the service daemon + unregisters the agent. An agent can use it to do + cleanup tasks. There is no need to unregister the + agent, because when this method gets called it has + already been unregistered. + +Fields array{byte} EIR + + This is EIR blob. Used by SSP capable devices. + + array{byte} nokia.com:bt + + This is a proprietary extension blob used by some + Nokia Bluetooth 2.0 devices. + + +NDEFAgent hierarchy +======================= + +Service unique name +Interface org.neard.NDEFAgent +Object path freely definable + +Methods dict GetNDEF(dict values) [experimental] + + This method gets called when the service daemon + found an NDEF matching the registered type. + + The parameter is a dictionary where the keys are the + field names and the values are the actual fields. + + void Release() [experimental] + + This method gets called when the service daemon + unregisters the agent. An agent can use it to do + cleanup tasks. There is no need to unregister the + agent, because when this method gets called it has + already been unregistered. + +Fields array{byte} NDEF + + This is the raw NDEF data. + + object Record + + This is a record object path. diff --git a/doc/coding-style.txt b/doc/coding-style.txt new file mode 100644 index 0000000..47f9c4a --- /dev/null +++ b/doc/coding-style.txt @@ -0,0 +1,344 @@ +Every project has its coding style, and neard is not an exception. This +document describes the preferred coding style for neard code, in order to keep +some level of consistency among developers so that code can be easily +understood and maintained, and also to help your code survive under +maintainer's fastidious eyes so that you can get a passport for your patch +ASAP. + +First of all, neard coding style must follow every rule for Linux kernel +(http://www.kernel.org/doc/Documentation/CodingStyle). There also exists a tool +named checkpatch.pl to help you check the compliance with it. Just type +"checkpatch.pl --no-tree patch_name" to check your patch. In theory, you need +to clean up all the warnings and errors except this one: "ERROR: Missing +Signed-off-by: line(s)". neard does not used Signed-Off lines, so including +them is actually an error. In certain circumstances one can ignore the 80 +character per line limit. This is generally only allowed if the alternative +would make the code even less readable. + +Besides the kernel coding style above, neard has special flavors for its own. +Some of them are mandatory (marked as 'M'), while some others are optional +(marked as 'O'), but generally preferred. + +M1: Blank line before and after an if/while/do/for statement +============================================================ +There should be a blank line before if statement unless the if is nested and +not preceded by an expression or variable declaration. + +Example: +1) +a = 1; +if (b) { // wrong + +2) +a = 1 + +if (b) { +} +a = 2; // wrong + +3) +if (a) { + if (b) // correct + +4) +b = 2; + +if (a) { // correct + +} + +b = 3; + +The only exception to this rule applies when a variable is being allocated: +array = g_try_new0(int, 20); +if (array == NULL) // Correct + return; + + +M2: Multiple line comment +========================= +If your comments have more then one line, please start it from the second line. + +Example: +/* + * first line comment // correct + * ... + * last line comment + */ + + +M3: Space before and after operator +=================================== +There should be a space before and after each operator. + +Example: +a + b; // correct + + +M4: Wrap long lines +=================== +If your condition in if, while, for statement or a function declaration is too +long to fit in one line, the new line needs to be indented not aligned with the +body. + +Example: +1) +if (call->status == CALL_STATUS_ACTIVE || + call->status == CALL_STATUS_HELD) { // wrong + neard_dbus_dict_append(); + +2) +if (call->status == CALL_STATUS_ACTIVE || + call->status == CALL_STATUS_HELD) { // correct + neard_dbus_dict_append(); + +3) +gboolean sim_ust_is_available(unsigned char *service_ust, unsigned char len, + num sim_ust_service index) // wrong +{ + int a; + ... +} + +4) +gboolean sim_ust_is_available(unsigned char *service_ust, unsigned char len, + enum sim_ust_service index) // correct +{ + int a; + ... +} + +If the line being wrapped is a function call or function declaration, the +preferred style is to indent at least past the opening parenthesis. Indenting +further is acceptable as well (as long as you don't hit the 80 character +limit). + +If this is not possible due to hitting the 80 character limit, then indenting +as far as possible to the right without hitting the limit is preferred. + +Example: + +1) +gboolean sim_ust_is_available(unsigned char *service_ust, unsigned char len, + enum sim_ust_service index); // worse + +2) +gboolean sim_ust_is_available(unsigned char *service_ust, unsigned char len, + enum sim_ust_service index); + // better + +M5: Git commit message 50/72 formatting +======================================= +The commit message header should be within 50 characters. And if you have +detailed explanatory text, wrap it to 72 character. + + +M6: Space when doing type casting +================================= +There should be a space between new type and variable. + +Example: +1) +a = (int *)b; // wrong +2) +a = (int *) b; // correct + + +M7: Don't initialize variable unnecessarily +=========================================== +When declaring a variable, try not to initialize it unless necessary. + +Example: +int i = 1; // wrong + +for (i = 0; i < 3; i++) { +} + + +M8: Use g_try_malloc instead of g_malloc +======================================== +When g_malloc fails, the whole program would exit. Most of time, this is not +the expected behavior, and you may want to use g_try_malloc instead. + +Example: +additional = g_try_malloc(len - 1); // correct +if (additional == NULL) + return FALSE; + + +M9: Follow the order of include header elements +=============================================== +When writing an include header the various elements should be in the following +order: + - #includes + - forward declarations + - #defines + - enums + - typedefs + - function declarations and inline function definitions + + +M10: Internal headers must not use include guards +================================================= +Any time when creating a new header file with non-public API, that header +must not contain include guards. + + +M11: Naming of enums +==================== + +Enums must have a descriptive name. The enum type should be small caps and +it should not be typedef-ed. Enum contents should be in CAPITAL letters and +prefixed by the enum type name. + +Example: + +enum animal_type { + ANIMAL_TYPE_FOUR_LEGS, + ANIMAL_TYPE_EIGHT_LEGS, + ANIMAL_TYPE_TWO_LEGS, +}; + +If the enum contents have values (e.g. from specification) the formatting +should be as follows: + +enum animal_type { + ANIMAL_TYPE_FOUR_LEGS = 4, + ANIMAL_TYPE_EIGHT_LEGS = 8, + ANIMAL_TYPE_TWO_LEGS = 2, +}; + +M12: Enum as switch variable +==================== + +If the variable of a switch is an enum, you must not include a default in +switch body. The reason for this is: If later on you modify the enum by adding +a new type, and forget to change the switch accordingly, the compiler will +complain the new added type hasn't been handled. + +Example: + +enum animal_type { + ANIMAL_TYPE_FOUR_LEGS = 4, + ANIMAL_TYPE_EIGHT_LEGS = 8, + ANIMAL_TYPE_TWO_LEGS = 2, +}; + +enum animal_type t; + +switch (t) { +case ANIMAL_TYPE_FOUR_LEGS: + ... + break; +case ANIMAL_TYPE_EIGHT_LEGS: + ... + break; +case ANIMAL_TYPE_TWO_LEGS: + ... + break; +default: // wrong + break; +} + +However if the enum comes from an external header file outside neard +we cannot make any assumption of how the enum is defined and this +rule might not apply. + +M13: Check for pointer being NULL +================================= + +When checking if a pointer or a return value is NULL, explicitly compare to +NULL rather than use the shorter check with "!" operator. + +Example: +1) +array = g_try_new0(int, 20); +if (!array) // Wrong + return; + +2) +if (!g_at_chat_get_slave(chat)) // Wrong + return -EINVAL; + +3) +array = g_try_new0(int, 20); +if (array == NULL) // Correct + return; + + +M14: Always use parenthesis with sizeof +======================================= +The expression argument to the sizeof operator should always be in +parenthesis, too. + +Example: +1) +memset(stuff, 0, sizeof(*stuff)); + +2) +memset(stuff, 0, sizeof *stuff); // Wrong + + +M15: Use void if function has no parameters +=========================================================== +A function with no parameters must use void in the parameter list. + +Example: +1) +void foo(void) +{ +} + +2) +void foo() // Wrong +{ +} + +M16: Don't use hex value with shift operators +============================================== +The expression argument to the shift operators should not be in hex. + +Example: + +1) +1 << y + +2) +0x1 << y // Wrong + +O1: Shorten the name +==================== +Better to use abbreviation, rather than full name, to name a variable, +function, struct, etc. + +Example: +supplementary_service // too long +ss // better + +O2: Try to avoid complex if body +================================ +It's better not to have a complicated statement for if. You may judge its +contrary condition and return | break | continue | goto ASAP. + +Example: +1) +if (a) { // worse + struct voicecall *v; + call = synthesize_outgoing_call(vc, vc->pending); + v = voicecall_create(vc, call); + v->detect_time = time(NULL); + DBG("Registering new call: %d", call->id); + voicecall_dbus_register(v); +} else + return; + +2) +if (!a) + return; + +struct voicecall *v; +call = synthesize_outgoing_call(vc, vc->pending); +v = voicecall_create(vc, call); +v->detect_time = time(NULL); +DBG("Registering new call: %d", call->id); +voicecall_dbus_register(v); diff --git a/doc/device-api.txt b/doc/device-api.txt new file mode 100644 index 0000000..a098bbb --- /dev/null +++ b/doc/device-api.txt @@ -0,0 +1,46 @@ +Device hierarchy +================ + +Service org.neard +Interface org.neard.Device +Object path [variable prefix]/{nfc0}/{device0, device1...} + +Method dict GetProperties() + + Returns all properties for the device. See the + properties section for available properties. + + Possible Errors: org.neard.Error.DoesNotExist + + void SetProperty(string name, variant value) + + Changes the value of the specified property. Only + properties that are listed a read-write are changeable. + On success this will emit a PropertyChanged signal. + + Possible Errors: org.neard.Error.DoesNotExist + org.neard.Error.InvalidArguments + + void Push(dict attributes) + + Creates an NDEF record from the attributes dictionary. + + The attribute argument should at least contain a + record type and is described by the Record properties. + For example, one would add a type, a Language, an + Encoding and a Representation for a text record. + + Possible Errors: org.neard.Error.PermissionDenied + org.neard.Error.InvalidArguments + org.neard.Error.InProgress + + +Signals PropertyChanged(string name, variant value) + + This signal indicates a changed value of the given + property. + + +Properties array{object} Records [readonly] + + List of NDEF records object paths. diff --git a/doc/features.txt b/doc/features.txt new file mode 100644 index 0000000..5745dc0 --- /dev/null +++ b/doc/features.txt @@ -0,0 +1,45 @@ +neard - Near Field Communication daemon +======================================= + +Purpose +======= + +This document describes all the major functionalities +supported by neard. + + +Reader Mode +=========== +Supported tags: + + * Type 1: Supported. + * Type 2: Supported. + * MIFARE classic 1K and 4K supported. + * Type 3: Supported. + * Type 4: Supported. + + +Card Emulation Mode +=================== +Not supported. + + +Writer Mode +=========== +Supported tags: + + * Type 1: Supported. + * Type 2: Supported. + * Type 3: Supported. + * Type 4: Supported. + + +Peer To Peer Mode +================= +neard reads and write NDEFs from p2p devices, through SNEP and NPP. +Both initiator and target modes are supported. + + +Handover +======== +WIP. diff --git a/doc/manager-api.txt b/doc/manager-api.txt new file mode 100644 index 0000000..47547ff --- /dev/null +++ b/doc/manager-api.txt @@ -0,0 +1,67 @@ +Manager hierarchy +================= + +Service org.neard +Interface org.neard.Manager +Object path / + +Methods dict GetProperties() + + Returns all properties for the device. See the + properties section for available properties. + + Possible Errors: org.neard.Error.DoesNotExist + + void SetProperty(string name, variant value) + + Changes the value of the specified property. Only + properties that are listed a read-write are changeable. + On success this will emit a PropertyChanged signal. + + Possible Errors: org.neard.Error.DoesNotExist + org.neard.Error.InvalidArguments + + void RegisterHandoverAgent(object path) + + Register new handover agent. + + Possible Errors: org.neard.Error.InvalidArguments + + void UnregisterHandoverAgent(object path) + + Unregister an existing handover agent. + + Possible Errors: org.neard.Error.InvalidArguments + + void RegisterNDEFAgent(object path, string type) + + Register new NDEF agent. + + When a record macthing the registered type is found, + the agent will get the whole NDEF as a raw byte stream. + + Possible Errors: org.neard.Error.InvalidArguments + + void UnregisterNDEFAgent(object path, string type) + + Unregister an existing NDEF agent. + + Possible Errors: org.neard.Error.InvalidArguments + + +Signals PropertyChanged(string name, variant value) + + This signal indicates a changed value of the given + property. + + AdapterAdded(object adapter) + + Parameter is the object path of added adapter. + + AdapterRemoved(object adapter) + + Parameter is the object path of removed adapter. + +Properties array{object} Adapters [readonly] + + List of adapter object paths. diff --git a/doc/tag-api.txt b/doc/tag-api.txt new file mode 100644 index 0000000..6fb2a0a --- /dev/null +++ b/doc/tag-api.txt @@ -0,0 +1,156 @@ +Tag hierarchy +================ + +Service org.neard +Interface org.neard.Tag +Object path [variable prefix]/{nfc0}/{tag0, tag1...} + +Method dict GetProperties() + + Returns all properties for the device. See the + properties section for available properties. + + Possible Errors: org.neard.Error.DoesNotExist + + void SetProperty(string name, variant value) + + Changes the value of the specified property. Only + properties that are listed a read-write are changeable. + On success this will emit a PropertyChanged signal. + + Possible Errors: org.neard.Error.DoesNotExist + org.neard.Error.InvalidArguments + + void Write(dict attributes) + + Creates an NDEF record from the attributes dictionary. + + The attribute argument should at least contain a + record type and is described by the Record properties. + For example, one would add a type, a Language, an + Encoding and a Representation for a text record. + To push raw NDEF, one should use the NDEF key and use + an array of bytes for the NDEF stream. + + Possible Errors: org.neard.Error.PermissionDenied + org.neard.Error.InvalidArguments + org.neard.Error.InProgress + + array{byte} GetRawNDEF() + + Return the tag's NDEF as a raw bytes stream. + + +Signals PropertyChanged(string name, variant value) + + This signal indicates a changed value of the given + property. + + +Properties string Type [readonly] + + The NFC tag type. + Possible values are "Type 1", "Type 2", "Type 3", + "Type 4" and "NFC-DEP" + + string Protocol [readonly] + + The tag radio protocol. + Possible values are "Felica", "MIFARE", "Jewel", + and "ISO-DEP". + + array{object} Records [readonly] + + List of NDEF records object paths. + + boolean ReadOnly [readonly] + + Give the current status of tag's read mode + + +Record hierarchy +================ + +Service org.neard +Interface org.neard.Record +Object path [variable prefix]/{nfc0}/{tag0|device}/{record0,record1,...} + +Method dict GetProperties() + + Returns all properties for the record. Each record + has it's type and properties. + + If type has "Text", possible properties are "Encoding", + "Language" and "Representation". + + See the properties section for available properties. + + Possible Errors: org.neard.Error.DoesNotExist + +Properties string Type [readonly] + + The NDEF record type name. + + Possible values are "SmartPoster", "Text", "URI", + "HandoverRequest", "HandoverSelect", "HandoverCarrier". + + string Encoding [readonly] + + The character encoding. + + Possible values are "UTF-8" or "UTF-16". + This property is only valid for Text and SmartPoster's + title records. + + string Language [readonly] + + The ISO/IANA language code (For example "en" or "jp"). + + This property is only valid for Text and SmartPoster's + title records. + + string Representation [readonly] + + The human readable representation of a text or + title record. + + This property is only valid for Text and SmartPoster's + title records. + + string URI [readonly] + + The record URI (for example https://nfc-forum.org). + + This is the complete URI, including the scheme and + the resource. + This property is only valid for SmartPoster's URI records. + + string MIMEType [readonly] + + The URI object MIME type. + + This is a description of the MIME type of the object + the URI points at. + This is not a mandatory field and is only valid for + Smart Posters carrying a URI record. + + uint32 Size [readonly] + + The URI object size. + + This is the size of the object the URI points at. + It should be used by applications to decide if they can + afford to fetch the object or not. + This is not a mandatory field and is only valid for + Smart Posters carrying a URI record. + + string Action [readonly] + + The suggested course of action. + + This one is only valid for Smart Posters and is a + suggestion only. It can be ignored, and the possible + values are "Do" (for example launch the browser), + "Save" (for example save the URI in the bookmarks folder, + or "Edit" (for example open the URI in an URI editor for + the user to modify it. diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h new file mode 100644 index 0000000..0a8a27c --- /dev/null +++ b/gdbus/gdbus.h @@ -0,0 +1,227 @@ +/* + * + * D-Bus helper library + * + * Copyright (C) 2004-2011 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __GDBUS_H +#define __GDBUS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <dbus/dbus.h> +#include <glib.h> + +typedef void (* GDBusWatchFunction) (DBusConnection *connection, + void *user_data); + +typedef gboolean (* GDBusSignalFunction) (DBusConnection *connection, + DBusMessage *message, void *user_data); + +DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name, + DBusError *error); + +DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name, + DBusError *error); + +gboolean g_dbus_request_name(DBusConnection *connection, const char *name, + DBusError *error); + +gboolean g_dbus_set_disconnect_function(DBusConnection *connection, + GDBusWatchFunction function, + void *user_data, DBusFreeFunction destroy); + +typedef void (* GDBusDestroyFunction) (void *user_data); + +typedef DBusMessage * (* GDBusMethodFunction) (DBusConnection *connection, + DBusMessage *message, void *user_data); + +typedef guint32 GDBusPendingReply; + +typedef void (* GDBusSecurityFunction) (DBusConnection *connection, + const char *action, + gboolean interaction, + GDBusPendingReply pending); + +typedef enum { + G_DBUS_METHOD_FLAG_DEPRECATED = (1 << 0), + G_DBUS_METHOD_FLAG_NOREPLY = (1 << 1), + G_DBUS_METHOD_FLAG_ASYNC = (1 << 2), +} GDBusMethodFlags; + +typedef enum { + G_DBUS_SIGNAL_FLAG_DEPRECATED = (1 << 0), +} GDBusSignalFlags; + +typedef enum { + G_DBUS_PROPERTY_FLAG_DEPRECATED = (1 << 0), +} GDBusPropertyFlags; + +typedef enum { + G_DBUS_SECURITY_FLAG_DEPRECATED = (1 << 0), + G_DBUS_SECURITY_FLAG_BUILTIN = (1 << 1), + G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION = (1 << 2), +} GDBusSecurityFlags; + +typedef struct { + const char *name; + const char *signature; +} GDBusArgInfo; + +typedef struct { + const char *name; + GDBusMethodFunction function; + GDBusMethodFlags flags; + unsigned int privilege; + const GDBusArgInfo *in_args; + const GDBusArgInfo *out_args; +} GDBusMethodTable; + +typedef struct { + const char *name; + GDBusSignalFlags flags; + const GDBusArgInfo *args; +} GDBusSignalTable; + +typedef struct { + const char *name; + const char *type; + GDBusPropertyFlags flags; +} GDBusPropertyTable; + +typedef struct { + unsigned int privilege; + const char *action; + GDBusSecurityFlags flags; + GDBusSecurityFunction function; +} GDBusSecurityTable; + +#define GDBUS_ARGS(args...) (const GDBusArgInfo[]) { args, { } } + +#define GDBUS_METHOD(_name, _in_args, _out_args, _function) \ + .name = _name, \ + .in_args = _in_args, \ + .out_args = _out_args, \ + .function = _function + +#define GDBUS_ASYNC_METHOD(_name, _in_args, _out_args, _function) \ + .name = _name, \ + .in_args = _in_args, \ + .out_args = _out_args, \ + .function = _function, \ + .flags = G_DBUS_METHOD_FLAG_ASYNC + +#define GDBUS_DEPRECATED_METHOD(_name, _in_args, _out_args, _function) \ + .name = _name, \ + .in_args = _in_args, \ + .out_args = _out_args, \ + .function = _function, \ + .flags = G_DBUS_METHOD_FLAG_DEPRECATED + +#define GDBUS_DEPRECATED_ASYNC_METHOD(_name, _in_args, _out_args, _function) \ + .name = _name, \ + .in_args = _in_args, \ + .out_args = _out_args, \ + .function = _function, \ + .flags = G_DBUS_METHOD_FLAG_ASYNC | G_DBUS_METHOD_FLAG_DEPRECATED + +#define GDBUS_NOREPLY_METHOD(_name, _in_args, _out_args, _function) \ + .name = _name, \ + .in_args = _in_args, \ + .out_args = _out_args, \ + .function = _function, \ + .flags = G_DBUS_METHOD_FLAG_NOREPLY + +#define GDBUS_SIGNAL(_name, _args) \ + .name = _name, \ + .args = _args + +#define GDBUS_DEPRECATED_SIGNAL(_name, _args) \ + .name = _name, \ + .args = _args, \ + .flags = G_DBUS_SIGNAL_FLAG_DEPRECATED + +gboolean g_dbus_register_interface(DBusConnection *connection, + const char *path, const char *name, + const GDBusMethodTable *methods, + const GDBusSignalTable *signals, + const GDBusPropertyTable *properties, + void *user_data, + GDBusDestroyFunction destroy); +gboolean g_dbus_unregister_interface(DBusConnection *connection, + const char *path, const char *name); + +gboolean g_dbus_register_security(const GDBusSecurityTable *security); +gboolean g_dbus_unregister_security(const GDBusSecurityTable *security); + +void g_dbus_pending_success(DBusConnection *connection, + GDBusPendingReply pending); +void g_dbus_pending_error(DBusConnection *connection, + GDBusPendingReply pending, + const char *name, const char *format, ...) + __attribute__((format(printf, 4, 5))); +void g_dbus_pending_error_valist(DBusConnection *connection, + GDBusPendingReply pending, const char *name, + const char *format, va_list args); + +DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name, + const char *format, ...) + __attribute__((format(printf, 3, 4))); +DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name, + const char *format, va_list args); +DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...); +DBusMessage *g_dbus_create_reply_valist(DBusMessage *message, + int type, va_list args); + +gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message); +gboolean g_dbus_send_reply(DBusConnection *connection, + DBusMessage *message, int type, ...); +gboolean g_dbus_send_reply_valist(DBusConnection *connection, + DBusMessage *message, int type, va_list args); + +gboolean g_dbus_emit_signal(DBusConnection *connection, + const char *path, const char *interface, + const char *name, int type, ...); +gboolean g_dbus_emit_signal_valist(DBusConnection *connection, + const char *path, const char *interface, + const char *name, int type, va_list args); + +guint g_dbus_add_service_watch(DBusConnection *connection, const char *name, + GDBusWatchFunction connect, + GDBusWatchFunction disconnect, + void *user_data, GDBusDestroyFunction destroy); +guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name, + GDBusWatchFunction function, + void *user_data, GDBusDestroyFunction destroy); +guint g_dbus_add_signal_watch(DBusConnection *connection, + const char *sender, const char *path, + const char *interface, const char *member, + GDBusSignalFunction function, void *user_data, + GDBusDestroyFunction destroy); +gboolean g_dbus_remove_watch(DBusConnection *connection, guint tag); +void g_dbus_remove_all_watches(DBusConnection *connection); + +#ifdef __cplusplus +} +#endif + +#endif /* __GDBUS_H */ diff --git a/gdbus/mainloop.c b/gdbus/mainloop.c new file mode 100644 index 0000000..cff326f --- /dev/null +++ b/gdbus/mainloop.c @@ -0,0 +1,379 @@ +/* + * + * D-Bus helper library + * + * Copyright (C) 2004-2011 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib.h> +#include <dbus/dbus.h> + +#include "gdbus.h" + +#define DISPATCH_TIMEOUT 0 + +#define info(fmt...) +#define error(fmt...) +#define debug(fmt...) + +struct timeout_handler { + guint id; + DBusTimeout *timeout; +}; + +struct watch_info { + guint id; + DBusWatch *watch; + DBusConnection *conn; +}; + +struct disconnect_data { + GDBusWatchFunction function; + void *user_data; +}; + +static gboolean disconnected_signal(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct disconnect_data *dc_data = data; + + error("Got disconnected from the system message bus"); + + dc_data->function(conn, dc_data->user_data); + + dbus_connection_unref(conn); + + return TRUE; +} + +static gboolean message_dispatch(void *data) +{ + DBusConnection *conn = data; + + dbus_connection_ref(conn); + + /* Dispatch messages */ + while (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS); + + dbus_connection_unref(conn); + + return FALSE; +} + +static inline void queue_dispatch(DBusConnection *conn, + DBusDispatchStatus status) +{ + if (status == DBUS_DISPATCH_DATA_REMAINS) + g_timeout_add(DISPATCH_TIMEOUT, message_dispatch, conn); +} + +static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data) +{ + struct watch_info *info = data; + unsigned int flags = 0; + DBusDispatchStatus status; + + dbus_connection_ref(info->conn); + + if (cond & G_IO_IN) flags |= DBUS_WATCH_READABLE; + if (cond & G_IO_OUT) flags |= DBUS_WATCH_WRITABLE; + if (cond & G_IO_HUP) flags |= DBUS_WATCH_HANGUP; + if (cond & G_IO_ERR) flags |= DBUS_WATCH_ERROR; + + dbus_watch_handle(info->watch, flags); + + status = dbus_connection_get_dispatch_status(info->conn); + queue_dispatch(info->conn, status); + + dbus_connection_unref(info->conn); + + return TRUE; +} + +static void watch_info_free(void *data) +{ + struct watch_info *info = data; + + if (info->id > 0) { + g_source_remove(info->id); + info->id = 0; + } + + dbus_connection_unref(info->conn); + + g_free(info); +} + +static dbus_bool_t add_watch(DBusWatch *watch, void *data) +{ + DBusConnection *conn = data; + GIOCondition cond = G_IO_HUP | G_IO_ERR; + GIOChannel *chan; + struct watch_info *info; + unsigned int flags; + int fd; + + if (!dbus_watch_get_enabled(watch)) + return TRUE; + + info = g_new0(struct watch_info, 1); + + fd = dbus_watch_get_unix_fd(watch); + chan = g_io_channel_unix_new(fd); + + info->watch = watch; + info->conn = dbus_connection_ref(conn); + + dbus_watch_set_data(watch, info, watch_info_free); + + flags = dbus_watch_get_flags(watch); + + if (flags & DBUS_WATCH_READABLE) cond |= G_IO_IN; + if (flags & DBUS_WATCH_WRITABLE) cond |= G_IO_OUT; + + info->id = g_io_add_watch(chan, cond, watch_func, info); + + g_io_channel_unref(chan); + + return TRUE; +} + +static void remove_watch(DBusWatch *watch, void *data) +{ + if (dbus_watch_get_enabled(watch)) + return; + + /* will trigger watch_info_free() */ + dbus_watch_set_data(watch, NULL, NULL); +} + +static void watch_toggled(DBusWatch *watch, void *data) +{ + /* Because we just exit on OOM, enable/disable is + * no different from add/remove */ + if (dbus_watch_get_enabled(watch)) + add_watch(watch, data); + else + remove_watch(watch, data); +} + +static gboolean timeout_handler_dispatch(gpointer data) +{ + struct timeout_handler *handler = data; + + handler->id = 0; + + /* if not enabled should not be polled by the main loop */ + if (!dbus_timeout_get_enabled(handler->timeout)) + return FALSE; + + dbus_timeout_handle(handler->timeout); + + return FALSE; +} + +static void timeout_handler_free(void *data) +{ + struct timeout_handler *handler = data; + + if (handler->id > 0) { + g_source_remove(handler->id); + handler->id = 0; + } + + g_free(handler); +} + +static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) +{ + int interval = dbus_timeout_get_interval(timeout); + struct timeout_handler *handler; + + if (!dbus_timeout_get_enabled(timeout)) + return TRUE; + + handler = g_new0(struct timeout_handler, 1); + + handler->timeout = timeout; + + dbus_timeout_set_data(timeout, handler, timeout_handler_free); + + handler->id = g_timeout_add(interval, timeout_handler_dispatch, + handler); + + return TRUE; +} + +static void remove_timeout(DBusTimeout *timeout, void *data) +{ + /* will trigger timeout_handler_free() */ + dbus_timeout_set_data(timeout, NULL, NULL); +} + +static void timeout_toggled(DBusTimeout *timeout, void *data) +{ + if (dbus_timeout_get_enabled(timeout)) + add_timeout(timeout, data); + else + remove_timeout(timeout, data); +} + +static void dispatch_status(DBusConnection *conn, + DBusDispatchStatus status, void *data) +{ + if (!dbus_connection_get_is_connected(conn)) + return; + + queue_dispatch(conn, status); +} + +static inline void setup_dbus_with_main_loop(DBusConnection *conn) +{ + dbus_connection_set_watch_functions(conn, add_watch, remove_watch, + watch_toggled, conn, NULL); + + dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, + timeout_toggled, NULL, NULL); + + dbus_connection_set_dispatch_status_function(conn, dispatch_status, + NULL, NULL); +} + +static gboolean setup_bus(DBusConnection *conn, const char *name, + DBusError *error) +{ + gboolean result; + DBusDispatchStatus status; + + if (name != NULL) { + result = g_dbus_request_name(conn, name, error); + + if (error != NULL) { + if (dbus_error_is_set(error) == TRUE) + return FALSE; + } + + if (result == FALSE) + return FALSE; + } + + setup_dbus_with_main_loop(conn); + + status = dbus_connection_get_dispatch_status(conn); + queue_dispatch(conn, status); + + return TRUE; +} + +DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name, + DBusError *error) +{ + DBusConnection *conn; + + conn = dbus_bus_get(type, error); + + if (error != NULL) { + if (dbus_error_is_set(error) == TRUE) + return NULL; + } + + if (conn == NULL) + return NULL; + + if (setup_bus(conn, name, error) == FALSE) { + dbus_connection_unref(conn); + return NULL; + } + + return conn; +} + +DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name, + DBusError *error) +{ + DBusConnection *conn; + + conn = dbus_bus_get_private(type, error); + + if (error != NULL) { + if (dbus_error_is_set(error) == TRUE) + return NULL; + } + + if (conn == NULL) + return NULL; + + if (setup_bus(conn, name, error) == FALSE) { + dbus_connection_unref(conn); + return NULL; + } + + return conn; +} + +gboolean g_dbus_request_name(DBusConnection *connection, const char *name, + DBusError *error) +{ + int result; + + result = dbus_bus_request_name(connection, name, + DBUS_NAME_FLAG_DO_NOT_QUEUE, error); + + if (error != NULL) { + if (dbus_error_is_set(error) == TRUE) + return FALSE; + } + + if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + if (error != NULL) + dbus_set_error(error, name, "Name already in use"); + + return FALSE; + } + + return TRUE; +} + +gboolean g_dbus_set_disconnect_function(DBusConnection *connection, + GDBusWatchFunction function, + void *user_data, DBusFreeFunction destroy) +{ + struct disconnect_data *dc_data; + + dc_data = g_new0(struct disconnect_data, 1); + + dc_data->function = function; + dc_data->user_data = user_data; + + dbus_connection_set_exit_on_disconnect(connection, FALSE); + + if (g_dbus_add_signal_watch(connection, NULL, NULL, + DBUS_INTERFACE_LOCAL, "Disconnected", + disconnected_signal, dc_data, g_free) == 0) { + error("Failed to add watch for D-Bus Disconnected signal"); + g_free(dc_data); + return FALSE; + } + + return TRUE; +} diff --git a/gdbus/object.c b/gdbus/object.c new file mode 100644 index 0000000..9689006 --- /dev/null +++ b/gdbus/object.c @@ -0,0 +1,858 @@ +/* + * + * D-Bus helper library + * + * Copyright (C) 2004-2011 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <string.h> + +#include <glib.h> +#include <dbus/dbus.h> + +#include "gdbus.h" + +#define info(fmt...) +#define error(fmt...) +#define debug(fmt...) + +struct generic_data { + unsigned int refcount; + GSList *interfaces; + char *introspect; +}; + +struct interface_data { + char *name; + const GDBusMethodTable *methods; + const GDBusSignalTable *signals; + const GDBusPropertyTable *properties; + void *user_data; + GDBusDestroyFunction destroy; +}; + +struct security_data { + GDBusPendingReply pending; + DBusMessage *message; + const GDBusMethodTable *method; + void *iface_user_data; +}; + +static void print_arguments(GString *gstr, const GDBusArgInfo *args, + const char *direction) +{ + for (; args && args->name; args++) { + g_string_append_printf(gstr, + "\t\t\t<arg name=\"%s\" type=\"%s\"", + args->name, args->signature); + + if (direction) + g_string_append_printf(gstr, + " direction=\"%s\"/>\n", direction); + else + g_string_append_printf(gstr, "/>\n"); + + } +} + +static void generate_interface_xml(GString *gstr, struct interface_data *iface) +{ + const GDBusMethodTable *method; + const GDBusSignalTable *signal; + + for (method = iface->methods; method && method->name; method++) { + gboolean deprecated = method->flags & + G_DBUS_METHOD_FLAG_DEPRECATED; + gboolean noreply = method->flags & + G_DBUS_METHOD_FLAG_NOREPLY; + + if (!deprecated && !noreply && + !(method->in_args && method->in_args->name) && + !(method->out_args && method->out_args->name)) + g_string_append_printf(gstr, "\t\t<method name=\"%s\"/>\n", + method->name); + else { + g_string_append_printf(gstr, "\t\t<method name=\"%s\">\n", + method->name); + print_arguments(gstr, method->in_args, "in"); + print_arguments(gstr, method->out_args, "out"); + + if (deprecated) + g_string_append_printf(gstr, "\t\t\t<annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n"); + + if (noreply) + g_string_append_printf(gstr, "\t\t\t<annotation name=\"org.freedesktop.DBus.Method.NoReply\" value=\"true\"/>\n"); + + g_string_append_printf(gstr, "\t\t</method>\n"); + } + } + + for (signal = iface->signals; signal && signal->name; signal++) { + gboolean deprecated = signal->flags & + G_DBUS_SIGNAL_FLAG_DEPRECATED; + + if (!deprecated && !(signal->args && signal->args->name)) + g_string_append_printf(gstr, "\t\t<signal name=\"%s\"/>\n", + signal->name); + else { + g_string_append_printf(gstr, "\t\t<signal name=\"%s\">\n", + signal->name); + print_arguments(gstr, signal->args, NULL); + + if (deprecated) + g_string_append_printf(gstr, "\t\t\t<annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n"); + + g_string_append_printf(gstr, "\t\t</signal>\n"); + } + } +} + +static void generate_introspection_xml(DBusConnection *conn, + struct generic_data *data, const char *path) +{ + GSList *list; + GString *gstr; + char **children; + int i; + + g_free(data->introspect); + + gstr = g_string_new(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE); + + g_string_append_printf(gstr, "<node>\n"); + + for (list = data->interfaces; list; list = list->next) { + struct interface_data *iface = list->data; + + g_string_append_printf(gstr, "\t<interface name=\"%s\">\n", + iface->name); + + generate_interface_xml(gstr, iface); + + g_string_append_printf(gstr, "\t</interface>\n"); + } + + if (!dbus_connection_list_registered(conn, path, &children)) + goto done; + + for (i = 0; children[i]; i++) + g_string_append_printf(gstr, "\t<node name=\"%s\"/>\n", + children[i]); + + dbus_free_string_array(children); + +done: + g_string_append_printf(gstr, "</node>\n"); + + data->introspect = g_string_free(gstr, FALSE); +} + +static DBusMessage *introspect(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct generic_data *data = user_data; + DBusMessage *reply; + + if (data->introspect == NULL) + generate_introspection_xml(connection, data, + dbus_message_get_path(message)); + + reply = dbus_message_new_method_return(message); + if (reply == NULL) + return NULL; + + dbus_message_append_args(reply, DBUS_TYPE_STRING, &data->introspect, + DBUS_TYPE_INVALID); + + return reply; +} + +static DBusHandlerResult process_message(DBusConnection *connection, + DBusMessage *message, const GDBusMethodTable *method, + void *iface_user_data) +{ + DBusMessage *reply; + + reply = method->function(connection, message, iface_user_data); + + if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY) { + if (reply != NULL) + dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_HANDLED; + } + + if (method->flags & G_DBUS_METHOD_FLAG_ASYNC) { + if (reply == NULL) + return DBUS_HANDLER_RESULT_HANDLED; + } + + if (reply == NULL) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + dbus_connection_send(connection, reply, NULL); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static GDBusPendingReply next_pending = 1; +static GSList *pending_security = NULL; + +static const GDBusSecurityTable *security_table = NULL; + +void g_dbus_pending_success(DBusConnection *connection, + GDBusPendingReply pending) +{ + GSList *list; + + for (list = pending_security; list; list = list->next) { + struct security_data *secdata = list->data; + + if (secdata->pending != pending) + continue; + + pending_security = g_slist_remove(pending_security, secdata); + + process_message(connection, secdata->message, + secdata->method, secdata->iface_user_data); + + dbus_message_unref(secdata->message); + g_free(secdata); + return; + } +} + +void g_dbus_pending_error_valist(DBusConnection *connection, + GDBusPendingReply pending, const char *name, + const char *format, va_list args) +{ + GSList *list; + + for (list = pending_security; list; list = list->next) { + struct security_data *secdata = list->data; + DBusMessage *reply; + + if (secdata->pending != pending) + continue; + + pending_security = g_slist_remove(pending_security, secdata); + + reply = g_dbus_create_error_valist(secdata->message, + name, format, args); + if (reply != NULL) { + dbus_connection_send(connection, reply, NULL); + dbus_message_unref(reply); + } + + dbus_message_unref(secdata->message); + g_free(secdata); + return; + } +} + +void g_dbus_pending_error(DBusConnection *connection, + GDBusPendingReply pending, + const char *name, const char *format, ...) +{ + va_list args; + + va_start(args, format); + + g_dbus_pending_error_valist(connection, pending, name, format, args); + + va_end(args); +} + +int polkit_check_authorization(DBusConnection *conn, + const char *action, gboolean interaction, + void (*function) (dbus_bool_t authorized, + void *user_data), + void *user_data, int timeout); + +struct builtin_security_data { + DBusConnection *conn; + GDBusPendingReply pending; +}; + +static void builtin_security_result(dbus_bool_t authorized, void *user_data) +{ + struct builtin_security_data *data = user_data; + + if (authorized == TRUE) + g_dbus_pending_success(data->conn, data->pending); + else + g_dbus_pending_error(data->conn, data->pending, + DBUS_ERROR_AUTH_FAILED, NULL); + + g_free(data); +} + +static void builtin_security_function(DBusConnection *conn, + const char *action, + gboolean interaction, + GDBusPendingReply pending) +{ + struct builtin_security_data *data; + + data = g_new0(struct builtin_security_data, 1); + data->conn = conn; + data->pending = pending; + + if (polkit_check_authorization(conn, action, interaction, + builtin_security_result, data, 30000) < 0) + g_dbus_pending_error(conn, pending, NULL, NULL); +} + +static gboolean check_privilege(DBusConnection *conn, DBusMessage *msg, + const GDBusMethodTable *method, void *iface_user_data) +{ + const GDBusSecurityTable *security; + + for (security = security_table; security && security->privilege; + security++) { + struct security_data *secdata; + gboolean interaction; + + if (security->privilege != method->privilege) + continue; + + secdata = g_new(struct security_data, 1); + secdata->pending = next_pending++; + secdata->message = dbus_message_ref(msg); + secdata->method = method; + secdata->iface_user_data = iface_user_data; + + pending_security = g_slist_prepend(pending_security, secdata); + + if (security->flags & G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION) + interaction = TRUE; + else + interaction = FALSE; + + if (!(security->flags & G_DBUS_SECURITY_FLAG_BUILTIN) && + security->function) + security->function(conn, security->action, + interaction, secdata->pending); + else + builtin_security_function(conn, security->action, + interaction, secdata->pending); + + return TRUE; + } + + return FALSE; +} + +static void generic_unregister(DBusConnection *connection, void *user_data) +{ + struct generic_data *data = user_data; + + g_free(data->introspect); + g_free(data); +} + +static struct interface_data *find_interface(GSList *interfaces, + const char *name) +{ + GSList *list; + + if (name == NULL) + return NULL; + + for (list = interfaces; list; list = list->next) { + struct interface_data *iface = list->data; + if (!strcmp(name, iface->name)) + return iface; + } + + return NULL; +} + +static gboolean g_dbus_args_have_signature(const GDBusArgInfo *args, + DBusMessage *message) +{ + const char *sig = dbus_message_get_signature(message); + const char *p = NULL; + + for (; args && args->signature && *sig; args++) { + p = args->signature; + + for (; *sig && *p; sig++, p++) { + if (*p != *sig) + return FALSE; + } + } + + if (*sig || (p && *p) || (args && args->signature)) + return FALSE; + + return TRUE; +} + +static DBusHandlerResult generic_message(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct generic_data *data = user_data; + struct interface_data *iface; + const GDBusMethodTable *method; + const char *interface; + + interface = dbus_message_get_interface(message); + + iface = find_interface(data->interfaces, interface); + if (iface == NULL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + for (method = iface->methods; method && + method->name && method->function; method++) { + if (dbus_message_is_method_call(message, iface->name, + method->name) == FALSE) + continue; + + if (g_dbus_args_have_signature(method->in_args, + message) == FALSE) + continue; + + if (check_privilege(connection, message, method, + iface->user_data) == TRUE) + return DBUS_HANDLER_RESULT_HANDLED; + + return process_message(connection, message, method, + iface->user_data); + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusObjectPathVTable generic_table = { + .unregister_function = generic_unregister, + .message_function = generic_message, +}; + +static void invalidate_parent_data(DBusConnection *conn, const char *child_path) +{ + struct generic_data *data = NULL; + char *parent_path, *slash; + + parent_path = g_strdup(child_path); + slash = strrchr(parent_path, '/'); + if (slash == NULL) + goto done; + + if (slash == parent_path && parent_path[1] != '\0') + parent_path[1] = '\0'; + else + *slash = '\0'; + + if (!strlen(parent_path)) + goto done; + + if (dbus_connection_get_object_path_data(conn, parent_path, + (void *) &data) == FALSE) { + goto done; + } + + invalidate_parent_data(conn, parent_path); + + if (data == NULL) + goto done; + + g_free(data->introspect); + data->introspect = NULL; + +done: + g_free(parent_path); +} + +static const GDBusMethodTable introspect_methods[] = { + { GDBUS_METHOD("Introspect", NULL, + GDBUS_ARGS({ "xml", "s" }), introspect) }, + { } +}; + +static void add_interface(struct generic_data *data, const char *name, + const GDBusMethodTable *methods, + const GDBusSignalTable *signals, + const GDBusPropertyTable *properties, + void *user_data, + GDBusDestroyFunction destroy) +{ + struct interface_data *iface; + + iface = g_new0(struct interface_data, 1); + iface->name = g_strdup(name); + iface->methods = methods; + iface->signals = signals; + iface->properties = properties; + iface->user_data = user_data; + iface->destroy = destroy; + + data->interfaces = g_slist_append(data->interfaces, iface); +} + +static struct generic_data *object_path_ref(DBusConnection *connection, + const char *path) +{ + struct generic_data *data; + + if (dbus_connection_get_object_path_data(connection, path, + (void *) &data) == TRUE) { + if (data != NULL) { + data->refcount++; + return data; + } + } + + data = g_new0(struct generic_data, 1); + data->refcount = 1; + + data->introspect = g_strdup(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "<node></node>"); + + if (!dbus_connection_register_object_path(connection, path, + &generic_table, data)) { + g_free(data->introspect); + g_free(data); + return NULL; + } + + invalidate_parent_data(connection, path); + + add_interface(data, DBUS_INTERFACE_INTROSPECTABLE, + introspect_methods, NULL, NULL, data, NULL); + + return data; +} + +static gboolean remove_interface(struct generic_data *data, const char *name) +{ + struct interface_data *iface; + + iface = find_interface(data->interfaces, name); + if (iface == NULL) + return FALSE; + + data->interfaces = g_slist_remove(data->interfaces, iface); + + if (iface->destroy) + iface->destroy(iface->user_data); + + g_free(iface->name); + g_free(iface); + + return TRUE; +} + +static void object_path_unref(DBusConnection *connection, const char *path) +{ + struct generic_data *data = NULL; + + if (dbus_connection_get_object_path_data(connection, path, + (void *) &data) == FALSE) + return; + + if (data == NULL) + return; + + data->refcount--; + + if (data->refcount > 0) + return; + + remove_interface(data, DBUS_INTERFACE_INTROSPECTABLE); + + invalidate_parent_data(connection, path); + + dbus_connection_unregister_object_path(connection, path); +} + +static gboolean check_signal(DBusConnection *conn, const char *path, + const char *interface, const char *name, + const GDBusArgInfo **args) +{ + struct generic_data *data = NULL; + struct interface_data *iface; + const GDBusSignalTable *signal; + + *args = NULL; + if (!dbus_connection_get_object_path_data(conn, path, + (void *) &data) || data == NULL) { + error("dbus_connection_emit_signal: path %s isn't registered", + path); + return FALSE; + } + + iface = find_interface(data->interfaces, interface); + if (iface == NULL) { + error("dbus_connection_emit_signal: %s does not implement %s", + path, interface); + return FALSE; + } + + for (signal = iface->signals; signal && signal->name; signal++) { + if (!strcmp(signal->name, name)) { + *args = signal->args; + return TRUE; + } + } + + error("No signal named %s on interface %s", name, interface); + return FALSE; +} + +static dbus_bool_t emit_signal_valist(DBusConnection *conn, + const char *path, + const char *interface, + const char *name, + int first, + va_list var_args) +{ + DBusMessage *signal; + dbus_bool_t ret; + const GDBusArgInfo *args; + + if (!check_signal(conn, path, interface, name, &args)) + return FALSE; + + signal = dbus_message_new_signal(path, interface, name); + if (signal == NULL) { + error("Unable to allocate new %s.%s signal", interface, name); + return FALSE; + } + + ret = dbus_message_append_args_valist(signal, first, var_args); + if (!ret) + goto fail; + + if (g_dbus_args_have_signature(args, signal) == FALSE) { + error("%s.%s: got unexpected signature '%s'", interface, name, + dbus_message_get_signature(signal)); + ret = FALSE; + goto fail; + } + + ret = dbus_connection_send(conn, signal, NULL); + +fail: + dbus_message_unref(signal); + + return ret; +} + +gboolean g_dbus_register_interface(DBusConnection *connection, + const char *path, const char *name, + const GDBusMethodTable *methods, + const GDBusSignalTable *signals, + const GDBusPropertyTable *properties, + void *user_data, + GDBusDestroyFunction destroy) +{ + struct generic_data *data; + + data = object_path_ref(connection, path); + if (data == NULL) + return FALSE; + + if (find_interface(data->interfaces, name)) { + object_path_unref(connection, path); + return FALSE; + } + + add_interface(data, name, methods, signals, + properties, user_data, destroy); + + g_free(data->introspect); + data->introspect = NULL; + + return TRUE; +} + +gboolean g_dbus_unregister_interface(DBusConnection *connection, + const char *path, const char *name) +{ + struct generic_data *data = NULL; + + if (path == NULL) + return FALSE; + + if (dbus_connection_get_object_path_data(connection, path, + (void *) &data) == FALSE) + return FALSE; + + if (data == NULL) + return FALSE; + + if (remove_interface(data, name) == FALSE) + return FALSE; + + g_free(data->introspect); + data->introspect = NULL; + + object_path_unref(connection, path); + + return TRUE; +} + +gboolean g_dbus_register_security(const GDBusSecurityTable *security) +{ + if (security_table != NULL) + return FALSE; + + security_table = security; + + return TRUE; +} + +gboolean g_dbus_unregister_security(const GDBusSecurityTable *security) +{ + security_table = NULL; + + return TRUE; +} + +DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name, + const char *format, va_list args) +{ + char str[1024]; + + vsnprintf(str, sizeof(str), format, args); + + return dbus_message_new_error(message, name, str); +} + +DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name, + const char *format, ...) +{ + va_list args; + DBusMessage *reply; + + va_start(args, format); + + reply = g_dbus_create_error_valist(message, name, format, args); + + va_end(args); + + return reply; +} + +DBusMessage *g_dbus_create_reply_valist(DBusMessage *message, + int type, va_list args) +{ + DBusMessage *reply; + + reply = dbus_message_new_method_return(message); + if (reply == NULL) + return NULL; + + if (dbus_message_append_args_valist(reply, type, args) == FALSE) { + dbus_message_unref(reply); + return NULL; + } + + return reply; +} + +DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...) +{ + va_list args; + DBusMessage *reply; + + va_start(args, type); + + reply = g_dbus_create_reply_valist(message, type, args); + + va_end(args); + + return reply; +} + +gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message) +{ + dbus_bool_t result; + + if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL) + dbus_message_set_no_reply(message, TRUE); + + result = dbus_connection_send(connection, message, NULL); + + dbus_message_unref(message); + + return result; +} + +gboolean g_dbus_send_reply_valist(DBusConnection *connection, + DBusMessage *message, int type, va_list args) +{ + DBusMessage *reply; + + reply = dbus_message_new_method_return(message); + if (reply == NULL) + return FALSE; + + if (dbus_message_append_args_valist(reply, type, args) == FALSE) { + dbus_message_unref(reply); + return FALSE; + } + + return g_dbus_send_message(connection, reply); +} + +gboolean g_dbus_send_reply(DBusConnection *connection, + DBusMessage *message, int type, ...) +{ + va_list args; + gboolean result; + + va_start(args, type); + + result = g_dbus_send_reply_valist(connection, message, type, args); + + va_end(args); + + return result; +} + +gboolean g_dbus_emit_signal(DBusConnection *connection, + const char *path, const char *interface, + const char *name, int type, ...) +{ + va_list args; + gboolean result; + + va_start(args, type); + + result = emit_signal_valist(connection, path, interface, + name, type, args); + + va_end(args); + + return result; +} + +gboolean g_dbus_emit_signal_valist(DBusConnection *connection, + const char *path, const char *interface, + const char *name, int type, va_list args) +{ + return emit_signal_valist(connection, path, interface, + name, type, args); +} diff --git a/gdbus/polkit.c b/gdbus/polkit.c new file mode 100644 index 0000000..9e95fa3 --- /dev/null +++ b/gdbus/polkit.c @@ -0,0 +1,202 @@ +/* + * + * D-Bus helper library + * + * Copyright (C) 2004-2011 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> + +#include <dbus/dbus.h> + +#include <glib.h> + +int polkit_check_authorization(DBusConnection *conn, + const char *action, gboolean interaction, + void (*function) (dbus_bool_t authorized, + void *user_data), + void *user_data, int timeout); + +static void add_dict_with_string_value(DBusMessageIter *iter, + const char *key, const char *str) +{ + DBusMessageIter dict, entry, value; + + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); + dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, + NULL, &entry); + + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, + DBUS_TYPE_STRING_AS_STRING, &value); + dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str); + dbus_message_iter_close_container(&entry, &value); + + dbus_message_iter_close_container(&dict, &entry); + dbus_message_iter_close_container(iter, &dict); +} + +static void add_empty_string_dict(DBusMessageIter *iter) +{ + DBusMessageIter dict; + + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); + + dbus_message_iter_close_container(iter, &dict); +} + +static void add_arguments(DBusConnection *conn, DBusMessageIter *iter, + const char *action, dbus_uint32_t flags) +{ + const char *busname = dbus_bus_get_unique_name(conn); + const char *kind = "system-bus-name"; + const char *cancel = ""; + DBusMessageIter subject; + + dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, + NULL, &subject); + dbus_message_iter_append_basic(&subject, DBUS_TYPE_STRING, &kind); + add_dict_with_string_value(&subject, "name", busname); + dbus_message_iter_close_container(iter, &subject); + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &action); + add_empty_string_dict(iter); + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &flags); + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &cancel); +} + +static dbus_bool_t parse_result(DBusMessageIter *iter) +{ + DBusMessageIter result; + dbus_bool_t authorized, challenge; + + dbus_message_iter_recurse(iter, &result); + + dbus_message_iter_get_basic(&result, &authorized); + dbus_message_iter_get_basic(&result, &challenge); + + return authorized; +} + +struct authorization_data { + void (*function) (dbus_bool_t authorized, void *user_data); + void *user_data; +}; + +static void authorization_reply(DBusPendingCall *call, void *user_data) +{ + struct authorization_data *data = user_data; + DBusMessage *reply; + DBusMessageIter iter; + dbus_bool_t authorized = FALSE; + + reply = dbus_pending_call_steal_reply(call); + + if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) + goto done; + + if (dbus_message_has_signature(reply, "(bba{ss})") == FALSE) + goto done; + + dbus_message_iter_init(reply, &iter); + + authorized = parse_result(&iter); + +done: + if (data->function != NULL) + data->function(authorized, data->user_data); + + dbus_message_unref(reply); + + dbus_pending_call_unref(call); +} + +#define AUTHORITY_DBUS "org.freedesktop.PolicyKit1" +#define AUTHORITY_INTF "org.freedesktop.PolicyKit1.Authority" +#define AUTHORITY_PATH "/org/freedesktop/PolicyKit1/Authority" + +int polkit_check_authorization(DBusConnection *conn, + const char *action, gboolean interaction, + void (*function) (dbus_bool_t authorized, + void *user_data), + void *user_data, int timeout) +{ + struct authorization_data *data; + DBusMessage *msg; + DBusMessageIter iter; + DBusPendingCall *call; + dbus_uint32_t flags = 0x00000000; + + if (conn == NULL) + return -EINVAL; + + data = dbus_malloc0(sizeof(*data)); + if (data == NULL) + return -ENOMEM; + + msg = dbus_message_new_method_call(AUTHORITY_DBUS, AUTHORITY_PATH, + AUTHORITY_INTF, "CheckAuthorization"); + if (msg == NULL) { + dbus_free(data); + return -ENOMEM; + } + + if (interaction == TRUE) + flags |= 0x00000001; + + if (action == NULL) + action = "org.freedesktop.policykit.exec"; + + dbus_message_iter_init_append(msg, &iter); + add_arguments(conn, &iter, action, flags); + + if (dbus_connection_send_with_reply(conn, msg, + &call, timeout) == FALSE) { + dbus_message_unref(msg); + dbus_free(data); + return -EIO; + } + + if (call == NULL) { + dbus_message_unref(msg); + dbus_free(data); + return -EIO; + } + + data->function = function; + data->user_data = user_data; + + dbus_pending_call_set_notify(call, authorization_reply, + data, dbus_free); + + dbus_message_unref(msg); + + return 0; +} diff --git a/gdbus/watch.c b/gdbus/watch.c new file mode 100644 index 0000000..d749176 --- /dev/null +++ b/gdbus/watch.c @@ -0,0 +1,747 @@ +/* + * + * D-Bus helper library + * + * Copyright (C) 2004-2011 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <string.h> + +#include <glib.h> +#include <dbus/dbus.h> + +#include "gdbus.h" + +#define info(fmt...) +#define error(fmt...) +#define debug(fmt...) + +static DBusHandlerResult message_filter(DBusConnection *connection, + DBusMessage *message, void *user_data); + +static guint listener_id = 0; +static GSList *listeners = NULL; + +struct service_data { + DBusConnection *conn; + DBusPendingCall *call; + char *name; + const char *owner; + guint id; + struct filter_callback *callback; +}; + +struct filter_callback { + GDBusWatchFunction conn_func; + GDBusWatchFunction disc_func; + GDBusSignalFunction signal_func; + GDBusDestroyFunction destroy_func; + struct service_data *data; + void *user_data; + guint id; +}; + +struct filter_data { + DBusConnection *connection; + DBusHandleMessageFunction handle_func; + char *name; + char *owner; + char *path; + char *interface; + char *member; + char *argument; + GSList *callbacks; + GSList *processed; + guint name_watch; + gboolean lock; + gboolean registered; +}; + +static struct filter_data *filter_data_find(DBusConnection *connection, + const char *name, + const char *owner, + const char *path, + const char *interface, + const char *member, + const char *argument) +{ + GSList *current; + + for (current = listeners; + current != NULL; current = current->next) { + struct filter_data *data = current->data; + + if (connection != data->connection) + continue; + + if (name && data->name && + g_str_equal(name, data->name) == FALSE) + continue; + + if (owner && data->owner && + g_str_equal(owner, data->owner) == FALSE) + continue; + + if (path && data->path && + g_str_equal(path, data->path) == FALSE) + continue; + + if (interface && data->interface && + g_str_equal(interface, data->interface) == FALSE) + continue; + + if (member && data->member && + g_str_equal(member, data->member) == FALSE) + continue; + + if (argument && data->argument && + g_str_equal(argument, data->argument) == FALSE) + continue; + + return data; + } + + return NULL; +} + +static void format_rule(struct filter_data *data, char *rule, size_t size) +{ + const char *sender; + int offset; + + offset = snprintf(rule, size, "type='signal'"); + sender = data->name ? : data->owner; + + if (sender) + offset += snprintf(rule + offset, size - offset, + ",sender='%s'", sender); + if (data->path) + offset += snprintf(rule + offset, size - offset, + ",path='%s'", data->path); + if (data->interface) + offset += snprintf(rule + offset, size - offset, + ",interface='%s'", data->interface); + if (data->member) + offset += snprintf(rule + offset, size - offset, + ",member='%s'", data->member); + if (data->argument) + snprintf(rule + offset, size - offset, + ",arg0='%s'", data->argument); +} + +static gboolean add_match(struct filter_data *data, + DBusHandleMessageFunction filter) +{ + DBusError err; + char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH]; + + format_rule(data, rule, sizeof(rule)); + dbus_error_init(&err); + + dbus_bus_add_match(data->connection, rule, &err); + if (dbus_error_is_set(&err)) { + error("Adding match rule \"%s\" failed: %s", rule, + err.message); + dbus_error_free(&err); + return FALSE; + } + + data->handle_func = filter; + data->registered = TRUE; + + return TRUE; +} + +static gboolean remove_match(struct filter_data *data) +{ + DBusError err; + char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH]; + + format_rule(data, rule, sizeof(rule)); + + dbus_error_init(&err); + + dbus_bus_remove_match(data->connection, rule, &err); + if (dbus_error_is_set(&err)) { + error("Removing owner match rule for %s failed: %s", + rule, err.message); + dbus_error_free(&err); + return FALSE; + } + + return TRUE; +} + +static struct filter_data *filter_data_get(DBusConnection *connection, + DBusHandleMessageFunction filter, + const char *sender, + const char *path, + const char *interface, + const char *member, + const char *argument) +{ + struct filter_data *data; + const char *name = NULL, *owner = NULL; + + if (filter_data_find(connection, NULL, NULL, NULL, NULL, NULL, NULL) == NULL) { + if (!dbus_connection_add_filter(connection, + message_filter, NULL, NULL)) { + error("dbus_connection_add_filter() failed"); + return NULL; + } + } + + if (sender == NULL) + goto proceed; + + if (sender[0] == ':') + owner = sender; + else + name = sender; + +proceed: + data = filter_data_find(connection, name, owner, path, interface, + member, argument); + if (data) + return data; + + data = g_new0(struct filter_data, 1); + + data->connection = dbus_connection_ref(connection); + data->name = name ? g_strdup(name) : NULL; + data->owner = owner ? g_strdup(owner) : NULL; + data->path = g_strdup(path); + data->interface = g_strdup(interface); + data->member = g_strdup(member); + data->argument = g_strdup(argument); + + if (!add_match(data, filter)) { + g_free(data); + return NULL; + } + + listeners = g_slist_append(listeners, data); + + return data; +} + +static struct filter_callback *filter_data_find_callback( + struct filter_data *data, + guint id) +{ + GSList *l; + + for (l = data->callbacks; l; l = l->next) { + struct filter_callback *cb = l->data; + if (cb->id == id) + return cb; + } + for (l = data->processed; l; l = l->next) { + struct filter_callback *cb = l->data; + if (cb->id == id) + return cb; + } + + return NULL; +} + +static void filter_data_free(struct filter_data *data) +{ + GSList *l; + + for (l = data->callbacks; l != NULL; l = l->next) + g_free(l->data); + + g_slist_free(data->callbacks); + g_dbus_remove_watch(data->connection, data->name_watch); + g_free(data->name); + g_free(data->owner); + g_free(data->path); + g_free(data->interface); + g_free(data->member); + g_free(data->argument); + dbus_connection_unref(data->connection); + g_free(data); +} + +static void filter_data_call_and_free(struct filter_data *data) +{ + GSList *l; + + for (l = data->callbacks; l != NULL; l = l->next) { + struct filter_callback *cb = l->data; + if (cb->disc_func) + cb->disc_func(data->connection, cb->user_data); + if (cb->destroy_func) + cb->destroy_func(cb->user_data); + g_free(cb); + } + + filter_data_free(data); +} + +static struct filter_callback *filter_data_add_callback( + struct filter_data *data, + GDBusWatchFunction connect, + GDBusWatchFunction disconnect, + GDBusSignalFunction signal, + GDBusDestroyFunction destroy, + void *user_data) +{ + struct filter_callback *cb = NULL; + + cb = g_new0(struct filter_callback, 1); + + cb->conn_func = connect; + cb->disc_func = disconnect; + cb->signal_func = signal; + cb->destroy_func = destroy; + cb->user_data = user_data; + cb->id = ++listener_id; + + if (data->lock) + data->processed = g_slist_append(data->processed, cb); + else + data->callbacks = g_slist_append(data->callbacks, cb); + + return cb; +} + +static void service_data_free(struct service_data *data) +{ + struct filter_callback *callback = data->callback; + + dbus_connection_unref(data->conn); + + if (data->call) + dbus_pending_call_unref(data->call); + + if (data->id) + g_source_remove(data->id); + + g_free(data->name); + g_free(data); + + callback->data = NULL; +} + +static gboolean filter_data_remove_callback(struct filter_data *data, + struct filter_callback *cb) +{ + DBusConnection *connection; + + data->callbacks = g_slist_remove(data->callbacks, cb); + data->processed = g_slist_remove(data->processed, cb); + + /* Cancel pending operations */ + if (cb->data) { + if (cb->data->call) + dbus_pending_call_cancel(cb->data->call); + service_data_free(cb->data); + } + + if (cb->destroy_func) + cb->destroy_func(cb->user_data); + + g_free(cb); + + /* Don't remove the filter if other callbacks exist or data is lock + * processing callbacks */ + if (data->callbacks || data->lock) + return TRUE; + + if (data->registered && !remove_match(data)) + return FALSE; + + connection = dbus_connection_ref(data->connection); + listeners = g_slist_remove(listeners, data); + + /* Remove filter if there are no listeners left for the connection */ + if (filter_data_find(connection, NULL, NULL, NULL, NULL, NULL, + NULL) == NULL) + dbus_connection_remove_filter(connection, message_filter, + NULL); + + filter_data_free(data); + dbus_connection_unref(connection); + + return TRUE; +} + +static DBusHandlerResult signal_filter(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct filter_data *data = user_data; + struct filter_callback *cb; + + while (data->callbacks) { + cb = data->callbacks->data; + + if (cb->signal_func && !cb->signal_func(connection, message, + cb->user_data)) { + filter_data_remove_callback(data, cb); + continue; + } + + /* Check if the watch was removed/freed by the callback + * function */ + if (!g_slist_find(data->callbacks, cb)) + continue; + + data->callbacks = g_slist_remove(data->callbacks, cb); + data->processed = g_slist_append(data->processed, cb); + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static void update_name_cache(const char *name, const char *owner) +{ + GSList *l; + + for (l = listeners; l != NULL; l = l->next) { + struct filter_data *data = l->data; + + if (g_strcmp0(data->name, name) != 0) + continue; + + g_free(data->owner); + data->owner = g_strdup(owner); + } +} + +static const char *check_name_cache(const char *name) +{ + GSList *l; + + for (l = listeners; l != NULL; l = l->next) { + struct filter_data *data = l->data; + + if (g_strcmp0(data->name, name) != 0) + continue; + + return data->owner; + } + + return NULL; +} + +static DBusHandlerResult service_filter(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct filter_data *data = user_data; + struct filter_callback *cb; + char *name, *old, *new; + + if (!dbus_message_get_args(message, NULL, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &old, + DBUS_TYPE_STRING, &new, + DBUS_TYPE_INVALID)) { + error("Invalid arguments for NameOwnerChanged signal"); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + update_name_cache(name, new); + + while (data->callbacks) { + cb = data->callbacks->data; + + if (*new == '\0') { + if (cb->disc_func) + cb->disc_func(connection, cb->user_data); + } else { + if (cb->conn_func) + cb->conn_func(connection, cb->user_data); + } + + /* Check if the watch was removed/freed by the callback + * function */ + if (!g_slist_find(data->callbacks, cb)) + continue; + + /* Only auto remove if it is a bus name watch */ + if (data->argument[0] == ':' && + (cb->conn_func == NULL || cb->disc_func == NULL)) { + filter_data_remove_callback(data, cb); + continue; + } + + data->callbacks = g_slist_remove(data->callbacks, cb); + data->processed = g_slist_append(data->processed, cb); + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + + +static DBusHandlerResult message_filter(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct filter_data *data; + const char *sender, *path, *iface, *member, *arg = NULL; + + /* Only filter signals */ + if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + sender = dbus_message_get_sender(message); + path = dbus_message_get_path(message); + iface = dbus_message_get_interface(message); + member = dbus_message_get_member(message); + dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID); + + /* Sender is always bus name */ + data = filter_data_find(connection, NULL, sender, path, iface, member, + arg); + if (data == NULL) { + error("Got %s.%s signal which has no listeners", iface, member); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + if (data->handle_func) { + data->lock = TRUE; + + data->handle_func(connection, message, data); + + data->callbacks = data->processed; + data->processed = NULL; + data->lock = FALSE; + } + + if (data->callbacks) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + remove_match(data); + + listeners = g_slist_remove(listeners, data); + + /* Remove filter if there are no listeners left for the connection */ + if (filter_data_find(connection, NULL, NULL, NULL, NULL, NULL, + NULL) == NULL) + dbus_connection_remove_filter(connection, message_filter, + NULL); + + filter_data_free(data); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static gboolean update_service(void *user_data) +{ + struct service_data *data = user_data; + struct filter_callback *cb = data->callback; + + update_name_cache(data->name, data->owner); + if (cb->conn_func) + cb->conn_func(data->conn, cb->user_data); + + service_data_free(data); + + return FALSE; +} + +static void service_reply(DBusPendingCall *call, void *user_data) +{ + struct service_data *data = user_data; + DBusMessage *reply; + DBusError err; + + reply = dbus_pending_call_steal_reply(call); + if (reply == NULL) + return; + + dbus_error_init(&err); + + if (dbus_set_error_from_message(&err, reply)) + goto fail; + + if (dbus_message_get_args(reply, &err, + DBUS_TYPE_STRING, &data->owner, + DBUS_TYPE_INVALID) == FALSE) + goto fail; + + update_service(data); + + goto done; + +fail: + error("%s", err.message); + dbus_error_free(&err); + service_data_free(data); +done: + dbus_message_unref(reply); +} + +static void check_service(DBusConnection *connection, + const char *name, + struct filter_callback *callback) +{ + DBusMessage *message; + struct service_data *data; + + data = g_try_malloc0(sizeof(*data)); + if (data == NULL) { + error("Can't allocate data structure"); + return; + } + + data->conn = dbus_connection_ref(connection); + data->name = g_strdup(name); + data->callback = callback; + callback->data = data; + + data->owner = check_name_cache(name); + if (data->owner != NULL) { + data->id = g_idle_add(update_service, data); + return; + } + + message = dbus_message_new_method_call(DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner"); + if (message == NULL) { + error("Can't allocate new message"); + g_free(data); + return; + } + + dbus_message_append_args(message, DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID); + + if (dbus_connection_send_with_reply(connection, message, + &data->call, -1) == FALSE) { + error("Failed to execute method call"); + g_free(data); + goto done; + } + + if (data->call == NULL) { + error("D-Bus connection not available"); + g_free(data); + goto done; + } + + dbus_pending_call_set_notify(data->call, service_reply, data, NULL); + +done: + dbus_message_unref(message); +} + +guint g_dbus_add_service_watch(DBusConnection *connection, const char *name, + GDBusWatchFunction connect, + GDBusWatchFunction disconnect, + void *user_data, GDBusDestroyFunction destroy) +{ + struct filter_data *data; + struct filter_callback *cb; + + if (name == NULL) + return 0; + + data = filter_data_get(connection, service_filter, NULL, NULL, + DBUS_INTERFACE_DBUS, "NameOwnerChanged", + name); + if (data == NULL) + return 0; + + cb = filter_data_add_callback(data, connect, disconnect, NULL, destroy, + user_data); + if (cb == NULL) + return 0; + + if (connect) + check_service(connection, name, cb); + + return cb->id; +} + +guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name, + GDBusWatchFunction func, + void *user_data, GDBusDestroyFunction destroy) +{ + return g_dbus_add_service_watch(connection, name, NULL, func, + user_data, destroy); +} + +guint g_dbus_add_signal_watch(DBusConnection *connection, + const char *sender, const char *path, + const char *interface, const char *member, + GDBusSignalFunction function, void *user_data, + GDBusDestroyFunction destroy) +{ + struct filter_data *data; + struct filter_callback *cb; + + data = filter_data_get(connection, signal_filter, sender, path, + interface, member, NULL); + if (data == NULL) + return 0; + + cb = filter_data_add_callback(data, NULL, NULL, function, destroy, + user_data); + if (cb == NULL) + return 0; + + if (data->name != NULL && data->name_watch == 0) + data->name_watch = g_dbus_add_service_watch(connection, + data->name, NULL, + NULL, NULL, NULL); + + return cb->id; +} + +gboolean g_dbus_remove_watch(DBusConnection *connection, guint id) +{ + struct filter_data *data; + struct filter_callback *cb; + GSList *ldata; + + if (id == 0) + return FALSE; + + for (ldata = listeners; ldata; ldata = ldata->next) { + data = ldata->data; + + cb = filter_data_find_callback(data, id); + if (cb) { + filter_data_remove_callback(data, cb); + return TRUE; + } + } + + return FALSE; +} + +void g_dbus_remove_all_watches(DBusConnection *connection) +{ + struct filter_data *data; + + while ((data = filter_data_find(connection, NULL, NULL, NULL, NULL, + NULL, NULL))) { + listeners = g_slist_remove(listeners, data); + filter_data_call_and_free(data); + } + + dbus_connection_remove_filter(connection, message_filter, NULL); +} diff --git a/include/adapter.h b/include/adapter.h new file mode 100644 index 0000000..e547d3d --- /dev/null +++ b/include/adapter.h @@ -0,0 +1,38 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __NEAR_ADAPTER_H +#define __NEAR_ADAPTER_H + +#include <stdint.h> +#include <stdlib.h> +#include <unistd.h> + +typedef int (*near_recv)(uint8_t *resp, int length, void *data); +typedef int (*near_release)(int err, void *data); + +int near_adapter_connect(uint32_t idx, uint32_t target_idx, uint8_t protocol); +int near_adapter_disconnect(uint32_t idx); +int near_adapter_send(uint32_t idx, uint8_t *buf, size_t length, + near_recv rx_cb, void *data, near_release data_rel); +int near_adapter_recv(uint32_t idx, uint8_t *buf, size_t length); + +#endif diff --git a/include/dbus.h b/include/dbus.h new file mode 100644 index 0000000..e5c74af --- /dev/null +++ b/include/dbus.h @@ -0,0 +1,138 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include <dbus/dbus.h> + +#define NFC_SERVICE "org.neard" +#define NFC_PATH "/org/neard" + +#define NFC_ERROR_INTERFACE NFC_SERVICE ".Error" +#define NFC_NDEF_AGENT_INTERFACE NFC_SERVICE ".NDEFAgent" + +#define NFC_MANAGER_INTERFACE NFC_SERVICE ".Manager" +#define NFC_MANAGER_PATH "/" + +#define NFC_ADAPTER_INTERFACE NFC_SERVICE ".Adapter" +#define NFC_DEVICE_INTERFACE NFC_SERVICE ".Device" +#define NFC_TAG_INTERFACE NFC_SERVICE ".Tag" +#define NFC_RECORD_INTERFACE NFC_SERVICE ".Record" + +typedef void (* near_dbus_append_cb_t) (DBusMessageIter *iter, + void *user_data); + +DBusConnection *near_dbus_get_connection(void); + +void near_dbus_property_append_basic(DBusMessageIter *iter, + const char *key, int type, void *val); +void near_dbus_property_append_dict(DBusMessageIter *iter, const char *key, + near_dbus_append_cb_t function, void *user_data); +void near_dbus_property_append_array(DBusMessageIter *iter, + const char *key, int type, + near_dbus_append_cb_t function, void *user_data); +void near_dbus_property_append_fixed_array(DBusMessageIter *iter, + const char *key, int type, void *val, int len); + +dbus_bool_t near_dbus_property_changed_basic(const char *path, + const char *interface, const char *key, + int type, void *val); +dbus_bool_t near_dbus_property_changed_dict(const char *path, + const char *interface, const char *key, + near_dbus_append_cb_t function, void *user_data); +dbus_bool_t near_dbus_property_changed_array(const char *path, + const char *interface, const char *key, int type, + near_dbus_append_cb_t function, void *user_data); + +dbus_bool_t near_dbus_setting_changed_basic(const char *owner, + const char *path, const char *key, + int type, void *val); +dbus_bool_t near_dbus_setting_changed_dict(const char *owner, + const char *path, const char *key, + near_dbus_append_cb_t function, + void *user_data); +dbus_bool_t near_dbus_setting_changed_array(const char *owner, + const char *path, const char *key, int type, + near_dbus_append_cb_t function, + void *user_data); + +static inline void near_dbus_dict_open(DBusMessageIter *iter, + DBusMessageIter *dict) +{ + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, dict); +} + +static inline void near_dbus_dict_close(DBusMessageIter *iter, + DBusMessageIter *dict) +{ + dbus_message_iter_close_container(iter, dict); +} + +static inline void near_dbus_dict_append_basic(DBusMessageIter *dict, + const char *key, int type, void *val) +{ + DBusMessageIter entry; + + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, + NULL, &entry); + near_dbus_property_append_basic(&entry, key, type, val); + dbus_message_iter_close_container(dict, &entry); +} + +static inline void near_dbus_dict_append_dict(DBusMessageIter *dict, + const char *key, near_dbus_append_cb_t function, + void *user_data) +{ + DBusMessageIter entry; + + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, + NULL, &entry); + near_dbus_property_append_dict(&entry, key, function, user_data); + dbus_message_iter_close_container(dict, &entry); +} + +static inline void near_dbus_dict_append_array(DBusMessageIter *dict, + const char *key, int type, near_dbus_append_cb_t function, + void *user_data) +{ + DBusMessageIter entry; + + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, + NULL, &entry); + near_dbus_property_append_array(&entry, key, + type, function, user_data); + dbus_message_iter_close_container(dict, &entry); +} + +static inline void near_dbus_dict_append_fixed_array(DBusMessageIter *dict, + const char *key, int type, void *val, int len) +{ + DBusMessageIter entry; + + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, + NULL, &entry); + near_dbus_property_append_fixed_array(&entry, key, type, val, len); + dbus_message_iter_close_container(dict, &entry); +} + +dbus_bool_t near_dbus_validate_ident(const char *ident); +char *near_dbus_encode_string(const char *value); diff --git a/include/device.h b/include/device.h new file mode 100644 index 0000000..15df8a9 --- /dev/null +++ b/include/device.h @@ -0,0 +1,63 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __NEAR_DEVICE_H +#define __NEAR_DEVICE_H + +#include <stdint.h> + +#include <glib.h> + +struct near_device; + +typedef void (*near_device_io_cb) (uint32_t adapter_idx, uint32_t target_idx, + int status); + +struct near_ndef_message; + +#define NEAR_DEVICE_PRIORITY_LOW -100 +#define NEAR_DEVICE_PRIORITY_DEFAULT 0 +#define NEAR_DEVICE_PRIORITY_HIGH 100 + +#define NEAR_DEVICE_SN_NPP "com.android.npp" +#define NEAR_DEVICE_SN_SNEP "urn:nfc:sn:snep" +#define NEAR_DEVICE_SN_HANDOVER "urn:nfc:sn:handover" + +struct near_device_driver { + int priority; + + int (*listen)(uint32_t adapter_idx, near_device_io_cb cb); + int (*push)(uint32_t adapter_idx, uint32_t target_idx, + struct near_ndef_message *ndef, + char *service_name, + near_device_io_cb cb); +}; + +struct near_device *near_device_get_device(uint32_t adapter_idx, + uint32_t target_idx); +int near_device_add_data(uint32_t adapter_idx, uint32_t target_idx, + uint8_t *data, size_t data_length); +int near_device_add_records(struct near_device *device, GList *records, + near_device_io_cb cb, int status); +int near_device_driver_register(struct near_device_driver *driver); +void near_device_driver_unregister(struct near_device_driver *driver); + +#endif diff --git a/include/log.h b/include/log.h new file mode 100644 index 0000000..6183b88 --- /dev/null +++ b/include/log.h @@ -0,0 +1,47 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +void near_info(const char *format, ...) + __attribute__((format(printf, 1, 2))); +void near_warn(const char *format, ...) + __attribute__((format(printf, 1, 2))); +void near_error(const char *format, ...) + __attribute__((format(printf, 1, 2))); +void near_debug(const char *format, ...) + __attribute__((format(printf, 1, 2))); + +struct near_debug_desc { + const char *name; + const char *file; +#define NEAR_DEBUG_FLAG_DEFAULT (0) +#define NEAR_DEBUG_FLAG_PRINT (1 << 0) + unsigned int flags; +} __attribute__((aligned(8))); + +#define DBG(fmt, arg...) do { \ + static struct near_debug_desc __near_debug_desc \ + __attribute__((used, section("__debug"), aligned(8))) = { \ + .file = __FILE__, .flags = NEAR_DEBUG_FLAG_DEFAULT, \ + }; \ + if (__near_debug_desc.flags & NEAR_DEBUG_FLAG_PRINT) \ + near_debug("%s:%s() " fmt, \ + __FILE__, __FUNCTION__ , ## arg); \ +} while (0) diff --git a/include/ndef.h b/include/ndef.h new file mode 100644 index 0000000..fdc5006 --- /dev/null +++ b/include/ndef.h @@ -0,0 +1,65 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __NEAR_NDEF_H +#define __NEAR_NDEF_H + +#include <near/tag.h> + +struct near_ndef_record; + +struct near_ndef_message { + size_t length; + size_t offset; + uint8_t *data; +}; + +/* near_ndef_handover_carrier*/ +#define NEAR_CARRIER_EMPTY 0x00 +#define NEAR_CARRIER_BLUETOOTH 0x01 /* bit 0 */ +#define NEAR_CARRIER_WIFI 0x02 /* bit 1 */ +#define NEAR_CARRIER_UNKNOWN 0x80 /* Bit 7 */ + +int near_ndef_count_records(uint8_t *ndef_in, size_t ndef_in_length, + uint8_t record_type); + +int near_ndef_record_length(uint8_t *ndef_in, size_t ndef_in_length); + +GList *near_ndef_parse_msg(uint8_t *ndef_data, size_t ndef_length); + +void near_ndef_records_free(GList *records); + +struct near_ndef_message *near_ndef_prepare_text_record(char *encoding, + char *language_code, char *text); + +struct near_ndef_message *near_ndef_prepare_uri_record(uint8_t identifier, + uint32_t field_length, uint8_t *field); + +struct near_ndef_message *near_ndef_prepare_handover_record(char* type_name, + struct near_ndef_record *record, + uint8_t carriers); + +struct near_ndef_message * +near_ndef_prepare_smartposter_record(uint8_t uri_identifier, + uint32_t uri_field_length, + uint8_t *uri_field); + +#endif diff --git a/include/nfc.h b/include/nfc.h new file mode 100644 index 0000000..6189f27 --- /dev/null +++ b/include/nfc.h @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2011 Instituto Nokia de Tecnologia + * + * Authors: + * Lauro Ramos Venancio <lauro.venancio@openbossa.org> + * Aloisio Almeida Jr <aloisio.almeida@openbossa.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __LINUX_NFC_H +#define __LINUX_NFC_H + +#include <linux/types.h> +#include <linux/socket.h> + +#define NFC_GENL_NAME "nfc" +#define NFC_GENL_VERSION 1 + +#define NFC_GENL_MCAST_EVENT_NAME "events" + +/** + * enum nfc_commands - supported nfc commands + * + * @NFC_CMD_UNSPEC: unspecified command + * + * @NFC_CMD_GET_DEVICE: request information about a device (requires + * %NFC_ATTR_DEVICE_INDEX) or dump request to get a list of all nfc devices + * @NFC_CMD_DEV_UP: turn on the nfc device + * (requires %NFC_ATTR_DEVICE_INDEX) + * @NFC_CMD_DEV_DOWN: turn off the nfc device + * (requires %NFC_ATTR_DEVICE_INDEX) + * @NFC_CMD_START_POLL: start polling for targets using the given protocols + * (requires %NFC_ATTR_DEVICE_INDEX and %NFC_ATTR_PROTOCOLS) + * @NFC_CMD_STOP_POLL: stop polling for targets (requires + * %NFC_ATTR_DEVICE_INDEX) + * @NFC_CMD_GET_TARGET: dump all targets found by the previous poll (requires + * %NFC_ATTR_DEVICE_INDEX) + * @NFC_EVENT_TARGETS_FOUND: event emitted when a new target is found + * (it sends %NFC_ATTR_DEVICE_INDEX) + * @NFC_EVENT_DEVICE_ADDED: event emitted when a new device is registred + * (it sends %NFC_ATTR_DEVICE_NAME, %NFC_ATTR_DEVICE_INDEX and + * %NFC_ATTR_PROTOCOLS) + * @NFC_EVENT_DEVICE_REMOVED: event emitted when a device is removed + * (it sends %NFC_ATTR_DEVICE_INDEX) + * @NFC_EVENT_TM_ACTIVATED: event emitted when the adapter is activated in + * target mode. + * @NFC_EVENT_DEVICE_DEACTIVATED: event emitted when the adapter is deactivated + * from target mode. + */ +enum nfc_commands { + NFC_CMD_UNSPEC, + NFC_CMD_GET_DEVICE, + NFC_CMD_DEV_UP, + NFC_CMD_DEV_DOWN, + NFC_CMD_DEP_LINK_UP, + NFC_CMD_DEP_LINK_DOWN, + NFC_CMD_START_POLL, + NFC_CMD_STOP_POLL, + NFC_CMD_GET_TARGET, + NFC_EVENT_TARGETS_FOUND, + NFC_EVENT_DEVICE_ADDED, + NFC_EVENT_DEVICE_REMOVED, + NFC_EVENT_TARGET_LOST, + NFC_EVENT_TM_ACTIVATED, + NFC_EVENT_TM_DEACTIVATED, +/* private: internal use only */ + __NFC_CMD_AFTER_LAST +}; +#define NFC_CMD_MAX (__NFC_CMD_AFTER_LAST - 1) + +/** + * enum nfc_attrs - supported nfc attributes + * + * @NFC_ATTR_UNSPEC: unspecified attribute + * + * @NFC_ATTR_DEVICE_INDEX: index of nfc device + * @NFC_ATTR_DEVICE_NAME: device name, max 8 chars + * @NFC_ATTR_PROTOCOLS: nfc protocols - bitwise or-ed combination from + * NFC_PROTO_*_MASK constants + * @NFC_ATTR_TARGET_INDEX: index of the nfc target + * @NFC_ATTR_TARGET_SENS_RES: NFC-A targets extra information such as NFCID + * @NFC_ATTR_TARGET_SEL_RES: NFC-A targets extra information (useful if the + * target is not NFC-Forum compliant) + * @NFC_ATTR_TARGET_NFCID1: NFC-A targets identifier, max 10 bytes + * @NFC_ATTR_TARGET_SENSB_RES: NFC-B targets extra information, max 12 bytes + * @NFC_ATTR_TARGET_SENSF_RES: NFC-F targets extra information, max 18 bytes + * @NFC_ATTR_COMM_MODE: Passive or active mode + * @NFC_ATTR_RF_MODE: Initiator or target + * @NFC_ATTR_IM_PROTOCOLS: Initiator mode protocols to poll for + * @NFC_ATTR_TM_PROTOCOLS: Target mode protocols to listen for + */ +enum nfc_attrs { + NFC_ATTR_UNSPEC, + NFC_ATTR_DEVICE_INDEX, + NFC_ATTR_DEVICE_NAME, + NFC_ATTR_PROTOCOLS, + NFC_ATTR_TARGET_INDEX, + NFC_ATTR_TARGET_SENS_RES, + NFC_ATTR_TARGET_SEL_RES, + NFC_ATTR_TARGET_NFCID1, + NFC_ATTR_TARGET_SENSB_RES, + NFC_ATTR_TARGET_SENSF_RES, + NFC_ATTR_COMM_MODE, + NFC_ATTR_RF_MODE, + NFC_ATTR_DEVICE_POWERED, + NFC_ATTR_IM_PROTOCOLS, + NFC_ATTR_TM_PROTOCOLS, +/* private: internal use only */ + __NFC_ATTR_AFTER_LAST +}; +#define NFC_ATTR_MAX (__NFC_ATTR_AFTER_LAST - 1) + +#define NFC_DEVICE_NAME_MAXSIZE 8 +#define NFC_NFCID1_MAXSIZE 10 +#define NFC_SENSB_RES_MAXSIZE 12 +#define NFC_SENSF_RES_MAXSIZE 18 +#define NFC_GB_MAXSIZE 48 + +/* NFC protocols */ +#define NFC_PROTO_JEWEL 1 +#define NFC_PROTO_MIFARE 2 +#define NFC_PROTO_FELICA 3 +#define NFC_PROTO_ISO14443 4 +#define NFC_PROTO_NFC_DEP 5 +#define NFC_PROTO_ISO14443_B 6 + +#define NFC_PROTO_MAX 7 + +/* NFC communication modes */ +#define NFC_COMM_ACTIVE 0 +#define NFC_COMM_PASSIVE 1 + +/* NFC RF modes */ +#define NFC_RF_INITIATOR 0 +#define NFC_RF_TARGET 1 +#define NFC_RF_NONE 2 + +/* NFC protocols masks used in bitsets */ +#define NFC_PROTO_JEWEL_MASK (1 << NFC_PROTO_JEWEL) +#define NFC_PROTO_MIFARE_MASK (1 << NFC_PROTO_MIFARE) +#define NFC_PROTO_FELICA_MASK (1 << NFC_PROTO_FELICA) +#define NFC_PROTO_ISO14443_MASK (1 << NFC_PROTO_ISO14443) +#define NFC_PROTO_NFC_DEP_MASK (1 << NFC_PROTO_NFC_DEP) +#define NFC_PROTO_ISO14443_B_MASK (1 << NFC_PROTO_ISO14443_B) + +struct sockaddr_nfc { + sa_family_t sa_family; + __u32 dev_idx; + __u32 target_idx; + __u32 nfc_protocol; +}; + +#define NFC_LLCP_MAX_SERVICE_NAME 63 +struct sockaddr_nfc_llcp { + sa_family_t sa_family; + __u32 dev_idx; + __u32 target_idx; + __u32 nfc_protocol; + __u8 dsap; /* Destination SAP, if known */ + __u8 ssap; /* Source SAP to be bound to */ + char service_name[NFC_LLCP_MAX_SERVICE_NAME]; /* Service name URI */; + size_t service_name_len; +}; + +/* NFC socket protocols */ +#define NFC_SOCKPROTO_RAW 0 +#define NFC_SOCKPROTO_LLCP 1 +#define NFC_SOCKPROTO_MAX 2 + +#define NFC_HEADER_SIZE 1 + +#endif /*__LINUX_NFC_H */ diff --git a/include/plugin.h b/include/plugin.h new file mode 100644 index 0000000..609775d --- /dev/null +++ b/include/plugin.h @@ -0,0 +1,95 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef __NEAR_PLUGIN_H +#define __NEAR_PLUGIN_H + +#include <near/version.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define NEAR_PLUGIN_PRIORITY_LOW -100 +#define NEAR_PLUGIN_PRIORITY_DEFAULT 0 +#define NEAR_PLUGIN_PRIORITY_HIGH 100 + +/** + * SECTION:plugin + * @title: Plugin premitives + * @short_description: Functions for declaring plugins + */ + +struct near_plugin_desc { + const char *name; + const char *description; + const char *version; + int priority; + int (*init) (void); + void (*exit) (void); +}; + +/** + * NEAR_PLUGIN_DEFINE: + * @name: plugin name + * @description: plugin description + * @version: plugin version string + * @init: init function called on plugin loading + * @exit: exit function called on plugin removal + * + * Macro for defining a plugin descriptor + * + * |[ + * #include <near/plugin.h> + * + * static int example_init(void) + * { + * return 0; + * } + * + * static void example_exit(void) + * { + * } + * + * NEAR_PLUGIN_DEFINE(example, "Example plugin", NEAR_VERSION, + * example_init, example_exit) + * ]| + */ +#ifdef NEAR_PLUGIN_BUILTIN +#define NEAR_PLUGIN_DEFINE(name, description, version, priority, init, exit) \ + struct near_plugin_desc __near_builtin_ ## name = { \ + #name, description, version, priority, init, exit \ + }; +#else +#define NEAR_PLUGIN_DEFINE(name, description, version, priority, init, exit) \ + extern struct near_plugin_desc near_plugin_desc \ + __attribute__ ((visibility("default"))); \ + struct near_plugin_desc near_plugin_desc = { \ + #name, description, version, priority, init, exit \ + }; +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __NEAR_PLUGIN_H */ diff --git a/include/setting.h b/include/setting.h new file mode 100644 index 0000000..8f7a26a --- /dev/null +++ b/include/setting.h @@ -0,0 +1,35 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __NEAR_SETTING_H +#define __NEAR_SETTING_H + +#ifdef __cplusplus +extern "C" { +#endif + +near_bool_t near_setting_get_bool(const char *key); + +#ifdef __cplusplus +} +#endif + +#endif /* __NEAR_SETTING_H */ diff --git a/include/tag.h b/include/tag.h new file mode 100644 index 0000000..7a2ca6e --- /dev/null +++ b/include/tag.h @@ -0,0 +1,113 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __NEAR_TAG_H +#define __NEAR_TAG_H + +#include <stdint.h> + +#include <glib.h> + +#define NFC_HEADER_SIZE 1 + +#define NFC_MAX_NFCID1_LEN 10 + +enum near_tag_sub_type { + NEAR_TAG_NFC_T2_MIFARE_ULTRALIGHT = 0, // SAK 0x00 + NEAR_TAG_NFC_T2_MIFARE_CLASSIC_1K, // SAK:0x08 + NEAR_TAG_NFC_T2_MIFARE_MINI, // SAK 0x09 + NEAR_TAG_NFC_T2_MIFARE_CLASSIC_4K, // SAK:0x18 + NEAR_TAG_NFC_T2_MIFARE_DESFIRE, // SAK:0x20 + NEAR_TAG_NFC_T2_JCOP30, // SAK:0x28 + NEAR_TAG_NFC_T2_MIFARE_4K_EMUL, // SAK:0x38 + NEAR_TAG_NFC_T2_MIFARE_1K_INFINEON, // SAK:0x88 + NEAR_TAG_NFC_T2_MPCOS, // SAK:0x98 + NEAR_TAG_NFC_SUBTYPE_UNKNOWN = 0xFF +}; + +enum near_tag_memory_layout { + NEAR_TAG_MEMORY_STATIC = 0, + NEAR_TAG_MEMORY_DYNAMIC, + NEAR_TAG_MEMORY_OTHER, + NEAR_TAG_MEMORY_UNKNOWN = 0xFF +}; + +typedef void (*near_tag_io_cb) (uint32_t adapter_idx, uint32_t target_idx, + int status); + +struct near_ndef_message; + +#define NEAR_TAG_PRIORITY_LOW -100 +#define NEAR_TAG_PRIORITY_DEFAULT 0 +#define NEAR_TAG_PRIORITY_HIGH 100 + +struct near_tag_driver { + uint16_t type; + int priority; + + int (*read)(uint32_t adapter_idx, uint32_t target_idx, + near_tag_io_cb cb); + int (*write)(uint32_t adapter_idx, uint32_t target_idx, + struct near_ndef_message *ndef, + near_tag_io_cb cb); + int (*check_presence)(uint32_t adapter_idx, uint32_t target_idx, + near_tag_io_cb cb); + int (*format)(uint32_t adapter_idx, uint32_t target_idx, + near_tag_io_cb cb); +}; + +struct near_tag; + +struct near_tag *near_tag_get_tag(uint32_t adapter_idx, uint32_t target_idx); +void near_tag_set_ro(struct near_tag *tag, near_bool_t readonly); +void near_tag_set_blank(struct near_tag *tag, near_bool_t blank); +near_bool_t near_tag_get_blank(struct near_tag *tag); +int near_tag_add_data(uint32_t adapter_idx, uint32_t target_idx, + uint8_t *data, size_t data_length); +int near_tag_add_records(struct near_tag *tag, GList *records, + near_tag_io_cb cb, int status); +enum near_tag_sub_type near_tag_get_subtype(uint32_t adapter_idx, + uint32_t target_idx); +uint8_t *near_tag_get_nfcid(uint32_t adapter_idx, uint32_t target_idx, + uint8_t *nfcid_len); +int near_tag_set_nfcid(uint32_t adapter_idx, uint32_t target_idx, + uint8_t *nfcid, size_t nfcid_len); +uint8_t *near_tag_get_data(struct near_tag *tag, size_t *data_length); +uint32_t near_tag_get_adapter_idx(struct near_tag *tag); +uint32_t near_tag_get_target_idx(struct near_tag *tag); +int near_tag_add_ndef(struct near_tag *tag, uint8_t *ndef_data, size_t ndef_length); +int near_tag_driver_register(struct near_tag_driver *driver); +void near_tag_driver_unregister(struct near_tag_driver *driver); +void near_tag_set_memory_layout(struct near_tag *tag, + enum near_tag_memory_layout); +enum near_tag_memory_layout near_tag_get_memory_layout(struct near_tag *tag); +void near_tag_set_max_ndef_size(struct near_tag *tag, uint16_t size); +uint16_t near_tag_get_max_ndef_size(struct near_tag *tag); +void near_tag_set_c_apdu_max_size(struct near_tag *tag, uint16_t size); +uint16_t near_tag_get_c_apdu_max_size(struct near_tag *tag); +void near_tag_set_idm(struct near_tag *tag, uint8_t *idm, uint8_t len); +uint8_t *near_tag_get_idm(struct near_tag *tag, uint8_t *len); +void near_tag_set_attr_block(struct near_tag *tag, uint8_t *attr, uint8_t len); +uint8_t *near_tag_get_attr_block(struct near_tag *tag, uint8_t *len); +void near_tag_set_ic_type(struct near_tag *tag, uint8_t ic_type); +uint8_t near_tag_get_ic_type(struct near_tag *tag); + +#endif diff --git a/include/tlv.h b/include/tlv.h new file mode 100644 index 0000000..2142512 --- /dev/null +++ b/include/tlv.h @@ -0,0 +1,37 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __NEAR_TLV_H +#define __NEAR_TLV_H + +#define TLV_NULL 0x00 +#define TLV_LOCK 0x01 +#define TLV_MEM 0x02 +#define TLV_NDEF 0x03 +#define TLV_PROP 0xfd +#define TLV_END 0xfe + +uint16_t near_tlv_length(uint8_t *tlv); +uint8_t *near_tlv_next(uint8_t *tlv); +uint8_t *near_tlv_data(uint8_t *tlv); +GList *near_tlv_parse(uint8_t *tlv, size_t tlv_length); + +#endif diff --git a/include/types.h b/include/types.h new file mode 100644 index 0000000..bd5b53d --- /dev/null +++ b/include/types.h @@ -0,0 +1,35 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __NEAR_TYPES_H +#define __NEAR_TYPES_H + +#ifndef FALSE +#define FALSE (0) +#endif + +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +typedef int near_bool_t; + +#endif diff --git a/include/version.h.in b/include/version.h.in new file mode 100644 index 0000000..2119dcc --- /dev/null +++ b/include/version.h.in @@ -0,0 +1,35 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __NEAR_VERSION_H +#define __NEAR_VERSION_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define NEAR_VERSION "@VERSION@" + +#ifdef __cplusplus +} +#endif + +#endif /* __NEAR_VERSION_H */ diff --git a/neard.pc.in b/neard.pc.in new file mode 100644 index 0000000..3a46466 --- /dev/null +++ b/neard.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +plugindir=${libdir}/neard/plugins + +Name: neard +Description: NFC daemon +Requires: glib-2.0 dbus-1 libnl +Version: @VERSION@ +Libs: -module -avoid-version -export-symbols-regex neard_plugin_desc +Cflags: -I${includedir}
\ No newline at end of file diff --git a/packaging/init b/packaging/init new file mode 100644 index 0000000..759a352 --- /dev/null +++ b/packaging/init @@ -0,0 +1,3 @@ +#!/bin/sh + +/usr/libexec/neard diff --git a/packaging/neard.changes b/packaging/neard.changes new file mode 100644 index 0000000..e3af7b5 --- /dev/null +++ b/packaging/neard.changes @@ -0,0 +1,24 @@ +* Tue Sep 11 2012 arron.wang <arron.wang@intel.com> submit/2.0_beta/20120831.083207@3ecb862 +- Update License Info + +* Tue Aug 28 2012 Arron < arron.wang@intel.com> - 0.6 +- Upgrade to version 0.6 + +* Mon Aug 6 10:39:42 CST 2012 Arron <arron.wang@intel.com> +- Add systemd support + +* Fri Aug 03 2012 Anas Nashif <anas.nashif@intel.com> a7864dd +- fix runtime requirements + +* Tue Jul 31 09:22:14 CST 2012 Arron <arron.wang@intel.com> - 0.5 +- Upgrade to version 0.5 + +* Tue Apr 24 2012 Arron <arron.wang@intel.com> - 0.2.26 +- Upgrade to latest version + +* Fri Apr 20 2012 Arron <arron.wang@intel.com> - 0.1.64 +- Add building require for kernel-adaptation-bb-devel to fix the building error + +* Fri Mar 30 2012 Arron <arron.wang@intel.com> - 0.1.64 +- Init package for neard + diff --git a/packaging/neard.service b/packaging/neard.service new file mode 100644 index 0000000..5ea1e81 --- /dev/null +++ b/packaging/neard.service @@ -0,0 +1,11 @@ +[Unit] +Description=NFC Manager Daemon +Before=network.target + +[Service] +Type=dbus +BusName=org.neard +ExecStart=/usr/libexec/neard -n + +[Install] +WantedBy=multi-user.target diff --git a/packaging/neard.spec b/packaging/neard.spec new file mode 100644 index 0000000..fcdd501 --- /dev/null +++ b/packaging/neard.spec @@ -0,0 +1,99 @@ +Name: neard-tizen +Summary: Near Field Communication Manager +Version: 0.8 +Release: 1 +Group: System/Networking +License: GPLv2 +Source0: http://www.kernel.org/pub/linux/network/nfc/neard-%{version}.tar.bz2 +Source1: init +Source2: neard.service +Requires(post): /bin/ln +BuildRequires: pkgconfig(glib-2.0) +BuildRequires: pkgconfig(dbus-1) +BuildRequires: pkgconfig(libnl-2.0) + +Requires: systemd +Requires(post): systemd +Requires(preun): systemd +Requires(postun): systemd + +%description +Near Field Communication Manager + + + +%package devel +Summary: Development files for NFC Manager +Group: Development/Libraries +Requires: %{name} = %{version}-%{release} + +%description devel +neard-devel contains development files for use with neard. + +%package test +Summary: Test Scripts for NFC Manager +Group: Development/Tools +Requires: %{name} = %{version}-%{release} +Requires: dbus-python +Requires: pygobject + +%description test +Scripts for testing neard and its functionality + +%prep +%setup -q + +%build +./bootstrap +%configure \ + --enable-debug \ + --prefix=/usr \ + --sysconfdir=/etc \ + --enable-nfctype1=builtin \ + --enable-nfctype2=builtin \ + --enable-nfctype3=builtin \ + --enable-nfctype4=builtin \ + --disable-p2p \ + --enable-test + +make %{?jobs:-j%jobs} + +%install +%make_install + +mkdir -p %{buildroot}/etc/rc.d/init.d +cp %{SOURCE1} %{buildroot}/etc/rc.d/init.d/neard +chmod +x %{buildroot}/etc/rc.d/init.d/neard + +# Systemd service file +install -d %{buildroot}%{_libdir}/systemd/system/ +install -m 644 %{S:2} %{buildroot}%{_libdir}/systemd/system/neard.service +install -d %{buildroot}%{_libdir}/systemd/system/network.target.wants/ +ln -s ../neard.service %{buildroot}%{_libdir}/systemd/system/network.target.wants/neard.service + +%post +ln -sf ../init.d/neard /etc/rc.d/rc3.d/S64neard +systemctl daemon-reload +systemctl restart neard.service + +%preun +systemctl stop neard.service + +%postun +systemctl daemon-reload + +%files +%doc COPYING +/usr/libexec/neard +/etc/dbus-1/system.d/org.neard.conf +/etc/rc.d/init.d/* +%{_libdir}/systemd/system/neard.service +%{_libdir}/systemd/system/network.target.wants/neard.service + +%files devel +%{_includedir}/near/*.h +%{_libdir}/pkgconfig/*.pc + +%files test +%defattr(-,root,root,-) +%{_libdir}/neard/test/* diff --git a/plugins/handover.c b/plugins/handover.c new file mode 100644 index 0000000..0e148bd --- /dev/null +++ b/plugins/handover.c @@ -0,0 +1,509 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdint.h> +#include <errno.h> +#include <string.h> +#include <sys/socket.h> + +#include <linux/socket.h> + +#include <near/nfc.h> +#include <near/types.h> +#include <near/log.h> +#include <near/adapter.h> +#include <near/device.h> +#include <near/tag.h> +#include <near/ndef.h> +#include <near/tlv.h> + +#include "p2p.h" + +#define NDEF_HR_MSG_MIN_LENGTH 0x06 +#define HR_HEADER_SIZE 6 /* header (1) + type len (1) + + * payload len (1) + rec type (2) 'Hx' + * + version (1) + */ + +#define RECORD_TYPE_WKT_ALTERNATIVE_CARRIER 0x0a +#define FRAME_TYPE_OFFSET 3 + +enum loop_stage_flag { + STATE_MAIN_NDEF = 0x00, + STATE_CFG_RECORD = 0x01, +}; + +static GHashTable *hr_ndef_hash = NULL; + +struct extra_ndef { + uint8_t *ndef; + uint8_t length; +}; + +struct hr_ndef { + uint8_t *ndef; + uint16_t cur_ptr; + int cur_record_len; + int missing_bytes; + uint32_t adapter_idx; + uint32_t target_idx; + near_tag_io_cb cb; + int extra_ndef_count; + int block_free_size; + near_bool_t cfg_record_state; + near_bool_t in_extra_read; +}; + +struct hr_push_client { + uint8_t fd; + uint32_t adapter_idx; + uint32_t target_idx; + near_device_io_cb cb; + guint watch; +}; + +static void free_hr_ndef(gpointer data) +{ + struct hr_ndef *ndef = data; + + if (ndef != NULL) + g_free(ndef->ndef); + + g_free(ndef); +} + +static void handover_close(int client_fd, int err) +{ + struct hr_ndef *ndef; + + DBG(""); + + ndef = g_hash_table_lookup(hr_ndef_hash, GINT_TO_POINTER(client_fd)); + if (ndef == NULL) + return; + + g_hash_table_remove(hr_ndef_hash, GINT_TO_POINTER(client_fd)); +} + +/* Parse an incoming handover buffer*/ +static int handover_ndef_parse(int client_fd, struct hr_ndef *ndef) +{ + int err; + GList *records; + struct near_ndef_message *msg; + + DBG(""); + + if ((ndef->ndef == NULL) || + (ndef->cur_ptr < NDEF_HR_MSG_MIN_LENGTH)) { + err = -EINVAL; + goto fail; + } + + /* call the global parse function */ + records = near_ndef_parse_msg(ndef->ndef, ndef->cur_ptr); + if (records == NULL) { + err = -ENOMEM; + goto fail; + } + + /* + * If we receive a request, we should reply with a Hs but + * if the initial frame is Hs (it means we initiated the + * exchange with a Hr), so we have to do some actions (e.g.: + * pairing with bluetooth) + */ + if (strncmp((char *) (ndef->ndef + FRAME_TYPE_OFFSET), "Hr", 2) == 0) { + /* + * The first entry on the record list is the Hr record. + * We build the Hs based on it. + */ + msg = near_ndef_prepare_handover_record("Hs", records->data, + NEAR_CARRIER_UNKNOWN); + if (msg == NULL) { + err = -EINVAL; + goto fail; + } + + near_info("Send Hs frame"); + err = send(client_fd, msg->data, msg->length, MSG_DONTWAIT); + + g_free(msg->data); + g_free(msg); + } else { + err = 0; + } + + near_ndef_records_free(records); + + return err; + +fail: + near_error("ndef parsing failed %d", err); + + handover_close(client_fd, 0); + + return err; +} + +static near_bool_t handover_recv_error(void) +{ + near_error("%s", strerror(errno)); + + if (errno == EAGAIN) + return TRUE; + + return FALSE; +} + +/* Add extra records right after the end of the "Hr" ndef record */ +static near_bool_t handover_read_cfg_records(int client_fd, + uint32_t adapter_idx, uint32_t target_idx, + near_tag_io_cb cb) +{ + struct hr_ndef *ndef; + int bytes_recv; + int ndef_size; + int err; + + ndef = g_hash_table_lookup(hr_ndef_hash, GINT_TO_POINTER(client_fd)); + if (ndef == NULL) { + near_error("hr_ndef should exist"); + return FALSE; + } + + if (ndef->in_extra_read == TRUE) { + /* Next prepare read to complete the Hr */ + ndef->ndef = g_try_realloc(ndef->ndef, ndef->cur_record_len + + NDEF_HR_MSG_MIN_LENGTH); + if (ndef == NULL) + return FALSE; + + /* Read header bytes */ + bytes_recv = recv(client_fd, ndef->ndef + ndef->cur_ptr, + NDEF_HR_MSG_MIN_LENGTH, MSG_DONTWAIT); + if (bytes_recv < 0) + return handover_recv_error(); + + /* Now, check the ndef payload size plus header bytes */ + ndef_size = near_ndef_record_length(ndef->ndef + ndef->cur_ptr, + bytes_recv); + if (ndef_size < 0) + goto fail; + + ndef->cur_ptr += bytes_recv; + ndef->missing_bytes = ndef_size - bytes_recv; + + /* Next prepare read to complete the NDEF */ + ndef->ndef = g_try_realloc(ndef->ndef, ndef->cur_record_len + + ndef_size); + if (ndef->ndef == NULL) { + g_free(ndef); + return FALSE; + } + ndef->cur_record_len += ndef_size; + ndef->in_extra_read = FALSE; + + return TRUE; + } + + /* Read remaining bytes */ + bytes_recv = recv(client_fd, ndef->ndef + ndef->cur_ptr, + ndef->missing_bytes, MSG_DONTWAIT); + if (bytes_recv < 0) + return handover_recv_error(); + + ndef->cur_ptr += bytes_recv; + ndef->missing_bytes -= bytes_recv; + + /* Is the NDEF read complete ? */ + if (ndef->missing_bytes) + return TRUE; /* more bytes to come... */ + + ndef->extra_ndef_count--; + ndef->in_extra_read = TRUE; + + if (ndef->extra_ndef_count == 0) { + /* All the bytes are read so now, parse the frame */ + err = handover_ndef_parse(client_fd, ndef); + if (err > 0) { + /* clean memory */ + handover_close(client_fd, 0); + return TRUE; + } + + return FALSE; + } + + /* Process the next NDEF */ + return TRUE; + +fail: + near_error("Handover read NDEFs failed"); + return FALSE; +} + +static near_bool_t handover_read_hr(int client_fd, + uint32_t adapter_idx, uint32_t target_idx, near_tag_io_cb cb) +{ + int bytes_recv; + int extra_ndefs; + struct hr_ndef *ndef; + + DBG(""); + + ndef = g_hash_table_lookup(hr_ndef_hash, GINT_TO_POINTER(client_fd)); + if (ndef == NULL) + return FALSE; + + /* Read remaining bytes */ + bytes_recv = recv(client_fd, ndef->ndef + ndef->cur_ptr, + ndef->missing_bytes, MSG_DONTWAIT); + if (bytes_recv < 0) + return handover_recv_error(); + + ndef->cur_ptr += bytes_recv; + ndef->missing_bytes -= bytes_recv; + + /* Is the ndef "Hr" read complete or should we loop */ + if (ndef->missing_bytes) + return TRUE; + + /* + * The first NDEF frame is read. We now should determine how many + * extra records follow the NDEF frame. + * We skip the first 6 bytes (Hr header) to jump on the first record + */ + extra_ndefs = near_ndef_count_records(ndef->ndef + HR_HEADER_SIZE, + ndef->cur_record_len - HR_HEADER_SIZE, + RECORD_TYPE_WKT_ALTERNATIVE_CARRIER); + if (extra_ndefs < 0) + goto fail; + + /* There's still some extra ndefs to read */ + ndef->extra_ndef_count = extra_ndefs; + + /* End of Handover message - now process extra records */ + ndef->in_extra_read = TRUE; + ndef->cfg_record_state = TRUE; + + /* But, if there's no ac record, we jump to the parsing */ + if (ndef->extra_ndef_count == 0) { + handover_ndef_parse(client_fd, ndef); + return FALSE; + } + + return TRUE; + +fail: + near_error("Handover read failed"); + return FALSE; +} + +static near_bool_t handover_read_initialize(int client_fd, + uint32_t adapter_idx, uint32_t target_idx, near_tag_io_cb cb) +{ + int bytes_recv; + struct hr_ndef *ndef; + + DBG(""); + + /* Allocate the ndef structure */ + ndef = g_try_malloc0(sizeof(struct hr_ndef)); + if (ndef == NULL) + goto fail; + + /* Allocate and read frame header (6 bytes) */ + ndef->ndef = g_try_malloc0(NDEF_HR_MSG_MIN_LENGTH); + if (ndef->ndef == NULL) + goto fail; + + /* Initialize default values */ + ndef->cur_ptr = 0; + ndef->cur_record_len = -1; + ndef->adapter_idx = adapter_idx; + ndef->target_idx = target_idx; + ndef->cb = cb; + ndef->cfg_record_state = FALSE; + + g_hash_table_insert(hr_ndef_hash, GINT_TO_POINTER(client_fd), ndef); + + /* Read header bytes (6) */ + bytes_recv = recv(client_fd, ndef->ndef, + NDEF_HR_MSG_MIN_LENGTH, MSG_DONTWAIT); + if (bytes_recv < 0) + return handover_recv_error(); + + /* Now, check the ndef payload size plus header bytes */ + ndef->cur_record_len = near_ndef_record_length(ndef->ndef, bytes_recv); + if (ndef->cur_record_len < 0) + goto fail; + + ndef->cur_ptr += bytes_recv; + ndef->missing_bytes = ndef->cur_record_len - bytes_recv; + + DBG("Handover frame size is %d", ndef->cur_ptr); + + /* Next prepare read to complete the read */ + ndef->ndef = g_try_realloc(ndef->ndef, ndef->cur_record_len); + if (ndef->ndef == NULL) + goto fail; + + return TRUE; + +fail: + free_hr_ndef(ndef); + + return FALSE; +} + +/* + * This function is a "dispatcher", to read Hr/Hs messages, + * and/or additional NDEF messages + */ +static near_bool_t handover_read(int client_fd, + uint32_t adapter_idx, uint32_t target_idx, + near_tag_io_cb cb) +{ + struct hr_ndef *ndef; + + ndef = g_hash_table_lookup(hr_ndef_hash, GINT_TO_POINTER(client_fd)); + if (ndef == NULL) { + /* First call: allocate and read header bytes */ + return handover_read_initialize(client_fd, adapter_idx, + target_idx, cb); + } + + if (ndef->cfg_record_state == TRUE) { + return handover_read_cfg_records(client_fd, adapter_idx, + target_idx, cb); + } + + return handover_read_hr(client_fd, adapter_idx, target_idx, cb); +} + +static void free_hr_push_client(struct hr_push_client *client, int status) +{ + DBG(""); + + handover_close(client->fd, 0); + + if (client->cb) + client->cb(client->adapter_idx, client->target_idx, status); + + if (client->watch > 0) + g_source_remove(client->watch); + + g_free(client); +} + +static gboolean handover_push_event(GIOChannel *channel, + GIOCondition condition, gpointer data) +{ + near_bool_t ret; + struct hr_push_client *client = (struct hr_push_client *) data; + + DBG("condition 0x%x", condition); + + if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + near_error("Error with Handover client"); + + free_hr_push_client(client, -EIO); + + return FALSE; + } + + ret = handover_read(client->fd, + client->adapter_idx, client->target_idx, + client->cb); + + if (ret == FALSE) + free_hr_push_client(client, 0); + + return ret; +} + +static int handover_push(int client_fd, + uint32_t adapter_idx, uint32_t target_idx, + struct near_ndef_message *ndef, + near_device_io_cb cb) +{ + int err; + struct hr_push_client *client; + GIOChannel *channel; + + DBG(""); + + client = g_try_malloc0(sizeof(struct hr_push_client)); + if (client == NULL) + return -ENOMEM; + + channel = g_io_channel_unix_new(client_fd); + g_io_channel_set_close_on_unref(channel, TRUE); + + client->fd = client_fd; + client->adapter_idx = adapter_idx; + client->target_idx = target_idx; + client->cb = cb; + client->watch = g_io_add_watch(channel, + G_IO_IN | G_IO_HUP | G_IO_NVAL | + G_IO_ERR, handover_push_event, + (gpointer) client); + + g_io_channel_unref(channel); + + err = send(client_fd, ndef->data, ndef->length, MSG_DONTWAIT); + if (err < 0) { + free_hr_push_client(client, err); + g_io_channel_unref(channel); + } + + return err; +} + +struct near_p2p_driver handover_driver = { + .name = "Handover", + .service_name = NEAR_DEVICE_SN_HANDOVER, + .fallback_service_name = NEAR_DEVICE_SN_SNEP, + .read = handover_read, + .push = handover_push, + .close = handover_close, +}; + +int handover_init(void) +{ + hr_ndef_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, free_hr_ndef); + + return near_p2p_register(&handover_driver); +} + +void handover_exit(void) +{ + near_p2p_unregister(&handover_driver); + + g_hash_table_destroy(hr_ndef_hash); + hr_ndef_hash = NULL; +} diff --git a/plugins/mifare.c b/plugins/mifare.c new file mode 100644 index 0000000..368d470 --- /dev/null +++ b/plugins/mifare.c @@ -0,0 +1,1343 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdint.h> +#include <errno.h> +#include <string.h> +#include <sys/socket.h> + +#include <linux/socket.h> + +#include <near/nfc.h> +#include <near/plugin.h> +#include <near/log.h> +#include <near/types.h> +#include <near/adapter.h> +#include <near/tag.h> +#include <near/ndef.h> +#include <near/tlv.h> + +/* + * NXP Application Notes: + * AN1304, AN1305, ... + * http://www.nxp.com/technical-support-portal/53420/71108/application-notes + */ + +/* Prototypes */ +int mifare_read(uint32_t adapter_idx, uint32_t target_idx, + near_tag_io_cb cb, enum near_tag_sub_type tgt_subtype); + +int mifare_check_presence(uint32_t adapter_idx, uint32_t target_idx, + near_tag_io_cb cb, enum near_tag_sub_type tgt_subtype); + +int mifare_write(uint32_t adapter_idx, uint32_t target_idx, + struct near_ndef_message *ndef, + near_tag_io_cb cb, enum near_tag_sub_type tgt_subtype); + +/* MIFARE command set */ +#define MF_CMD_WRITE 0xA0 +#define MF_CMD_READ 0x30 +#define MF_CMD_AUTH_KEY_A 0x60 + +#define NFC_AID_TAG 0xE103 + +/* + * Define boundaries for 1K / 2K / 4K + * 1K: sector 0 to 15 (3 blocks each + trailer block ) + * 2K: sector 0 to 31 (3 blocks each + trailer block ) + * 4K: sector 0 to 31 (3 blocks each + trailer block ) + * and sector 32 to 39 (15 blocks each + trailer block ) + */ +#define DEFAULT_BLOCK_SIZE 16 /* MF_CMD_READ */ + +#define STD_BLK_SECT_TRAILER 4 /* bl per sect with trailer 1K/2K */ +#define EXT_BLK_SECT_TRAILER 16 /* bl per sect with trailer 4K */ + +#define STD_BLK_PER_SECT 3 /* 1 sect == 3blocks */ +#define EXT_BLK_PER_SECT 15 /* for 4K tags */ + +/* Usual sector size, including trailer */ +#define STD_SECTOR_SIZE (4 * DEFAULT_BLOCK_SIZE) /* 00-31 */ +#define EXT_SECTOR_SIZE (16 * DEFAULT_BLOCK_SIZE) /* 32-39 */ + +/* Usual sector size, without trailer */ +#define SECTOR_SIZE (3 * DEFAULT_BLOCK_SIZE) +#define BIG_SECTOR_SIZE (15 * DEFAULT_BLOCK_SIZE) + +#define T4K_BOUNDARY 32 +#define T4K_BLK_OFF 0x80 /* blocks count before sector 32 */ + +#define NO_TRAILER 0 +#define WITH_TRAILER 1 +#define SECT_IS_NFC 1 + +/* Default MAD keys. Key length = 6 bytes */ +#define MAD_KEY_LEN 6 +static uint8_t MAD_public_key[] = {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5}; +static uint8_t MAD_NFC_key[] = {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}; + +#define MAD1_SECTOR 0x00 /* Sector 0 is for MAD1 */ +#define MAD1_1ST_BLOCK 0x00 /* 1st block of sector 0 */ +#define MAD2_GPB_BITS 0x02 /* MAD v2 flag */ + +#define MAD2_SECTOR 0x10 /* Sector 16 is for MAD2 */ +#define MAD2_1ST_BLOCK 0x40 /* 1st block of MAD2 */ + +#define MAD_V1_AIDS_LEN 15 /* 1 to 0x0F */ +#define MAD_V2_AIDS_LEN 23 /*0x11 to 0x27 */ + +#define NFC_1ST_BLOCK 0x04 /* Sectors from 1 are for NFC */ + +#define ACC_BITS_LEN 3 + +/* Access bits for data blocks mask */ +static uint8_t DATA_access_mask[] = {0x77, 0x77, 0x77}; + +/* Write with key A access bits configuration */ +static uint8_t WRITE_with_key_A[] = {0x77, 0x07, 0x00}; + +/* MAD1 sector structure. Start at block 0x00 */ +struct MAD_1 { + uint8_t man_info[16]; + uint16_t crc_dir; + uint16_t aids[MAD_V1_AIDS_LEN]; + /* Trailer */ + uint8_t key_A[MAD_KEY_LEN]; + uint8_t access_cond[3]; + uint8_t GPB; + uint8_t key_B[MAD_KEY_LEN]; +} __attribute__((packed)); + +/* MAD2 sector structure. Start at block 0x40 */ +struct MAD_2 { + uint16_t crc_dir; + uint16_t aids[MAD_V2_AIDS_LEN]; + /* Trailer */ + uint8_t key_A[MAD_KEY_LEN]; + uint8_t access_cond[3]; + uint8_t GPB; + uint8_t key_B[MAD_KEY_LEN]; +} __attribute__((packed)); + +struct mifare_cookie { + uint32_t adapter_idx; + uint32_t target_idx; + uint8_t *nfcid1; + uint8_t nfcid1_len; + + struct near_tag *tag; + near_tag_io_cb cb; + near_recv next_far_func; + + /* For MAD access */ + struct MAD_1 *mad_1; + struct MAD_2 *mad_2; + GSList *g_sect_list; /* Global sectors list */ + + /* For read and write functions */ + near_recv rws_next_fct; /* next function */ + int rws_block_start; /* first block */ + int rws_block_end; /* last block */ + int rws_completed; /* read blocks */ + + + /* For read only */ + int rs_length; /* read length */ + uint8_t *rs_pmem; /* Stored read sector */ + int rs_max_length; /* available size */ + uint8_t *nfc_data; + size_t nfc_data_length; + + /* For write only */ + struct near_ndef_message *ndef; /* message to write */ + size_t ndef_length; /* message length */ + + /* For access check */ + int (*acc_check_function)(void *data); /* acc check fnc */ + uint8_t *acc_bits_mask; /* blocks to check */ + uint8_t *acc_rights; /* condition */ + int (*acc_denied_fct)(void *data);/* fnc to call on access denial */ + GSList *acc_sect; /* sector from g_sect_list to check */ +}; + +struct type2_cmd { + uint8_t cmd; + uint8_t block; + uint8_t data[]; +} __attribute__((packed)); + +struct mf_write_cmd { + uint8_t cmd; + uint8_t block; + uint8_t data[DEFAULT_BLOCK_SIZE]; +} __attribute__((packed)); + +struct mifare_cmd { + uint8_t cmd; + uint8_t block; + uint8_t key[MAD_KEY_LEN]; + uint8_t nfcid[NFC_NFCID1_MAXSIZE]; +} __attribute__((packed)); + +static int mifare_release(int err, void *data) +{ + struct mifare_cookie *cookie = data; + + DBG("%p", cookie); + + if (cookie == NULL) + return err; + + if (err < 0 && cookie->cb) { + cookie->cb(cookie->adapter_idx, cookie->target_idx, err); + near_adapter_disconnect(cookie->adapter_idx); + } + + /* Now free allocs */ + g_free(cookie->nfcid1); + g_slist_free(cookie->g_sect_list); + g_free(cookie->mad_1); + g_free(cookie->mad_2); + + if (cookie->ndef) + g_free(cookie->ndef->data); + + g_free(cookie->ndef); + g_free(cookie); + cookie = NULL; + + return err; +} + +/* + * Mifare_generic MAD unlock block function + * This function send unlock code to the tag, and so, allow access + * to the complete related sector. + */ +static int mifare_unlock_sector(int block_id, + near_recv next_far_fct, + void *data) +{ + struct mifare_cmd cmd; + struct mifare_cookie *cookie = data; + uint8_t *key_ref; + + /* + * For MADs sectors we use public key A (a0a1a2a3a4a5) but +- * for NFC sectors we use NFC_KEY_A (d3f7d3f7d3f7) + */ + if ((block_id == MAD1_1ST_BLOCK) || (block_id == MAD2_1ST_BLOCK)) + key_ref = MAD_public_key; + else + key_ref = MAD_NFC_key; + + /* CMD AUTHENTICATION */ + cmd.cmd = MF_CMD_AUTH_KEY_A; + + /* Authenticate will be on the 1st block of the sector */ + cmd.block = block_id; + + /* Store the AUTH KEY */ + memcpy(&cmd.key, key_ref, MAD_KEY_LEN); + + /* add the UID */ + memcpy(&cmd.nfcid, cookie->nfcid1, cookie->nfcid1_len); + + return near_adapter_send(cookie->adapter_idx, (uint8_t *)&cmd, + sizeof(cmd) - NFC_NFCID1_MAXSIZE + cookie->nfcid1_len, + next_far_fct, cookie, mifare_release); +} + +/* + * Common MIFARE Block read: + * Each call will read 16 bytes from tag... so to read 1 sector, + * it has to be called it 4 times or 16 times + * (minus 1 or not for the trailer block) + * + * data: mifare_cookie *mf_ck + * mf_ck->read_block: block number to read + */ +static int mifare_read_block(uint8_t block_id, + void *data, + near_recv far_func) +{ + struct type2_cmd cmd; + struct mifare_cookie *mf_ck = data; + + cmd.cmd = MF_CMD_READ; /* MIFARE READ */ + cmd.block = block_id; + + return near_adapter_send(mf_ck->adapter_idx, (uint8_t *) &cmd, 2, + far_func, mf_ck, mifare_release); +} + +/* + * Check access rights + * Function processes sector trailer received from tag and checks access rights. + * In case specified access isn't granted it calls appropriate + * access denial function. + * If access is granted, previous action (e.g. read, write) is continued. + */ +static int mifare_check_rights_cb(uint8_t *resp, int length, void *data) +{ + struct mifare_cookie *mf_ck = data; + int err; + uint8_t *c; + int i; + + if (length < 0) { + err = length; + goto out_err; + } + + /* skip reader byte and key A */ + c = resp + 1 + MAD_KEY_LEN; + + for (i = 0; i < ACC_BITS_LEN; i++) { + if ((c[i] & mf_ck->acc_bits_mask[i]) != mf_ck->acc_rights[i]) { + (*mf_ck->acc_denied_fct)(data); + return 0; + } + } + + /* Continue previous action (read/write) */ + err = (*mf_ck->rws_next_fct)(resp, length, data); + + if (err < 0) + goto out_err; + + return err; + +out_err: + return mifare_release(err, mf_ck); +} + +/* Calls to mifare_read_block to get sector trailer */ +static int mifare_check_rights(uint8_t *resp, int length, void *data) +{ + struct mifare_cookie *mf_ck = data; + int err; + + err = mifare_read_block(mf_ck->rws_block_start, mf_ck, + mifare_check_rights_cb); + + if (err < 0) + return mifare_release(err, mf_ck); + + return err; +} + +static int mifare_read_sector_cb(uint8_t *resp, int length, void *data) +{ + struct mifare_cookie *mf_ck = data; + int err = -1; + + if (length < 0) { + err = length; + goto out_err; + } + + /* Save the data */ + length = length - 1; /* ignore first byte - Reader byte */ + + /* save the length: */ + mf_ck->rs_length = mf_ck->rs_length + length; + + memcpy(mf_ck->rs_pmem + mf_ck->rws_completed * DEFAULT_BLOCK_SIZE, + resp + 1,/* ignore reader byte */ + length); + + /* Next block */ + mf_ck->rws_completed = mf_ck->rws_completed + 1; + + if ((mf_ck->rws_block_start + mf_ck->rws_completed) + < mf_ck->rws_block_end) + err = mifare_read_block( + (mf_ck->rws_block_start + mf_ck->rws_completed), + data, + mifare_read_sector_cb); + else { + /* Now Process the callback ! */ + err = (*mf_ck->rws_next_fct)(mf_ck->rs_pmem, + mf_ck->rs_length, data); + } + + if (err < 0) + goto out_err; + return err; + +out_err: + return mifare_release(err, mf_ck); +} + +static int mifare_read_sector_unlocked(uint8_t *resp, int length, void *data) +{ + struct mifare_cookie *mf_ck = data; + int err; + + if (length < 0) { + err = length; + goto out_err; + } + /* And run the read process on the first block of the sector */ + err = mifare_read_block(mf_ck->rws_block_start, data, + mifare_read_sector_cb); + + if (err < 0) + goto out_err; + return err; + +out_err: + return mifare_release(err, mf_ck); +} + +/* + * This function reads a complete sector, using block per block function. + * sector sizes can be: + * Sectors 0 to 31: + * 48 bytes: 3*16 no trailer + * 64 bytes: 4*16 with trailer + * Sectors 32 to 39: + * 240 bytes: 15*16 no trailer + * 256 bytes: 16*16 with trailer + * + * Unlock is done at the beginning of first sector. + */ +static int mifare_read_sector(void *cookie, + uint8_t *pmem, /* memory to fill */ + uint16_t memsize, /* remaining free size */ + uint8_t sector_id, /* sector to read */ + near_bool_t trailer, /* Add trailer or not */ + near_recv next_func) +{ + struct mifare_cookie *mf_ck = cookie; + int err; + int blocks_count; + + DBG(""); + + /* Prepare call values */ + mf_ck->rs_pmem = pmem; /* where to store */ + mf_ck->rs_max_length = memsize; /* max size to store */ + mf_ck->rs_length = 0; /* no bytes yet */ + mf_ck->rws_completed = 0; /* blocks read */ + + /* According to tag size, compute the correct block offset */ + if (sector_id < T4K_BOUNDARY) + mf_ck->rws_block_start = sector_id * 4; /* 1st block to read */ + else + mf_ck->rws_block_start = + (sector_id - T4K_BOUNDARY) * 16 + T4K_BLK_OFF; + + /* Find blocks_per_sect, according to position and trailer or not */ + if (sector_id < T4K_BOUNDARY) + blocks_count = (STD_BLK_PER_SECT + trailer); + else + blocks_count = (EXT_BLK_PER_SECT + trailer); + + mf_ck->rws_block_end = mf_ck->rws_block_start + blocks_count; + + mf_ck->rws_next_fct = next_func; /* leaving function */ + + /* Being on the first block of a sector, unlock it */ + err = mifare_unlock_sector(mf_ck->rws_block_start, + mifare_read_sector_unlocked, mf_ck); + + return err; +} + +static int mifare_read_NFC_loop(uint8_t *resp, int length, void *data) +{ + struct mifare_cookie *mf_ck = data; + int err = 0; + + DBG(""); + + if (length < 0) { + err = length; + goto out_err; + } + + /* ptr to the next read ptr */ + mf_ck->nfc_data = mf_ck->nfc_data + length; + + /* remaining free mem */ + mf_ck->nfc_data_length = mf_ck->nfc_data_length - length; + + + /* Additional sectors to read ? */; + if (mf_ck->g_sect_list != NULL && mf_ck->g_sect_list->next != NULL) { + + err = mifare_read_sector(data, /* cookie */ + mf_ck->nfc_data, /* where to store */ + (int) mf_ck->nfc_data_length, /* global length */ + GPOINTER_TO_INT(mf_ck->g_sect_list->data), /* id */ + NO_TRAILER, /* Trailer ? */ + mifare_read_NFC_loop); /* next function */ + + mf_ck->g_sect_list = g_slist_remove(mf_ck->g_sect_list, + mf_ck->g_sect_list->data); + + if (err < 0) + goto out_err; + return err; + } else { + GList *records; + uint8_t *nfc_data; + size_t nfc_data_length; + + DBG("Done reading"); + + nfc_data = near_tag_get_data(mf_ck->tag, &nfc_data_length); + if (nfc_data == NULL) { + err = -ENOMEM; + goto out_err; + } + + records = near_tlv_parse(nfc_data, nfc_data_length); + near_tag_add_records(mf_ck->tag, records, mf_ck->cb, 0); + + err = 0; + } + +out_err: + return mifare_release(err, mf_ck); +} + +/* Prepare read NFC loop */ +static int mifare_read_NFC(uint8_t *resp, int length, void *data) +{ + struct mifare_cookie *mf_ck = data; + int err; + + /* save tag memory pointer to data_block */ + mf_ck->nfc_data = near_tag_get_data(mf_ck->tag, + &mf_ck->nfc_data_length); + + /* First read here: */ + err = mifare_read_sector(data, /* cookie */ + mf_ck->nfc_data, /* where to store */ + mf_ck->nfc_data_length, /* global length */ + GPOINTER_TO_INT(mf_ck->g_sect_list->data), /* sector id */ + NO_TRAILER, /* Don't want Trailer */ + mifare_read_NFC_loop); /* next function */ + + mf_ck->g_sect_list = g_slist_remove(mf_ck->g_sect_list, + mf_ck->g_sect_list->data); + if (err < 0) + goto out_err; + return err; + +out_err: + return mifare_release(err, mf_ck); +} + +static int mifare_process_MADs(void *data) +{ + struct mifare_cookie *mf_ck = data; + int err; + int i; + int global_tag_size = 0; + int ioffset; + uint8_t *tag_data; + size_t data_size; + + DBG(""); + + /* Parse MAD entries to get the global size and fill the array */ + if (mf_ck->mad_1 == NULL) { + err = -EINVAL; + goto out_err; + } + + /* Skip non-NFC sectors at the beginning of the tag, if any */ + for (i = 0 ; i < MAD_V1_AIDS_LEN; i++) { + if (mf_ck->mad_1->aids[i] == NFC_AID_TAG) + break; + } + + /* + * NFC sectors have to be continuous, + * so only some sectors at the beginning and at the end of tag + * can be non-NFC. + */ + for (; i < MAD_V1_AIDS_LEN; i++) { + if (mf_ck->mad_1->aids[i] != NFC_AID_TAG) + goto done_mad; + + /* Save in the global list */ + mf_ck->g_sect_list = g_slist_append(mf_ck->g_sect_list, + GINT_TO_POINTER(i + 1)); + global_tag_size += SECTOR_SIZE; + } + + /* Now MAD 2 */ + ioffset = MAD_V1_AIDS_LEN + 1 + 1; /* skip 0x10 */ + if (mf_ck->mad_2 == NULL) + goto done_mad; + + /* + * If all sectors from MAD1 were non-NFC, + * skip initial non-NFC sectors from MAD2 + */ + i = 0; + + if (global_tag_size == 0) + for (; i < MAD_V2_AIDS_LEN; i++) + if (mf_ck->mad_2->aids[i] == NFC_AID_TAG) + break; + + for (; i < MAD_V2_AIDS_LEN; i++) { + if (mf_ck->mad_2->aids[i] != NFC_AID_TAG) + goto done_mad; + + mf_ck->g_sect_list = g_slist_append( mf_ck->g_sect_list, + GINT_TO_POINTER(ioffset + i)); + if (i < EXT_BLK_PER_SECT) + global_tag_size += SECTOR_SIZE; + else + global_tag_size += BIG_SECTOR_SIZE; + } + +done_mad: + if (global_tag_size == 0) { + + /* no NFC sectors - mark tag as blank */ + near_error("TAG Global size: [%d], not valid NFC tag.", + global_tag_size); + return -ENODEV; + } + + /* n sectors, each sector is 3 blocks, each block is 16 bytes */ + DBG("TAG Global size: [%d]", global_tag_size); + + mf_ck->tag = near_tag_get_tag(mf_ck->adapter_idx, mf_ck->target_idx); + if (mf_ck->tag == NULL) { + err = -ENOMEM; + goto out_err; + } + + /* don't allocate new data before writing */ + tag_data = near_tag_get_data(mf_ck->tag, &data_size); + if (tag_data == NULL) { + err = near_tag_add_data(mf_ck->adapter_idx, + mf_ck->target_idx, + NULL, /* Empty */ + global_tag_size); + + if (err < 0) + goto out_err; + } + + /* Check access rights */ + err = mf_ck->acc_check_function(data); + + if (err < 0) + goto out_err; + + return err; + +out_err: + return mifare_release(err, mf_ck); +} + +/* Transitional function - async */ +static int read_MAD2_complete(uint8_t *empty, int iempty, void *data) +{ + return mifare_process_MADs(data); +} + +/* This function reads the MAD2 sector */ +static int mifare_read_MAD2(void *data) +{ + struct mifare_cookie *mf_ck = data; + int err = 0; + + DBG(""); + + /* As auth is ok, allocate Mifare Access Directory v1 */ + mf_ck->mad_2 = g_try_malloc0(STD_SECTOR_SIZE); + if (mf_ck->mad_2 == NULL) { + near_error("Memory allocation failed (MAD2)"); + err = -ENOMEM; + goto out_err; + } + + err = mifare_read_sector(data, + (uint8_t *) mf_ck->mad_2, + (int) STD_SECTOR_SIZE, + MAD2_SECTOR, /* sector 0x10 */ + WITH_TRAILER, /* Want Trailer */ + read_MAD2_complete); + + if (err < 0) + goto out_err; + return err; + +out_err: + return mifare_release(err, mf_ck); +} + +/* + * This function checks, in MAD1, if there's a MAD2 directory + * available. This is is the case for 2K and 4K tag + * If MAD2 exists, read it, elsewhere process the current MAD + */ +static int read_MAD1_complete(uint8_t *empty, int iempty, void *data) +{ + struct mifare_cookie *mf_ck = data; + int err; + + DBG(""); + + /* Check if there's a need to get MAD2 sector */ + if ((mf_ck->mad_1->GPB & 0x03) == MAD2_GPB_BITS) + err = mifare_read_MAD2(mf_ck); + else + err = mifare_process_MADs(data); + + return err; +} + +/* + * Function called to read the first MAD sector + * MAD is mandatory + */ +static int mifare_read_MAD1(uint8_t *resp, int length, void *data) +{ + struct mifare_cookie *mf_ck = data; + int err = 0; + + DBG("%p %d", data, length); + + if (length < 0) { + err = length; + return err; + } + + /* + * As auth is ok, allocate Mifare Access Directory v1 + * allocated size is also STD_SECTOR_SIZE + */ + mf_ck->mad_1 = g_try_malloc0(STD_SECTOR_SIZE); + if (mf_ck->mad_1 == NULL) { + near_error("Memory allocation failed (MAD1)"); + err = -ENOMEM; + goto out_err; + } + + /* Call to mifare_read_sector */ + err = mifare_read_sector(data, + (uint8_t *)mf_ck->mad_1, /* where to store */ + (int) STD_SECTOR_SIZE, /* allocated size */ + MAD1_SECTOR, /* sector 0 */ + WITH_TRAILER, /* Want Trailer */ + read_MAD1_complete); + + if (err < 0) + goto out_err; + return err; + +out_err: + return mifare_release(err, mf_ck); +} + +/* If first NFC sector isn't writable, mark whole tag as read only */ +static int is_read_only(void *data) +{ + struct mifare_cookie *mf_ck = data; + + DBG("Tag is read only"); + + near_tag_set_ro(mf_ck->tag, TRUE); + + /* Continue previous action (read) */ + (*mf_ck->rws_next_fct)(NULL, 0, data); + + return 0; +} + + +static int mifare_check_read_only(void *data) +{ + struct mifare_cookie *mf_ck = data; + int err; + + DBG(""); + + /* + * As authorisation with key B is not supported, + * in case writing with key A is not permitted, tag is read-only + */ + mf_ck->acc_bits_mask = DATA_access_mask; + mf_ck->acc_rights = WRITE_with_key_A; + + /* Check acces rights of first NFC sector */ + mf_ck->rws_block_start = NFC_1ST_BLOCK + STD_BLK_PER_SECT; + /* Afterwards read tag */ + mf_ck->rws_next_fct = mifare_read_NFC; + /* In case of writing access denial, set read only */ + mf_ck->acc_denied_fct = is_read_only; + + err = mifare_unlock_sector(mf_ck->rws_block_start, + mifare_check_rights, mf_ck); + + if (err < 0) + return mifare_release(err, mf_ck); + + return err; +} + +/* + * MIFARE: entry point: + * Read all the MAD sectors (0x00, 0x10) to get the Application Directory + * entries. + * On sector 0x00, App. directory is on block 0x01 & block 0x02 + * On sector 0x10, App. directory is on block 0x40, 0x41 & 0x42 + * On reading, CRC is ignored. + */ +int mifare_read(uint32_t adapter_idx, uint32_t target_idx, + near_tag_io_cb cb, enum near_tag_sub_type tgt_subtype) +{ + struct mifare_cookie *cookie; + int err; + + DBG(""); + + /*Check supported and tested Mifare type */ + switch (tgt_subtype) { + case NEAR_TAG_NFC_T2_MIFARE_CLASSIC_1K: + case NEAR_TAG_NFC_T2_MIFARE_CLASSIC_4K: + break; + default: + near_error("Mifare tag type [%d] not supported.", tgt_subtype); + return -1; + } + + /* Alloc global cookie */ + cookie = g_try_malloc0(sizeof(struct mifare_cookie)); + if (cookie == NULL) + return -ENOMEM; + + /* Get the nfcid1 */ + cookie->nfcid1 = near_tag_get_nfcid(adapter_idx, target_idx, + &cookie->nfcid1_len); + cookie->adapter_idx = adapter_idx; + cookie->target_idx = target_idx; + cookie->cb = cb; + + /* check access rights - while reading just check read only */ + cookie->acc_check_function = mifare_check_read_only; + + /* + * Need to unlock before reading + * This will check if public keys are allowed (and, so, NDEF could + * be "readable"... + */ + err = mifare_unlock_sector(MAD1_1ST_BLOCK, /* related block */ + mifare_read_MAD1, /* callback function */ + cookie); /* target data */ + if (err < 0) + return mifare_release(err, cookie); + + return 0; +} + +static int check_presence(uint8_t *resp, int length, void *data) +{ + struct mifare_cookie *cookie = data; + int err = 0; + + DBG("%d", length); + + if (length < 0) { + err = -EIO; + goto out; + } + + if (cookie->cb) + cookie->cb(cookie->adapter_idx, cookie->target_idx, err); + +out: + return mifare_release(err, cookie); +} + +int mifare_check_presence(uint32_t adapter_idx, uint32_t target_idx, + near_tag_io_cb cb, enum near_tag_sub_type tgt_subtype) +{ + struct mifare_cmd cmd; + struct mifare_cookie *cookie; + uint8_t *key_ref = MAD_public_key; + + DBG(""); + + /* Check supported and tested Mifare type */ + switch (tgt_subtype) { + case NEAR_TAG_NFC_T2_MIFARE_CLASSIC_1K: + case NEAR_TAG_NFC_T2_MIFARE_CLASSIC_4K: + break; + default: + near_error("Mifare tag type %d not supported.", tgt_subtype); + return -1; + } + + /* Alloc global cookie */ + cookie = g_try_malloc0(sizeof(struct mifare_cookie)); + if (cookie == NULL) + return -ENOMEM; + + /* Get the nfcid1 */ + cookie->nfcid1 = near_tag_get_nfcid(adapter_idx, target_idx, + &cookie->nfcid1_len); + cookie->adapter_idx = adapter_idx; + cookie->target_idx = target_idx; + cookie->cb = cb; + + /* + * To check presence of Mifare Classic Tag, + * send authentication command instead of read one + */ + cmd.cmd = MF_CMD_AUTH_KEY_A; + + /* Authenticate the 1st block of the MAD sector */ + cmd.block = MAD1_1ST_BLOCK; + + /* Store the AUTH KEY */ + memcpy(&cmd.key, key_ref, MAD_KEY_LEN); + + /* add the UID */ + memcpy(&cmd.nfcid, cookie->nfcid1, cookie->nfcid1_len); + + return near_adapter_send(cookie->adapter_idx, + (uint8_t *) &cmd, + sizeof(cmd) - NFC_NFCID1_MAXSIZE + cookie->nfcid1_len, + check_presence, + cookie, mifare_release); +} + +/* + * Common MIFARE Block write: + * Each call will write 16 bytes to tag... so to write 1 sector, + * it has to be called it 4 or 16 times (minus 1 for the trailer block) + */ +static int mifare_write_block(uint8_t block_id, void *data, + near_recv far_func) +{ + struct mf_write_cmd cmd; + struct mifare_cookie *mf_ck = data; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd = MF_CMD_WRITE; /* MIFARE WRITE */ + cmd.block = block_id; + + if ((mf_ck->ndef->offset + DEFAULT_BLOCK_SIZE) < + mf_ck->ndef->length) { + memcpy(cmd.data, mf_ck->ndef->data + + mf_ck->ndef->offset, DEFAULT_BLOCK_SIZE); + mf_ck->ndef->offset += DEFAULT_BLOCK_SIZE; + } else { + memcpy(cmd.data, mf_ck->ndef->data + mf_ck->ndef->offset, + mf_ck->ndef->length - mf_ck->ndef->offset); + mf_ck->ndef->offset = mf_ck->ndef->length + 1; + } + + return near_adapter_send(mf_ck->adapter_idx, + (uint8_t *) &cmd, sizeof(cmd), + far_func, data, NULL); +} + +static int mifare_correct_length_cb(uint8_t *resp, int length, void *data) +{ + struct mifare_cookie *mf_ck = data; + + DBG("Done writing"); + + if (mf_ck->cb) + mf_ck->cb(mf_ck->adapter_idx, mf_ck->target_idx, 0); + + return mifare_release(0, mf_ck); +} + +/* After writing ndef message, its length has to be updated */ +static int mifare_correct_length(uint8_t *resp, int length, void *data) +{ + struct mifare_cookie *mf_ck = data; + + DBG(""); + + /* Correct length field */ + mf_ck->ndef->data[1] = mf_ck->ndef_length; + /* and ndef offset so it points to the beginning */ + mf_ck->ndef->offset = 0; + + /* Run the write process only on the first block of the sector */ + return mifare_write_block(NFC_1ST_BLOCK, mf_ck, + mifare_correct_length_cb); +} + +static int mifare_write_sector_cb(uint8_t *resp, int length, void *data) +{ + struct mifare_cookie *mf_ck = data; + int err; + + /* Next block */ + mf_ck->rws_completed = mf_ck->rws_completed + 1; + + /* Check if it's the last block */ + if ((mf_ck->rws_block_start + mf_ck->rws_completed) + < mf_ck->rws_block_end) { + /* then check if there's still data to write */ + if (mf_ck->ndef->offset < mf_ck->ndef->length) + err = mifare_write_block( + mf_ck->rws_block_start + mf_ck->rws_completed, + data, mifare_write_sector_cb); + else + /* No more Data to write */ + /* Correct length of the ndef message */ + err = mifare_unlock_sector(NFC_1ST_BLOCK, + mifare_correct_length, mf_ck); + } else { + /* Process the callback */ + err = (*mf_ck->rws_next_fct)(resp, length, data); + } + + if (err < 0) + return mifare_release(err, mf_ck); + + return err; + +} + +static int mifare_write_sector_unlocked(uint8_t *resp, int length, void *data) +{ + struct mifare_cookie *mf_ck = data; + int err; + + if (length < 0) { + err = length; + goto out_err; + } + + /* Run the write process on the first block of the sector */ + err = mifare_write_block(mf_ck->rws_block_start, data, + mifare_write_sector_cb); + + if (err < 0) + goto out_err; + return err; + +out_err: + return mifare_release(err, mf_ck); +} + +/* + * This function writes a complete sector, using block per block function. + * sector sizes can be: + * Sectors 0 to 31: + * 48 bytes: 3*16 (no trailer) + * Sectors 32 to 39: + * 240 bytes: 15*16 (no trailer) + * + * Unlock is done at the beginning of each sector. + */ +static int mifare_write_sector(void *cookie, + uint8_t sector_id, /* sector to write */ + near_recv next_func) +{ + struct mifare_cookie *mf_ck = cookie; + int blocks_count; + + DBG(""); + + /* Prepare call values */ + + /* According to tag size, compute the correct block offset */ + if (sector_id < T4K_BOUNDARY) + mf_ck->rws_block_start = sector_id * STD_BLK_SECT_TRAILER; + else + mf_ck->rws_block_start = T4K_BLK_OFF + + (sector_id - T4K_BOUNDARY) * EXT_BLK_SECT_TRAILER; + + /* Find blocks_per_sect, according to position, no trailer */ + if (sector_id < T4K_BOUNDARY) + blocks_count = STD_BLK_PER_SECT; + else + blocks_count = EXT_BLK_PER_SECT; + + mf_ck->rws_block_end = mf_ck->rws_block_start + blocks_count; + mf_ck->rws_completed = 0; + mf_ck->rws_next_fct = next_func; + + /* Being on the first block of the sector, unlock it */ + return mifare_unlock_sector(mf_ck->rws_block_start, + mifare_write_sector_unlocked, mf_ck); +} + +static int mifare_write_NFC_loop(uint8_t *resp, int length, void *data) +{ + struct mifare_cookie *mf_ck = data; + int err = 0; + + if (length < 0 || resp[0] != 0) { + err = -EIO; + goto out_err; + } + + /* Something more to write? */; + if (mf_ck->ndef->offset < mf_ck->ndef->length) { + err = mifare_write_sector(data, /* cookie */ + GPOINTER_TO_INT(mf_ck->g_sect_list->data), + mifare_write_NFC_loop); /* next function */ + + mf_ck->g_sect_list = g_slist_remove(mf_ck->g_sect_list, + mf_ck->g_sect_list->data); + + if (err < 0) + goto out_err; + + } else { + /* Correct length of an NDEF message */ + err = mifare_unlock_sector(NFC_1ST_BLOCK, + mifare_correct_length, mf_ck); + + if (err < 0) + goto out_err; + } + + return err; +out_err: + return mifare_release(err, mf_ck); +} + +static int mifare_write_NFC(void *data) +{ + struct mifare_cookie *mf_ck = data; + int err; + + DBG(""); + + mf_ck->rws_completed = 0; /* written blocks */ + + /* First write here: */ + err = mifare_write_sector(data, /* cookie */ + GPOINTER_TO_INT(mf_ck->g_sect_list->data), /* sector id */ + mifare_write_NFC_loop); /* next function */ + + mf_ck->g_sect_list = g_slist_remove(mf_ck->g_sect_list, + mf_ck->g_sect_list->data); + + if (err < 0) + return mifare_release(err, mf_ck); + + return err; +} + +static int mifare_check_rights_loop(uint8_t *resp, int length, void *data) +{ + struct mifare_cookie *mf_ck = data; + int err; + int sector_id; + + if (mf_ck->acc_sect->next != NULL) { + + mf_ck->acc_sect = mf_ck->acc_sect->next; + sector_id = GPOINTER_TO_INT(mf_ck->acc_sect->data); + + if (sector_id < T4K_BOUNDARY) + mf_ck->rws_block_start = sector_id * 4 + + STD_BLK_PER_SECT; + else + mf_ck->rws_block_start = T4K_BLK_OFF + EXT_BLK_PER_SECT + + (sector_id - T4K_BOUNDARY) * 16; + + err = mifare_unlock_sector(mf_ck->rws_block_start, + mifare_check_rights, mf_ck); + } else { + /* Full access granted, start writing */ + err = mifare_write_NFC(data); + } + + if (err < 0) + return mifare_release(err, mf_ck); + + return err; +} + + +/* + * If one of NFC sectors isn't writable, + * tag size for writing is smaller than actual memory size, + * so calculate it and check if it is enough for ndef message. + */ +static int writing_not_permitted(void *data) +{ + struct mifare_cookie *mf_ck = data; + unsigned int new_tag_size = 0; + int sector_id; + int i; + + sector_id = GPOINTER_TO_INT(mf_ck->acc_sect->data); + DBG("Writing sector %i not permitted", sector_id); + + /* Read only sector found, calculate new tag size */ + if (sector_id <= MAD_V1_AIDS_LEN) { + for (i = GPOINTER_TO_INT(mf_ck->g_sect_list->data); + i < sector_id; i++) + new_tag_size += SECTOR_SIZE; + } else { + /* Start from first NFC sector */ + for (i = GPOINTER_TO_INT(mf_ck->g_sect_list->data); + i <= MAD_V1_AIDS_LEN; i++) + new_tag_size += SECTOR_SIZE; + + /* + * If any of previous sector was NFC, skip MAD2 + * If not, leave "i" as it was + */ + if (i < MAD2_SECTOR) + i = MAD2_SECTOR + 1; + + for (; i < sector_id; i++) { + if (i < T4K_BOUNDARY) + new_tag_size += SECTOR_SIZE; + else + new_tag_size += BIG_SECTOR_SIZE; + } + } + + DBG("TAG writable sectors' size: [%d].", new_tag_size); + + /* Check if there's enough space on tag */ + if (new_tag_size < mf_ck->ndef->length) { + near_error("Not enough space on tag"); + + if (mf_ck->cb) + mf_ck->cb(mf_ck->adapter_idx, + mf_ck->target_idx, -ENOSPC); + + mifare_release(0, data); + return -ENOSPC; + } + + /* Enough space on tag, continue writing */ + mifare_write_NFC(data); + + return 0; +} + +static int mifare_check_rights_NFC(void *data) +{ + struct mifare_cookie *mf_ck = data; + int err; + + DBG(""); + + /* + * As authorisation with key B is not supported, + * in case writing with key A is not permitted, tag is read-only + */ + mf_ck->acc_bits_mask = DATA_access_mask; + mf_ck->acc_rights = WRITE_with_key_A; + + mf_ck->acc_sect = mf_ck->g_sect_list; + mf_ck->rws_block_start = NFC_1ST_BLOCK + STD_BLK_PER_SECT; + mf_ck->rws_next_fct = mifare_check_rights_loop; + + mf_ck->acc_denied_fct = writing_not_permitted; + err = mifare_unlock_sector(mf_ck->rws_block_start, + mifare_check_rights, mf_ck); + + if (err < 0) + return mifare_release(err, mf_ck); + + return err; +} + +int mifare_write(uint32_t adapter_idx, uint32_t target_idx, + struct near_ndef_message *ndef, + near_tag_io_cb cb, enum near_tag_sub_type tgt_subtype) +{ + struct mifare_cookie *cookie; + struct near_tag *tag; + size_t tag_size; + int err; + + DBG(""); + + /* Check supported and tested Mifare type */ + switch (tgt_subtype) { + case NEAR_TAG_NFC_T2_MIFARE_CLASSIC_1K: + case NEAR_TAG_NFC_T2_MIFARE_CLASSIC_4K: + break; + default: + near_error("Mifare tag type %d not supported.", tgt_subtype); + return -1; + } + + /* Check if there's enough space on tag */ + tag = near_tag_get_tag(adapter_idx, target_idx); + near_tag_get_data(tag, &tag_size); + + if (tag_size < ndef->length) { + near_error("Not enough space on tag"); + return -ENOSPC; + } + + /* Alloc global cookie */ + cookie = g_try_malloc0(sizeof(struct mifare_cookie)); + if (cookie == NULL) + return -ENOMEM; + + /* Get the nfcid1 */ + cookie->nfcid1 = near_tag_get_nfcid(adapter_idx, target_idx, + &cookie->nfcid1_len); + cookie->adapter_idx = adapter_idx; + cookie->target_idx = target_idx; + cookie->cb = cb; + + cookie->ndef = ndef; + /* Save ndef length */ + cookie->ndef_length = cookie->ndef->data[1]; + cookie->ndef->data[1] = 0; + + /* + * Check if all sectors are writable + * if not, message may be too long to be written + */ + cookie->acc_check_function = mifare_check_rights_NFC; + + /* + * Mifare Classic Tag needs to be unlocked before writing + * This will check if public keys are allowed (NDEF could be "readable") + */ + err = mifare_unlock_sector(MAD1_1ST_BLOCK, /* related block */ + mifare_read_MAD1, /* callback */ + cookie); /* target data */ + + if (err < 0) + return mifare_release(err, cookie); + + return 0; +} diff --git a/plugins/nfctype1.c b/plugins/nfctype1.c new file mode 100644 index 0000000..5cfc3a0 --- /dev/null +++ b/plugins/nfctype1.c @@ -0,0 +1,825 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdint.h> +#include <errno.h> +#include <string.h> +#include <sys/socket.h> + +#include <linux/socket.h> + +#include <near/nfc.h> +#include <near/plugin.h> +#include <near/log.h> +#include <near/types.h> +#include <near/adapter.h> +#include <near/tag.h> +#include <near/ndef.h> +#include <near/tlv.h> + +#define CMD_READ_ALL 0x00 /* Read seg 0 (incl: HR) */ +#define CMD_READ_SEGS 0x10 /* Read 16 blocks (128 bytes) */ +#define CMD_RID 0x78 /* Read tag UID */ + +#define CMD_WRITE_E 0x53 /* Write with erase */ +#define CMD_WRITE_NE 0x1A /* Write no erase */ + +#define OFFSET_STATUS_CMD 0x00 +#define OFFSET_HEADER_ROM 0x01 + +#define HR0_TYPE1_STATIC 0x11 +#define HR0_TYPE2_HIGH 0x10 +#define HR0_TYPE2_LOW 0x0F + +#define BLOCK_SIZE 8 +#define LEN_STATUS_BYTE 0x01 /* Status byte */ +#define LEN_SPEC_BYTES (LEN_STATUS_BYTE + 0x02) /* HRx */ +#define LEN_UID_BYTES (LEN_STATUS_BYTE + 0x07) /* UID bytes */ +#define LEN_CC_BYTES 0x04 /* Capab. container */ +#define LEN_DYN_BYTES 0x0A /* Bytes CtrlIT and TLV - Dyn. only */ + +#define TYPE1_MAGIC 0xe1 + +#define TAG_T1_DATA_UID(data) ((data) + LEN_SPEC_BYTES) +#define TAG_T1_DATA_CC(data) ((data) + LEN_SPEC_BYTES + LEN_UID_BYTES) +#define TAG_T1_DATA_LENGTH(cc) ((cc[2] + 1) * 8 - LEN_CC_BYTES) + +#define TAG_T1_DATA_NFC(cc) ((cc)[0] & TYPE1_MAGIC) + +#define TYPE1_NOWRITE_ACCESS 0x0F +#define TAG_T1_WRITE_FLAG(cc) ((cc)[3] & TYPE1_NOWRITE_ACCESS) +#define TAG_T1_SEGMENT_SIZE 128 + +#define TYPE1_STATIC_MAX_DATA_SIZE 0x60 + +#define UID_LENGTH 4 + +#define TYPE1_TAG_VER_1_1 0x11 +#define TYPE1_TAG_STATIC_SIZE_120 0x0E +#define TYPE1_READ_WRITE_ACCESS 0x00 +#define TYPE1_STATIC_TAG_DATA_LENGTH 116 + +struct type1_cmd { + uint8_t cmd; + uint8_t addr; + uint8_t data[1]; + uint8_t uid[UID_LENGTH]; +} __attribute__((packed)); + +struct type1_tag { + uint32_t adapter_idx; + uint16_t current_block; + uint16_t current_seg; + uint16_t last_seg; + uint16_t data_read; + uint8_t uid[UID_LENGTH]; + + near_tag_io_cb cb; + struct near_tag *tag; +}; + +struct t1_cookie { + uint32_t adapter_idx; + uint32_t target_idx; + uint8_t uid[UID_LENGTH]; + uint32_t current_block; /* Static tag */ + uint32_t current_byte; /* Static tag */ + struct near_ndef_message *ndef; + near_tag_io_cb cb; + uint8_t cc[LEN_CC_BYTES]; +}; + +static void t1_init_cmd(struct type1_tag *tag, struct type1_cmd *cmd) +{ + if (tag == NULL || cmd == NULL) + return; + + memcpy(cmd->uid, tag->uid, UID_LENGTH); +} + +static int t1_cookie_release(int err, void *data) +{ + struct t1_cookie *cookie = data; + + DBG("%p", cookie); + + if (cookie == NULL) + return err; + + if (cookie->cb != NULL) + cookie->cb(cookie->adapter_idx, cookie->target_idx, err); + + if (cookie->ndef) + g_free(cookie->ndef->data); + + g_free(cookie->ndef); + g_free(cookie); + cookie = NULL; + + return err; +} + +/* Read segments (128 bytes) and store them to the tag data block */ +static int data_recv(uint8_t *resp, int length, void *data) +{ + struct type1_tag *t1_tag = data; + struct type1_cmd t1_cmd; + uint8_t *tagdata; + size_t data_length; + + DBG("%d", length); + + if (length < 0) + return length; + + length = length - LEN_STATUS_BYTE; /* ignore first byte */ + + /* Add data to tag mem */ + tagdata = near_tag_get_data(t1_tag->tag, &data_length); + memcpy(tagdata + t1_tag->data_read, resp + 1, length); + + /* Next segment */ + t1_tag->data_read = t1_tag->data_read + length; + t1_tag->current_seg = t1_tag->current_seg + 1; + + if (t1_tag->current_seg <= t1_tag->last_seg) { + /* RSEG cmd */ + t1_init_cmd(t1_tag, &t1_cmd); + + t1_cmd.cmd = CMD_READ_SEGS; + t1_cmd.addr = (t1_tag->current_seg << 4) & 0xFF; + + return near_adapter_send(t1_tag->adapter_idx, + (uint8_t *) &t1_cmd, sizeof(t1_cmd), + data_recv, t1_tag, NULL); + } else { /* This is the end */ + GList *records; + + DBG("READ complete"); + + records = near_tlv_parse(tagdata, data_length); + near_tag_add_records(t1_tag->tag, records, t1_tag->cb, 0); + + /* free memory */ + g_free(t1_tag); + + return 0; + } +} + +/* + * The dynamic read function: + * Bytes [0..3] : CC + * [4..8]: TLV Lock ControlIT (0x01, 0x03, v1, V2, V3) + * [9..13]: TLV Reserved Memory Control (0x02, 0x03, V1, V2, V3) + * [14..]: TLV NDEF (0x03, L0, L1, L2, V1,V2 ...) + */ +static int read_dynamic_tag(uint8_t *cc, int length, void *data) +{ + struct type1_tag *t1_tag = data; + struct type1_cmd t1_cmd; + + uint8_t *tagdata; + uint8_t *pndef; + size_t data_length; + + DBG("Dynamic Mode"); + + tagdata = near_tag_get_data(t1_tag->tag, &data_length); + + /* Skip un-needed bytes */ + pndef = cc + 4; /* right after CC bytes */ + pndef = pndef + 5; /* skip TLV Lock bits bytes */ + pndef = pndef + 5; /* skip TLV ControlIT bytes */ + + /* + * Save first NFC bytes to tag memory + * 10 blocks[0x3..0xC] of 8 bytes + 2 bytes from block 2 + */ + memcpy(tagdata, pndef, 10 * BLOCK_SIZE + 2); + + /* Read the next one, up to the end of the data area */ + t1_tag->current_seg = 1; + t1_tag->last_seg = ((cc[2] * BLOCK_SIZE) / TAG_T1_SEGMENT_SIZE); + t1_tag->data_read = 10 * BLOCK_SIZE + 2; + + t1_init_cmd(t1_tag, &t1_cmd); + + /* T1 read segment */ + t1_cmd.cmd = CMD_READ_SEGS; + /* 5.3.3 ADDS operand is [b8..b5] */ + t1_cmd.addr = (t1_tag->current_seg << 4) & 0xFF; + + return near_adapter_send(t1_tag->adapter_idx, + (uint8_t *)&t1_cmd, sizeof(t1_cmd), + data_recv, t1_tag, NULL); +} + +static int meta_recv(uint8_t *resp, int length, void *data) +{ + struct t1_cookie *cookie = data; + struct near_tag *tag; + struct type1_tag *t1_tag; + + uint8_t *cc; + int err = -EOPNOTSUPP; + + DBG("%d", length); + + if (length < 0) { + err = length; + goto out_err; + } + + /* First byte is cmd status */ + if (resp[OFFSET_STATUS_CMD] != 0) { + DBG("Command failed: 0x%x", resp[OFFSET_STATUS_CMD]); + err = -EIO; + goto out_err; + } + + /* Check Magic NFC tag */ + cc = TAG_T1_DATA_CC(resp); + if (TAG_T1_DATA_NFC(cc) == 0) { + if (resp[OFFSET_HEADER_ROM] == HR0_TYPE1_STATIC) { + err = near_tag_add_data(cookie->adapter_idx, + cookie->target_idx, + NULL, + TYPE1_STATIC_TAG_DATA_LENGTH); + } else { + near_error("Not a valid NFC magic tag 0x%x", cc[0]); + err = -EINVAL; + goto out_err; + } + } else { + /* Add data to the tag */ + err = near_tag_add_data(cookie->adapter_idx, cookie->target_idx, + NULL, TAG_T1_DATA_LENGTH(cc)); + } + + if (err < 0) + goto out_err; + + tag = near_tag_get_tag(cookie->adapter_idx, cookie->target_idx); + if (tag == NULL) { + err = -ENOMEM; + goto out_err; + } + + t1_tag = g_try_malloc0(sizeof(struct type1_tag)); + if (t1_tag == NULL) { + err = -ENOMEM; + goto out_err; + } + + t1_tag->adapter_idx = cookie->adapter_idx; + t1_tag->cb = cookie->cb; + t1_tag->tag = tag; + memcpy(t1_tag->uid, cookie->uid, UID_LENGTH); + + /* Set the ReadWrite flag */ + if (TAG_T1_WRITE_FLAG(cc) == TYPE1_NOWRITE_ACCESS) + near_tag_set_ro(tag, TRUE); + else + near_tag_set_ro(tag, FALSE); + + /* Check Static or Dynamic memory model */ + if (resp[OFFSET_HEADER_ROM] == HR0_TYPE1_STATIC) { + uint8_t *tagdata; + size_t data_length; + GList *records; + + if (TAG_T1_DATA_NFC(cc) == 0) + near_tag_set_blank(tag, TRUE); + else + near_tag_set_blank(tag, FALSE); + + DBG("READ Static complete"); + + tagdata = near_tag_get_data(t1_tag->tag, &data_length); + memcpy(tagdata, cc + LEN_CC_BYTES, TAG_T1_DATA_LENGTH(cc)); + + near_tag_set_memory_layout(tag, NEAR_TAG_MEMORY_STATIC); + + records = near_tlv_parse(tagdata, data_length); + near_tag_add_records(t1_tag->tag, records, t1_tag->cb, 0); + + g_free(t1_tag); + + return 0; + } else if ((resp[OFFSET_HEADER_ROM] & 0xF0) == HR0_TYPE2_HIGH) { + near_tag_set_memory_layout(tag, NEAR_TAG_MEMORY_DYNAMIC); + err = read_dynamic_tag(cc, length, t1_tag); + /* + * As reading isn't complete, + * callback shouldn't be called while freeing the cookie + */ + cookie->cb = NULL; + } else { + err = -EOPNOTSUPP; + } + + if (err < 0) + g_free(t1_tag); + +out_err: + DBG("err %d", err); + + return t1_cookie_release(err, cookie); +} + +/* + * READALL to read a maximum of 124 bytes. + * This cmd is common to static and dynamic targets + * This should allow to get the HR0 byte. + */ +static int rid_resp(uint8_t *resp, int length, void *data) +{ + struct t1_cookie *cookie = data; + struct type1_cmd t1_cmd; + uint8_t *uid; + int err; + + DBG(""); + + /* First byte is cmd status */ + if (resp[OFFSET_STATUS_CMD] != 0) { + DBG("Command failed: 0x%x", resp[OFFSET_STATUS_CMD]); + err = -EIO; + goto out_err; + } + + uid = TAG_T1_DATA_UID(resp); + + DBG("UID 0x%x 0x%x 0x%x 0x%x", uid[0], uid[1], uid[2], uid[3]); + + near_tag_set_nfcid(cookie->adapter_idx, cookie->target_idx, + uid, UID_LENGTH); + + t1_cmd.cmd = CMD_READ_ALL; /* Read ALL cmd give 124 bytes */ + t1_cmd.addr = 0; /* NA */ + t1_cmd.data[0] = 0; + memcpy(t1_cmd.uid, uid, UID_LENGTH); + + memcpy(cookie->uid, uid, UID_LENGTH); + + return near_adapter_send(cookie->adapter_idx, + (uint8_t *)&t1_cmd, sizeof(t1_cmd), + meta_recv, cookie, t1_cookie_release); + +out_err: + DBG("err %d", err); + + return t1_cookie_release(err, cookie); +} + +static int nfctype1_read_meta(uint32_t adapter_idx, uint32_t target_idx, + near_tag_io_cb cb, uint8_t *uid) +{ + struct type1_cmd cmd; + struct t1_cookie *cookie; + + DBG(""); + + memset(&cmd, 0, sizeof(cmd)); + + cookie = g_try_malloc0(sizeof(struct t1_cookie)); + if (cookie == NULL) + return -ENOMEM; + + cookie->adapter_idx = adapter_idx; + cookie->target_idx = target_idx; + cookie->cb = cb; + + if (uid != NULL) { + cmd.cmd = CMD_READ_ALL; /* Read ALL cmd give 124 bytes */ + memcpy(cmd.uid, uid, UID_LENGTH); + memcpy(cookie->uid, uid, UID_LENGTH); + + return near_adapter_send(adapter_idx, (uint8_t *) &cmd, + sizeof(cmd), meta_recv, cookie, + t1_cookie_release); + } else { + cmd.cmd = CMD_RID; + + return near_adapter_send(adapter_idx, (uint8_t *) &cmd, + sizeof(cmd), rid_resp, cookie, + t1_cookie_release); + } +} + +/* First step: RID to get the tag UID */ +static int nfctype1_read(uint32_t adapter_idx, + uint32_t target_idx, near_tag_io_cb cb) +{ + uint8_t *uid; + uint8_t uid_length; + int err; + + DBG(""); + + uid = near_tag_get_nfcid(adapter_idx, target_idx, &uid_length); + if (uid == NULL || uid_length != UID_LENGTH) { + if (uid != NULL) { + near_error("Invalid UID"); + + g_free(uid); + return -EINVAL; + } + + return nfctype1_read_meta(adapter_idx, target_idx, cb, NULL); + } + + err = nfctype1_read_meta(adapter_idx, target_idx, cb, uid); + + g_free(uid); + + return err; +} + +static int write_nmn_e1_resp(uint8_t *resp, int length, void *data) +{ + int err = 0; + struct t1_cookie *cookie = data; + + DBG(""); + + if (length < 0) + err = length; + + if (resp[OFFSET_STATUS_CMD] != 0) + err = -EIO; + + DBG("Done writing"); + + return t1_cookie_release(err, cookie); +} + +static int write_nmn_e1(struct t1_cookie *cookie) +{ + struct type1_cmd cmd; + + DBG(""); + + cmd.cmd = CMD_WRITE_E; + cmd.addr = 0x08; + cmd.data[0] = TYPE1_MAGIC; + memcpy(cmd.uid, cookie->uid, UID_LENGTH); + + return near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd, + sizeof(cmd), write_nmn_e1_resp, cookie, + t1_cookie_release); +} + +static int data_write_resp(uint8_t *resp, int length, void *data) +{ + struct t1_cookie *cookie = data; + uint8_t addr = 0; + struct type1_cmd cmd; + int err; + + DBG(""); + + if (length < 0) { + err = length; + goto out_err; + } + + if (resp[OFFSET_STATUS_CMD] != 0) { + err = -EIO; + goto out_err; + } + + if (cookie->ndef->offset > cookie->ndef->length) + return write_nmn_e1(cookie); + + if (cookie->current_byte >= BLOCK_SIZE) { + cookie->current_byte = 0; + cookie->current_block++; + } + + cmd.cmd = CMD_WRITE_E; + addr = cookie->current_block << 3; + cmd.addr = addr | (cookie->current_byte & 0x7); + cmd.data[0] = cookie->ndef->data[cookie->ndef->offset]; + memcpy(cmd.uid, cookie->uid, UID_LENGTH); + cookie->ndef->offset++; + cookie->current_byte++; + + err = near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd, + sizeof(cmd), data_write_resp, cookie, + NULL); + if (err < 0) + goto out_err; + + return 0; + +out_err: + return t1_cookie_release(err, cookie); +} + +static int data_write(uint32_t adapter_idx, uint32_t target_idx, + struct near_ndef_message *ndef, near_tag_io_cb cb) +{ + int err; + struct type1_cmd cmd; + struct t1_cookie *cookie; + uint8_t *uid, uid_length; + + DBG(""); + + uid = near_tag_get_nfcid(adapter_idx, target_idx, &uid_length); + if (uid == NULL || uid_length != UID_LENGTH) { + near_error("Invalid type 1 UID"); + err = -EINVAL; + goto out_err; + } + + cmd.cmd = CMD_WRITE_E; + cmd.addr = 0x08; + cmd.data[0] = 0x00; + memcpy(cmd.uid, uid, UID_LENGTH); + + cookie = g_try_malloc0(sizeof(struct t1_cookie)); + if (cookie == NULL) { + g_free(uid); + err = -ENOMEM; + goto out_err; + } + + cookie->adapter_idx = adapter_idx; + cookie->target_idx = target_idx; + memcpy(cookie->uid, uid, UID_LENGTH); + cookie->current_block = 1; + cookie->current_byte = LEN_CC_BYTES; + cookie->ndef = ndef; + cookie->cb = cb; + + g_free(uid); + + return near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd, + sizeof(cmd), data_write_resp, cookie, + t1_cookie_release); +out_err: + if (cb != NULL) + cb(adapter_idx, target_idx, err); + + return err; +} + +/* + * The writing of a new NDEF message SHALL occur as follows: + * Write NMN = 00h to indicate that no valid NDEF message is present + * during writing to allow error detection in the event that the tag + * is removed from the field prior to completion of operation. + * Write VNo and RWA if required + * Write NDEF Message TLV + * Write NDEF Message data + * Write NMN = E1h as the last byte to be written + */ +static int nfctype1_write(uint32_t adapter_idx, uint32_t target_idx, + struct near_ndef_message *ndef, + near_tag_io_cb cb) +{ + struct near_tag *tag; + int err; + + DBG(""); + + if (ndef == NULL || cb == NULL) { + err = -EINVAL; + goto out_err; + } + + tag = near_tag_get_tag(adapter_idx, target_idx); + if (tag == NULL) { + err = -EINVAL; + goto out_err; + } + + /* This check is valid for only static tags. + * Max data length on Type 1 Tag including TLV's + * is TYPE1_STATIC_MAX_DATA_SIZE */ + if (near_tag_get_memory_layout(tag) == NEAR_TAG_MEMORY_STATIC) { + if ((ndef->length + 3) > TYPE1_STATIC_MAX_DATA_SIZE) { + near_error("not enough space on tag"); + err = -ENOSPC; + goto out_err; + } + } + + return data_write(adapter_idx, target_idx, ndef, cb); + + if (err < 0) + goto out_err; + + return 0; + +out_err: + if (cb != NULL) + cb(adapter_idx, target_idx, err); + + return err; +} + +static int check_presence(uint8_t *resp, int length, void *data) +{ + struct t1_cookie *cookie = data; + int err = 0; + + DBG("%d", length); + + if (length < 0) + err = -EIO; + + return t1_cookie_release(err, cookie); +} + +static int nfctype1_check_presence(uint32_t adapter_idx, + uint32_t target_idx, near_tag_io_cb cb) +{ + struct type1_cmd t1_cmd; + struct t1_cookie *cookie; + uint8_t *uid, uid_length; + + DBG(""); + + uid = near_tag_get_nfcid(adapter_idx, target_idx, &uid_length); + if (uid == NULL || uid_length != UID_LENGTH) { + near_error("Invalid type 1 UID"); + return -EINVAL; + } + + t1_cmd.cmd = CMD_READ_ALL; /* Read ALL cmd give 124 bytes */ + t1_cmd.addr = 0; /* NA */ + t1_cmd.data[0] = 0; + memcpy(t1_cmd.uid, uid, UID_LENGTH); + + g_free(uid); + + cookie = g_try_malloc0(sizeof(struct t1_cookie)); + if (cookie == NULL) + return -ENOMEM; + + cookie->adapter_idx = adapter_idx; + cookie->target_idx = target_idx; + cookie->cb = cb; + + return near_adapter_send(adapter_idx, (uint8_t *) &t1_cmd, + sizeof(t1_cmd), check_presence, cookie, + t1_cookie_release); +} + +static int format_resp(uint8_t *resp, int length, void *data) +{ + int err = 0; + struct t1_cookie *cookie = data; + struct near_tag *tag; + struct type1_cmd cmd; + uint8_t addr; + + DBG(""); + + if (length < 0 || resp[0] != 0) { + err = -EIO; + goto out_err; + } + + if (cookie->current_byte < LEN_CC_BYTES) { + cmd.cmd = CMD_WRITE_E; + addr = cookie->current_block << 3; + cmd.addr = addr | (cookie->current_byte & 0x7); + cmd.data[0] = cookie->cc[cookie->current_byte]; + cookie->current_byte++; + memcpy(cmd.uid, cookie->uid, UID_LENGTH); + + return near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd, + sizeof(cmd), format_resp, cookie, + t1_cookie_release); + } else { + tag = near_tag_get_tag(cookie->adapter_idx, cookie->target_idx); + if (tag == NULL) { + err = -EINVAL; + goto out_err; + } + + DBG("Done formatting"); + near_tag_set_blank(tag, FALSE); + } + +out_err: + return t1_cookie_release(err, cookie); +} + +static int nfctype1_format(uint32_t adapter_idx, uint32_t target_idx, + near_tag_io_cb cb) +{ + int err; + struct near_tag *tag; + struct type1_cmd cmd; + struct t1_cookie *cookie; + uint8_t *uid, uid_length, addr; + + DBG(""); + + tag = near_tag_get_tag(adapter_idx, target_idx); + if (tag == NULL) + return -EINVAL; + + /* TODO: Dynamic tag format */ + if (near_tag_get_memory_layout(tag) != NEAR_TAG_MEMORY_STATIC) + return -EOPNOTSUPP; + + uid = near_tag_get_nfcid(adapter_idx, target_idx, &uid_length); + if (uid == NULL || uid_length != UID_LENGTH) { + near_error("Invalid type 1 UID"); + return -EINVAL; + } + + cookie = g_try_malloc0(sizeof(struct t1_cookie)); + if (cookie == NULL) { + err = -EINVAL; + goto out_err; + } + + cookie->adapter_idx = adapter_idx; + cookie->target_idx = target_idx; + cookie->cb = cb; + memcpy(cookie->uid, uid, UID_LENGTH); + cookie->cc[0] = TYPE1_MAGIC; + cookie->cc[1] = TYPE1_TAG_VER_1_1; + cookie->cc[2] = TYPE1_TAG_STATIC_SIZE_120; + cookie->cc[3] = TYPE1_READ_WRITE_ACCESS; + cookie->current_block = 1; + cookie->current_byte = 0; + + cmd.cmd = CMD_WRITE_E; + addr = cookie->current_block << 3; + cmd.addr = addr | (cookie->current_byte & 0x7); + cmd.data[0] = cookie->cc[cookie->current_byte]; + cookie->current_byte++; + memcpy(cmd.uid, cookie->uid, UID_LENGTH); + g_free(uid); + + err = near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd, + sizeof(cmd), format_resp, cookie, + t1_cookie_release); + if (err < 0) + goto out_err; + + return 0; + +out_err: + g_free(cookie); + g_free(uid); + + return err; +} + +static struct near_tag_driver type1_driver = { + .type = NFC_PROTO_JEWEL, + .priority = NEAR_TAG_PRIORITY_DEFAULT, + .read = nfctype1_read, + .write = nfctype1_write, + .check_presence = nfctype1_check_presence, + .format = nfctype1_format, +}; + +static int nfctype1_init(void) +{ + DBG(""); + + return near_tag_driver_register(&type1_driver); +} + +static void nfctype1_exit(void) +{ + DBG(""); + + near_tag_driver_unregister(&type1_driver); +} + +NEAR_PLUGIN_DEFINE(nfctype1, "NFC Forum Type 1 tags support", VERSION, + NEAR_PLUGIN_PRIORITY_HIGH, nfctype1_init, nfctype1_exit) diff --git a/plugins/nfctype2.c b/plugins/nfctype2.c new file mode 100644 index 0000000..3dc7856 --- /dev/null +++ b/plugins/nfctype2.c @@ -0,0 +1,635 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdint.h> +#include <errno.h> +#include <string.h> +#include <sys/socket.h> + +#include <linux/socket.h> + +#include <near/nfc.h> +#include <near/plugin.h> +#include <near/log.h> +#include <near/types.h> +#include <near/adapter.h> +#include <near/tag.h> +#include <near/ndef.h> +#include <near/tlv.h> + +extern int mifare_read(uint32_t adapter_idx, uint32_t target_idx, + near_tag_io_cb cb, enum near_tag_sub_type tgt_subtype); + +extern int mifare_check_presence(uint32_t adapter_idx, uint32_t target_idx, + near_tag_io_cb cb, enum near_tag_sub_type tgt_subtype); + +extern int mifare_write(uint32_t adapter_idx, uint32_t target_idx, + struct near_ndef_message *ndef, + near_tag_io_cb cb, enum near_tag_sub_type tgt_subtype); + +#define CMD_READ 0x30 +#define CMD_READ_SIZE 0x02 + +#define CMD_WRITE 0xA2 + +#define READ_SIZE 16 +#define BLOCK_SIZE 4 + +#define META_BLOCK_START 0 +#define DATA_BLOCK_START 4 +#define TYPE2_MAGIC 0xe1 + +#define TAG_DATA_CC(data) ((data) + 12) +#define TAG_DATA_LENGTH(cc) ((cc)[2] * 8) +#define TAG_DATA_NFC(cc) ((cc)[0] & TYPE2_MAGIC) + +#define TYPE2_NOWRITE_ACCESS 0x0F +#define TYPE2_READWRITE_ACCESS 0x00 +#define TAG_T2_WRITE_FLAG(cc) ((cc)[3] & TYPE2_NOWRITE_ACCESS) + +#define NDEF_MAX_SIZE 0x30 + +#define CC_BLOCK_START 3 +#define TYPE2_TAG_VER_1_0 0x10 +#define TYPE2_DATA_SIZE_48 0x6 + +struct type2_cmd { + uint8_t cmd; + uint8_t block; + uint8_t data[BLOCK_SIZE]; +} __attribute__((packed)); + +struct type2_tag { + uint32_t adapter_idx; + uint16_t current_block; + + near_tag_io_cb cb; + struct near_tag *tag; +}; + +struct t2_cookie { + uint32_t adapter_idx; + uint32_t target_idx; + uint8_t current_block; + struct near_ndef_message *ndef; + near_tag_io_cb cb; +}; + +struct type2_cc { + uint8_t magic; + uint8_t version; + uint8_t mem_size; + uint8_t read_write; +}; + +static int t2_cookie_release(int err, void *data) +{ + struct t2_cookie *cookie = data; + + DBG("%p", cookie); + + if (cookie == NULL) + return err; + + if (cookie->cb != NULL) + cookie->cb(cookie->adapter_idx, cookie->target_idx, err); + + if (cookie->ndef) + g_free(cookie->ndef->data); + + g_free(cookie->ndef); + g_free(cookie); + cookie = NULL; + + return err; +} + +static int data_recv(uint8_t *resp, int length, void *data) +{ + struct type2_tag *tag = data; + struct type2_cmd cmd; + uint8_t *nfc_data; + size_t current_length, length_read, data_length; + uint32_t adapter_idx; + int read_blocks; + + DBG("%d", length); + + if (length < 0) { + g_free(tag); + + return length; + } + + nfc_data = near_tag_get_data(tag->tag, &data_length); + adapter_idx = near_tag_get_adapter_idx(tag->tag); + + length_read = length - NFC_HEADER_SIZE; + current_length = tag->current_block * BLOCK_SIZE; + if (current_length + length - NFC_HEADER_SIZE > data_length) + length_read = data_length - current_length; + + memcpy(nfc_data + current_length, resp + NFC_HEADER_SIZE, length_read); + + if (current_length + length_read == data_length) { + GList *records; + + /* TODO parse tag->data for NDEFS, and notify target.c */ + tag->current_block = 0; + + DBG("Done reading"); + + records = near_tlv_parse(nfc_data, data_length); + near_tag_add_records(tag->tag, records, tag->cb, 0); + + g_free(tag); + + return 0; + } + + read_blocks = length / BLOCK_SIZE; + tag->current_block += read_blocks; + + cmd.cmd = CMD_READ; + cmd.block = DATA_BLOCK_START + tag->current_block; + + DBG("adapter %d", adapter_idx); + + return near_adapter_send(adapter_idx, + (uint8_t *) &cmd, CMD_READ_SIZE, + data_recv, tag, NULL); +} + +static int data_read(struct type2_tag *tag) +{ + struct type2_cmd cmd; + uint32_t adapter_idx; + + DBG(""); + + tag->current_block = 0; + + cmd.cmd = CMD_READ; + cmd.block = DATA_BLOCK_START; + + adapter_idx = near_tag_get_adapter_idx(tag->tag); + + return near_adapter_send(adapter_idx, + (uint8_t *) &cmd, CMD_READ_SIZE, + data_recv, tag, NULL); +} + +static int meta_recv(uint8_t *resp, int length, void *data) +{ + struct t2_cookie *cookie = data; + struct near_tag *tag; + struct type2_tag *t2_tag; + uint8_t *cc; + int err; + + DBG("%d", length); + + if (length < 0) { + err = length; + goto out_err; + } + + if (resp[0] != 0) { + err = -EIO; + goto out_err; + } + + cc = TAG_DATA_CC(resp + NFC_HEADER_SIZE); + + /* Default to 48 bytes data size in case of blank tag */ + err = near_tag_add_data(cookie->adapter_idx, cookie->target_idx, + NULL, (TAG_DATA_LENGTH(cc) ? TAG_DATA_LENGTH(cc) : + TYPE2_DATA_SIZE_48 << 3)); + + if (err < 0) + goto out_err; + + tag = near_tag_get_tag(cookie->adapter_idx, cookie->target_idx); + if (tag == NULL) { + err = -ENOMEM; + goto out_err; + } + + t2_tag = g_try_malloc0(sizeof(struct type2_tag)); + if (t2_tag == NULL) { + err = -ENOMEM; + goto out_err; + } + + t2_tag->adapter_idx = cookie->adapter_idx; + t2_tag->cb = cookie->cb; + t2_tag->tag = tag; + + /* Set the ReadWrite flag */ + if (TAG_T2_WRITE_FLAG(cc) == TYPE2_NOWRITE_ACCESS) + near_tag_set_ro(tag, TRUE); + else + near_tag_set_ro(tag, FALSE); + + near_tag_set_memory_layout(tag, NEAR_TAG_MEMORY_STATIC); + + if (TAG_DATA_NFC(cc) == 0) { + DBG("Mark as blank tag"); + near_tag_set_blank(tag, TRUE); + } else { + near_tag_set_blank(tag, FALSE); + } + + err = data_read(t2_tag); + if (err < 0) + goto out_tag; + + /* + * As reading isn't complete, + * callback shouldn't be called while freeing the cookie + */ + cookie->cb = NULL; + return t2_cookie_release(err, cookie); + +out_tag: + g_free(t2_tag); + +out_err: + return t2_cookie_release(err, cookie); +} + +static int nfctype2_read_meta(uint32_t adapter_idx, uint32_t target_idx, + near_tag_io_cb cb) +{ + struct type2_cmd cmd; + struct t2_cookie *cookie; + + DBG(""); + + cmd.cmd = CMD_READ; + cmd.block = META_BLOCK_START; + + cookie = g_try_malloc0(sizeof(struct t2_cookie)); + if (cookie == NULL) + return -ENOMEM; + + cookie->adapter_idx = adapter_idx; + cookie->target_idx = target_idx; + cookie->cb = cb; + + return near_adapter_send(adapter_idx, (uint8_t *) &cmd, CMD_READ_SIZE, + meta_recv, cookie, t2_cookie_release); +} + +static int nfctype2_read(uint32_t adapter_idx, + uint32_t target_idx, near_tag_io_cb cb) +{ + enum near_tag_sub_type tgt_subtype; + + DBG(""); + + tgt_subtype = near_tag_get_subtype(adapter_idx, target_idx); + + switch (tgt_subtype) { + case NEAR_TAG_NFC_T2_MIFARE_ULTRALIGHT: + return nfctype2_read_meta(adapter_idx, target_idx, cb); + + /* Specific Mifare read access */ + case NEAR_TAG_NFC_T2_MIFARE_CLASSIC_1K: + case NEAR_TAG_NFC_T2_MIFARE_CLASSIC_4K: + return mifare_read(adapter_idx, target_idx, + cb, tgt_subtype); + + default: + DBG("Unknown Tag Type 2 subtype %d", tgt_subtype); + return -1; + } +} + +static int data_write_resp(uint8_t *resp, int length, void *data) +{ + int err; + struct t2_cookie *cookie = data; + struct type2_cmd cmd; + + DBG(""); + + if (length < 0 || resp[0] != 0) { + err = -EIO; + goto out_err; + } + + if (cookie->ndef->offset > cookie->ndef->length) { + DBG("Done writing"); + + return t2_cookie_release(0, cookie); + } + + cmd.cmd = CMD_WRITE; + cmd.block = cookie->current_block; + cookie->current_block++; + + if ((cookie->ndef->offset + BLOCK_SIZE) < + cookie->ndef->length) { + memcpy(cmd.data, cookie->ndef->data + + cookie->ndef->offset, BLOCK_SIZE); + cookie->ndef->offset += BLOCK_SIZE; + } else { + memcpy(cmd.data, cookie->ndef->data + cookie->ndef->offset, + cookie->ndef->length - cookie->ndef->offset); + cookie->ndef->offset = cookie->ndef->length + 1; + } + + return near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd, + sizeof(cmd), data_write_resp, cookie, + t2_cookie_release); + +out_err: + return t2_cookie_release(err, cookie); +} + +static int data_write(uint32_t adapter_idx, uint32_t target_idx, + struct near_ndef_message *ndef, + near_tag_io_cb cb) +{ + struct type2_cmd cmd; + struct t2_cookie *cookie; + int err; + + DBG(""); + + cookie = g_try_malloc0(sizeof(struct t2_cookie)); + if (cookie == NULL) { + err = -ENOMEM; + if (cb != NULL) + cb(adapter_idx, target_idx, err); + return err; + } + + cookie->adapter_idx = adapter_idx; + cookie->target_idx = target_idx; + cookie->current_block = DATA_BLOCK_START; + cookie->ndef = ndef; + cookie->cb = cb; + + cmd.cmd = CMD_WRITE; + cmd.block = cookie->current_block; + memcpy(cmd.data, cookie->ndef->data, BLOCK_SIZE); + cookie->ndef->offset += BLOCK_SIZE; + cookie->current_block++; + + return near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd, + sizeof(cmd), data_write_resp, cookie, + t2_cookie_release); +} + +static int nfctype2_write(uint32_t adapter_idx, uint32_t target_idx, + struct near_ndef_message *ndef, + near_tag_io_cb cb) +{ + struct near_tag *tag; + enum near_tag_sub_type tgt_subtype; + int err; + + DBG(""); + + if (ndef == NULL || cb == NULL) { + err = -EINVAL; + goto out_err; + } + + tag = near_tag_get_tag(adapter_idx, target_idx); + if (tag == NULL) { + err = -EINVAL; + goto out_err; + } + + tgt_subtype = near_tag_get_subtype(adapter_idx, target_idx); + + switch (tgt_subtype) { + case NEAR_TAG_NFC_T2_MIFARE_ULTRALIGHT: + /* + * This check is valid for only static tags. + * Max data length on Type 2 Tag + * including TLV's is NDEF_MAX_SIZE + */ + if (near_tag_get_memory_layout(tag) == NEAR_TAG_MEMORY_STATIC) { + if ((ndef->length + 3) > NDEF_MAX_SIZE) { + near_error("not enough space on tag"); + err = -ENOSPC; + goto out_err; + } + } + + return data_write(adapter_idx, target_idx, ndef, cb); + + /* Specific Mifare write access */ + case NEAR_TAG_NFC_T2_MIFARE_CLASSIC_1K: + case NEAR_TAG_NFC_T2_MIFARE_CLASSIC_4K: + return mifare_write(adapter_idx, target_idx, ndef, + cb, tgt_subtype); + default: + DBG("Unknown TAG Type 2 subtype %d", tgt_subtype); + err = -EINVAL; + goto out_err; + } + + return 0; + +out_err: + if (cb != NULL) + cb(adapter_idx, target_idx, err); + + return err; +} + +static int check_presence(uint8_t *resp, int length, void *data) +{ + struct t2_cookie *cookie = data; + int err = 0; + + DBG("%d", length); + + if (length < 0) + err = -EIO; + + return t2_cookie_release(err, cookie); +} + +static int nfctype2_check_presence(uint32_t adapter_idx, uint32_t target_idx, + near_tag_io_cb cb) +{ + struct type2_cmd cmd; + struct t2_cookie *cookie; + enum near_tag_sub_type tgt_subtype; + + DBG(""); + + tgt_subtype = near_tag_get_subtype(adapter_idx, target_idx); + + switch (tgt_subtype) { + case NEAR_TAG_NFC_T2_MIFARE_ULTRALIGHT: + cmd.cmd = CMD_READ; + cmd.block = META_BLOCK_START; + + cookie = g_try_malloc0(sizeof(struct t2_cookie)); + if (cookie == NULL) + return -ENOMEM; + + cookie->adapter_idx = adapter_idx; + cookie->target_idx = target_idx; + cookie->cb = cb; + + return near_adapter_send(adapter_idx, (uint8_t *) &cmd, + CMD_READ_SIZE, check_presence, cookie, + t2_cookie_release); + + /* Specific Mifare check presence */ + case NEAR_TAG_NFC_T2_MIFARE_CLASSIC_1K: + case NEAR_TAG_NFC_T2_MIFARE_CLASSIC_4K: + return mifare_check_presence(adapter_idx, target_idx, + cb, tgt_subtype); + + default: + DBG("Unknown TAG Type 2 subtype %d", tgt_subtype); + + return -1; + } +} + +static int format_resp(uint8_t *resp, int length, void *data) +{ + int err = 0; + struct t2_cookie *cookie = data; + struct near_tag *tag; + + DBG(""); + + if (length < 0 || resp[0] != 0) { + err = -EIO; + goto out_err; + } + + tag = near_tag_get_tag(cookie->adapter_idx, cookie->target_idx); + if (tag == NULL) { + err = -EINVAL; + goto out_err; + } + + DBG("Done formatting"); + near_tag_set_blank(tag, FALSE); + +out_err: + return t2_cookie_release(err, cookie); +} + +static int nfctype2_format(uint32_t adapter_idx, uint32_t target_idx, + near_tag_io_cb cb) +{ + struct type2_cmd cmd; + struct t2_cookie *cookie; + struct near_ndef_message *cc_ndef; + struct type2_cc *t2_cc; + struct near_tag *tag; + enum near_tag_sub_type tgt_subtype; + int err; + + DBG(""); + + tag = near_tag_get_tag(adapter_idx, target_idx); + if (tag == NULL) + return -EINVAL; + + + tgt_subtype = near_tag_get_subtype(adapter_idx, target_idx); + + if (tgt_subtype != NEAR_TAG_NFC_T2_MIFARE_ULTRALIGHT) { + DBG("Unknown Tag Type 2 subtype %d", tgt_subtype); + return -1; + } + + t2_cc = g_try_malloc0(sizeof(struct type2_cc)); + cc_ndef = g_try_malloc0(sizeof(struct near_ndef_message)); + cookie = g_try_malloc0(sizeof(struct t2_cookie)); + + if (t2_cc == NULL || cc_ndef == NULL || cookie == NULL) { + err = -ENOMEM; + goto out_err; + } + + t2_cc->magic = TYPE2_MAGIC; + t2_cc->version = TYPE2_TAG_VER_1_0; + t2_cc->mem_size = TYPE2_DATA_SIZE_48; + t2_cc->read_write = TYPE2_READWRITE_ACCESS; + + cookie->adapter_idx = adapter_idx; + cookie->target_idx = target_idx; + cookie->current_block = CC_BLOCK_START; + cookie->ndef = cc_ndef; + cookie->ndef->data = (uint8_t *) t2_cc; + cookie->cb = cb; + + cmd.cmd = CMD_WRITE; + cmd.block = CC_BLOCK_START; + memcpy(cmd.data, (uint8_t *) t2_cc, BLOCK_SIZE); + + err = near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd, + sizeof(cmd), format_resp, cookie, NULL); + +out_err: + if (err < 0) { + g_free(t2_cc); + g_free(cc_ndef); + g_free(cookie); + } + + return err; +} + +static struct near_tag_driver type2_driver = { + .type = NFC_PROTO_MIFARE, + .priority = NEAR_TAG_PRIORITY_DEFAULT, + .read = nfctype2_read, + .write = nfctype2_write, + .check_presence = nfctype2_check_presence, + .format = nfctype2_format, +}; + +static int nfctype2_init(void) +{ + DBG(""); + + return near_tag_driver_register(&type2_driver); +} + +static void nfctype2_exit(void) +{ + DBG(""); + + near_tag_driver_unregister(&type2_driver); +} + +NEAR_PLUGIN_DEFINE(nfctype2, "NFC Forum Type 2 tags support", VERSION, + NEAR_PLUGIN_PRIORITY_HIGH, nfctype2_init, nfctype2_exit) diff --git a/plugins/nfctype3.c b/plugins/nfctype3.c new file mode 100644 index 0000000..f45e1a7 --- /dev/null +++ b/plugins/nfctype3.c @@ -0,0 +1,998 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdint.h> +#include <errno.h> +#include <string.h> +#include <sys/socket.h> + +#include <linux/socket.h> + +#include <near/nfc.h> +#include <near/plugin.h> +#include <near/log.h> +#include <near/types.h> +#include <near/adapter.h> +#include <near/tag.h> +#include <near/ndef.h> +#include <near/tlv.h> + +#define CMD_POLL 0x00 +#define RESP_POLL 0x01 + +#define CMD_REQUEST_SERVICE 0x02 +#define RESP_REQUEST_SERVICE 0x03 + +#define CMD_REQUEST_RESPONSE 0x04 +#define RESP_REQUEST_RESPONSE 0x05 + +#define CMD_READ_WO_ENCRYPT 0x06 +#define RESP_READ_WO_ENCRYPT 0x07 + +#define CMD_WRITE_WO_ENCRYPT 0x08 +#define RESP_WRITE_WO_ENCRYPT 0x09 + +#define CMD_REQUEST_SYS_CODE 0x0C +#define RESP_REQUEST_SYS_CODE 0x0D + +#define CMD_AUTHENTICATION_1 0x10 +#define RESP_AUTHENTICATION_1 0x11 + +#define CMD_AUTHENTICATION_2 0x12 +#define RESP_AUTHENTICATION_2 0x13 + +#define CMD_READ 0x14 +#define RESP_READ 0x15 + +#define CMD_WRITE 0x16 +#define RESP_WRITE 0x17 + +#define NFC_SERVICE_CODE 0x000B +#define BLOCK_SIZE 16 +#define META_BLOCK_START 0 +#define DATA_BLOCK_START 1 + +#define LEN_ID 0x08 +#define LEN_CMD 0x01 +#define LEN_REPLY_CMD 0x02 +#define LEN_CMD_LEN 0x01 + +/* offsets */ +#define OFS_NFC_STATUS 0 +#define OFS_NFC_LEN 1 +#define OFS_CMD_RESP 2 +#define OFS_IDM 3 +#define OFS_CMD_DATA (LEN_CMD_LEN + LEN_CMD + LEN_ID) +#define OFS_READ_FLAG 12 +#define OFS_READ_DATA 14 +#define BLOCK_SIZE 16 +#define CHECKSUM_LEN 2 + +#define MAX_DATA_SIZE 254 + +#define NDEF_MAPPING_VERSION 0x10 +#define MAX_READ_BLOCKS_PER_CHECK 0x04 +#define MAX_WRITE_BLOCKS_PER_UPDATE 0x01 +#define MAX_BLOCKS_FOR_NDEF_DATA 0x000D +#define ATTR_BLOCK_WRITE_FLAG 0x00 +#define ATTR_BLOCK_RW_FLAG 0x01 + +#define IC_TYPE_OFFSET 12 +#define SYSTEM_OPTION_OFFSET 17 +#define FELICA_LITE_MC_BLOCK 0x88 +#define FELICA_LITE_IC_TYPE 0xF0 +#define FELICA_LITE_S_IC_TYPE 0xF1 +#define FELICA_PLUG_IC_TYPE 0xE0 + +#define FELICA_LITE_AND_LITE_S_SYS_CODE 0x88B4 + +struct type3_cmd { + uint8_t len; + uint8_t cmd; + uint8_t data[MAX_DATA_SIZE]; +} __attribute__((packed)); + +struct type3_tag { + uint32_t adapter_idx; + uint16_t current_block; + uint8_t IDm[LEN_ID]; + + near_tag_io_cb cb; + struct near_tag *tag; +}; + +struct t3_cookie { + uint32_t adapter_idx; + uint32_t target_idx; + near_tag_io_cb cb; + uint8_t IDm[LEN_ID]; + uint8_t current_block; + uint8_t attr[BLOCK_SIZE]; + struct near_ndef_message *ndef; + uint8_t ic_type; + uint8_t mc_block[BLOCK_SIZE]; +}; + +static int t3_cookie_release(int err, void *data) +{ + struct t3_cookie *cookie = data; + + DBG("%p", cookie); + + if (cookie == NULL) + return err; + + if (cookie->cb != NULL) + cookie->cb(cookie->adapter_idx, cookie->target_idx, err); + + if (cookie->ndef != NULL) + g_free(cookie->ndef->data); + + g_free(cookie->ndef); + g_free(cookie); + cookie = NULL; + + return err; +} + +/* common: Initialize structure to write block */ +static void prepare_write_block(uint8_t *UID, struct type3_cmd *cmd, + uint8_t block, uint8_t *data) +{ + cmd->cmd = CMD_WRITE_WO_ENCRYPT; /* command */ + memcpy(cmd->data, UID, LEN_ID); /* IDm */ + + cmd->data[LEN_ID] = 1; /* number of services */ + cmd->data[LEN_ID + 1] = 0x09; /* service 0x0009 */ + cmd->data[LEN_ID + 2] = 0x00; + + cmd->data[LEN_ID + 3] = 0x01; /* number of blocks */ + cmd->data[LEN_ID + 4] = 0x80; /* 2 byte block number format */ + cmd->data[LEN_ID + 5] = block; /* block number */ + memcpy(cmd->data + LEN_ID + 6, data, BLOCK_SIZE); /* data to write */ + + cmd->len = LEN_ID + LEN_CMD + LEN_CMD_LEN + 6 + BLOCK_SIZE; +} + +/* common: Initialize structure to read block */ +static void prepare_read_block(uint8_t cur_block, + uint8_t *UID, + struct type3_cmd *cmd) +{ + cmd->cmd = CMD_READ_WO_ENCRYPT; /* command */ + memcpy(cmd->data, UID, LEN_ID); /* IDm */ + + cmd->data[LEN_ID] = 1; /* number of service */ + cmd->data[LEN_ID + 1] = 0x0B; /* service x000B */ + cmd->data[LEN_ID + 2] = 0x00; + + cmd->data[LEN_ID + 3] = 0x01; /* number of block */ + cmd->data[LEN_ID + 4] = 0x80; /* 2 bytes block id*/ + cmd->data[LEN_ID + 5] = cur_block; /* block number */ + + cmd->len = LEN_ID + LEN_CMD + LEN_CMD_LEN + 6; +} + +/* common: Simple checks on received frame */ +static int check_recv_frame(uint8_t *resp, uint8_t reply_code) +{ + if (resp[OFS_NFC_STATUS] != 0) { + DBG("NFC Command failed: 0x%x", resp[OFS_NFC_STATUS]); + return -EIO; + } + + if (resp[OFS_CMD_RESP] != reply_code) { + DBG("Felica cmd failed: 0x%x", resp[OFS_CMD_RESP]); + return -EIO; + } + + return 0; +} + +static int data_recv(uint8_t *resp, int length, void *data) +{ + struct type3_tag *tag = data; + struct type3_cmd cmd; + uint8_t *nfc_data; + size_t current_length, length_read, data_length; + uint32_t adapter_idx; + uint32_t target_idx; + int read_blocks; + int err; + + DBG("%d", length); + + adapter_idx = near_tag_get_adapter_idx(tag->tag); + target_idx = near_tag_get_target_idx(tag->tag); + + if (length < 0) { + err = -EIO; + goto out_err; + } + + nfc_data = near_tag_get_data(tag->tag, &data_length); + length_read = length - OFS_READ_DATA; + current_length = tag->current_block * BLOCK_SIZE; + if (current_length + (length - OFS_READ_DATA) > data_length) + length_read = data_length - current_length; + + memcpy(nfc_data + current_length, resp + OFS_READ_DATA, length_read); + + if (current_length + length_read >= data_length) { + GList *records; + + tag->current_block = 0; + + DBG("Done reading %zd bytes at %p", data_length, nfc_data); + records = near_ndef_parse_msg(nfc_data, data_length); + near_tag_add_records(tag->tag, records, tag->cb, 0); + + g_free(tag); + + return 0; + } + + /* Read the next block */ + read_blocks = length / BLOCK_SIZE; + tag->current_block += read_blocks; + + prepare_read_block(DATA_BLOCK_START + tag->current_block, + tag->IDm, &cmd); + + err = near_adapter_send(adapter_idx, (uint8_t *) &cmd, cmd.len, + data_recv, tag, NULL); + + if (err < 0) + goto out_err; + + return 0; + +out_err: + if (err < 0 && tag->cb) + tag->cb(adapter_idx, target_idx, err); + + g_free(tag); + + return err; +} + +static int data_read(struct type3_tag *tag) +{ + struct type3_cmd cmd; + uint32_t adapter_idx; + + DBG(""); + + tag->current_block = 0; + + prepare_read_block(DATA_BLOCK_START + tag->current_block, + tag->IDm, &cmd ); + + adapter_idx = near_tag_get_adapter_idx(tag->tag); + + return near_adapter_send(adapter_idx, + (uint8_t *) &cmd, cmd.len, + data_recv, tag, NULL); +} + +/* Read block 0 to retrieve the data length */ +static int nfctype3_recv_block_0(uint8_t *resp, int length, void *data) +{ + struct t3_cookie *cookie = data; + int err = 0; + struct near_tag *tag; + struct type3_tag *t3_tag = NULL; + uint32_t ndef_data_length; + + DBG("%d", length); + + if (length < 0) { + err = -EIO; + goto out_err; + } + + err = check_recv_frame(resp, RESP_READ_WO_ENCRYPT); + if (err < 0) + goto out_err; + + if (resp[OFS_READ_FLAG] != 0) { + DBG("Status 0x%x", resp[OFS_READ_FLAG]); + err = -EIO; + goto out_err; + } + + /* Block 0:[11 - 13]: length is a 3 bytes value */ + ndef_data_length = resp[OFS_READ_DATA + 11] * 0x100; + ndef_data_length += resp[OFS_READ_DATA + 12]; + ndef_data_length *= 0x100; + ndef_data_length += resp[OFS_READ_DATA + 13]; + + /* Add data to the tag */ + err = near_tag_add_data(cookie->adapter_idx, cookie->target_idx, + NULL, ndef_data_length); + if (err < 0) + goto out_err; + + tag = near_tag_get_tag(cookie->adapter_idx, cookie->target_idx); + if (tag == NULL) { + err = -ENOMEM; + goto out_err; + } + + /* Block 0:[10]: RW Flag. 1 for RW */ + if (resp[OFS_READ_DATA + 10] == 0) + near_tag_set_ro(tag, TRUE); + else + near_tag_set_ro(tag, FALSE); + + t3_tag = g_try_malloc0(sizeof(struct type3_tag)); + if (t3_tag == NULL) { + err = -ENOMEM; + goto out_err; + } + + memcpy(t3_tag->IDm, cookie->IDm, LEN_ID); + + near_tag_set_idm(tag, cookie->IDm, LEN_ID); + near_tag_set_attr_block(tag, resp + OFS_READ_DATA, BLOCK_SIZE); + near_tag_set_blank(tag, FALSE); + near_tag_set_ic_type(tag, cookie->ic_type); + + t3_tag->adapter_idx = cookie->adapter_idx; + t3_tag->cb = cookie->cb; + t3_tag->tag = tag; + + err = data_read(t3_tag); + + /* + * As reading isn't complete, + * callback shouldn't be called while freeing the cookie + */ + if (err == 0) + cookie->cb = NULL; + +out_err: + if (err < 0) + g_free(t3_tag); + + return t3_cookie_release(err, cookie); +} + +static int poll_ndef_system_code(uint8_t *resp, int length, void *data) +{ + struct t3_cookie *cookie = data; + int err = 0; + struct type3_cmd cmd; + + DBG("length: %d", length); + + if (length < 0) { + err = -EIO; + goto out_err; + } + + err = check_recv_frame(resp, RESP_POLL); + if (err < 0) + goto out_err; + + prepare_read_block(META_BLOCK_START, cookie->IDm, &cmd); + + err = near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd, + cmd.len, nfctype3_recv_block_0, cookie, NULL); + if (err < 0) + goto out_err; + + return 0; + +out_err: + return t3_cookie_release(err, cookie); +} + +static int check_sys_op_in_mc_block(uint8_t *resp, int length, void *data) +{ + struct type3_cmd cmd; + struct near_tag *tag; + struct t3_cookie *cookie = data; + int err = 0; + + DBG("length %d", length); + + if (length < 0) { + err = -EIO; + goto out_err; + } + + err = check_recv_frame(resp, RESP_READ_WO_ENCRYPT); + if (err < 0) + goto out_err; + + if (resp[SYSTEM_OPTION_OFFSET] == 0x00) { + DBG("Blank tag detected"); + + err = near_tag_add_data(cookie->adapter_idx, + cookie->target_idx, + NULL, 1 /* dummy length */); + if (err < 0) + goto out_err; + + tag = near_tag_get_tag(cookie->adapter_idx, + cookie->target_idx); + if (tag == NULL) { + err = -ENOMEM; + goto out_err; + } + + near_tag_set_idm(tag, cookie->IDm, LEN_ID); + near_tag_set_ic_type(tag, cookie->ic_type); + near_tag_set_blank(tag, TRUE); + + return t3_cookie_release(0, cookie); + } else { + /* CMD POLL */ + cmd.cmd = CMD_POLL; /* POLL command */ + cmd.data[0] = 0x12; /* System code (NFC SC) */ + cmd.data[1] = 0xFC; + cmd.data[2] = 01; /* request code */ + cmd.data[3] = 0x00; /* time slot */ + /* data len + 2 bytes */ + cmd.len = LEN_CMD + LEN_CMD_LEN + 4 ; + + err = near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd, + cmd.len , poll_ndef_system_code, cookie, NULL); + } + + if (err < 0) + goto out_err; + + return 0; + +out_err: + return t3_cookie_release(err, cookie); +} + +static int receive_system_code(uint8_t *resp, int length, void *data) +{ + struct t3_cookie *cookie = data; + int err = 0; + struct type3_cmd cmd; + uint16_t system_code; + + DBG("length: %d", length); + + if (length < 0) { + err = -EIO; + goto out_err; + } + + err = check_recv_frame(resp, RESP_POLL); + if (err < 0) + goto out_err; + + cookie->ic_type = resp[IC_TYPE_OFFSET]; + memcpy(cookie->IDm, resp + OFS_IDM, LEN_ID); + system_code = ((uint16_t) (resp[length - 2])) << 8; + system_code |= resp[length - 1]; + + switch (resp[IC_TYPE_OFFSET]) { + case FELICA_LITE_IC_TYPE: + prepare_read_block(FELICA_LITE_MC_BLOCK, cookie->IDm, &cmd); + err = near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd, + cmd.len, check_sys_op_in_mc_block, + cookie, NULL); + break; + + case FELICA_LITE_S_IC_TYPE: + case FELICA_PLUG_IC_TYPE: + /* CMD POLL */ + cmd.cmd = CMD_POLL; /* POLL command */ + cmd.data[0] = 0x12; /* System code (NFC SC) */ + cmd.data[1] = 0xFC; + cmd.data[2] = 01; /* request code */ + cmd.data[3] = 0x00; /* time slot */ + /* data len + 2 bytes */ + cmd.len = LEN_CMD + LEN_CMD_LEN + 4 ; + + err = near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd, + cmd.len, poll_ndef_system_code, cookie, + NULL); + } + + if (err < 0) + goto out_err; + + return 0; + +out_err: + return t3_cookie_release(err, cookie); +} + +static int nfctype3_read(uint32_t adapter_idx, + uint32_t target_idx, near_tag_io_cb cb) +{ + struct type3_cmd cmd; + struct t3_cookie *cookie; + + DBG(""); + + /* CMD POLL */ + cmd.cmd = CMD_POLL; /* POLL command */ + cmd.data[0] = 0xFF; /* System code */ + cmd.data[1] = 0xFF; + cmd.data[2] = 01; /* request code */ + cmd.data[3] = 0x00; /* time slot */ + + /* data len + 2 bytes */ + cmd.len = LEN_CMD + LEN_CMD_LEN + 4; + + cookie = g_try_malloc0(sizeof(struct t3_cookie)); + if (cookie == NULL) + return -ENOMEM; + + cookie->adapter_idx = adapter_idx; + cookie->target_idx = target_idx; + cookie->cb = cb; + + return near_adapter_send(adapter_idx, (uint8_t *) &cmd, cmd.len, + receive_system_code, cookie, + t3_cookie_release); +} + +static int update_attr_block_cb(uint8_t *resp, int length, void *data) +{ + struct t3_cookie *cookie = data; + int err; + + DBG(""); + + if (length < 0) { + err = -EIO; + goto out_err; + } + + err = check_recv_frame(resp, RESP_WRITE_WO_ENCRYPT); + if (err < 0) + goto out_err; + + DBG("Done writing"); + +out_err: + return t3_cookie_release(err, cookie); +} + +static int update_attr_block(struct t3_cookie *cookie) +{ + struct type3_cmd cmd; + uint16_t checksum; + uint8_t i; + + DBG(""); + + cookie->attr[9] = 0x00; /* writing data completed */ + cookie->attr[11] = (uint8_t) (cookie->ndef->length >> 16); + cookie->attr[12] = (uint8_t) (cookie->ndef->length >> 8); + cookie->attr[13] = (uint8_t) cookie->ndef->length; + checksum = 0; + + for (i = 0; i < (BLOCK_SIZE - CHECKSUM_LEN); i++) + checksum += cookie->attr[i]; + + cookie->attr[14] = (uint8_t) (checksum >> 8); + cookie->attr[15] = (uint8_t) checksum; + + prepare_write_block(cookie->IDm, &cmd, 0, cookie->attr); + + return near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd, cmd.len, + update_attr_block_cb, cookie, + t3_cookie_release); +} + +static int data_write_resp(uint8_t *resp, int length, void *data) +{ + struct t3_cookie *cookie = data; + struct type3_cmd cmd; + uint8_t padding[BLOCK_SIZE] = {0}; + int err; + + DBG(""); + + if (length < 0) { + err = -EIO; + goto out_err; + } + + err = check_recv_frame(resp, RESP_WRITE_WO_ENCRYPT); + if (err < 0) + goto out_err; + + if (cookie->ndef->offset >= cookie->ndef->length) { + err = update_attr_block(cookie); + if (err < 0) + goto out_err; + + return 0; + } + + if ((cookie->ndef->length - cookie->ndef->offset) < + BLOCK_SIZE) { + memcpy(padding, cookie->ndef->data + cookie->ndef->offset, + cookie->ndef->length - cookie->ndef->offset); + prepare_write_block(cookie->IDm, &cmd, + cookie->current_block, padding); + } else { + prepare_write_block(cookie->IDm, &cmd, cookie->current_block, + cookie->ndef->data + cookie->ndef->offset); + } + + cookie->current_block++; + cookie->ndef->offset += BLOCK_SIZE; + + err = near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd, cmd.len, + data_write_resp, cookie, NULL); + if (err < 0) + goto out_err; + + return 0; + +out_err: + return t3_cookie_release(err, cookie); +} + +static int data_write(uint32_t adapter_idx, uint32_t target_idx, + struct near_ndef_message *ndef, + struct near_tag *tag, + near_tag_io_cb cb) +{ + struct t3_cookie *cookie; + struct type3_cmd cmd; + uint16_t checksum, nmaxb; + uint8_t i, len = 0; + uint8_t *idm, *attr; + int err; + + DBG(""); + + cookie = g_try_malloc0(sizeof(struct t3_cookie)); + + if (cookie == NULL) { + err = -ENOMEM; + + if (cb != NULL) + cb(adapter_idx, target_idx, err); + + return err; + } + + cookie->adapter_idx = adapter_idx; + cookie->target_idx = target_idx; + cookie->ndef = ndef; + cookie->cb = cb; + cookie->current_block = 0; + + idm = near_tag_get_idm(tag, &len); + if (idm == NULL) + return t3_cookie_release(-EINVAL, cookie); + + memcpy(cookie->IDm, idm, len); + + attr = near_tag_get_attr_block(tag, &len); + if (attr == NULL) + return t3_cookie_release(-EINVAL, cookie); + + memcpy(cookie->attr, attr, len); + nmaxb = (((uint16_t) (cookie->attr[3])) << 8) | cookie->attr[4]; + + if (cookie->ndef->length > (nmaxb * BLOCK_SIZE)) { + near_error("not enough space on tag"); + + return t3_cookie_release(-ENOSPC, cookie); + } + + cookie->attr[9] = 0x0F; /* writing data in progress */ + checksum = 0; + + for (i = 0; i < 14; i++) + checksum += cookie->attr[i]; + + cookie->attr[14] = (uint8_t) (checksum >> 8); + cookie->attr[15] = (uint8_t) checksum; + + prepare_write_block(cookie->IDm, &cmd, cookie->current_block, + cookie->attr); + cookie->current_block++; + + return near_adapter_send(adapter_idx, (uint8_t *) &cmd, cmd.len, + data_write_resp, cookie, + t3_cookie_release); +} + +static int nfctype3_write(uint32_t adapter_idx, uint32_t target_idx, + struct near_ndef_message *ndef, + near_tag_io_cb cb) +{ + struct near_tag *tag; + int err; + + DBG(""); + + if (ndef == NULL || cb == NULL) { + err = -EINVAL; + goto out_err; + } + + tag = near_tag_get_tag(adapter_idx, target_idx); + if (tag == NULL) { + err = -EINVAL; + goto out_err; + } + + err = data_write(adapter_idx, target_idx, ndef, tag, cb); + +out_err: + if (cb != NULL) + cb(adapter_idx, target_idx, err); + + return err; +} + +static int check_presence(uint8_t *resp, int length, void *data) +{ + struct t3_cookie *cookie = data; + int err = 0; + + DBG("length %d", length); + + if (length < 0) + err = -EIO; + + return t3_cookie_release(err, cookie); +} + +static int nfctype3_check_presence(uint32_t adapter_idx, + uint32_t target_idx, near_tag_io_cb cb) +{ + struct type3_cmd cmd; + struct t3_cookie *cookie; + + DBG(""); + + /* CMD POLL */ + cmd.cmd = CMD_POLL; /* POLL command */ + cmd.data[0] = 0xFF; /* System code */ + cmd.data[1] = 0xFF; + cmd.data[2] = 01; /* request code */ + cmd.data[3] = 0x00; /* time slot */ + + /* data len + 2 bytes */ + cmd.len = LEN_CMD + LEN_CMD_LEN + 4 ; + + cookie = g_try_malloc0(sizeof(struct t3_cookie)); + if (cookie == NULL) + return -ENOMEM; + + cookie->adapter_idx = adapter_idx; + cookie->target_idx = target_idx; + cookie->cb = cb; + + return near_adapter_send(adapter_idx, (uint8_t *) &cmd, + cmd.len, check_presence, cookie, + t3_cookie_release); +} + +static int format_resp(uint8_t *resp, int length, void *data) +{ + struct near_tag *tag; + struct t3_cookie *cookie = data; + int err; + + DBG(""); + + if (length < 0) { + err = -EIO; + goto out_err; + } + + err = check_recv_frame(resp, RESP_WRITE_WO_ENCRYPT); + if (err < 0) + goto out_err; + + tag = near_tag_get_tag(cookie->adapter_idx, cookie->target_idx); + if (tag == NULL) { + err = -ENOMEM; + goto out_err; + } + + near_tag_set_ro(tag, FALSE); + near_tag_set_idm(tag, cookie->IDm, LEN_ID); + near_tag_set_attr_block(tag, cookie->attr, BLOCK_SIZE); + near_tag_set_blank(tag, FALSE); + + DBG("Formatting is done"); + +out_err: + return t3_cookie_release(err, cookie); +} + +static int write_attr_block(uint8_t *resp, int length , void *data) +{ + struct type3_cmd cmd; + struct t3_cookie *cookie = data; + int err, i; + uint16_t checksum = 0; + + DBG("length %d", length); + + if (length < 0) { + err = -EIO; + goto out_err; + } + + err = check_recv_frame(resp, RESP_WRITE_WO_ENCRYPT); + if (err < 0) + goto out_err; + + cookie->attr[0] = NDEF_MAPPING_VERSION; + cookie->attr[1] = MAX_READ_BLOCKS_PER_CHECK; + cookie->attr[2] = MAX_WRITE_BLOCKS_PER_UPDATE; + cookie->attr[3] = (uint8_t) (MAX_BLOCKS_FOR_NDEF_DATA >> 8); + cookie->attr[4] = (uint8_t) (MAX_BLOCKS_FOR_NDEF_DATA); + cookie->attr[5] = 0; + cookie->attr[6] = 0; + cookie->attr[7] = 0; + cookie->attr[8] = 0; + cookie->attr[9] = ATTR_BLOCK_WRITE_FLAG; + cookie->attr[10] = ATTR_BLOCK_RW_FLAG; + cookie->attr[11] = 0; + cookie->attr[12] = 0; + cookie->attr[13] = 0; + + for (i = 0; i < (BLOCK_SIZE - CHECKSUM_LEN); i++) + checksum += cookie->attr[i]; + + cookie->attr[14] = (uint8_t) (checksum >> 8); + cookie->attr[15] = (uint8_t) checksum; + + prepare_write_block(cookie->IDm, &cmd, META_BLOCK_START, + cookie->attr); + + err = near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd, + cmd.len, format_resp, cookie, NULL); + if (err < 0) + goto out_err; + + return 0; + +out_err: + return t3_cookie_release(err, cookie); +} + +static int write_mc_block(uint8_t *resp, int length, void *data) +{ + struct type3_cmd cmd; + struct t3_cookie *cookie = data; + int err; + + DBG("length %d", length); + + if (length < 0) { + err = -EIO; + goto out_err; + } + + err = check_recv_frame(resp, RESP_READ_WO_ENCRYPT); + if (err < 0) + goto out_err; + + if (resp[OFS_READ_FLAG] != 0) { + DBG("Status 0x%x", resp[OFS_READ_FLAG]); + err = -EIO; + goto out_err; + } + + memcpy(cookie->mc_block, resp + 14, BLOCK_SIZE); + /* + * By updating Byte3 to 01h means making Felica Lite + * compatible with NDEF. + */ + cookie->mc_block[3] = 1; + prepare_write_block(cookie->IDm, &cmd, FELICA_LITE_MC_BLOCK, + cookie->mc_block); + err = near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd, + cmd.len, write_attr_block, cookie, NULL); + if (err < 0) + goto out_err; + + return 0; + +out_err: + return t3_cookie_release(err, cookie); +} + +static int nfctype3_format(uint32_t adapter_idx, + uint32_t target_idx, near_tag_io_cb cb) +{ + struct type3_cmd cmd; + struct near_tag *tag; + struct t3_cookie *cookie; + uint8_t ic_type; + uint8_t *idm, len; + + DBG(""); + + tag = near_tag_get_tag(adapter_idx, target_idx); + if (tag == NULL) + return -ENOMEM; + + ic_type = near_tag_get_ic_type(tag); + if (ic_type != FELICA_LITE_IC_TYPE) + return -EOPNOTSUPP; + + cookie = g_try_malloc0(sizeof(struct t3_cookie)); + if (cookie == NULL) + return -ENOMEM; + + cookie->adapter_idx = adapter_idx; + cookie->target_idx = target_idx; + cookie->cb = cb; + cookie->ic_type = ic_type; + + idm = near_tag_get_idm(tag, &len); + if (idm == NULL) + return t3_cookie_release(-EINVAL, cookie); + + memcpy(cookie->IDm, idm, len); + + prepare_read_block(FELICA_LITE_MC_BLOCK, cookie->IDm, &cmd); + + return near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd, cmd.len, + write_mc_block, cookie, + t3_cookie_release); + +} + +static struct near_tag_driver type1_driver = { + .type = NFC_PROTO_FELICA, + .priority = NEAR_TAG_PRIORITY_DEFAULT, + .read = nfctype3_read, + .write = nfctype3_write, + .format = nfctype3_format, + .check_presence = nfctype3_check_presence, +}; + +static int nfctype3_init(void) +{ + DBG(""); + + return near_tag_driver_register(&type1_driver); +} + +static void nfctype3_exit(void) +{ + DBG(""); + + near_tag_driver_unregister(&type1_driver); +} + +NEAR_PLUGIN_DEFINE(nfctype3, "NFC Forum Type 3 tags support", VERSION, + NEAR_PLUGIN_PRIORITY_HIGH, nfctype3_init, nfctype3_exit) diff --git a/plugins/nfctype4.c b/plugins/nfctype4.c new file mode 100644 index 0000000..694d613 --- /dev/null +++ b/plugins/nfctype4.c @@ -0,0 +1,1481 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdint.h> +#include <errno.h> +#include <string.h> +#include <sys/socket.h> + +#include <linux/socket.h> + +#include <near/nfc.h> +#include <near/plugin.h> +#include <near/log.h> +#include <near/types.h> +#include <near/adapter.h> +#include <near/tag.h> +#include <near/ndef.h> +#include <near/tlv.h> + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +#define NFC_STATUS 0 +#define NFC_STATUS_BYTE_LEN 1 + +#define STATUS_WORD_1 1 +#define STATUS_WORD_2 2 +#define APDU_HEADER_LEN 5 +#define APDU_OK 0x9000 +#define APDU_NOT_FOUND 0x6A82 + +/* PICC Level Commands */ +#define PICC_CLASS 0x90 +#define GET_VERSION 0x60 +#define CREATE_APPLICATION 0xCA +#define SELECT_APPLICATION 0x5A +#define CREATE_STD_DATA_FILE 0xCD +#define WRITE_DATA_TO_FILE 0x3D + +#define DESFire_EV1_MAJOR_VERSION 0x01 +#define PICC_LEVEL_APDU_OK 0x9100 +#define GET_VERSION_FRAME_RESPONSE_BYTE 0xAF +#define DESFIRE_KEY_SETTINGS 0x0F +#define DESFIRE_NUM_OF_KEYS 0x21 +#define DESFIRE_CC_FILE_NUM 0x01 +#define DESFIRE_NDEF_FILE_NUM 0x02 +#define DESFIRE_COMMSET 0x00 +#define MAPPING_VERSION 0x20 +#define FREE_READ_ACCESS 0x00 +#define FREE_WRITE_ACCESS 0x00 + +#define T4_ALL_ACCESS 0x00 +#define T4_READ_ONLY 0xFF + +#define APDU_STATUS(a) (g_ntohs(*((uint16_t *)(a)))) + +/* Tag Type 4 version ID */ +static uint8_t iso_appname_v1[] = { 0xd2, 0x76, 0x0, 0x0, 0x85, 0x01, 0x0 }; +static uint8_t iso_appname_v2[] = { 0xd2, 0x76, 0x0, 0x0, 0x85, 0x01, 0x1 }; + +/* Tag T4 File ID */ +static uint8_t iso_cc_fileid[] = { 0xe1, 0x03 }; +#define LEN_ISO_CC_FILEID 2 + +#define LEN_ISO_CC_READ_SIZE 0x0F + +#define CMD_HEADER_SIZE 5 +struct type4_cmd { /* iso 7816 */ + uint8_t class; + uint8_t instruction; + uint8_t param1; + uint8_t param2; + uint8_t data_length; + uint8_t data[]; +} __attribute__((packed)); + +struct type4_NDEF_file_control_tlv { + uint8_t tag ; /* should be 4 */ + uint8_t len ; /* should be 6 */ + uint16_t file_id ; + uint16_t max_ndef_size ; + uint8_t read_access ; + uint8_t write_access ; +} __attribute__((packed)); + +struct type4_cc { /* Capability Container */ + uint16_t CCLEN; + uint8_t mapping_version; + uint16_t max_R_apdu_data_size; + uint16_t max_C_apdu_data_size; + struct type4_NDEF_file_control_tlv tlv_fc ; + uint8_t tlv_blocks[]; +} __attribute__((packed)); + +struct desfire_app { + uint8_t aid[3]; + uint8_t key_settings; + uint8_t number_of_keys; + uint8_t file_id[2]; + uint8_t iso_appname[7]; +} __attribute__((packed)); + +struct desfire_std_file { + uint8_t file_num; + uint8_t file_id[2]; + uint8_t comm_set; + uint8_t access_rights[2]; + uint8_t size[3]; +} __attribute__((packed)); + +struct desfire_cc_file { + uint8_t file_num; + uint8_t offset[3]; + uint8_t max_len[3]; + uint8_t cc_len[2]; + uint8_t version; + uint8_t mle[2]; + uint8_t mlc[2]; + uint8_t ndef_tlv[4]; + uint8_t ndef_size[2]; + uint8_t read_access; + uint8_t write_access; +} __attribute__((packed)); + +struct t4_cookie { + uint32_t adapter_idx; + uint32_t target_idx; + near_tag_io_cb cb; + struct near_tag *tag; + uint16_t read_data; + uint16_t r_apdu_max_size; + uint16_t c_apdu_max_size; + uint16_t max_ndef_size; + uint8_t write_access; + struct near_ndef_message *ndef; + uint16_t memory_size; +}; + +static int t4_cookie_release(int err, void *data) +{ + struct t4_cookie *cookie = data; + + DBG("%p", cookie); + + if (cookie == NULL) + return err; + + if (err < 0 && cookie->cb) + cookie->cb(cookie->adapter_idx, cookie->target_idx, err); + + if (cookie->ndef) + g_free(cookie->ndef->data); + + g_free(cookie->ndef); + g_free(cookie); + + return err; +} + +/* ISO functions: This code prepares APDU */ +static int ISO_send_cmd(uint8_t class, + uint8_t instruction, + uint8_t param1, + uint8_t param2, + uint8_t *cmd_data, + uint8_t cmd_data_length, + near_bool_t le, + near_recv cb, + void *in_data) +{ + struct type4_cmd *cmd; + struct t4_cookie *in_rcv = in_data; + uint8_t total_cmd_length; + int err; + + DBG("CLA-%02x INS-%02x P1-%02x P2-%02x", + class, instruction, param1, param2); + + if (le == FALSE) { + if (cmd_data) + total_cmd_length = APDU_HEADER_LEN + cmd_data_length; + else + total_cmd_length = APDU_HEADER_LEN; + } else { /* Extra byte for Le */ + total_cmd_length = APDU_HEADER_LEN + cmd_data_length + 1; + } + + cmd = g_try_malloc0(total_cmd_length); + if (cmd == NULL) { + DBG("Mem alloc failed"); + err = -ENOMEM; + goto out_err; + } + + cmd->class = class; + cmd->instruction = instruction ; + cmd->param1 = param1 ; + cmd->param2 = param2 ; + cmd->data_length = cmd_data_length; + + if (cmd_data) { + memcpy(cmd->data, cmd_data, cmd_data_length); + /* The Le byte set to 0x00 defined that any length + * of PICC response is allowed */ + if (le == TRUE) + cmd->data[cmd_data_length] = 0; + } + + return near_adapter_send(in_rcv->adapter_idx, (uint8_t *) cmd, + total_cmd_length, cb, in_rcv, + t4_cookie_release); + +out_err: + /* On exit, clean memory */ + g_free(cmd); + + return err; +} + +/* ISO 7816 command: Select applications or files + * p1=0 select by "file id" + * P1=4 select by "DF name" + * */ +static int ISO_Select(uint8_t *filename, uint8_t fnamelen, uint8_t P1, + near_recv cb, void *cookie) +{ + DBG(""); + + return ISO_send_cmd( + 0x00, /* CLA */ + 0xA4, /* INS: Select file */ + P1, /* P1: select by name */ + 0x00, /* P2: First or only occurrence */ + filename, /* cmd_data */ + fnamelen, /* uint8_t cmd_data_length*/ + FALSE, + cb, + cookie); +} + +/* ISO 7816 command: Read binary data from files */ +static int ISO_ReadBinary(uint16_t offset, uint8_t readsize, + near_recv cb, void *cookie) +{ + DBG(""); + return ISO_send_cmd( + 0x00, /* CLA */ + 0xB0, /* INS: Select file */ + (uint8_t) ((offset & 0xFF00) >> 8), + (uint8_t) (offset & 0xFF), + 0, /* no data send */ + readsize, /* bytes to read */ + FALSE, + cb, + cookie); +} + +/* ISO 7816 command: Update data */ +static int ISO_Update(uint16_t offset, uint8_t nlen, + uint8_t *data, near_recv cb, void *cookie) +{ + DBG(""); + return ISO_send_cmd( + 0x00, /* CLA */ + 0xD6, /* INS: Select file */ + (uint8_t) ((offset & 0xFF00) >> 8), + (uint8_t) (offset & 0xFF), + data, /* length of NDEF data */ + nlen, /* NLEN + NDEF data */ + FALSE, + cb, + cookie); +} + +static int data_read_cb(uint8_t *resp, int length, void *data) +{ + struct t4_cookie *cookie = data ; + uint8_t *nfc_data; + size_t data_length, length_read, current_length; + uint16_t remain_bytes; + + DBG("%d", length); + + if (length < 0) + return t4_cookie_release(length, cookie); + + if (APDU_STATUS(resp + length - 2) != APDU_OK) { + DBG("Fail read_cb SW:x%04x", APDU_STATUS(resp + length - 2)); + + return t4_cookie_release(-EIO, cookie); + } + + nfc_data = near_tag_get_data(cookie->tag, &data_length); + + /* Remove SW1 / SW2 and NFC header */ + length_read = length - NFC_HEADER_SIZE - 2 ; + length = length_read; + + current_length = cookie->read_data; + + if (current_length + (length_read) > data_length) + length_read = data_length - current_length; + + memcpy(nfc_data + current_length, resp + NFC_HEADER_SIZE, length_read); + if (current_length + length_read == data_length) { + GList *records; + + DBG("Done reading"); + + records = near_ndef_parse_msg(nfc_data, data_length); + near_tag_add_records(cookie->tag, records, cookie->cb, 0); + + return t4_cookie_release(0, cookie); + } + + cookie->read_data += length ; + remain_bytes = (data_length - cookie->read_data); + + if (remain_bytes >= cookie->r_apdu_max_size) + return ISO_ReadBinary(cookie->read_data + 2, + cookie->r_apdu_max_size, data_read_cb, cookie); + else + return ISO_ReadBinary(cookie->read_data + 2, + (uint8_t) remain_bytes, data_read_cb, cookie); +} + +static int t4_readbin_NDEF_ID(uint8_t *resp, int length, void *data) +{ + struct t4_cookie *cookie = data; + struct near_tag *tag; + int err; + + DBG("%d", length); + + if (length < 0) + return t4_cookie_release(length, cookie); + + if (APDU_STATUS(resp + length - 2) != APDU_OK) { + DBG("Fail SW:x%04x", APDU_STATUS(resp + length - 2)); + + return t4_cookie_release(-EIO, cookie); + } + + /* Add data to the tag */ + err = near_tag_add_data(cookie->adapter_idx, cookie->target_idx, NULL, + g_ntohs(*((uint16_t *)(resp + NFC_STATUS_BYTE_LEN)))); + if (err < 0) + return t4_cookie_release(err, cookie); + + tag = near_tag_get_tag(cookie->adapter_idx, cookie->target_idx); + if (tag == NULL) + return t4_cookie_release(-ENOMEM, cookie); + + near_tag_set_max_ndef_size(tag, cookie->max_ndef_size); + near_tag_set_c_apdu_max_size(tag, cookie->c_apdu_max_size); + near_tag_set_blank(tag, FALSE); + + /* save the tag */ + cookie->tag = tag; + + /* Set write conditions */ + if (cookie->write_access == T4_READ_ONLY) + near_tag_set_ro(tag, TRUE); + else + near_tag_set_ro(tag, FALSE); + + /* + * TODO: see how we can get the UID value: + * near_tag_set_uid(tag, resp + NFC_HEADER_SIZE, 8); + */ + + /* Read 1st block */ + return ISO_ReadBinary(2, cookie->r_apdu_max_size - 2, + data_read_cb, cookie); +} + +static int t4_select_NDEF_ID(uint8_t *resp, int length, void *data) +{ + struct t4_cookie *cookie = data; + + DBG("%d", length); + + if (length < 0) + return t4_cookie_release(length, cookie); + + /* Check for APDU error */ + if (APDU_STATUS(resp + STATUS_WORD_1) != APDU_OK) { + DBG("Fail SW:x%04x", APDU_STATUS(resp + STATUS_WORD_1)); + + return t4_cookie_release(-EIO, cookie); + } + + /* Read 0x0f bytes, to grab the NDEF msg length */ + return ISO_ReadBinary(0, LEN_ISO_CC_READ_SIZE, + t4_readbin_NDEF_ID, cookie); +} + +static int t4_readbin_cc(uint8_t *resp, int length, void *data) +{ + struct t4_cookie *cookie = data; + struct type4_cc *read_cc; + + DBG("%d", length); + + if (length < 0) + return t4_cookie_release(length, cookie); + + /* Check APDU error ( the two last bytes of the resp) */ + if (APDU_STATUS(resp + length - 2) != APDU_OK) { + DBG("Fail SW:x%04x", APDU_STATUS(resp + length - 2)); + + return t4_cookie_release(-EIO, cookie); + } + + /* -2 for status word and -1 is for NFC first byte... */ + read_cc = g_try_malloc0(length - 2 - NFC_STATUS_BYTE_LEN); + if (read_cc == NULL) { + DBG("Mem alloc failed"); + + return t4_cookie_release(-ENOMEM, cookie); + } + + memcpy(read_cc, &resp[1], length - 2 - NFC_STATUS_BYTE_LEN) ; + + cookie->r_apdu_max_size = g_ntohs(read_cc->max_R_apdu_data_size) - + APDU_HEADER_LEN; + cookie->c_apdu_max_size = g_ntohs(read_cc->max_C_apdu_data_size); + cookie->max_ndef_size = g_ntohs(read_cc->tlv_fc.max_ndef_size); + + /* TODO 5.1.1: TLV blocks can be zero, one or more... */ + /* TODO 5.1.2: Must ignore proprietary blocks (x05)... */ + if (read_cc->tlv_fc.tag != 0x4) { + DBG("NDEF File Control tag not found"); + + return t4_cookie_release(-EINVAL, cookie); + } + + /* save rw conditions */ + cookie->write_access = read_cc->tlv_fc.write_access; + + return ISO_Select((uint8_t *) &read_cc->tlv_fc.file_id, + LEN_ISO_CC_FILEID, 0, t4_select_NDEF_ID, cookie); +} + +static int t4_select_cc(uint8_t *resp, int length, void *data) +{ + struct t4_cookie *cookie = data; + int err; + + DBG("%d", length); + + if (length < 0) + return t4_cookie_release(length, cookie); + + /* Check for APDU error */ + if (APDU_STATUS(resp + STATUS_WORD_1) != APDU_OK) { + + DBG(" Found empty tag"); + /* Add data to the tag */ + err = near_tag_add_data(cookie->adapter_idx, cookie->target_idx, + NULL, 1 /* dummy length */); + if (err < 0) + return t4_cookie_release(err, cookie); + + cookie->tag = near_tag_get_tag(cookie->adapter_idx, + cookie->target_idx); + if (cookie->tag == NULL) + return t4_cookie_release(-ENOMEM, cookie); + + near_tag_set_blank(cookie->tag, TRUE); + + if (cookie->cb) + cookie->cb(cookie->adapter_idx, cookie->target_idx, 0); + + return t4_cookie_release(0, cookie); + } + + return ISO_ReadBinary(0, LEN_ISO_CC_READ_SIZE, t4_readbin_cc, cookie); +} + +static int t4_select_file_by_name_v1(uint8_t *resp, int length, void *data) +{ + struct t4_cookie *cookie = data; + + DBG("%d", length); + + if (length < 0) + return t4_cookie_release(length, cookie); + + /* Check for APDU error */ + if (APDU_STATUS(resp + STATUS_WORD_1) != APDU_OK) { + DBG("V1 Fail SW:x%04x", APDU_STATUS(resp + STATUS_WORD_1)); + + return t4_cookie_release(-EIO, cookie); + } + + if (resp[NFC_STATUS] != 0) + return t4_cookie_release(-EIO, cookie); + + /* Jump to select phase */ + return ISO_Select(iso_cc_fileid, LEN_ISO_CC_FILEID, 0, + t4_select_cc, cookie); +} + +static int t4_select_file_by_name_v2(uint8_t *resp, int length, void *data) +{ + struct t4_cookie *cookie = data; + + DBG("%d", length); + + if (length < 0) + t4_cookie_release(length, cookie); + + /* Check for APDU error - Not found */ + if (APDU_STATUS(resp + STATUS_WORD_1) == APDU_NOT_FOUND) { + DBG("Fallback to V1"); + + return ISO_Select(iso_appname_v1, ARRAY_SIZE(iso_appname_v1), + 0x4, t4_select_file_by_name_v1, cookie); + } + + if (APDU_STATUS(resp + STATUS_WORD_1) != APDU_OK) { + DBG("V2 Fail SW:x%04x", APDU_STATUS(resp + STATUS_WORD_1)); + + return t4_cookie_release(-EIO, cookie); + } + + if (resp[NFC_STATUS] != 0) + return t4_cookie_release(-EIO, cookie); + + /* Jump to select phase */ + return ISO_Select(iso_cc_fileid, LEN_ISO_CC_FILEID, 0, t4_select_cc, + cookie); +} + +static int nfctype4_read(uint32_t adapter_idx, + uint32_t target_idx, near_tag_io_cb cb) +{ + struct t4_cookie *cookie; + + DBG(""); + + cookie = g_try_malloc0(sizeof(struct t4_cookie)); + if (cookie == NULL) + return -ENOMEM; + + cookie->adapter_idx = adapter_idx; + cookie->target_idx = target_idx; + cookie->cb = cb; + cookie->tag = NULL; + cookie->read_data = 0; + + /* Check for V2 type 4 tag */ + return ISO_Select(iso_appname_v2, ARRAY_SIZE(iso_appname_v2), + 0x4, t4_select_file_by_name_v2, cookie); +} + +static int data_write_cb(uint8_t *resp, int length, void *data) +{ + struct t4_cookie *cookie = data; + int err = 0; + + DBG("%d", length); + + if (length < 0) + return t4_cookie_release(length, cookie); + + if (APDU_STATUS(resp + length - 2) != APDU_OK) { + near_error("write failed SWx%04x", + APDU_STATUS(resp + length - 2)); + + return t4_cookie_release(-EIO, cookie); + } + + if (cookie->ndef->offset >= cookie->ndef->length) { + DBG("Done writing"); + near_adapter_disconnect(cookie->adapter_idx); + + if (cookie->cb) + cookie->cb(cookie->adapter_idx, cookie->target_idx, 0); + + return t4_cookie_release(0, cookie); + } + + if ((cookie->ndef->length - cookie->ndef->offset) > + cookie->c_apdu_max_size) { + err = ISO_Update(cookie->ndef->offset, + cookie->c_apdu_max_size, + cookie->ndef->data + cookie->ndef->offset, + data_write_cb, cookie); + cookie->ndef->offset += cookie->c_apdu_max_size; + } else { + err = ISO_Update(cookie->ndef->offset, + cookie->ndef->length - cookie->ndef->offset, + cookie->ndef->data + cookie->ndef->offset, + data_write_cb, cookie); + cookie->ndef->offset = cookie->ndef->length; + } + + if (err < 0) + return t4_cookie_release(err, cookie); + + return err; +} + +static int data_write(uint32_t adapter_idx, uint32_t target_idx, + struct near_ndef_message *ndef, + struct near_tag *tag, near_tag_io_cb cb) +{ + struct t4_cookie *cookie; + int err; + + cookie = g_try_malloc0(sizeof(struct t4_cookie)); + + if (cookie == NULL) { + err = -ENOMEM; + + if (cb != NULL) + cb(adapter_idx, target_idx, err); + + return err; + } + + cookie->adapter_idx = adapter_idx; + cookie->target_idx = target_idx; + cookie->cb = cb; + cookie->tag = NULL; + cookie->read_data = 0; + cookie->max_ndef_size = near_tag_get_max_ndef_size(tag); + cookie->c_apdu_max_size = near_tag_get_c_apdu_max_size(tag); + cookie->ndef = ndef; + + if (cookie->max_ndef_size < cookie->ndef->length) { + near_error("not enough space on tag to write data"); + + return t4_cookie_release(-ENOMEM, cookie); + } + + if ((cookie->ndef->length - cookie->ndef->offset) > + cookie->c_apdu_max_size) { + err = ISO_Update(cookie->ndef->offset, + cookie->c_apdu_max_size, + cookie->ndef->data, + data_write_cb, cookie); + cookie->ndef->offset += cookie->c_apdu_max_size; + } else { + err = ISO_Update(cookie->ndef->offset, + cookie->ndef->length, + cookie->ndef->data, + data_write_cb, cookie); + cookie->ndef->offset = cookie->ndef->length; + } + + if (err < 0) + goto out_err; + + return 0; + +out_err: + return t4_cookie_release(err, cookie); +} + +static int nfctype4_write(uint32_t adapter_idx, uint32_t target_idx, + struct near_ndef_message *ndef, near_tag_io_cb cb) +{ + struct near_tag *tag; + int err; + + DBG(""); + + if (ndef == NULL || cb == NULL) { + err = -EINVAL; + goto out_err; + } + + tag = near_tag_get_tag(adapter_idx, target_idx); + if (tag == NULL) { + err = -EINVAL; + goto out_err; + } + + err = data_write(adapter_idx, target_idx, ndef, tag, cb); + +out_err: + if (cb != NULL) + cb(adapter_idx, target_idx, err); + + return err; +} + +static int check_presence(uint8_t *resp, int length, void *data) +{ + struct t4_cookie *cookie = data; + int err = 0; + + DBG("%d", length); + + if (length < 0) + err = -EIO; + + if (cookie->cb) + cookie->cb(cookie->adapter_idx, + cookie->target_idx, err); + + return t4_cookie_release(err, cookie); +} + +static int nfctype4_check_presence(uint32_t adapter_idx, + uint32_t target_idx, near_tag_io_cb cb) +{ + struct t4_cookie *cookie; + + DBG(""); + + cookie = g_try_malloc0(sizeof(struct t4_cookie)); + if (cookie == NULL) + return -ENOMEM; + + cookie->adapter_idx = adapter_idx; + cookie->target_idx = target_idx; + cookie->cb = cb; + cookie->tag = NULL; + cookie->read_data = 0; + + /* Check for V2 type 4 tag */ + return ISO_Select(iso_appname_v2, ARRAY_SIZE(iso_appname_v2), + 0x4, check_presence, cookie); +} + +static int select_ndef_file(uint8_t *resp, int length, void *data) +{ + struct t4_cookie *cookie = data; + + DBG("%d", length); + + if (length < 0) + return t4_cookie_release(length, cookie); + + if (APDU_STATUS(resp + STATUS_WORD_1) != APDU_OK) { + near_error("select ndef file resp failed %02X", + resp[length - 1]); + + return t4_cookie_release(-EIO, cookie); + } + + DBG("ndef file selected"); + + if (cookie->cb) + cookie->cb(cookie->adapter_idx, cookie->target_idx, 0); + + return t4_cookie_release(0, cookie); +} + +static int read_cc_file(uint8_t *resp, int length, void *data) +{ + struct t4_cookie *cookie = data; + struct near_tag *tag; + struct type4_cc *read_cc = NULL; + int err = 0; + + DBG("%d", length); + + if (length < 0) { + err = length; + goto out_err; + } + + /* Check APDU error ( the two last bytes of the resp) */ + if (APDU_STATUS(resp + length - 2) != APDU_OK) { + near_error("read cc failed SWx%04x", + APDU_STATUS(resp + length - 2)); + err = -EIO; + goto out_err; + } + + /* -2 for status word and -1 is for NFC first byte... */ + read_cc = g_try_malloc0(length - 2 - NFC_STATUS_BYTE_LEN); + if (read_cc == NULL) { + err = -ENOMEM; + goto out_err; + } + + memcpy(read_cc, &resp[1], length - 2 - NFC_STATUS_BYTE_LEN) ; + cookie->c_apdu_max_size = g_ntohs(read_cc->max_C_apdu_data_size); + cookie->max_ndef_size = g_ntohs(read_cc->tlv_fc.max_ndef_size); + + tag = near_tag_get_tag(cookie->adapter_idx, cookie->target_idx); + if (tag == NULL) { + err = -EINVAL; + goto out_err; + } + + near_tag_set_max_ndef_size(tag, cookie->memory_size); + near_tag_set_c_apdu_max_size(tag, cookie->c_apdu_max_size); + + if (read_cc->tlv_fc.tag != 0x4) { + near_error("NDEF File not found") ; + err = -EINVAL ; + goto out_err; + } + + err = ISO_Select((uint8_t *)&read_cc->tlv_fc.file_id, + LEN_ISO_CC_FILEID, 0, select_ndef_file, cookie); + if (err < 0) { + near_error("select ndef file req failed %d", err); + goto out_err; + } + + g_free(read_cc); + return 0; + +out_err: + g_free(read_cc); + return t4_cookie_release(err, cookie); +} + +static int select_cc_file(uint8_t *resp, int length, void *data) +{ + int err = 0; + struct t4_cookie *cookie = data; + + if (length < 0) { + near_error("CC file select resp failed %d", length); + + return t4_cookie_release(length, cookie); + } + + if (APDU_STATUS(resp + STATUS_WORD_1) != APDU_OK) { + near_error("CC file select response %02X", + resp[length - 1]); + + return t4_cookie_release(-EIO, cookie); + } + + err = ISO_ReadBinary(0, LEN_ISO_CC_READ_SIZE, read_cc_file, cookie); + if (err < 0) { + near_error("read cc file req failed %d", err); + goto out_err; + } + + return 0; + +out_err: + return t4_cookie_release(err, cookie); +} + +static int select_iso_appname_v2(uint8_t *resp, int length, void *data) +{ + int err = 0; + struct t4_cookie *cookie = data; + + if (length < 0) { + near_error("iso app select resp failed %d", length); + + return t4_cookie_release(length, cookie); + } + + if (resp[NFC_STATUS] != 0x00) { + near_error("iso app select response %02X", + resp[length - 1]); + + return t4_cookie_release(-EIO, cookie); + } + + err = ISO_Select(iso_cc_fileid, LEN_ISO_CC_FILEID, 0, + select_cc_file, cookie); + if (err < 0) { + near_error("select cc req failed %d", err); + goto out_err; + } + + return 0; + +out_err: + return t4_cookie_release(err, cookie); +} + +static int format_resp(uint8_t *resp, int length, void *data) +{ + int err = 0; + struct t4_cookie *cookie = data; + struct near_tag *tag; + + DBG(""); + + if (length < 0) { + near_error("write data to ndef file resp failed %d", length); + + return t4_cookie_release(length, cookie); + } + + if (APDU_STATUS(resp + 1) != PICC_LEVEL_APDU_OK) { + near_error("wrtie data to ndef file response %02X", + resp[length - 1]); + + return t4_cookie_release(-EIO, cookie); + } + + tag = near_tag_get_tag(cookie->adapter_idx, cookie->target_idx); + if (tag == NULL) + return t4_cookie_release(-EINVAL, cookie); + + DBG("Formatting is done"); + near_tag_set_blank(tag, FALSE); + + /* + * 1) Till now all commands which are used for formatting are + * at mifare desfire level. Now select iso appname_v2, + * cc file and ndef file with ISO 7816-4 commands. + * 2) Selecting ndef file means making sure that read write + * operations will perform on NDEF file. + */ + err = ISO_Select(iso_appname_v2, ARRAY_SIZE(iso_appname_v2), + 0x4, select_iso_appname_v2, cookie); + if (err < 0) { + near_error("iso_select appnamev2 req failed %d", err); + goto out_err; + } + + return err; + +out_err: + return t4_cookie_release(err, cookie); +} + +static int write_data_to_ndef_file(uint8_t *resp, int length, void *data) +{ + int err = 0; + struct t4_cookie *cookie = data; + uint8_t *cmd_data = NULL; + uint8_t cmd_data_length; + uint8_t ndef_file_offset[] = {0x00, 0x00, 0x00}; + uint8_t empty_ndef_file_len[] = {0x02, 0x00, 0x00}; /* 000002h */ + uint8_t ndef_nlen[] = {0x00, 0x00}; /* 0000h */ + + DBG(""); + + if (length < 0) { + near_error("create ndef file resp failed %d", length); + err = length; + goto out_err; + } + + if (APDU_STATUS(resp + 1) != PICC_LEVEL_APDU_OK) { + near_error("create ndef file response %02X", + resp[length - 1]); + err = -EIO; + goto out_err; + } + + /* Step8 : Write data to NDEF file ( no NDEF message) */ + cmd_data_length = 1 /* File num */ + + ARRAY_SIZE(ndef_file_offset) + + ARRAY_SIZE(empty_ndef_file_len) + + ARRAY_SIZE(ndef_nlen); + + cmd_data = g_try_malloc0(cmd_data_length); + if (cmd_data == NULL) { + err = -ENOMEM; + goto out_err; + } + + cmd_data[0] = DESFIRE_NDEF_FILE_NUM; + memcpy(cmd_data + 1, ndef_file_offset, ARRAY_SIZE(ndef_file_offset)); + memcpy(cmd_data + 4, empty_ndef_file_len, + ARRAY_SIZE(empty_ndef_file_len)); + memcpy(cmd_data + 7, ndef_nlen, ARRAY_SIZE(ndef_nlen)); + + err = ISO_send_cmd(PICC_CLASS, WRITE_DATA_TO_FILE, + 0x00, 0x00, cmd_data, cmd_data_length, + TRUE, format_resp, cookie); + if (err < 0) { + near_error("wrtie data to ndef file req failed %d", err); + goto out_err; + } + + g_free(cmd_data); + return 0; + +out_err: + g_free(cmd_data); + return t4_cookie_release(err, cookie); +} + +static int create_ndef_file(uint8_t *resp, int length, void *data) +{ + int err = 0; + struct t4_cookie *cookie = data; + struct desfire_std_file *ndef = NULL; + uint8_t iso_ndef_file_id[] = {0x04, 0xE1}; /* E104h */ + uint8_t ndef_file_access_rights[] = {0xE0, 0xEE}; /* EEE0h */ + + DBG(""); + + if (length < 0) { + near_error("write data to cc file resp failed %d", length); + err = length; + goto out_err; + } + + if (APDU_STATUS(resp + 1) != PICC_LEVEL_APDU_OK) { + near_error("write data to cc file response %02X", + resp[length - 1]); + err = -EIO; + goto out_err; + } + + ndef = g_try_malloc0(sizeof(struct desfire_std_file)); + if (ndef == NULL) { + err = -ENOMEM; + goto out_err; + } + + ndef->file_num = DESFIRE_NDEF_FILE_NUM; + memcpy(ndef->file_id, iso_ndef_file_id, ARRAY_SIZE(iso_ndef_file_id)); + ndef->comm_set = DESFIRE_COMMSET; + memcpy(ndef->access_rights, ndef_file_access_rights, + ARRAY_SIZE(ndef_file_access_rights)); + ndef->size[0] = 0; + ndef->size[1] = (uint8_t) (cookie->memory_size >> 8); + ndef->size[2] = (uint8_t) cookie->memory_size; + + err = ISO_send_cmd(PICC_CLASS, CREATE_STD_DATA_FILE, + 0x00, 0x00, (uint8_t *)ndef, + sizeof(struct desfire_std_file), + TRUE, write_data_to_ndef_file, cookie); + if (err < 0) { + near_error("create ndef file req failed %d", err); + goto out_err; + } + + g_free(ndef); + return 0; + +out_err: + g_free(ndef); + return t4_cookie_release(err, cookie); +} + +static int write_data_to_cc_file(uint8_t *resp, int length, void *data) +{ + int err = 0; + struct t4_cookie *cookie = data; + struct desfire_cc_file *cc = NULL; + uint8_t cc_file_offset[] = {0x00, 0x00, 0x00}; + uint8_t cc_file_max_len[] = {0x0F, 0x00, 0x00}; /* 00000Fh */ + uint8_t cc_len[] = {0x00, 0x0F}; /* 000Fh*/ + uint8_t mle_r_apdu[] = {0x00, 0x3B}; /* 003Bh */ + uint8_t mlc_c_apdu[] = {0x00, 0x34}; /* 0034h */ + /* T: 04, L: 06: V: E104h (NDEF ISO FID = E104h)*/ + uint8_t ndef_tlv[] = {0x04, 0x06, 0xE1, 0x04}; + + + DBG(""); + + if (length < 0) { + near_error("create cc file resp failed %d", length); + err = length; + goto out_err; + } + + if (APDU_STATUS(resp + 1) != PICC_LEVEL_APDU_OK) { + near_error("create cc file response %02X", + resp[length - 1]); + err = -EIO; + goto out_err; + } + + cc = g_try_malloc0(sizeof(struct desfire_cc_file)); + if (cc == NULL) { + err = -ENOMEM; + goto out_err; + } + + cc->file_num = DESFIRE_CC_FILE_NUM; + memcpy(cc->offset, cc_file_offset, ARRAY_SIZE(cc_file_offset)); + memcpy(cc->max_len, cc_file_max_len, ARRAY_SIZE(cc_file_max_len)); + memcpy(cc->cc_len, cc_len, ARRAY_SIZE(cc_len)); + cc->version = MAPPING_VERSION; + memcpy(cc->mle, mle_r_apdu, ARRAY_SIZE(mle_r_apdu)); + memcpy(cc->mlc, mlc_c_apdu, ARRAY_SIZE(mlc_c_apdu)); + memcpy(cc->ndef_tlv, ndef_tlv, ARRAY_SIZE(ndef_tlv)); + cc->ndef_size[0] = (uint8_t) (cookie->memory_size >> 8); + cc->ndef_size[1] = (uint8_t) cookie->memory_size; + cc->read_access = FREE_READ_ACCESS; + cc->write_access = FREE_WRITE_ACCESS; + + err = ISO_send_cmd(PICC_CLASS, WRITE_DATA_TO_FILE, + 0x00, 0x00, (uint8_t *)cc, + sizeof(struct desfire_cc_file), + TRUE, create_ndef_file, cookie); + if (err < 0) { + near_error("write data to cc file req failed %d", err); + goto out_err; + } + + g_free(cc); + return 0; + +out_err: + g_free(cc); + return t4_cookie_release(err, cookie); +} + +static int create_cc_file(uint8_t *resp, int length, void *data) +{ + int err = 0; + struct t4_cookie *cookie = data; + struct desfire_std_file *cc = NULL; + uint8_t iso_cc_file_id[] = {0x03, 0xe1}; /* E103h */ + uint8_t cc_file_access_rights[] = {0xE0, 0xEE}; /* EEE0h */ + uint8_t cc_file_max_len[] = {0x0F, 0x00, 0x00}; /* 00000Fh */ + + DBG(""); + + if (length < 0) { + near_error("select application1 resp failed %d", length); + err = length; + goto out_err; + } + + if (APDU_STATUS(resp + 1) != PICC_LEVEL_APDU_OK) { + near_error("select application1 response %02X", + resp[length - 1]); + err = -EIO; + goto out_err; + } + + cc = g_try_malloc0(sizeof(struct desfire_std_file)); + if (cc == NULL) { + err = -ENOMEM; + goto out_err; + } + + cc->file_num = DESFIRE_CC_FILE_NUM; + memcpy(cc->file_id, iso_cc_file_id, ARRAY_SIZE(iso_cc_file_id)); + cc->comm_set = DESFIRE_COMMSET; + memcpy(cc->access_rights, cc_file_access_rights, + ARRAY_SIZE(cc_file_access_rights)); + memcpy(cc->size, cc_file_max_len, ARRAY_SIZE(cc_file_max_len)); + + err = ISO_send_cmd(PICC_CLASS, + CREATE_STD_DATA_FILE, + 0x00, 0x00, (uint8_t *)cc, + sizeof(struct desfire_std_file), + TRUE, write_data_to_cc_file, cookie); + if (err < 0) { + near_error("create cc file req failed %d", err); + goto out_err; + } + + g_free(cc); + return 0; + +out_err: + g_free(cc); + return t4_cookie_release(err, cookie); +} + +static int select_application_1(uint8_t *resp, int length, void *data) +{ + int err = 0; + struct t4_cookie *cookie = data; + uint8_t *cmd_data = NULL; + uint8_t cmd_data_length; + uint8_t desfire_aid_1[] = {0x01, 0x00, 0x00}; /* 000001h */ + + DBG(""); + + if (length < 0) { + near_error("create application resp failed %d", length); + err = length; + goto out_err; + } + + if (APDU_STATUS(resp + 1) != PICC_LEVEL_APDU_OK) { + near_error("create application response %02X", + resp[length - 1]); + err = -EIO; + goto out_err; + } + + /* Step4 : Select application (which is created just now) */ + cmd_data_length = ARRAY_SIZE(desfire_aid_1); + cmd_data = g_try_malloc0(cmd_data_length); + if (cmd_data == NULL) { + err = -ENOMEM; + goto out_err; + } + + memcpy(cmd_data, desfire_aid_1, cmd_data_length); + err = ISO_send_cmd(PICC_CLASS, SELECT_APPLICATION, + 0x00, 0x00, cmd_data, cmd_data_length, + TRUE, create_cc_file, cookie); + if (err < 0) { + near_error("select application1 req failed %d", err); + goto out_err; + } + + g_free(cmd_data); + return 0; + +out_err: + g_free(cmd_data); + return t4_cookie_release(err, cookie); +} + +static int create_application(uint8_t *resp, int length, void *data) +{ + int err = 0; + struct t4_cookie *cookie = data; + uint8_t desfire_aid_1[] = {0x01, 0x00, 0x00}; /* 000001h */ + uint8_t desfire_file_id[] = {0x10, 0xE1}; /* E110h */ + struct desfire_app *app = NULL; + + DBG(""); + + if (length < 0) { + near_error("select application resp failed %d", length); + err = length; + goto out_err; + } + + if (APDU_STATUS(resp + 1) != PICC_LEVEL_APDU_OK) { + near_error("select application response %02X", + resp[length - 1]); + err = -EIO; + goto out_err; + } + + app = g_try_malloc0(sizeof(struct desfire_app)); + if (app == NULL) { + err = -ENOMEM; + goto out_err; + } + + memcpy(app->aid, desfire_aid_1, ARRAY_SIZE(desfire_aid_1)); + app->key_settings = DESFIRE_KEY_SETTINGS; + app->number_of_keys = DESFIRE_NUM_OF_KEYS; + memcpy(app->file_id, desfire_file_id, ARRAY_SIZE(desfire_file_id)); + memcpy(app->iso_appname, iso_appname_v2, ARRAY_SIZE(iso_appname_v2)); + + /* Step3 : Create Application */ + err = ISO_send_cmd(PICC_CLASS, CREATE_APPLICATION, + 0x00, 0x00, (uint8_t *)app, + sizeof(struct desfire_app), + TRUE, select_application_1, cookie); + if (err < 0) { + near_error("create application req failed %d", err); + goto out_err; + } + + g_free(app); + return 0; + +out_err: + g_free(app); + return t4_cookie_release(err, cookie); +} + +static int select_application(uint8_t *resp, int length, void *data) +{ + int err; + struct t4_cookie *cookie = data; + uint8_t *cmd_data = NULL; + uint8_t cmd_data_length; + uint8_t desfire_aid[] = {0x00, 0x00, 0x00}; /* 000000h */ + + DBG(""); + + if (length < 0) { + near_error("get version3 resp failed %d", length); + err = length; + goto out_err; + } + + if (resp[length - 1] != 0x00) { + near_error("get version3 response %02X", + resp[length - 1]); + err = -EIO; + goto out_err; + } + + /* AID : 000000h */ + cmd_data_length = ARRAY_SIZE(desfire_aid); + cmd_data = g_try_malloc0(cmd_data_length); + if (cmd_data == NULL) { + err = -ENOMEM; + goto out_err; + } + + memcpy(cmd_data, desfire_aid, cmd_data_length); + /* Step2 : Select Application */ + err = ISO_send_cmd(PICC_CLASS, + SELECT_APPLICATION, + 0x00, 0x00, cmd_data, cmd_data_length, + TRUE, create_application, cookie); + if (err < 0) { + near_error("select application req failed %d", err); + goto out_err; + } + + g_free(cmd_data); + return 0; + +out_err: + g_free(cmd_data); + return t4_cookie_release(err, cookie); +} + +static int get_version_frame3(uint8_t *resp, int length, void *data) +{ + int err; + struct t4_cookie *cookie = data; + + DBG(""); + + if (length < 0) { + near_error("get version2 resp failed %d", length); + + return t4_cookie_release(length, cookie); + } + + if (resp[4] == 0x01 /* Major Version */ + && resp[length - 1] == GET_VERSION_FRAME_RESPONSE_BYTE) { + + err = ISO_send_cmd(PICC_CLASS, + GET_VERSION_FRAME_RESPONSE_BYTE, + 0x00, 0x00, NULL, 0, FALSE, + select_application, cookie); + if (err < 0) { + near_error("get version3 req failed %d", err); + + return t4_cookie_release(err, cookie); + } + } else { + near_error("get version2 response %02X", resp[length - 1]); + + return t4_cookie_release(-EIO, cookie); + } + + return 0; +} + +static int get_version_frame2(uint8_t *resp, int length, void *data) +{ + int err; + struct t4_cookie *cookie = data; + + DBG(""); + + if (length < 0) { + near_error(" get version resp failed %d", length); + + return t4_cookie_release(length, cookie); + } + + if (resp[4] == 0x01 /* Major Version */ + && resp[length - 1] == GET_VERSION_FRAME_RESPONSE_BYTE) { + + /* + * When N is the GET_VERSION response 6th byte, + * the DESFire tag memory size is 2 ^ (N /2). + */ + cookie->memory_size = (1 << (resp[6] / 2)); + err = ISO_send_cmd(PICC_CLASS, + GET_VERSION_FRAME_RESPONSE_BYTE, + 0x00, 0x00, NULL, 0, FALSE, + get_version_frame3, cookie); + if (err < 0) { + near_error("get version2 req failed %d", err); + + return t4_cookie_release(err, cookie); + } + } else { + near_error("get version response %02X", resp[length - 1]); + + return t4_cookie_release(-EIO, cookie); + } + + return 0; +} + +/* Steps to format Type 4 (MIFARE DESFire EV1) tag as per AN1104.pdf from nxp. + * 1) Get version to determine memory size of tag + * 2) Select applciation with AID equal to 000000h (PICC level) + * 3) Create application with AID equal to 000001h + * 4) Select application (Select previously created application in step3) + * 5) Create std data file with File number equal to 01h (CC file), ISOFileID + * equal to E103h, ComSet equal to 00h, AccesRights to EEEEh, FileSize bigger + * equal to 00000Fh + * 6) Write data to CC file with CCLEN equal to 000Fh, Mapping version equal to + * 20h, MLe equal to 003Bh, MLc equal to 0034h, and NDEF File control TLV + * equal to: T=04h, L=06h, V=E1 04 (NDEF ISO FID = E104h), 08 00 (NDEF File + * size = 2048 Bytes) 00 (free read access) 00 (free write access) + * 7) Create std data file with File number equal to 02h (NDEF File DESFireFId), + * ISO FileID equal to E104h, ComSet equal to 00h, ComSet equal to 00h, + * AccessRights equal to EEE0h, FileSize equal to 000800h (2048 bytes) + * 8) Write data to write content of the NDEF File with NLEN equal to 0000h, and + * no NDEF messsage. + * 9) Now Formatting is done, then select ISO appname2, select CC file and read. + * 10) Select NDEF file (by doing last two steps means, making sure that read + * write operations perform on NDEF file). + * */ + +static int nfctype4_format(uint32_t adapter_idx, uint32_t target_idx, + near_tag_io_cb cb) +{ + int err; + struct t4_cookie *cookie; + + DBG(""); + + cookie = g_try_malloc0(sizeof(struct t4_cookie)); + if (cookie == NULL) + return -ENOMEM; + + cookie->adapter_idx = adapter_idx; + cookie->target_idx = target_idx; + cookie->cb = cb; + + /* Step1 : Get Version */ + err = ISO_send_cmd(PICC_CLASS, GET_VERSION, + 0x00, 0x00, NULL, 0, FALSE, + get_version_frame2, cookie); + + if (err < 0) { + near_error("get version req failed %d", err); + g_free(cookie); + } + + return err; +} + +static struct near_tag_driver type4_driver = { + .type = NFC_PROTO_ISO14443, + .priority = NEAR_TAG_PRIORITY_DEFAULT, + .read = nfctype4_read, + .write = nfctype4_write, + .check_presence = nfctype4_check_presence, + .format = nfctype4_format, +}; + +static int nfctype4_init(void) +{ + DBG(""); + + return near_tag_driver_register(&type4_driver); +} + +static void nfctype4_exit(void) +{ + DBG(""); + + near_tag_driver_unregister(&type4_driver); +} + +NEAR_PLUGIN_DEFINE(nfctype4, "NFC Forum Type 4 tags support", VERSION, + NEAR_PLUGIN_PRIORITY_HIGH, nfctype4_init, nfctype4_exit) diff --git a/plugins/npp.c b/plugins/npp.c new file mode 100644 index 0000000..90ebc1c --- /dev/null +++ b/plugins/npp.c @@ -0,0 +1,159 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdint.h> +#include <errno.h> +#include <string.h> +#include <sys/socket.h> + +#include <linux/socket.h> + +#include <near/nfc.h> +#include <near/plugin.h> +#include <near/log.h> +#include <near/types.h> +#include <near/adapter.h> +#include <near/device.h> +#include <near/ndef.h> +#include <near/tlv.h> + +#include "p2p.h" + +struct p2p_npp_ndef_entry { + uint8_t action; + uint32_t ndef_length; + uint8_t ndef[]; +} __attribute__((packed)); + +struct p2p_npp_frame { + uint8_t version; + uint32_t n_ndef; + struct p2p_npp_ndef_entry ndefs[]; +} __attribute__((packed)); + +static near_bool_t npp_read(int client_fd, + uint32_t adapter_idx, uint32_t target_idx, + near_tag_io_cb cb) +{ + struct near_device *device; + struct p2p_npp_frame frame; + struct p2p_npp_ndef_entry entry; + int bytes_recv, n_ndef, i, ndef_length, total_ndef_length, err; + uint8_t *ndefs, *current_ndef; + GList *records; + + ndefs = NULL; + total_ndef_length = 0; + err = 0; + + bytes_recv = recv(client_fd, &frame, sizeof(frame), 0); + if (bytes_recv < 0) { + near_error("Could not read NPP frame %d", bytes_recv); + return bytes_recv; + } + + n_ndef = GINT_FROM_BE(frame.n_ndef); + + DBG("version %d %d NDEFs", frame.version, n_ndef); + + for (i = 0; i < n_ndef; i++) { + bytes_recv = recv(client_fd, &entry, sizeof(entry), 0); + if (bytes_recv < 0) { + near_error("Could not read NPP NDEF entry %d", + bytes_recv); + err = bytes_recv; + break; + } + + ndef_length = GINT_FROM_BE(entry.ndef_length); + total_ndef_length += ndef_length + TLV_SIZE; + DBG("NDEF %d length %d", i, ndef_length); + + ndefs = g_try_realloc(ndefs, total_ndef_length); + if (ndefs == NULL) { + near_error("Could not allocate NDEF buffer %d", + bytes_recv); + err = -ENOMEM; + break; + } + + current_ndef = ndefs + total_ndef_length + - (ndef_length + TLV_SIZE); + current_ndef[0] = TLV_NDEF; + current_ndef[1] = ndef_length; + + bytes_recv = recv(client_fd, current_ndef + TLV_SIZE, + ndef_length, 0); + if (bytes_recv < 0) { + near_error("Could not read NDEF entry %d", + bytes_recv); + err = bytes_recv; + break; + } + } + + if (total_ndef_length == 0) + return err; + + DBG("Total NDEF length %d", total_ndef_length); + + err = near_tag_add_data(adapter_idx, target_idx, + ndefs, total_ndef_length); + if (err < 0) + return FALSE; + + device = near_device_get_device(adapter_idx, target_idx); + if (device == NULL) { + g_free(ndefs); + return -ENOMEM; + } + + for (i = 0; i < total_ndef_length; i++) + DBG("NDEF[%d] 0x%x", i, ndefs[i]); + + records = near_tlv_parse(ndefs, total_ndef_length); + near_device_add_records(device, records, cb, 0); + + g_free(ndefs); + + return FALSE; +} + +struct near_p2p_driver npp_driver = { + .name = "NPP", + .service_name = NEAR_DEVICE_SN_NPP, + .fallback_service_name = NULL, + .read = npp_read, +}; + +int npp_init(void) +{ + return near_p2p_register(&npp_driver); +} + +void npp_exit(void) +{ + near_p2p_unregister(&npp_driver); +} diff --git a/plugins/p2p.c b/plugins/p2p.c new file mode 100644 index 0000000..be5c6e1 --- /dev/null +++ b/plugins/p2p.c @@ -0,0 +1,396 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <unistd.h> +#include <stdint.h> +#include <errno.h> +#include <string.h> +#include <sys/socket.h> + +#include <linux/socket.h> + +#include <near/nfc.h> +#include <near/plugin.h> +#include <near/log.h> +#include <near/types.h> +#include <near/tag.h> +#include <near/device.h> +#include <near/adapter.h> +#include <near/tlv.h> +#include <near/ndef.h> + +#include "p2p.h" + +static GSList *driver_list = NULL; +static GList *server_list = NULL; + +struct p2p_data { + struct near_p2p_driver *driver; + uint32_t adapter_idx; + uint32_t target_idx; + near_device_io_cb cb; + int fd; + guint watch; + + GList *client_list; +}; + +static gboolean p2p_client_event(GIOChannel *channel, GIOCondition condition, + gpointer user_data) +{ + struct p2p_data *client_data = user_data; + near_bool_t more; + + DBG("condition 0x%x", condition); + + if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + int err; + + if (client_data->watch > 0) + g_source_remove(client_data->watch); + client_data->watch = 0; + + if (condition & (G_IO_NVAL | G_IO_ERR)) + err = -EIO; + else + err = 0; + + if (client_data->driver->close != NULL) + client_data->driver->close(client_data->fd, err); + + near_error("%s client channel closed", + client_data->driver->name); + + return FALSE; + } + + more = client_data->driver->read(client_data->fd, + client_data->adapter_idx, + client_data->target_idx, + client_data->cb); + + return more; +} + +static void free_client_data(gpointer data) +{ + struct p2p_data *client_data; + + DBG(""); + + client_data = (struct p2p_data *) data; + + if (client_data->driver->close != NULL) + client_data->driver->close(client_data->fd, 0); + + if (client_data->watch > 0) + g_source_remove(client_data->watch); + + g_free(client_data); +} + +static void free_server_data(gpointer data) +{ + struct p2p_data *server_data; + + DBG(""); + + server_data = (struct p2p_data *)data; + + if (server_data->watch > 0) + g_source_remove(server_data->watch); + server_data->watch = 0; + g_list_free_full(server_data->client_list, free_client_data); + server_data->client_list = NULL; + + DBG("Closing server socket"); + + close(server_data->fd); + + g_free(server_data); +} + +static gboolean p2p_listener_event(GIOChannel *channel, GIOCondition condition, + gpointer user_data) +{ + struct sockaddr_nfc_llcp client_addr; + socklen_t client_addr_len; + int server_fd, client_fd; + struct p2p_data *client_data, *server_data = user_data; + GIOChannel *client_channel; + struct near_p2p_driver *driver = server_data->driver; + + DBG("condition 0x%x", condition); + + server_fd = g_io_channel_unix_get_fd(channel); + + if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + if (server_data->watch > 0) + g_source_remove(server_data->watch); + server_data->watch = 0; + + g_list_free_full(server_data->client_list, free_client_data); + server_data->client_list = NULL; + + near_error("Error with %s server channel", driver->name); + + return FALSE; + } + + client_addr_len = sizeof(client_addr); + client_fd = accept(server_fd, (struct sockaddr *) &client_addr, + &client_addr_len); + if (client_fd < 0) { + near_error("accept failed %d", client_fd); + + return FALSE; + } + + DBG("client dsap %d ssap %d", + client_addr.dsap, client_addr.ssap); + DBG("target idx %d", client_addr.target_idx); + + client_data = g_try_malloc0(sizeof(struct p2p_data)); + if (client_data == NULL) { + close(client_fd); + return FALSE; + } + + client_data->driver = server_data->driver; + client_data->adapter_idx = server_data->adapter_idx; + client_data->target_idx = client_addr.target_idx; + client_data->fd = client_fd; + client_data->cb = server_data->cb; + + client_channel = g_io_channel_unix_new(client_fd); + g_io_channel_set_close_on_unref(client_channel, TRUE); + + client_data->watch = g_io_add_watch(client_channel, + G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR, + p2p_client_event, + client_data); + + server_data->client_list = g_list_append(server_data->client_list, client_data); + + return TRUE; +} + +static int p2p_bind(struct near_p2p_driver *driver, uint32_t adapter_idx, + near_device_io_cb cb) +{ + int err, fd; + struct sockaddr_nfc_llcp addr; + GIOChannel *channel; + struct p2p_data *server_data; + + DBG("Binding %s", driver->name); + + fd = socket(AF_NFC, SOCK_STREAM, NFC_SOCKPROTO_LLCP); + if (fd < 0) + return -errno; + + memset(&addr, 0, sizeof(struct sockaddr_nfc_llcp)); + addr.sa_family = AF_NFC; + addr.dev_idx = adapter_idx; + addr.nfc_protocol = NFC_PROTO_NFC_DEP; + addr.service_name_len = strlen(driver->service_name); + strcpy(addr.service_name, driver->service_name); + + err = bind(fd, (struct sockaddr *) &addr, + sizeof(struct sockaddr_nfc_llcp)); + if (err < 0) { + if (errno == EADDRINUSE) { + DBG("%s is already bound", driver->name); + close(fd); + return 0; + } + + near_error("%s bind failed %d %d", driver->name, err, errno); + + close(fd); + return err; + } + + err = listen(fd, 10); + if (err < 0) { + near_error("%s listen failed %d", driver->name, err); + + close(fd); + return err; + } + + server_data = g_try_malloc0(sizeof(struct p2p_data)); + if (server_data == NULL) { + close(fd); + return -ENOMEM; + } + + server_data->driver = driver; + server_data->adapter_idx = adapter_idx; + server_data->fd = fd; + server_data->cb = cb; + + channel = g_io_channel_unix_new(fd); + g_io_channel_set_close_on_unref(channel, TRUE); + + server_data->watch = g_io_add_watch(channel, + G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR, + p2p_listener_event, + (gpointer) server_data); + g_io_channel_unref(channel); + + server_list = g_list_append(server_list, server_data); + + return 0; +} + +static int p2p_listen(uint32_t adapter_idx, near_device_io_cb cb) +{ + int err = -1; + GSList *list; + + for (list = driver_list; list != NULL; list = list->next) { + struct near_p2p_driver *driver = list->data; + + if (p2p_bind(driver, adapter_idx, cb) == 0) + err = 0; + } + + return err; +} + +static int p2p_connect(uint32_t adapter_idx, uint32_t target_idx, + struct near_ndef_message *ndef, + near_device_io_cb cb, struct near_p2p_driver *driver) +{ + int fd, err = 0; + struct sockaddr_nfc_llcp addr; + + DBG(""); + + fd = socket(AF_NFC, SOCK_STREAM, NFC_SOCKPROTO_LLCP); + if (fd < 0) + return -errno; + + memset(&addr, 0, sizeof(struct sockaddr_nfc_llcp)); + addr.sa_family = AF_NFC; + addr.dev_idx = adapter_idx; + addr.target_idx = target_idx; + addr.nfc_protocol = NFC_PROTO_NFC_DEP; + addr.service_name_len = strlen(driver->service_name); + strcpy(addr.service_name, driver->service_name); + + err = connect(fd, (struct sockaddr *) &addr, + sizeof(struct sockaddr_nfc_llcp)); + if (err < 0) { + near_error("Connect failed %d", err); + close(fd); + + return err; + } + + return fd; +} + +static int p2p_push(uint32_t adapter_idx, uint32_t target_idx, + struct near_ndef_message *ndef, char *service_name, + near_device_io_cb cb) +{ + int fd; + GSList *list; + + DBG(""); + + for (list = driver_list; list != NULL; list = list->next) { + struct near_p2p_driver *driver = list->data; + + if (strcmp(driver->service_name, service_name) != 0) + continue; + /* + * Because of Android's implementation, we have use SNEP for + * Handover. So, on Handover session, we try to connect to + * the handover service and fallback to SNEP on connect fail. + */ + fd = p2p_connect(adapter_idx, target_idx, ndef, cb, driver); + if (fd < 0) + return p2p_push(adapter_idx, target_idx, ndef, + (char *) driver->fallback_service_name, cb); + else + return driver->push(fd, adapter_idx, target_idx, + ndef, cb); + } + + return -1; +} + +static struct near_device_driver p2p_driver = { + .priority = NEAR_DEVICE_PRIORITY_HIGH, + .listen = p2p_listen, + .push = p2p_push, +}; + + +int near_p2p_register(struct near_p2p_driver *driver) +{ + DBG("driver %p name %s", driver, driver->name); + + driver_list = g_slist_prepend(driver_list, driver); + + return 0; +} + +void near_p2p_unregister(struct near_p2p_driver *driver) +{ + DBG("driver %p name %s", driver, driver->name); + + driver_list = g_slist_remove(driver_list, driver); +} + +static int p2p_init(void) +{ + DBG(""); + + npp_init(); + snep_init(); + handover_init(); + + return near_device_driver_register(&p2p_driver); +} + +static void p2p_exit(void) +{ + DBG(""); + + g_list_free_full(server_list, free_server_data); + + snep_exit(); + npp_exit(); + handover_exit(); + + near_device_driver_unregister(&p2p_driver); +} + +NEAR_PLUGIN_DEFINE(p2p, "NFC Forum peer to peer mode support", VERSION, + NEAR_PLUGIN_PRIORITY_HIGH, p2p_init, p2p_exit) diff --git a/plugins/p2p.h b/plugins/p2p.h new file mode 100644 index 0000000..033ecd2 --- /dev/null +++ b/plugins/p2p.h @@ -0,0 +1,50 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef AF_NFC +#define AF_NFC 39 +#endif + +struct near_p2p_driver { + const char *name; + const char *service_name; + const char *fallback_service_name; + near_bool_t (*read)(int client_fd, + uint32_t adapter_idx, uint32_t target_idx, + near_device_io_cb cb); + int (*push)(int client_fd, uint32_t adapter_idx, uint32_t target_idx, + struct near_ndef_message *ndef, near_device_io_cb cb); + void (*close)(int client_fd, int err); +}; + +#define TLV_SIZE 2 + +int npp_init(void); +void npp_exit(void); + +int snep_init(void); +void snep_exit(void); + +int handover_init(void); +void handover_exit(void); + +int near_p2p_register(struct near_p2p_driver *driver); +void near_p2p_unregister(struct near_p2p_driver *driver); diff --git a/plugins/snep.c b/plugins/snep.c new file mode 100644 index 0000000..262cf4d --- /dev/null +++ b/plugins/snep.c @@ -0,0 +1,691 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdint.h> +#include <errno.h> +#include <string.h> +#include <sys/socket.h> + +#include <linux/socket.h> + +#include <near/nfc.h> +#include <near/plugin.h> +#include <near/log.h> +#include <near/types.h> +#include <near/adapter.h> +#include <near/device.h> +#include <near/ndef.h> +#include <near/tlv.h> + +#include "p2p.h" + +#define SNEP_VERSION 0x10 + +/* Request codes */ +#define SNEP_REQ_CONTINUE 0x00 +#define SNEP_REQ_GET 0x01 +#define SNEP_REQ_PUT 0x02 +#define SNEP_REQ_REJECT 0x7f + +/* Response codes */ +#define SNEP_RESP_CONTINUE 0x80 +#define SNEP_RESP_SUCCESS 0x81 +#define SNEP_RESP_NOT_FOUND 0xc0 +#define SNEP_RESP_EXCESS 0xc1 +#define SNEP_RESP_BAD_REQ 0xc2 +#define SNEP_RESP_NOT_IMPL 0xe0 +#define SNEP_RESP_VERSION 0xe1 +#define SNEP_RESP_REJECT 0xff + +#define SNEP_REQ_PUT_HEADER_LENGTH 6 +#define SNEP_REQ_GET_HEADER_LENGTH 10 +/* TODO: Right now it is dummy, need to get correct value + * from lower layers */ +#define SNEP_REQ_MAX_FRAGMENT_LENGTH 128 + +struct p2p_snep_data { + uint8_t request_code; + uint8_t *nfc_data; + uint32_t nfc_data_length; + uint32_t nfc_data_current_length; + uint8_t *nfc_data_ptr; + uint32_t adapter_idx; + uint32_t target_idx; + gboolean respond_continue; + near_tag_io_cb cb; +}; + +struct snep_fragment { + uint32_t len; + uint8_t *data; +}; + +struct p2p_snep_put_req_data { + uint8_t fd; + uint32_t adapter_idx; + uint32_t target_idx; + near_device_io_cb cb; + guint watch; + + GSList *fragments; +}; + +struct p2p_snep_req_frame { + uint8_t version; + uint8_t request; + uint32_t length; + uint8_t ndef[]; +} __attribute__((packed)); + +struct p2p_snep_resp_frame { + uint8_t version; + uint8_t response; + uint32_t length; + uint8_t info[]; +} __attribute__((packed)); + +static GHashTable *snep_client_hash = NULL; + +static void free_snep_client(gpointer data) +{ + struct p2p_snep_data *snep_data = data; + + g_free(snep_data->nfc_data); + g_free(snep_data); +} + +static void snep_response_noinfo(int client_fd, uint8_t response) +{ + struct p2p_snep_resp_frame resp; + + DBG("Response 0x%x", response); + + resp.version = SNEP_VERSION; + resp.response = response; + resp.length = 0; + + send(client_fd, &resp, sizeof(resp), 0); +} + +static void snep_close(int client_fd, int err) +{ + struct p2p_snep_data *snep_data; + + DBG(""); + + snep_data = g_hash_table_lookup(snep_client_hash, + GINT_TO_POINTER(client_fd)); + if (snep_data == NULL) + return; + + snep_data->cb(snep_data->adapter_idx, snep_data->target_idx, err); + + g_hash_table_remove(snep_client_hash, GINT_TO_POINTER(client_fd)); +} + +static void snep_response_with_info(int client_fd, uint8_t response, + uint8_t *data, int length) +{ + struct p2p_snep_resp_frame *resp; + + DBG("Response with info 0x%x (len:%d)", response, length); + + resp = g_try_malloc0(sizeof(struct p2p_snep_resp_frame) + length); + if (resp == NULL) { + DBG("Memory allocation error"); + return; + } + + /* Fill */ + resp->version = SNEP_VERSION; + resp->response = response; + resp->length = GUINT32_TO_BE(length); + memcpy(resp->info, data, length); + + send(client_fd, resp, sizeof(struct p2p_snep_resp_frame) + length, 0); + + g_free(resp); +} + +/* + * snep_parse_handover_record + * + * The hr frame should be here BUT: + * The first 4 bytes are the Max Allowed Length + * + * - Because of an Android's BUGs: + * - the Hr frame is not correct; a Hr record + * is embedded in a ... Hr record !!! The author + * used 'Hr' instead of 'cr' + * - The OOB block is badly written: + * - the payload ID should be the same in the 'ac' record + * and the OOB record. + * - The OOB data length bytes must be swapped (Big endian to Little E.) + * + * The hack fixes the first issue (bluetooth.c fixes the second) ! + * */ +static void snep_parse_handover_record(int client_fd, uint8_t *ndef, + uint32_t nfc_data_length) +{ + GList *records; + struct near_ndef_message *msg; + + if (ndef == NULL) + return; + + /* + * Bugfix Android: Fix 'cr' instead of 'Hr' + * Bug is in Google:HandoverManager.java:645 + */ + if (strncmp((char *)(ndef + 9), "Hr", 2) == 0) + *(ndef + 9) = 'c'; + + /* Parse the incoming frame */ + records = near_ndef_parse_msg(ndef, nfc_data_length); + + /* + * If we received a Hr, we must build a Hs and send it. + * If the frame is a Hs, nothing more to do (SNEP REPLY is SUCCESS and + * the pairing is done in near_ndef_parse_msg() + * */ + if (strncmp((char *)(ndef + 3), "Hr", 2) == 0) { + msg = near_ndef_prepare_handover_record("Hs", records->data, + NEAR_CARRIER_BLUETOOTH); + + near_info("Send SNEP / Hs frame"); + snep_response_with_info(client_fd, SNEP_RESP_SUCCESS, + msg->data, msg->length); + g_free(msg->data); + g_free(msg); + } + + near_ndef_records_free(records); + + return; +} + +static near_bool_t snep_read_ndef(int client_fd, + struct p2p_snep_data *snep_data) +{ + int bytes_recv, remaining_bytes; + struct near_device *device; + GList *records; + + DBG(""); + + remaining_bytes = snep_data->nfc_data_length - + snep_data->nfc_data_current_length; + + DBG("Remaining bytes %d", remaining_bytes); + + bytes_recv = recv(client_fd, snep_data->nfc_data_ptr, remaining_bytes, + MSG_DONTWAIT); + if (bytes_recv < 0) { + near_error("%d %s", bytes_recv, strerror(errno)); + + /* Some more data should show up */ + if (errno == EAGAIN) + return TRUE; + + goto out; + } + + DBG("Received %d bytes", bytes_recv); + + snep_data->nfc_data_current_length += bytes_recv; + snep_data->nfc_data_ptr += bytes_recv; + + if (snep_data->nfc_data_length != snep_data->nfc_data_current_length) { + if (snep_data->respond_continue == FALSE) { + DBG("Continue"); + snep_data->respond_continue = TRUE; + snep_response_noinfo(client_fd, SNEP_RESP_CONTINUE); + } + + return TRUE; + } + + if (snep_data->request_code == SNEP_REQ_GET) { + /* + * This goes against the SNEP specification: + * "The default server SHALL NOT accept Get requests." but + * the first Android Handover implementation (Jelly Bean) + * does Handover through SNEP via GET frames...Since Android + * seems popular these days, we'd better support that spec + * violation. + * + * Parse the Hr and send a Hs + * Max allowed size in the first 4 bytes + */ + snep_parse_handover_record(client_fd, snep_data->nfc_data + 4, + snep_data->nfc_data_length - 4); + } else { + snep_response_noinfo(client_fd, SNEP_RESP_SUCCESS); + if (near_device_add_data(snep_data->adapter_idx, + snep_data->target_idx, + snep_data->nfc_data, + snep_data->nfc_data_length) < 0) + goto out; + + device = near_device_get_device(snep_data->adapter_idx, + snep_data->target_idx); + if (device == NULL) + goto out; + + records = near_ndef_parse_msg(snep_data->nfc_data, + snep_data->nfc_data_length); + near_device_add_records(device, records, snep_data->cb, 0); + } + +out: + g_hash_table_remove(snep_client_hash, GINT_TO_POINTER(client_fd)); + + return FALSE; +} + +static near_bool_t snep_read(int client_fd, + uint32_t adapter_idx, uint32_t target_idx, + near_tag_io_cb cb) +{ + struct p2p_snep_data *snep_data; + struct p2p_snep_req_frame frame; + int bytes_recv; + uint32_t ndef_length; + + DBG(""); + + snep_data = g_hash_table_lookup(snep_client_hash, + GINT_TO_POINTER(client_fd)); + + /* + * We already got something from this client, we should try + * to continue reading. + */ + if (snep_data != NULL) + return snep_read_ndef(client_fd, snep_data); + + /* TODO Try with PEEK */ + bytes_recv = recv(client_fd, &frame, sizeof(frame), 0); + if (bytes_recv < 0) { + near_error("Could not read SNEP frame %d", bytes_recv); + return bytes_recv; + } + + ndef_length = GINT_FROM_BE(frame.length); + + DBG("Allocating SNEP data %d", ndef_length); + + snep_data = g_try_malloc0(sizeof(struct p2p_snep_data)); + if (snep_data == NULL) + return FALSE; + + snep_data->nfc_data = g_try_malloc0(ndef_length + TLV_SIZE); + if (snep_data->nfc_data == NULL) { + g_free(snep_data); + return FALSE; + } + + snep_data->nfc_data_length = ndef_length; + snep_data->nfc_data_ptr = snep_data->nfc_data; + snep_data->adapter_idx = adapter_idx; + snep_data->target_idx = target_idx; + snep_data->respond_continue = FALSE; + snep_data->cb = cb; + + g_hash_table_insert(snep_client_hash, + GINT_TO_POINTER(client_fd), snep_data); + + snep_data->request_code = frame.request; + + DBG("Request 0x%x", frame.request); + + switch (frame.request) { + case SNEP_REQ_CONTINUE: + near_error("Unsupported SNEP request code"); + snep_response_noinfo(client_fd, SNEP_RESP_NOT_IMPL); + return FALSE; + case SNEP_REQ_GET: + case SNEP_REQ_PUT: + return snep_read_ndef(client_fd, snep_data); + } + + return FALSE; +} + +static void free_snep_fragment(gpointer data) +{ + struct snep_fragment *fragment = data; + + if (fragment != NULL) + g_free(fragment->data); + + g_free(fragment); + fragment = NULL; +} + +static void free_snep_push_data(gpointer userdata, int status) +{ + struct p2p_snep_put_req_data *data; + + DBG(""); + + data = (struct p2p_snep_put_req_data *) userdata; + + close(data->fd); + + if (data->cb) + data->cb(data->adapter_idx, data->target_idx, status); + + if (data->watch > 0) + g_source_remove(data->watch); + + g_slist_free_full(data->fragments, free_snep_fragment); + g_free(data); +} + +static int snep_send_fragment(struct p2p_snep_put_req_data *req) +{ + struct snep_fragment *fragment; + int err; + + DBG(""); + + if (req == NULL || req->fragments == NULL || + g_slist_length(req->fragments) == 0) + return -EINVAL; + + fragment = req->fragments->data; + + err = send(req->fd, fragment->data, fragment->len, 0); + + req->fragments = g_slist_remove(req->fragments, fragment); + g_free(fragment->data); + g_free(fragment); + + return err; +} + +static int snep_push_response(struct p2p_snep_put_req_data *req) +{ + struct p2p_snep_resp_frame frame; + uint8_t *ndef; + uint32_t ndef_len; + int bytes_recv, err; + + DBG(""); + + bytes_recv = recv(req->fd, &frame, sizeof(frame), 0); + if (bytes_recv < 0) { + near_error("Could not read SNEP frame %d", bytes_recv); + return bytes_recv; + } + + /* Check frame length */ + frame.length = g_ntohl(frame.length); + + DBG("Response 0x%x", frame.response); + + switch (frame.response) { + case SNEP_RESP_CONTINUE: + while (g_slist_length(req->fragments) != 0) { + err = snep_send_fragment(req); + if (err < 0) + return err; + } + + return frame.response; + + case SNEP_RESP_SUCCESS: + if (frame.length == 0) + return 0; + + /* Get the incoming data */ + ndef_len = frame.length; + ndef = g_try_malloc0(ndef_len); + if (ndef == NULL) + return -ENOMEM; + + bytes_recv = recv(req->fd, ndef, ndef_len, 0); + if (bytes_recv < 0) { + near_error("Could not read SNEP frame %d", bytes_recv); + return bytes_recv; + } + + /* Not enough bytes */ + if (bytes_recv < 6) + return -EINVAL; + + if (strncmp((char *)(ndef + 3), "Hs", 2) == 0) + snep_parse_handover_record(req->fd, ndef, ndef_len); + + g_free(ndef); + + return 0; + } + + return -1; +} + +static gboolean snep_push_event(GIOChannel *channel, + GIOCondition condition, gpointer data) +{ + int err; + + DBG("condition 0x%x", condition); + + if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + + near_error("Error with SNEP channel"); + + free_snep_push_data(data, -1); + + return FALSE; + } + + err = snep_push_response(data); + if (err <= 0) { + free_snep_push_data(data, err); + + return FALSE; + } + + return TRUE; +} + +static int snep_push_prepare_fragments(struct p2p_snep_put_req_data *req, + struct near_ndef_message *ndef) +{ + struct snep_fragment *fragment; + uint32_t max_fragment_len; + + DBG(""); + + max_fragment_len = SNEP_REQ_MAX_FRAGMENT_LENGTH; + + while (ndef->offset < ndef->length) { + + fragment = g_try_malloc0(sizeof(struct snep_fragment)); + if (fragment == NULL) + return -ENOMEM; + + if (max_fragment_len <= (ndef->length - ndef->offset)) + fragment->len = max_fragment_len; + else + fragment->len = ndef->length - ndef->offset; + + fragment->data = g_try_malloc0(fragment->len); + if (fragment->data == NULL) { + g_free(fragment); + return -ENOMEM; + } + + memcpy(fragment->data, ndef->data + ndef->offset, + fragment->len); + ndef->offset += fragment->len; + req->fragments = g_slist_append(req->fragments, fragment); + } + + return 0; +} + +static int snep_push(int fd, uint32_t adapter_idx, uint32_t target_idx, + struct near_ndef_message *ndef, + near_device_io_cb cb) +{ + struct p2p_snep_put_req_data *req; + struct p2p_snep_req_frame header; + struct snep_fragment *fragment; + uint32_t max_fragment_len; + GIOChannel *channel; + gboolean fragmenting; + int err; + int snep_req_header_length, snep_additional_length; + + DBG(""); + + req = g_try_malloc0(sizeof(struct p2p_snep_put_req_data)); + if (req == NULL) { + err = -ENOMEM; + goto error; + } + + channel = g_io_channel_unix_new(fd); + g_io_channel_set_close_on_unref(channel, TRUE); + + req->fd = fd; + req->adapter_idx = adapter_idx; + req->target_idx = target_idx; + req->cb = cb; + ndef->offset = 0; + req->watch = g_io_add_watch(channel, G_IO_IN | G_IO_HUP | G_IO_NVAL | + G_IO_ERR, snep_push_event, + (gpointer) req); + + max_fragment_len = SNEP_REQ_MAX_FRAGMENT_LENGTH; + header.version = SNEP_VERSION; + + /* Check if Hr or Hs for Handover over SNEP */ + if (*(char *)(ndef->data + 3) == 'H') { + header.request = SNEP_REQ_GET; /* Get for android */ + snep_req_header_length = SNEP_REQ_GET_HEADER_LENGTH; + snep_additional_length = 4; /* 4 Acceptable Length */ + } else { + header.request = SNEP_REQ_PUT; + snep_req_header_length = SNEP_REQ_PUT_HEADER_LENGTH; + snep_additional_length = 0; + } + + header.length = GUINT32_TO_BE(ndef->length + snep_additional_length); + + fragment = g_try_malloc0(sizeof(struct snep_fragment)); + if (fragment == NULL) { + err = -ENOMEM; + goto error; + } + + if (max_fragment_len >= (ndef->length + snep_req_header_length)) { + fragment->len = ndef->length + snep_req_header_length; + fragmenting = FALSE; + } else { + fragment->len = max_fragment_len; + fragmenting = TRUE; + } + + fragment->data = g_try_malloc0(fragment->len); + if (fragment->data == NULL) { + g_free(fragment); + err = ENOMEM; + goto error; + } + + /* Header to data - common header */ + memcpy(fragment->data, (uint8_t *)&header, SNEP_REQ_PUT_HEADER_LENGTH); + + /* if GET, we add the Acceptable length */ + if (header.request == SNEP_REQ_GET) + *(uint32_t *)(fragment->data + SNEP_REQ_PUT_HEADER_LENGTH) = + GUINT32_TO_BE(snep_req_header_length); + + if (fragmenting == TRUE) { + memcpy(fragment->data + snep_req_header_length, ndef->data, + max_fragment_len - snep_req_header_length); + ndef->offset = max_fragment_len - snep_req_header_length; + + err = snep_push_prepare_fragments(req, ndef); + if (err < 0) { + g_free(fragment->data); + g_free(fragment); + goto error; + } + + } else { + memcpy(fragment->data + snep_req_header_length, + ndef->data, ndef->length); + } + + err = send(fd, fragment->data, fragment->len, 0); + if (err < 0) { + near_error("Sending failed %d", err); + g_free(fragment->data); + g_free(fragment); + + goto error; + } + + g_free(fragment->data); + g_free(fragment); + + return 0; + +error: + free_snep_push_data(req, err); + + return err; +} + +struct near_p2p_driver snep_driver = { + .name = "SNEP", + .service_name = NEAR_DEVICE_SN_SNEP, + .fallback_service_name = NEAR_DEVICE_SN_NPP, + .read = snep_read, + .push = snep_push, + .close = snep_close, +}; + +int snep_init(void) +{ + snep_client_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, free_snep_client); + + return near_p2p_register(&snep_driver); +} + +void snep_exit(void) +{ + near_p2p_unregister(&snep_driver); + + g_hash_table_destroy(snep_client_hash); + snep_client_hash = NULL; +} diff --git a/src/adapter.c b/src/adapter.c new file mode 100644 index 0000000..25cf956 --- /dev/null +++ b/src/adapter.c @@ -0,0 +1,1193 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> + +#include <glib.h> + +#include <gdbus.h> + +#include "near.h" + +/* We check for the tag being present every 2 seconds */ +#define CHECK_PRESENCE_PERIOD 2 + +static DBusConnection *connection = NULL; + +static GHashTable *adapter_hash; + +enum near_adapter_rf_mode { + NEAR_ADAPTER_RF_MODE_IDLE = 0, + NEAR_ADAPTER_RF_MODE_INITIATOR = 1, + NEAR_ADAPTER_RF_MODE_TARGET = 2 +}; + +#define NEAR_ADAPTER_MODE_INITIATOR 0x1 +#define NEAR_ADAPTER_MODE_TARGET 0x2 +#define NEAR_ADAPTER_MODE_DUAL 0x3 + +struct near_adapter { + char *path; + + char *name; + uint32_t idx; + uint32_t protocols; + uint32_t poll_mode; + enum near_adapter_rf_mode rf_mode; + + near_bool_t powered; + near_bool_t polling; + near_bool_t constant_poll; + near_bool_t dep_up; + + GHashTable *tags; + struct near_tag *tag_link; + int tag_sock; + + GHashTable *devices; + struct near_device *device_link; + int device_sock; + + GIOChannel *channel; + guint watch; + GList *ioreq_list; + + guint presence_timeout; +}; + +struct near_adapter_ioreq { + uint32_t target_idx; + near_recv cb; + unsigned char buf[1024]; + size_t len; + void *data; +}; + +/* HACK HACK */ +#ifndef AF_NFC +#define AF_NFC 39 +#endif + +static void free_adapter(gpointer data) +{ + struct near_adapter *adapter = data; + + if (adapter->presence_timeout > 0) + g_source_remove(adapter->presence_timeout); + + g_free(adapter->name); + g_free(adapter->path); + g_free(adapter); +} + +static void free_tag(gpointer data) +{ + struct near_tag *tag = data; + + __near_tag_remove(tag); +} + +static void free_device(gpointer data) +{ + struct near_device *device = data; + + __near_device_remove(device); +} + +static char *rf_mode_to_string(struct near_adapter *adapter) +{ + switch (adapter->rf_mode) { + case NEAR_ADAPTER_RF_MODE_IDLE: + return "Idle"; + case NEAR_ADAPTER_RF_MODE_INITIATOR: + return "Initiator"; + case NEAR_ADAPTER_RF_MODE_TARGET: + return "Target"; + } + + return NULL; +} + +static void polling_changed(struct near_adapter *adapter) +{ + + near_dbus_property_changed_basic(adapter->path, + NFC_ADAPTER_INTERFACE, "Polling", + DBUS_TYPE_BOOLEAN, &adapter->polling); +} + +static void rf_mode_changed(struct near_adapter *adapter) +{ + const char *rf_mode = rf_mode_to_string(adapter); + + if (rf_mode == NULL) + return; + + near_dbus_property_changed_basic(adapter->path, + NFC_ADAPTER_INTERFACE, "Mode", + DBUS_TYPE_STRING, &rf_mode); +} + +static int adapter_start_poll(struct near_adapter *adapter) +{ + int err; + uint32_t im_protos, tm_protos; + + if (g_hash_table_size(adapter->tags) > 0) { + DBG("Clearing tags"); + + g_hash_table_remove_all(adapter->tags); + __near_adapter_tags_changed(adapter->idx); + } + + if (g_hash_table_size(adapter->devices) > 0) { + DBG("Clearing devices"); + + g_hash_table_remove_all(adapter->devices); + __near_adapter_devices_changed(adapter->idx); + } + + DBG("Poll mode 0x%x", adapter->poll_mode); + + im_protos = tm_protos = 0; + + if (adapter->poll_mode & NEAR_ADAPTER_MODE_INITIATOR) + im_protos = adapter->protocols; + + if (adapter->poll_mode & NEAR_ADAPTER_MODE_TARGET) + tm_protos = adapter->protocols; + + err = __near_netlink_start_poll(adapter->idx, im_protos, tm_protos); + if (err < 0) + return err; + + adapter->polling = TRUE; + + polling_changed(adapter); + + return 0; +} + +static void append_path(gpointer key, gpointer value, gpointer user_data) +{ + struct near_adapter *adapter = value; + DBusMessageIter *iter = user_data; + + DBG("%s", adapter->path); + + if (adapter->path == NULL) + return; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, + &adapter->path); +} + +void __near_adapter_list(DBusMessageIter *iter, void *user_data) +{ + g_hash_table_foreach(adapter_hash, append_path, iter); +} + +static void append_protocols(DBusMessageIter *iter, void *user_data) +{ + struct near_adapter *adapter = user_data; + const char *str; + + DBG("protocols 0x%x", adapter->protocols); + + if (adapter->protocols & NFC_PROTO_FELICA_MASK) { + str = "Felica"; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str); + } + + if (adapter->protocols & NFC_PROTO_MIFARE_MASK) { + str = "MIFARE"; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str); + } + + if (adapter->protocols & NFC_PROTO_JEWEL_MASK) { + str = "Jewel"; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str); + } + + if (adapter->protocols & NFC_PROTO_ISO14443_MASK) { + str = "ISO-DEP"; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str); + } + + if (adapter->protocols & NFC_PROTO_NFC_DEP_MASK) { + str = "NFC-DEP"; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str); + } +} + +static void append_tag_path(gpointer key, gpointer value, gpointer user_data) +{ + struct near_tag *tag = value; + DBusMessageIter *iter = user_data; + const char *tag_path; + + tag_path = __near_tag_get_path(tag); + if (tag_path == NULL) + return; + + DBG("%s", tag_path); + + dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &tag_path); +} + +static void append_tags(DBusMessageIter *iter, void *user_data) +{ + struct near_adapter *adapter = user_data; + + DBG(""); + + g_hash_table_foreach(adapter->tags, append_tag_path, iter); +} + +static void append_device_path(gpointer key, gpointer value, gpointer user_data) +{ + struct near_device *device = value; + DBusMessageIter *iter = user_data; + const char *device_path; + + device_path = __near_device_get_path(device); + if (device_path == NULL) + return; + + DBG("%s", device_path); + + dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, + &device_path); +} + +static void append_devices(DBusMessageIter *iter, void *user_data) +{ + struct near_adapter *adapter = user_data; + + DBG(""); + + g_hash_table_foreach(adapter->devices, append_device_path, iter); +} + +void __near_adapter_tags_changed(uint32_t adapter_idx) +{ + struct near_adapter *adapter; + + DBG(""); + + adapter = g_hash_table_lookup(adapter_hash, + GINT_TO_POINTER(adapter_idx)); + if (adapter == NULL) + return; + + near_dbus_property_changed_array(adapter->path, + NFC_ADAPTER_INTERFACE, "Tags", + DBUS_TYPE_OBJECT_PATH, append_tags, + adapter); +} + +void __near_adapter_devices_changed(uint32_t adapter_idx) +{ + struct near_adapter *adapter; + + DBG(""); + + adapter = g_hash_table_lookup(adapter_hash, + GINT_TO_POINTER(adapter_idx)); + if (adapter == NULL) + return; + + near_dbus_property_changed_array(adapter->path, + NFC_ADAPTER_INTERFACE, "Devices", + DBUS_TYPE_OBJECT_PATH, append_devices, + adapter); +} + +static DBusMessage *get_properties(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct near_adapter *adapter = data; + const char *rf_mode; + DBusMessage *reply; + DBusMessageIter array, dict; + + DBG("conn %p", conn); + + reply = dbus_message_new_method_return(msg); + if (reply == NULL) + return NULL; + + dbus_message_iter_init_append(reply, &array); + + near_dbus_dict_open(&array, &dict); + + near_dbus_dict_append_basic(&dict, "Powered", + DBUS_TYPE_BOOLEAN, &adapter->powered); + + near_dbus_dict_append_basic(&dict, "Polling", + DBUS_TYPE_BOOLEAN, &adapter->polling); + + rf_mode = rf_mode_to_string(adapter); + if (rf_mode != NULL) + near_dbus_dict_append_basic(&dict, "Mode", + DBUS_TYPE_STRING, &rf_mode); + + near_dbus_dict_append_array(&dict, "Protocols", + DBUS_TYPE_STRING, append_protocols, adapter); + + near_dbus_dict_append_array(&dict, "Tags", + DBUS_TYPE_OBJECT_PATH, append_tags, adapter); + + near_dbus_dict_append_array(&dict, "Devices", + DBUS_TYPE_OBJECT_PATH, append_devices, adapter); + + near_dbus_dict_close(&array, &dict); + + return reply; +} + +static DBusMessage *set_property(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct near_adapter *adapter = data; + DBusMessageIter iter, value; + const char *name; + int type, err; + + DBG("conn %p", conn); + + if (dbus_message_iter_init(msg, &iter) == FALSE) + return __near_error_invalid_arguments(msg); + + dbus_message_iter_get_basic(&iter, &name); + dbus_message_iter_next(&iter); + dbus_message_iter_recurse(&iter, &value); + + type = dbus_message_iter_get_arg_type(&value); + + if (g_str_equal(name, "Powered") == TRUE) { + near_bool_t powered; + + if (type != DBUS_TYPE_BOOLEAN) + return __near_error_invalid_arguments(msg); + + dbus_message_iter_get_basic(&value, &powered); + + err = __near_netlink_adapter_enable(adapter->idx, powered); + if (err < 0) { + if (err == -EALREADY) { + if (powered == TRUE) + return __near_error_already_enabled(msg); + else + return __near_error_already_disabled(msg); + } + + return __near_error_failed(msg, -err); + } + + adapter->powered = powered; + } else { + return __near_error_invalid_property(msg); + } + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + +static DBusMessage *start_poll_loop(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct near_adapter *adapter = data; + const char *dbus_mode; + int err; + + DBG("conn %p", conn); + + dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &dbus_mode, + DBUS_TYPE_INVALID); + + DBG("Mode %s", dbus_mode); + + if (g_strcmp0(dbus_mode, "Initiator") == 0) + adapter->poll_mode = NEAR_ADAPTER_MODE_INITIATOR; + else if (g_strcmp0(dbus_mode, "Target") == 0) + adapter->poll_mode = NEAR_ADAPTER_MODE_TARGET; + else if (g_strcmp0(dbus_mode, "Dual") == 0) + adapter->poll_mode = NEAR_ADAPTER_MODE_DUAL; + else + adapter->poll_mode = NEAR_ADAPTER_MODE_INITIATOR; + + err = adapter_start_poll(adapter); + if (err < 0) + return __near_error_failed(msg, -err); + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + +static DBusMessage *stop_poll_loop(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct near_adapter *adapter = data; + int err; + + DBG("conn %p", conn); + + if (adapter->polling == FALSE) + return __near_error_not_polling(msg); + + err = __near_netlink_stop_poll(adapter->idx); + if (err < 0) + return __near_error_failed(msg, -err); + + adapter->polling = FALSE; + + polling_changed(adapter); + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + +static void tag_present_cb(uint32_t adapter_idx, uint32_t target_idx, + int status); + +static gboolean check_presence(gpointer user_data) +{ + struct near_adapter *adapter = user_data; + struct near_tag *tag; + int err; + + DBG(""); + + if (adapter == NULL) + return FALSE; + + tag = adapter->tag_link; + if (tag == NULL) + goto out_err; + + err = __near_tag_check_presence(tag, tag_present_cb); + if (err < 0) { + DBG("Could not check target presence"); + goto out_err; + } + + return FALSE; + +out_err: + near_adapter_disconnect(adapter->idx); + if (adapter->constant_poll == TRUE) + adapter_start_poll(adapter); + + return FALSE; +} + +static void tag_present_cb(uint32_t adapter_idx, uint32_t target_idx, + int status) +{ + struct near_adapter *adapter; + + DBG(""); + + adapter = g_hash_table_lookup(adapter_hash, + GINT_TO_POINTER(adapter_idx)); + if (adapter == NULL) + return; + + if (status < 0) { + DBG("Tag is gone"); + + near_adapter_disconnect(adapter->idx); + if (adapter->constant_poll == TRUE) + adapter_start_poll(adapter); + + return; + } + + adapter->presence_timeout = + g_timeout_add_seconds(CHECK_PRESENCE_PERIOD, + check_presence, adapter); +} + +void __near_adapter_start_check_presence(uint32_t adapter_idx, + uint32_t target_idx) +{ + struct near_adapter *adapter; + + DBG(""); + + adapter = g_hash_table_lookup(adapter_hash, + GINT_TO_POINTER(adapter_idx)); + if (adapter == NULL) + return; + + adapter->presence_timeout = + g_timeout_add_seconds(CHECK_PRESENCE_PERIOD, + check_presence, adapter); +} + +void __near_adapter_stop_check_presence(uint32_t adapter_idx, + uint32_t target_idx) +{ + struct near_adapter *adapter; + + DBG(""); + + adapter = g_hash_table_lookup(adapter_hash, + GINT_TO_POINTER(adapter_idx)); + if (adapter == NULL) + return; + + if (adapter->presence_timeout > 0) + g_source_remove(adapter->presence_timeout); +} + +static const GDBusMethodTable adapter_methods[] = { + { GDBUS_METHOD("GetProperties", + NULL, GDBUS_ARGS({"properties", "a{sv}"}), + get_properties) }, + { GDBUS_METHOD("SetProperty", + GDBUS_ARGS({"name", "s"}, {"value", "v"}), + NULL, set_property) }, + { GDBUS_METHOD("StartPollLoop", GDBUS_ARGS({"name", "s"}), NULL, + start_poll_loop) }, + { GDBUS_METHOD("StopPollLoop", NULL, NULL, stop_poll_loop) }, + { }, +}; + +static const GDBusSignalTable adapter_signals[] = { + { GDBUS_SIGNAL("PropertyChanged", + GDBUS_ARGS({"name", "s"}, {"value", "v"})) }, + { GDBUS_SIGNAL("TagFound", GDBUS_ARGS({"address", "o"})) }, + { GDBUS_SIGNAL("TagLost", GDBUS_ARGS({"address", "o"})) }, + { } +}; + +struct near_adapter * __near_adapter_create(uint32_t idx, + const char *name, uint32_t protocols, near_bool_t powered) +{ + struct near_adapter *adapter; + + adapter = g_try_malloc0(sizeof(struct near_adapter)); + if (adapter == NULL) + return NULL; + + adapter->name = g_strdup(name); + if (adapter->name == NULL) { + g_free(adapter); + return NULL; + } + adapter->idx = idx; + adapter->protocols = protocols; + adapter->powered = powered; + adapter->constant_poll = near_setting_get_bool("ConstantPoll"); + adapter->dep_up = FALSE; + adapter->tags = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, free_tag); + adapter->tag_sock = -1; + + adapter->devices = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, free_device); + adapter->device_sock = -1; + + adapter->path = g_strdup_printf("%s/nfc%d", NFC_PATH, idx); + + return adapter; +} + +void __near_adapter_destroy(struct near_adapter *adapter) +{ + DBG(""); + + free_adapter(adapter); +} + +const char *__near_adapter_get_path(struct near_adapter *adapter) +{ + return adapter->path; +} + +struct near_adapter *__near_adapter_get(uint32_t idx) +{ + return g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx)); +} + +int __near_adapter_set_dep_state(uint32_t idx, near_bool_t dep) +{ + struct near_adapter *adapter; + + DBG("idx %d", idx); + + adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx)); + if (adapter == NULL) + return -ENODEV; + + adapter->dep_up = dep; + + if (dep == FALSE && adapter->constant_poll == TRUE) + adapter_start_poll(adapter); + + if (dep == FALSE) { + uint32_t target_idx; + + target_idx = __neard_device_get_idx(adapter->device_link); + __near_adapter_remove_target(idx, target_idx); + } else { + __near_adapter_devices_changed(idx); + } + + return 0; +} + +near_bool_t __near_adapter_get_dep_state(uint32_t idx) +{ + struct near_adapter *adapter; + + DBG("idx %d", idx); + + adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx)); + if (adapter == NULL) + return FALSE; + + return adapter->dep_up; +} + +int __near_adapter_add(struct near_adapter *adapter) +{ + uint32_t idx = adapter->idx; + + DBG("%s", adapter->path); + + if (g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx)) != NULL) + return -EEXIST; + + g_hash_table_insert(adapter_hash, GINT_TO_POINTER(idx), adapter); + + DBG("connection %p", connection); + + g_dbus_register_interface(connection, adapter->path, + NFC_ADAPTER_INTERFACE, + adapter_methods, adapter_signals, + NULL, adapter, NULL); + + return 0; +} + +void __near_adapter_remove(struct near_adapter *adapter) +{ + DBG("%s", adapter->path); + + g_dbus_unregister_interface(connection, adapter->path, + NFC_ADAPTER_INTERFACE); + + g_hash_table_remove(adapter_hash, GINT_TO_POINTER(adapter->idx)); +} + +static void tag_read_cb(uint32_t adapter_idx, uint32_t target_idx, int status) +{ + struct near_adapter *adapter; + + DBG("status %d", status); + + adapter = g_hash_table_lookup(adapter_hash, + GINT_TO_POINTER(adapter_idx)); + if (adapter == NULL) + return; + + if (status < 0) { + near_adapter_disconnect(adapter->idx); + if (adapter->constant_poll == TRUE) + adapter_start_poll(adapter); + + return; + } + + __near_adapter_tags_changed(adapter_idx); + + adapter->presence_timeout = + g_timeout_add_seconds(CHECK_PRESENCE_PERIOD, + check_presence, adapter); +} + +static void device_read_cb(uint32_t adapter_idx, uint32_t target_idx, + int status) +{ + struct near_adapter *adapter; + + DBG("status %d", status); + + adapter = g_hash_table_lookup(adapter_hash, + GINT_TO_POINTER(adapter_idx)); + if (adapter == NULL) + return; + + if (status < 0) { + if (adapter->device_link != NULL) { + __near_netlink_dep_link_down(adapter->idx); + adapter->device_link = NULL; + } + + if (adapter->constant_poll == TRUE) + adapter_start_poll(adapter); + + return; + } +} + +static int adapter_add_tag(struct near_adapter *adapter, uint32_t target_idx, + uint32_t protocols, + uint16_t sens_res, uint8_t sel_res, + uint8_t *nfcid, uint8_t nfcid_len) +{ + struct near_tag *tag; + uint32_t tag_type; + int err; + + tag = __near_tag_add(adapter->idx, target_idx, protocols, + sens_res, sel_res, + nfcid, nfcid_len); + if (tag == NULL) + return -ENODEV; + + g_hash_table_insert(adapter->tags, GINT_TO_POINTER(target_idx), tag); + + tag_type = __near_tag_get_type(tag); + + err = near_adapter_connect(adapter->idx, target_idx, tag_type); + if (err < 0) { + near_error("Could not connect"); + return err; + } + + return __near_tag_read(tag, tag_read_cb); +} + +static int adapter_add_device(struct near_adapter *adapter, + uint32_t target_idx, + uint8_t *nfcid, uint8_t nfcid_len) +{ + struct near_device *device; + int err; + + device = __near_device_add(adapter->idx, target_idx, nfcid, nfcid_len); + if (device == NULL) + return -ENODEV; + + g_hash_table_insert(adapter->devices, GINT_TO_POINTER(target_idx), + device); + + /* For p2p, reading is listening for an incoming connection */ + err = __near_device_listen(device, device_read_cb); + if (err < 0) { + near_error("Could not read device"); + return err; + } + + adapter->device_link = device; + + if (adapter->dep_up == TRUE) + return 0; + + err = __near_netlink_dep_link_up(adapter->idx, target_idx, + NFC_COMM_ACTIVE, NFC_RF_INITIATOR); + + if (err < 0) + adapter->device_link = NULL; + + return err; +} + +int __near_adapter_add_target(uint32_t idx, uint32_t target_idx, + uint32_t protocols, uint16_t sens_res, uint8_t sel_res, + uint8_t *nfcid, uint8_t nfcid_len) +{ + struct near_adapter *adapter; + + DBG("idx %d", idx); + + adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx)); + if (adapter == NULL) + return -ENODEV; + + adapter->polling = FALSE; + polling_changed(adapter); + + adapter->rf_mode = NEAR_ADAPTER_RF_MODE_INITIATOR; + rf_mode_changed(adapter); + + if (protocols & NFC_PROTO_NFC_DEP_MASK) + return adapter_add_device(adapter, target_idx, + nfcid, nfcid_len); + else + return adapter_add_tag(adapter, target_idx, protocols, + sens_res, sel_res, nfcid, nfcid_len); +} + +int __near_adapter_remove_target(uint32_t idx, uint32_t target_idx) +{ + struct near_adapter *adapter; + + DBG("idx %d", idx); + + adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx)); + if (adapter == NULL) + return -ENODEV; + + adapter->rf_mode = NEAR_ADAPTER_RF_MODE_IDLE; + rf_mode_changed(adapter); + + if (g_hash_table_remove(adapter->tags, + GINT_TO_POINTER(target_idx)) == TRUE) { + __near_adapter_tags_changed(idx); + + return 0; + } + + if (g_hash_table_remove(adapter->devices, + GINT_TO_POINTER(target_idx)) == TRUE) { + __near_adapter_devices_changed(idx); + + return 0; + } + + return 0; +} + +int __near_adapter_add_device(uint32_t idx, uint8_t *nfcid, uint8_t nfcid_len) +{ + struct near_adapter *adapter; + int ret; + + DBG("idx %d", idx); + + adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx)); + if (adapter == NULL) + return -ENODEV; + + adapter->polling = FALSE; + adapter->dep_up = TRUE; + adapter->rf_mode = NEAR_ADAPTER_RF_MODE_TARGET; + polling_changed(adapter); + rf_mode_changed(adapter); + + ret = adapter_add_device(adapter, 0, nfcid, nfcid_len); + if (ret < 0) + return ret; + + __near_adapter_devices_changed(idx); + + return 0; +} + +int __near_adapter_remove_device(uint32_t idx) +{ + struct near_adapter *adapter; + uint32_t device_idx = 0; + + DBG("idx %d", idx); + + adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx)); + if (adapter == NULL) + return -ENODEV; + + if (g_hash_table_remove(adapter->devices, + GINT_TO_POINTER(device_idx)) == FALSE) + return 0; + + adapter->rf_mode = NEAR_ADAPTER_RF_MODE_IDLE; + rf_mode_changed(adapter); + __near_adapter_devices_changed(idx); + + adapter->dep_up = FALSE; + + if (adapter->constant_poll == TRUE) + adapter_start_poll(adapter); + + return 0; +} + +static void adapter_flush_rx(struct near_adapter *adapter, int error) +{ + GList *list; + + for (list = adapter->ioreq_list; list; list = list->next) { + struct near_adapter_ioreq *req = list->data; + + if (req == NULL) + continue; + + req->cb(NULL, error, req->data); + g_free(req); + } + + g_list_free(adapter->ioreq_list); + adapter->ioreq_list = NULL; +} + +static gboolean execute_recv_cb(gpointer user_data) +{ + struct near_adapter_ioreq *req = user_data; + + DBG("data %p", req->data); + + req->cb(req->buf, req->len, req->data); + + g_free(req); + + return FALSE; +} + +static gboolean adapter_recv_event(GIOChannel *channel, GIOCondition condition, + gpointer user_data) +{ + struct near_adapter *adapter = user_data; + struct near_adapter_ioreq *req; + GList *first; + int sk; + + DBG("condition 0x%x", condition); + + if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + near_error("Error while reading NFC bytes"); + + adapter_flush_rx(adapter, -EIO); + + near_adapter_disconnect(adapter->idx); + + adapter->presence_timeout = + g_timeout_add_seconds(2 * CHECK_PRESENCE_PERIOD, + check_presence, adapter); + return FALSE; + } + + sk = g_io_channel_unix_get_fd(channel); + first = g_list_first(adapter->ioreq_list); + if (first == NULL) + return TRUE; + + req = first->data; + req->len = recv(sk, req->buf, sizeof(req->buf), 0); + + adapter->ioreq_list = g_list_remove(adapter->ioreq_list, req); + + g_idle_add(execute_recv_cb, req); + + return TRUE; +} + +int near_adapter_connect(uint32_t idx, uint32_t target_idx, uint8_t protocol) +{ + struct near_adapter *adapter; + struct near_tag *tag; + struct sockaddr_nfc addr; + int err, sock; + + DBG("idx %d", idx); + + adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx)); + if (adapter == NULL) + return -ENODEV; + + if (adapter->tag_sock != -1) + return -EALREADY; + + tag = g_hash_table_lookup(adapter->tags, + GINT_TO_POINTER(target_idx)); + if (tag == NULL) + return -ENOLINK; + + sock = socket(AF_NFC, SOCK_SEQPACKET, NFC_SOCKPROTO_RAW); + if (sock == -1) + return sock; + + addr.sa_family = AF_NFC; + addr.dev_idx = idx; + addr.target_idx = target_idx; + addr.nfc_protocol = protocol; + + err = connect(sock, (struct sockaddr *) &addr, sizeof(addr)); + if (err) { + close(sock); + return err; + } + + adapter->tag_sock = sock; + adapter->tag_link = tag; + + if (adapter->channel == NULL) + adapter->channel = g_io_channel_unix_new(adapter->tag_sock); + + g_io_channel_set_flags(adapter->channel, G_IO_FLAG_NONBLOCK, NULL); + g_io_channel_set_close_on_unref(adapter->channel, TRUE); + + if (adapter->watch == 0) + adapter->watch = g_io_add_watch(adapter->channel, + G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP, + adapter_recv_event, adapter); + + return 0; +} + +int near_adapter_disconnect(uint32_t idx) +{ + struct near_adapter *adapter; + uint32_t target_idx; + uint16_t tag_type; + + DBG("idx %d", idx); + + adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx)); + if (adapter == NULL) + return -ENODEV; + + DBG("link %p", adapter->tag_link); + + if (adapter->tag_link == NULL) + return -ENOLINK; + + tag_type = __near_tag_get_type(adapter->tag_link); + target_idx = near_tag_get_target_idx(adapter->tag_link); + + DBG("tag type %d", tag_type); + + __near_adapter_remove_target(adapter->idx, target_idx); + + if (adapter->tag_sock == -1) + return -ENOLINK; + + if (adapter->watch > 0) { + g_source_remove(adapter->watch); + adapter->watch = 0; + } + + g_io_channel_unref(adapter->channel); + adapter->channel = NULL; + adapter->tag_sock = -1; + adapter->tag_link = NULL; + + return 0; +} + +int near_adapter_send(uint32_t idx, uint8_t *buf, size_t length, + near_recv cb, void *data, near_release data_rel) +{ + struct near_adapter *adapter; + struct near_adapter_ioreq *req = NULL; + int err; + + DBG("idx %d", idx); + + adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx)); + if (adapter == NULL) { + err = -ENODEV; + goto out_err; + } + + if (adapter->tag_sock == -1 || adapter->tag_link == NULL) { + err = -ENOLINK; + goto out_err; + } + + if (cb != NULL && adapter->watch != 0) { + req = g_try_malloc0(sizeof(*req)); + if (req == NULL) { + err = -ENOMEM; + goto out_err; + } + + DBG("req %p cb %p data %p", req, cb, data); + + req->target_idx = near_tag_get_target_idx(adapter->tag_link); + req->cb = cb; + req->data = data; + + adapter->ioreq_list = + g_list_append(adapter->ioreq_list, req); + } + + err = send(adapter->tag_sock, buf, length, 0); + if (err < 0) + goto out_err; + + return err; + +out_err: + if (req != NULL) { + GList *last = g_list_last(adapter->ioreq_list); + + g_free(req); + adapter->ioreq_list = + g_list_delete_link(adapter->ioreq_list, last); + } + + if (data_rel != NULL) + return (*data_rel)(err, data); + + return err; +} + +static void adapter_listen(gpointer key, gpointer value, gpointer user_data) +{ + struct near_adapter *adapter = value; + struct near_device_driver *driver = user_data; + + DBG("%s", adapter->path); + + if (adapter->path == NULL) + return; + + driver->listen(adapter->idx, device_read_cb); +} + +void __near_adapter_listen(struct near_device_driver *driver) +{ + g_hash_table_foreach(adapter_hash, adapter_listen, driver); +} + +int __near_adapter_init(void) +{ + DBG(""); + + connection = near_dbus_get_connection(); + + adapter_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, free_adapter); + + return 0; +} + +void __near_adapter_cleanup(void) +{ + DBG(""); + + g_hash_table_destroy(adapter_hash); + adapter_hash = NULL; +} diff --git a/src/agent.c b/src/agent.c new file mode 100644 index 0000000..5fe34cf --- /dev/null +++ b/src/agent.c @@ -0,0 +1,322 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> + +#include <glib.h> + +#include <gdbus.h> + +#include "near.h" + +static DBusConnection *connection = NULL; +static GHashTable *ndef_app_hash; + +struct near_ndef_agent { + char *sender; + char *path; + char *record_type; + guint watch; +}; + +static void ndef_agent_free(gpointer data) +{ + DBusMessage *message; + struct near_ndef_agent *agent = data; + + DBG(""); + + if (agent == NULL) + return; + + DBG("%s %s %s", agent->sender, agent->path, NFC_NDEF_AGENT_INTERFACE); + + message = dbus_message_new_method_call(agent->sender, agent->path, + NFC_NDEF_AGENT_INTERFACE, "Release"); + if (message == NULL) + return; + + dbus_message_set_no_reply(message, TRUE); + + g_dbus_send_message(connection, message); + + g_dbus_remove_watch(connection, agent->watch); + + g_free(agent->sender); + g_free(agent->path); +} + +static void ndef_agent_disconnect(DBusConnection *conn, void *user_data) +{ + struct near_ndef_agent *agent = user_data; + + DBG("agent %s disconnected", agent->path); + + g_hash_table_remove(ndef_app_hash, agent->record_type); +} + +static void append_record_path(DBusMessageIter *iter, void *user_data) +{ + GList *records = user_data, *list; + struct near_ndef_record *record; + char *path; + + for (list = records; list; list = list->next) { + record = list->data; + + path = __near_ndef_record_get_path(record); + if (path == NULL) + continue; + + dbus_message_iter_append_basic(iter, + DBUS_TYPE_STRING, &path); + } +} + +static void append_ndef(DBusMessageIter *iter, void *user_data) +{ + GList *records = user_data; + + __near_ndef_append_records(iter, records); +} + +static void ndef_agent_push_records(struct near_ndef_agent *agent, + GList *records) +{ + DBusMessageIter iter, dict; + DBusMessage *message; + + DBG(""); + + if (agent->sender == NULL || agent->path == NULL) + return; + + DBG("Sending NDEF to %s %s", agent->path, agent->sender); + + message = dbus_message_new_method_call(agent->sender, agent->path, + NFC_NDEF_AGENT_INTERFACE, + "GetNDEF"); + if (message == NULL) + return; + + dbus_message_iter_init_append(message, &iter); + + near_dbus_dict_open(&iter, &dict); + near_dbus_dict_append_array(&dict, "Records", + DBUS_TYPE_STRING, append_record_path, records); + near_dbus_dict_append_array(&dict, "NDEF", + DBUS_TYPE_BYTE, append_ndef, records); + near_dbus_dict_close(&iter, &dict); + + DBG("sending..."); + + dbus_message_set_no_reply(message, TRUE); + + g_dbus_send_message(connection, message); +} + +void __near_agent_ndef_parse_records(GList *records) +{ + GList *list; + struct near_ndef_record *record; + struct near_ndef_agent *agent; + char *type; + + DBG(""); + + for (list = records, agent = NULL; list; list = list->next) { + record = list->data; + type = __near_ndef_record_get_type(record); + + if (type == NULL) + continue; + + DBG("Looking for type %s", type); + + agent = g_hash_table_lookup(ndef_app_hash, type); + if (agent != NULL) + break; + } + + if (agent == NULL) + return; + + ndef_agent_push_records(agent, records); + + return; +} + +int __near_agent_ndef_register(const char *sender, const char *path, + const char *record_type) +{ + struct near_ndef_agent *agent; + + DBG("%s registers path %s for %s", sender, path, record_type); + + if (g_hash_table_lookup(ndef_app_hash, record_type) != NULL) + return -EEXIST; + + agent = g_try_malloc0(sizeof(struct near_ndef_agent)); + if (agent == NULL) + return -ENOMEM; + + agent->sender = g_strdup(sender); + agent->path = g_strdup(path); + agent->record_type = g_strdup(record_type); + + if (agent->sender == NULL || agent->path == NULL || + agent->record_type == NULL) { + g_free(agent); + return -ENOMEM; + } + + agent->watch = g_dbus_add_disconnect_watch(connection, sender, + ndef_agent_disconnect, agent, NULL); + g_hash_table_insert(ndef_app_hash, agent->record_type, agent); + + return 0; +} + +int __near_agent_ndef_unregister(const char *sender, const char *path, + const char *record_type) +{ + struct near_ndef_agent *agent; + + DBG("sender %s path %s type %s", sender, path, record_type); + + agent = g_hash_table_lookup(ndef_app_hash, record_type); + if (agent == NULL) + return -EINVAL; + + if (strcmp(agent->path, path) != 0 || strcmp(agent->sender, sender) != 0) + return -EINVAL; + + g_hash_table_remove(ndef_app_hash, record_type); + + return 0; +} + +static guint handover_agent_watch = 0; +static gchar *handover_agent_path = NULL; +static gchar *handover_agent_sender = NULL; + +static void handover_agent_free(void) +{ + if (handover_agent_watch > 0) { + g_dbus_remove_watch(connection, handover_agent_watch); + handover_agent_watch = 0; + } + + g_free(handover_agent_sender); + handover_agent_sender = NULL; + + g_free(handover_agent_path); + handover_agent_path = NULL; +} + +static void handover_agent_disconnect(DBusConnection *conn, void *data) +{ + DBG("data %p", data); + + handover_agent_watch = 0; + + handover_agent_free(); +} + +static void handover_agent_release(void) +{ + DBusMessage *message; + + if (handover_agent_watch == 0) + return; + + message = dbus_message_new_method_call(handover_agent_sender, + handover_agent_path, + "org.neard.HandoverAgent", + "Release"); + if (message != NULL) + g_dbus_send_message(connection, message); + + handover_agent_free(); +} + +int __near_agent_handover_register(const char *sender, const char *path) +{ + DBG("sender %s path %s", sender, path); + + if (handover_agent_path != NULL) + return -EEXIST; + + handover_agent_watch = g_dbus_add_disconnect_watch(connection, sender, + handover_agent_disconnect, NULL, NULL); + if (handover_agent_watch == 0) + return -ENOMEM; + + handover_agent_sender = g_strdup(sender); + handover_agent_path = g_strdup(path); + + return 0; +} + +int __near_agent_handover_unregister(const char *sender, const char *path) +{ + DBG("sender %s path %s", sender, path); + + if (handover_agent_path == NULL) + return -ESRCH; + + handover_agent_free(); + + return 0; +} + +int __near_agent_init(void) +{ + DBG(""); + + connection = near_dbus_get_connection(); + if (connection == NULL) + return -1; + + ndef_app_hash = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, ndef_agent_free); + + return 0; +} + +void __near_agent_cleanup(void) +{ + DBG(""); + + g_hash_table_destroy(ndef_app_hash); + ndef_app_hash = NULL; + + handover_agent_release(); + + dbus_connection_unref(connection); +} diff --git a/src/bluetooth.c b/src/bluetooth.c new file mode 100644 index 0000000..63e76da --- /dev/null +++ b/src/bluetooth.c @@ -0,0 +1,1062 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <gdbus.h> + +#include "near.h" + +#define BLUEZ_SERVICE "org.bluez" +#define MANAGER_INTF BLUEZ_SERVICE ".Manager" +#define ADAPTER_INTF BLUEZ_SERVICE ".Adapter" +#define OOB_INTF BLUEZ_SERVICE ".OutOfBand" +#define DEFAULT_ADAPTER "DefaultAdapter" +#define ADAPTER_REMOVED "AdapterRemoved" +#define DEFAULT_ADAPTER_CHANGED "DefaultAdapterChanged" +#define MANAGER_PATH "/" +#define OOB_AGENT "/org/neard/agent/neard_oob" + +#define BT_NOINPUTOUTPUT "NoInputNoOutput" +#define BT_DISPLAY_YESNO "DisplayYesNo" + +/* BT EIR list */ +#define EIR_UUID128_ALL 0x07 /* 128-bit UUID, all listed */ +#define EIR_NAME_SHORT 0x08 /* shortened local name */ +#define EIR_NAME_COMPLETE 0x09 /* complete local name */ + +/* Specific OOB EIRs */ +#define EIR_CLASS_OF_DEVICE 0x0D /* class of device */ +#define EIR_SP_HASH 0x0E /* simple pairing hash C */ +#define EIR_SP_RANDOMIZER 0x0F /* simple pairing randomizer R */ +/* Optional EIRs */ +#define EIR_DEVICE_ID 0x10 /* device ID */ +#define EIR_SECURITY_MGR_FLAGS 0x11 /* security manager flags */ + +#define EIR_SIZE_LEN 1 +#define EIR_HEADER_LEN (EIR_SIZE_LEN + 1) +#define BT_ADDRESS_SIZE 6 +#define COD_SIZE 3 +#define OOB_SP_SIZE 16 + +#define get_unaligned(ptr) \ +({ \ + struct __attribute__((packed)) { \ + typeof(*(ptr)) __v; \ + } *__p = (typeof(__p)) (ptr); \ + __p->__v; \ +}) + +struct near_oob_data { + char *def_adapter; + + char *bd_addr; /* oob mandatory */ + + /* optional */ + char *bt_name; /* short or long name */ + uint8_t bt_name_len; + int class_of_device; /* Class of device */ + near_bool_t powered; + near_bool_t pairable; + near_bool_t discoverable; + uint8_t *uuids; + int uuids_len; + + uint8_t *spair_hash; /* OOB hash Key */ + uint8_t *spair_randomizer; /* OOB randomizer key */ + uint8_t authentication[OOB_SP_SIZE]; /* On BT 2.0 */ + uint8_t security_manager_oob_flags; /* see BT Core 4.0 */ +}; + +static DBusConnection *bt_conn; +static struct near_oob_data bt_def_oob_data; + +static int bt_do_pairing(struct near_oob_data *oob); + +static void __bt_eir_free(struct near_oob_data *oob) +{ + DBG(""); + + if (oob->def_adapter != NULL) { + g_free(oob->def_adapter); + oob->def_adapter = NULL; + } + + if (oob->bd_addr != NULL) { + g_free(oob->bd_addr); + oob->bd_addr = NULL; + } + + if (oob->bt_name != NULL) { + g_free(oob->bt_name); + oob->bt_name = NULL; + } + + if (oob->spair_hash != NULL) { + g_free(oob->spair_hash); + oob->spair_hash = NULL; + } + + if (oob->spair_randomizer != NULL) { + g_free(oob->spair_randomizer); + oob->spair_randomizer = NULL; + } + + +} + +static void bt_eir_free(struct near_oob_data *oob) +{ + __bt_eir_free(oob); + + g_free(oob); +} + +/* D-Bus helper functions */ +static int bt_generic_call(DBusConnection *conn, + struct near_oob_data *oob, /* user data */ + const char *dest, /* method call */ + const char *path, + const char *interface, + const char *method, + DBusPendingCallNotifyFunction bt_cb, /* callback */ + int type, ...) /* params */ +{ + DBusMessage *msg; + DBusPendingCall *pending; + va_list args; + int err; + + DBG("%s", method); + + msg = dbus_message_new_method_call(dest, path, interface, method); + + if (msg == NULL) { + near_error("Unable to allocate new D-Bus %s message", method); + err = -ENOMEM; + } + + va_start(args, type); + + if (!dbus_message_append_args_valist(msg, type, args)) { + va_end(args); + err = -EIO; + goto error_done; + } + va_end(args); + + if (!dbus_connection_send_with_reply(conn, msg, &pending, -1)) { + near_error("Sending %s failed", method); + err = -EIO; + goto error_done; + } + + if (pending == NULL) { + near_error("D-Bus connection not available"); + err = -EIO; + goto error_done; + } + + /* Prepare for notification */ + dbus_pending_call_set_notify(pending, bt_cb, oob, NULL); + err = 0 ; + +error_done: + dbus_message_unref(msg); + return err; +} + +static void bt_create_paired_device_cb(DBusPendingCall *pending, + void *user_data) +{ + DBusMessage *reply; + DBusError error; + struct near_oob_data *oob = user_data; + + DBG(""); + + reply = dbus_pending_call_steal_reply(pending); + if (reply == NULL) + return; + + dbus_error_init(&error); + + if (dbus_set_error_from_message(&error, reply)) { + near_error("%s", error.message); + dbus_error_free(&error); + goto cb_done; + } + + DBG("Successful pairing"); + +cb_done: + /* task completed - clean memory*/ + bt_eir_free(oob); + + dbus_message_unref(reply); + dbus_pending_call_unref(pending); + + return; +} + +static int bt_create_paired_device(DBusConnection *conn, + struct near_oob_data *oob, + const char *capabilities) +{ + const char *agent_path = OOB_AGENT; + + return bt_generic_call(bt_conn, oob, BLUEZ_SERVICE, + oob->def_adapter, ADAPTER_INTF, "CreatePairedDevice", + bt_create_paired_device_cb, + /* params */ + DBUS_TYPE_STRING, &oob->bd_addr, + DBUS_TYPE_OBJECT_PATH, &agent_path, + DBUS_TYPE_STRING, &capabilities, + DBUS_TYPE_INVALID); + +} + +static void bt_oob_add_remote_data_cb(DBusPendingCall *pending, void *user_data) +{ + DBusMessage *reply; + DBusError error; + struct near_oob_data *oob = user_data; + + DBG(""); + + reply = dbus_pending_call_steal_reply(pending); + if (reply == NULL) + return; + + dbus_error_init(&error); + + if (dbus_set_error_from_message(&error, reply)) + goto cb_fail; + + near_info("OOB data added"); + + dbus_message_unref(reply); + dbus_pending_call_unref(pending); + + /* Jump to the next: Pairing !!!*/ + DBG("Try to pair devices..."); + bt_create_paired_device(bt_conn, oob, BT_DISPLAY_YESNO); + return; + +cb_fail: + near_error("%s", error.message); + dbus_error_free(&error); + + bt_eir_free(oob); + + dbus_message_unref(reply); + dbus_pending_call_unref(pending); + + return; +} + +static int bt_oob_add_remote_data(DBusConnection *conn, + struct near_oob_data *oob) +{ + int16_t hash_len = 16; + int16_t rdm_len = 16; + + return bt_generic_call(bt_conn, oob, BLUEZ_SERVICE, + oob->def_adapter, OOB_INTF, "AddRemoteData", + bt_oob_add_remote_data_cb, + /* params */ + DBUS_TYPE_STRING, &oob->bd_addr, + DBUS_TYPE_ARRAY, + DBUS_TYPE_BYTE, &oob->spair_hash, hash_len, + DBUS_TYPE_ARRAY, + DBUS_TYPE_BYTE, &oob->spair_randomizer, rdm_len, + DBUS_TYPE_INVALID); +} + +/* Pairing: JustWorks or OOB */ +static int bt_do_pairing(struct near_oob_data *oob) +{ + int err = 0; + + DBG("%s", oob->bd_addr); + + /* Is this a *real* oob pairing or a "JustWork" */ + if ((oob->spair_hash) && (oob->spair_randomizer)) + err = bt_oob_add_remote_data(bt_conn, oob); + else + err = bt_create_paired_device(bt_conn, oob, + BT_NOINPUTOUTPUT); + + if (err < 0) + near_error("Pairing failed. Err[%d]", err); + + return err; +} + +/* + */ +static int extract_properties(DBusMessage *reply, + struct near_oob_data *oob) +{ + char *data = NULL; + int idata; + int i, j; + + DBusMessageIter array, dict; + + if (dbus_message_iter_init(reply, &array) == FALSE) + return -1; + + if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY) + return -1; + + dbus_message_iter_recurse(&array, &dict); + + while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter entry, value; + const char *key; + + dbus_message_iter_recurse(&dict, &entry); + dbus_message_iter_get_basic(&entry, &key); + + dbus_message_iter_next(&entry); + dbus_message_iter_recurse(&entry, &value); + + if (g_str_equal(key, "Address") == TRUE) { + dbus_message_iter_get_basic(&value, &data); + + /* Now, fill the local struct */ + oob->bd_addr = g_try_malloc0(BT_ADDRESS_SIZE); + if (oob->bd_addr == NULL) + return -ENOMEM; + + /* Address is like: "ff:ee:dd:cc:bb:aa" */ + for (i = 5, j = 0 ; i >= 0; i--, j += 3) + oob->bd_addr[i] = strtol(data + j, NULL, 16); + DBG("local address: %s", data); + + } else if (g_str_equal(key, "Name") == TRUE) { + dbus_message_iter_get_basic(&value, &data); + oob->bt_name = g_strdup(data); + if (oob->bt_name != NULL) { + oob->bt_name_len = strlen(oob->bt_name); + DBG("local name: %s", oob->bt_name); + } + + } else if (g_str_equal(key, "Class") == TRUE) { + dbus_message_iter_get_basic(&value, &idata); + oob->class_of_device = idata; + + } else if (g_str_equal(key, "Powered") == TRUE) { + dbus_message_iter_get_basic(&value, &idata); + oob->powered = idata; + + } else if (g_str_equal(key, "Discoverable") == TRUE) { + dbus_message_iter_get_basic(&value, &idata); + oob->discoverable = idata; + + } else if (g_str_equal(key, "Pairable") == TRUE) { + dbus_message_iter_get_basic(&value, &idata); + oob->pairable = idata; + + } else if (g_str_equal(key, "UUIDs") == TRUE) { + oob->uuids_len = sizeof(value); + oob->uuids = g_try_malloc0(oob->uuids_len); + if (oob->uuids == NULL) + return -ENOMEM; + memcpy(oob->uuids, &value, oob->uuids_len); + } + + dbus_message_iter_next(&dict); + } + + return 0; +} + +static int bt_parse_properties(DBusMessage *reply, void *user_data) +{ + struct near_oob_data *bt_props = user_data; + + DBG(""); + + /* Free datas */ + g_free(bt_props->bd_addr); + g_free(bt_props->bt_name); + + /* Grab properties from dbus */ + if (extract_properties(reply, bt_props) < 0) + goto fail; + + return 0; + +fail: + g_free(bt_props->bd_addr); + bt_props->bd_addr = NULL; + + g_free(bt_props->bt_name); + bt_props->bt_name = NULL; + + return -ENOMEM; +} + + +/* Get default local adapter properties */ +static void bt_get_properties_cb(DBusPendingCall *pending, void *user_data) +{ + struct near_oob_data *bt_props = user_data; + DBusMessage *reply; + DBusError error; + int err; + + DBG(""); + + reply = dbus_pending_call_steal_reply(pending); + if (reply == NULL) + return; + + dbus_error_init(&error); + + if (dbus_set_error_from_message(&error, reply)) + goto cb_fail; + + err = bt_parse_properties(reply, bt_props); + if (err < 0) + near_error("Problem parsing local properties %d", err); + else + DBG("Get Properties complete: %s", bt_props->def_adapter); + + /* clean */ + dbus_message_unref(reply); + dbus_pending_call_unref(pending); + return; + +cb_fail: + near_error("%s", error.message); + dbus_error_free(&error); + + dbus_message_unref(reply); + dbus_pending_call_unref(pending); + + return; +} + +static void bt_get_default_adapter_cb(DBusPendingCall *pending, void *user_data) +{ + struct near_oob_data *bt_props = user_data; + DBusMessage *reply; + DBusError error; + gchar *path; + + DBG(""); + + reply = dbus_pending_call_steal_reply(pending); + if (reply == NULL) + return; + + dbus_error_init(&error); + + if (dbus_set_error_from_message(&error, reply)) + goto cb_fail; + + if (dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, + &path, DBUS_TYPE_INVALID) == FALSE) + goto cb_fail; + + /* Save the default adapter */ + bt_props->def_adapter = g_strdup(path); + DBG("Using default adapter %s", bt_props->def_adapter); + + /* clean */ + dbus_message_unref(reply); + dbus_pending_call_unref(pending); + + /* Jump on getAdapterProperties */ + bt_generic_call(bt_conn, bt_props, + BLUEZ_SERVICE, + bt_props->def_adapter, + ADAPTER_INTF, "GetProperties", + bt_get_properties_cb, + DBUS_TYPE_INVALID); + return; + +cb_fail: + near_error("%s", error.message); + dbus_error_free(&error); + + dbus_message_unref(reply); + dbus_pending_call_unref(pending); + + return; +} + +static int bt_refresh_adapter_props(DBusConnection *conn, void *user_data) +{ + DBG("%p %p", conn, user_data); + + return bt_generic_call(conn, user_data, + BLUEZ_SERVICE, + MANAGER_PATH, MANAGER_INTF, + DEFAULT_ADAPTER, + bt_get_default_adapter_cb, + DBUS_TYPE_INVALID); +} + +/* Parse and fill the bluetooth oob information block */ +static void bt_parse_eir(uint8_t *eir_data, uint16_t eir_data_len, + struct near_oob_data *oob, uint16_t *props) +{ + char *tmp; + uint16_t len = 0; + + DBG("total len: %u", eir_data_len); + + while (len < eir_data_len - 1) { + uint8_t eir_len = eir_data[0]; /* EIR field length */ + uint8_t eir_code; /* EIR field type*/ + uint8_t data_len; /* EIR data length */ + uint8_t *data; + + /* check for early termination */ + if (eir_len == 0) + break; + + len += eir_len + 1; + + /* Do not continue EIR Data parsing if got incorrect length */ + if (len > eir_data_len) + break; + + data_len = eir_len - 1; + + eir_code = eir_data[1]; /* EIR code */ + data = &eir_data[2]; + + DBG("type 0x%.2X data_len %u", eir_code, data_len); + + switch (eir_code) { + case EIR_NAME_SHORT: + case EIR_NAME_COMPLETE: + oob->bt_name = g_try_malloc0(data_len + 1); /* eos */ + if (oob->bt_name) { + oob->bt_name_len = data_len; + memcpy(oob->bt_name, data, oob->bt_name_len); + oob->bt_name[data_len] = 0; /* end str*/ + } + break; + + case EIR_CLASS_OF_DEVICE: + tmp = g_strdup_printf("%02X%02X%02X", + *data, *(data + 1), *(data + 2)); + if (tmp != NULL) { + oob->class_of_device = strtol(tmp, NULL, 16); + *props |= OOB_PROPS_COD; + } + g_free(tmp); + break; + + case EIR_SP_HASH: + oob->spair_hash = g_try_malloc0(OOB_SP_SIZE); + if (oob->spair_hash) { + memcpy(oob->spair_hash, data, OOB_SP_SIZE); + *props |= OOB_PROPS_SP_HASH; + } + break; + + case EIR_SP_RANDOMIZER: + oob->spair_randomizer = g_try_malloc0(OOB_SP_SIZE); + if (oob->spair_randomizer) { + memcpy(oob->spair_randomizer, + data, OOB_SP_SIZE); + *props |= OOB_PROPS_SP_RANDOM; + } + break; + + case EIR_SECURITY_MGR_FLAGS: + oob->security_manager_oob_flags = *data; + break; + + case EIR_UUID128_ALL: + /* TODO: Process uuids128 + * */ + break; + + default: /* ignore and skip */ + near_error("Unknown EIR x%02x (len: %d)", eir_code, + eir_len); + break; + } + /* Next eir */ + eir_data += eir_len + 1; + } +} + +/* + * Because of some "old" implementation, "version" will help + * to determine the record data structure. + * Some specifications are proprietary (eg. "short mode") + * and are not fully documented. + * mime_properties is a bitmask and should reflect the fields found in + * the incoming oob. + */ +int __near_bluetooth_parse_oob_record(uint8_t version, uint8_t *bt_data, + uint16_t *mime_properties, near_bool_t pair) +{ + struct near_oob_data *oob; + uint16_t bt_oob_data_size; + uint8_t *ptr = bt_data; + uint8_t marker; + char *tmp; + + DBG(""); + + oob = g_try_malloc0(sizeof(struct near_oob_data)); + + if (version == BT_MIME_V2_1) { + /* + * Total OOB data size (including size bytes) + * Some implementations (e.g. Android 4.1) stores + * the data_size in big endian but NDEF forum spec (BT Secure + * Simple Pairing) requires a little endian. At the same time, + * the NDEF forum NDEF spec define a payload length as single + * byte (and the payload size IS the oob data size). + */ + bt_oob_data_size = + GUINT16_FROM_LE(get_unaligned((uint16_t *) bt_data)); + if (bt_oob_data_size > 0xFF) /* Big Endian */ + bt_oob_data_size = GUINT16_FROM_BE(bt_oob_data_size); + + bt_oob_data_size -= 2 ; /* remove oob datas size len */ + + /* First item: BD_ADDR (mandatory) */ + ptr = &bt_data[2]; + oob->bd_addr = g_strdup_printf("%02X:%02X:%02X:%02X:%02X:%02X", + ptr[5], ptr[4], ptr[3], ptr[2], ptr[1], ptr[0]); + + /* Skip to the next element (optional) */ + ptr += BT_ADDRESS_SIZE; + bt_oob_data_size -= BT_ADDRESS_SIZE ; + + if (bt_oob_data_size) + bt_parse_eir(ptr, bt_oob_data_size, oob, + mime_properties); + } else if (version == BT_MIME_V2_0) { + marker = *ptr++; /* could be '$' */ + + oob->bd_addr = g_strdup_printf( + "%02X:%02X:%02X:%02X:%02X:%02X", + ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]); + ptr = ptr + BT_ADDRESS_SIZE; + + /* Class of device */ + tmp = g_strdup_printf("%02X%02X%02X", + *ptr, *(ptr + 1), *(ptr + 2)); + if (tmp != NULL) + oob->class_of_device = strtol(tmp, NULL, 16); + g_free(tmp); + + ptr = ptr + 3; + + /* "Short mode" seems to use a 4 bytes code + * instead of 16 bytes... + */ + if (marker == '$') { /* Short NFC */ + memcpy(oob->authentication, ptr, 4); + ptr = ptr + 4; + } else { + memcpy(oob->authentication, ptr, 16); + ptr = ptr + 16; + } + + /* get the device name */ + oob->bt_name_len = *ptr++; + oob->bt_name = g_try_malloc0(oob->bt_name_len+1); + if (oob->bt_name) { + memcpy(oob->bt_name, ptr, oob->bt_name_len); + oob->bt_name[oob->bt_name_len+1] = 0; + } + ptr = ptr + oob->bt_name_len; + } else { + return -EINVAL; + } + + if (pair == FALSE) + return 0; + + /* check and get the default adapter */ + oob->def_adapter = g_strdup(bt_def_oob_data.def_adapter); + if (oob->def_adapter == NULL) { + near_error("bt_get_default_adapter failed"); + bt_eir_free(oob); + return -EIO; + } + + return bt_do_pairing(oob); +} + +int __near_bluetooth_pair(void *data) +{ + struct near_oob_data *oob = data; + + /* check and get the default adapter */ + oob->def_adapter = g_strdup(bt_def_oob_data.def_adapter); + if (oob->bt_name == NULL) { + near_error("bt_get_default_adapter failed: %d", -EIO); + bt_eir_free(oob); + return -EIO; + } + + return bt_do_pairing(oob); +} + +/* This function is synchronous as oob datas change on each session */ +static int bt_sync_oob_readlocaldata(DBusConnection *conn, char *adapter_path, + char *spair_hash, + char *spair_randomizer) +{ + DBusMessage *message, *reply; + DBusError error; + int hash_len, rndm_len; + + message = dbus_message_new_method_call(BLUEZ_SERVICE, adapter_path, + OOB_INTF, "ReadLocalData"); + if (!message) + return 0; + + dbus_error_init(&error); + + reply = dbus_connection_send_with_reply_and_block(conn, + message, -1, &error); + + dbus_message_unref(message); + + if (!reply) { + if (dbus_error_is_set(&error) == TRUE) { + near_error("%s", error.message); + dbus_error_free(&error); + } else { + near_error("Failed to set property"); + } + return 0; + } + + if (dbus_message_get_args(reply, NULL, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, spair_hash, &hash_len, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + spair_randomizer, &rndm_len, + DBUS_TYPE_INVALID) == FALSE) + goto done; + + if ((hash_len != OOB_SP_SIZE) || (rndm_len != OOB_SP_SIZE)) { + DBG("no OOB data found !"); + goto done; + } + + dbus_message_unref(reply); + DBG("OOB data found"); + return hash_len; + +done: + dbus_message_unref(reply); + return 0; +} + +/* + * External API to get bt properties + * Prepare a "real" oob datas block + * mime_props is a bitmask we use to add or not specific fields in the + * oob frame (e.g.: OOB keys) + * */ +uint8_t *__near_bluetooth_local_get_properties(int *bt_data_len, + uint16_t mime_props) +{ + uint8_t *bt_oob_block = NULL; + uint16_t bt_oob_block_size = 0; + int max_block_size; + uint8_t offset; + + char hash[OOB_SP_SIZE]; + char random[OOB_SP_SIZE]; + + /* Check adapter datas */ + if (bt_def_oob_data.def_adapter == NULL) { + near_error("No bt adapter info"); + goto fail; + } + + /* Prepare the BT block */ + max_block_size = sizeof(uint16_t) + /* stored oob size */ + BT_ADDRESS_SIZE + + EIR_HEADER_LEN + bt_def_oob_data.bt_name_len + + EIR_HEADER_LEN + COD_SIZE; /* class */ + + /* Should we add oob pairing keys ?*/ + if (mime_props & OOB_PROPS_SP) { + max_block_size += (EIR_HEADER_LEN + OOB_SP_SIZE + /* oob hash */ + EIR_HEADER_LEN + OOB_SP_SIZE); /* oob random */ + } + + bt_oob_block_size = sizeof(uint16_t) /* stored oob size */ + + BT_ADDRESS_SIZE; /* device address */ + + bt_oob_block = g_try_malloc0(max_block_size); + if (bt_oob_block == NULL) + goto fail; + offset = sizeof(uint16_t); /* Skip size...will be filled later */ + + /* Now prepare data frame */ + memcpy(bt_oob_block + offset, bt_def_oob_data.bd_addr, BT_ADDRESS_SIZE); + offset += BT_ADDRESS_SIZE; + + /* bt name */ + if (bt_def_oob_data.bt_name != NULL) { + bt_oob_block_size += (bt_def_oob_data.bt_name_len + + EIR_HEADER_LEN); + + bt_oob_block[offset++] = bt_def_oob_data.bt_name_len + + EIR_SIZE_LEN; + bt_oob_block[offset++] = EIR_NAME_COMPLETE; /* EIR data type */ + memcpy(bt_oob_block + offset, bt_def_oob_data.bt_name, + bt_def_oob_data.bt_name_len); + offset += bt_def_oob_data.bt_name_len; + } + + /* CoD */ + bt_oob_block_size += COD_SIZE + EIR_HEADER_LEN; + + bt_oob_block[offset++] = COD_SIZE + EIR_SIZE_LEN; + bt_oob_block[offset++] = EIR_CLASS_OF_DEVICE; + + memcpy(bt_oob_block + offset, + (uint8_t *)&bt_def_oob_data.class_of_device, COD_SIZE); + offset += COD_SIZE; + + /* The following data are generated dynamically + * so we have to read the local oob data + * */ + /* Should we add oob pairing keys */ + if ((mime_props & OOB_PROPS_SP) == 0) + goto out; + + if (bt_sync_oob_readlocaldata(bt_conn, bt_def_oob_data.def_adapter, + hash, random) == OOB_SP_SIZE) { + bt_oob_block_size += 2 * (OOB_SP_SIZE + EIR_HEADER_LEN); + + /* OOB datas */ + if (hash != NULL) { + bt_oob_block[offset++] = OOB_SP_SIZE + EIR_SIZE_LEN; + bt_oob_block[offset++] = EIR_SP_HASH; + memcpy(bt_oob_block + offset, hash, OOB_SP_SIZE); + offset += OOB_SP_SIZE; + } + + if (random != NULL) { + bt_oob_block[offset++] = OOB_SP_SIZE + EIR_SIZE_LEN; + bt_oob_block[offset++] = EIR_SP_RANDOMIZER; + memcpy(bt_oob_block + offset, random, OOB_SP_SIZE); + offset += OOB_SP_SIZE; + } + } + +out: + *(uint16_t *)bt_oob_block = bt_oob_block_size ; + *bt_data_len = bt_oob_block_size; + + return bt_oob_block; + +fail: + g_free(bt_oob_block); + return NULL; +} + +static void bt_connect(DBusConnection *conn, void *user_data) +{ + + DBG("connection %p with %p", conn, user_data); + if (bt_refresh_adapter_props(conn, user_data) < 0) + near_error("bt_get_default_adapter failed"); + + return; +} + +static void bt_disconnect(DBusConnection *conn, void *user_data) +{ + DBG("%p", conn); + + __bt_eir_free(user_data); +} + +/* BT adapter removed handler */ +static gboolean bt_adapter_removed(DBusConnection *conn, + DBusMessage *message, + void *user_data) +{ + DBusMessageIter iter; + struct near_oob_data *bt_props = user_data; + const char *adapter_path; + + DBG(""); + + if (bt_props->def_adapter == NULL) + return TRUE; + + if (dbus_message_iter_init(message, &iter) == FALSE) + return TRUE; + + dbus_message_iter_get_basic(&iter, &adapter_path); + + if (g_strcmp0(adapter_path, bt_props->def_adapter) == 0) { + near_info("Remove the default adapter [%s]", adapter_path); + + __bt_eir_free(bt_props); + bt_props->def_adapter = NULL; + } + + return TRUE; +} + +/* BT default adapter changed handler */ +static gboolean bt_default_adapter_changed(DBusConnection *conn, + DBusMessage *message, + void *user_data) +{ + struct near_oob_data *bt_props = user_data; + DBusMessageIter iter; + const char *adapter_path; + + DBG(""); + + if (dbus_message_iter_init(message, &iter) == FALSE) + return TRUE; + + dbus_message_iter_get_basic(&iter, &adapter_path); + DBG("New default adapter [%s]", adapter_path); + + /* Disable the old one */ + __bt_eir_free(bt_props); + bt_props->def_adapter = NULL; + + /* Refresh */ + bt_refresh_adapter_props(conn, user_data); + + return TRUE; +} + +static void bt_dbus_disconnect_cb(DBusConnection *conn, void *user_data) +{ + near_error("D-Bus disconnect (BT)"); + bt_conn = NULL; +} + +static guint watch; +static guint removed_watch; +static guint adapter_watch; + +static int bt_prepare_handlers(DBusConnection *conn) +{ + DBG("%p", conn); + + if (conn == NULL) + return -EIO; + + watch = g_dbus_add_service_watch(conn, BLUEZ_SERVICE, + bt_connect, + bt_disconnect, + &bt_def_oob_data, NULL); + + removed_watch = g_dbus_add_signal_watch(conn, NULL, NULL, MANAGER_INTF, + ADAPTER_REMOVED, + bt_adapter_removed, + &bt_def_oob_data, NULL); + + + adapter_watch = g_dbus_add_signal_watch(conn, NULL, NULL, MANAGER_INTF, + DEFAULT_ADAPTER_CHANGED, + bt_default_adapter_changed, + &bt_def_oob_data, NULL); + + if (watch == 0 || removed_watch == 0 || adapter_watch == 0) { + near_error("Bluez event handlers failed to register."); + g_dbus_remove_watch(conn, watch); + g_dbus_remove_watch(conn, removed_watch); + g_dbus_remove_watch(conn, adapter_watch); + + return -EIO; + } + + return 0; +} + +/* Bluetooth exiting function */ +void __near_bluetooth_cleanup(void) +{ + DBG(""); + + if (bt_conn == NULL) + return; + + g_dbus_remove_watch(bt_conn, watch); + g_dbus_remove_watch(bt_conn, removed_watch); + g_dbus_remove_watch(bt_conn, adapter_watch); + + dbus_connection_unref(bt_conn); + + __bt_eir_free(&bt_def_oob_data); + + return; +} + +/* + * Bluetooth initialization function. + * Allocate bt local settings storage + * and setup event handlers + */ +int __near_bluetooth_init(void) +{ + DBusError err; + + DBG(""); + + dbus_error_init(&err); + + /* save the dbus connection */ + bt_conn = near_dbus_get_connection(); + if (bt_conn == NULL) { + if (dbus_error_is_set(&err) == TRUE) { + near_error("%s", err.message); + dbus_error_free(&err); + } else + near_error("Can't register with system bus\n"); + return -EIO; + } + + /* dbus disconnect callback */ + g_dbus_set_disconnect_function(bt_conn, bt_dbus_disconnect_cb, + NULL, NULL); + + /* Set bluez event handlers */ + return bt_prepare_handlers(bt_conn); +} diff --git a/src/dbus.c b/src/dbus.c new file mode 100644 index 0000000..ad65544 --- /dev/null +++ b/src/dbus.c @@ -0,0 +1,298 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <gdbus.h> + +#include "near.h" + +static DBusConnection *connection; + +dbus_bool_t near_dbus_validate_ident(const char *ident) +{ + unsigned int i; + + if (ident == NULL) + return FALSE; + + for (i = 0; i < strlen(ident); i++) { + if (ident[i] >= '0' && ident[i] <= '9') + continue; + if (ident[i] >= 'a' && ident[i] <= 'z') + continue; + if (ident[i] >= 'A' && ident[i] <= 'Z') + continue; + return FALSE; + } + + return TRUE; +} + +char *near_dbus_encode_string(const char *value) +{ + GString *str; + unsigned int i, size; + + if (value == NULL) + return NULL; + + size = strlen(value); + + str = g_string_new(NULL); + if (str == NULL) + return NULL; + + for (i = 0; i < size; i++) { + const char tmp = value[i]; + if ((tmp < '0' || tmp > '9') && (tmp < 'A' || tmp > 'Z') && + (tmp < 'a' || tmp > 'z')) + g_string_append_printf(str, "_%02x", tmp); + else + str = g_string_append_c(str, tmp); + } + + return g_string_free(str, FALSE); +} + +void near_dbus_property_append_basic(DBusMessageIter *iter, + const char *key, int type, void *val) +{ + DBusMessageIter value; + const char *signature; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key); + + switch (type) { + case DBUS_TYPE_BOOLEAN: + signature = DBUS_TYPE_BOOLEAN_AS_STRING; + break; + case DBUS_TYPE_STRING: + signature = DBUS_TYPE_STRING_AS_STRING; + break; + case DBUS_TYPE_BYTE: + signature = DBUS_TYPE_BYTE_AS_STRING; + break; + case DBUS_TYPE_UINT16: + signature = DBUS_TYPE_UINT16_AS_STRING; + break; + case DBUS_TYPE_INT16: + signature = DBUS_TYPE_INT16_AS_STRING; + break; + case DBUS_TYPE_UINT32: + signature = DBUS_TYPE_UINT32_AS_STRING; + break; + case DBUS_TYPE_INT32: + signature = DBUS_TYPE_INT32_AS_STRING; + break; + case DBUS_TYPE_OBJECT_PATH: + signature = DBUS_TYPE_OBJECT_PATH_AS_STRING; + break; + default: + signature = DBUS_TYPE_VARIANT_AS_STRING; + break; + } + + dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, + signature, &value); + dbus_message_iter_append_basic(&value, type, val); + dbus_message_iter_close_container(iter, &value); +} + +void near_dbus_property_append_dict(DBusMessageIter *iter, const char *key, + near_dbus_append_cb_t function, void *user_data) +{ + DBusMessageIter value, dict; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key); + + dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, + DBUS_TYPE_ARRAY_AS_STRING + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &value); + + near_dbus_dict_open(&value, &dict); + if (function) + function(&dict, user_data); + near_dbus_dict_close(&value, &dict); + + dbus_message_iter_close_container(iter, &value); +} + +void near_dbus_property_append_fixed_array(DBusMessageIter *iter, + const char *key, int type, void *val, int len) +{ + DBusMessageIter value, array; + const char *variant_sig, *array_sig; + + switch (type) { + case DBUS_TYPE_BYTE: + variant_sig = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING; + array_sig = DBUS_TYPE_BYTE_AS_STRING; + break; + default: + return; + } + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key); + + dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, + variant_sig, &value); + + dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY, + array_sig, &array); + dbus_message_iter_append_fixed_array(&array, type, val, len); + dbus_message_iter_close_container(&value, &array); + + dbus_message_iter_close_container(iter, &value); +} + +void near_dbus_property_append_array(DBusMessageIter *iter, + const char *key, int type, + near_dbus_append_cb_t function, void *user_data) +{ + DBusMessageIter value, array; + const char *variant_sig, *array_sig; + + switch (type) { + case DBUS_TYPE_STRING: + variant_sig = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING; + array_sig = DBUS_TYPE_STRING_AS_STRING; + break; + case DBUS_TYPE_OBJECT_PATH: + variant_sig = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING; + array_sig = DBUS_TYPE_OBJECT_PATH_AS_STRING; + break; + case DBUS_TYPE_BYTE: + variant_sig = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING; + array_sig = DBUS_TYPE_BYTE_AS_STRING; + break; + default: + return; + } + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key); + + dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, + variant_sig, &value); + + dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY, + array_sig, &array); + if (function) + function(&array, user_data); + dbus_message_iter_close_container(&value, &array); + + dbus_message_iter_close_container(iter, &value); +} + +static DBusConnection *connection = NULL; + +dbus_bool_t near_dbus_property_changed_basic(const char *path, + const char *interface, const char *key, + int type, void *val) +{ + DBusMessage *signal; + DBusMessageIter iter; + + if (path == NULL) + return FALSE; + + signal = dbus_message_new_signal(path, interface, "PropertyChanged"); + if (signal == NULL) + return FALSE; + + dbus_message_iter_init_append(signal, &iter); + near_dbus_property_append_basic(&iter, key, type, val); + + g_dbus_send_message(connection, signal); + + return TRUE; +} + +dbus_bool_t near_dbus_property_changed_dict(const char *path, + const char *interface, const char *key, + near_dbus_append_cb_t function, void *user_data) +{ + DBusMessage *signal; + DBusMessageIter iter; + + if (path == NULL) + return FALSE; + + signal = dbus_message_new_signal(path, interface, "PropertyChanged"); + if (signal == NULL) + return FALSE; + + dbus_message_iter_init_append(signal, &iter); + near_dbus_property_append_dict(&iter, key, function, user_data); + + g_dbus_send_message(connection, signal); + + return TRUE; +} + +dbus_bool_t near_dbus_property_changed_array(const char *path, + const char *interface, const char *key, int type, + near_dbus_append_cb_t function, void *user_data) +{ + DBusMessage *signal; + DBusMessageIter iter; + + if (path == NULL) + return FALSE; + + signal = dbus_message_new_signal(path, interface, "PropertyChanged"); + if (signal == NULL) + return FALSE; + + dbus_message_iter_init_append(signal, &iter); + near_dbus_property_append_array(&iter, key, type, + function, user_data); + + g_dbus_send_message(connection, signal); + + return TRUE; +} + +DBusConnection *near_dbus_get_connection(void) +{ + return connection; +} + +int __near_dbus_init(DBusConnection *conn) +{ + DBG(""); + + connection = conn; + + return 0; +} + +void __near_dbus_cleanup(void) +{ + DBG(""); + + connection = NULL; +} diff --git a/src/device.c b/src/device.c new file mode 100644 index 0000000..de9cbe8 --- /dev/null +++ b/src/device.c @@ -0,0 +1,503 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> + +#include <glib.h> + +#include <gdbus.h> + +#include "near.h" + +struct near_device { + char *path; + + uint32_t adapter_idx; + uint32_t target_idx; + + uint8_t nfcid[NFC_MAX_NFCID1_LEN]; + uint8_t nfcid_len; + + size_t data_length; + uint8_t *data; + + uint32_t n_records; + GList *records; + + DBusMessage *push_msg; /* Push pending message */ +}; + +static DBusConnection *connection = NULL; + +static GHashTable *device_hash; + +static GSList *driver_list = NULL; + +static void free_device(gpointer data) +{ + struct near_device *device = data; + GList *list; + + DBG("device %p", device); + + for (list = device->records; list; list = list->next) { + struct near_ndef_record *record = list->data; + + __near_ndef_record_free(record); + } + + g_list_free(device->records); + g_free(device->path); + g_free(device->data); + g_free(device); +} + +struct near_device *near_device_get_device(uint32_t adapter_idx, + uint32_t target_idx) +{ + struct near_device *device; + char *path; + + DBG(""); + + path = g_strdup_printf("%s/nfc%d/device%d", NFC_PATH, + adapter_idx, target_idx); + if (path == NULL) + return NULL; + + device = g_hash_table_lookup(device_hash, path); + g_free(path); + + /* TODO refcount */ + return device; +} + +void __near_device_remove(struct near_device *device) +{ + char *path = device->path; + + DBG("path %s", device->path); + + if (g_hash_table_lookup(device_hash, device->path) == NULL) + return; + + g_dbus_unregister_interface(connection, device->path, + NFC_DEVICE_INTERFACE); + + g_hash_table_remove(device_hash, path); +} + +const char *__near_device_get_path(struct near_device *device) +{ + return device->path; +} + +uint32_t __neard_device_get_idx(struct near_device *device) +{ + return device->target_idx; +} + +static void append_records(DBusMessageIter *iter, void *user_data) +{ + struct near_device *device = user_data; + GList *list; + + DBG(""); + + for (list = device->records; list; list = list->next) { + struct near_ndef_record *record = list->data; + char *path; + + path = __near_ndef_record_get_path(record); + if (path == NULL) + continue; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, + &path); + } +} + +static DBusMessage *get_properties(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct near_device *device = data; + DBusMessage *reply; + DBusMessageIter array, dict; + + DBG("conn %p", conn); + + reply = dbus_message_new_method_return(msg); + if (reply == NULL) + return NULL; + + dbus_message_iter_init_append(reply, &array); + + near_dbus_dict_open(&array, &dict); + + near_dbus_dict_append_array(&dict, "Records", + DBUS_TYPE_OBJECT_PATH, append_records, device); + + near_dbus_dict_close(&array, &dict); + + return reply; +} + +static DBusMessage *set_property(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + DBG("conn %p", conn); + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + +static void push_cb(uint32_t adapter_idx, uint32_t target_idx, int status) +{ + struct near_device *device; + DBusConnection *conn; + DBusMessage *reply; + + DBG("Push status %d", status); + + conn = near_dbus_get_connection(); + device = near_device_get_device(adapter_idx, target_idx); + + if (conn == NULL || device == NULL) + return; + + if (status != 0) { + reply = __near_error_failed(device->push_msg, EINVAL); + if (reply != NULL) + g_dbus_send_message(conn, reply); + } else { + g_dbus_send_reply(conn, device->push_msg, DBUS_TYPE_INVALID); + } + + dbus_message_unref(device->push_msg); + device->push_msg = NULL; +} + +static char *sn_from_message(DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessageIter arr_iter; + + DBG(""); + + dbus_message_iter_init(msg, &iter); + dbus_message_iter_recurse(&iter, &arr_iter); + + while (dbus_message_iter_get_arg_type(&arr_iter) != + DBUS_TYPE_INVALID) { + const char *key, *value; + DBusMessageIter ent_iter; + DBusMessageIter var_iter; + + dbus_message_iter_recurse(&arr_iter, &ent_iter); + dbus_message_iter_get_basic(&ent_iter, &key); + + if (g_strcmp0(key, "Type") != 0) { + dbus_message_iter_next(&arr_iter); + continue; + } + + dbus_message_iter_next(&ent_iter); + dbus_message_iter_recurse(&ent_iter, &var_iter); + + switch (dbus_message_iter_get_arg_type(&var_iter)) { + case DBUS_TYPE_STRING: + dbus_message_iter_get_basic(&var_iter, &value); + + if (g_strcmp0(value, "Text") == 0) + return NEAR_DEVICE_SN_SNEP; + else if (g_strcmp0(value, "URI") == 0) + return NEAR_DEVICE_SN_SNEP; + else if (g_strcmp0(value, "SmartPoster") == 0) + return NEAR_DEVICE_SN_SNEP; + else if (g_strcmp0(value, "Handover") == 0) + return NEAR_DEVICE_SN_HANDOVER; + else + return NULL; + + break; + } + + dbus_message_iter_next(&arr_iter); + } + + return NULL; +} + +static DBusMessage *push_ndef(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct near_device *device = data; + struct near_ndef_message *ndef; + char *service_name; + int err; + + DBG("conn %p", conn); + + if (device->push_msg) + return __near_error_in_progress(msg); + + device->push_msg = dbus_message_ref(msg); + + service_name = sn_from_message(msg); + if (service_name == NULL) { + err = -EINVAL; + goto error; + } + + ndef = __ndef_build_from_message(msg); + if (ndef == NULL) { + err = -EINVAL; + goto error; + } + + err = __near_device_push(device, ndef, service_name, push_cb); + if (err < 0) + goto error; + + g_free(ndef); + g_free(ndef->data); + + return NULL; + +error: + dbus_message_unref(device->push_msg); + device->push_msg = NULL; + + return __near_error_failed(msg, -err); +} + +static const GDBusMethodTable device_methods[] = { + { GDBUS_METHOD("GetProperties", + NULL, GDBUS_ARGS({"properties", "a{sv}"}), + get_properties) }, + { GDBUS_METHOD("SetProperty", + GDBUS_ARGS({"name", "s"}, {"value", "v"}), + NULL, set_property) }, + { GDBUS_ASYNC_METHOD("Push", GDBUS_ARGS({"attributes", "a{sv}"}), + NULL, push_ndef) }, + { }, +}; + +static const GDBusSignalTable device_signals[] = { + { GDBUS_SIGNAL("PropertyChanged", + GDBUS_ARGS({"name", "s"}, {"value", "v"})) }, + { } +}; + +int near_device_add_data(uint32_t adapter_idx, uint32_t target_idx, + uint8_t *data, size_t data_length) +{ + struct near_device *device; + + device = near_device_get_device(adapter_idx, target_idx); + if (device == NULL) + return -ENODEV; + + device->data_length = data_length; + device->data = g_try_malloc0(data_length); + if (device->data == NULL) + return -ENOMEM; + + if (data != NULL) + memcpy(device->data, data, data_length); + + return 0; +} + +int near_device_add_records(struct near_device *device, GList *records, + near_device_io_cb cb, int status) +{ + GList *list; + struct near_ndef_record *record; + char *path; + + DBG("records %p", records); + + for (list = records; list; list = list->next) { + record = list->data; + + path = g_strdup_printf("%s/nfc%d/device%d/record%d", + NFC_PATH, device->adapter_idx, + device->target_idx, device->n_records); + + if (path == NULL) + continue; + + __near_ndef_record_register(record, path); + + device->n_records++; + device->records = g_list_append(device->records, record); + } + + __near_agent_ndef_parse_records(device->records); + + if (cb != NULL) + cb(device->adapter_idx, device->target_idx, status); + + g_list_free(records); + + return 0; +} + +struct near_device *__near_device_add(uint32_t adapter_idx, uint32_t target_idx, + uint8_t *nfcid, uint8_t nfcid_len) +{ + struct near_device *device; + char *path; + + device = near_device_get_device(adapter_idx, target_idx); + if (device != NULL) + return NULL; + + device = g_try_malloc0(sizeof(struct near_device)); + if (device == NULL) + return NULL; + + device->path = g_strdup_printf("%s/nfc%d/device%d", NFC_PATH, + adapter_idx, target_idx); + if (device->path == NULL) { + g_free(device); + return NULL; + } + device->adapter_idx = adapter_idx; + device->target_idx = target_idx; + device->n_records = 0; + + if (nfcid_len <= NFC_MAX_NFCID1_LEN && nfcid_len > 0) { + device->nfcid_len = nfcid_len; + memcpy(device->nfcid, nfcid, nfcid_len); + } + + path = g_strdup(device->path); + if (path == NULL) { + g_free(device); + return NULL; + } + + g_hash_table_insert(device_hash, path, device); + + DBG("connection %p", connection); + + g_dbus_register_interface(connection, device->path, + NFC_DEVICE_INTERFACE, + device_methods, device_signals, + NULL, device, NULL); + + return device; +} + +int __near_device_listen(struct near_device *device, near_device_io_cb cb) +{ + GSList *list; + + DBG(""); + + for (list = driver_list; list; list = list->next) { + struct near_device_driver *driver = list->data; + + return driver->listen(device->adapter_idx, cb); + } + + return 0; +} + +int __near_device_push(struct near_device *device, + struct near_ndef_message *ndef, char *service_name, + near_device_io_cb cb) +{ + GSList *list; + + DBG(""); + + if (__near_adapter_get_dep_state(device->adapter_idx) == FALSE) { + near_error("DEP link is not established"); + return -ENOLINK; + } + + for (list = driver_list; list; list = list->next) { + struct near_device_driver *driver = list->data; + + return driver->push(device->adapter_idx, device->target_idx, + ndef, service_name, cb); + } + + return 0; +} + +static gint cmp_prio(gconstpointer a, gconstpointer b) +{ + const struct near_tag_driver *driver1 = a; + const struct near_tag_driver *driver2 = b; + + return driver2->priority - driver1->priority; +} + +int near_device_driver_register(struct near_device_driver *driver) +{ + DBG(""); + + if (driver->listen == NULL) + return -EINVAL; + + driver_list = g_slist_insert_sorted(driver_list, driver, cmp_prio); + + __near_adapter_listen(driver); + + return 0; +} + +void near_device_driver_unregister(struct near_device_driver *driver) +{ + DBG(""); + + driver_list = g_slist_remove(driver_list, driver); +} + +int __near_device_init(void) +{ + DBG(""); + + connection = near_dbus_get_connection(); + + device_hash = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, free_device); + + return 0; +} + +void __near_device_cleanup(void) +{ + DBG(""); + + g_hash_table_destroy(device_hash); + device_hash = NULL; +} diff --git a/src/error.c b/src/error.c new file mode 100644 index 0000000..4012b5b --- /dev/null +++ b/src/error.c @@ -0,0 +1,191 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <string.h> + +#include <gdbus.h> + +#include "near.h" + +DBusMessage *__near_error_failed(DBusMessage *msg, int errnum) +{ + const char *str = strerror(errnum); + + switch (errnum) { + case ESRCH: + return __near_error_not_registered(msg); + case ENXIO: + return __near_error_not_found(msg); + case EACCES: + return __near_error_permission_denied(msg); + case EEXIST: + return __near_error_already_exists(msg); + case EINVAL: + return __near_error_invalid_arguments(msg); + case ENOSYS: + return __near_error_not_implemented(msg); + case ENOLINK: + return __near_error_no_carrier(msg); + case ENOTUNIQ: + return __near_error_not_unique(msg); + case EOPNOTSUPP: + return __near_error_not_supported(msg); + case ECONNABORTED: + return __near_error_operation_aborted(msg); + case EISCONN: + return __near_error_already_connected(msg); + case ENOTCONN: + return __near_error_not_connected(msg); + case ETIMEDOUT: + return __near_error_operation_timeout(msg); + case EALREADY: + return __near_error_in_progress(msg); + case ENOKEY: + return __near_error_passphrase_required(msg); + } + + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".Failed", "%s", str); +} + +DBusMessage *__near_error_invalid_arguments(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".InvalidArguments", "Invalid arguments"); +} + +DBusMessage *__near_error_permission_denied(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".PermissionDenied", "Permission denied"); +} + +DBusMessage *__near_error_passphrase_required(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".PassphraseRequired", "Passphrase required"); +} + +DBusMessage *__near_error_not_registered(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".NotRegistered", "Not registered"); +} + +DBusMessage *__near_error_not_unique(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".NotUnique", "Not unique"); +} + +DBusMessage *__near_error_not_supported(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".NotSupported", "Not supported"); +} + +DBusMessage *__near_error_not_implemented(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".NotImplemented", "Not implemented"); +} + +DBusMessage *__near_error_not_found(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".NotFound", "Not found"); +} + +DBusMessage *__near_error_not_polling(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".Failed", "Not polling"); +} + +DBusMessage *__near_error_no_carrier(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".NoCarrier", "No carrier"); +} + +DBusMessage *__near_error_in_progress(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".InProgress", "In progress"); +} + +DBusMessage *__near_error_already_exists(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".AlreadyExists", "Already exists"); +} + +DBusMessage *__near_error_already_enabled(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".AlreadyEnabled", "Already enabled"); +} + +DBusMessage *__near_error_already_disabled(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".AlreadyDisabled", "Already disabled"); +} + +DBusMessage *__near_error_already_connected(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".AlreadyConnected", "Already connected"); +} + +DBusMessage *__near_error_not_connected(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".NotConnected", "Not connected"); +} +DBusMessage *__near_error_operation_aborted(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".OperationAborted", "Operation aborted"); +} + +DBusMessage *__near_error_operation_timeout(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".OperationTimeout", "Operation timeout"); +} + +DBusMessage *__near_error_invalid_service(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".InvalidService", "Invalid service"); +} + +DBusMessage *__near_error_invalid_property(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".InvalidProperty", "Invalid property"); +} diff --git a/src/genbuiltin b/src/genbuiltin new file mode 100755 index 0000000..9c2ba15 --- /dev/null +++ b/src/genbuiltin @@ -0,0 +1,17 @@ +#!/bin/sh + +for i in $* +do + echo "extern struct near_plugin_desc __near_builtin_$i;" +done + +echo +echo "static struct near_plugin_desc *__near_builtin[] = {" + +for i in $* +do + echo " &__near_builtin_$i," +done + +echo " NULL" +echo "};" diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..99fb984 --- /dev/null +++ b/src/log.c @@ -0,0 +1,138 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdarg.h> +#include <syslog.h> + +#include "near.h" + +void near_info(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + + vsyslog(LOG_INFO, format, ap); + + va_end(ap); +} + +void near_warn(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + + vsyslog(LOG_WARNING, format, ap); + + va_end(ap); +} + +void near_error(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + + vsyslog(LOG_ERR, format, ap); + + va_end(ap); +} + +void near_debug(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + + vsyslog(LOG_DEBUG, format, ap); + + va_end(ap); +} + +extern struct near_debug_desc __start___debug[]; +extern struct near_debug_desc __stop___debug[]; + +static gchar **enabled = NULL; + +static gboolean is_enabled(struct near_debug_desc *desc) +{ + int i; + + if (enabled == NULL) + return FALSE; + + for (i = 0; enabled[i] != NULL; i++) { + if (desc->name != NULL && g_pattern_match_simple(enabled[i], + desc->name) == TRUE) + return TRUE; + if (desc->file != NULL && g_pattern_match_simple(enabled[i], + desc->file) == TRUE) + return TRUE; + } + + return FALSE; +} + +int __near_log_init(const char *debug, gboolean detach) +{ + int option = LOG_NDELAY | LOG_PID; + struct near_debug_desc *desc; + const char *name = NULL, *file = NULL; + + if (debug != NULL) + enabled = g_strsplit_set(debug, ":, ", 0); + + for (desc = __start___debug; desc < __stop___debug; desc++) { + if (file != NULL || name != NULL) { + if (g_strcmp0(desc->file, file) == 0) { + if (desc->name == NULL) + desc->name = name; + } else + file = NULL; + } + + if (is_enabled(desc) == TRUE) + desc->flags |= NEAR_DEBUG_FLAG_PRINT; + } + + if (detach == FALSE) + option |= LOG_PERROR; + + openlog("neard", option, LOG_DAEMON); + + syslog(LOG_INFO, "NEAR daemon version %s", VERSION); + + return 0; +} + +void __near_log_cleanup(void) +{ + syslog(LOG_INFO, "Exit"); + + closelog(); + + g_strfreev(enabled); +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..dea9995 --- /dev/null +++ b/src/main.c @@ -0,0 +1,245 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> + +#include <gdbus.h> + +#include "near.h" + +static struct { + near_bool_t constant_poll; +} near_settings = { + .constant_poll = FALSE, +}; + +static GKeyFile *load_config(const char *file) +{ + GError *err = NULL; + GKeyFile *keyfile; + + keyfile = g_key_file_new(); + + g_key_file_set_list_separator(keyfile, ','); + + if (!g_key_file_load_from_file(keyfile, file, 0, &err)) { + if (err->code != G_FILE_ERROR_NOENT) { + near_error("Parsing %s failed: %s", file, + err->message); + } + + g_error_free(err); + g_key_file_free(keyfile); + return NULL; + } + + return keyfile; +} + +static void parse_config(GKeyFile *config) +{ + GError *error = NULL; + gboolean boolean; + + if (config == NULL) + return; + + DBG("parsing main.conf"); + + boolean = g_key_file_get_boolean(config, "General", + "ConstantPoll", &error); + if (error == NULL) + near_settings.constant_poll = boolean; + + g_clear_error(&error); +} + +static GMainLoop *main_loop = NULL; + +static volatile sig_atomic_t __terminated = 0; + +static void sig_term(int sig) +{ + if (__terminated > 0) + return; + + __terminated = 1; + + near_info("Terminating"); + + g_main_loop_quit(main_loop); +} + +static void disconnect_callback(DBusConnection *conn, void *user_data) +{ + near_error("D-Bus disconnect"); + + g_main_loop_quit(main_loop); +} + +static gchar *option_debug = NULL; +static gchar *option_plugin = NULL; +static gchar *option_noplugin = NULL; +static gboolean option_detach = TRUE; +static gboolean option_version = FALSE; + +static gboolean parse_debug(const char *key, const char *value, + gpointer user_data, GError **error) +{ + if (value) + option_debug = g_strdup(value); + else + option_debug = g_strdup("*"); + + return TRUE; +} + +static GOptionEntry options[] = { + { "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG, + G_OPTION_ARG_CALLBACK, parse_debug, + "Specify debug options to enable", "DEBUG" }, + { "nodaemon", 'n', G_OPTION_FLAG_REVERSE, + G_OPTION_ARG_NONE, &option_detach, + "Don't fork daemon to background" }, + { "plugin", 'p', 0, G_OPTION_ARG_STRING, &option_plugin, + "Specify plugins to load", "NAME,..." }, + { "noplugin", 'P', 0, G_OPTION_ARG_STRING, &option_noplugin, + "Specify plugins not to load", "NAME,..." }, + { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, + "Show version information and exit" }, + { NULL }, +}; + +near_bool_t near_setting_get_bool(const char *key) +{ + if (g_str_equal(key, "ConstantPoll") == TRUE) + return near_settings.constant_poll; + + return FALSE; +} + +int main(int argc, char *argv[]) +{ + GOptionContext *context; + GError *error = NULL; + DBusConnection *conn; + DBusError err; + GKeyFile *config; + struct sigaction sa; + + context = g_option_context_new(NULL); + g_option_context_add_main_entries(context, options, NULL); + + if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) { + if (error != NULL) { + g_printerr("%s\n", error->message); + g_error_free(error); + } else + g_printerr("An unknown error occurred\n"); + exit(1); + } + + g_option_context_free(context); + + if (option_version == TRUE) { + printf("%s\n", VERSION); + exit(0); + } + + if (option_detach == TRUE) { + if (daemon(0, 0)) { + perror("Can't start daemon"); + exit(1); + } + } + + main_loop = g_main_loop_new(NULL, FALSE); + + dbus_error_init(&err); + + conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NFC_SERVICE, &err); + if (conn == NULL) { + if (dbus_error_is_set(&err) == TRUE) { + fprintf(stderr, "%s\n", err.message); + dbus_error_free(&err); + } else + fprintf(stderr, "Can't register with system bus\n"); + exit(1); + } + + g_dbus_set_disconnect_function(conn, disconnect_callback, NULL, NULL); + + __near_log_init(option_debug, option_detach); + __near_dbus_init(conn); + + config = load_config(CONFIGDIR "/main.conf"); + + parse_config(config); + + __near_netlink_init(); + __near_tag_init(); + __near_device_init(); + __near_adapter_init(); + __near_ndef_init(); + __near_manager_init(conn); + __near_bluetooth_init(); + __near_agent_init(); + + __near_plugin_init(option_plugin, option_noplugin); + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sig_term; + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + g_main_loop_run(main_loop); + + __near_plugin_cleanup(); + + __near_agent_cleanup(); + __near_bluetooth_cleanup(); + __near_manager_cleanup(); + __near_ndef_cleanup(); + __near_adapter_cleanup(); + __near_device_cleanup(); + __near_tag_cleanup(); + __near_netlink_cleanup(); + + __near_dbus_cleanup(); + __near_log_cleanup(); + + dbus_connection_unref(conn); + + g_main_loop_unref(main_loop); + + if (config) + g_key_file_free(config); + + return 0; +} diff --git a/src/main.conf b/src/main.conf new file mode 100644 index 0000000..65b6ac3 --- /dev/null +++ b/src/main.conf @@ -0,0 +1,7 @@ +[General] + +# Enable constant polling. Default value is false. +# Constant polling will automatically trigger a new +# polling loop whenever a tag or a device is no longer +# in the RF field. +ConstantPoll = true diff --git a/src/manager.c b/src/manager.c new file mode 100644 index 0000000..2002411 --- /dev/null +++ b/src/manager.c @@ -0,0 +1,294 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> + +#include <glib.h> + +#include <gdbus.h> + +#include "near.h" + +static DBusConnection *connection; + +static DBusMessage *get_properties(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + DBusMessage *reply; + DBusMessageIter array, dict; + + DBG("conn %p", conn); + + reply = dbus_message_new_method_return(msg); + if (reply == NULL) + return NULL; + + dbus_message_iter_init_append(reply, &array); + + near_dbus_dict_open(&array, &dict); + + near_dbus_dict_append_array(&dict, "Adapters", + DBUS_TYPE_OBJECT_PATH, __near_adapter_list, NULL); + + near_dbus_dict_close(&array, &dict); + + return reply; +} + +static DBusMessage *set_property(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + DBG("conn %p", conn); + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + +int __near_manager_adapter_add(uint32_t idx, const char *name, + uint32_t protocols, near_bool_t powered) +{ + struct near_adapter *adapter; + const char *path; + int err; + + DBG("idx %d", idx); + + adapter = __near_adapter_create(idx, name, protocols, powered); + if (adapter == NULL) + return -ENOMEM; + + path = __near_adapter_get_path(adapter); + if (path == NULL) { + __near_adapter_destroy(adapter); + return -EINVAL; + } + + err = __near_adapter_add(adapter); + if (err < 0) { + __near_adapter_destroy(adapter); + } else { + near_dbus_property_changed_array(NFC_MANAGER_PATH, + NFC_MANAGER_INTERFACE, "Adapters", + DBUS_TYPE_OBJECT_PATH, __near_adapter_list, + NULL); + + g_dbus_emit_signal(connection, "/", + NFC_MANAGER_INTERFACE, "AdapterAdded", + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID); + } + + return err; +} + +void __near_manager_adapter_remove(uint32_t idx) +{ + struct near_adapter *adapter; + const char *path; + + DBG("idx %d", idx); + + adapter = __near_adapter_get(idx); + if (adapter == NULL) + return; + + path = __near_adapter_get_path(adapter); + if (path == NULL) + return; + + + g_dbus_emit_signal(connection, "/", + NFC_MANAGER_INTERFACE, "AdapterRemoved", + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID); + + __near_adapter_remove(adapter); + + near_dbus_property_changed_array(NFC_MANAGER_PATH, + NFC_MANAGER_INTERFACE, "Adapters", + DBUS_TYPE_OBJECT_PATH, __near_adapter_list, + NULL); +} + +static DBusMessage *register_handover_agent(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + const char *sender, *path; + int err; + + DBG("conn %p", conn); + + sender = dbus_message_get_sender(msg); + + dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID); + + err = __near_agent_handover_register(sender, path); + if (err < 0) + return __near_error_failed(msg, -err); + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + +static DBusMessage *unregister_handover_agent(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + const char *sender, *path; + int err; + + DBG("conn %p", conn); + + sender = dbus_message_get_sender(msg); + + dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID); + + err = __near_agent_handover_unregister(sender, path); + if (err < 0) + return __near_error_failed(msg, -err); + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + +static DBusMessage *register_ndef_agent(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + DBusMessageIter iter; + const char *sender, *path, *type; + int err; + + DBG("conn %p", conn); + + sender = dbus_message_get_sender(msg); + + if (dbus_message_iter_init(msg, &iter) == FALSE) + return __near_error_invalid_arguments(msg); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) + return __near_error_invalid_arguments(msg); + + dbus_message_iter_get_basic(&iter, &path); + dbus_message_iter_next(&iter); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + return __near_error_invalid_arguments(msg); + + dbus_message_iter_get_basic(&iter, &type); + + err = __near_agent_ndef_register(sender, path, type); + if (err < 0) + return __near_error_failed(msg, -err); + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + +static DBusMessage *unregister_ndef_agent(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + DBusMessageIter iter; + const char *sender, *path, *type; + int err; + + DBG("conn %p", conn); + + sender = dbus_message_get_sender(msg); + + if (dbus_message_iter_init(msg, &iter) == FALSE) + return __near_error_invalid_arguments(msg); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) + return __near_error_invalid_arguments(msg); + + dbus_message_iter_get_basic(&iter, &path); + dbus_message_iter_next(&iter); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + return __near_error_invalid_arguments(msg); + + dbus_message_iter_get_basic(&iter, &type); + + err = __near_agent_ndef_unregister(sender, path, type); + if (err < 0) + return __near_error_failed(msg, -err); + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + +static const GDBusMethodTable manager_methods[] = { + { GDBUS_METHOD("GetProperties", + NULL, GDBUS_ARGS({"properties", "a{sv}"}), + get_properties) }, + { GDBUS_METHOD("SetProperty", + GDBUS_ARGS({"name", "s"}, {"value", "v"}), + NULL, set_property) }, + { GDBUS_METHOD("RegisterHandoverAgent", + GDBUS_ARGS({ "path", "o" }), NULL, + register_handover_agent) }, + { GDBUS_METHOD("UnregisterHandoverAgent", + GDBUS_ARGS({ "path", "o" }), NULL, + unregister_handover_agent) }, + { GDBUS_METHOD("RegisterNDEFAgent", + GDBUS_ARGS({"path", "o"}, {"type", "s"}), + NULL, register_ndef_agent) }, + { GDBUS_METHOD("UnregisterNDEFAgent", + GDBUS_ARGS({"path", "o"}, {"type", "s"}), + NULL, unregister_ndef_agent) }, + { }, +}; + +static const GDBusSignalTable manager_signals[] = { + { GDBUS_SIGNAL("PropertyChanged", + GDBUS_ARGS({"name", "s"}, {"value", "v"})) }, + { GDBUS_SIGNAL("AdapterAdded", GDBUS_ARGS({"adapter", "o" })) }, + { GDBUS_SIGNAL("AdapterRemoved", GDBUS_ARGS({"adapter", "o" })) }, + { } +}; + +int __near_manager_init(DBusConnection *conn) +{ + DBG(""); + + connection = dbus_connection_ref(conn); + + DBG("connection %p", connection); + + g_dbus_register_interface(connection, NFC_MANAGER_PATH, + NFC_MANAGER_INTERFACE, + manager_methods, + manager_signals, NULL, NULL, NULL); + + return __near_netlink_get_adapters(); +} + +void __near_manager_cleanup(void) +{ + DBG(""); + + g_dbus_unregister_interface(connection, NFC_MANAGER_PATH, + NFC_MANAGER_INTERFACE); + + dbus_connection_unref(connection); +} diff --git a/src/ndef.c b/src/ndef.c new file mode 100644 index 0000000..f123d62 --- /dev/null +++ b/src/ndef.c @@ -0,0 +1,3049 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> + +#include <glib.h> + +#include <gdbus.h> + +#include "near.h" + +enum record_tnf { + RECORD_TNF_EMPTY = 0x00, + RECORD_TNF_WELLKNOWN = 0x01, + RECORD_TNF_MIME = 0x02, + RECORD_TNF_URI = 0x03, + RECORD_TNF_EXTERNAL = 0x04, + RECORD_TNF_UNKNOWN = 0x05, + RECORD_TNF_UNCHANGED = 0x06, +}; + +#define RECORD_ACTION_DO 0x00 +#define RECORD_ACTION_SAVE 0x01 +#define RECORD_ACTION_EDIT 0x02 + +#define RECORD_MB_BIT(val) ((val & 0x80) >> 7) +#define RECORD_ME_BIT(val) ((val & 0x40) >> 6) +#define RECORD_CF_BIT(val) ((val & 0x20) >> 5) +#define RECORD_SR_BIT(val) ((val & 0x10) >> 4) +#define RECORD_IL_BIT(val) ((val & 0x8) >> 3) +#define RECORD_TNF_BIT(val) (val & 0x7) + +#define NDEF_MSG_MIN_LENGTH 0x03 +#define NDEF_PAYLOAD_LENGTH_OFFSET 0x02 + +#define RECORD_MB 0x80 +#define RECORD_ME 0x40 +#define RECORD_CF 0x20 +#define RECORD_SR 0x10 +#define RECORD_IL 0x08 +#define RECORD_TNF_EMPTY_SET(val) ((val & ~0x7) | RECORD_TNF_EMPTY) +#define RECORD_TNF_WKT_SET(val) ((val & ~0x7) | RECORD_TNF_WELLKNOWN) +#define RECORD_TNF_MIME_SET(val) ((val & ~0x7) | RECORD_TNF_MIME) +#define RECORD_TNF_URI_SET(val) ((val & ~0x7) | RECORD_TNF_URI) +#define RECORD_TNF_EXTERNAL_SET(val) ((val & ~0x7) | RECORD_TNF_EXTERNAL) +#define RECORD_TNF_UKNOWN_SET(val) ((val & ~0x7) | RECORD_TNF_UNKNOWN) +#define RECORD_TNF_UNCHANGED_SET(val) ((val & ~0x7) | RECORD_TNF_UNCHANGED) + +#define NDEF_MSG_SHORT_RECORD_MAX_LENGTH 0xFF +#define NDEF_TEXT_RECORD_TYPE_NAME_HEX_VALUE 0x54 +#define NDEF_TEXT_RECORD_UTF16_STATUS 0x80 + +enum record_type { + RECORD_TYPE_WKT_SMART_POSTER = 0x01, + RECORD_TYPE_WKT_URI = 0x02, + RECORD_TYPE_WKT_TEXT = 0x03, + RECORD_TYPE_WKT_SIZE = 0x04, + RECORD_TYPE_WKT_TYPE = 0x05, + RECORD_TYPE_WKT_ACTION = 0x06, + RECORD_TYPE_WKT_HANDOVER_REQUEST = 0x07, + RECORD_TYPE_WKT_HANDOVER_SELECT = 0x08, + RECORD_TYPE_WKT_HANDOVER_CARRIER = 0x09, + RECORD_TYPE_WKT_ALTERNATIVE_CARRIER = 0x0a, + RECORD_TYPE_WKT_COLLISION_RESOLUTION = 0x0b, + RECORD_TYPE_WKT_ERROR = 0x0c, + RECORD_TYPE_MIME_TYPE = 0x0d, + RECORD_TYPE_UNKNOWN = 0xfe, + RECORD_TYPE_ERROR = 0xff +}; + +#define RECORD_TYPE_WKT "urn:nfc:wkt:" +#define RECORD_TYPE_EXTERNAL "urn:nfc:ext:" + +struct near_ndef_record_header { + uint8_t mb; + uint8_t me; + uint8_t cf; + uint8_t sr; + uint8_t il; + uint8_t tnf; + uint8_t il_length; + uint8_t *il_field; + uint32_t payload_len; + uint32_t offset; + uint8_t type_len; + enum record_type rec_type; + char *type_name; + uint32_t header_len; +}; + +struct near_ndef_text_payload { + char *encoding; + char *language_code; + char *data; +}; + +struct near_ndef_uri_payload { + uint8_t identifier; + + uint32_t field_length; + uint8_t *field; +}; + +struct near_ndef_sp_payload { + struct near_ndef_uri_payload *uri; + + uint8_t number_of_title_records; + struct near_ndef_text_payload **title_records; + + uint32_t size; /* from Size record*/ + char *type; /* from Type record*/ + char *action; + /* TODO add icon and other records fields*/ +}; + +struct near_ndef_mime_payload { + char *type; + + struct { + uint8_t carrier_type; + uint16_t properties; /* e.g.: NO_PAIRING_KEY */ + } handover; +}; + +enum carrier_power_state { + CPS_INACTIVE = 0x00, + CPS_ACTIVE = 0x01, + CPS_ACTIVATING = 0x02, + CPS_UNKNOWN = 0x03, +}; + +/* Handover record definitions */ + +/* alternative record (AC) */ +#define AC_RECORD_PAYLOAD_LEN 4 + +struct near_ndef_ac_payload { + enum carrier_power_state cps; /* carrier power state */ + + uint8_t cdr_len; /* carrier data reference length: 0x01 */ + uint8_t cdr; /* carrier data reference */ + uint8_t adata_refcount; /* auxiliary data reference count */ + + /* !: if adata_refcount == 0, then there's no data reference */ + uint16_t **adata; /* auxiliary data reference */ +}; + +/* + * carrier data (see cdr in near_ndef_ac_payload ) + * These settings can be retrieved from mime, carrier records, etc... + */ +struct near_ndef_carrier_data { + uint8_t cdr; /* carrier data reference */ + uint8_t *data; + size_t data_len; +}; + +/* Default Handover version */ +#define HANDOVER_VERSION 0x12 +#define HANDOVER_MAJOR(version) (((version) >> 4) & 0xf) +#define HANDOVER_MINOR(version) ((version) & 0xf) + + +/* General Handover Request/Select record */ +struct near_ndef_ho_payload { + uint8_t version; /* version id */ + uint16_t collision_record; /* collision record */ + + uint8_t number_of_ac_payloads; /* At least 1 ac is needed */ + struct near_ndef_ac_payload **ac_payloads; + + /* Optional records */ + uint16_t *err_record; /* not NULL if present */ + + uint8_t number_of_cfg_payloads; /* extra NDEF records */ + struct near_ndef_mime_payload **cfg_payloads; +}; + +struct near_ndef_record { + char *path; + + struct near_ndef_record_header *header; + + /* specific payloads */ + struct near_ndef_text_payload *text; + struct near_ndef_uri_payload *uri; + struct near_ndef_sp_payload *sp; + struct near_ndef_mime_payload *mime; + struct near_ndef_ho_payload *ho; /* handover payload */ + + char *type; + + uint8_t *data; + size_t data_len; +}; + +static DBusConnection *connection = NULL; + +static inline void fillb8(uint8_t *ptr, uint32_t len) +{ + (*(uint8_t *)(ptr)) = ((uint8_t)(len)); +} + +static inline void fillb16(uint8_t *ptr, uint32_t len) +{ + fillb8((ptr), (uint16_t)(len) >> 8); + fillb8((uint8_t *)(ptr) + 1, len); +} + +static inline void fillb32(uint8_t *ptr, uint32_t len) +{ + fillb16((ptr), (uint32_t)(len) >> 16); + fillb16((uint8_t *)(ptr) + 2, (uint32_t)(len)); +} + +char *__near_ndef_record_get_path(struct near_ndef_record *record) +{ + return record->path; +} + +char *__near_ndef_record_get_type(struct near_ndef_record *record) +{ + return record->type; +} + +uint8_t *__near_ndef_record_get_data(struct near_ndef_record *record, + size_t *len) +{ + *len = record->data_len; + + return record->data; +} + +void __near_ndef_append_records(DBusMessageIter *iter, GList *records) +{ + GList *list; + + DBG(""); + + for (list = records; list; list = list->next) { + struct near_ndef_record *record = list->data; + uint8_t *data; + size_t data_len; + + data = __near_ndef_record_get_data(record, &data_len); + if (data == NULL) + continue; + + dbus_message_iter_append_fixed_array(iter, DBUS_TYPE_BYTE, + &data, data_len); + } +} + +static void append_text_payload(struct near_ndef_text_payload *text, + DBusMessageIter *dict) +{ + DBG(""); + + if (text == NULL || dict == NULL) + return; + + if (text->encoding != NULL) + near_dbus_dict_append_basic(dict, "Encoding", + DBUS_TYPE_STRING, + &(text->encoding)); + + if (text->language_code != NULL) + near_dbus_dict_append_basic(dict, "Language", + DBUS_TYPE_STRING, + &(text->language_code)); + + if (text->data != NULL) + near_dbus_dict_append_basic(dict, "Representation", + DBUS_TYPE_STRING, + &(text->data)); +} + +static const char *uri_prefixes[NFC_MAX_URI_ID + 1] = { + "", + "http://www.", + "https://www.", + "http://", + "https://", + "tel:", + "mailto:", + "ftp://anonymous:anonymous@", + "ftp://ftp.", + "ftps://", + "sftp://", + "smb://", + "nfs://", + "ftp://", + "dav://", + "news:", + "telnet://", + "imap:", + "rstp://", + "urn:", + "pop:", + "sip:", + "sips:", + "tftp:", + "btspp://", + "btl2cap://", + "btgoep://", + "tcpobex://", + "irdaobex://", + "file://", + "urn:epc:id:", + "urn:epc:tag:", + "urn:epc:pat:", + "urn:epc:raw:", + "urn:epc:", + "urn:nfc:", +}; + +const char *__near_ndef_get_uri_prefix(uint8_t id) +{ + if (id > NFC_MAX_URI_ID) + return NULL; + + return uri_prefixes[id]; +} + +static void append_uri_payload(struct near_ndef_uri_payload *uri, + DBusMessageIter *dict) +{ + char *value; + const char *prefix = NULL; + + DBG(""); + + if (uri == NULL || dict == NULL) + return; + + if (uri->identifier > NFC_MAX_URI_ID) { + near_error("Invalid URI identifier 0x%x", uri->identifier); + return; + } + + prefix = uri_prefixes[uri->identifier]; + + DBG("URI prefix %s", prefix); + + value = g_strdup_printf("%s%.*s", prefix, uri->field_length, + uri->field); + + near_dbus_dict_append_basic(dict, "URI", DBUS_TYPE_STRING, &value); + + g_free(value); +} + +static void append_sp_payload(struct near_ndef_sp_payload *sp, + DBusMessageIter *dict) +{ + uint8_t i; + + DBG(""); + + if (sp == NULL || dict == NULL) + return; + + if (sp->action != NULL) + near_dbus_dict_append_basic(dict, "Action", DBUS_TYPE_STRING, + &(sp->action)); + + if (sp->uri != NULL) + append_uri_payload(sp->uri, dict); + + if (sp->title_records != NULL && + sp->number_of_title_records > 0) { + for (i = 0; i < sp->number_of_title_records; i++) + append_text_payload(sp->title_records[i], dict); + } + + if (sp->type != NULL) + near_dbus_dict_append_basic(dict, "MIMEType", DBUS_TYPE_STRING, + &(sp->type)); + + if (sp->size > 0) + near_dbus_dict_append_basic(dict, "Size", DBUS_TYPE_UINT32, + &(sp->size)); +} + +static void append_mime_payload(struct near_ndef_mime_payload *mime, + DBusMessageIter *dict) +{ + DBG(""); + + if (mime == NULL || dict == NULL) + return; + + if (mime->type != NULL) + near_dbus_dict_append_basic(dict, "MIME", + DBUS_TYPE_STRING, + &(mime->type)); +} + +static void append_record(struct near_ndef_record *record, + DBusMessageIter *dict) +{ + char *type; + + DBG(""); + + if (record == NULL || dict == NULL) + return; + + switch (record->header->rec_type) { + case RECORD_TYPE_WKT_SIZE: + case RECORD_TYPE_WKT_TYPE: + case RECORD_TYPE_WKT_ACTION: + case RECORD_TYPE_WKT_ALTERNATIVE_CARRIER: + case RECORD_TYPE_WKT_COLLISION_RESOLUTION: + case RECORD_TYPE_WKT_ERROR: + case RECORD_TYPE_UNKNOWN: + case RECORD_TYPE_ERROR: + break; + + case RECORD_TYPE_WKT_TEXT: + type = "Text"; + near_dbus_dict_append_basic(dict, "Type", + DBUS_TYPE_STRING, &type); + append_text_payload(record->text, dict); + break; + + case RECORD_TYPE_WKT_URI: + type = "URI"; + near_dbus_dict_append_basic(dict, "Type", + DBUS_TYPE_STRING, &type); + append_uri_payload(record->uri, dict); + break; + + case RECORD_TYPE_WKT_SMART_POSTER: + type = "SmartPoster"; + near_dbus_dict_append_basic(dict, "Type", + DBUS_TYPE_STRING, &type); + append_sp_payload(record->sp, dict); + break; + + case RECORD_TYPE_WKT_HANDOVER_REQUEST: + type = "HandoverRequest"; + near_dbus_dict_append_basic(dict, "Type", + DBUS_TYPE_STRING, &type); + break; + + case RECORD_TYPE_WKT_HANDOVER_SELECT: + type = "HandoverSelect"; + near_dbus_dict_append_basic(dict, "Type", + DBUS_TYPE_STRING, &type); + break; + + case RECORD_TYPE_WKT_HANDOVER_CARRIER: + type = "HandoverCarrier"; + near_dbus_dict_append_basic(dict, "Type", + DBUS_TYPE_STRING, &type); + break; + + case RECORD_TYPE_MIME_TYPE: + type = "MIME Type (RFC 2046)"; + near_dbus_dict_append_basic(dict, "Type", + DBUS_TYPE_STRING, &type); + append_mime_payload(record->mime, dict); + break; + } +} + +static DBusMessage *get_properties(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct near_ndef_record *record = data; + DBusMessage *reply; + DBusMessageIter array, dict; + + DBG("conn %p", conn); + + if (conn == NULL || msg == NULL || + data == NULL) + return NULL; + + reply = dbus_message_new_method_return(msg); + if (reply == NULL) + return NULL; + + dbus_message_iter_init_append(reply, &array); + + near_dbus_dict_open(&array, &dict); + + append_record(record, &dict); + + near_dbus_dict_close(&array, &dict); + + return reply; +} + +static const GDBusMethodTable record_methods[] = { + { GDBUS_METHOD("GetProperties", + NULL, GDBUS_ARGS({"properties", "a{sv}"}), + get_properties) }, + { }, +}; + +static void free_text_payload(struct near_ndef_text_payload *text) +{ + if (text == NULL) + return; + + g_free(text->encoding); + g_free(text->language_code); + g_free(text->data); + g_free(text); + + text = NULL; +} + +static void free_uri_payload(struct near_ndef_uri_payload *uri) +{ + if (uri == NULL) + return; + + g_free(uri->field); + g_free(uri); + + uri = NULL; +} + +static void free_sp_payload(struct near_ndef_sp_payload *sp) +{ + uint8_t i; + + if (sp == NULL) + return; + + free_uri_payload(sp->uri); + + if (sp->title_records != NULL) { + for (i = 0; i < sp->number_of_title_records; i++) + free_text_payload(sp->title_records[i]); + } + + g_free(sp->title_records); + g_free(sp->type); + g_free(sp->action); + g_free(sp); + + sp = NULL; +} + +static void free_mime_payload(struct near_ndef_mime_payload *mime) +{ + if (mime == NULL) + return; + + g_free(mime->type); + g_free(mime); + + mime = NULL; +} + +static void free_ac_payload(struct near_ndef_ac_payload *ac) +{ + if (ac == NULL) + return; + + g_free(ac->adata); + g_free(ac); + ac = NULL; +} + +static void free_ho_payload(struct near_ndef_ho_payload *ho) +{ + int i; + + if (ho == NULL) + return; + + if (ho->ac_payloads != NULL) { + for (i = 0; i < ho->number_of_ac_payloads; i++) + free_ac_payload(ho->ac_payloads[i]); + } + + g_free(ho->ac_payloads); + g_free(ho); + + ho = NULL; +} + +static void free_ndef_record(struct near_ndef_record *record) +{ + if (record == NULL) + return; + + g_free(record->path); + + if (record->header != NULL) { + + switch (record->header->rec_type) { + case RECORD_TYPE_WKT_SIZE: + case RECORD_TYPE_WKT_TYPE: + case RECORD_TYPE_WKT_ACTION: + case RECORD_TYPE_WKT_HANDOVER_CARRIER: + case RECORD_TYPE_WKT_ALTERNATIVE_CARRIER: + case RECORD_TYPE_WKT_COLLISION_RESOLUTION: + case RECORD_TYPE_WKT_ERROR: + case RECORD_TYPE_UNKNOWN: + case RECORD_TYPE_ERROR: + break; + + case RECORD_TYPE_WKT_HANDOVER_REQUEST: + case RECORD_TYPE_WKT_HANDOVER_SELECT: + free_ho_payload(record->ho); + break; + + case RECORD_TYPE_WKT_TEXT: + free_text_payload(record->text); + break; + + case RECORD_TYPE_WKT_URI: + free_uri_payload(record->uri); + break; + + case RECORD_TYPE_WKT_SMART_POSTER: + free_sp_payload(record->sp); + break; + + case RECORD_TYPE_MIME_TYPE: + free_mime_payload(record->mime); + } + + g_free(record->header->il_field); + g_free(record->header->type_name); + } + + g_free(record->header); + g_free(record->type); + g_free(record->data); + g_free(record); + record = NULL; +} + +void __near_ndef_record_free(struct near_ndef_record *record) +{ + g_dbus_unregister_interface(connection, record->path, + NFC_RECORD_INTERFACE); + + free_ndef_record(record); +} + +static char *action_to_string(uint8_t action) +{ + switch (action) { + case RECORD_ACTION_DO: + return "Do"; + case RECORD_ACTION_SAVE: + return "Save"; + case RECORD_ACTION_EDIT: + return "Edit"; + default: + near_error("Unknown action 0x%x", action); + return NULL; + } +} + +/** + * @brief returns record type for external type + * Validate type and type length and returns + * type. + * + * @param type Type name in hex format + * @param type_lenth Type name length + * + * @return enum record type + */ + +static enum record_type get_external_record_type(uint8_t *type, + size_t type_length) +{ + DBG(""); + + if (strncmp((char *) type, BT_MIME_STRING_2_0, + strlen(BT_MIME_STRING_2_0)) == 0) + return RECORD_TYPE_MIME_TYPE; + else + return RECORD_TYPE_UNKNOWN; +} + +/** + * @brief returns record type + * Validate type name format, type and type length and returns + * type. + * + * @param tnf TypeNameFormat value + * @param type Type name in hex format + * @param type_lenth Type name length + * + * @return enum record type + */ + +static enum record_type get_record_type(enum record_tnf tnf, + uint8_t *type, size_t type_length) +{ + DBG(""); + + switch (tnf) { + case RECORD_TNF_EMPTY: + case RECORD_TNF_URI: + case RECORD_TNF_UNKNOWN: + case RECORD_TNF_UNCHANGED: + break; + + case RECORD_TNF_WELLKNOWN: + if (type_length == 1) { + if (type[0] == 'T') + return RECORD_TYPE_WKT_TEXT; + else if (type[0] == 'U') + return RECORD_TYPE_WKT_URI; + else if (type[0] == 's') + return RECORD_TYPE_WKT_SIZE; + else if (type[0] == 't') + return RECORD_TYPE_WKT_TYPE; + else + return RECORD_TYPE_UNKNOWN; + + } else if (type_length == 2) { + if (strncmp((char *)type, "Sp", 2) == 0) + return RECORD_TYPE_WKT_SMART_POSTER; + else if (strncmp((char *) type, "Hr", 2) == 0) + return RECORD_TYPE_WKT_HANDOVER_REQUEST; + else if (strncmp((char *) type, "Hs", 2) == 0) + return RECORD_TYPE_WKT_HANDOVER_SELECT; + else if (strncmp((char *) type, "Hc", 2) == 0) + return RECORD_TYPE_WKT_HANDOVER_CARRIER; + else if (strncmp((char *) type, "ac", 2) == 0) + return RECORD_TYPE_WKT_ALTERNATIVE_CARRIER; + else if (strncmp((char *) type, "cr", 2) == 0) + return RECORD_TYPE_WKT_COLLISION_RESOLUTION; + else + return RECORD_TYPE_UNKNOWN; + + } else if (type_length == 3) { + if (strncmp((char *)type, "act", 3) == 0) + return RECORD_TYPE_WKT_ACTION; + else if (strncmp((char *)type, "err", 3) == 0) + return RECORD_TYPE_WKT_ERROR; + else + return RECORD_TYPE_UNKNOWN; + + } + + case RECORD_TNF_MIME: + return RECORD_TYPE_MIME_TYPE; + + case RECORD_TNF_EXTERNAL: + return get_external_record_type(type, type_length); + + } + + return RECORD_TYPE_UNKNOWN; +} + +static int build_record_type_string(struct near_ndef_record *rec) +{ + uint8_t tnf; + + DBG(""); + + if (rec == NULL || rec->header == NULL) + return -EINVAL; + + tnf = rec->header->tnf; + + if (rec->header->rec_type == RECORD_TYPE_WKT_SMART_POSTER) { + rec->type = g_strdup_printf(RECORD_TYPE_WKT "U"); + return 0; + } + + switch (tnf) { + case RECORD_TNF_EMPTY: + case RECORD_TNF_UNKNOWN: + case RECORD_TNF_UNCHANGED: + return -EINVAL; + + case RECORD_TNF_URI: + case RECORD_TNF_MIME: + rec->type = g_strndup(rec->header->type_name, + rec->header->type_len); + break; + + case RECORD_TNF_WELLKNOWN: + rec->type = g_strdup_printf(RECORD_TYPE_WKT "%s", + rec->header->type_name); + break; + + case RECORD_TNF_EXTERNAL: + rec->type = g_strdup_printf(RECORD_TYPE_EXTERNAL "%s", + rec->header->type_name); + break; + } + + return 0; +} + +static uint8_t validate_record_begin_and_end_bits(uint8_t *msg_mb, + uint8_t *msg_me, uint8_t rec_mb, + uint8_t rec_me) +{ + DBG(""); + + if (msg_mb == NULL || msg_me == NULL) + return 0; + + /* Validating record header begin and end bits + * eg: Single record: [mb:1,me:1] + * Two records: [mb:1,me:0 - mb:0,me:1] + * Three or more records [mb:1,me:0 - mb:0,me:0 .. mb:0,me:1] + **/ + + if (rec_mb == 1) { + if (*msg_mb != 1) + *msg_mb = rec_mb; + else + return -EINVAL; + + } + + if (rec_me == 1) { + if (*msg_me != 1) { + if (*msg_mb == 1) + *msg_me = rec_me; + else + return -EINVAL; + + } else + return -EINVAL; + + } + + return 0; +} + +/** + * @brief Parse the ndef record header. + * + * Parse the ndef record header and cache the begin, end, chunkflag, + * short-record and type-name-format bits. ID length and field, record + * type, payload length and offset (where payload byte starts in input + * parameter). Validate offset for every step forward against total + * available length. + * + * @note : Caller responsibility to free the memory. + * + * @param[in] rec ndef byte stream + * @param[in] offset record header offset + * @param[in] length total length in byte stream + * + * @return struct near_ndef_record_header * RecordHeader on Success + * NULL on Failure + */ +static struct near_ndef_record_header *parse_record_header(uint8_t *rec, + uint32_t offset, uint32_t length) +{ + struct near_ndef_record_header *rec_header = NULL; + uint8_t *type = NULL; + uint32_t header_len = 0; + + DBG("length %d", length); + + if (rec == NULL || offset >= length) + return NULL; + + /* This check is for empty record. */ + if ((length - offset) < NDEF_MSG_MIN_LENGTH) + return NULL; + + rec_header = g_try_malloc0(sizeof(struct near_ndef_record_header)); + if (rec_header == NULL) + return NULL; + + rec_header->mb = RECORD_MB_BIT(rec[offset]); + rec_header->me = RECORD_ME_BIT(rec[offset]); + rec_header->sr = RECORD_SR_BIT(rec[offset]); + rec_header->il = RECORD_IL_BIT(rec[offset]); + rec_header->tnf = RECORD_TNF_BIT(rec[offset]); + + DBG("mb %d me %d sr %d il %d tnf %d", + rec_header->mb, rec_header->me, rec_header->sr, + rec_header->il, rec_header->tnf); + + offset++; + rec_header->type_len = rec[offset++]; + header_len = 2; /* type length + header bits */ + + if (rec_header->sr == 1) { + rec_header->payload_len = rec[offset++]; + header_len++; + } else { + rec_header->payload_len = + g_ntohl(*((uint32_t *)(rec + offset))); + offset += 4; + header_len += 4; + + if ((offset + rec_header->payload_len) > length) + goto fail; + } + + DBG("payload length %d", rec_header->payload_len); + + if (rec_header->il == 1) { + rec_header->il_length = rec[offset++]; + header_len++; + + if ((offset + rec_header->payload_len) > length) + goto fail; + } + + if (rec_header->type_len > 0) { + if ((offset + rec_header->type_len) > length) + goto fail; + + type = g_try_malloc0(rec_header->type_len); + if (type == NULL) + goto fail; + + memcpy(type, rec + offset, rec_header->type_len); + offset += rec_header->type_len; + header_len += rec_header->type_len; + + if ((offset + rec_header->payload_len) > length) + goto fail; + } + + if (rec_header->il_length > 0) { + if ((offset + rec_header->il_length) > length) + goto fail; + + rec_header->il_field = g_try_malloc0(rec_header->il_length); + if (rec_header->il_field == NULL) + goto fail; + + memcpy(rec_header->il_field, rec + offset, + rec_header->il_length); + offset += rec_header->il_length; + header_len += rec_header->il_length; + + if ((offset + rec_header->payload_len) > length) + goto fail; + } + + rec_header->rec_type = get_record_type(rec_header->tnf, type, + rec_header->type_len); + rec_header->offset = offset; + rec_header->header_len = header_len; + rec_header->type_name = g_strndup((char *) type, rec_header->type_len); + + g_free(type); + + return rec_header; + +fail: + near_error("parsing record header failed"); + + g_free(type); + g_free(rec_header->il_field); + g_free(rec_header->type_name); + g_free(rec_header); + + return NULL; +} + +/** + * @brief Parse the Text record payload + * + * Parse the Text payload. + * + * @param[in] payload NDEF pointer set to record payload first byte + * @param[in] length payload_len + * + * @return struct near_ndef_text_payload * Payload on Success + * NULL on Failure + */ + +static struct near_ndef_text_payload * +parse_text_payload(uint8_t *payload, uint32_t length) +{ + struct near_ndef_text_payload *text_payload = NULL; + uint8_t status, lang_length; + uint32_t offset; + + DBG(""); + + if (payload == NULL) + return NULL; + + offset = 0; + text_payload = g_try_malloc0(sizeof(struct near_ndef_text_payload)); + if (text_payload == NULL) + return NULL; + + /* 0x80 is used to get 7th bit value (0th bit is LSB) */ + status = ((payload[offset] & 0x80) >> 7); + + text_payload->encoding = (status == 0) ? + g_strdup("UTF-8") : g_strdup("UTF-16"); + + /* 0x3F is used to get 5th-0th bits value (0th bit is LSB) */ + lang_length = (payload[offset] & 0x3F); + offset++; + + if (lang_length > 0) { + if ((offset + lang_length) >= length) + goto fail; + + text_payload->language_code = g_strndup( + (char *)(payload + offset), + lang_length); + } else { + text_payload->language_code = NULL; + } + + offset += lang_length; + + if ((length - lang_length - 1) > 0) { + text_payload->data = g_strndup((char *)(payload + offset), + length - lang_length - 1); + } else { + text_payload->data = NULL; + } + + if (offset >= length) + goto fail; + + DBG("Encoding '%s'", text_payload->encoding); + DBG("Language Code '%s'", text_payload->language_code); + DBG("Data '%s'", text_payload->data); + + return text_payload; + +fail: + near_error("text payload parsing failed"); + free_text_payload(text_payload); + + return NULL; +} + +/** + * @brief Parse the URI record payload + * + * Parse the URI payload. + * + * @param[in] payload NDEF pointer set to record payload first byte + * @param[in] length Payload length + * + * @return struct near_ndef_uri_payload * payload on Success + * NULL on Failure + */ + +static struct near_ndef_uri_payload * +parse_uri_payload(uint8_t *payload, uint32_t length) +{ + struct near_ndef_uri_payload *uri_payload = NULL; + uint32_t index, offset; + + DBG(""); + + if (payload == NULL) + return NULL; + + offset = 0; + uri_payload = g_try_malloc0(sizeof(struct near_ndef_uri_payload)); + if (uri_payload == NULL) + return NULL; + + uri_payload->identifier = payload[offset]; + offset++; + + uri_payload->field_length = length - 1; + + if (uri_payload->field_length > 0) { + uri_payload->field = g_try_malloc0(uri_payload->field_length); + if (uri_payload->field == NULL) + goto fail; + + memcpy(uri_payload->field, payload + offset, + uri_payload->field_length); + + for (index = 0; index < uri_payload->field_length; index++) { + /* URI Record Type Definition 1.0 [3.2.3] + * Any character value within the URI between + * (and including) 0 and 31 SHALL be recorded as + * an error, and the URI record to be discarded */ + if (uri_payload->field[index] <= 31) + goto fail; + } + + } + + DBG("Identifier '0X%X'", uri_payload->identifier); + DBG("Field '%.*s'", uri_payload->field_length, uri_payload->field); + + return uri_payload; + +fail: + near_error("uri payload parsing failed"); + free_uri_payload(uri_payload); + + return NULL; +} + +/** + * @brief Validate titles records language code in Smartposter. + * There must not be two or more records with the same language identifier. + * + * @param[in] GSList * list of title records (struct near_ndef_text_payload *) + * + * @return Zero on success + * Negative value on failure + */ + +static int8_t validate_language_code_in_sp_record(GSList *titles) +{ + uint8_t i, j, length; + struct near_ndef_text_payload *title1, *title2; + + DBG(""); + + if (titles == NULL) + return -EINVAL; + + length = g_slist_length(titles); + + for (i = 0; i < length; i++) { + title1 = g_slist_nth_data(titles, i); + + for (j = i + 1; j < length; j++) { + title2 = g_slist_nth_data(titles, j); + + if ((title1->language_code == NULL) && + (title2->language_code == NULL)) + continue; + + if (g_strcmp0(title1->language_code, + title2->language_code) == 0) + return -EINVAL; + } + } + + return 0; +} + +/** + * @brief Parse the smart poster record payload. + * + * Parse the smart poster payload and cache the + * data in respective fields of smart poster structure. + * + * @note Caller responsibility to free the memory. + * + * @param[in] payload NDEF pointer set to record payload first byte + * @param[in] length Record payload length + * + * @return struct near_ndef_sp_payload * Record on Success + * NULL on Failure + */ + +static struct near_ndef_sp_payload * +parse_sp_payload(uint8_t *payload, uint32_t length) +{ + struct near_ndef_sp_payload *sp_payload = NULL; + struct near_ndef_record_header *rec_header = NULL; + uint8_t mb = 0, me = 0, i; + uint32_t offset; + GSList *titles = NULL, *temp; + + DBG(""); + + if (payload == NULL) + return NULL; + + offset = 0; + sp_payload = g_try_malloc0(sizeof(struct near_ndef_sp_payload)); + if (sp_payload == NULL) + return NULL; + + while (offset < length) { + + DBG("Record header : 0x%x", payload[offset]); + + rec_header = parse_record_header(payload, offset, length); + if (rec_header == NULL) + goto fail; + + if (validate_record_begin_and_end_bits(&mb, &me, + rec_header->mb, rec_header->me) != 0) { + DBG("validate mb me failed"); + goto fail; + } + + offset = rec_header->offset; + + switch (rec_header->rec_type) { + case RECORD_TYPE_WKT_SMART_POSTER: + case RECORD_TYPE_WKT_HANDOVER_REQUEST: + case RECORD_TYPE_WKT_HANDOVER_SELECT: + case RECORD_TYPE_WKT_HANDOVER_CARRIER: + case RECORD_TYPE_WKT_ALTERNATIVE_CARRIER: + case RECORD_TYPE_WKT_COLLISION_RESOLUTION: + case RECORD_TYPE_MIME_TYPE: + case RECORD_TYPE_WKT_ERROR: + case RECORD_TYPE_UNKNOWN: + case RECORD_TYPE_ERROR: + break; + + case RECORD_TYPE_WKT_URI: + /* URI record should be only one. */ + if (sp_payload->uri != NULL) + goto fail; + + sp_payload->uri = parse_uri_payload(payload + offset, + rec_header->payload_len); + if (sp_payload->uri == NULL) + goto fail; + + break; + + case RECORD_TYPE_WKT_TEXT: + /* + * Title records can zero or more. First fill the + * records in list and validate language identifier + * and then cache them into sp record structure. + */ + { + struct near_ndef_text_payload *title; + title = parse_text_payload(payload + offset, + rec_header->payload_len); + if (title == NULL) + goto fail; + + titles = g_slist_append(titles, title); + } + break; + + case RECORD_TYPE_WKT_SIZE: + /* + * If payload length is not exactly 4 bytes + * then record is wrong. + */ + if (rec_header->payload_len != 4) + goto fail; + + sp_payload->size = + g_ntohl(*((uint32_t *)(payload + offset))); + break; + + case RECORD_TYPE_WKT_TYPE: + + if (rec_header->payload_len > 0) { + sp_payload->type = g_try_malloc0( + rec_header->payload_len); + if (sp_payload->type == NULL) + goto fail; + + sp_payload->type = g_strndup( + (char *) payload + offset, + rec_header->payload_len); + } + + break; + + case RECORD_TYPE_WKT_ACTION: + /* + * If the action record exists, payload should be + * single byte, otherwise consider it as error. + */ + if (rec_header->payload_len != 1) + goto fail; + + sp_payload->action = + g_strdup(action_to_string(payload[offset])); + + break; + } + + offset += rec_header->payload_len; + g_free(rec_header->il_field); + g_free(rec_header->type_name); + g_free(rec_header); + rec_header = NULL; + } + + /* + * Code to fill smart poster record structure from + * 'titles' list. + */ + if (titles == NULL) + return sp_payload; + + if (validate_language_code_in_sp_record(titles) != 0) { + DBG("language code validation failed"); + goto fail; + } + + temp = titles; + sp_payload->number_of_title_records = g_slist_length(temp); + sp_payload->title_records = g_try_malloc0( + sp_payload->number_of_title_records * + sizeof(struct near_ndef_text_payload *)); + if (sp_payload->title_records == NULL) + goto fail; + + for (i = 0; i < sp_payload->number_of_title_records; i++) { + sp_payload->title_records[i] = temp->data; + temp = temp->next; + } + + g_slist_free(titles); + titles = NULL; + + return sp_payload; + +fail: + near_error("smart poster payload parsing failed"); + + if (rec_header != NULL) { + g_free(rec_header->type_name); + g_free(rec_header->il_field); + g_free(rec_header); + } + + free_sp_payload(sp_payload); + g_slist_free(titles); + + return NULL; +} + +static struct near_ndef_mime_payload * +parse_mime_type(struct near_ndef_record *record, + uint8_t *ndef_data, size_t ndef_length, size_t offset, + uint32_t payload_length, near_bool_t action) +{ + struct near_ndef_mime_payload *mime = NULL; + int err = 0; + + DBG(""); + + if ((ndef_data == NULL) || ((offset + payload_length) > ndef_length)) + return NULL; + + mime = g_try_malloc0(sizeof(struct near_ndef_mime_payload)); + if (mime == NULL) + return NULL; + + mime->type = g_strdup(record->header->type_name); + + DBG("MIME Type '%s' action: %d", mime->type, action); + if (strcmp(mime->type, BT_MIME_STRING_2_1) == 0) { + mime->handover.carrier_type = NEAR_CARRIER_BLUETOOTH; + err = __near_bluetooth_parse_oob_record(BT_MIME_V2_1, + &ndef_data[offset], &mime->handover.properties, + action); + } else if (strcmp(mime->type, BT_MIME_STRING_2_0) == 0) { + mime->handover.carrier_type = NEAR_CARRIER_BLUETOOTH; + err = __near_bluetooth_parse_oob_record(BT_MIME_V2_0, + &ndef_data[offset], &mime->handover.properties, + action); + } + + if (err < 0) { + DBG("Parsing mime error %d", err); + g_free(mime->type); + g_free(mime); + return NULL; + } + + return mime; +} + +/* Set the MB bit in message header */ +static uint8_t near_ndef_set_mb(uint8_t *hdr, near_bool_t first_rec) +{ + /* Reset bits 0x40 */ + *hdr &= (0xFF & (~RECORD_MB)); + + /* Set if needed */ + if (first_rec == TRUE) + *hdr |= RECORD_MB; + + return *hdr; +} + +/* Set the MB/ME bit in message header */ +static uint8_t near_ndef_set_me(uint8_t *hdr, near_bool_t last_rec) +{ + /* Reset bits 0x80 */ + *hdr &= (0xFF & (~RECORD_ME)); + + /* Set if needed */ + if (last_rec == TRUE) + *hdr |= RECORD_ME; + + return *hdr; +} + +/* Set the MB/ME bit in message header */ +static uint8_t near_ndef_set_mb_me(uint8_t *hdr, near_bool_t first_rec, + near_bool_t last_rec) +{ + near_ndef_set_mb(hdr, first_rec); + return near_ndef_set_me(hdr, last_rec); +} + +/** + * @brief Allocates ndef message structure + * + * Allocates ndef message structure and fill message header byte, + * type length byte, payload length and type name. Offset is payload + * first byte (caller of this API can start filling their payload + * from offset value). + * + * @note : caller responsibility to free the input and output + * parameters memory. + * + * @param[in] type_name Record type name + * @param[in] payload_len Record payload length + * @param[in] payload_id Record payload id string + * @param[in] payload_id_len Record payload id string length + * @param[in] tnf Type name format to set + * @param[in] first_rec Message begin (MB) flag + * @param[in] last_rec Message end (ME) flag + * + * @return struct near_ndef_message * - Success + * NULL - Failure + */ +static struct near_ndef_message *ndef_message_alloc_complete(char *type_name, + uint32_t payload_len, + char *payload_id, + uint8_t payload_id_len, + enum record_tnf tnf, + near_bool_t first_rec, + near_bool_t last_rec) +{ + struct near_ndef_message *msg; + uint8_t hdr = 0, type_len, sr_bit, il_bit, id_len; + + msg = g_try_malloc0(sizeof(struct near_ndef_message)); + if (msg == NULL) + return NULL; + + msg->length = 0; + msg->offset = 0; + msg->length++; /* record header*/ + msg->length++; /* type name length byte*/ + + type_len = (type_name != NULL) ? strlen(type_name) : 0; + id_len = (payload_id != NULL) ? payload_id_len : 0; + sr_bit = (payload_len <= NDEF_MSG_SHORT_RECORD_MAX_LENGTH) + ? TRUE : FALSE; + + il_bit = (payload_id != NULL) ? TRUE : FALSE; + + msg->length += (sr_bit == TRUE) ? 1 : 4; + msg->length += (il_bit == TRUE) ? 1 : 0; + msg->length += type_len; + msg->length += payload_len; + msg->length += id_len; + + msg->data = g_try_malloc0(msg->length); + if (msg->data == NULL) + goto fail; + + /* Set MB ME bits */ + hdr = near_ndef_set_mb_me(&hdr, first_rec, last_rec); + + if (sr_bit == TRUE) + hdr |= RECORD_SR; + + hdr = RECORD_TNF_WKT_SET(hdr); + if (il_bit == TRUE) + hdr |= RECORD_IL; + + switch (tnf) { + case RECORD_TNF_EMPTY: + hdr = RECORD_TNF_EMPTY_SET(hdr); + break; + + case RECORD_TNF_URI: + hdr = RECORD_TNF_URI_SET(hdr); + break; + + case RECORD_TNF_EXTERNAL: + hdr = RECORD_TNF_EXTERNAL_SET(hdr); + break; + case RECORD_TNF_UNKNOWN: + hdr = RECORD_TNF_UKNOWN_SET(hdr); + break; + + case RECORD_TNF_UNCHANGED: + hdr = RECORD_TNF_UNCHANGED_SET(hdr); + break; + + case RECORD_TNF_WELLKNOWN: + hdr = RECORD_TNF_WKT_SET(hdr); + break; + + case RECORD_TNF_MIME: + hdr = RECORD_TNF_MIME_SET(hdr); + break; + } + + msg->data[msg->offset++] = hdr; + msg->data[msg->offset++] = type_len; + + if (sr_bit == TRUE) { + msg->data[msg->offset++] = payload_len; + } else { + fillb32((msg->data + msg->offset), payload_len); + msg->offset += 4; + } + + if (il_bit == TRUE) + msg->data[msg->offset++] = payload_id_len; + + if (type_name != NULL) { + memcpy(msg->data + msg->offset, type_name, type_len); + msg->offset += type_len; + } + + if (il_bit == TRUE) { + memcpy(msg->data + msg->offset, payload_id, payload_id_len); + msg->offset += payload_id_len; + } + + return msg; + +fail: + near_error("ndef message struct allocation failed"); + g_free(msg->data); + g_free(msg); + + return NULL; +} + +/* + * @brief Allocates ndef message structure + * + * This is a wrapper to ndef_message_alloc, as, in most cases, + * there's no payload id, and MB=TRUE and ME=TRUE. Default type name format + * is also set to RECORD_TNF_WELLKNOWN + * + */ +static struct near_ndef_message *ndef_message_alloc(char* type_name, + uint32_t payload_len) +{ + return ndef_message_alloc_complete(type_name, payload_len, + NULL, 0, + RECORD_TNF_WELLKNOWN, + TRUE, TRUE); +} + +static struct near_ndef_ac_payload *parse_ac_payload(uint8_t *payload, + uint32_t length) +{ + struct near_ndef_ac_payload *ac_payload = NULL; + uint32_t offset; + + DBG(""); + + if (payload == NULL) + return NULL; + + offset = 0; + ac_payload = g_try_malloc0(sizeof(struct near_ndef_ac_payload)); + if (ac_payload == NULL) + goto fail; + + /* Carrier flag */ + ac_payload->cps = payload[offset]; /* TODO Check enum */ + offset++; + + /* Carrier data reference length */ + ac_payload->cdr_len = payload[offset]; + offset++; + + /* Carrier data reference */ + ac_payload->cdr = payload[offset]; + offset = offset + ac_payload->cdr_len; + + /* Auxiliary data reference count */ + ac_payload->adata_refcount = payload[offset]; + offset++; + + if (ac_payload->adata_refcount == 0) + return ac_payload; + + /* save the auxiliary data reference */ + ac_payload->adata = g_try_malloc0( + ac_payload->adata_refcount * sizeof(uint16_t)); + if (ac_payload->adata == NULL) + goto fail; + + memcpy(ac_payload->adata, payload + offset, + ac_payload->adata_refcount * sizeof(uint16_t)); + + /* and leave */ + return ac_payload; + +fail: + near_error("ac payload parsing failed"); + free_ac_payload(ac_payload); + + return NULL; +} + +/* carrier power state & carrier reference */ +static struct near_ndef_message *near_ndef_prepare_ac_message(uint8_t cps, + char cdr) +{ + struct near_ndef_message *ac_msg; + + /* alloc "ac" message minus adata*/ + ac_msg = ndef_message_alloc_complete("ac", AC_RECORD_PAYLOAD_LEN, + NULL, 0, + RECORD_TNF_WELLKNOWN, + TRUE, TRUE); + if (ac_msg == NULL) + return NULL; + + /* Prepare ac message */ + ac_msg->data[ac_msg->offset++] = cps; + ac_msg->data[ac_msg->offset++] = 1; /* cdr_len def size */ + ac_msg->data[ac_msg->offset++] = cdr; /* cdr */ + ac_msg->data[ac_msg->offset] = 0; /* adata ref count */ + + /* Check if we want an empty record */ + if (cdr == 0x00) + ac_msg->length = 0; + + return ac_msg; +} + +/* Collision Record message */ +static struct near_ndef_message *near_ndef_prepare_cr_message(uint16_t cr_id) +{ + struct near_ndef_message *cr_msg; + + cr_msg = ndef_message_alloc_complete("cr", sizeof(uint16_t), + NULL, 0, + RECORD_TNF_WELLKNOWN, + TRUE, TRUE); + if (cr_msg == NULL) + return NULL; + + /* Prepare ac message */ + *(uint16_t *)(cr_msg->data + cr_msg->offset) = g_htons(cr_id); + + return cr_msg; +} + +/* Prepare the bluetooth data record */ +static struct near_ndef_message *near_ndef_prepare_bt_message(uint8_t *bt_data, + int bt_data_len, char cdr, uint8_t cdr_len) +{ + struct near_ndef_message *bt_msg = NULL; + + if (bt_data == NULL) + goto fail; + + bt_msg = ndef_message_alloc_complete(BT_MIME_STRING_2_1, bt_data_len, + &cdr, cdr_len, RECORD_TNF_MIME, + TRUE, TRUE); + if (bt_msg == NULL) + goto fail; + + /* store data */ + memcpy(bt_msg->data + bt_msg->offset, bt_data, bt_data_len); + + return bt_msg; + +fail: + if (bt_msg != NULL) + g_free(bt_msg->data); + + g_free(bt_msg); + + return NULL; +} + +/* + * Walk thru the cfgs list and set the carriers bitfield + */ +static uint8_t near_get_carriers_list(struct near_ndef_record *record) +{ + struct near_ndef_ho_payload *ho = record->ho; + uint8_t carriers; + int i; + + carriers = 0; + + for (i = 0; i < ho->number_of_cfg_payloads; i++) { + struct near_ndef_mime_payload *rec = ho->cfg_payloads[i]; + + carriers |= rec->handover.carrier_type; + } + + return carriers; +} + +/* + * Walk thru the cfgs list and get the properties corresponding + * to the carrier bit. + */ +static uint16_t near_get_carrier_properties(struct near_ndef_record *record, + uint8_t carrier_bit) +{ + struct near_ndef_ho_payload *ho = record->ho; + int i; + + for (i = 0; i < ho->number_of_cfg_payloads; i++) { + struct near_ndef_mime_payload *rec = ho->cfg_payloads[i]; + + if ((rec->handover.carrier_type & carrier_bit) != 0) + return rec->handover.properties; + } + + return OOB_PROPS_EMPTY; +} + +/* + * @brief Prepare Handover select record with mandatory fields. + * + * TODO: only mime (BT) are supported now... Wifi will come soon... + * Only 1 ac record + 1 collision record+ Hr header + */ +struct near_ndef_message *near_ndef_prepare_handover_record(char* type_name, + struct near_ndef_record *record, + uint8_t carriers) + +{ + uint8_t *oob_data = NULL; + int oob_size; + struct near_ndef_message *hs_msg = NULL; + struct near_ndef_message *ac_msg = NULL; + struct near_ndef_message *cr_msg = NULL; + struct near_ndef_message *bt_msg = NULL; + uint16_t props; + uint16_t collision; + uint8_t hs_length; + near_bool_t mb, me; + char cdr = '0'; /* Carrier data reference */ + + if (record->ho == NULL) + goto fail; + + collision = record->ho->collision_record; + + /* no cr on Hs */ + if (strncmp((char *) type_name, "Hs", 2) == 0) + collision = 0; + + /* Walk the cfg list to get the carriers */ + if (carriers == NEAR_CARRIER_UNKNOWN) + carriers = near_get_carriers_list(record); + + /* + * Prepare records to be added + * now prepare the cr message: MB=1 ME=0 + */ + if (collision != 0) { + cr_msg = near_ndef_prepare_cr_message(collision); + if (cr_msg == NULL) + goto fail; + } + + /* If there's no carrier, we create en empty ac record */ + if (carriers == NEAR_CARRIER_EMPTY) + cdr = 0x00; + + /* + * ac record: if only one: MB=0 ME=1 + * cps should be active + */ + ac_msg = near_ndef_prepare_ac_message(CPS_ACTIVE, cdr); + if (ac_msg == NULL) + goto fail; + + if (carriers & NEAR_CARRIER_BLUETOOTH) { + /* Retrieve the bluetooth settings */ + props = near_get_carrier_properties(record, + NEAR_CARRIER_BLUETOOTH); + + oob_data = __near_bluetooth_local_get_properties(&oob_size, + props); + if (oob_data == NULL) { + near_error("Getting Bluetooth OOB data failed"); + goto fail; + } + + bt_msg = near_ndef_prepare_bt_message(oob_data, oob_size, + cdr, 1); + if (bt_msg == NULL) + goto fail; + + near_ndef_set_mb_me(bt_msg->data, FALSE, TRUE); + } + + if (carriers & NEAR_CARRIER_WIFI) { + /* TODO LATER */ + goto fail; + } + + /* + * Build the complete handover frame + * Prepare Hs or Hr message (1 for version) + */ + hs_length = 1 + ac_msg->length; + if (cr_msg != NULL) + hs_length += cr_msg->length; + + if (bt_msg != NULL) + hs_msg = ndef_message_alloc(type_name, hs_length + + bt_msg->length); + else + hs_msg = ndef_message_alloc(type_name, hs_length); + if (hs_msg == NULL) + goto fail; + + /* + * The handover payload length is not the *real* length. + * The PL refers to the NDEF record, not the extra ones. + * So, we have to fix the payload length in the header. + */ + hs_msg->data[NDEF_PAYLOAD_LENGTH_OFFSET] = hs_length; + + near_ndef_set_mb_me(hs_msg->data, TRUE, TRUE); + + if ((carriers != NEAR_CARRIER_EMPTY) || (cr_msg != NULL)) + near_ndef_set_me(hs_msg->data, FALSE); + + /* Add version */ + hs_msg->data[hs_msg->offset++] = HANDOVER_VERSION; + + /* Prepare MB / ME flags */ + /* cr */ + mb = TRUE; + me = TRUE; + if (cr_msg != NULL) { + near_ndef_set_mb_me(cr_msg->data, mb, me); + if (ac_msg->length != 0) + near_ndef_set_me(cr_msg->data, FALSE); + mb = FALSE; + } + + /* ac */ + if (ac_msg->length != 0) + near_ndef_set_mb_me(ac_msg->data, mb, TRUE); /* xxx, TRUE */ + + /* Now, copy the datas */ + /* copy cr */ + if (cr_msg != NULL) { + memcpy(hs_msg->data + hs_msg->offset, cr_msg->data, + cr_msg->length); + hs_msg->offset += cr_msg->length; + } + + /* copy ac */ + memcpy(hs_msg->data + hs_msg->offset, ac_msg->data, ac_msg->length); + hs_msg->offset += ac_msg->length; + + if (hs_msg->offset > hs_msg->length) + goto fail; + + /* + * Additional NDEF (associated to the ac records) + * Add the BT record which is not part in hs initial size + */ + if (bt_msg != NULL) + memcpy(hs_msg->data + hs_msg->offset, bt_msg->data, + bt_msg->length); + + if (ac_msg != NULL) { + g_free(ac_msg->data); + g_free(ac_msg); + } + + if (cr_msg != NULL) { + g_free(cr_msg->data); + g_free(cr_msg); + } + + if (bt_msg != NULL) { + g_free(bt_msg->data); + g_free(bt_msg); + } + + g_free(oob_data); + + DBG("Hs NDEF done"); + + return hs_msg; + +fail: + near_error("handover select record preparation failed"); + + if (ac_msg != NULL) { + g_free(ac_msg->data); + g_free(ac_msg); + } + + if (cr_msg != NULL) { + g_free(cr_msg->data); + g_free(cr_msg); + } + + if (hs_msg != NULL) { + g_free(hs_msg->data); + g_free(hs_msg); + } + + if (bt_msg != NULL) { + g_free(bt_msg->data); + g_free(bt_msg); + } + + g_free(oob_data); + + return NULL; +} + +/* Code to fill hr record structure from acs and mimes lists */ +static int near_fill_ho_payload(struct near_ndef_ho_payload *ho, + GSList *acs, GSList *mimes) +{ + int rec_count; + int i; + GSList *temp; + + rec_count = g_slist_length(acs); + ho->ac_payloads = g_try_malloc0(rec_count * + sizeof(struct near_ndef_ac_payload *)); + if (ho->ac_payloads == NULL) + goto fail; + temp = acs; + for (i = 0; i < rec_count; i++) { + ho->ac_payloads[i] = temp->data; + temp = temp->next; + } + ho->number_of_ac_payloads = rec_count; + g_slist_free(acs); + + /* Same process for cfg mimes */ + rec_count = g_slist_length(mimes); + ho->cfg_payloads = g_try_malloc0(rec_count * + sizeof(struct near_ndef_mime_payload *)); + if (ho->cfg_payloads == NULL) + goto fail; + temp = mimes; + for (i = 0; i < rec_count; i++) { + ho->cfg_payloads[i] = temp->data; + temp = temp->next; + } + + ho->number_of_cfg_payloads = rec_count; + g_slist_free(mimes); + + return 0; +fail: + g_free(ho->ac_payloads); + g_free(ho->cfg_payloads); + ho->ac_payloads = NULL; + ho->cfg_payloads = NULL; + return -ENOMEM; +} + +/* + * @brief Parse the Handover request record payload + * This function will parse an Hr record payload, retrieving sub records + * like (ac, cr, er) but it will also get the associated + * ndefs (eg: handover carrier record, mime type for BT) + * In a handover frame, only the following types are expected: + * RECORD_TYPE_WKT_HANDOVER_CARRIER: + * RECORD_TYPE_WKT_COLLISION_RESOLUTION + * RECORD_TYPE_MIME_TYPE + * RECORD_TYPE_WKT_ALTERNATIVE_CARRIER + */ +static struct near_ndef_ho_payload *parse_ho_payload(enum record_type rec_type, + uint8_t *payload, uint32_t ho_length, size_t frame_length, + uint8_t ho_mb, uint8_t ho_me) +{ + struct near_ndef_ho_payload *ho_payload = NULL; + struct near_ndef_ac_payload *ac = NULL; + struct near_ndef_mime_payload *mime = NULL; + struct near_ndef_record *trec = NULL; + GSList *acs = NULL, *mimes = NULL; + uint8_t mb = 0, me = 0; + uint32_t offset; + int16_t count_ac = 0; + near_bool_t bt_pair; + + DBG(""); + + if (payload == NULL) + return NULL; + offset = 0; + + /* Create the handover record payload */ + ho_payload = g_try_malloc0(sizeof(struct near_ndef_ho_payload)); + if (ho_payload == NULL) + return NULL; + + /* Version is the first mandatory field of hr payload */ + ho_payload->version = payload[offset]; + + /* If major is different, reply with an empty Hs */ + if (HANDOVER_MAJOR(ho_payload->version) != + HANDOVER_MAJOR(HANDOVER_VERSION)) { + near_error("Unsupported version (%d)", ho_payload->version); + /* Skip parsing and return an empty record */ + return ho_payload; + } + + offset = offset + 1; + + /* We should work on the whole frame */ + ho_length = frame_length; + + while (offset < ho_length) { + /* Create local record for mime parsing */ + trec = g_try_malloc0(sizeof(struct near_ndef_record)); + if (trec == NULL) + return NULL; + + trec->header = parse_record_header(payload, offset, ho_length); + + if (trec->header == NULL) + goto fail; + + offset = trec->header->offset; + + switch (trec->header->rec_type) { + case RECORD_TYPE_WKT_SMART_POSTER: + case RECORD_TYPE_WKT_SIZE: + case RECORD_TYPE_WKT_TEXT: + case RECORD_TYPE_WKT_TYPE: + case RECORD_TYPE_WKT_ACTION: + case RECORD_TYPE_WKT_URI: + case RECORD_TYPE_WKT_HANDOVER_REQUEST: + case RECORD_TYPE_WKT_HANDOVER_SELECT: + case RECORD_TYPE_WKT_ERROR: + case RECORD_TYPE_UNKNOWN: + case RECORD_TYPE_ERROR: + break; + + case RECORD_TYPE_WKT_HANDOVER_CARRIER: + DBG("HANDOVER_CARRIER"); + /* + * TODO process Hc record too !!! + * Used for Wifi session + */ + break; + + case RECORD_TYPE_MIME_TYPE: + DBG("TYPE_MIME_TYPE"); + + /* check mb/me bits */ + if (validate_record_begin_and_end_bits(&ho_mb, &ho_me, + trec->header->mb, trec->header->me) != 0) { + DBG("validate mb me failed"); + goto fail; + } + + /* + * In Handover, the mime type gives bluetooth handover + * configuration datas. + * If we initiated the session, the received Hs frame + * is the signal to launch the pairing. + */ + if (rec_type == RECORD_TYPE_WKT_HANDOVER_SELECT) + bt_pair = TRUE; + else + bt_pair = FALSE; + + mime = parse_mime_type(trec, payload, frame_length, + offset, trec->header->payload_len, + bt_pair); + if (mime == NULL) + goto fail; + + /* add the mime to the list */ + mimes = g_slist_append(mimes, mime); + count_ac--; + if (count_ac == 0) + offset = ho_length; + break; + + case RECORD_TYPE_WKT_COLLISION_RESOLUTION: + DBG("COLLISION_RESOLUTION"); + + /* check nested mb/me bits */ + if (validate_record_begin_and_end_bits(&mb, &me, + trec->header->mb, trec->header->me) != 0) { + DBG("validate mb me failed"); + goto fail; + } + + ho_payload->collision_record = + g_ntohs(*((uint16_t *)(payload + offset))); + break; + + case RECORD_TYPE_WKT_ALTERNATIVE_CARRIER: + DBG("ALTERNATIVE_CARRIER"); + + /* check nested mb/me bits */ + if (validate_record_begin_and_end_bits(&mb, &me, + trec->header->mb, trec->header->me) != 0) { + DBG("validate mb me failed"); + goto fail; + } + + ac = parse_ac_payload(payload + offset, + trec->header->payload_len); + if (ac == NULL) + goto fail; + + acs = g_slist_append(acs, ac); + + /* TODO check if adata are present */ + count_ac++; + break; + } + + offset += trec->header->payload_len; + g_free(trec->header->il_field); + g_free(trec->header->type_name); + g_free(trec->header); + trec->header = NULL; + + g_free(trec); + } + + if ((acs == NULL) || (mimes == NULL)) + return ho_payload; + + /* Save the records */ + if (near_fill_ho_payload(ho_payload, acs, mimes) < 0) + goto fail; + + DBG("handover payload parsing complete"); + + return ho_payload; + +fail: + near_error("handover payload parsing failed"); + + if (trec != NULL) { + if (trec->header != NULL) { + g_free(trec->header->type_name); + g_free(trec->header->il_field); + g_free(trec->header); + } + g_free(trec); + } + + free_ho_payload(ho_payload); + + return NULL; +} + +int __near_ndef_record_register(struct near_ndef_record *record, char *path) +{ + record->path = path; + + g_dbus_register_interface(connection, record->path, + NFC_RECORD_INTERFACE, + record_methods, + NULL, NULL, + record, NULL); + + return 0; +} + +/** + * @brief Parse message represented by bytes block + * + * @param[in] ndef_data pointer on data representing ndef message + * @param[in] ndef_length size of ndef_data + * @param[out] records list, contains all the records + * from parsed message + */ +GList *near_ndef_parse_msg(uint8_t *ndef_data, size_t ndef_length) +{ + GList *records; + uint8_t p_mb = 0, p_me = 0, *record_start; + size_t offset = 0; + struct near_ndef_record *record = NULL; + + DBG(""); + + records = NULL; + + if (ndef_data == NULL || + ndef_length < NDEF_MSG_MIN_LENGTH) + goto fail; + + while (offset < ndef_length) { + + DBG("Record Header : 0x%X", ndef_data[offset]); + + record = g_try_malloc0(sizeof(struct near_ndef_record)); + if (record == NULL) + goto fail; + + record->header = parse_record_header(ndef_data, offset, + ndef_length); + if (record->header == NULL) + goto fail; + + if (validate_record_begin_and_end_bits(&p_mb, &p_me, + record->header->mb, + record->header->me) != 0) { + DBG("validate mb me failed"); + goto fail; + } + + record_start = ndef_data + offset; + offset = record->header->offset; + + switch (record->header->rec_type) { + case RECORD_TYPE_WKT_SIZE: + case RECORD_TYPE_WKT_TYPE: + case RECORD_TYPE_WKT_ACTION: + case RECORD_TYPE_WKT_HANDOVER_CARRIER: + case RECORD_TYPE_WKT_ALTERNATIVE_CARRIER: + case RECORD_TYPE_WKT_COLLISION_RESOLUTION: + case RECORD_TYPE_WKT_ERROR: + case RECORD_TYPE_UNKNOWN: + case RECORD_TYPE_ERROR: + break; + + case RECORD_TYPE_WKT_HANDOVER_REQUEST: + case RECORD_TYPE_WKT_HANDOVER_SELECT: + /* + * Handover frame are a little bit special as the NDEF + * length (specified in the header) is not the real + * frame size. The complete frame includes extra NDEF + * following the initial handover NDEF + */ + record->ho = parse_ho_payload(record->header->rec_type, + ndef_data + offset, + record->header->payload_len, + ndef_length - offset, + record->header->mb, record->header->me); + if (record->ho == NULL) + goto fail; + + /* the complete frame is processed, break the loop */ + record->header->payload_len = ndef_length; + break; + + case RECORD_TYPE_WKT_TEXT: + record->text = parse_text_payload(ndef_data + offset, + record->header->payload_len); + + if (record->text == NULL) + goto fail; + + break; + + case RECORD_TYPE_WKT_URI: + record->uri = parse_uri_payload(ndef_data + offset, + record->header->payload_len); + + if (record->uri == NULL) + goto fail; + + break; + + case RECORD_TYPE_WKT_SMART_POSTER: + record->sp = parse_sp_payload( + ndef_data + offset, + record->header->payload_len); + + if (record->sp == NULL) + goto fail; + + break; + + case RECORD_TYPE_MIME_TYPE: + record->mime = parse_mime_type(record, ndef_data, + ndef_length, offset, + record->header->payload_len, + TRUE); + + + if (record->mime == NULL) + goto fail; + + break; + } + + record->data_len = record->header->header_len + + record->header->payload_len; + + record->data = g_try_malloc0(record->data_len); + if (record->data == NULL) + goto fail; + + memcpy(record->data, record_start, record->data_len); + + records = g_list_append(records, record); + + build_record_type_string(record); + + offset += record->header->payload_len; + } + + return records; + +fail: + near_error("ndef parsing failed"); + free_ndef_record(record); + + return records; +} + +void near_ndef_records_free(GList *records) +{ + GList *list; + + for (list = records; list; list = list->next) { + struct near_ndef_record *record = list->data; + + __near_ndef_record_free(record); + } + + g_list_free(records); +} + +/* + * @brief Compute an NDEF record length + * + * Would compute ndef records length, even though the submitted frame + * is incomplete. This code is used in the handover read function, as + * we have to "guess" the final frame size. + * + * Message size for SR=1 is: + * 1 : ndef rec header (offset 0) + * x : record type length (offset 1) + * y : payload length (offset 2) 1 byte ONLY if SR=1 + * if SR=0: (4bytes) 32 bits + * z : payload id length (offset 3) ONLY if il_length=1 + * */ +int near_ndef_record_length(uint8_t *ndef_in, size_t ndef_in_length) +{ + int ndef_size; /* default size for NDEF hdr + rec typ len + payl */ + size_t offset; + uint8_t hdr; + + DBG(""); + + if (ndef_in_length < 3) + return -EINVAL; + + ndef_size = 3; + offset = 0; + + /* save header byte */ + hdr = ndef_in[offset]; + offset++; + + /* header->type_len */ + ndef_size += ndef_in[offset++]; + + /* header->payload_len */ + if (RECORD_SR_BIT(hdr) == 1) { + ndef_size += ndef_in[offset++]; + } else { + ndef_size += g_ntohl(*((uint32_t *)(ndef_in + offset))); + offset += 4; + + if (offset >= ndef_in_length) + return -ERANGE; + } + + /* header->il */ + ndef_size += RECORD_IL_BIT(hdr); + + /* header->il_length */ + if (RECORD_IL_BIT(hdr) == 1) + ndef_size += ndef_in[offset++]; + + DBG("near_ndef_message_length is %d", ndef_size); + + return ndef_size; +} + +int near_ndef_count_records(uint8_t *ndef_in, size_t ndef_in_length, + uint8_t record_type) +{ + uint8_t p_mb = 0, p_me = 0; + int err; + size_t offset; + struct near_ndef_record *record = NULL; + int counted_records = 0 ; + + DBG(""); + + offset = 0; + + if (ndef_in == NULL || ndef_in_length < NDEF_MSG_MIN_LENGTH) { + err = -EINVAL; + goto fail; + } + + while (offset < ndef_in_length) { + record = g_try_malloc0(sizeof(struct near_ndef_record)); + if (record == NULL) { + err = -ENOMEM; + goto fail; + } + + /* Create a record */ + record->header = parse_record_header(ndef_in, offset, + ndef_in_length); + if (record->header == NULL) { + err = -EINVAL; + goto fail; + } + + /* Validate MB ME */ + if (validate_record_begin_and_end_bits(&p_mb, &p_me, + record->header->mb, + record->header->me) != 0) { + DBG("validate mb me failed"); + err = -EINVAL; + goto fail; + } + + /* Is this what we want ? */ + if (record->header->rec_type == record_type) + counted_records++; + + /* Jump to the next record */ + offset = record->header->offset + record->header->payload_len; + + free_ndef_record(record); + } + + DBG("Type %d Records found: %d", record_type, counted_records); + + return counted_records; + +fail: + near_error("ndef counting failed"); + free_ndef_record(record); + return err; +} + +/** + * @brief Prepare Text ndef record + * + * Prepare text ndef record with provided input data and return + * ndef message structure (length and byte stream) in success or + * NULL in failure case. + * + * @note : caller responsibility to free the input and output + * parameters memory. + * + * @param[in] encoding Encoding (UTF-8 | UTF-16) + * @param[in] language_code Language Code + * @param[in] text Actual text + * + * @return struct near_ndef_message * - Success + * NULL - Failure + */ +struct near_ndef_message *near_ndef_prepare_text_record(char *encoding, + char *language_code, char *text) +{ + struct near_ndef_message *msg; + uint32_t text_len, payload_length; + uint8_t code_len, status = 0; + + DBG(""); + + /* Validate input parameters*/ + if (((g_strcmp0(encoding, "UTF-8") != 0) && + (g_strcmp0(encoding, "UTF-16") != 0)) || + (language_code == NULL) || + (text == NULL)) { + return NULL; + } + + code_len = strlen(language_code); + text_len = strlen(text); + payload_length = 1 + code_len + text_len; + + msg = ndef_message_alloc("T", payload_length); + if (msg == NULL) + return NULL; + + if (g_strcmp0(encoding, "UTF-16") == 0) + status |= NDEF_TEXT_RECORD_UTF16_STATUS; + + status = status | code_len; + msg->data[msg->offset++] = status; + + if (code_len > 0) + memcpy(msg->data + msg->offset, language_code, code_len); + + msg->offset += code_len; + + if (text_len > 0) + memcpy(msg->data + msg->offset, text, text_len); + + msg->offset += text_len; + + if (msg->offset > msg->length) + goto fail; + + return msg; + +fail: + near_error("text record preparation failed"); + g_free(msg->data); + g_free(msg); + + return NULL; +} + +/** + * @brief Prepare URI ndef record + * + * Prepare uri ndef record with provided input data and return + * ndef message structure (length and byte stream) in success or + * NULL in failure case. + * + * @note : caller responsibility to free the input and output + * parameters memory. + * + * @param[in] identifier URI Identifier + * @param[in] field_length URI field length + * @param[in] field URI field + * + * @return struct near_ndef_message * - Success + * NULL - Failure + */ +struct near_ndef_message *near_ndef_prepare_uri_record(uint8_t identifier, + uint32_t field_length, uint8_t *field) +{ + struct near_ndef_message *msg = NULL; + uint32_t payload_length; + + DBG(""); + + /* Validate input parameters*/ + if ((field_length == 0 && field != NULL) || + (field_length != 0 && field == NULL)) { + return NULL; + } + + payload_length = field_length + 1; + + msg = ndef_message_alloc("U", payload_length); + if (msg == NULL) + return NULL; + + msg->data[msg->offset++] = identifier; + + if (field_length > 0) { + memcpy(msg->data + msg->offset, field, field_length); + msg->offset += field_length; + } + + if (msg->offset > msg->length) + goto fail; + + return msg; + +fail: + near_error("uri record preparation failed"); + g_free(msg->data); + g_free(msg); + + return NULL; +} + +/** + * @brief Prepare Smartposter ndef record with mandatory URI fields. + * + * Prepare smartposter ndef record with provided input data and + * return ndef message structure (length and byte stream) in success or + * NULL in failure case. + * + * @note : caller responsibility to free the input and output + * parameters memory. + * + * @param[in] uri_identfier + * @param[in] uri_field_length + * @param[in] uri_field + * + * @return struct near_ndef_message * - Success + * NULL - Failure + */ +struct near_ndef_message * +near_ndef_prepare_smartposter_record(uint8_t uri_identifier, + uint32_t uri_field_length, + uint8_t *uri_field) +{ + struct near_ndef_message *msg = NULL, *uri = NULL; + + /* URI is mandatory in Smartposter */ + uri = near_ndef_prepare_uri_record(uri_identifier, uri_field_length, + uri_field); + if (uri == NULL) + goto fail; + + /* URI record length is equal to payload length of Sp record */ + msg = ndef_message_alloc("Sp", uri->length); + if (msg == NULL) + goto fail; + + memcpy(msg->data + msg->offset, uri->data, uri->length); + msg->offset += uri->length; + + if (msg->offset > msg->length) + goto fail; + + if (uri != NULL) { + g_free(uri->data); + g_free(uri); + } + + return msg; + +fail: + near_error("smartposter record preparation failed"); + + if (uri != NULL) { + g_free(uri->data); + g_free(uri); + } + + if (msg != NULL) { + g_free(msg->data); + g_free(msg); + } + + return NULL; +} + +static char *get_text_field(DBusMessage *msg, char *text) +{ + DBusMessageIter iter, arr_iter; + char *uri = NULL; + + DBG(""); + + if (text == NULL) + return NULL; + + dbus_message_iter_init(msg, &iter); + dbus_message_iter_recurse(&iter, &arr_iter); + + while (dbus_message_iter_get_arg_type(&arr_iter) != + DBUS_TYPE_INVALID) { + const char *key; + DBusMessageIter ent_iter; + DBusMessageIter var_iter; + + dbus_message_iter_recurse(&arr_iter, &ent_iter); + dbus_message_iter_get_basic(&ent_iter, &key); + dbus_message_iter_next(&ent_iter); + dbus_message_iter_recurse(&ent_iter, &var_iter); + + switch (dbus_message_iter_get_arg_type(&var_iter)) { + case DBUS_TYPE_STRING: + if (g_strcmp0(key, text) == 0) + dbus_message_iter_get_basic(&var_iter, &uri); + + break; + } + + dbus_message_iter_next(&arr_iter); + } + + return uri; +} + +static inline char *get_uri_field(DBusMessage *msg) +{ + return get_text_field(msg, "URI"); +} + +static inline char *get_carrier_field(DBusMessage *msg) +{ + return get_text_field(msg, "Carrier"); +} + +static struct near_ndef_message *build_text_record(DBusMessage *msg) +{ + DBusMessageIter iter, arr_iter; + char *cod = NULL, *lang = NULL, *rep = NULL; + + DBG(""); + + dbus_message_iter_init(msg, &iter); + dbus_message_iter_recurse(&iter, &arr_iter); + + while (dbus_message_iter_get_arg_type(&arr_iter) != + DBUS_TYPE_INVALID) { + const char *key; + DBusMessageIter ent_iter; + DBusMessageIter var_iter; + + dbus_message_iter_recurse(&arr_iter, &ent_iter); + dbus_message_iter_get_basic(&ent_iter, &key); + dbus_message_iter_next(&ent_iter); + dbus_message_iter_recurse(&ent_iter, &var_iter); + + switch (dbus_message_iter_get_arg_type(&var_iter)) { + case DBUS_TYPE_STRING: + if (g_strcmp0(key, "Encoding") == 0) + dbus_message_iter_get_basic(&var_iter, &cod); + else if (g_strcmp0(key, "Language") == 0) + dbus_message_iter_get_basic(&var_iter, &lang); + else if (g_strcmp0(key, "Representation") == 0) + dbus_message_iter_get_basic(&var_iter, &rep); + + break; + } + + dbus_message_iter_next(&arr_iter); + } + + return near_ndef_prepare_text_record(cod, lang, rep); +} + +static struct near_ndef_message *build_uri_record(DBusMessage *msg) +{ + char *uri = NULL; + const char *uri_prefix = NULL; + uint8_t id_len, i, id; + uint32_t uri_len; + + DBG(""); + + uri = get_uri_field(msg); + if (uri == NULL) + return NULL; + + id = 0; + id_len = 0; + + for (i = 1; i <= NFC_MAX_URI_ID; i++) { + uri_prefix = __near_ndef_get_uri_prefix(i); + + if (uri_prefix != NULL && + g_str_has_prefix(uri, uri_prefix) == TRUE) { + id = i; + id_len = strlen(uri_prefix); + break; + } + } + + DBG("%d %d\n", i, id_len); + + uri_len = strlen(uri) - id_len; + return near_ndef_prepare_uri_record(id, uri_len, + (uint8_t *)(uri + id_len)); +} + +static struct near_ndef_message *build_sp_record(DBusMessage *msg) +{ + char *uri = NULL; + const char *uri_prefix; + uint8_t id_len, i; + uint32_t uri_len; + + DBG(""); + + /* + * Currently this function supports only mandatory URI record, + * TODO: Other records support. + */ + uri = get_uri_field(msg); + if (uri == NULL) + return NULL; + + for (i = 1; i <= NFC_MAX_URI_ID; i++) { + uri_prefix = __near_ndef_get_uri_prefix(i); + + if (uri_prefix != NULL && + g_str_has_prefix(uri, uri_prefix) == TRUE) + break; + } + + if (uri_prefix == NULL) { + i = 0; + id_len = 0; + } else + id_len = strlen(uri_prefix); + + uri_len = strlen(uri) - id_len; + return near_ndef_prepare_smartposter_record(i, uri_len, + (uint8_t *)(uri + id_len)); +} + +static struct near_ndef_ac_payload *build_ho_local_ac_record(void) +{ + struct near_ndef_ac_payload *ac_payload = NULL; + + DBG(""); + + /* Allocate ac record */ + ac_payload = g_try_malloc0(sizeof(struct near_ndef_ac_payload)); + if (ac_payload == NULL) + return NULL; + + /* Carrier flag */ + ac_payload->cps = CPS_ACTIVE; /* TODO Should reflect BT state */ + + /* Carrier data reference length */ + ac_payload->cdr_len = 1; + + /* Carrier data reference */ + ac_payload->cdr = '0'; + + /* Auxiliary data reference count */ + ac_payload->adata_refcount = 0; + + return ac_payload; +} + +static struct near_ndef_message *build_ho_record(DBusMessage *msg) +{ + char *carrier_type = NULL; + uint8_t carrier; + struct near_ndef_record *record = NULL; + + DBG(""); + + carrier_type = get_carrier_field(msg); + if (carrier_type == NULL) { + DBG("Empty carrier name"); + return NULL; + } + + carrier = NEAR_CARRIER_EMPTY; + if (g_strcmp0(carrier_type, "bluetooth") == 0) + carrier |= NEAR_CARRIER_BLUETOOTH; + if (g_strcmp0(carrier_type, "wifi") == 0) + carrier |= NEAR_CARRIER_WIFI; + + if (carrier == NEAR_CARRIER_EMPTY) { + DBG("Invalid carrier name"); + return NULL; + } + + /* Build local record */ + record = g_try_malloc0(sizeof(struct near_ndef_record)); + if (record == NULL) + return NULL; + + /* Do a Ho */ + record->ho = g_try_malloc0(sizeof(struct near_ndef_ho_payload)); + if (record->ho == NULL) + goto fail; + + /* fill the Ho */ + record->ho->version = HANDOVER_VERSION; + + /* Generate random number for Collision Resolution Record */ + record->ho->collision_record = GUINT16_TO_BE( + g_random_int_range(0, G_MAXUINT16 + 1)); + record->ho->err_record = NULL; + + record->ho->number_of_ac_payloads = 1; + record->ho->ac_payloads = g_try_malloc0( + sizeof(struct near_ndef_ac_payload *)); + if (record->ho->ac_payloads == NULL) + goto fail; + record->ho->ac_payloads[0] = build_ho_local_ac_record(); + + return near_ndef_prepare_handover_record("Hr", record, carrier); + +fail: + free_ho_payload(record->ho); + g_free(record); + + return NULL; +} + +static struct near_ndef_message * build_raw_ndef(DBusMessage *msg) +{ + DBusMessageIter iter, arr_iter; + struct near_ndef_message *ndef; + + DBG(""); + + ndef = NULL; + + dbus_message_iter_init(msg, &iter); + dbus_message_iter_recurse(&iter, &arr_iter); + + while (dbus_message_iter_get_arg_type(&arr_iter) != + DBUS_TYPE_INVALID) { + const char *key; + DBusMessageIter ent_iter; + DBusMessageIter var_iter; + + dbus_message_iter_recurse(&arr_iter, &ent_iter); + dbus_message_iter_get_basic(&ent_iter, &key); + dbus_message_iter_next(&ent_iter); + dbus_message_iter_recurse(&ent_iter, &var_iter); + + switch (dbus_message_iter_get_arg_type(&var_iter)) { + case DBUS_TYPE_ARRAY: + if (g_strcmp0(key, "NDEF") == 0) { + DBusMessageIter array; + uint8_t *data; + int data_size; + + dbus_message_iter_recurse(&var_iter, &array); + dbus_message_iter_get_fixed_array(&array, + &data, + &data_size); + + ndef = g_try_malloc0(data_size); + if (ndef == NULL) + break; + + ndef->data = g_try_malloc0(data_size); + if (ndef->data == NULL) { + g_free(ndef); + break; + } + + ndef->length = data_size; + memcpy(ndef->data, data, data_size); + } + + break; + } + + dbus_message_iter_next(&arr_iter); + } + + return ndef; +} + +struct near_ndef_message *__ndef_build_from_message(DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessageIter arr_iter; + struct near_ndef_message *ndef; + + DBG(""); + + dbus_message_iter_init(msg, &iter); + dbus_message_iter_recurse(&iter, &arr_iter); + + ndef = NULL; + + while (dbus_message_iter_get_arg_type(&arr_iter) != + DBUS_TYPE_INVALID) { + const char *key, *value; + DBusMessageIter ent_iter; + DBusMessageIter var_iter; + + dbus_message_iter_recurse(&arr_iter, &ent_iter); + dbus_message_iter_get_basic(&ent_iter, &key); + + if (g_strcmp0(key, "Type") != 0) { + dbus_message_iter_next(&arr_iter); + continue; + } + + dbus_message_iter_next(&ent_iter); + dbus_message_iter_recurse(&ent_iter, &var_iter); + + switch (dbus_message_iter_get_arg_type(&var_iter)) { + case DBUS_TYPE_STRING: + dbus_message_iter_get_basic(&var_iter, &value); + + if (g_strcmp0(value, "Text") == 0) { + ndef = build_text_record(msg); + break; + } else if (g_strcmp0(value, "URI") == 0) { + ndef = build_uri_record(msg); + break; + } else if (g_strcmp0(value, "SmartPoster") == 0) { + ndef = build_sp_record(msg); + break; + } else if (g_strcmp0(value, "Handover") == 0) { + ndef = build_ho_record(msg); + break; + } else if (g_strcmp0(value, "Raw") == 0) { + ndef = build_raw_ndef(msg); + break; + } else { + near_error("%s not supported", value); + ndef = NULL; + break; + } + + break; + } + + dbus_message_iter_next(&arr_iter); + } + + return ndef; +} + +int __near_ndef_init(void) +{ + DBG(""); + + connection = near_dbus_get_connection(); + + return 0; +} + +void __near_ndef_cleanup(void) +{ +} diff --git a/src/near.h b/src/near.h new file mode 100644 index 0000000..e47d1fb --- /dev/null +++ b/src/near.h @@ -0,0 +1,199 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include <stdint.h> +#include <sys/socket.h> + +#include <linux/socket.h> + + +#include <glib.h> + +#include <near/nfc.h> +#include <near/types.h> + +struct near_adapter; +struct near_device_driver; + +#include <near/log.h> + +int __near_log_init(const char *debug, gboolean detach); +void __near_log_cleanup(void); + +#include <near/dbus.h> + +int __near_dbus_init(DBusConnection *conn); +void __near_dbus_cleanup(void); + +DBusMessage *__near_error_failed(DBusMessage *msg, int errnum); +DBusMessage *__near_error_invalid_arguments(DBusMessage *msg); +DBusMessage *__near_error_permission_denied(DBusMessage *msg); +DBusMessage *__near_error_passphrase_required(DBusMessage *msg); +DBusMessage *__near_error_not_registered(DBusMessage *msg); +DBusMessage *__near_error_not_unique(DBusMessage *msg); +DBusMessage *__near_error_not_supported(DBusMessage *msg); +DBusMessage *__near_error_not_implemented(DBusMessage *msg); +DBusMessage *__near_error_not_found(DBusMessage *msg); +DBusMessage *__near_error_not_polling(DBusMessage *msg); +DBusMessage *__near_error_no_carrier(DBusMessage *msg); +DBusMessage *__near_error_in_progress(DBusMessage *msg); +DBusMessage *__near_error_already_exists(DBusMessage *msg); +DBusMessage *__near_error_already_enabled(DBusMessage *msg); +DBusMessage *__near_error_already_disabled(DBusMessage *msg); +DBusMessage *__near_error_already_connected(DBusMessage *msg); +DBusMessage *__near_error_not_connected(DBusMessage *msg); +DBusMessage *__near_error_operation_aborted(DBusMessage *msg); +DBusMessage *__near_error_operation_timeout(DBusMessage *msg); +DBusMessage *__near_error_invalid_service(DBusMessage *msg); +DBusMessage *__near_error_invalid_property(DBusMessage *msg); + +int __near_manager_adapter_add(uint32_t idx, const char *name, + uint32_t protocols, near_bool_t powered); +void __near_manager_adapter_remove(uint32_t idx); +int __near_manager_init(DBusConnection *conn); +void __near_manager_cleanup(void); + +#include <near/adapter.h> + +struct near_adapter * __near_adapter_create(uint32_t idx, + const char *name, uint32_t protocols, near_bool_t powered); +void __near_adapter_destroy(struct near_adapter *adapter); +const char *__near_adapter_get_path(struct near_adapter *adapter); +struct near_adapter *__near_adapter_get(uint32_t idx); +int __near_adapter_add(struct near_adapter *adapter); +void __near_adapter_remove(struct near_adapter *adapter); +int __near_adapter_add_target(uint32_t idx, uint32_t target_idx, + uint32_t protocols, uint16_t sens_res, uint8_t sel_res, + uint8_t *nfcid, uint8_t nfcid_len); +int __near_adapter_remove_target(uint32_t idx, uint32_t target_idx); +int __near_adapter_add_device(uint32_t idx, uint8_t *nfcid, uint8_t nfcid_len); +int __near_adapter_remove_device(uint32_t idx); +int __near_adapter_set_dep_state(uint32_t idx, near_bool_t dep); +near_bool_t __near_adapter_get_dep_state(uint32_t idx); +void __near_adapter_tags_changed(uint32_t adapter_idx); +void __near_adapter_devices_changed(uint32_t adapter_idx); +void __near_adapter_listen(struct near_device_driver *driver); +void __near_adapter_list(DBusMessageIter *iter, void *user_data); +void __near_adapter_start_check_presence(uint32_t adapter_idx, uint32_t target_idx); +void __near_adapter_stop_check_presence(uint32_t adapter_idx, uint32_t target_idx); +int __near_adapter_init(void); +void __near_adapter_cleanup(void); + +#include <near/ndef.h> + +#define NFC_MAX_URI_ID 0x23 + +int __near_ndef_init(void); +void __near_ndef_cleanup(void); +int __near_ndef_record_register(struct near_ndef_record *record, char *path); +void __near_ndef_record_free(struct near_ndef_record *record); +char *__near_ndef_record_get_path(struct near_ndef_record *record); +char *__near_ndef_record_get_type(struct near_ndef_record *record); +uint8_t *__near_ndef_record_get_data(struct near_ndef_record *record, size_t *len); +void __near_ndef_append_records(DBusMessageIter *iter, GList *record); +const char *__near_ndef_get_uri_prefix(uint8_t id); +struct near_ndef_message *__ndef_build_from_message(DBusMessage *msg); + +#include <near/tag.h> + +int __near_tag_init(void); +void __near_tag_cleanup(void); +struct near_tag *__near_tag_add(uint32_t idx, uint32_t target_idx, + uint32_t protocols, + uint16_t sens_res, uint8_t sel_res, + uint8_t *nfcid, uint8_t nfcid_len); +void __near_tag_remove(struct near_tag *tag); +const char *__near_tag_get_path(struct near_tag *tag); +uint32_t __near_tag_get_type(struct near_tag *tag); +void __near_tag_append_records(struct near_tag *tag, DBusMessageIter *iter); +int __near_tag_read(struct near_tag *tag, near_tag_io_cb cb); +int __near_tag_write(struct near_tag *tag, + struct near_ndef_message *ndef, + near_tag_io_cb cb); +int __near_tag_check_presence(struct near_tag *tag, near_tag_io_cb cb); + +#include <near/device.h> + +int __near_device_init(void); +void __near_device_cleanup(void); +const char *__near_device_get_path(struct near_device *device); +uint32_t __neard_device_get_idx(struct near_device *device); +struct near_device *__near_device_add(uint32_t idx, uint32_t target_idx, + uint8_t *nfcid, uint8_t nfcid_len); +void __near_device_remove(struct near_device *device); +int __near_device_listen(struct near_device *device, near_device_io_cb cb); +int __near_device_push(struct near_device *device, + struct near_ndef_message *ndef, char *service_name, + near_device_io_cb cb); + +#include <near/tlv.h> + +int __near_netlink_get_adapters(void); +int __near_netlink_start_poll(int idx, + uint32_t im_protocols, uint32_t tm_protocols); +int __near_netlink_stop_poll(int idx); +int __near_netlink_dep_link_up(uint32_t idx, uint32_t target_idx, + uint8_t comm_mode, uint8_t rf_mode); +int __near_netlink_dep_link_down(uint32_t idx); +int __near_netlink_adapter_enable(int idx, near_bool_t enable); +int __near_netlink_init(void); +void __near_netlink_cleanup(void); + +#include <near/setting.h> + +#include <near/plugin.h> + +int __near_plugin_init(const char *pattern, const char *exclude); +void __near_plugin_cleanup(void); + +/* NFC Bluetooth Secure Simple Pairing */ +#define BT_MIME_V2_0 0 +#define BT_MIME_V2_1 1 +#define BT_MIME_STRING_2_0 "nokia.com:bt" +#define BT_MIME_STRING_2_1 "application/vnd.bluetooth.ep.oob" +#define WIFI_MIME_STRING "application/vnd.wfa.wsc" + +/* Mime specific properties */ +#define OOB_PROPS_EMPTY 0x00 +#define OOB_PROPS_SP_HASH 0x01 +#define OOB_PROPS_SP_RANDOM 0x02 +#define OOB_PROPS_SHORT_NAME 0x04 +#define OOB_PROPS_COD 0x08 +#define OOB_PROPS_SP (OOB_PROPS_SP_HASH | OOB_PROPS_SP_RANDOM) + +int __near_bluetooth_init(void); +void __near_bluetooth_cleanup(void); +int __near_bluetooth_parse_oob_record(uint8_t version, uint8_t *bt_data, + uint16_t *properties, near_bool_t pair); +int __near_bluetooth_pair(void *data); +uint8_t *__near_bluetooth_local_get_properties(int *bt_data_len, + uint16_t mime_props); + +void __near_agent_ndef_parse_records(GList *records); +int __near_agent_ndef_register(const char *sender, const char *path, + const char *record_type); +int __near_agent_ndef_unregister(const char *sender, const char *path, + const char *record_type); +int __near_agent_handover_register(const char *sender, const char *path); +int __near_agent_handover_unregister(const char *sender, const char *path); + +int __near_agent_init(void); +void __near_agent_cleanup(void); diff --git a/src/netlink.c b/src/netlink.c new file mode 100644 index 0000000..fa83723 --- /dev/null +++ b/src/netlink.c @@ -0,0 +1,910 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright 2007, 2008 Johannes Berg <johannes@sipsolutions.net> + * Copyright (C) 2011 Instituto Nokia de Tecnologia + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> + +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> + +#include "near.h" + +#ifdef NEED_LIBNL_COMPAT +#define nl_sock nl_handle + +static inline struct nl_handle *nl_socket_alloc(void) +{ + return nl_handle_alloc(); +} + +static inline void nl_socket_free(struct nl_sock *h) +{ + nl_handle_destroy(h); +} + +#define NLE_MISSING_ATTR 14 + +static inline void __nl_perror(int error, const char *s) +{ + nl_perror(s); +} +#define nl_perror __nl_perror +#endif + +struct nlnfc_state { + struct nl_sock *cmd_sock; + struct nl_sock *event_sock; + int nfc_id; + int mcid; +}; + +static struct nlnfc_state *nfc_state; +static GIOChannel *netlink_channel = NULL; + +static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, + void *arg) +{ + int *ret = arg; + + DBG(""); + + *ret = err->error; + + return NL_STOP; +} + +static int finish_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + + DBG(""); + + *ret = 1; + + return NL_SKIP; +} + +static int ack_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + + DBG(""); + + *ret = 1; + + return NL_STOP; +} + +static int nl_send_msg(struct nl_sock *sock, struct nl_msg *msg, + int (*rx_handler)(struct nl_msg *, void *), + void *data) +{ + struct nl_cb *cb; + int err, done; + + DBG(""); + + cb = nl_cb_alloc(NL_CB_DEFAULT); + if (cb == NULL) + return -ENOMEM; + + err = nl_send_auto_complete(sock, msg); + if (err < 0) { + nl_cb_put(cb); + near_error("%s", strerror(err)); + + return err; + } + + err = done = 0; + + nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &done); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &done); + + if (rx_handler != NULL) + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, rx_handler, data); + + while (err == 0 && done == 0) + nl_recvmsgs(sock, cb); + + nl_cb_put(cb); + + return err; +} + + +static int get_devices_handler(struct nl_msg *n, void *arg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(n); + struct nlattr *attrs[NFC_ATTR_MAX + 1]; + char *name; + uint32_t idx, protocols; + near_bool_t powered; + + DBG(""); + + genlmsg_parse(nlh, 0, attrs, NFC_ATTR_MAX, NULL); + + if (attrs[NFC_ATTR_DEVICE_INDEX] == NULL || + attrs[NFC_ATTR_DEVICE_NAME] == NULL || + attrs[NFC_ATTR_PROTOCOLS] == NULL) { + nl_perror(NLE_MISSING_ATTR, "NFC_CMD_GET_DEVICE"); + return NL_STOP; + } + + + idx = nla_get_u32(attrs[NFC_ATTR_DEVICE_INDEX]); + name = nla_get_string(attrs[NFC_ATTR_DEVICE_NAME]); + protocols = nla_get_u32(attrs[NFC_ATTR_PROTOCOLS]); + + if (attrs[NFC_ATTR_DEVICE_POWERED] == NULL) + powered = FALSE; + else + powered = nla_get_u8(attrs[NFC_ATTR_DEVICE_POWERED]); + + __near_manager_adapter_add(idx, name, protocols, powered); + + return NL_SKIP; +} + +int __near_netlink_get_adapters(void) +{ + struct nl_msg *msg; + void *hdr; + int err; + + DBG(""); + + if (nfc_state == NULL || nfc_state->nfc_id < 0) + return -ENODEV; + + msg = nlmsg_alloc(); + if (msg == NULL) + return -ENOMEM; + + hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nfc_state->nfc_id, 0, + NLM_F_DUMP, NFC_CMD_GET_DEVICE, NFC_GENL_VERSION); + if (hdr == NULL) { + err = -EINVAL; + goto out; + } + + err = nl_send_msg(nfc_state->cmd_sock, msg, get_devices_handler, NULL); + +out: + nlmsg_free(msg); + + return err; +} + +int __near_netlink_start_poll(int idx, + uint32_t im_protocols, uint32_t tm_protocols) +{ + struct nl_msg *msg; + void *hdr; + int err; + + DBG("IM protos 0x%x TM protos 0x%x", im_protocols, tm_protocols); + + msg = nlmsg_alloc(); + if (msg == NULL) + return -ENOMEM; + + hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nfc_state->nfc_id, 0, + NLM_F_REQUEST, NFC_CMD_START_POLL, NFC_GENL_VERSION); + if (hdr == NULL) { + err = -EINVAL; + goto nla_put_failure; + } + + err = -EMSGSIZE; + + NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, idx); + if (im_protocols != 0) { + NLA_PUT_U32(msg, NFC_ATTR_IM_PROTOCOLS, im_protocols); + NLA_PUT_U32(msg, NFC_ATTR_PROTOCOLS, im_protocols); + } + if (tm_protocols != 0) + NLA_PUT_U32(msg, NFC_ATTR_TM_PROTOCOLS, tm_protocols); + + err = nl_send_msg(nfc_state->cmd_sock, msg, NULL, NULL); + +nla_put_failure: + nlmsg_free(msg); + + return err; +} + + +int __near_netlink_stop_poll(int idx) +{ + struct nl_msg *msg; + void *hdr; + int err; + + DBG(""); + + msg = nlmsg_alloc(); + if (msg == NULL) + return -ENOMEM; + + hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nfc_state->nfc_id, 0, + NLM_F_REQUEST, NFC_CMD_STOP_POLL, NFC_GENL_VERSION); + if (hdr == NULL) { + err = -EINVAL; + goto nla_put_failure; + } + + err = -EMSGSIZE; + + NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, idx); + + err = nl_send_msg(nfc_state->cmd_sock, msg, NULL, NULL); + +nla_put_failure: + nlmsg_free(msg); + + return err; +} + +int __near_netlink_dep_link_up(uint32_t idx, uint32_t target_idx, + uint8_t comm_mode, uint8_t rf_mode) +{ + struct nl_msg *msg; + void *hdr; + int err; + + DBG(""); + + msg = nlmsg_alloc(); + if (msg == NULL) + return -ENOMEM; + + hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nfc_state->nfc_id, 0, + NLM_F_REQUEST, NFC_CMD_DEP_LINK_UP, NFC_GENL_VERSION); + if (hdr == NULL) { + err = -EINVAL; + goto nla_put_failure; + } + + err = -EMSGSIZE; + + NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, idx); + NLA_PUT_U32(msg, NFC_ATTR_TARGET_INDEX, target_idx); + NLA_PUT_U8(msg, NFC_ATTR_COMM_MODE, comm_mode); + NLA_PUT_U8(msg, NFC_ATTR_RF_MODE, rf_mode); + + err = nl_send_msg(nfc_state->cmd_sock, msg, NULL, NULL); + +nla_put_failure: + nlmsg_free(msg); + + return err; +} + +int __near_netlink_dep_link_down(uint32_t idx) +{ + struct nl_msg *msg; + void *hdr; + int err; + + DBG(""); + + msg = nlmsg_alloc(); + if (msg == NULL) + return -ENOMEM; + + hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nfc_state->nfc_id, 0, + NLM_F_REQUEST, NFC_CMD_DEP_LINK_DOWN, NFC_GENL_VERSION); + if (hdr == NULL) { + err = -EINVAL; + goto nla_put_failure; + } + + err = -EMSGSIZE; + + NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, idx); + + err = nl_send_msg(nfc_state->cmd_sock, msg, NULL, NULL); + +nla_put_failure: + nlmsg_free(msg); + + return err; +} + +int __near_netlink_adapter_enable(int idx, near_bool_t enable) +{ + struct nl_msg *msg; + void *hdr; + int err; + uint8_t cmd; + + DBG(""); + + msg = nlmsg_alloc(); + if (msg == NULL) + return -ENOMEM; + + if (enable == TRUE) + cmd = NFC_CMD_DEV_UP; + else + cmd = NFC_CMD_DEV_DOWN; + + hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nfc_state->nfc_id, 0, + NLM_F_REQUEST, cmd, NFC_GENL_VERSION); + if (hdr == NULL) { + err = -EINVAL; + goto nla_put_failure; + } + + err = -EMSGSIZE; + + NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, idx); + + err = nl_send_msg(nfc_state->cmd_sock, msg, NULL, NULL); + +nla_put_failure: + nlmsg_free(msg); + + return err; +} + + +static int no_seq_check(struct nl_msg *n, void *arg) +{ + DBG(""); + + return NL_OK; +} + +static int nfc_netlink_event_adapter(struct genlmsghdr *gnlh, near_bool_t add) +{ + struct nlattr *attrs[NFC_ATTR_MAX + 1]; + uint32_t idx; + + DBG(""); + + nla_parse(attrs, NFC_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (attrs[NFC_ATTR_DEVICE_INDEX] == NULL) { + near_error("Missing device index"); + return -ENODEV; + } + + idx = nla_get_u32(attrs[NFC_ATTR_DEVICE_INDEX]); + + if (add == TRUE && + (attrs[NFC_ATTR_DEVICE_NAME] == NULL || + attrs[NFC_ATTR_PROTOCOLS] == NULL)) { + near_error("Missing attributes"); + return -EINVAL; + } + + if (add == TRUE) { + char *name; + uint32_t protocols; + near_bool_t powered; + + name = nla_get_string(attrs[NFC_ATTR_DEVICE_NAME]); + protocols = nla_get_u32(attrs[NFC_ATTR_PROTOCOLS]); + if (attrs[NFC_ATTR_DEVICE_POWERED] == NULL) + powered = FALSE; + else + powered = nla_get_u8(attrs[NFC_ATTR_DEVICE_POWERED]); + + return __near_manager_adapter_add(idx, name, + protocols, powered); + } else { + __near_manager_adapter_remove(idx); + } + + return 0; +} + +static int get_targets_handler(struct nl_msg *n, void *arg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(n); + struct nlattr *attrs[NFC_ATTR_MAX + 1]; + uint32_t adapter_idx, target_idx, protocols; + uint16_t sens_res = 0; + uint8_t sel_res = 0; + uint8_t nfcid[NFC_MAX_NFCID1_LEN], nfcid_len; + + DBG(""); + + genlmsg_parse(nlh, 0, attrs, NFC_ATTR_MAX, NULL); + + adapter_idx = *((uint32_t *)arg); + target_idx = nla_get_u32(attrs[NFC_ATTR_TARGET_INDEX]); + protocols = nla_get_u32(attrs[NFC_ATTR_PROTOCOLS]); + + if (attrs[NFC_ATTR_TARGET_SENS_RES] != NULL) + sens_res = + nla_get_u16(attrs[NFC_ATTR_TARGET_SENS_RES]); + + if (attrs[NFC_ATTR_TARGET_SEL_RES] != NULL) + sel_res = + nla_get_u16(attrs[NFC_ATTR_TARGET_SEL_RES]); + + if (attrs[NFC_ATTR_TARGET_NFCID1] != NULL) { + nfcid_len = nla_len(attrs[NFC_ATTR_TARGET_NFCID1]); + if (nfcid_len <= NFC_MAX_NFCID1_LEN) + memcpy(nfcid, nla_data(attrs[NFC_ATTR_TARGET_NFCID1]), + nfcid_len); + } else { + nfcid_len = 0; + } + + DBG("target idx %d proto 0x%x sens_res 0x%x sel_res 0x%x NFCID len %d", + target_idx, protocols, sens_res, sel_res, nfcid_len); + + __near_adapter_add_target(adapter_idx, target_idx, protocols, + sens_res, sel_res, nfcid, nfcid_len); + + return 0; +} + +static int nfc_netlink_event_targets_found(struct genlmsghdr *gnlh) +{ + struct nlattr *attr[NFC_ATTR_MAX + 1]; + struct nl_msg *msg; + void *hdr; + int err; + uint32_t adapter_idx; + + DBG(""); + + nla_parse(attr, NFC_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (attr[NFC_ATTR_DEVICE_INDEX] == NULL) + return -ENODEV; + + adapter_idx = nla_get_u32(attr[NFC_ATTR_DEVICE_INDEX]); + + DBG("adapter %d", adapter_idx); + + msg = nlmsg_alloc(); + if (msg == NULL) + return -ENOMEM; + + hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nfc_state->nfc_id, 0, + NLM_F_DUMP, NFC_CMD_GET_TARGET, NFC_GENL_VERSION); + if (hdr == NULL) { + err = -EINVAL; + goto nla_put_failure; + } + + err = -EMSGSIZE; + + NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, adapter_idx); + + err = nl_send_msg(nfc_state->cmd_sock, msg, + get_targets_handler, &adapter_idx); + +nla_put_failure: + nlmsg_free(msg); + + return err; +} + +static int nfc_netlink_event_target_lost(struct genlmsghdr *gnlh) +{ + struct nlattr *attr[NFC_ATTR_MAX + 1]; + uint32_t adapter_idx, target_idx; + + DBG(""); + + nla_parse(attr, NFC_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (attr[NFC_ATTR_DEVICE_INDEX] == NULL) + return -ENODEV; + + if (attr[NFC_ATTR_TARGET_INDEX] == NULL) + return -ENODEV; + + adapter_idx = nla_get_u32(attr[NFC_ATTR_DEVICE_INDEX]); + target_idx = nla_get_u32(attr[NFC_ATTR_TARGET_INDEX]); + + DBG("adapter %d target %d", adapter_idx, target_idx); + + return __near_adapter_remove_target(adapter_idx, target_idx); +} + +static int nfc_netlink_event_dep_up(struct genlmsghdr *gnlh) +{ + struct nlattr *attrs[NFC_ATTR_MAX + 1]; + uint32_t idx, target_idx = 0; + uint8_t rf_mode; + + DBG(""); + + nla_parse(attrs, NFC_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (attrs[NFC_ATTR_DEVICE_INDEX] == NULL) { + near_error("Missing device index"); + return -ENODEV; + } + + if (attrs[NFC_ATTR_COMM_MODE] == NULL || + attrs[NFC_ATTR_RF_MODE] == NULL) { + near_error("Missing rf or comm modes"); + return -ENODEV; + } + + idx = nla_get_u32(attrs[NFC_ATTR_DEVICE_INDEX]); + rf_mode = nla_get_u8(attrs[NFC_ATTR_RF_MODE]); + + if (rf_mode == NFC_RF_INITIATOR) { + if (attrs[NFC_ATTR_TARGET_INDEX] == NULL) { + near_error("Missing target index"); + return -ENODEV; + }; + + target_idx = nla_get_u32(attrs[NFC_ATTR_TARGET_INDEX]); + + DBG("%d %d", idx, target_idx); + + return __near_adapter_set_dep_state(idx, TRUE); + } else { + return -EOPNOTSUPP; + } +} + +static int nfc_netlink_event_dep_down(struct genlmsghdr *gnlh) +{ + struct nlattr *attrs[NFC_ATTR_MAX + 1]; + uint32_t idx; + + DBG(""); + + nla_parse(attrs, NFC_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (attrs[NFC_ATTR_DEVICE_INDEX] == NULL) { + near_error("Missing device index"); + return -ENODEV; + } + + idx = nla_get_u32(attrs[NFC_ATTR_DEVICE_INDEX]); + + __near_adapter_set_dep_state(idx, FALSE); + + return 0; +} + +static int nfc_netlink_event_tm_activated(struct genlmsghdr *gnlh) +{ + struct nlattr *attrs[NFC_ATTR_MAX + 1]; + uint32_t idx; + + DBG(""); + + nla_parse(attrs, NFC_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (attrs[NFC_ATTR_DEVICE_INDEX] == NULL) { + near_error("Missing device index"); + return -ENODEV; + } + + idx = nla_get_u32(attrs[NFC_ATTR_DEVICE_INDEX]); + + DBG("%d", idx); + + return __near_adapter_add_device(idx, NULL, 0); +} + +static int nfc_netlink_event_tm_deactivated(struct genlmsghdr *gnlh) +{ + struct nlattr *attrs[NFC_ATTR_MAX + 1]; + uint32_t idx; + + DBG(""); + + nla_parse(attrs, NFC_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (attrs[NFC_ATTR_DEVICE_INDEX] == NULL) { + near_error("Missing device index"); + return -ENODEV; + } + + idx = nla_get_u32(attrs[NFC_ATTR_DEVICE_INDEX]); + + DBG("%d", idx); + + return __near_adapter_remove_device(idx); +} + +static int nfc_netlink_event(struct nl_msg *n, void *arg) +{ + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(n)); + + DBG("event 0x%x", gnlh->cmd); + + switch (gnlh->cmd) { + case NFC_EVENT_TARGETS_FOUND: + DBG("Targets found"); + nfc_netlink_event_targets_found(gnlh); + break; + case NFC_EVENT_TARGET_LOST: + DBG("Target lost"); + nfc_netlink_event_target_lost(gnlh); + break; + case NFC_EVENT_DEVICE_ADDED: + DBG("Adapter added"); + nfc_netlink_event_adapter(gnlh, TRUE); + + break; + case NFC_EVENT_DEVICE_REMOVED: + DBG("Adapter removed"); + nfc_netlink_event_adapter(gnlh, FALSE); + + break; + case NFC_CMD_DEP_LINK_UP: + DBG("DEP link is up"); + nfc_netlink_event_dep_up(gnlh); + + break; + case NFC_CMD_DEP_LINK_DOWN: + DBG("DEP link is down"); + nfc_netlink_event_dep_down(gnlh); + + break; + case NFC_EVENT_TM_ACTIVATED: + DBG("Target mode activated"); + nfc_netlink_event_tm_activated(gnlh); + + break; + case NFC_EVENT_TM_DEACTIVATED: + DBG("Target mode deactivated"); + nfc_netlink_event_tm_deactivated(gnlh); + + break; + } + + return NL_SKIP; +} + +static gboolean __nfc_netlink_event(GIOChannel *channel, + GIOCondition cond, gpointer data) +{ + struct nl_cb *cb; + struct nlnfc_state *state = data; + + if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) + return FALSE; + + cb = nl_cb_alloc(NL_CB_VERBOSE); + if (cb == NULL) + return TRUE; + + nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, nfc_netlink_event, data); + + nl_recvmsgs(state->event_sock, cb); + + nl_cb_put(cb); + + return TRUE; +} + +static int nfc_event_listener(struct nlnfc_state *state) +{ + int sock; + + sock = nl_socket_get_fd(state->event_sock); + netlink_channel = g_io_channel_unix_new(sock); + g_io_channel_set_close_on_unref(netlink_channel, TRUE); + + g_io_channel_set_encoding(netlink_channel, NULL, NULL); + g_io_channel_set_buffered(netlink_channel, FALSE); + + g_io_add_watch(netlink_channel, + G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR, + __nfc_netlink_event, state); + + return 0; +} + +struct handler_args { + const char *group; + int id; +}; + +static int family_handler(struct nl_msg *msg, void *arg) +{ + struct handler_args *grp = arg; + struct nlattr *tb[CTRL_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *mcgrp; + int rem_mcgrp; + + DBG(""); + + nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[CTRL_ATTR_MCAST_GROUPS]) + return NL_SKIP; + + nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mcgrp) { + struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1]; + + nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX, + nla_data(mcgrp), nla_len(mcgrp), NULL); + + if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME] || + !tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]) + continue; + if (strncmp(nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]), + grp->group, nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]))) + continue; + grp->id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]); + break; + } + + return NL_SKIP; +} + +static int nl_get_multicast_id(struct nl_sock *sock, const char *family, + const char *group) +{ + struct nl_msg *msg; + int err, ctrlid; + struct handler_args grp = { + .group = group, + .id = -ENOENT, + }; + + DBG(""); + + msg = nlmsg_alloc(); + if (msg == NULL) + return -ENOMEM; + + ctrlid = genl_ctrl_resolve(sock, "nlctrl"); + + genlmsg_put(msg, 0, 0, ctrlid, 0, + 0, CTRL_CMD_GETFAMILY, 0); + + err = -EMSGSIZE; + + NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family); + + err = nl_send_msg(sock, msg, family_handler, &grp); + if (err) + goto nla_put_failure; + + DBG("multicast id %d", grp.id); + + err = grp.id; + +nla_put_failure: + nlmsg_free(msg); + + return err; +} + +int __near_netlink_init(void) +{ + int err; + + DBG(""); + + nfc_state = g_try_malloc0(sizeof(struct nlnfc_state)); + if (nfc_state == NULL) + return -ENOMEM; + + nfc_state->cmd_sock = nl_socket_alloc(); + if (nfc_state->cmd_sock == NULL) { + near_error("Failed to allocate NFC command netlink socket"); + err = -ENOMEM; + goto state_free; + } + + nfc_state->event_sock = nl_socket_alloc(); + if (nfc_state->event_sock == NULL) { + near_error("Failed to allocate NFC event netlink socket"); + err = -ENOMEM; + goto handle_cmd_destroy; + } + + if (genl_connect(nfc_state->cmd_sock)) { + near_error("Failed to connect to generic netlink"); + err = -ENOLINK; + goto handle_event_destroy; + } + + if (genl_connect(nfc_state->event_sock)) { + near_error("Failed to connect to generic netlink"); + err = -ENOLINK; + goto handle_event_destroy; + } + + nfc_state->nfc_id = genl_ctrl_resolve(nfc_state->cmd_sock, "nfc"); + if (nfc_state->nfc_id < 0) { + near_error("Unable to find NFC netlink family"); + err = -ENOENT; + goto handle_event_destroy; + } + + nfc_state->mcid = nl_get_multicast_id(nfc_state->cmd_sock, NFC_GENL_NAME, + NFC_GENL_MCAST_EVENT_NAME); + if (nfc_state->mcid <= 0) { + near_error("Wrong mcast id %d", nfc_state->mcid); + err = nfc_state->mcid; + goto handle_event_destroy; + } + + err = nl_socket_add_membership(nfc_state->event_sock, nfc_state->mcid); + if (err) { + near_error("Error adding nl event socket to membership"); + goto handle_event_destroy; + } + + return nfc_event_listener(nfc_state); + +handle_event_destroy: + nl_socket_free(nfc_state->event_sock); + +handle_cmd_destroy: + nl_socket_free(nfc_state->cmd_sock); + +state_free: + g_free(nfc_state); + + nfc_state = NULL; + + near_error("netlink init failed"); + + return err; +} + +void __near_netlink_cleanup(void) +{ + if (netlink_channel != NULL) { + g_io_channel_shutdown(netlink_channel, TRUE, NULL); + g_io_channel_unref(netlink_channel); + + netlink_channel = NULL; + } + + if (nfc_state == NULL) + return; + + nl_socket_free(nfc_state->cmd_sock); + nl_socket_free(nfc_state->event_sock); + + g_free(nfc_state); + + DBG(""); +} + diff --git a/src/org.neard.conf b/src/org.neard.conf new file mode 100644 index 0000000..12c0ab9 --- /dev/null +++ b/src/org.neard.conf @@ -0,0 +1,15 @@ +<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + <policy user="root"> + <allow own="org.neard"/> + <allow send_destination="org.neard"/> + <allow send_interface="org.neard.NDEFAgent"/> + </policy> + <policy at_console="true"> + <allow send_destination="org.neard"/> + </policy> + <policy context="default"> + <deny send_destination="org.neard"/> + </policy> +</busconfig> diff --git a/src/plugin.c b/src/plugin.c new file mode 100644 index 0000000..6c5b676 --- /dev/null +++ b/src/plugin.c @@ -0,0 +1,211 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <dlfcn.h> + +#include <glib.h> + +#ifdef NEAR_PLUGIN_BUILTIN +#undef NEAR_PLUGIN_BUILTIN +#endif + +#include "near.h" + +static GSList *plugins = NULL; + +struct near_plugin { + void *handle; + gboolean active; + struct near_plugin_desc *desc; +}; + +static gint compare_priority(gconstpointer a, gconstpointer b) +{ + const struct near_plugin *plugin1 = a; + const struct near_plugin *plugin2 = b; + + return plugin2->desc->priority - plugin1->desc->priority; +} + +static gboolean add_plugin(void *handle, struct near_plugin_desc *desc) +{ + struct near_plugin *plugin; + + if (desc->init == NULL) + return FALSE; + + if (g_str_equal(desc->version, NEAR_VERSION) == FALSE) { + near_error("Version mismatch for %s", desc->description); + return FALSE; + } + + plugin = g_try_new0(struct near_plugin, 1); + if (plugin == NULL) + return FALSE; + + plugin->handle = handle; + plugin->active = FALSE; + plugin->desc = desc; + + plugins = g_slist_insert_sorted(plugins, plugin, compare_priority); + + return TRUE; +} + +static gboolean check_plugin(struct near_plugin_desc *desc, + char **patterns, char **excludes) +{ + if (excludes) { + for (; *excludes; excludes++) + if (g_pattern_match_simple(*excludes, desc->name)) + break; + if (*excludes) { + near_info("Excluding %s", desc->description); + return FALSE; + } + } + + if (patterns) { + for (; *patterns; patterns++) + if (g_pattern_match_simple(*patterns, desc->name)) + break; + if (!*patterns) { + near_info("Ignoring %s", desc->description); + return FALSE; + } + } + + return TRUE; +} + +#include "builtin.h" + +int __near_plugin_init(const char *pattern, const char *exclude) +{ + gchar **patterns = NULL; + gchar **excludes = NULL; + GSList *list; + GDir *dir; + const gchar *file; + gchar *filename; + unsigned int i; + + DBG(""); + + if (pattern) + patterns = g_strsplit_set(pattern, ":, ", -1); + + if (exclude) + excludes = g_strsplit_set(exclude, ":, ", -1); + + for (i = 0; __near_builtin[i]; i++) { + if (check_plugin(__near_builtin[i], + patterns, excludes) == FALSE) + continue; + + add_plugin(NULL, __near_builtin[i]); + } + + dir = g_dir_open(PLUGINDIR, 0, NULL); + if (dir != NULL) { + while ((file = g_dir_read_name(dir)) != NULL) { + void *handle; + struct near_plugin_desc *desc; + + if (g_str_has_prefix(file, "lib") == TRUE || + g_str_has_suffix(file, ".so") == FALSE) + continue; + + filename = g_build_filename(PLUGINDIR, file, NULL); + + handle = dlopen(filename, RTLD_NOW); + if (handle == NULL) { + near_error("Can't load %s: %s", + filename, dlerror()); + g_free(filename); + continue; + } + + g_free(filename); + + desc = dlsym(handle, "near_plugin_desc"); + if (desc == NULL) { + near_error("Can't load symbol: %s", + dlerror()); + dlclose(handle); + continue; + } + + if (check_plugin(desc, patterns, excludes) == FALSE) { + dlclose(handle); + continue; + } + + if (add_plugin(handle, desc) == FALSE) + dlclose(handle); + } + + g_dir_close(dir); + } + + for (list = plugins; list; list = list->next) { + struct near_plugin *plugin = list->data; + + if (plugin->desc->init() < 0) + continue; + + plugin->active = TRUE; + } + + g_strfreev(patterns); + g_strfreev(excludes); + + return 0; +} + +void __near_plugin_cleanup(void) +{ + GSList *list; + + DBG(""); + + for (list = plugins; list; list = list->next) { + struct near_plugin *plugin = list->data; + + if (plugin->active == TRUE && plugin->desc->exit) + plugin->desc->exit(); + + if (plugin->handle != NULL) + dlclose(plugin->handle); + + g_free(plugin); + } + + g_slist_free(plugins); +} diff --git a/src/tag.c b/src/tag.c new file mode 100644 index 0000000..68b04a4 --- /dev/null +++ b/src/tag.c @@ -0,0 +1,1097 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> + +#include <glib.h> + +#include <gdbus.h> + +#include "near.h" + +#define TYPE3_IDM_LEN 8 +#define TYPE3_ATTR_BLOCK_SIZE 16 + +struct near_tag { + char *path; + + uint32_t adapter_idx; + uint32_t target_idx; + + uint32_t protocol; + uint32_t type; + enum near_tag_sub_type sub_type; + enum near_tag_memory_layout layout; + near_bool_t readonly; + + uint8_t nfcid[NFC_MAX_NFCID1_LEN]; + uint8_t nfcid_len; + + size_t data_length; + uint8_t *data; + + uint32_t n_records; + GList *records; + near_bool_t blank; + + /* Tag specific structures */ + struct { + uint8_t IDm[TYPE3_IDM_LEN]; + uint8_t attr[TYPE3_ATTR_BLOCK_SIZE]; + uint8_t ic_type; + } t3; + + struct { + uint16_t max_ndef_size; + uint16_t c_apdu_max_size; + } t4; + + DBusMessage *write_msg; /* Pending write message */ + struct near_ndef_message *write_ndef; +}; + +static DBusConnection *connection = NULL; + +static GHashTable *tag_hash; + +static GSList *driver_list = NULL; + +struct near_tag *near_tag_get_tag(uint32_t adapter_idx, uint32_t target_idx) +{ + struct near_tag *tag; + char *path; + + path = g_strdup_printf("%s/nfc%d/tag%d", NFC_PATH, + adapter_idx, target_idx); + if (path == NULL) + return NULL; + + tag = g_hash_table_lookup(tag_hash, path); + g_free(path); + + /* TODO refcount */ + return tag; +} + +static void append_records(DBusMessageIter *iter, void *user_data) +{ + struct near_tag *tag = user_data; + GList *list; + + DBG(""); + + for (list = tag->records; list; list = list->next) { + struct near_ndef_record *record = list->data; + char *path; + + path = __near_ndef_record_get_path(record); + if (path == NULL) + continue; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, + &path); + } +} + +static const char *type_string(struct near_tag *tag) +{ + const char *type; + + DBG("type 0x%x", tag->type); + + switch (tag->type) { + case NFC_PROTO_JEWEL: + type = "Type 1"; + break; + + case NFC_PROTO_MIFARE: + type = "Type 2"; + break; + + case NFC_PROTO_FELICA: + type = "Type 3"; + break; + + case NFC_PROTO_ISO14443: + type = "Type 4"; + break; + + default: + type = NULL; + near_error("Unknown tag type 0x%x", tag->type); + break; + } + + return type; +} + +static const char *protocol_string(struct near_tag *tag) +{ + const char *protocol; + + DBG("protocol 0x%x", tag->protocol); + + switch (tag->protocol) { + case NFC_PROTO_FELICA_MASK: + protocol = "Felica"; + break; + + case NFC_PROTO_MIFARE_MASK: + protocol = "MIFARE"; + break; + + case NFC_PROTO_JEWEL_MASK: + protocol = "Jewel"; + break; + + case NFC_PROTO_ISO14443_MASK: + protocol = "ISO-DEP"; + break; + + default: + near_error("Unknown tag protocol 0x%x", tag->protocol); + protocol = NULL; + } + + return protocol; +} + +static DBusMessage *get_properties(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct near_tag *tag = data; + const char *protocol, *type; + DBusMessage *reply; + DBusMessageIter array, dict; + + DBG("conn %p", conn); + + reply = dbus_message_new_method_return(msg); + if (reply == NULL) + return NULL; + + dbus_message_iter_init_append(reply, &array); + + near_dbus_dict_open(&array, &dict); + + type = type_string(tag); + if (type != NULL) + near_dbus_dict_append_basic(&dict, "Type", + DBUS_TYPE_STRING, &type); + + protocol = protocol_string(tag); + if (protocol != NULL) + near_dbus_dict_append_basic(&dict, "Protocol", + DBUS_TYPE_STRING, &protocol); + + near_dbus_dict_append_basic(&dict, "ReadOnly", + DBUS_TYPE_BOOLEAN, &tag->readonly); + + near_dbus_dict_append_array(&dict, "Records", + DBUS_TYPE_OBJECT_PATH, append_records, tag); + + near_dbus_dict_close(&array, &dict); + + return reply; +} + +static DBusMessage *set_property(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + DBG("conn %p", conn); + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + +static void tag_read_cb(uint32_t adapter_idx, uint32_t target_idx, int status) +{ + struct near_tag *tag; + + tag = near_tag_get_tag(adapter_idx, target_idx); + + if (tag == NULL) + return; + + dbus_message_unref(tag->write_msg); + tag->write_msg = NULL; + + __near_adapter_start_check_presence(adapter_idx, target_idx); + + __near_adapter_tags_changed(adapter_idx); +} + +static void write_cb(uint32_t adapter_idx, uint32_t target_idx, int status) +{ + struct near_tag *tag; + DBusConnection *conn; + DBusMessage *reply; + + DBG("Write status %d", status); + + tag = near_tag_get_tag(adapter_idx, target_idx); + if (tag == NULL) + return; + + conn = near_dbus_get_connection(); + if (conn == NULL) + goto out; + + if (status != 0) { + reply = __near_error_failed(tag->write_msg, EINVAL); + if (reply != NULL) + g_dbus_send_message(conn, reply); + } else { + g_dbus_send_reply(conn, tag->write_msg, DBUS_TYPE_INVALID); + } + + near_ndef_records_free(tag->records); + tag->n_records = 0; + tag->records = NULL; + g_free(tag->data); + tag->data = NULL; + + if (status == 0) { + /* + * If writing succeeded, + * check presence will be restored after reading + */ + __near_tag_read(tag, tag_read_cb); + return; + } + +out: + dbus_message_unref(tag->write_msg); + tag->write_msg = NULL; + + __near_adapter_start_check_presence(tag->adapter_idx, tag->target_idx); +} + +static void format_cb(uint32_t adapter_idx, uint32_t target_idx, int status) +{ + struct near_tag *tag; + int err; + + DBG("format status %d", status); + + tag = near_tag_get_tag(adapter_idx, target_idx); + if (tag == NULL) + return; + + if (tag->write_msg == NULL) + return; + + if (status == 0) { + err = __near_tag_write(tag, tag->write_ndef, + write_cb); + if (err < 0) + goto error; + } else { + err = status; + goto error; + } + + return; + +error: + write_cb(tag->adapter_idx, tag->target_idx, err); +} + +static DBusMessage *write_ndef(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct near_tag *tag = data; + struct near_ndef_message *ndef, *ndef_with_header = NULL; + int tlv_len_size, err; + + DBG("conn %p", conn); + + if (tag->readonly == TRUE) { + DBG("Read only tag"); + return __near_error_permission_denied(msg); + } + + if (tag->write_msg) + return __near_error_in_progress(msg); + + ndef = __ndef_build_from_message(msg); + if (ndef == NULL) + return __near_error_failed(msg, EINVAL); + + tag->write_msg = dbus_message_ref(msg); + + /* Add NDEF header information depends upon tag type */ + switch (tag->type) { + case NFC_PROTO_JEWEL: + case NFC_PROTO_MIFARE: + if (ndef->length < 0xff) + tlv_len_size = 3; + else + tlv_len_size = 5; + + ndef_with_header = g_try_malloc0(sizeof( + struct near_ndef_message)); + if (ndef_with_header == NULL) + goto fail; + + ndef_with_header->offset = 0; + ndef_with_header->length = ndef->length + tlv_len_size; + ndef_with_header->data = + g_try_malloc0(ndef->length + tlv_len_size); + if (ndef_with_header->data == NULL) + goto fail; + + ndef_with_header->data[0] = TLV_NDEF; + + if (ndef->length < 0xff) { + ndef_with_header->data[1] = ndef->length; + } else { + ndef_with_header->data[1] = 0xff; + ndef_with_header->data[2] = + (uint8_t)(ndef->length >> 8); + ndef_with_header->data[3] = (uint8_t)(ndef->length); + } + + memcpy(ndef_with_header->data + tlv_len_size - 1, ndef->data, + ndef->length); + ndef_with_header->data[ndef->length + tlv_len_size - 1] = + TLV_END; + break; + + case NFC_PROTO_FELICA: + ndef_with_header = g_try_malloc0(sizeof( + struct near_ndef_message)); + if (ndef_with_header == NULL) + goto fail; + + ndef_with_header->offset = 0; + ndef_with_header->length = ndef->length; + ndef_with_header->data = g_try_malloc0( + ndef_with_header->length); + if (ndef_with_header->data == NULL) + goto fail; + + memcpy(ndef_with_header->data, ndef->data, ndef->length); + + break; + + case NFC_PROTO_ISO14443: + ndef_with_header = g_try_malloc0(sizeof( + struct near_ndef_message)); + if (ndef_with_header == NULL) + goto fail; + + ndef_with_header->offset = 0; + ndef_with_header->length = ndef->length + 2; + ndef_with_header->data = g_try_malloc0(ndef->length + 2); + if (ndef_with_header->data == NULL) + goto fail; + + ndef_with_header->data[0] = (uint8_t)(ndef->length >> 8); + ndef_with_header->data[1] = (uint8_t)(ndef->length); + memcpy(ndef_with_header->data + 2, ndef->data, ndef->length); + + break; + + default: + g_free(ndef->data); + g_free(ndef); + + return __near_error_failed(msg, EOPNOTSUPP); + } + + g_free(ndef->data); + g_free(ndef); + + tag->write_ndef = ndef_with_header; + err = __near_tag_write(tag, ndef_with_header, write_cb); + if (err < 0) { + g_free(ndef_with_header->data); + g_free(ndef_with_header); + + return __near_error_failed(msg, -err); + } + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); + +fail: + dbus_message_unref(tag->write_msg); + tag->write_msg = NULL; + + return __near_error_failed(msg, ENOMEM); +} + +static DBusMessage *get_raw_ndef(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct near_tag *tag = data; + DBusMessage *reply; + DBusMessageIter iter, array; + + DBG(""); + + reply = dbus_message_new_method_return(msg); + if (reply == NULL) + return NULL; + + dbus_message_iter_init_append(reply, &iter); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_BYTE_AS_STRING, + &array); + + __near_ndef_append_records(&array, tag->records); + + dbus_message_iter_close_container(&iter, &array); + + return reply; +} + +static const GDBusMethodTable tag_methods[] = { + { GDBUS_METHOD("GetProperties", + NULL, GDBUS_ARGS({"properties", "a{sv}"}), + get_properties) }, + { GDBUS_METHOD("SetProperty", + GDBUS_ARGS({"name", "s"}, {"value", "v"}), + NULL, set_property) }, + { GDBUS_ASYNC_METHOD("Write", GDBUS_ARGS({"attributes", "a{sv}"}), + NULL, write_ndef) }, + { GDBUS_METHOD("GetRawNDEF", + NULL, GDBUS_ARGS({"NDEF", "ay"}), + get_raw_ndef) }, + { }, +}; + +static const GDBusSignalTable tag_signals[] = { + { GDBUS_SIGNAL("PropertyChanged", + GDBUS_ARGS({"name", "s"}, {"value", "v"})) }, + { } +}; + + +void __near_tag_append_records(struct near_tag *tag, DBusMessageIter *iter) +{ + GList *list; + + for (list = tag->records; list; list = list->next) { + struct near_ndef_record *record = list->data; + char *path; + + path = __near_ndef_record_get_path(record); + if (path == NULL) + continue; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, + &path); + } +} + +#define NFC_TAG_A (NFC_PROTO_ISO14443_MASK | NFC_PROTO_NFC_DEP_MASK | \ + NFC_PROTO_JEWEL_MASK | NFC_PROTO_MIFARE_MASK) +#define NFC_TAG_A_TYPE2 0x00 +#define NFC_TAG_A_TYPE4 0x01 +#define NFC_TAG_A_NFC_DEP 0x02 +#define NFC_TAG_A_TYPE4_DEP 0x03 + +#define NFC_TAG_A_SENS_RES_SSD_JEWEL 0x00 +#define NFC_TAG_A_SENS_RES_PLATCONF_JEWEL 0x0c + +#define NFC_TAG_A_SEL_PROT(sel_res) (((sel_res) & 0x60) >> 5) +#define NFC_TAG_A_SEL_CASCADE(sel_res) (((sel_res) & 0x04) >> 2) +#define NFC_TAG_A_SENS_RES_SSD(sens_res) ((sens_res) & 0x001f) +#define NFC_TAG_A_SENS_RES_PLATCONF(sens_res) (((sens_res) & 0x0f00) >> 8) + +static enum near_tag_sub_type get_tag_type2_sub_type(uint8_t sel_res) +{ + switch(sel_res) { + case 0x00 : + return NEAR_TAG_NFC_T2_MIFARE_ULTRALIGHT; + case 0x08: + return NEAR_TAG_NFC_T2_MIFARE_CLASSIC_1K; + case 0x09: + return NEAR_TAG_NFC_T2_MIFARE_MINI; + case 0x18: + return NEAR_TAG_NFC_T2_MIFARE_CLASSIC_4K; + case 0x20: + return NEAR_TAG_NFC_T2_MIFARE_DESFIRE; + case 0x28 : + return NEAR_TAG_NFC_T2_JCOP30; + case 0x38: + return NEAR_TAG_NFC_T2_MIFARE_4K_EMUL; + case 0x88: + return NEAR_TAG_NFC_T2_MIFARE_1K_INFINEON; + case 0x98: + return NEAR_TAG_NFC_T2_MPCOS; + } + + return NEAR_TAG_NFC_SUBTYPE_UNKNOWN; +} + +static void set_tag_type(struct near_tag *tag, + uint16_t sens_res, uint8_t sel_res) +{ + uint8_t platconf, ssd, proto; + + DBG("protocol 0x%x sens_res 0x%x sel_res 0x%x", tag->protocol, + sens_res, sel_res); + + switch (tag->protocol) { + case NFC_PROTO_JEWEL_MASK: + platconf = NFC_TAG_A_SENS_RES_PLATCONF(sens_res); + ssd = NFC_TAG_A_SENS_RES_SSD(sens_res); + + DBG("Jewel"); + + if ((ssd == NFC_TAG_A_SENS_RES_SSD_JEWEL) && + (platconf == NFC_TAG_A_SENS_RES_PLATCONF_JEWEL)) + tag->type = NFC_PROTO_JEWEL; + break; + + case NFC_PROTO_MIFARE_MASK: + case NFC_PROTO_ISO14443_MASK: + proto = NFC_TAG_A_SEL_PROT(sel_res); + + DBG("proto 0x%x", proto); + + switch(proto) { + case NFC_TAG_A_TYPE2: + tag->type = NFC_PROTO_MIFARE; + tag->sub_type = get_tag_type2_sub_type(sel_res); + break; + case NFC_TAG_A_TYPE4: + tag->type = NFC_PROTO_ISO14443; + break; + case NFC_TAG_A_TYPE4_DEP: + tag->type = NFC_PROTO_NFC_DEP; + break; + } + break; + + case NFC_PROTO_FELICA_MASK: + tag->type = NFC_PROTO_FELICA; + break; + + default: + tag->type = NFC_PROTO_MAX; + break; + } + + DBG("tag type 0x%x", tag->type); +} + +static int tag_initialize(struct near_tag *tag, + uint32_t adapter_idx, uint32_t target_idx, + uint32_t protocols, + uint16_t sens_res, uint8_t sel_res, + uint8_t *nfcid, uint8_t nfcid_len) +{ + DBG(""); + + tag->path = g_strdup_printf("%s/nfc%d/tag%d", NFC_PATH, + adapter_idx, target_idx); + if (tag->path == NULL) + return -ENOMEM; + tag->adapter_idx = adapter_idx; + tag->target_idx = target_idx; + tag->protocol = protocols; + tag->n_records = 0; + tag->readonly = FALSE; + + if (nfcid_len <= NFC_MAX_NFCID1_LEN) { + tag->nfcid_len = nfcid_len; + memcpy(tag->nfcid, nfcid, nfcid_len); + } + + set_tag_type(tag, sens_res, sel_res); + + return 0; +} + +struct near_tag *__near_tag_add(uint32_t adapter_idx, uint32_t target_idx, + uint32_t protocols, + uint16_t sens_res, uint8_t sel_res, + uint8_t *nfcid, uint8_t nfcid_len) +{ + struct near_tag *tag; + char *path; + + tag = near_tag_get_tag(adapter_idx, target_idx); + if (tag != NULL) + return NULL; + + tag = g_try_malloc0(sizeof(struct near_tag)); + if (tag == NULL) + return NULL; + + if (tag_initialize(tag, adapter_idx, target_idx, + protocols, + sens_res, sel_res, + nfcid, nfcid_len) < 0) { + g_free(tag); + return NULL; + } + + path = g_strdup(tag->path); + if (path == NULL) { + g_free(tag); + return NULL; + } + + g_hash_table_insert(tag_hash, path, tag); + + DBG("connection %p", connection); + + g_dbus_register_interface(connection, tag->path, + NFC_TAG_INTERFACE, + tag_methods, tag_signals, + NULL, tag, NULL); + + return tag; +} + +void __near_tag_remove(struct near_tag *tag) +{ + char *path = tag->path; + + DBG("path %s", tag->path); + + if (g_hash_table_lookup(tag_hash, tag->path) == NULL) + return; + + g_dbus_unregister_interface(connection, tag->path, + NFC_TAG_INTERFACE); + + g_hash_table_remove(tag_hash, path); +} + +const char *__near_tag_get_path(struct near_tag *tag) +{ + return tag->path; +} + +uint32_t __near_tag_get_type(struct near_tag *tag) +{ + return tag->type; +} + +enum near_tag_sub_type near_tag_get_subtype(uint32_t adapter_idx, + uint32_t target_idx) + +{ + struct near_tag *tag; + + tag = near_tag_get_tag(adapter_idx, target_idx); + if (tag == NULL) + return NEAR_TAG_NFC_SUBTYPE_UNKNOWN; + + return tag->sub_type; +} + +uint8_t *near_tag_get_nfcid(uint32_t adapter_idx, uint32_t target_idx, + uint8_t *nfcid_len) +{ + struct near_tag *tag; + uint8_t *nfcid; + + tag = near_tag_get_tag(adapter_idx, target_idx); + if (tag == NULL) + goto fail; + + nfcid = g_try_malloc0(tag->nfcid_len); + if (nfcid == NULL) + goto fail; + + memcpy(nfcid, tag->nfcid, tag->nfcid_len); + *nfcid_len = tag->nfcid_len; + + return nfcid; + +fail: + *nfcid_len = 0; + return NULL; +} + +int near_tag_set_nfcid(uint32_t adapter_idx, uint32_t target_idx, + uint8_t *nfcid, size_t nfcid_len) +{ + struct near_tag *tag; + + DBG("NFCID len %zd", nfcid_len); + + tag = near_tag_get_tag(adapter_idx, target_idx); + if (tag == NULL) + return -ENODEV; + + if (tag->nfcid_len > 0) + return -EALREADY; + + if (nfcid_len > NFC_MAX_NFCID1_LEN) + return -EINVAL; + + memcpy(tag->nfcid, nfcid, nfcid_len); + tag->nfcid_len = nfcid_len; + + return 0; +} + +int near_tag_add_data(uint32_t adapter_idx, uint32_t target_idx, + uint8_t *data, size_t data_length) +{ + struct near_tag *tag; + + tag = near_tag_get_tag(adapter_idx, target_idx); + if (tag == NULL) + return -ENODEV; + + tag->data_length = data_length; + tag->data = g_try_malloc0(data_length); + if (tag->data == NULL) + return -ENOMEM; + + if (data != NULL) + memcpy(tag->data, data, data_length); + + return 0; +} + +int near_tag_add_records(struct near_tag *tag, GList *records, + near_tag_io_cb cb, int status) +{ + GList *list; + struct near_ndef_record *record; + char *path; + + DBG("records %p", records); + + for (list = records; list; list = list->next) { + record = list->data; + + path = g_strdup_printf("%s/nfc%d/tag%d/record%d", + NFC_PATH, tag->adapter_idx, + tag->target_idx, tag->n_records); + + if (path == NULL) + continue; + + __near_ndef_record_register(record, path); + + tag->n_records++; + tag->records = g_list_append(tag->records, record); + } + + __near_agent_ndef_parse_records(tag->records); + + if (cb != NULL) + cb(tag->adapter_idx, tag->target_idx, status); + + g_list_free(records); + + return 0; +} + +void near_tag_set_ro(struct near_tag *tag, near_bool_t readonly) +{ + tag->readonly = readonly; +} + +void near_tag_set_blank(struct near_tag *tag, near_bool_t blank) +{ + tag->blank = blank; +} + +near_bool_t near_tag_get_blank(struct near_tag *tag) +{ + return tag->blank; +} + +uint8_t *near_tag_get_data(struct near_tag *tag, size_t *data_length) +{ + if (data_length == NULL) + return NULL; + + *data_length = tag->data_length; + + return tag->data; +} + +uint32_t near_tag_get_adapter_idx(struct near_tag *tag) +{ + return tag->adapter_idx; +} + +uint32_t near_tag_get_target_idx(struct near_tag *tag) +{ + return tag->target_idx; +} + +enum near_tag_memory_layout near_tag_get_memory_layout(struct near_tag *tag) +{ + if (tag == NULL) + return NEAR_TAG_MEMORY_UNKNOWN; + + return tag->layout; +} + +void near_tag_set_memory_layout(struct near_tag *tag, + enum near_tag_memory_layout layout) +{ + if (tag == NULL) + return; + + tag->layout = layout; +} + +void near_tag_set_max_ndef_size(struct near_tag *tag, uint16_t size) +{ + if (tag == NULL) + return; + + tag->t4.max_ndef_size = size; +} + +uint16_t near_tag_get_max_ndef_size(struct near_tag *tag) +{ + if (tag == NULL) + return 0; + + return tag->t4.max_ndef_size; +} + +void near_tag_set_c_apdu_max_size(struct near_tag *tag, uint16_t size) +{ + if (tag == NULL) + return; + + tag->t4.c_apdu_max_size = size; +} + +uint16_t near_tag_get_c_apdu_max_size(struct near_tag *tag) +{ + if (tag == NULL) + return 0; + + return tag->t4.c_apdu_max_size; +} + +void near_tag_set_idm(struct near_tag *tag, uint8_t *idm, uint8_t len) +{ + if (tag == NULL || len > TYPE3_IDM_LEN) + return; + + memset(tag->t3.IDm, 0, TYPE3_IDM_LEN); + memcpy(tag->t3.IDm, idm, len); +} + +uint8_t *near_tag_get_idm(struct near_tag *tag, uint8_t *len) +{ + if (tag == NULL || len == NULL) + return NULL; + + *len = TYPE3_IDM_LEN; + return tag->t3.IDm; +} + +void near_tag_set_attr_block(struct near_tag *tag, uint8_t *attr, uint8_t len) +{ + if (tag == NULL || len > TYPE3_ATTR_BLOCK_SIZE) + return; + + memset(tag->t3.attr, 0, TYPE3_ATTR_BLOCK_SIZE); + memcpy(tag->t3.attr, attr, len); +} + +uint8_t *near_tag_get_attr_block(struct near_tag *tag, uint8_t *len) +{ + if (tag == NULL || len == NULL) + return NULL; + + *len = TYPE3_ATTR_BLOCK_SIZE; + return tag->t3.attr; +} + +void near_tag_set_ic_type(struct near_tag *tag, uint8_t ic_type) +{ + if (tag == NULL) + return; + + tag->t3.ic_type = ic_type; +} + +uint8_t near_tag_get_ic_type(struct near_tag *tag) +{ + if (tag == NULL) + return 0; + + return tag->t3.ic_type; +} + +static gint cmp_prio(gconstpointer a, gconstpointer b) +{ + const struct near_tag_driver *driver1 = a; + const struct near_tag_driver *driver2 = b; + + return driver2->priority - driver1->priority; +} + +int near_tag_driver_register(struct near_tag_driver *driver) +{ + DBG(""); + + if (driver->read == NULL) + return -EINVAL; + + driver_list = g_slist_insert_sorted(driver_list, driver, cmp_prio); + + return 0; +} + +void near_tag_driver_unregister(struct near_tag_driver *driver) +{ + DBG(""); + + driver_list = g_slist_remove(driver_list, driver); +} + +int __near_tag_read(struct near_tag *tag, near_tag_io_cb cb) +{ + GSList *list; + + DBG("type 0x%x", tag->type); + + /* Stop check presence while reading */ + __near_adapter_stop_check_presence(tag->adapter_idx, tag->target_idx); + + for (list = driver_list; list; list = list->next) { + struct near_tag_driver *driver = list->data; + + DBG("driver type 0x%x", driver->type); + + if (driver->type == tag->type) + return driver->read(tag->adapter_idx, tag->target_idx, + cb); + } + + return 0; +} + +int __near_tag_write(struct near_tag *tag, + struct near_ndef_message *ndef, + near_tag_io_cb cb) +{ + GSList *list; + int err; + + DBG("type 0x%x", tag->type); + + for (list = driver_list; list; list = list->next) { + struct near_tag_driver *driver = list->data; + + DBG("driver type 0x%x", driver->type); + + if (driver->type == tag->type) { + /* Stop check presence while writing */ + __near_adapter_stop_check_presence(tag->adapter_idx, + tag->target_idx); + + if (tag->blank == TRUE && driver->format != NULL) { + DBG("Blank tag detected, formatting"); + err = driver->format(tag->adapter_idx, + tag->target_idx, format_cb); + } else { + err = driver->write(tag->adapter_idx, + tag->target_idx, ndef, + cb); + } + + break; + } + } + + if (list == NULL) + err = -EOPNOTSUPP; + + if (err < 0) + __near_adapter_start_check_presence(tag->adapter_idx, + tag->target_idx); + + return err; +} + +int __near_tag_check_presence(struct near_tag *tag, near_tag_io_cb cb) +{ + GSList *list; + + DBG("type 0x%x", tag->type); + + for (list = driver_list; list; list = list->next) { + struct near_tag_driver *driver = list->data; + + DBG("driver type 0x%x", driver->type); + + if (driver->type == tag->type) { + if (driver->check_presence == NULL) + continue; + + return driver->check_presence(tag->adapter_idx, tag->target_idx, cb); + } + } + + return -EOPNOTSUPP; +} + +static void free_tag(gpointer data) +{ + struct near_tag *tag = data; + + DBG("tag %p", tag); + + near_ndef_records_free(tag->records); + + g_free(tag->path); + g_free(tag->data); + g_free(tag); +} + +int __near_tag_init(void) +{ + DBG(""); + + connection = near_dbus_get_connection(); + + tag_hash = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, free_tag); + + return 0; +} + +void __near_tag_cleanup(void) +{ + DBG(""); + + g_hash_table_destroy(tag_hash); + tag_hash = NULL; +} diff --git a/src/tlv.c b/src/tlv.c new file mode 100644 index 0000000..d16ddb0 --- /dev/null +++ b/src/tlv.c @@ -0,0 +1,115 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "near.h" + +uint16_t near_tlv_length(uint8_t *tlv) +{ + uint16_t length; + + if (tlv[0] == TLV_NULL || tlv[0] == TLV_END) + length = 0; + else if (tlv[1] == 0xff) + length = g_ntohs(*(uint16_t *)(tlv + 2)); + else + length = tlv[1]; + + return length; +} + +uint8_t *near_tlv_next(uint8_t *tlv) +{ + uint16_t length; + uint8_t l_length; + + length = near_tlv_length(tlv); + if (length > 0xfe) + l_length = 3; + else if (length == 0) + l_length = 0; + else + l_length = 1; + + /* T (1 byte) + L (1 or 3 bytes) + V */ + return tlv + 1 + l_length + length; +} + +uint8_t *near_tlv_data(uint8_t *tlv) +{ + uint16_t length; + uint8_t l_length; + + length = near_tlv_length(tlv); + if (length > 0xfe) + l_length = 3; + else if (length == 0) + l_length = 0; + else + l_length = 1; + + /* T (1 byte) + L (1 or 3 bytes) */ + return tlv + 1 + l_length; +} + +GList *near_tlv_parse(uint8_t *tlv, size_t tlv_length) +{ + GList *records; + uint8_t *data, t; + + DBG(""); + + records = NULL; + data = tlv; + + while(1) { + t = tlv[0]; + + DBG("tlv 0x%x", tlv[0]); + + switch (t) { + case TLV_NDEF: + DBG("NDEF found %d bytes long", near_tlv_length(tlv)); + + records = near_ndef_parse_msg(near_tlv_data(tlv), + near_tlv_length(tlv)); + + break; + case TLV_END: + break; + } + + if (t == TLV_END) + break; + + tlv = near_tlv_next(tlv); + + if (tlv - data >= (uint16_t) tlv_length) + break; + } + + DBG("Done"); + + return records; +} diff --git a/test/bt-handover b/test/bt-handover new file mode 100755 index 0000000..95b6dbf --- /dev/null +++ b/test/bt-handover @@ -0,0 +1,69 @@ +#!/usr/bin/python + +import os +import sys +import gobject + +import dbus +import dbus.mainloop.glib + +from dbus.lowlevel import MethodCallMessage, HANDLER_RESULT_NOT_YET_HANDLED + +mainloop = gobject.MainLoop() + +def property_changed_adapter(name, value, path): + if name in ["Devices"]: + if (len(value) == 0): + print "Lost device, exiting" + mainloop.quit() + else: + device_path = value[0] + + print "Pairing with %s" % (device_path) + + device = dbus.Interface(bus.get_object("org.neard", device_path), "org.neard.Device") + device.Push(({ "Type" : "Handover", "Carrier" : "bluetooth"})) + +if __name__ == '__main__': + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + + bus = dbus.SystemBus() + + bluez_manager = dbus.Interface(bus.get_object("org.bluez", "/"), "org.bluez.Manager") + + bluez_adapter_path = bluez_manager.DefaultAdapter() + + bluez_adapter = dbus.Interface(bus.get_object("org.bluez", bluez_adapter_path), + "org.bluez.Adapter") + + for bluez_path in bluez_adapter.ListDevices(): + print("Removing %s" % (bluez_path)) + bluez_adapter.RemoveDevice(bluez_path) + + + manager = dbus.Interface(bus.get_object("org.neard", "/"), + "org.neard.Manager") + + properties = manager.GetProperties() + device_path = properties["Adapters"][0] + adapter = dbus.Interface(bus.get_object("org.neard", device_path), + "org.neard.Adapter") + + adapter_properties = adapter.GetProperties() + + for key in adapter_properties.keys(): + if key in ["Polling"]: + if adapter_properties[key] == dbus.Boolean(1): + print "Stoping poll on %s" % (device_path) + adapter.StopPollLoop() + + print "Starting poll on %s" % (device_path) + adapter.StartPollLoop("Initiator") + + bus.add_signal_receiver(property_changed_adapter, + bus_name="org.neard", + dbus_interface="org.neard.Adapter", + signal_name = "PropertyChanged", + path_keyword="path") + + mainloop.run() diff --git a/test/disable-adapter b/test/disable-adapter new file mode 100755 index 0000000..fe514ff --- /dev/null +++ b/test/disable-adapter @@ -0,0 +1,23 @@ +#!/usr/bin/python + +import sys +import dbus + +if len(sys.argv) < 2: + print "Usage: %s <nfc device>" % (sys.argv[0]) + sys.exit(1) + +bus = dbus.SystemBus() + +manager = dbus.Interface(bus.get_object("org.neard", "/"), + "org.neard.Manager") + + +path = "/org/neard/" + sys.argv[1] +adapter = dbus.Interface(bus.get_object("org.neard", path), + "org.neard.Adapter") + +try: + adapter.SetProperty("Powered", dbus.Boolean(0), timeout = 10) +except dbus.DBusException, error: + print "%s: %s" % (error._dbus_error_name, error.message)
\ No newline at end of file diff --git a/test/dump-device b/test/dump-device new file mode 100755 index 0000000..58b090c --- /dev/null +++ b/test/dump-device @@ -0,0 +1,30 @@ +#!/usr/bin/python + +import sys +import dbus + +bus = dbus.SystemBus() + +def extract_record(key, list): + for i in list: + record = dbus.Interface(bus.get_object("org.neard", i), + "org.neard.Record") + + properties = record.GetProperties() + print " Record = [ %s ]" % (str(i)) + + for key in properties.keys(): + val = str(properties[key]) + print " %s = %s" % (key, val) + +device = dbus.Interface(bus.get_object("org.neard", sys.argv[1]), + "org.neard.Device") + +properties = device.GetProperties() + +print "[ %s ]" % (sys.argv[1]) + +for key in properties.keys(): + if key in ["Records"]: + extract_record(key, properties[key]) + diff --git a/test/dump-record b/test/dump-record new file mode 100755 index 0000000..8cf3cb1 --- /dev/null +++ b/test/dump-record @@ -0,0 +1,24 @@ +#!/usr/bin/python + +import sys +import dbus + +def extract_list(list): + val = "[" + for i in list: + val += " " + str(i) + val += " ]" + return val + +bus = dbus.SystemBus() + +record = dbus.Interface(bus.get_object("org.neard", sys.argv[1]), + "org.neard.Record") + +properties = record.GetProperties() + +print "[ %s ]" % (sys.argv[1]) + +for key in properties.keys(): + val = str(properties[key]) + print " %s = %s" % (key, val) diff --git a/test/dump-tag b/test/dump-tag new file mode 100755 index 0000000..4e987fc --- /dev/null +++ b/test/dump-tag @@ -0,0 +1,48 @@ +#!/usr/bin/python + +import sys +import dbus + +bus = dbus.SystemBus() + +def extract_ndef(list): + val = "[" + for i in list: + val += " 0x%x" % i + val += " ]" + + return val + +def extract_record(key, list): + for i in list: + record = dbus.Interface(bus.get_object("org.neard", i), + "org.neard.Record") + + properties = record.GetProperties() + print " Record = [ %s ]" % (str(i)) + + for key in properties.keys(): + val = str(properties[key]) + print " %s = %s" % (key, val) + +tag = dbus.Interface(bus.get_object("org.neard", sys.argv[1]), + "org.neard.Tag") + +properties = tag.GetProperties() +raw_ndef = tag.GetRawNDEF() + +print "[ %s ]" % (sys.argv[1]) + +print " Raw NDEF = %s" % (extract_ndef(raw_ndef)) + +for key in properties.keys(): + if key in ["Type"]: + val = str(properties[key]) + print " %s = %s" % (key, val) + elif key in ["Protocol"]: + val = str(properties[key]) + print " %s = %s" % (key, val) + + if key in ["Records"]: + extract_record(key, properties[key]) + diff --git a/test/enable-adapter b/test/enable-adapter new file mode 100755 index 0000000..bd6f03a --- /dev/null +++ b/test/enable-adapter @@ -0,0 +1,23 @@ +#!/usr/bin/python + +import sys +import dbus + +if len(sys.argv) < 2: + print "Usage: %s <nfc device>" % (sys.argv[0]) + sys.exit(1) + +bus = dbus.SystemBus() + +manager = dbus.Interface(bus.get_object("org.neard", "/"), + "org.neard.Manager") + + +path = "/org/neard/" + sys.argv[1] +adapter = dbus.Interface(bus.get_object("org.neard", path), + "org.neard.Adapter") + +try: + adapter.SetProperty("Powered", dbus.Boolean(1), timeout = 10) +except dbus.DBusException, error: + print "%s: %s" % (error._dbus_error_name, error.message) diff --git a/test/list-adapters b/test/list-adapters new file mode 100755 index 0000000..9e11322 --- /dev/null +++ b/test/list-adapters @@ -0,0 +1,40 @@ +#!/usr/bin/python + +import dbus + + +def extract_list(list): + val = "[" + for i in list: + val += " " + str(i) + val += " ]" + return val + +bus = dbus.SystemBus() + +manager = dbus.Interface(bus.get_object("org.neard", "/"), + "org.neard.Manager") + + +properties = manager.GetProperties() + +for path in properties["Adapters"]: + print "[ %s ]" % (path) + + adapter = dbus.Interface(bus.get_object("org.neard", path), + "org.neard.Adapter") + + properties = adapter.GetProperties() + + for key in properties.keys(): + if key in ["Powered", "Polling"]: + if properties[key] == dbus.Boolean(1): + val = "true" + else: + val = "false" + elif key in ["Protocols", "Tags", "Devices"]: + val = extract_list(properties[key]) + else: + val = str(properties[key]) + + print " %s = %s" % (key, val) diff --git a/test/monitor-near b/test/monitor-near new file mode 100755 index 0000000..fef307e --- /dev/null +++ b/test/monitor-near @@ -0,0 +1,62 @@ +#!/usr/bin/python + +import gobject + +import dbus +import dbus.mainloop.glib + +from dbus.lowlevel import MethodCallMessage, HANDLER_RESULT_NOT_YET_HANDLED + +def extract_list(list): + val = "[" + for i in list: + val += " " + str(i) + val += " ]" + return val + +def extract_bool(b): + if b == dbus.Boolean(1): + val = "true" + else: + val = "false" + return val + + +def property_changed_adapter(name, value, path): + adapter = path[path.rfind("/") + 1:] + if name in ["Polling"]: + val = extract_bool(value) + elif name in ["Tags", "Devices"]: + val = extract_list(value) + else: + val = str(value) + + print "[Adapter] [%s] %s = %s" % (adapter, name, val) + +def property_changed_manager(name, value, path): + manager = path[path.rfind("/") + 1:] + if name in ["Adapters"]: + val = extract_list(value) + + print "[Manager] %s = %s" % (name, val) + + +if __name__ == '__main__': + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + + bus = dbus.SystemBus() + + bus.add_signal_receiver(property_changed_manager, + bus_name="org.neard", + dbus_interface="org.neard.Manager", + signal_name = "PropertyChanged", + path_keyword="path") + + bus.add_signal_receiver(property_changed_adapter, + bus_name="org.neard", + dbus_interface="org.neard.Adapter", + signal_name = "PropertyChanged", + path_keyword="path") + + mainloop = gobject.MainLoop() + mainloop.run() diff --git a/test/neard-ui.py b/test/neard-ui.py new file mode 100755 index 0000000..1113ca8 --- /dev/null +++ b/test/neard-ui.py @@ -0,0 +1,586 @@ +#!/usr/bin/env python + +import pdb +import sys + +import gobject +import gtk + +import dbus +import dbus.service +import dbus.mainloop.glib + +import traceback + +##================================================================= +class Neard: + + #signal handler #1 for Manager + def manager_Added(self, name): + print (" Added %s") % name + self.manager_getDetails() + + #signal handler #2 for Manager + def manager_Removed(self, name): + print (" Removed %s") % name + if self.adapters_remove is not None: + self.adapter_update(name) + return + + #connect to the manager in order to be notified on + #add/remove adapter + def manager_Connect(self): + try: + manager_obj = self.systemBus.get_object('org.neard', "/") + # Add 2 handlers to follow Adapters + manager_obj.connect_to_signal('AdapterAdded', + self.manager_Added, + 'org.neard.Manager') + manager_obj.connect_to_signal('AdapterRemoved', + self.manager_Removed, + 'org.neard.Manager') + self.iface_manager = dbus.Interface(manager_obj, 'org.neard.Manager') + + except: + print ("Can't connect to org.neard.Manager"); + self.iface_manager = None + #Retrieve the manager informations + self.manager_getDetails() + + def manager_getDetails(self): + #No iface_manager means adapter removed + if self.iface_manager is None: + if self.adapters_remove is not None: + self.adapters_remove() + return + #Need to update adapter's details + self.adapter_updateDetails(self.iface_manager.GetProperties()) + + def adapter_PropertyChanged(self, prop, value, adapt_path = None): + print("Prop changed: %s") % prop + adapt_properties = {} + adapt_properties[prop] = value + if prop == "Tags": + self.tag_updateDetails(adapt_properties) + else: + self.adapter_update(adapt_path, adapt_properties) + + #Update the records UI + def record_updateDetails(self, tag_properties): + for record_path in tag_properties["Records"]: + print ("REC %s ") % record_path + record_obj = self.systemBus.get_object('org.neard', + record_path) + record_iface = dbus.Interface(record_obj,'org.neard.Record') + record_properties = record_iface.GetProperties() + + self.recordregistered[record_path] = True + + # call UI update + self.records_update(record_path, record_properties) + + #Update the tags UI + def tag_updateDetails(self, adapt_properties): + if adapt_properties["Tags"]: + for tag_path in adapt_properties["Tags"]: + print ("TAG %s ") % tag_path + tag_obj = self.systemBus.get_object('org.neard', tag_path) + + tag_iface = dbus.Interface(tag_obj,'org.neard.Tag') + tag_properties = tag_iface.GetProperties() + + self.tagregistered[tag_path] = True + # call UI update + self.tags_update(tag_path, tag_properties) + #Process the records + self.record_updateDetails(tag_properties) + else: + print ("remove tags and records") + self.tags_update() + self.records_update() + + + #Something changed, must update the UI + def adapter_updateDetails(self, properties): + if self.adapter_update is not None and "Adapters" in properties: + for adapt_path in properties["Adapters"]: + if adapt_path in self.adaptregistered: + print (" already registered %s") % adapt_path + else: + #Get valuable informations from the object + adapter_obj = self.systemBus.get_object('org.neard', + adapt_path) + adapter_obj.connect_to_signal('PropertyChanged', + self.adapter_PropertyChanged, + 'org.neard.Adapter', + path_keyword='adapt_path') + + adapt_iface = dbus.Interface(adapter_obj,'org.neard.Adapter') + adapt_properties = adapt_iface.GetProperties() + + self.adaptregistered[adapt_path] = True + + #Update display info + self.adapter_update(adapt_path, adapt_properties) + + #udpate UI for tags + self.tag_updateDetails(adapt_properties) + + + #Search DBUS to find any neard instance + def neardNameOwnerChanged(self, proxy): + if proxy: + print("Neard is connected to System bus") + self.manager_Connect() + else: + print("Neard is disconnected from System bus") + self.iface_manager = None + self.adaptregistered = {} + self.manager_getDetails() + + #Main init function + def __init__(self, adapter_update = None, adapters_remove = None, + tags_update = None, records_update = None): + self.test = False + self.adapter_update = adapter_update + self.adapters_remove = adapters_remove + self.tags_update = tags_update + self.records_update = records_update + + self.adaptregistered = {} + self.tagregistered = {} + self.recordregistered = {} + + self.systemBus = dbus.SystemBus() + + #Prepare the first handler + self.systemBus.watch_name_owner('org.neard', + self.neardNameOwnerChanged) + +##================================================================= +class NeardUI(Neard): + + # return the current selection + def adapters_actionToggle(self, i, col): + if i: + return self.adapters_list.get_value(i, col) + return True + + # Action: activate or not the polling mode + def adapter_pollingToggled(self, poolingRendererToggle, path, user): + if path: + i = self.adapters_list.get_iter(path) + objpath = self.adapters_list.get_value(i, 0) + adapter_obj = self.systemBus.get_object('org.neard', objpath) + adapt_iface = dbus.Interface(adapter_obj,'org.neard.Adapter') + + if self.adapters_actionToggle(i, 3): + print ("Stop Polling %s") % objpath + adapt_iface.StopPollLoop() + else: + print ("Start Polling %s") % objpath + adapt_iface.StartPollLoop("Initiator") + + + #------------------------------ + #Set the field values + def adapters_setUIList(self, adapt_properties, i, col, name, path = None): + if name in adapt_properties: + value = adapt_properties[name] + + if name == "Tags": + value = "{" + for tgts in adapt_properties[name]: + #For each tag, add it to the tag lbox: + #Trim path.... + short_tgt = tgts[len(path)+1:] + + if value == "{": + value = "{" + short_tgt + else: + value = value + "," + short_tgt + value = value + "}" + + if name == "Protocols": + value = None + for protos in adapt_properties[name]: + if value is None: + value = protos + else: + value = value + " " + protos + + if value is not None: + self.adapters_list.set_value(i, col, value) + print (" property %s, value %s") % (name, value) + + # Clear one or all the adapters present in list + def adapter_RemoveUI(self): + self.adapters_list.clear() + + #Add, Update or delete a list entry + def adapter_UpdateUI(self, path = None, adapt_properties = None): + i = self.adapters_list.get_iter_first() + while i is not None: + if self.adapters_list.get_value(i, 0) == path: + break + i = self.adapters_list.iter_next(i) + + if adapt_properties is None: + if i: + print ("Delete adapter %s") % path + self.adapters_list.remove(i) + else: + print ("Already deleted adapter %s") % path + return + + if i is None: + i = self.adapters_list.append() + self.adapters_list.set_value(i, 0, path) + print ("Add adapter %s") % (path) + else: + print ("Update adapter %s") % (path) + + + self.adapters_setUIList(adapt_properties, i, 2, "Powered") + self.adapters_setUIList(adapt_properties, i, 3, "Polling") + self.adapters_setUIList(adapt_properties, i, 4, "Protocols") + self.adapters_setUIList(adapt_properties, i, 5, "Tags", path) + + #-------------------------------------------------- + def tags_setUIList(self, tag_properties, i, col, name): + if name in tag_properties: + value = tag_properties[name] + + if name == "Records": + value = None + for tags in tag_properties[name]: + #For each tag, add it to the tag lbox: + if value is None: + value = tags + else: + value = value + "-" + tags + + if name == "Type": + value = None + for tags in tag_properties[name]: + #For each tag, add it to the tag lbox: + if value is None: + value = tags + else: + value = value + "-" + tags + + if value is not None: + self.tags_list.set_value(i, col, value) + print (" property %s, value %s") % (name, value) + + #Add, Update or delete a list entry + def tag_UpdateUI(self, path = None, tag_properties = None): + print("Tag Update %s") % path + i = self.tags_list.get_iter_first() + while i is not None: + if self.tags_list.get_value(i, 0) == path: + break + i = self.tags_list.iter_next(i) + + #Delete mode: Remove all + if tag_properties is None: + i = self.tags_list.get_iter_first() + while i is not None: + path_name = self.tags_list.get_value(i, 0) + print ("Deleted tag %s") % path_name + self.tags_list.remove(i) + if self.tags_list.iter_is_valid(i): + i = self.tags_list.iter_next(i) + else: + i = None + return + + if i is None: + i = self.tags_list.append() + self.tags_list.set_value(i, 0, path) + print ("Add tag %s") % (path) + else: + print ("Update tag %s") % (path) + self.tags_setUIList(tag_properties, i, 2, "ReadOnly") + self.tags_setUIList(tag_properties, i, 3, "Type") + self.tags_setUIList(tag_properties, i, 4, "Records") + + #-------------------------------------------------- + def records_setUIList(self, record_properties, i, col, name): + if name in record_properties: + value = record_properties[name] + else: + value = "" + for rec_data in record_properties: + if rec_data != "Type": + if value != "": + value = value + "\n" + value = value + rec_data + " : " +record_properties[rec_data] + + if value is not None: + self.records_list.set_value(i, col, value) + print (" property %s, value %s") % (name, value) + + #Add, Update or delete a list entry + def record_UpdateUI(self, path = None, record_properties = None): + print("Record Update %s") % path + i = self.records_list.get_iter_first() + while i is not None: + if self.records_list.get_value(i, 0) == path: + break + i = self.records_list.iter_next(i) + + #Delete mode: Remove all records + if record_properties is None: + i = self.records_list.get_iter_first() + while i is not None: + path_name = self.records_list.get_value(i, 0) + print ("Delete record %s") % path_name + self.records_list.remove(i) + if self.records_list.iter_is_valid(i): + i = self.records_list.iter_next(i) + else: + i = None + return + + if i is None: + i = self.records_list.append() + self.records_list.set_value(i, 0, path) + print ("Add record %s") % (path) + else: + print ("Update record %s") % (path) + + self.records_setUIList(record_properties, i, 2, "Type") + self.records_setUIList(record_properties, i, 3, "Data") + + + def tag_RemoveUI(self): + printf("Tag Remove") + + #Adapter selected on lbox + def on_adapter_sel_changed(self, selection = None): + model, iter = selection.get_selected() + if iter: + value = self.adapters_list.get_value(iter, 0) + print ("value %s") % value + value = self.adapters_list.get_value(iter, 5) + print ("tag: %s") % value + + + #----------------------------------------------------- + # Prepare TreeView for Adapters + def createAdaptersWidgets(self, adaptlist): + + #treeview adapters + tv_adapters = gtk.TreeView(adaptlist) + + column = gtk.TreeViewColumn("Path", gtk.CellRendererText(), text=0) + tv_adapters.append_column(column) + + toggle = gtk.CellRendererToggle() + column = gtk.TreeViewColumn("Powered", toggle, active=2) + tv_adapters.append_column(column) + + toggle = gtk.CellRendererToggle() + column = gtk.TreeViewColumn("Polling", toggle, active=3) + toggle.connect('toggled', self.adapter_pollingToggled, None) + tv_adapters.append_column(column) + + column = gtk.TreeViewColumn("Protocols",gtk.CellRendererText(), text=4) + tv_adapters.append_column(column) + + column = gtk.TreeViewColumn("Tags", gtk.CellRendererText(), text=5) + tv_adapters.append_column(column) + + tv_adapters.get_selection().connect("changed", + self.on_adapter_sel_changed) + + return tv_adapters; + + # Prepare TreeView for Tags + def createTagsWidgets(self, tags_list): + + + tv_tags = gtk.TreeView(tags_list) + + column = gtk.TreeViewColumn("Path", gtk.CellRendererText(), text=0) + tv_tags.append_column(column) + toggle = gtk.CellRendererToggle() + column = gtk.TreeViewColumn("ReadOnly", toggle, active=2) + tv_tags.append_column(column) + + column = gtk.TreeViewColumn("Type", gtk.CellRendererText(), text=3) + tv_tags.append_column(column) + + column = gtk.TreeViewColumn("Record", gtk.CellRendererText(), text=4) + tv_tags.append_column(column) + + return tv_tags;# + + # Prepare TreeView for Records + def createRecordsWidgets(self, records_list): + #treeview Records + tv_records = gtk.TreeView(records_list) + tv_records.connect("row-activated", self.on_record_activated) + + column = gtk.TreeViewColumn("Path", gtk.CellRendererText(), text=0) + tv_records.append_column(column) + + column = gtk.TreeViewColumn("Type", gtk.CellRendererText(), text=2) + tv_records.append_column(column) + + column = gtk.TreeViewColumn("Data", gtk.CellRendererText(), text=3) + tv_records.append_column(column) + return tv_records; + + def on_record_activated(self, widget, row, col): + model = widget.get_model() + recordUI = RecordUI(self.neardDialog, model[row][0], model[row][2]) + recordUI.show() + + def dlg_onResponse(self, dialog, response): + self.neardDialog.destroy() + self.neardDialog = None + if self.response_callback is not None: + self.response_callback(response) + + #------------------------------ + #Prepare the dialog + def createDialog(self, title = None): + if self.title is not None: + title = self.title + dialog = gtk.Dialog(title, None, + gtk.DIALOG_MODAL | + gtk.DIALOG_DESTROY_WITH_PARENT, + (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) + dialog.set_property("resizable", True) + dialog.set_default_size(800, 300) + + notebook = gtk.Notebook() + dialog.child.add(notebook) + + # Create the first tab...an adapters's list + scrolledwindow = gtk.ScrolledWindow() + widget = self.createAdaptersWidgets(self.adapters_list) + scrolledwindow.add(widget) +# notebook.append_page(widget, gtk.Label("Adapters")) + notebook.append_page(scrolledwindow, gtk.Label("Adapters")) + + scrolledwindow = gtk.ScrolledWindow() + widget = self.createTagsWidgets(self.tags_list) + scrolledwindow.add(widget) + + notebook.append_page(scrolledwindow, gtk.Label("Tags")) + + scrolledwindow = gtk.ScrolledWindow() + widget = self.createRecordsWidgets(self.records_list) + scrolledwindow.add(widget) + notebook.append_page(scrolledwindow, gtk.Label("Records")) + + dialog.connect('response', self.dlg_onResponse) +# dialog.vbox.pack_end(vbox, True, True, 0) + return dialog + + def show(self): + if self.neardDialog is None: + self.neardDialog = self.createDialog() + self.neardDialog.show_all() + + def __init__(self, title = None, response_callback = None): + self.title = title + self.response_callback = response_callback + self.neardDialog = None + + self.adapters_list = gtk.ListStore(gobject.TYPE_STRING, # path =0 + gobject.TYPE_STRING, # Name = 1 + gobject.TYPE_BOOLEAN, # powered = 2 + gobject.TYPE_BOOLEAN, # polling = 3 + gobject.TYPE_STRING, # protocols = 4 + gobject.TYPE_STRING) # tags = 5 + + self.tags_list = gtk.ListStore(gobject.TYPE_STRING, # path =0 + gobject.TYPE_STRING, # Name = 1 + gobject.TYPE_BOOLEAN, # Read Only 2 + gobject.TYPE_STRING, # Type = 3 + gobject.TYPE_STRING) # Record = 4 + + self.records_list = gtk.ListStore(gobject.TYPE_STRING, # path =0 + gobject.TYPE_STRING, # Name = 1 + gobject.TYPE_STRING, # Type = 2 + gobject.TYPE_STRING) # content = 3 + + Neard.__init__(self, self.adapter_UpdateUI, self.adapter_RemoveUI + , self.tag_UpdateUI, self.record_UpdateUI) + +class RecordUI(): + def wr_onResponse(self, dialog, response): + if response != gtk.RESPONSE_ACCEPT: + return + content = self.content_entry.get_text() + type_name = self.type_combo.get_active_text() + bus = dbus.SystemBus() + record_path = self.path + tag_path = record_path[:record_path.rfind("/")] + tag = dbus.Interface(bus.get_object("org.neard", tag_path), "org.neard.Tag") + if type_name in ["Text"]: + content1 = content.split() + tag.Write({"Type" : type_name, + "Encoding" : content1[0], + "Language" : content1[1], + "Representation" : ' '.join(content1[2:])}) + else: + tag.Write({"Type" : type_name, + "URI" : content}) + dialog.destroy() + + def __init__(self, parent = None, path = None, type_name = None): + self.path = path + type_combo = gtk.combo_box_new_text() + type_combo.append_text('Text') + type_combo.append_text('URI') + type_combo.append_text('SmartPoster') + if type_name == 'Text': + type_combo.set_active(0) + elif type_name == 'URI': + type_combo.set_active(1) + elif type_name == 'SmartPoster': + type_combo.set_active(2) + fixed = gtk.Fixed() + content_entry = gtk.Entry() + fixed.put(type_combo, 20, 40) + fixed.put(content_entry, 150, 40) + type_label = gtk.Label("Type") + content_label = gtk.Label("Content") + fixed.put(type_label, 20, 15) + fixed.put(content_label, 150, 15) + + record_dialog = gtk.Dialog("Write Record", parent, + gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, + (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) + self.record_dialog = record_dialog + record_dialog.set_default_size(280, 120) + record_dialog.set_position(gtk.WIN_POS_CENTER) + record_dialog.connect('response', self.wr_onResponse) + hbox = record_dialog.get_content_area() + hbox.pack_start(fixed) + self.type_combo = type_combo + self.content_entry = content_entry + fixed.show_all() + + def show(self): + self.record_dialog.run() + self.record_dialog.destroy() + +##================================================================= +if __name__ == "__main__": + + def endmainloop(response): + mainloop.quit() + + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + mainloop = gobject.MainLoop() + + m = NeardUI("Neard", endmainloop) + m.show() + + mainloop.run() diff --git a/test/push-device b/test/push-device new file mode 100755 index 0000000..614f4fa --- /dev/null +++ b/test/push-device @@ -0,0 +1,48 @@ +#!/usr/bin/python + +import sys +import dbus + +def help_text(): + print "Usage: %s <device-path> <record-type> <...>" % (sys.argv[0]) + print " If type is Text, parameters are <encoding> <language> <representation>" + print " If type is URI, parameters are <uri>" + print " If type is SmartPoster, parameters are <uri>" + print " If type is Handover, parameters are <carrier>" + print "e.g. < %s /org/neard/nfc0/device0 Text UTF-8 en-US hello,Type2! >" % (sys.argv[0]) + print "e.g. < %s /org/neard/nfc0/device0 URI http://www.nfc-forum.com >" % (sys.argv[0]) + print "e.g. < %s /org/neard/nfc0/device0 SmartPoster http://www.nfc-forum.com >" % (sys.argv[0]) + print "e.g. < %s /org/neard/nfc0/device0 Handover bluetooth >" % (sys.argv[0]) + sys.exit(1) + +if len(sys.argv) < 2: + help_text() + +bus = dbus.SystemBus() + +device = dbus.Interface(bus.get_object("org.neard", sys.argv[1]), + "org.neard.Device") + +if len(sys.argv) == 6: + if sys.argv[2] in ["Text"]: + device.Push(({ "Type" : "Text", + "Encoding" : sys.argv[3], + "Language" : sys.argv[4], + "Representation" : sys.argv[5] })) + else: + help_text() + +elif len(sys.argv) == 4: + if sys.argv[2] in ["URI"]: + device.Push(({ "Type" : "URI", + "URI" : sys.argv[3] })) + elif sys.argv[2] in ["SmartPoster"]: + device.Push(({ "Type" : "SmartPoster", + "URI" : sys.argv[3] })) + elif sys.argv[2] in ["Handover"]: + device.Push(({ "Type" : "Handover", + "Carrier" : sys.argv[3] })) + else: + help_text() +else: + help_text() diff --git a/test/simple-agent b/test/simple-agent new file mode 100755 index 0000000..1c7fb4d --- /dev/null +++ b/test/simple-agent @@ -0,0 +1,68 @@ +#!/usr/bin/python + +import gobject + +import dbus +import dbus.service +import dbus.mainloop.glib +import sys + +class NDEFAgent(dbus.service.Object): + + @dbus.service.method("org.neard.NDEFAgent", + in_signature='', out_signature='') + def Release(self): + print "Release" + mainloop.quit() + + @dbus.service.method("org.neard.NDEFAgent", + in_signature='a{sv}', + out_signature='') + def GetNDEF(self, fields): + print "GetNDEF %s" + + if fields.has_key("Records"): + for path in fields["Records"]: + print "Record path %s" % (path) + + if fields.has_key("NDEF"): + val = "[" + for i in fields["NDEF"]: + val += " 0x%x" % i + val += " ]" + print "NDEF %s" % val + + return + + @dbus.service.method("org.neard.NDEFAgent", + in_signature='', out_signature='') + def Cancel(self): + print "Cancel" + +def print_usage(): + print "Usage:" + print "For NDEF agent:" + print "%s NDEF Type=<record type>" % (sys.argv[0]) + print "Help: %s help" % (sys.argv[0]) + sys.exit(1) + +if __name__ == '__main__': + if len(sys.argv) == 2 and sys.argv[1] == "help": + print_usage() + + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + + bus = dbus.SystemBus() + manager = dbus.Interface(bus.get_object('org.neard', "/"), + 'org.neard.Manager') + + if len(sys.argv) > 2: + if sys.argv[1] == "NDEF": + path = "/test/ndef/agent" + object = NDEFAgent(bus, path) + rec_type = sys.argv[2].replace("Type=", "", 1) + + manager.RegisterNDEFAgent(path, rec_type) + + mainloop = gobject.MainLoop() + mainloop.run() diff --git a/test/start-poll b/test/start-poll new file mode 100755 index 0000000..858597b --- /dev/null +++ b/test/start-poll @@ -0,0 +1,30 @@ +#!/usr/bin/python + +import sys +import dbus + +if len(sys.argv) < 2: + print "Usage: %s [nfc device] <polling mode>" % (sys.argv[0]) + sys.exit(1) + +if len(sys.argv) < 3: + mode = "Initiator" +else: + mode = sys.argv[2] + +bus = dbus.SystemBus() + +manager = dbus.Interface(bus.get_object("org.neard", "/"), + "org.neard.Manager") + + +print "Polling Mode %s" % (mode) + +path = "/org/neard/" + sys.argv[1] +adapter = dbus.Interface(bus.get_object("org.neard", path), + "org.neard.Adapter") + +try: + adapter.StartPollLoop(mode) +except dbus.DBusException, error: + print "%s: %s" % (error._dbus_error_name, error.message) diff --git a/test/stop-poll b/test/stop-poll new file mode 100755 index 0000000..5ef2ef9 --- /dev/null +++ b/test/stop-poll @@ -0,0 +1,23 @@ +#!/usr/bin/python + +import sys +import dbus + +if len(sys.argv) < 2: + print "Usage: %s <nfc device>" % (sys.argv[0]) + sys.exit(1) + +bus = dbus.SystemBus() + +manager = dbus.Interface(bus.get_object("org.neard", "/"), + "org.neard.Manager") + + +path = "/org/neard/" + sys.argv[1] +adapter = dbus.Interface(bus.get_object("org.neard", path), + "org.neard.Adapter") + +try: + adapter.StopPollLoop() +except dbus.DBusException, error: + print "%s: %s" % (error._dbus_error_name, error.message) diff --git a/test/write-tag b/test/write-tag new file mode 100755 index 0000000..482239c --- /dev/null +++ b/test/write-tag @@ -0,0 +1,53 @@ +#!/usr/bin/python + +import sys +import dbus + +def help_text(): + print "Usage: %s <tag-path> <record-type> <...>" % (sys.argv[0]) + print " If type is Text, parameters are <encoding> <language> <representation>" + print " If type is URI, parameters are <uri>" + print " If type is SmartPoster, parameters are <uri>" + print "e.g. < %s /org/neard/nfc0/tag0 Text UTF-8 en-US hello,Type2! >" % (sys.argv[0]) + print "e.g. < %s /org/neard/nfc0/tag0 URI http://www.nfc-forum.com >" % (sys.argv[0]) + print "e.g. < %s /org/neard/nfc0/tag0 SmartPoster http://www.nfc-forum.com >" % (sys.argv[0]) + print "e.g. < %s /org/neard/nfc0/tag0 Raw <NDEF_file> >" % (sys.argv[0]) + sys.exit(1) + +if len(sys.argv) < 2: + help_text() + +bus = dbus.SystemBus() + +tag = dbus.Interface(bus.get_object("org.neard", sys.argv[1]), + "org.neard.Tag") + +if len(sys.argv) == 6: + if sys.argv[2] in ["Text"]: + tag.Write(({ "Type" : "Text", + "Encoding" : sys.argv[3], + "Language" : sys.argv[4], + "Representation" : sys.argv[5] })) + else: + help_text() + +elif len(sys.argv) == 4: + if sys.argv[2] in ["URI"]: + tag.Write(({ "Type" : "URI", + "URI" : sys.argv[3] })) + elif sys.argv[2] in ["SmartPoster"]: + tag.Write(({ "Type" : "SmartPoster", + "URI" : sys.argv[3] })) + elif sys.argv[2] in ["Raw"]: + ndef = file(sys.argv[3]).read().rsplit(' ') + ndef_stream = bytearray() + + for b in ndef: + ndef_stream.append(int(b, 16)) + + tag.Write(({ "Type" : "Raw", + "NDEF" : dbus.ByteArray(ndef_stream) })) + else: + help_text() +else: + help_text() diff --git a/tools/snep-send.c b/tools/snep-send.c new file mode 100644 index 0000000..a78d9f9 --- /dev/null +++ b/tools/snep-send.c @@ -0,0 +1,120 @@ +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <stdint.h> +#include <string.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <linux/socket.h> + +#include <near/nfc.h> +#include <near/types.h> +#include <near/ndef.h> + +#include "../src/near.h" + +/* HACK HACK */ +#ifndef AF_NFC +#define AF_NFC 39 +#endif + +#define SNEP_VERSION 0x10 + +/* Request codes */ +#define SNEP_REQ_CONTINUE 0x00 +#define SNEP_REQ_GET 0x01 +#define SNEP_REQ_PUT 0x02 +#define SNEP_REQ_REJECT 0x7f + +/* Response codes */ +#define SNEP_RESP_CONTINUE 0x80 +#define SNEP_RESP_SUCCESS 0x81 +#define SNEP_RESP_NOT_FOUND 0xc0 +#define SNEP_RESP_EXCESS 0xc1 +#define SNEP_RESP_BAD_REQ 0xc2 +#define SNEP_RESP_NOT_IMPL 0xe0 +#define SNEP_RESP_VERSION 0xe1 +#define SNEP_RESP_REJECT 0xff + +struct p2p_snep_req_frame { + uint8_t version; + uint8_t request; + uint32_t length; + uint8_t ndef[]; +} __attribute__((packed)); + +int main(int argc, char *argv[]) +{ + int fd, len; + struct near_ndef_message *ndef; + int adapter_idx, target_idx; + struct sockaddr_nfc_llcp addr; + struct p2p_snep_req_frame *frame; + size_t frame_length; + + if (argc < 3) { + printf("Usage: %s <adapter index> <target index>\n", argv[0]); + exit(0); + } + + adapter_idx = atoi(argv[1]); + target_idx = atoi(argv[2]); + + fd = socket(AF_NFC, SOCK_STREAM, NFC_SOCKPROTO_LLCP); + if (fd < 0) + return -1; + + memset(&addr, 0, sizeof(struct sockaddr_nfc_llcp)); + addr.sa_family = AF_NFC; + addr.dev_idx = adapter_idx; + addr.target_idx = target_idx; + addr.nfc_protocol = NFC_PROTO_NFC_DEP; + addr.service_name_len = strlen("urn:nfc:sn:snep"); + strcpy(addr.service_name, "urn:nfc:sn:snep"); + + if (connect(fd, (struct sockaddr *)&addr, + sizeof(struct sockaddr_nfc_llcp)) < 0) { + near_error("Connect error %s\n", strerror(errno)); + return -1; + } + + ndef = near_ndef_prepare_text_record("UTF-8", "en", "Hello world"); + if (ndef == NULL) { + close(fd); + near_error("Could not build NDEF"); + return -1; + } + + frame_length = sizeof(struct p2p_snep_req_frame) + ndef->length; + frame = g_try_malloc0(frame_length); + if (frame == NULL) { + close(fd); + near_error("Could not allocate SNEP frame"); + return -1; + } + + frame->version = SNEP_VERSION; + frame->request = SNEP_REQ_PUT; + frame->length = GUINT_TO_BE(ndef->length); + + memcpy(frame->ndef, ndef->data, ndef->length); + + len = send(fd, (uint8_t *)frame, frame_length, 0); + if (len < 0) { + near_error("Could not send text NDEF %s\n", strerror(errno)); + + g_free(frame); + close(fd); + + return -1; + } + + DBG("Sent %d bytes", len); + + g_free(frame); + close(fd); + + return 0; +} |