diff options
author | Jiri Pirko <jiri@mellanox.com> | 2017-07-07 10:14:00 +0200 |
---|---|---|
committer | Jiri Pirko <jiri@mellanox.com> | 2017-07-17 17:40:23 +0200 |
commit | 2cf25828e20df692bd8dbeff2196466e2bd008f7 (patch) | |
tree | 3e6157114e6336dffdeff31338e98bcc5aad1b10 | |
download | libactuator-master.tar.gz |
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
-rw-r--r-- | COPYING | 502 | ||||
-rw-r--r-- | Makefile.am | 5 | ||||
-rw-r--r-- | README | 28 | ||||
-rw-r--r-- | SubmittingPatches | 43 | ||||
-rwxr-xr-x | autogen.sh | 4 | ||||
-rw-r--r-- | configure.ac | 66 | ||||
-rw-r--r-- | include/Makefile.am | 6 | ||||
-rw-r--r-- | include/actuator.h | 93 | ||||
-rw-r--r-- | include/linux/actuator.h | 74 | ||||
-rw-r--r-- | libactuator/Makefile.am | 17 | ||||
-rw-r--r-- | libactuator/libactuator.c | 578 | ||||
-rw-r--r-- | libactuator/libactuator.pc.in | 10 | ||||
-rw-r--r-- | libactuator/mnlg.c | 292 | ||||
-rw-r--r-- | libactuator/mnlg.h | 37 | ||||
-rw-r--r-- | man/Makefile.am | 1 | ||||
-rw-r--r-- | man/act-dev.8 | 85 | ||||
-rw-r--r-- | man/act.8 | 71 | ||||
-rw-r--r-- | utils/Makefile.am | 10 | ||||
-rw-r--r-- | utils/act.c | 544 |
19 files changed, 2466 insertions, 0 deletions
@@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, 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. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, 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 library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete 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 distribute a copy of this License along with the +Library. + + 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 Library or any portion +of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +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 Library, 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 Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you 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. + + If distribution of 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 satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be 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. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library 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. + + 9. 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 Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +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 with +this License. + + 11. 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 Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library 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 Library. + +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. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library 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. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +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 Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +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 + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. 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 LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. 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 library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..c2e0c57 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,5 @@ +MAINTAINERCLEANFILES = Makefile.in + +ACLOCAL_AMFLAGS = -I m4 + +SUBDIRS = include libactuator utils man @@ -0,0 +1,28 @@ +# libactuator - Library for actuator manipulation # + +This package contains a library which provides a wrapper for generic +Netlink actuator kernel interface. It also provides a tool named "act" +for manipulation and monitoring of actuators. + +## Install + + $ ./autogen.sh + $ ./configure + $ make + $ sudo make install + +## Authors + +* Jiri Pirko <jiri@resnulli.us> + +## Internet Resources + +* Project Home: http://www.libactuator.org/ +* Git Source Tree: https://git.kernel.org/pub/scm/linux/kernel/git/jpirko/libactuator.git + +## License + +Copyright (C) 2017 Jiri Pirko <jiri@resnulli.us> + +libactuator is distributed under GNU Lesser General Public License version 2.1. +See the file "COPYING" in the source distribution for information on terms & conditions for accessing and otherwise using libactuator. diff --git a/SubmittingPatches b/SubmittingPatches new file mode 100644 index 0000000..f349815 --- /dev/null +++ b/SubmittingPatches @@ -0,0 +1,43 @@ +# How to Submit Patches for libactuator # +=========================================== + +Send changes to libactuator as patches to jiri@resnulli.us. + +Avoid using attachments because usually it doesn't work. +In another words, inline the patch inside the e-mail's body. + +The simplest workflow to send changes is using git. The following +steps will guide you to do the task. + +1) Clone the source code on your local machine. +$ git clone https://git.kernel.org/pub/scm/linux/kernel/git/jpirko/libactuator.git + +2) Create a local branch for you +$ cd libactuator +$ git checkout -b testbranch + +3) Do your changes +<<do some changes>> + +4) Commit them to the repository +$ git commit -a -s + +5) Optionally, you can verify how it looks like in the repo +$ git show + +6) Extract the patch in a standard format into /tmp directory +$ git format-patch origin -o /tmp --subject-prefix "patch libactuator" + +7) Use git send-email to send the patch*. +$ git send-email /tmp/0001-somepatch.patch --to=jiri@resnulli.us + +And you are done :) + +* You may need to configure smtp in order to send emails, so please + check the "Example" section in the reference [3] below as a start. + +Additional references about git: +[1] http://git-scm.com/docs/gittutorial +[2] http://git-scm.com/docs/gitworkflows.html +[3] http://git-scm.com/docs/git-send-email +[4] http://git-scm.com/book diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..665de9c --- /dev/null +++ b/autogen.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +autoreconf --force --install -I m4 +rm -Rf autom4te.cache; diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..e63027d --- /dev/null +++ b/configure.ac @@ -0,0 +1,66 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_INIT([libactuator], [0], [jiri@resnulli.us]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_MACRO_DIR([m4]) +AM_INIT_AUTOMAKE([-Wall foreign subdir-objects]) +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES(yes)], []) +AM_PROG_AR + +# Here are a set of rules to help you update your library version information: +# 1. Start with version information of ‘0:0:0’ for each libtool library. +# 2. Update the version information only immediately before a public release +# of your software. More frequent updates are unnecessary, and only guarantee +# that the current interface number gets larger faster. +# 3. If the library source code has changed at all since the last update, +# then increment revision (‘c:r:a’ becomes ‘c:r+1:a’). +# 4. If any interfaces have been added, removed, or changed since the last +# update, increment current, and set revision to 0. +# 5. If any interfaces have been added since the last public release, then +# increment age. +# 6. If any interfaces have been removed or changed since the last public +# release, then set age to 0. + +AC_SUBST(LIBACTUATOR_CURRENT, 0) +AC_SUBST(LIBACTUATOR_REVISION, 0) +AC_SUBST(LIBACTUATOR_AGE, 0) + +CFLAGS="$CFLAGS -Wall" + +# Checks for programs. +AC_PROG_CC +LT_INIT + +PKG_CHECK_MODULES([LIBMNL], [libmnl]) + +# Checks for header files. +AC_CHECK_HEADERS([stdint.h stdlib.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_INLINE + +# Checks for library functions. +AC_FUNC_MALLOC + +AC_ARG_ENABLE([logging], + AS_HELP_STRING([--disable-logging], [disable system logging @<:@default=enabled@:>@]), + [], enable_logging=yes) +AS_IF([test "x$enable_logging" = "xyes"], [ + AC_DEFINE(ENABLE_LOGGING, [1], [System logging.]) +]) + +AC_ARG_ENABLE([debug], + AS_HELP_STRING([--enable-debug], [enable debug messages @<:@default=disabled@:>@]), + [], [enable_debug=no]) +AS_IF([test "x$enable_debug" = "xyes"], [ + AC_DEFINE(ENABLE_DEBUG, [1], [Debug messages.]) +]) + +AC_CONFIG_FILES([Makefile +include/Makefile \ +libactuator/Makefile \ +libactuator/libactuator.pc \ +utils/Makefile \ +man/Makefile]) +AC_OUTPUT diff --git a/include/Makefile.am b/include/Makefile.am new file mode 100644 index 0000000..babb39e --- /dev/null +++ b/include/Makefile.am @@ -0,0 +1,6 @@ +MAINTAINERCLEANFILES = Makefile.in + +libactuatorincludedir = $(includedir) +nobase_libactuatorinclude_HEADERS = actuator.h + +noinst_HEADERS = linux/actuator.h diff --git a/include/actuator.h b/include/actuator.h new file mode 100644 index 0000000..61efc2a --- /dev/null +++ b/include/actuator.h @@ -0,0 +1,93 @@ +/* + * actuator.h - Actuator manipulation library + * Copyright (C) 2017 Jiri Pirko <jiri@resnulli.us> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _ACTUATOR_H_ +#define _ACTUATOR_H_ + +#include <stdarg.h> +#include <stdint.h> +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct actuator; + +void actuator_set_log_fn(struct actuator *actuator, + void (*log_fn)(struct actuator *actuator, int priority, + const char *file, int line, + const char *fn, const char *format, + va_list args)); +int actuator_get_log_priority(struct actuator *actuator); +void actuator_set_log_priority(struct actuator *actuator, int priority); + +struct actuator_handle { + const char *bus_name; + const char *dev_name; + uint32_t index; +}; + +struct actuator_info; + +enum actuator_event_type +actuator_info_event_type(const struct actuator_info *info); +void actuator_info_handle(const struct actuator_info *info, + struct actuator_handle *handle); +const char *actuator_info_driver_name(const struct actuator_info *info); +uint8_t actuator_info_type(const struct actuator_info *info); +uint8_t actuator_info_units(const struct actuator_info *info); +bool actuator_info_has_value(const struct actuator_info *info); +uint32_t actuator_info_value(const struct actuator_info *info); +bool actuator_info_has_move_state(const struct actuator_info *info); +uint8_t actuator_info_move_state(const struct actuator_info *info); + +typedef void (*actuator_cb_t)(struct actuator *actuator, + struct actuator_info *info, void *cb_priv); + +int actuator_get(struct actuator *actuator, + const struct actuator_handle *handle, + actuator_cb_t cb, void *cb_priv); +int actuator_move(struct actuator *actuator, + const struct actuator_handle *handle, const char *fmt, ...); +int actuator_stop(struct actuator *actuator, + const struct actuator_handle *handle, const char *fmt, ...); + +enum actuator_event_type { + ACTUATOR_EVENT_TYPE_GET, + ACTUATOR_EVENT_TYPE_NEW, + ACTUATOR_EVENT_TYPE_DEL, + __ACTUATOR_EVENT_TYPE_MAX, + ACTUATOR_EVENT_TYPE_MAX = __ACTUATOR_EVENT_TYPE_MAX - 1 +}; + +void actuator_event_cb_set(struct actuator *actuator, + enum actuator_event_type type, + actuator_cb_t cb, void *cb_priv); +int actuator_event_process(struct actuator *actuator); +int actuator_event_fd_get(struct actuator *actuator); + +int actuator_init(struct actuator **p_act); +void actuator_fini(struct actuator *actuator); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _ACTUATOR_H_ */ diff --git a/include/linux/actuator.h b/include/linux/actuator.h new file mode 100644 index 0000000..4e2d958 --- /dev/null +++ b/include/linux/actuator.h @@ -0,0 +1,74 @@ +/* + * include/uapi/linux/actuator.h - Generic actuator support + * Copyright (c) 2017 Jiri Pirko <jiri@resnulli.us> + * + * 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. + */ + +#ifndef _UAPI_LINUX_ACTUATOR_H_ +#define _UAPI_LINUX_ACTUATOR_H_ + +#define ACTUATOR_GENL_NAME "actuator" +#define ACTUATOR_GENL_VERSION 0x1 +#define ACTUATOR_GENL_MCGRP_CONFIG_NAME "config" + +enum actuator_command { + /* don't change the order or add anything between, this is ABI! */ + ACTUATOR_CMD_UNSPEC, + + ACTUATOR_CMD_GET, /* can dump */ + ACTUATOR_CMD_NEW, + ACTUATOR_CMD_DEL, + + ACTUATOR_CMD_MOVE, + ACTUATOR_CMD_STOP, + + /* add new commands above here */ + __ACTUATOR_CMD_MAX, + ACTUATOR_CMD_MAX = __ACTUATOR_CMD_MAX - 1 +}; + +enum actuator_type { + ACTUATOR_TYPE_UNSPEC, + ACTUATOR_TYPE_LINEAR, +}; + +enum actuator_units { + ACTUATOR_UNITS_UNSPEC, + ACTUATOR_UNITS_UM, /* micrometres */ +}; + +enum actuator_move_state { + ACTUATOR_MOVE_STATE_UNSPEC, + ACTUATOR_MOVE_STATE_STOPPED, + ACTUATOR_MOVE_STATE_POSITIVE, + ACTUATOR_MOVE_STATE_NEGATIVE, +}; + +enum actuator_attr { + /* don't change the order or add anything between, this is ABI! */ + ACTUATOR_ATTR_UNSPEC, + + /* bus name + dev name + index together are a handle + * for actuator entity + */ + ACTUATOR_ATTR_BUS_NAME, /* string */ + ACTUATOR_ATTR_DEV_NAME, /* string */ + ACTUATOR_ATTR_INDEX, /* u32 */ + + ACTUATOR_ATTR_DRIVER_NAME, /* string */ + ACTUATOR_ATTR_TYPE, /* u8, enum actuator_type */ + ACTUATOR_ATTR_UNITS, /* u8, enum actuator_units */ + ACTUATOR_ATTR_VALUE, /* u32, in units */ + ACTUATOR_ATTR_MOVE_STATE, /* u8, enum actuator_move_state */ + + /* add new attributes above here, update the policy in actuator.c */ + + __ACTUATOR_ATTR_MAX, + ACTUATOR_ATTR_MAX = __ACTUATOR_ATTR_MAX - 1 +}; + +#endif /* _UAPI_LINUX_ACTUATOR_H_ */ diff --git a/libactuator/Makefile.am b/libactuator/Makefile.am new file mode 100644 index 0000000..e11a477 --- /dev/null +++ b/libactuator/Makefile.am @@ -0,0 +1,17 @@ +MAINTAINERCLEANFILES = Makefile.in + +ACLOCAL_AMFLAGS = -I m4 + +AM_CFLAGS = -fvisibility=hidden -ffunction-sections -fdata-sections +AM_LDFLAGS = -Wl,--gc-sections -Wl,--as-needed + +lib_LTLIBRARIES = libactuator.la +libactuator_la_SOURCES = libactuator.c mnlg.c +libactuator_la_CFLAGS= $(AM_CFLAGS) $(LIBMNL_CFLAGS) -I${top_srcdir}/include -D_GNU_SOURCE +libactuator_la_LIBADD= $(LIBMNL_LIBS) +libactuator_la_LDFLAGS = $(AM_LDFLAGS) -version-info @LIBACTUATOR_CURRENT@:@LIBACTUATOR_REVISION@:@LIBACTUATOR_AGE@ + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libactuator.pc + +EXTRA_DIST = mnlg.h diff --git a/libactuator/libactuator.c b/libactuator/libactuator.c new file mode 100644 index 0000000..cf36069 --- /dev/null +++ b/libactuator/libactuator.c @@ -0,0 +1,578 @@ +/* + * libactuator.c - Actuator manipulation library + * Copyright (C) 2017 Jiri Pirko <jiri@resnulli.us> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdarg.h> +#include <stdint.h> +#include <syslog.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <assert.h> +#include <linux/genetlink.h> +#include <linux/actuator.h> +#include <libmnl/libmnl.h> +#include <actuator.h> + +#include "mnlg.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#define ACTUATOR_EXPORT __attribute__ ((visibility("default"))) + +struct actuator_cb { + actuator_cb_t cb; + void *cb_priv; +}; + +struct actuator { + void (*log_fn)(struct actuator *actuator, int priority, + const char *file, int line, const char *fn, + const char *format, va_list args); + int log_priority; + struct mnlg_socket *nlg; + struct actuator_cb cbs[ACTUATOR_EVENT_TYPE_MAX + 1]; +}; + +void actuator_log(struct actuator *actuator, int priority, + const char *file, int line, const char *fn, + const char *format, ...) +{ + va_list args; + + va_start(args, format); + actuator->log_fn(actuator, priority, file, line, fn, format, args); + va_end(args); +} + +static inline void __attribute__((always_inline, format(printf, 2, 3))) +actuator_log_null(struct actuator *actuator, const char *format, ...) {} + +#define actuator_log_cond(act, prio, arg...) \ + do { \ + if (actuator_get_log_priority(act) >= prio) \ + actuator_log(actuator, prio, __FILE__, __LINE__, \ + __FUNCTION__, ## arg); \ + } while (0) + +#ifdef ENABLE_LOGGING +# ifdef ENABLE_DEBUG +# define dbg(act, arg...) actuator_log_cond(act, LOG_DEBUG, ## arg) +# else +# define dbg(act, arg...) actuator_log_null(act, ## arg) +# endif +# define info(act, arg...) actuator_log_cond(act, LOG_INFO, ## arg) +# define warn(act, arg...) actuator_log_cond(act, LOG_WARNING, ## arg) +# define err(act, arg...) actuator_log_cond(act, LOG_ERR, ## arg) +#else +# define dbg(act, arg...) actuator_log_null(act, ## arg) +# define info(act, arg...) actuator_log_null(act, ## arg) +# define warn(act, arg...) actuator_log_null(act, ## arg) +# define err(act, arg...) actuator_log_null(act, ## arg) +#endif + +static void log_stderr(struct actuator *actuator, int priority, + const char *file, int line, const char *fn, + const char *format, va_list args) +{ + fprintf(stderr, "libactuator: %s: ", fn); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); +} + +static int log_priority(const char *priority) +{ + char *endptr; + int prio; + + prio = strtol(priority, &endptr, 10); + if (endptr[0] == '\0' || isspace(endptr[0])) + return prio; + if (strncmp(priority, "err", 3) == 0) + return LOG_ERR; + if (strncmp(priority, "info", 4) == 0) + return LOG_INFO; + if (strncmp(priority, "debug", 5) == 0) + return LOG_DEBUG; + return 0; +} + +/** + * actuator_set_log_fn - set logging callback + * + * @act: libactuator library context + * @log_fn: function to be called for logging messages + * + * The built-in logging writes to stderr. It can be overridden + * by a custom function, to plug log messages into the user's + * logging functionality. + */ +ACTUATOR_EXPORT +void actuator_set_log_fn(struct actuator *actuator, + void (*log_fn)(struct actuator *actuator, int priority, + const char *file, int line, + const char *fn, const char *format, + va_list args)) +{ + actuator->log_fn = log_fn; + dbg(actuator, "Custom logging function %p registered.", log_fn); +} + +/** + * actuator_get_log_priority - get logging priority + * + * @act: libactuator library context + * + * Get the current logging priority. + */ +ACTUATOR_EXPORT +int actuator_get_log_priority(struct actuator *actuator) +{ + return actuator->log_priority; +} + +/** + * actuator_set_log_priority - set logging priority + * + * @act: libactuator library context + * @priority: the new logging priority + * + * Set the current logging priority. The value controls which messages + * are logged. + */ +ACTUATOR_EXPORT +void actuator_set_log_priority(struct actuator *actuator, int priority) +{ + actuator->log_priority = priority; +} + +static int _mnlg_socket_recvfrom(struct actuator *actuator, + mnl_cb_t data_cb, void *data) +{ + int err; + + err = mnlg_socket_recvfrom(actuator->nlg, data_cb, data); + if (err < 0) { + err(actuator, "actuator netlink answers: %s\n", strerror(-err)); + return err; + } + return 0; +} + +static int _mnlg_socket_recv_run(struct actuator *actuator, + mnl_cb_t data_cb, void *data) +{ + int err; + + err = mnlg_socket_recv_run(actuator->nlg, data_cb, data); + if (err < 0) { + err(actuator, "actuator netlink answers: %s\n", strerror(-err)); + return err; + } + return 0; +} + +static int _mnlg_socket_sndrcv(struct actuator *actuator, + const struct nlmsghdr *nlh, + mnl_cb_t data_cb, void *data) +{ + int err; + + err = mnlg_socket_send(actuator->nlg, nlh); + if (err < 0) { + err(actuator, "Failed to call mnlg_socket_send\n"); + return err; + } + return _mnlg_socket_recv_run(actuator, data_cb, data); +} + +static int _mnlg_socket_group_add(struct actuator *actuator, + const char *group_name) +{ + int err; + + err = mnlg_socket_group_add(actuator->nlg, group_name); + if (err < 0) { + err(actuator, "Failed to call mnlg_socket_group_add\n"); + return err; + } + return 0; +} + +static void _handle_put(struct nlmsghdr *nlh, + const struct actuator_handle *handle) +{ + mnl_attr_put_strz(nlh, ACTUATOR_ATTR_BUS_NAME, handle->bus_name); + mnl_attr_put_strz(nlh, ACTUATOR_ATTR_DEV_NAME, handle->dev_name); + mnl_attr_put_u32(nlh, ACTUATOR_ATTR_INDEX, handle->index); +} + +static const enum mnl_attr_data_type actuator_policy[ACTUATOR_ATTR_MAX + 1] = { + [ACTUATOR_ATTR_BUS_NAME] = MNL_TYPE_NUL_STRING, + [ACTUATOR_ATTR_DEV_NAME] = MNL_TYPE_NUL_STRING, + [ACTUATOR_ATTR_INDEX] = MNL_TYPE_U32, + [ACTUATOR_ATTR_DRIVER_NAME] = MNL_TYPE_NUL_STRING, + [ACTUATOR_ATTR_TYPE] = MNL_TYPE_U8, + [ACTUATOR_ATTR_UNITS] = MNL_TYPE_U8, + [ACTUATOR_ATTR_VALUE] = MNL_TYPE_U32, + [ACTUATOR_ATTR_MOVE_STATE] = MNL_TYPE_U8, +}; + +static int actuator_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type; + + if (mnl_attr_type_valid(attr, ACTUATOR_ATTR_MAX) < 0) + return MNL_CB_ERROR; + + type = mnl_attr_get_type(attr); + if (mnl_attr_validate(attr, actuator_policy[type]) < 0) + return MNL_CB_ERROR; + + tb[type] = attr; + return MNL_CB_OK; +} + +struct actuator_info { + enum actuator_event_type type; + struct nlattr *tb[ACTUATOR_ATTR_MAX + 1]; +}; + +ACTUATOR_EXPORT +enum actuator_event_type +actuator_info_event_type(const struct actuator_info *info) +{ + return info->type; +} + +ACTUATOR_EXPORT +void actuator_info_handle(const struct actuator_info *info, + struct actuator_handle *handle) +{ + handle->bus_name = mnl_attr_get_str(info->tb[ACTUATOR_ATTR_BUS_NAME]); + handle->dev_name = mnl_attr_get_str(info->tb[ACTUATOR_ATTR_DEV_NAME]); + handle->index = mnl_attr_get_u32(info->tb[ACTUATOR_ATTR_INDEX]); +} + +ACTUATOR_EXPORT +const char *actuator_info_driver_name(const struct actuator_info *info) +{ + return mnl_attr_get_str(info->tb[ACTUATOR_ATTR_DRIVER_NAME]); +} + +ACTUATOR_EXPORT +uint8_t actuator_info_type(const struct actuator_info *info) +{ + return mnl_attr_get_u8(info->tb[ACTUATOR_ATTR_TYPE]); +} + +ACTUATOR_EXPORT +uint8_t actuator_info_units(const struct actuator_info *info) +{ + return mnl_attr_get_u8(info->tb[ACTUATOR_ATTR_UNITS]); +} + +ACTUATOR_EXPORT +bool actuator_info_has_value(const struct actuator_info *info) +{ + return info->tb[ACTUATOR_ATTR_VALUE]; +} + +ACTUATOR_EXPORT +uint32_t actuator_info_value(const struct actuator_info *info) +{ + return mnl_attr_get_u32(info->tb[ACTUATOR_ATTR_VALUE]); +} + +ACTUATOR_EXPORT +bool actuator_info_has_move_state(const struct actuator_info *info) +{ + return info->tb[ACTUATOR_ATTR_MOVE_STATE]; +} + +ACTUATOR_EXPORT +uint8_t actuator_info_move_state(const struct actuator_info *info) +{ + return mnl_attr_get_u8(info->tb[ACTUATOR_ATTR_MOVE_STATE]); +} + +struct actuator_cb_info { + struct actuator *actuator; + struct actuator_cb *cbs; + unsigned int cbs_count; +}; + +static int actuator_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct actuator_cb_info *cb_info = data; + struct actuator_info info = {}; + enum actuator_event_type type; + struct nlattr **tb = info.tb; + struct actuator_cb *cb; + uint8_t cmd = genl->cmd; + + mnl_attr_parse(nlh, sizeof(*genl), actuator_attr_cb, tb); + if (!tb[ACTUATOR_ATTR_BUS_NAME] || !tb[ACTUATOR_ATTR_DEV_NAME] || + !tb[ACTUATOR_ATTR_INDEX] || !tb[ACTUATOR_ATTR_DRIVER_NAME] || + !tb[ACTUATOR_ATTR_TYPE] || !tb[ACTUATOR_ATTR_UNITS]) + return MNL_CB_ERROR; + + switch (cmd) { + case ACTUATOR_CMD_GET: + type = ACTUATOR_EVENT_TYPE_GET; + break; + case ACTUATOR_CMD_NEW: + type = ACTUATOR_EVENT_TYPE_NEW; + break; + case ACTUATOR_CMD_DEL: + type = ACTUATOR_EVENT_TYPE_DEL; + break; + default: /* Silently ignore the unknowns */ + return MNL_CB_OK; + } + + if (cb_info->cbs_count <= type) + return MNL_CB_ERROR; + info.type = type; + cb = &cb_info->cbs[type]; + cb->cb(cb_info->actuator, &info, cb->cb_priv); + return MNL_CB_OK; +} + +/** + * actuator_get - gets actuator/actuators information + * + * @actuator: libactuator library context + * @handle: actuator handle + * @cb: per-actuator instance callback + * @cb_priv: callback private data + * + * Gets actuator/actuators information. In case the handle is NULL, + * all available actuators are processed. + */ +ACTUATOR_EXPORT +int actuator_get(struct actuator *actuator, + const struct actuator_handle *handle, + actuator_cb_t _cb, void *cb_priv) +{ + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + struct actuator_cb_info cb_info; + struct actuator_cb cb; + struct nlmsghdr *nlh; + + cb_info.actuator = actuator; + cb.cb = _cb; + cb.cb_priv = cb_priv; + cb_info.cbs = &cb; + cb_info.cbs_count = 1; + + if (!handle) + flags |= NLM_F_DUMP; + nlh = mnlg_msg_prepare(actuator->nlg, ACTUATOR_CMD_GET, flags); + if (handle) + _handle_put(nlh, handle); + return _mnlg_socket_sndrcv(actuator, nlh, actuator_cb, &cb_info); +} + +/** + * actuator_move - moves actuator + * + * @actuator: libactuator library context + * @handle: actuator handle + * @fmt: format for vararg + * + * Moves the actuator movement. + */ +ACTUATOR_EXPORT +int actuator_move(struct actuator *actuator, + const struct actuator_handle *handle, const char *fmt, ...) +{ + struct nlmsghdr *nlh; + va_list ap; + + nlh = mnlg_msg_prepare(actuator->nlg, ACTUATOR_CMD_MOVE, + NLM_F_REQUEST | NLM_F_ACK); + _handle_put(nlh, handle); + + va_start(ap, fmt); + while (*fmt) { + switch (*fmt++) { + case 'v': /* value */ + mnl_attr_put_u32(nlh, ACTUATOR_ATTR_VALUE, + va_arg(ap, uint32_t)); + break; + default: + err(actuator, "Unknown move argument format\n"); + return -EINVAL; + } + } + va_end(ap); + + return _mnlg_socket_sndrcv(actuator, nlh, NULL, NULL); +} + +/** + * actuator_stop - stops actuator + * + * @actuator: libactuator library context + * @handle: actuator handle + * @fmt: format for vararg + * + * Stops the actuator movement. + */ +ACTUATOR_EXPORT +int actuator_stop(struct actuator *actuator, + const struct actuator_handle *handle, const char *fmt, ...) +{ + struct nlmsghdr *nlh; + va_list ap; + + nlh = mnlg_msg_prepare(actuator->nlg, ACTUATOR_CMD_STOP, + NLM_F_REQUEST | NLM_F_ACK); + _handle_put(nlh, handle); + + va_start(ap, fmt); + while (*fmt) { + switch (*fmt++) { + default: + err(actuator, "Unknown stop argument format\n"); + return -EINVAL; + } + } + va_end(ap); + + return _mnlg_socket_sndrcv(actuator, nlh, NULL, NULL); +} + +/** + * actuator_eventfd_process - processes event filedesctiptor + * + * @actuator: libactuator library context + * + * Processes event filedesctiptor. + */ +ACTUATOR_EXPORT +void actuator_event_cb_set(struct actuator *actuator, + enum actuator_event_type type, + actuator_cb_t cb, void *cb_priv) +{ + actuator->cbs[type].cb = cb; + actuator->cbs[type].cb_priv = cb_priv; +} + +/** + * actuator_event_process - processes pending events + * + * @actuator: libactuator library context + * + * Processes pending events. + */ +ACTUATOR_EXPORT +int actuator_event_process(struct actuator *actuator) +{ + struct actuator_cb_info cb_info; + + cb_info.actuator = actuator; + cb_info.cbs = actuator->cbs; + cb_info.cbs_count = ARRAY_SIZE(actuator->cbs); + return _mnlg_socket_recvfrom(actuator, actuator_cb, &cb_info); +} + +/** + * actuator_event_fd_get - gets event filedesctiptor + * + * @actuator: libactuator library context + * + * Gets event filedesctiptor. + */ +ACTUATOR_EXPORT +int actuator_event_fd_get(struct actuator *actuator) +{ + return mnlg_socket_get_fd(actuator->nlg); +} + +static void *myzalloc(size_t size) +{ + return calloc(1, size); +} + +/** + * actuator_init - inits actuator context + * + * @p_actuator: pointer where new library context address will be stored + * + * Allocates and initializes library context, opens generic Netlink socket. + */ +ACTUATOR_EXPORT +int actuator_init(struct actuator **p_actuator) +{ + struct actuator *actuator; + const char *env; + int err; + + actuator = myzalloc(sizeof(*actuator)); + if (!actuator) + return -ENOMEM; + actuator->log_fn = log_stderr; + actuator->log_priority = LOG_ERR; + env = getenv("ACTUATOR_LOG"); + if (env != NULL) + actuator_set_log_priority(actuator, log_priority(env)); + + dbg(actuator, "actuator context %p created", actuator); + dbg(actuator, "log_priority=%d", actuator->log_priority); + + actuator->nlg = mnlg_socket_open(ACTUATOR_GENL_NAME, + ACTUATOR_GENL_VERSION); + if (!actuator->nlg) { + err(actuator, "Failed to connect to actuator Netlink"); + err = -errno; + goto err_mnlg_socket_open; + } + + err = _mnlg_socket_group_add(actuator, ACTUATOR_GENL_MCGRP_CONFIG_NAME); + if (err) + goto err_mnlg_socket_group_add; + + *p_actuator = actuator; + return 0; + +err_mnlg_socket_group_add: + mnlg_socket_close(actuator->nlg); +err_mnlg_socket_open: + free(actuator); + return err; +} + +/** + * actuator_fini - finishes actuator context + * + * @actuator: libactuator library context + * + * Do library context cleanup. + */ +ACTUATOR_EXPORT +void actuator_fini(struct actuator *actuator) +{ + mnlg_socket_close(actuator->nlg); + free(actuator); +} diff --git a/libactuator/libactuator.pc.in b/libactuator/libactuator.pc.in new file mode 100644 index 0000000..22254e4 --- /dev/null +++ b/libactuator/libactuator.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libactuator +Description: Actuator manipulation library. +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lactuator +Cflags: -I${includedir} diff --git a/libactuator/mnlg.c b/libactuator/mnlg.c new file mode 100644 index 0000000..18694f2 --- /dev/null +++ b/libactuator/mnlg.c @@ -0,0 +1,292 @@ +/* + * mnlg.c - Generic Netlink helpers for libmnl + * Copyright (C) 2017 Jiri Pirko <jiri@resnulli.us> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#include <libmnl/libmnl.h> +#include <linux/genetlink.h> + +#include "mnlg.h" + +struct mnlg_socket { + struct mnl_socket *nl; + char *buf; + uint32_t id; + uint8_t version; + unsigned int seq; + unsigned int portid; +}; + +static struct nlmsghdr *__mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd, + uint16_t flags, uint32_t id, + uint8_t version) +{ + struct nlmsghdr *nlh; + struct genlmsghdr *genl; + + nlh = mnl_nlmsg_put_header(nlg->buf); + nlh->nlmsg_type = id; + nlh->nlmsg_flags = flags; + nlg->seq = time(NULL); + nlh->nlmsg_seq = nlg->seq; + + genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr)); + genl->cmd = cmd; + genl->version = version; + + return nlh; +} + +struct nlmsghdr *mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd, + uint16_t flags) +{ + return __mnlg_msg_prepare(nlg, cmd, flags, nlg->id, nlg->version); +} + +int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh) +{ + return mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len); +} + +int mnlg_socket_recvfrom(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data) +{ + int err; + + err = mnl_socket_recvfrom(nlg->nl, nlg->buf, MNL_SOCKET_BUFFER_SIZE); + if (err <= 0) + return err; + return mnl_cb_run(nlg->buf, err, nlg->seq, nlg->portid, data_cb, data); +} + +int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data) +{ + int err; + + do { + err = mnlg_socket_recvfrom(nlg, data_cb, data); + } while (err > 0); + + return err; +} + +struct group_info { + bool found; + uint32_t id; + const char *name; +}; + +static int parse_mc_grps_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTRL_ATTR_MCAST_GRP_MAX) < 0) + return MNL_CB_OK; + + switch (type) { + case CTRL_ATTR_MCAST_GRP_ID: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + break; + case CTRL_ATTR_MCAST_GRP_NAME: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) + return MNL_CB_ERROR; + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static void parse_genl_mc_grps(struct nlattr *nested, + struct group_info *group_info) +{ + struct nlattr *pos; + const char *name; + + mnl_attr_for_each_nested(pos, nested) { + struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1] = {}; + + mnl_attr_parse_nested(pos, parse_mc_grps_cb, tb); + if (!tb[CTRL_ATTR_MCAST_GRP_NAME] || + !tb[CTRL_ATTR_MCAST_GRP_ID]) + continue; + + name = mnl_attr_get_str(tb[CTRL_ATTR_MCAST_GRP_NAME]); + if (strcmp(name, group_info->name) != 0) + continue; + + group_info->id = mnl_attr_get_u32(tb[CTRL_ATTR_MCAST_GRP_ID]); + group_info->found = true; + } +} + +static int get_group_id_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0) + return MNL_CB_ERROR; + + if (type == CTRL_ATTR_MCAST_GROUPS && + mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) + return MNL_CB_ERROR; + tb[type] = attr; + return MNL_CB_OK; +} + +static int get_group_id_cb(const struct nlmsghdr *nlh, void *data) +{ + struct group_info *group_info = data; + struct nlattr *tb[CTRL_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), get_group_id_attr_cb, tb); + if (!tb[CTRL_ATTR_MCAST_GROUPS]) + return MNL_CB_ERROR; + parse_genl_mc_grps(tb[CTRL_ATTR_MCAST_GROUPS], group_info); + return MNL_CB_OK; +} + +int mnlg_socket_group_add(struct mnlg_socket *nlg, const char *group_name) +{ + struct nlmsghdr *nlh; + struct group_info group_info; + int err; + + nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY, + NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1); + mnl_attr_put_u32(nlh, CTRL_ATTR_FAMILY_ID, nlg->id); + + err = mnlg_socket_send(nlg, nlh); + if (err < 0) + return err; + + group_info.found = false; + group_info.name = group_name; + err = mnlg_socket_recv_run(nlg, get_group_id_cb, &group_info); + if (err < 0) + return err; + + if (!group_info.found) { + errno = ENOENT; + return -1; + } + + err = mnl_socket_setsockopt(nlg->nl, NETLINK_ADD_MEMBERSHIP, + &group_info.id, sizeof(group_info.id)); + if (err < 0) + return err; + + return 0; +} + +static int get_family_id_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0) + return MNL_CB_ERROR; + + if (type == CTRL_ATTR_FAMILY_ID && + mnl_attr_validate(attr, MNL_TYPE_U16) < 0) + return MNL_CB_ERROR; + tb[type] = attr; + return MNL_CB_OK; +} + +static int get_family_id_cb(const struct nlmsghdr *nlh, void *data) +{ + uint32_t *p_id = data; + struct nlattr *tb[CTRL_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), get_family_id_attr_cb, tb); + if (!tb[CTRL_ATTR_FAMILY_ID]) + return MNL_CB_ERROR; + *p_id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]); + return MNL_CB_OK; +} + +int mnlg_socket_get_fd(struct mnlg_socket *nlg) +{ + return mnl_socket_get_fd(nlg->nl); +} + +struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version) +{ + struct mnlg_socket *nlg; + struct nlmsghdr *nlh; + int err; + + nlg = malloc(sizeof(*nlg)); + if (!nlg) + return NULL; + + nlg->buf = malloc(MNL_SOCKET_BUFFER_SIZE); + if (!nlg->buf) + goto err_buf_alloc; + + nlg->nl = mnl_socket_open(NETLINK_GENERIC); + if (!nlg->nl) + goto err_mnl_socket_open; + + err = mnl_socket_bind(nlg->nl, 0, MNL_SOCKET_AUTOPID); + if (err < 0) + goto err_mnl_socket_bind; + + nlg->portid = mnl_socket_get_portid(nlg->nl); + + nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY, + NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1); + mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, family_name); + + err = mnlg_socket_send(nlg, nlh); + if (err < 0) + goto err_mnlg_socket_send; + + err = mnlg_socket_recv_run(nlg, get_family_id_cb, &nlg->id); + if (err < 0) + goto err_mnlg_socket_recv_run; + + nlg->version = version; + return nlg; + +err_mnlg_socket_recv_run: +err_mnlg_socket_send: +err_mnl_socket_bind: + mnl_socket_close(nlg->nl); +err_mnl_socket_open: + free(nlg->buf); +err_buf_alloc: + free(nlg); + return NULL; +} + +void mnlg_socket_close(struct mnlg_socket *nlg) +{ + mnl_socket_close(nlg->nl); + free(nlg->buf); + free(nlg); +} diff --git a/libactuator/mnlg.h b/libactuator/mnlg.h new file mode 100644 index 0000000..807420d --- /dev/null +++ b/libactuator/mnlg.h @@ -0,0 +1,37 @@ +/* + * mnlg.h - Generic Netlink helpers for libmnl + * Copyright (C) 2017 Jiri Pirko <jiri@resnulli.us> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _MNLG_H_ +#define _MNLG_H_ + +#include <libmnl/libmnl.h> + +struct mnlg_socket; + +struct nlmsghdr *mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd, + uint16_t flags); +int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh); +int mnlg_socket_recvfrom(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data); +int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data); +int mnlg_socket_group_add(struct mnlg_socket *nlg, const char *group_name); +int mnlg_socket_get_fd(struct mnlg_socket *nlg); +struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version); +void mnlg_socket_close(struct mnlg_socket *nlg); + +#endif /* _MNLG_H_ */ diff --git a/man/Makefile.am b/man/Makefile.am new file mode 100644 index 0000000..dde0b7d --- /dev/null +++ b/man/Makefile.am @@ -0,0 +1 @@ +dist_man8_MANS = act.8 diff --git a/man/act-dev.8 b/man/act-dev.8 new file mode 100644 index 0000000..c339ca4 --- /dev/null +++ b/man/act-dev.8 @@ -0,0 +1,85 @@ +.TH ACT\-DEV 8 "14 Mar 2016" "libactuator" "Linux" +.SH NAME +act-dev \- Actuator device manipulation +.SH SYNOPSIS +.sp +.ad l +.in +8 +.ti -8 +.B act +.RI "[ " OPTIONS " ]" +.B dev +.RI " { " COMMAND " | " +.BR help " }" +.sp + +.ti -8 +.IR OPTIONS " := { " +\fB\-V\fR[\fIersion\fR] } + +.ti -8 +.B act dev show +.RI "[ " DEV " ]" + +.ti -8 +.BR "act dev move " +.IR DEV +.BR value +.IR VALUE + +.ti -8 +.BR "act dev stop " +.IR DEV + +.ti -8 +.B act dev help + +.SH "DESCRIPTION" +.SS act dev show - display actuator device attributes + +.PP +.B "DEV" +- specifies the actuator device to show. +If this argument is omitted all actuator devices are listed. + +.SS act dev move - move actuator to desired position according to value +Units of the value are per-actuator. + +.PP +.B "DEV" +- specifies the actuator device to operate on. + +.in +4 +Format is: +.in +2 +BUS_NAME/BUS_ADDRESS + +.SS act dev stop - stops actuator movement + +.PP +.B "DEV" +- specifies the actuator device to operate on. + +.SH "EXAMPLES" +.PP +act dev show +.RS 4 +Shows the state of all actuator devices on the system. +.RE +.PP +act dev show usb/1-2/0 +.RS 4 +Shows the state of specified actuator device. +.RE +.PP +act dev move usb/1-2/0 value 100000 +.RS 4 +Move the actuator to position 100000 (according to actuator units). + +.SH SEE ALSO +.BR act (8), +.br + +.SH AUTHOR +Jiri Pirko <jiri@resnulli.us> + diff --git a/man/act.8 b/man/act.8 new file mode 100644 index 0000000..3b18dfd --- /dev/null +++ b/man/act.8 @@ -0,0 +1,71 @@ +.TH ACT 8 "17 Jul 2017" "libactuator" "Linux" +.SH NAME +act \- Actuator tool +.SH SYNOPSIS +.sp +.ad l +.in +8 +.ti -8 +.B act +.RI "[ " OPTIONS " ] " OBJECT " { " COMMAND " | " +.BR help " }" +.sp + +.ti -8 +.IR OBJECT " := { " +.BR dev " | " monitor " }" +.sp + +.ti -8 +.IR OPTIONS " := { " +\fB\-V\fR[\fIersion\fR] } + +.SH OPTIONS + +.TP +.BR "\-V" , " --Version" +Print the version of the +.B act +utility and exit. + +.SS +.I OBJECT + +.TP +.B dev +- actuator device. + +.TP +.B monitor +- watch for actuator events. + +.SS +.I COMMAND + +Specifies the action to perform on the object. +The set of possible actions depends on the object type. +As a rule, it is possible to +.B show +(or +.B list +) objects, but some objects do not allow all of these operations +or have some additional commands. The +.B help +command is available for all objects. It prints +out a list of available commands and argument syntax conventions. +.sp +If no command is given, some default command is assumed. +Usually it is +.B list +or, if the objects of this class cannot be listed, +.BR "help" . + +.SH EXIT STATUS +Exit status is 0 if command was successful or a positive integer upon failure. + +.SH SEE ALSO +.BR act-dev (8), +.br + +.SH AUTHOR +Jiri Pirko <jiri@resnulli.us> diff --git a/utils/Makefile.am b/utils/Makefile.am new file mode 100644 index 0000000..dc94991 --- /dev/null +++ b/utils/Makefile.am @@ -0,0 +1,10 @@ +MAINTAINERCLEANFILES = Makefile.in + +ACLOCAL_AMFLAGS = -I m4 + +AM_CFLAGS = -I${top_srcdir}/include + +act_LDADD = $(top_builddir)/libactuator/libactuator.la + +bin_PROGRAMS=act +act_SOURCES=act.c diff --git a/utils/act.c b/utils/act.c new file mode 100644 index 0000000..5b9fc07 --- /dev/null +++ b/utils/act.c @@ -0,0 +1,544 @@ +/* + * act.c - Actuator manipulation tool + * Copyright (C) 2017 Jiri Pirko <jiri@resnulli.us> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdbool.h> +#include <limits.h> +#include <getopt.h> +#include <errno.h> +#include <actuator.h> +#include <sys/select.h> +#include <linux/actuator.h> + +#define pr_err(args...) fprintf(stderr, ##args) +#define pr_out(args...) \ + do { \ + fprintf(stdout, ##args); \ + } while (0) + +#define BIT(nr) (1UL << (nr)) +#define ACT_OPT_HANDLE BIT(0) +#define ACT_OPT_VALUE BIT(1) + +struct act_opts { + uint32_t present; /* flags of present items */ + struct actuator_handle handle; + uint32_t value; +}; + +struct act { + struct actuator *actuator; + int argc; + char **argv; + struct act_opts opts; +}; + +static int act_argc(struct act *act) +{ + return act->argc; +} + +static char *act_argv(struct act *act) +{ + if (act_argc(act) == 0) + return NULL; + return *act->argv; +} + +static void act_arg_inc(struct act *act) +{ + if (act_argc(act) == 0) + return; + act->argc--; + act->argv++; +} + +static char *act_argv_next(struct act *act) +{ + char *ret; + + if (act_argc(act) == 0) + return NULL; + + ret = *act->argv; + act_arg_inc(act); + return ret; +} + +static int strcmpx(const char *str1, const char *str2) +{ + if (strlen(str1) > strlen(str2)) + return -1; + return strncmp(str1, str2, strlen(str1)); +} + +static bool act_argv_match(struct act *act, const char *pattern) +{ + if (act_argc(act) == 0) + return false; + return strcmpx(act_argv(act), pattern) == 0; +} + +static bool act_no_arg(struct act *act) +{ + return act_argc(act) == 0; +} + +static unsigned int strslashcount(char *str) +{ + unsigned int count = 0; + char *pos = str; + + while ((pos = strchr(pos, '/'))) { + count++; + pos++; + } + return count; +} + +static int strslashrsplit(char *str, char **before, char **after) +{ + char *slash; + + slash = strrchr(str, '/'); + if (!slash) + return -EINVAL; + *slash = '\0'; + *before = str; + *after = slash + 1; + return 0; +} + +static int strtouint32_t(const char *str, uint32_t *p_val) +{ + char *endptr; + unsigned long int val; + + val = strtoul(str, &endptr, 10); + if (endptr == str || *endptr != '\0') + return -EINVAL; + if (val > UINT_MAX) + return -ERANGE; + *p_val = val; + return 0; +} + +static int act_argv_handle(struct act *act, struct actuator_handle *handle) +{ + char *str = act_argv_next(act); + unsigned int slash_count; + char *indexstr = indexstr; + char *reststr = reststr; + int err; + + if (!str) { + pr_err("Port identification (\"bus_name/dev_name/index\") expected.\n"); + return -EINVAL; + } + slash_count = strslashcount(str); + if (slash_count != 2) { + pr_err("Wrong port identification string format.\n"); + pr_err("Expected \"bus_name/dev_name/index\".\n"); + return -EINVAL; + } + + strslashrsplit(str, &reststr, &indexstr); + err = strtouint32_t(indexstr, &handle->index); + if (err) { + pr_err("Index \"%s\" is not a number or not within range\n", + indexstr); + return err; + } + strslashrsplit(reststr, (char **) &handle->bus_name, + (char **) &handle->dev_name); + return 0; +} + +static int act_argv_uint32_t(struct act *act, uint32_t *p_val) +{ + char *str = act_argv_next(act); + int err; + + if (!str) { + pr_err("Unsigned number argument expected\n"); + return -EINVAL; + } + + err = strtouint32_t(str, p_val); + if (err) { + pr_err("\"%s\" is not a number or not within range\n", str); + return err; + } + return 0; +} + +static int act_argv_parse(struct act *act, uint32_t o_required) +{ + struct act_opts *opts = &act->opts; + uint32_t o_found = 0; + int err; + + if (o_required & ACT_OPT_HANDLE) { + err = act_argv_handle(act, &opts->handle); + if (err) + return err; + o_found |= ACT_OPT_HANDLE; + } + + while (act_argc(act)) { + if (act_argv_match(act, "value") && (o_required & ACT_OPT_VALUE)) { + act_arg_inc(act); + err = act_argv_uint32_t(act, &opts->value); + if (err) + return err; + o_found |= ACT_OPT_VALUE; + } else { + pr_err("Unknown option \"%s\"\n", act_argv(act)); + return -EINVAL; + } + } + + opts->present = o_found; + + if ((o_required & ACT_OPT_VALUE) && !(o_found & ACT_OPT_VALUE)) { + pr_err("Value option expected.\n"); + return -EINVAL; + } + + return 0; +} + +static void act_cmd_dev_help(void) +{ + pr_err("Usage: act dev show [ DEV ]\n"); + pr_err(" act dev move DEV value VALUE\n"); + pr_err(" act dev stop DEV\n"); +} + +static void pr_out_handle_begin(struct act *act, struct actuator_handle *handle) +{ + pr_out("%s/%s/%u:", handle->bus_name, handle->dev_name, handle->index); +} + +static void pr_out_handle_end(struct act *act) +{ + pr_out("\n"); +} + +static const char *act_event_type_name(enum actuator_event_type event_type) +{ + switch (event_type) { + case ACTUATOR_EVENT_TYPE_GET: + return "get"; + case ACTUATOR_EVENT_TYPE_NEW: + return "new"; + case ACTUATOR_EVENT_TYPE_DEL: + return "del"; + default: + return "<unknown event_type>"; + } +} + +static void pr_out_event_type_begin(struct act *act, + enum actuator_event_type event_type) +{ + pr_out("%s: ", act_event_type_name(event_type)); +} + +static void pr_out_event_type_end(struct act *act) +{ +} + +static void pr_out_str(struct act *act, const char *label, const char *str) +{ + pr_out(" %s %s", label, str); +} + +static void pr_out_u32(struct act *act, const char *label, uint32_t val) +{ + pr_out(" %s %u", label, val); +} + +static const char *act_type_name(uint8_t type) +{ + switch (type) { + case ACTUATOR_TYPE_LINEAR: + return "linear"; + default: + return "<unknown type>"; + } +} + +static const char *act_units_name(uint8_t units) +{ + switch (units) { + case ACTUATOR_UNITS_UM: + return "um"; + default: + return "<unknown units>"; + } +} + +static const char *act_move_state_name(uint8_t move_state) +{ + switch (move_state) { + case ACTUATOR_MOVE_STATE_STOPPED: + return "stopped"; + case ACTUATOR_MOVE_STATE_POSITIVE: + return "positive"; + case ACTUATOR_MOVE_STATE_NEGATIVE: + return "negative"; + default: + return "<unknown move_state>"; + } +} + +static void pr_out_actuator_info(struct act *act, struct actuator_info *info) +{ + struct actuator_handle handle; + + actuator_info_handle(info, &handle); + pr_out_handle_begin(act, &handle); + pr_out_str(act, "driver_name", actuator_info_driver_name(info)); + pr_out_str(act, "type", act_type_name(actuator_info_type(info))); + pr_out_str(act, "units", act_units_name(actuator_info_units(info))); + if (actuator_info_has_value(info)) + pr_out_u32(act, "value", actuator_info_value(info)); + if (actuator_info_has_move_state(info)) + pr_out_str(act, "move_state", + act_move_state_name(actuator_info_move_state(info))); + pr_out_handle_end(act); +} + +static void act_cmd_dev_show_cb(struct actuator *actuator, + struct actuator_info *info, void *cb_priv) +{ + struct act *act = cb_priv; + + pr_out_actuator_info(act, info); +} + +static int act_cmd_dev_show(struct act *act) +{ + return actuator_get(act->actuator, NULL, &act_cmd_dev_show_cb, act); +} + +static int act_cmd_dev_move(struct act *act) +{ + int err; + + err = act_argv_parse(act, ACT_OPT_HANDLE | ACT_OPT_VALUE); + if (err) + return err; + return actuator_move(act->actuator, &act->opts.handle, + "v", act->opts.value); +} + +static int act_cmd_dev_stop(struct act *act) +{ + int err; + + err = act_argv_parse(act, ACT_OPT_HANDLE); + if (err) + return err; + return actuator_stop(act->actuator, &act->opts.handle, ""); +} + +static void act_cmd_mon_show_cb(struct actuator *actuator, + struct actuator_info *info, void *cb_priv) +{ + struct act *act = cb_priv; + + pr_out_event_type_begin(act, actuator_info_event_type(info)); + pr_out_actuator_info(act, info); + pr_out_event_type_end(act); +} + +static int act_cmd_mon(struct act *act) +{ + fd_set fds; + int fdmax; + struct timeval tv = {}; + int fd = actuator_event_fd_get(act->actuator); + int ret; + int err; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + fdmax = fd + 1; + + actuator_event_cb_set(act->actuator, ACTUATOR_EVENT_TYPE_GET, + &act_cmd_mon_show_cb, act); + actuator_event_cb_set(act->actuator, ACTUATOR_EVENT_TYPE_NEW, + &act_cmd_mon_show_cb, act); + actuator_event_cb_set(act->actuator, ACTUATOR_EVENT_TYPE_DEL, + &act_cmd_mon_show_cb, act); + + do { + ret = select(fdmax, &fds, NULL, NULL, &tv); + if (ret == -1) { + if (errno == EINTR) + return 0; + return -errno; + } + err = actuator_event_process(act->actuator); + if (err) + return err; + } while (true); +} + +static int act_cmd_dev(struct act *act) +{ + if (act_argv_match(act, "help")) { + act_cmd_dev_help(); + return 0; + } else if (act_argv_match(act, "show") || + act_argv_match(act, "list") || act_no_arg(act)) { + act_arg_inc(act); + return act_cmd_dev_show(act); + } else if (act_argv_match(act, "move")) { + act_arg_inc(act); + return act_cmd_dev_move(act); + } else if (act_argv_match(act, "stop")) { + act_arg_inc(act); + return act_cmd_dev_stop(act); + } + pr_err("Command \"%s\" not found\n", act_argv(act)); + return -ENOENT; +} + +static void help(void) +{ + pr_err("Usage: act [ OPTIONS ] OBJECT { COMMAND | help }\n" + "where OBJECT := { dev | monitor }\n" + " OPTIONS := { -V[ersion] }\n"); +} + +static int act_cmd(struct act *act) +{ + if (act_argv_match(act, "help") || act_no_arg(act)) { + help(); + return 0; + } else if (act_argv_match(act, "dev")) { + act_arg_inc(act); + return act_cmd_dev(act); + } else if (act_argv_match(act, "monitor")) { + act_arg_inc(act); + return act_cmd_mon(act); + } + pr_err("Object \"%s\" not found\n", act_argv(act)); + return -ENOENT; +} + +static int act_init(struct act *act, int argc, char **argv) +{ + int err; + + act->argc = argc; + act->argv = argv; + + err = actuator_init(&act->actuator); + if (err) { + pr_err("Failed to connect to actuator Netlink\n"); + return -errno; + } + + return 0; +} + +static void act_fini(struct act *act) +{ + actuator_fini(act->actuator); +} + +static struct act *act_alloc(void) +{ + struct act *act; + + act = calloc(1, sizeof(*act)); + if (!act) + return NULL; + return act; +} + +static void act_free(struct act *act) +{ + free(act); +} + +int main(int argc, char **argv) +{ + static const struct option long_options[] = { + { "Version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + struct act *act; + int opt; + int err; + int ret; + + act = act_alloc(); + if (!act) { + pr_err("Failed to allocate memory for context\n"); + return EXIT_FAILURE; + } + + while ((opt = getopt_long(argc, argv, "V", + long_options, NULL)) >= 0) { + + switch (opt) { + case 'V': + printf("act "PACKAGE_VERSION"\n"); + ret = EXIT_SUCCESS; + goto act_free; + default: + pr_err("Unknown option.\n"); + help(); + ret = EXIT_FAILURE; + goto act_free; + } + } + + argc -= optind; + argv += optind; + + err = act_init(act, argc, argv); + if (err) { + ret = EXIT_FAILURE; + goto act_free; + } + + err = act_cmd(act); + if (err) { + ret = EXIT_FAILURE; + goto act_fini; + } + + ret = EXIT_SUCCESS; + +act_fini: + act_fini(act); +act_free: + act_free(act); + + return ret; +} |