pax_global_header00006660000000000000000000000064144577036460014532gustar00rootroot0000000000000052 comment=29c79c9e3bd325c481bd9c52fdd520665bc0e28b iio-sensor-proxy-3.5/000077500000000000000000000000001445770364600146475ustar00rootroot00000000000000iio-sensor-proxy-3.5/.editorconfig000066400000000000000000000002001445770364600173140ustar00rootroot00000000000000root = true [*] indent_style = tab indent_size = 8 charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true iio-sensor-proxy-3.5/.gitignore000066400000000000000000000000621445770364600166350ustar00rootroot00000000000000iio-sensor-proxy data/net.hadess.SensorProxy.conf iio-sensor-proxy-3.5/.gitlab-ci.yml000066400000000000000000000023501445770364600173030ustar00rootroot00000000000000image: fedora:rawhide variables: DEPENDENCIES: gcc glibc-langpack-fr gtk-doc pkgconfig(udev) pkgconfig(systemd) pkgconfig(gio-2.0) pkgconfig(gudev-1.0) pkgconfig(polkit-gobject-1) systemd gtk3-devel meson git python3-gobject python3-dbusmock python3-psutil umockdev workflow: rules: - if: $CI_PIPELINE_SOURCE == 'merge_request_event' - if: $CI_PIPELINE_SOURCE == 'push' build_stable: before_script: # Undo delangification present in the Fedora Docker images - rm -f /etc/rpm/macros.image-language-conf - dnf update -y && dnf install -y $DEPENDENCIES - dnf reinstall -y glib2 script: - meson -Dtests=true -Dgtk_doc=true -Dgtk-tests=true _build - ninja -v -C _build - ninja -v -C _build install - ninja -v -C _build uninstall - ninja -v -C _build dist - meson test -C _build artifacts: when: always name: "iio-sensor-proxy-${CI_COMMIT_REF_NAME}" paths: - "${CI_PROJECT_DIR}/_build/meson-logs" - "${CI_PROJECT_DIR}/_build/meson-dist" iio-sensor-proxy-3.5/COPYING000066400000000000000000001045131445770364600157060ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. 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 them 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 prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. 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. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey 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; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. 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. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 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. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. 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 state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 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, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program 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, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU 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 Lesser General Public License instead of this License. But first, please read . iio-sensor-proxy-3.5/NEWS000066400000000000000000000117571445770364600153610ustar00rootroot000000000000003.5 --- This update fixes sensor hotplugging, and fixes some sensors not working when they were assigned a sampling frequency they did not support. This update also adds more tests, and corrects the location of the D-Bus policy file. 3.4 --- This update adds polkit checks for whether a user is allowed to claim a sensor. It also fixes the ambient light sensor readings on some systems, a regression in the fake-input-accelerometer test binary, and a number of memory leaks. 3.3 --- This update fixes a bug left-over in one of the 3.2 bug fixes where some accelerometers would fail to initialise. 3.2 --- This update fixes a number of problems new to 3.1 where we started initialising internationalisation support in the daemon, and caused problems parsing numbers with decimal separator. This update also requires an updated version of libgudev to fix one of the bugs uncovered. 3.1 --- This release includes a number of changes related to the maintenance of the project, including porting to meson as a build system, and adding a test suite. A long-standing problem with property changes being sent as a broadcast was also fixed, now only programs which register will receive notifications. This release also fixes sensor support for accelerometers with different scales on different axis, adds support for RGBC light sensors, and reads the proximity threshold from sysfs for DeviceTree devices. 3.0 --- This release adds support for a number of sensors that didn't work before, usually due to being combined with other sensors of the same or different types (eg. dual-accelerometers, light/proximity combined sensors, etc.), and adds support for kernel-exported mount-matrix properties on Device Tree hardware. 2.8 --- This release adds support for accelerometers in ChromeBooks, and also fixes the screen orientation in favour of portrait when rotating the display instead of favouring the previous orientation. 2.7 --- This release fixes broken sensor readings on multiple platforms due to a compiler optimisation. 2.6 --- This release adds support for mount matrices coming from the kernel itself, checking those more thoroughly for validity. On devices with multiple accelerometers, the ones in the base are now ignored. Support for sensors with uncommon formats was already enhanced (or fixed depending on the device). A couple of possible crashes and memory leaks were also fixed. 2.5 --- This release fixes a number of build warnings and bugs, as well as ensuring that some devices have non-0 update frequencies, and that the sensor scale is set to 1.0 when the device does not export one. 2.4 --- This release fixes data being incorrectly read from sensors due to a naively broken compilation fix. It also supports Geoclue running as a different user than "geoclue". 2.3 --- This release fixes API documentation not being in the tarball, removes a work-around for long-standing kernel bug (see README.md for details), and ignores accelerometers that are part of gaming devices. A number of build warnings were also fixed. 2.2 --- This release fixes iio-sensor-proxy not receiving udev events because of the excessive lockdown added in version 2.1. This also fixes a possible crash on startup due to a race condition. 2.1 --- This release fixes a security problem that will affect services other than iio-sensor-proxy, that have a system D-Bus interface. Upgrading to this version is highly recommended. - Make D-Bus policy rules only affect SensorProxy itself - Lockdown service through systemd - Fix startup problem on machines with a Compass device 2.0 --- This release adds build fixes, fixes the detection of a number of devices that should be supported, stops trying to use devices that shouldn't, and adds support for the "mount-matrix" property, which can be used to fix the orientation of accelerometers on devices where the default does not work. - Add support for ACCEL_MOUNT_MATRIX udev property - Fix startup failure on certain devices - Work-around possible kernel bug on certain devices - Better supported/unsupported sensors detection - Better debug output 1.3 --- This release adds build fixes, fixes accelerometer unit scaling and really fixes regression with the IIO accelerometer in kernel 4.8 that was supposed to be fixed in version 1.2. 1.2 --- This release mainly adds better debug output, and updated testing instructions in the README. - Fix regression with IIO accelerometer in kernel 4.8 - Fix possible crash in compass handling 1.1 --- This release adds support for Compasses (only accessible to GeoClue). - Fix service not starting in a number of cases - Fix excessive CPU usage monitoring light sensors on Macs - Support light sensors on some Samsung laptops - Fix incorrect sensor readings on some devices due to unit scaling 1.0 --- This is the first stable release of iio-sensor-proxy. It now uses a D-Bus API, and supports accelerometers and ambient light sensors. 0.1 --- This is the first version of iio-sensor-proxy. It supports accelerometers, proxying IIO sensors, and mimicking input device accelerometers. iio-sensor-proxy-3.5/README.md000066400000000000000000000210401445770364600161230ustar00rootroot00000000000000iio-sensor-proxy ================ IIO sensors to D-Bus proxy See https://developer.gnome.org/iio-sensor-proxy/1.0/ for developer information. Installation ------------ ```sh $ meson _build -Dprefix=/usr $ ninja -v -C _build install ``` It requires libgudev, systemd (>= 233 for the accelerometer quirks) and polkit-gobject. Running the tests will require gobject-introspection support for Python, [python-dbusmock](https://github.com/martinpitt/python-dbusmock/), [umockdev](https://github.com/martinpitt/umockdev) and [psutil](https://github.com/giampaolo/psutil). Usage ----- With a GNOME 3.18 (or newer) based system, orientation changes will automatically be applied when rotating the panel, ambient light will be used to change the screen brightness, and GeoClue will be able to read the compass data to show the direction in Maps. Note that nothing in iio-sensor-proxy is GNOME specific, or relies on GNOME. GNOME and GeoClue use the data provided by iio-sensor-proxy, other desktop environments are more than welcome to use this as a basis for their own integration. Debugging --------- Note that a number of kernel bugs will prevent it from working correctly on some machines so please make sure to use the latest upstream kernel (kernel crashes on the Surface Pro, sensor failing to work after suspend on the Yoga Pro, etc.). You can verify that sensors are detected by running `udevadm info --export-db` and checking for an output resembling this one: ``` P: /devices/platform/80860F41:04/i2c-12/i2c-BMA250E:00/iio:device0 N: iio:device0 E: DEVNAME=/dev/iio:device0 E: DEVPATH=/devices/platform/80860F41:04/i2c-12/i2c-BMA250E:00/iio:device0 E: DEVTYPE=iio_device E: MAJOR=249 E: MINOR=0 E: SUBSYSTEM=iio E: SYSTEMD_WANTS=iio-sensor-proxy.service E: TAGS=:systemd: E: USEC_INITIALIZED=7750292 ``` You can now check whether a sensor is detected by running: ``` gdbus introspect --system --dest net.hadess.SensorProxy --object-path /net/hadess/SensorProxy ``` After that, use `monitor-sensor` to see changes in the ambient light sensor or the accelerometer. Note that compass changes are only available to GeoClue but if you need to ensure that GeoClue is getting correct data you can run: ```sh su -s /bin/sh geoclue -c monitor-sensor` ``` If that doesn't work, please file an issue, make sure iio-sensor-proxy is new enough and attach the output of: ```sh /usr/libexec/iio-sensor-proxy -v -r ``` running as `root`. Note that, before filing any issues against iio-sensor-proxy, you'll want to make doubly-sure that the IIO device actually works, using `iio_generic_buffer` (usually packaged as part of the `kernel-tools` package). For example, to test IIO sensor `0`: ```sh sudo iio_generic_buffer -A -N 0 ``` Accelerometer orientation ------------------------- iio-sensor-proxy expects the accelerometer readings to match those defined in the [Linux IIO](https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git/tree/Documentation/ABI/testing/sysfs-bus-iio#n1638) documentation, the [Windows Integrating Motion and Orientation Sensors](https://docs.microsoft.com/en-us/windows-hardware/design/whitepapers/integrating-motion-and-orientation-sensors) whitepaper, and the [Android sensor documentation](https://developer.android.com/reference/android/hardware/SensorEvent). When the accelerometer is not mounted the same way as the screen, we need to modify the readings from the accelerometer to make sure that the computed orientation matches the screen one. This is done using a “mount matrix”, a matrix that applied to the reading will correct that reading to match the expected orientation, whether: - selecting a value for an axis (with a $`1`$) - negating that value (with a $`-1`$) - ignoring that value (with a $`0`$) ```math \left[ {\begin{array}{ccc} accel~x & accel~y & accel~z\\ \end{array} } \right] * \left[ {\begin{array}{ccc} x_1 & y_1 & z_1\\ x_2 & y_2 & z_2\\ x_3 & y_3 & z_3\\ \end{array} } \right] = \left[ {\begin{array}{ccc} corrected~x & corrected~y & corrected~z\\ \end{array} } \right] ``` For each axis of the original accelerometer reading, a corresponding line in the mount matrix will be used to compute which axis it should be assigned to in the corrected readings. For example, the first line of the matrix will define whether the $`accel~x`$ value will correspond to the corrected $`x`$, $`y`$ or $`z`$ axis (with a $`1`$ for that axis, and $`0`$ for the others), or needs to be negated (by setting it to $`-1`$). A matrix of $` \left[ {\begin{array}{ccc} 0 & 0 & 1\\ 0 & -1 & 0\\ 1 & 0 & 0\\ \end{array} } \right] `$ will swap the $`x`$ and $`z`$ values, and negate the $`y`$ reading. A matrix of $` \left[ {\begin{array}{ccc} 1 & 0 & 0\\ 0 & 1 & 0\\ 0 & 0 & 1\\ \end{array} } \right] `$ (the identity matrix) is the default, and will not change any of the read values. Each accelerometer axis reading can only be assigned (negated or not) to a single corrected axis reading, and each corrected axis reading can only come from a single accelerometer axis reading. `iio-sensor-proxy` reads this information from the device's `ACCEL_MOUNT_MATRIX` udev property. See [60-sensor.hwdb](https://github.com/systemd/systemd/blob/master/hwdb.d/60-sensor.hwdb) for details. For device-tree based devices, and some ACPI ones too, the `mount_matrix`, `in_accel_mount_matrix` and `in_mount_matrix` sysfs files can also be used to export that information directly from the kernel. The mount matrix format used in systemd's hwdb and the IIO subsystem is a one-line representation of the matrix above: ```math x_1, y_1, z_1; x_2, y_2, z_2; x_3, y_3, z_3 ``` Accelerometer testing (without an accelerometer) ------------------------------------------------ If your hardware lacks an accelerometer, you can run the `fake-input-accelerometer` binary as root. This should create a new virtual device on the system, which can be interacted through `monitor-sensor`, `evtest`, or raw D-Bus. Use the printed keyboard commands to change the calculated orientation of that accelerometer. Compass testing --------------- Only the GeoClue daemon (as the geoclue user) is allowed to access the `net.hadess.SensorProxy.Compass` interface, the results of which it will propagate to clients along with positional information. If your device does not contain a compass, you can run tests with: - If your device does not contain a real compass: - Add FAKE_COMPASS=1 to the environment of `iio-sensor-proxy.service` if your device does not contain a real one - Run the daemon manually with `systemctl start iio-sensor-proxy.service` - Verify that iio-sensor-proxy is running with `systemctl` or `ps` - As root, get a shell as the `geoclue` user with `su -s /bin/bash geoclue` - Run, as the `geoclue` user, `monitor-sensor` Proximity sensor near detection ------------------------------- Whether an object is considered near to a proximity sensor depends on the sensor configuration and scale but also on the device's physical properties (e.g. the distance of the sensor from the covering glass) so there is no good device-independent default. `iio-sensor-proxy` reads this information from the device's `PROXIMITY_NEAR_LEVEL` udev property. See [60-sensor.hwdb](https://github.com/systemd/systemd/blob/master/hwdb.d/60-sensor.hwdb) for details. For device-tree based devices, exporting the information through the kernel is done via the [near-level property](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/iio/common.yaml#n22). Known problems -------------- Every Linux kernel from 4.3 up to version 4.12 had a bug that made made iio-sensor-proxy fail to see any events coming from sensors until the sensor was power-cycled (unplugged and replugged, or suspended and resumed). The bug was finally fixed in [this commit](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f1664eaacec31035450132c46ed2915fd2b2049a) in the upstream kernel and backported to stable releases. If you experience unresponsive sensors, ask your distributor to make sure this patch was applied to the version you're using. The IIO sensors regressed again not long afterwards, which got fixed in [this commit](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=6f92253024d9d947a4f454654840ce479e251376). Again, make sure that your kernel contains this fix. References ---------- - [sensorfw](https://git.merproject.org/mer-core/sensorfw/tree/master) - [android-iio-sensors-hal](https://github.com/01org/android-iio-sensors-hal) - [Sensor orientation on MSDN](https://msdn.microsoft.com/en-us/windows/uwp/devices-sensors/sensor-orientation) iio-sensor-proxy-3.5/data/000077500000000000000000000000001445770364600155605ustar00rootroot00000000000000iio-sensor-proxy-3.5/data/80-iio-sensor-proxy.rules000066400000000000000000000030251445770364600223270ustar00rootroot00000000000000# iio-sensor-proxy # IIO sensor to D-Bus proxy ACTION=="remove", GOTO="iio_sensor_proxy_end" # Set the sensor type for all the types we recognise SUBSYSTEM=="hwmon", TEST=="light", ENV{IIO_SENSOR_PROXY_TYPE}+="hwmon-als" SUBSYSTEM=="iio", TEST=="in_accel_x_raw", TEST=="in_accel_y_raw", TEST=="in_accel_z_raw", ENV{IIO_SENSOR_PROXY_TYPE}+="iio-poll-accel" SUBSYSTEM=="iio", TEST=="scan_elements/in_accel_x_en", TEST=="scan_elements/in_accel_y_en", TEST=="scan_elements/in_accel_z_en", ENV{IIO_SENSOR_PROXY_TYPE}+="iio-buffer-accel" SUBSYSTEM=="iio", TEST=="scan_elements/in_rot_from_north_magnetic_tilt_comp_en", ENV{IIO_SENSOR_PROXY_TYPE}+="iio-buffer-compass" SUBSYSTEM=="iio", TEST=="in_illuminance_input", ENV{IIO_SENSOR_PROXY_TYPE}+="iio-poll-als" SUBSYSTEM=="iio", TEST=="in_illuminance0_input", ENV{IIO_SENSOR_PROXY_TYPE}+="iio-poll-als" SUBSYSTEM=="iio", TEST=="in_illuminance_raw", ENV{IIO_SENSOR_PROXY_TYPE}+="iio-poll-als" SUBSYSTEM=="iio", TEST=="in_intensity_clear_raw", ENV{IIO_SENSOR_PROXY_TYPE}+="iio-poll-als" SUBSYSTEM=="iio", TEST=="scan_elements/in_intensity_both_en", ENV{IIO_SENSOR_PROXY_TYPE}+="iio-buffer-als" SUBSYSTEM=="iio", TEST=="in_proximity_raw", ENV{IIO_SENSOR_PROXY_TYPE}+="iio-poll-proximity" SUBSYSTEM=="input", ENV{ID_INPUT_ACCELEROMETER}=="1", ENV{IIO_SENSOR_PROXY_TYPE}+="input-accel" ENV{IIO_SENSOR_PROXY_TYPE}=="", GOTO="iio_sensor_proxy_end" # We got here because we have a sensor type, which means we need the service TAG+="systemd", ENV{SYSTEMD_WANTS}+="iio-sensor-proxy.service" LABEL="iio_sensor_proxy_end" iio-sensor-proxy-3.5/data/iio-sensor-proxy.service.in000066400000000000000000000006501445770364600230160ustar00rootroot00000000000000[Unit] Description=IIO Sensor Proxy service [Service] Type=dbus BusName=net.hadess.SensorProxy ExecStart=@libexecdir@/iio-sensor-proxy #Uncomment this to enable debug #Environment="G_MESSAGES_DEBUG=all" # Lockdown ProtectSystem=strict ProtectControlGroups=true ProtectHome=true ProtectKernelModules=true PrivateTmp=true RestrictAddressFamilies=AF_UNIX AF_LOCAL AF_NETLINK MemoryDenyWriteExecute=true RestrictRealtime=true iio-sensor-proxy-3.5/data/meson.build000066400000000000000000000014661445770364600177310ustar00rootroot00000000000000install_data( '80-iio-sensor-proxy.rules', install_dir: udev_rules_dir ) data_conf = configuration_data() data_conf.set('libexecdir', libexecdir) data_conf.set('geoclue_user', get_option('geoclue-user')) configure_file( input: 'iio-sensor-proxy.service.in', output: 'iio-sensor-proxy.service', configuration: data_conf, install_dir: systemd_system_unit_dir, ) configure_file( input: 'net.hadess.SensorProxy.conf.in', output: 'net.hadess.SensorProxy.conf', configuration: data_conf, install_dir: dbusconfdir ) polkit_policy = 'net.hadess.SensorProxy.policy' if xmllint.found() test(polkit_policy, xmllint, args: [ '--noout', meson.source_root() / 'data' / polkit_policy, ]) endif install_data( polkit_policy, install_dir: polkit_policy_directory, ) iio-sensor-proxy-3.5/data/net.hadess.SensorProxy.conf.in000066400000000000000000000040401445770364600234000ustar00rootroot00000000000000 iio-sensor-proxy-3.5/data/net.hadess.SensorProxy.policy000066400000000000000000000012561445770364600233530ustar00rootroot00000000000000 iio-sensor-proxy https://gitlab.freedesktop.org/hadess/iio-sensor-proxy Claim Access to a Sensor Privileges are required to claim a sensor. no no yes iio-sensor-proxy-3.5/docs/000077500000000000000000000000001445770364600155775ustar00rootroot00000000000000iio-sensor-proxy-3.5/docs/iio-sensor-proxy-docs.xml000066400000000000000000000047561445770364600225310ustar00rootroot00000000000000 ]> Sensor Proxy Reference Manual Version &version; Bastien Nocera
hadess@hadess.net
2015 Red Hat, Inc. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. You may obtain a copy of the GNU Free Documentation License from the Free Software Foundation by visiting their Web site or by writing to:
The Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
Many of the names used by companies to distinguish their products and services are claimed as trademarks. Where those names appear in any GNOME documentation, and those trademarks are made aware to the members of the GNOME Documentation Project, the names have been printed in caps or initial caps.
D-Bus API Reference This part documents the D-Bus interface used to access the Sensor Proxy service. Index License FIXME: MISSING XINCLUDE CONTENT
iio-sensor-proxy-3.5/docs/meson.build000066400000000000000000000025301445770364600177410ustar00rootroot00000000000000content_files = files() version_conf = configuration_data() version_conf.set('VERSION', meson.project_version()) content_files += configure_file( input: 'version.xml.in', output: 'version.xml', configuration: version_conf, ) content_files += gnome.gdbus_codegen( meson.project_name(), sources: meson.source_root() / 'src' / 'net.hadess.SensorProxy.xml', interface_prefix: 'net.hadess', namespace: 'SensorProxy', docbook: 'docs', build_by_default: true, ) gnome.gtkdoc( meson.project_name(), main_xml: meson.project_name() + '-docs.xml', content_files: content_files, src_dir: [ meson.source_root() /'src', meson.build_root() / 'src', ], ignore_headers: ['drivers.h', 'iio-buffer-utils.h', 'orientation.h', 'uinput.h', 'accel-mount-matrix.h'], scan_args: ['--rebuild-sections'], ) man1_dir = join_paths(get_option('prefix'), get_option('mandir'), 'man1') xsltproc = find_program('xsltproc', required : true) xsltproc_command = [ xsltproc, '--nonet', '--stringparam', 'man.output.quietly', '1', '--stringparam', 'funcsynopsis.style', 'ansi', '--stringparam', 'man.th.extra1.suppress', '1', '--stringparam', 'man.authors.section.enabled', '0', '--stringparam', 'man.copyright.section.enabled', '0', '-o', '@OUTPUT@', 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl', '@INPUT@', ] iio-sensor-proxy-3.5/docs/version.xml.in000066400000000000000000000000121445770364600204040ustar00rootroot00000000000000@VERSION@ iio-sensor-proxy-3.5/meson.build000066400000000000000000000047451445770364600170230ustar00rootroot00000000000000project('iio-sensor-proxy', [ 'c' ], version: '3.5', license: 'GPLv3+', default_options: [ 'buildtype=debugoptimized', 'warning_level=1', 'c_std=c99', ], meson_version: '>= 0.54.0') cc = meson.get_compiler('c') common_cflags = cc.get_supported_arguments([ '-fgnu89-inline', '-fvisibility=hidden', '-std=gnu99', '-Wall', '-Wundef', '-Wunused', '-Wstrict-prototypes', '-Werror-implicit-function-declaration', '-Wno-pointer-sign', '-Wshadow' ]) prefix = get_option('prefix') libexecdir = prefix / get_option('libexecdir') bindir = get_option('bindir') dbusconfdir = get_option('datadir') / 'dbus-1' / 'system.d' mathlib_dep = cc.find_library('m', required: false) udev_rules_dir = get_option('udevrulesdir') if udev_rules_dir == 'auto' udev_dep = dependency('udev') udev_rules_dir = udev_dep.get_pkgconfig_variable('udevdir') + '/rules.d' endif systemd_system_unit_dir = get_option('systemdsystemunitdir') if systemd_system_unit_dir == 'auto' systemd_dep = dependency('systemd') systemd_system_unit_dir = systemd_dep.get_pkgconfig_variable('systemdsystemunitdir') endif if get_option('gtk-tests') gtk_dep = dependency('gtk+-3.0', required: false) endif gio_dep = dependency('gio-2.0') gudev_dep = dependency('gudev-1.0', version: '>= 237') polkit_gobject_dep = dependency('polkit-gobject-1', version: '>= 0.91') polkit_policy_directory = polkit_gobject_dep.get_pkgconfig_variable('policydir') xmllint = find_program('xmllint', required: false) gnome = import('gnome') add_global_arguments('-D_GNU_SOURCE=1', language: 'c') subdir('src') subdir('data') if get_option('gtk_doc') # Make like license available in the build root for docs configure_file( input: 'COPYING', output: 'COPYING', copy: true, ) subdir('docs') endif if get_option('tests') # Python 3 required modules python3_required_modules = ['psutil', 'dbusmock', 'gi'] python = import('python') python3 = python.find_installation('python3') foreach p : python3_required_modules # Source: https://docs.python.org/3/library/importlib.html#checking-if-a-module-can-be-imported script = 'import importlib.util; import sys; exit(1) if importlib.util.find_spec(\''+ p +'\') is None else exit(0)' if run_command(python3, '-c', script, check: false).returncode() != 0 error('Python3 module \'' + p + '\' required for running tests but not found') endif endforeach subdir('tests') endif iio-sensor-proxy-3.5/meson_options.txt000066400000000000000000000012751445770364600203110ustar00rootroot00000000000000option('udevrulesdir', description: 'udev rules directory', type: 'string', value: 'auto') option('systemdsystemunitdir', description: 'systemd unit directory', type: 'string', value: 'auto') option('tests', description: 'Whether to run tests', type: 'boolean', value: false) option('gtk-tests', description: 'Whether to build GTK tests', type: 'boolean', value: false) option('geoclue-user', description: 'The USER (existing) as which geoclue service is running', type: 'string', value: 'geoclue') option('gtk_doc', type: 'boolean', value: false, description: 'Build docs') iio-sensor-proxy-3.5/src/000077500000000000000000000000001445770364600154365ustar00rootroot00000000000000iio-sensor-proxy-3.5/src/accel-attributes.c000066400000000000000000000067611445770364600210470ustar00rootroot00000000000000/* * Copyright (c) 2019 Luís Ferreira * Copyright (c) 2019 Daniel Stuart * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. * */ #include "accel-attributes.h" AccelLocation setup_accel_location (GUdevDevice *device) { AccelLocation ret; const char *location; location = g_udev_device_get_property (device, "ACCEL_LOCATION"); if (location) { if (parse_accel_location (location, &ret)) return ret; g_warning ("Failed to parse ACCEL_LOCATION ('%s') from udev", location); } location = g_udev_device_get_sysfs_attr (device, "label"); if (location) { if (parse_accel_label (location, &ret)) return ret; } location = g_udev_device_get_sysfs_attr (device, "location"); if (location) { if (parse_accel_location (location, &ret)) return ret; g_warning ("Failed to parse location ('%s') from sysfs", location); } g_debug ("No auto-detected location, falling back to display location"); ret = ACCEL_LOCATION_DISPLAY; return ret; } gboolean parse_accel_label (const char *location, AccelLocation *value) { if (location == NULL || *location == '\0') return FALSE; if (g_str_equal (location, "accel-base")) { *value = ACCEL_LOCATION_BASE; return TRUE; } else if (g_str_equal (location, "accel-display")) { *value = ACCEL_LOCATION_DISPLAY; return TRUE; } g_debug ("Failed to parse label '%s' as a location", location); return FALSE; } gboolean parse_accel_location (const char *location, AccelLocation *value) { /* Empty string means we use the display location */ if (location == NULL || *location == '\0' || g_str_equal (location, "display") || g_str_equal (location, "lid")) { *value = ACCEL_LOCATION_DISPLAY; return TRUE; } else if (g_str_equal (location, "base")) { *value = ACCEL_LOCATION_BASE; return TRUE; } else { g_warning ("Failed to parse '%s' as a location", location); return FALSE; } } gboolean get_accel_scale (GUdevDevice *device, AccelScale *scale_vec) { gdouble scale; g_return_val_if_fail (scale_vec != NULL, FALSE); scale = g_udev_device_get_sysfs_attr_as_double (device, "in_accel_x_scale"); if (scale != 0.0) { scale_vec->x = scale; scale_vec->y = g_udev_device_get_sysfs_attr_as_double (device, "in_accel_y_scale"); scale_vec->z = g_udev_device_get_sysfs_attr_as_double (device, "in_accel_z_scale"); if (scale_vec->y != 0.0 && scale_vec->z != 0.0) { g_debug ("Attribute in_accel_{x,y,z}_scale (%f,%f,%f) found in sysfs", scale_vec->x, scale_vec->y, scale_vec->z); return TRUE; } g_warning ("Could not read in_accel_{x,y,z}_scale attributes, kernel bug"); } scale = g_udev_device_get_sysfs_attr_as_double (device, "in_accel_scale"); if (scale != 0.0) { g_debug ("Attribute in_accel_scale ('%f') found on sysfs", scale); set_accel_scale (scale_vec, scale); return TRUE; } scale = g_udev_device_get_sysfs_attr_as_double (device, "scale"); if (scale != 0.0) { g_debug ("Attribute scale ('%f') found on sysfs", scale); set_accel_scale (scale_vec, scale); return TRUE; } g_debug ("Failed to auto-detect scale, falling back to 1.0"); reset_accel_scale (scale_vec); return TRUE; } const char * accel_location_to_string (AccelLocation location) { switch (location) { case ACCEL_LOCATION_DISPLAY: return "display"; case ACCEL_LOCATION_BASE: return "base"; default: g_assert_not_reached (); } } iio-sensor-proxy-3.5/src/accel-attributes.h000066400000000000000000000015631445770364600210470ustar00rootroot00000000000000/* * Copyright (c) 2019 Luís Ferreira * Copyright (c) 2019 Daniel Stuart * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. * */ #include #include #include "accel-scale.h" typedef enum { ACCEL_LOCATION_DISPLAY, ACCEL_LOCATION_BASE, } AccelLocation; AccelLocation setup_accel_location (GUdevDevice *device); gboolean parse_accel_location (const char *location, AccelLocation *value); gboolean parse_accel_label (const char *location, AccelLocation *value); const char *accel_location_to_string (AccelLocation location); gboolean get_accel_scale (GUdevDevice *device, AccelScale *scale_vec); iio-sensor-proxy-3.5/src/accel-mount-matrix.c000066400000000000000000000113451445770364600213170ustar00rootroot00000000000000/* * Copyright (c) 2016 Bastien Nocera * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. * */ #include #include #include "accel-mount-matrix.h" /* The format is the same used in the iio core to export the values: * https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/iio/industrialio-core.c?id=dfc57732ad38f93ae6232a3b4e64fd077383a0f1#n431 */ static AccelVec3 id_matrix[3] = { { 1.0, 0.0, 0.0 }, { 0.0, 1.0, 0.0 }, { 0.0, 0.0, 1.0 } }; static char axis_names[] = "xyz"; AccelVec3 * get_id_matrix (void) { #if GLIB_CHECK_VERSION(2, 68, 0) return g_memdup2 (id_matrix, sizeof(id_matrix)); #else return g_memdup (id_matrix, sizeof(id_matrix)); #endif } AccelVec3 * setup_mount_matrix (GUdevDevice *device) { AccelVec3 *ret = NULL; const char *mount_matrix; mount_matrix = g_udev_device_get_property (device, "ACCEL_MOUNT_MATRIX"); if (mount_matrix) { if (parse_mount_matrix (mount_matrix, &ret)) return ret; g_warning ("Failed to parse ACCEL_MOUNT_MATRIX ('%s') from udev", mount_matrix); g_clear_pointer (&ret, g_free); } mount_matrix = g_udev_device_get_sysfs_attr (device, "mount_matrix"); if (mount_matrix) { if (parse_mount_matrix (mount_matrix, &ret)) return ret; g_warning ("Failed to parse mount_matrix ('%s') from sysfs", mount_matrix); g_clear_pointer (&ret, g_free); } /* Some IIO drivers provide multiple sensors via the same sysfs path * and thus they may have different matrices like in a case of * accelerometer and angular velocity for example. The accelerometer * mount matrix is named as in_accel_mount_matrix in that case. * * See https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-iio * for more details. */ mount_matrix = g_udev_device_get_sysfs_attr (device, "in_accel_mount_matrix"); if (mount_matrix) { if (parse_mount_matrix (mount_matrix, &ret)) return ret; g_warning ("Failed to parse in_accel_mount_matrix ('%s') from sysfs", mount_matrix); g_clear_pointer (&ret, g_free); } /* Linux kernel IIO accelerometer drivers provide mount matrix * via standardized sysfs interface. * * See https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-iio * for more details. */ mount_matrix = g_udev_device_get_sysfs_attr (device, "in_mount_matrix"); if (mount_matrix) { if (parse_mount_matrix (mount_matrix, &ret)) return ret; g_warning ("Failed to parse in_mount_matrix ('%s') from sysfs", mount_matrix); g_clear_pointer (&ret, g_free); } g_debug ("Failed to auto-detect mount matrix, falling back to identity"); return get_id_matrix (); } static char ** strsplit_num_tokens (const gchar *string, const gchar *delimiter, gint num_tokens) { g_auto(GStrv) elems = NULL; guint i; elems = g_strsplit (string, delimiter, num_tokens); if (elems == NULL) return NULL; for (i = 0; i < num_tokens; i++) { if (elems[i] == NULL) return NULL; } return g_steal_pointer (&elems); } gboolean parse_mount_matrix (const char *mtx, AccelVec3 **vecs) { AccelVec3 *ret; guint i; g_auto(GStrv) axis = NULL; g_return_val_if_fail (vecs != NULL, FALSE); /* Empty string means we use the identity matrix */ if (mtx == NULL || *mtx == '\0') { *vecs = get_id_matrix (); return TRUE; } ret = g_new0 (AccelVec3, 3); axis = strsplit_num_tokens (mtx, ";", 3); if (!axis) { g_free (ret); g_warning ("Failed to parse '%s' as a mount matrix", mtx); return FALSE; } for (i = 0; i < G_N_ELEMENTS(id_matrix); i++) { g_auto(GStrv) elems = NULL; elems = strsplit_num_tokens (axis[i], ",", 3); if (elems == NULL) { g_free (ret); g_warning ("Failed to parse '%s' as a mount matrix", mtx); return FALSE; } ret[i].x = g_ascii_strtod (elems[0], NULL); ret[i].y = g_ascii_strtod (elems[1], NULL); ret[i].z = g_ascii_strtod (elems[2], NULL); } for (i = 0; i < G_N_ELEMENTS(id_matrix); i++) { if (ret[i].x == 0.0f && ret[i].y == 0.0f && ret[i].z == 0.0f) { g_free (ret); g_warning ("In mount matrix '%s', axis %c is all zeroes, which is invalid", mtx, axis_names[i]); return FALSE; } } *vecs = ret; return TRUE; } gboolean apply_mount_matrix (const AccelVec3 vecs[3], AccelVec3 *accel) { float _x, _y, _z; g_return_val_if_fail (accel != NULL, FALSE); _x = accel->x * vecs[0].x + accel->y * vecs[0].y + accel->z * vecs[0].z; _y = accel->x * vecs[1].x + accel->y * vecs[1].y + accel->z * vecs[1].z; _z = accel->x * vecs[2].x + accel->y * vecs[2].y + accel->z * vecs[2].z; accel->x = _x; accel->y = _y; accel->z = _z; return TRUE; } iio-sensor-proxy-3.5/src/accel-mount-matrix.h000066400000000000000000000011571445770364600213240ustar00rootroot00000000000000/* * Copyright (c) 2016 Bastien Nocera * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. * */ #include #include typedef struct { float x; float y; float z; } AccelVec3; AccelVec3 *setup_mount_matrix (GUdevDevice *device); gboolean parse_mount_matrix (const char *mtx, AccelVec3 **vecs); gboolean apply_mount_matrix (const AccelVec3 vecs[3], AccelVec3 *accel); iio-sensor-proxy-3.5/src/accel-scale.c000066400000000000000000000013411445770364600177350ustar00rootroot00000000000000/* * Copyright (c) 2020 Bastien Nocera * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. * */ #include #include "accel-scale.h" void reset_accel_scale (AccelScale *scale) { set_accel_scale (scale, 1.0); } void set_accel_scale (AccelScale *scale, double value) { g_return_if_fail (scale != NULL); g_return_if_fail (value != 0.0); scale->x = scale->y = scale->z = value; } void copy_accel_scale (AccelScale *target, AccelScale source) { g_return_if_fail (target != NULL); target->x = source.x; target->y = source.y; target->z = source.z; } iio-sensor-proxy-3.5/src/accel-scale.h000066400000000000000000000007641445770364600177520ustar00rootroot00000000000000/* * Copyright (c) 2020 Bastien Nocera * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. * */ #pragma once typedef struct { double x; double y; double z; } AccelScale; void reset_accel_scale (AccelScale *scale); void set_accel_scale (AccelScale *scale, double value); void copy_accel_scale (AccelScale *target, AccelScale source); iio-sensor-proxy-3.5/src/drivers.c000066400000000000000000000013711445770364600172620ustar00rootroot00000000000000/* * Copyright (c) 2020 Purism SPC * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. */ #include "drivers.h" #include gboolean drv_check_udev_sensor_type (GUdevDevice *device, const gchar *match, const gchar *name) { g_auto (GStrv) types = NULL; const gchar *attr = g_udev_device_get_property (device, "IIO_SENSOR_PROXY_TYPE"); if (attr == NULL) return FALSE; types = g_strsplit (attr, " ", 0); if (!g_strv_contains ((const gchar * const *)types, match)) return FALSE; if (name) g_debug ("Found %s at %s", name, g_udev_device_get_sysfs_path (device)); return TRUE; } iio-sensor-proxy-3.5/src/drivers.h000066400000000000000000000075111445770364600172710ustar00rootroot00000000000000/* * Copyright (c) 2014 Bastien Nocera * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. */ #include #include #include "accel-attributes.h" #include "accel-scale.h" typedef enum { DRIVER_TYPE_ACCEL, DRIVER_TYPE_LIGHT, DRIVER_TYPE_COMPASS, DRIVER_TYPE_PROXIMITY, } DriverType; typedef enum { PROXIMITY_NEAR_ERROR = -1, PROXIMITY_NEAR_FALSE = 0, PROXIMITY_NEAR_TRUE = 1, } ProximityNear; typedef struct SensorDevice SensorDevice; typedef struct { int accel_x; int accel_y; int accel_z; AccelScale scale; } AccelReadings; typedef struct { gdouble level; gboolean uses_lux; } LightReadings; typedef struct { gdouble heading; } CompassReadings; typedef struct { ProximityNear is_near; } ProximityReadings; typedef void (*ReadingsUpdateFunc) (SensorDevice *sensor_device, gpointer readings, gpointer user_data); typedef struct { const char *driver_name; DriverType type; gboolean (*discover) (GUdevDevice *device); SensorDevice * (*open) (GUdevDevice *device); void (*set_polling) (SensorDevice *device, gboolean state); void (*close) (SensorDevice *device); } SensorDriver; struct SensorDevice { SensorDriver *drv; gpointer priv; char *name; /* Callback function and data as pass to driver_open() */ ReadingsUpdateFunc callback_func; gpointer user_data; }; static inline gboolean driver_discover (SensorDriver *driver, GUdevDevice *device) { AccelLocation location; g_return_val_if_fail (driver, FALSE); g_return_val_if_fail (driver->discover, FALSE); g_return_val_if_fail (device, FALSE); if (!driver->discover (device)) return FALSE; if (driver->type != DRIVER_TYPE_ACCEL) return TRUE; location = setup_accel_location (device); if (location == ACCEL_LOCATION_DISPLAY) return TRUE; g_debug ("Ignoring accelerometer in location '%s'", accel_location_to_string (location)); return FALSE; } static inline SensorDevice * driver_open (SensorDriver *driver, GUdevDevice *device, ReadingsUpdateFunc callback_func, gpointer user_data) { SensorDevice *sensor_device; g_return_val_if_fail (driver, NULL); g_return_val_if_fail (driver->open, NULL); g_return_val_if_fail (device, NULL); g_return_val_if_fail (callback_func, NULL); sensor_device = driver->open (device); if (!sensor_device) return NULL; sensor_device->drv = driver; sensor_device->callback_func = callback_func; sensor_device->user_data = user_data; return sensor_device; } static inline void driver_set_polling (SensorDevice *sensor_device, gboolean state) { SensorDriver *driver; g_return_if_fail (sensor_device); driver = sensor_device->drv; g_return_if_fail (driver); if (!driver->set_polling) return; driver->set_polling (sensor_device, state); } static inline void driver_close (SensorDevice *sensor_device) { SensorDriver *driver; g_return_if_fail (sensor_device); driver_set_polling (sensor_device, FALSE); g_clear_pointer (&sensor_device->name, g_free); driver = sensor_device->drv; g_return_if_fail (driver->close); driver->close (sensor_device); } extern SensorDriver iio_buffer_accel; extern SensorDriver iio_poll_accel; extern SensorDriver input_accel; extern SensorDriver fake_compass; extern SensorDriver fake_light; extern SensorDriver iio_poll_light; extern SensorDriver hwmon_light; extern SensorDriver iio_buffer_light; extern SensorDriver iio_buffer_compass; extern SensorDriver iio_poll_proximity; gboolean drv_check_udev_sensor_type (GUdevDevice *device, const gchar *match, const char *name); iio-sensor-proxy-3.5/src/drv-fake-compass.c000066400000000000000000000055401445770364600207500ustar00rootroot00000000000000/* * Copyright (c) 2015 Bastien Nocera * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. */ #include "drivers.h" #include #include #include #include #include #include typedef struct DrvData { guint timeout_id; } DrvData; static gboolean fake_compass_discover (GUdevDevice *device) { if (g_getenv ("FAKE_COMPASS") == NULL) return FALSE; if (g_strcmp0 (g_udev_device_get_subsystem (device), "input") != 0) return FALSE; /* "Power Button" is a random input device to latch onto */ if (g_strcmp0 (g_udev_device_get_property (device, "NAME"), "\"Power Button\"") != 0) return FALSE; g_debug ("Found fake compass at %s", g_udev_device_get_sysfs_path (device)); return TRUE; } static gboolean compass_changed (gpointer user_data) { SensorDevice *sensor_device = user_data; static gdouble heading = 0; CompassReadings readings; heading += 10; if (heading >= 360) heading = 0; g_debug ("Changed heading to %f", heading); readings.heading = heading; sensor_device->callback_func (sensor_device, (gpointer) &readings, sensor_device->user_data); return G_SOURCE_CONTINUE; } static gboolean first_values (gpointer user_data) { SensorDevice *sensor_device = user_data; DrvData *drv_data = (DrvData *) sensor_device->priv; compass_changed (sensor_device); drv_data->timeout_id = g_timeout_add_seconds (1, (GSourceFunc) compass_changed, sensor_device); g_source_set_name_by_id (drv_data->timeout_id, "[fake_compass_set_polling] compass_changed"); return G_SOURCE_REMOVE; } static SensorDevice * fake_compass_open (GUdevDevice *device) { SensorDevice *sensor_device; sensor_device = g_new0 (SensorDevice, 1); sensor_device->priv = g_new0 (DrvData, 1); sensor_device->name = g_strdup ("Fake Compass"); return sensor_device; } static void fake_compass_set_polling (SensorDevice *sensor_device, gboolean state) { DrvData *drv_data = (DrvData *) sensor_device->priv; if (drv_data->timeout_id > 0 && state) return; if (drv_data->timeout_id == 0 && !state) return; if (drv_data->timeout_id) { g_source_remove (drv_data->timeout_id); drv_data->timeout_id = 0; } if (state) { drv_data->timeout_id = g_idle_add (first_values, sensor_device); g_source_set_name_by_id (drv_data->timeout_id, "[fake_compass_set_polling] first_values"); } } static void fake_compass_close (SensorDevice *sensor_device) { g_clear_pointer (&sensor_device->priv, g_free); g_free (sensor_device); } SensorDriver fake_compass = { .driver_name = "Fake compass", .type = DRIVER_TYPE_COMPASS, .discover = fake_compass_discover, .open = fake_compass_open, .set_polling = fake_compass_set_polling, .close = fake_compass_close, }; iio-sensor-proxy-3.5/src/drv-fake-light.c000066400000000000000000000057231445770364600204150ustar00rootroot00000000000000/* * Copyright (c) 2014 Bastien Nocera * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. */ #include "drivers.h" #include #include #include #include #include #include typedef struct DrvData { guint timeout_id; } DrvData; static gboolean fake_light_discover (GUdevDevice *device) { if (g_getenv ("FAKE_LIGHT_SENSOR") == NULL) return FALSE; /* We need a udev device to associate with our fake light sensor, * and the power button is as good as any, and should be available * on most devices we want to run this on. */ if (g_strcmp0 (g_udev_device_get_subsystem (device), "input") != 0 || g_strcmp0 (g_udev_device_get_property (device, "NAME"), "\"Power Button\"") != 0) return FALSE; g_debug ("Found fake light at %s", g_udev_device_get_sysfs_path (device)); return TRUE; } static gboolean light_changed (gpointer user_data) { SensorDevice *sensor_device = user_data; static gdouble level = -1.0; LightReadings readings; /* XXX: * Might need to do something better here, like * replicate real readings from a device */ level += 1.0; readings.level = level; readings.uses_lux = TRUE; sensor_device->callback_func (sensor_device, (gpointer) &readings, sensor_device->user_data); return G_SOURCE_CONTINUE; } static gboolean first_values (gpointer user_data) { SensorDevice *sensor_device = user_data; DrvData *drv_data = (DrvData *) sensor_device->priv; light_changed (sensor_device); drv_data->timeout_id = g_timeout_add_seconds (1, (GSourceFunc) light_changed, sensor_device); g_source_set_name_by_id (drv_data->timeout_id, "[fake_light_set_polling] light_changed"); return G_SOURCE_REMOVE; } static SensorDevice * fake_light_open (GUdevDevice *device) { SensorDevice *sensor_device; sensor_device = g_new0 (SensorDevice, 1); sensor_device->priv = g_new0 (DrvData, 1); sensor_device->name = g_strdup ("Fake Light Sensor"); return sensor_device; } static void fake_light_set_polling (SensorDevice *sensor_device, gboolean state) { DrvData *drv_data = (DrvData *) sensor_device->priv; if (drv_data->timeout_id > 0 && state) return; if (drv_data->timeout_id == 0 && !state) return; if (drv_data->timeout_id) { g_source_remove (drv_data->timeout_id); drv_data->timeout_id = 0; } if (state) { drv_data->timeout_id = g_idle_add (first_values, sensor_device); g_source_set_name_by_id (drv_data->timeout_id, "[fake_light_set_polling] first_values"); } } static void fake_light_close (SensorDevice *sensor_device) { g_clear_pointer (&sensor_device->priv, g_free); g_free (sensor_device); } SensorDriver fake_light = { .driver_name = "Fake light", .type = DRIVER_TYPE_LIGHT, .discover = fake_light_discover, .open = fake_light_open, .set_polling = fake_light_set_polling, .close = fake_light_close, }; iio-sensor-proxy-3.5/src/drv-hwmon-light.c000066400000000000000000000056371445770364600206430ustar00rootroot00000000000000/* * Copyright (c) 2014 Bastien Nocera * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. */ #include "drivers.h" #include #include #include #include #include #include "utils.h" #define DEFAULT_POLL_TIME (IS_TEST ? 500 : 8000) #define MAX_LIGHT_LEVEL 255 typedef struct DrvData { GUdevDevice *device; guint timeout_id; } DrvData; static gboolean hwmon_light_discover (GUdevDevice *device) { return drv_check_udev_sensor_type (device, "hwmon-als", "HWMon light"); } static gboolean light_changed (gpointer user_data) { SensorDevice *sensor_device = user_data; DrvData *drv_data = (DrvData *) sensor_device->priv; LightReadings readings; gdouble level; const char *contents; int light1, light2; contents = g_udev_device_get_sysfs_attr_uncached (drv_data->device, "light"); if (!contents) return G_SOURCE_CONTINUE; if (sscanf (contents, "(%d,%d)", &light1, &light2) != 2) { g_warning ("Failed to parse light level: %s", contents); return G_SOURCE_CONTINUE; } level = (double) (((float) MAX(light1, light2)) / (float) MAX_LIGHT_LEVEL * 100.0f); readings.level = level; readings.uses_lux = FALSE; sensor_device->callback_func (sensor_device, (gpointer) &readings, sensor_device->user_data); return G_SOURCE_CONTINUE; } static SensorDevice * hwmon_light_open (GUdevDevice *device) { SensorDevice *sensor_device; DrvData *drv_data; sensor_device = g_new0 (SensorDevice, 1); sensor_device->name = g_strdup (g_udev_device_get_name (device)); sensor_device->priv = g_new0 (DrvData, 1); drv_data = (DrvData *) sensor_device->priv; drv_data->device = g_object_ref (device); return sensor_device; } static void hwmon_light_set_polling (SensorDevice *sensor_device, gboolean state) { DrvData *drv_data = (DrvData *) sensor_device->priv; if (drv_data->timeout_id > 0 && state) return; if (drv_data->timeout_id == 0 && !state) return; if (drv_data->timeout_id) { g_source_remove (drv_data->timeout_id); drv_data->timeout_id = 0; } if (state) { drv_data->timeout_id = g_timeout_add (DEFAULT_POLL_TIME, (GSourceFunc) light_changed, sensor_device); g_source_set_name_by_id (drv_data->timeout_id, "[hwmon_light_set_polling] light_changed"); /* And send a reading straight away */ light_changed (sensor_device); } } static void hwmon_light_close (SensorDevice *sensor_device) { DrvData *drv_data = (DrvData *) sensor_device->priv; g_clear_object (&drv_data->device); g_clear_pointer (&sensor_device->priv, g_free); g_free (sensor_device); } SensorDriver hwmon_light = { .driver_name = "Platform HWMon Light", .type = DRIVER_TYPE_LIGHT, .discover = hwmon_light_discover, .open = hwmon_light_open, .set_polling = hwmon_light_set_polling, .close = hwmon_light_close, }; iio-sensor-proxy-3.5/src/drv-iio-buffer-accel.c000066400000000000000000000162651445770364600215010ustar00rootroot00000000000000/* * Copyright (c) 2014 Bastien Nocera * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. */ #include "drivers.h" #include "iio-buffer-utils.h" #include "accel-mount-matrix.h" #include "utils.h" #include #include #include #include typedef struct { guint timeout_id; GUdevDevice *dev; char *dev_path; AccelVec3 *mount_matrix; AccelLocation location; int device_id; BufferDrvData *buffer_data; } DrvData; static int process_scan (IIOSensorData data, SensorDevice *sensor_device) { DrvData *drv_data = (DrvData *) sensor_device->priv; int i; int accel_x, accel_y, accel_z; gboolean present_x, present_y, present_z; AccelReadings readings; AccelVec3 tmp; AccelScale scale; if (data.read_size < 0) { g_warning ("Couldn't read from device '%s': %s", sensor_device->name, g_strerror (errno)); return 0; } /* Rather than read everything: * for (i = 0; i < data.read_size / drv_data->scan_size; i++)... * Just read the last one */ i = (data.read_size / drv_data->buffer_data->scan_size) - 1; if (i < 0) { g_debug ("Not enough data to read from '%s' (read_size: %d scan_size: %d)", sensor_device->name, (int) data.read_size, drv_data->buffer_data->scan_size); return 0; } process_scan_1(data.data + drv_data->buffer_data->scan_size*i, drv_data->buffer_data, "in_accel_x", &accel_x, &scale.x, &present_x); process_scan_1(data.data + drv_data->buffer_data->scan_size*i, drv_data->buffer_data, "in_accel_y", &accel_y, &scale.y, &present_y); process_scan_1(data.data + drv_data->buffer_data->scan_size*i, drv_data->buffer_data, "in_accel_z", &accel_z, &scale.z, &present_z); g_debug ("Accel read from IIO on '%s': %d, %d, %d (scale %lf,%lf,%lf)", sensor_device->name, accel_x, accel_y, accel_z, scale.x, scale.y, scale.z); tmp.x = accel_x; tmp.y = accel_y; tmp.z = accel_z; if (!apply_mount_matrix (drv_data->mount_matrix, &tmp)) g_warning ("Could not apply mount matrix"); //FIXME report errors readings.accel_x = tmp.x; readings.accel_y = tmp.y; readings.accel_z = tmp.z; copy_accel_scale (&readings.scale, scale); sensor_device->callback_func (sensor_device, (gpointer) &readings, sensor_device->user_data); return 1; } static void prepare_output (SensorDevice *sensor_device, const char *dev_dir_name, const char *trigger_name) { DrvData *drv_data = (DrvData *) sensor_device->priv; g_autoptr(IIOSensorData) data = NULL; g_auto(IioFd) fp = -1; int buf_len = 127; data = iio_sensor_data_new (drv_data->buffer_data->scan_size * buf_len); /* Attempt to open non blocking to access dev */ fp = open (drv_data->dev_path, O_RDONLY | O_NONBLOCK); if (fp == -1) { /* If it isn't there make the node */ if (!IS_TEST) g_warning ("Failed to open '%s' at %s: %s", sensor_device->name, drv_data->dev_path, g_strerror (errno)); return; } /* Actually read the data */ data->read_size = read (fp, data->data, buf_len * drv_data->buffer_data->scan_size); if (data->read_size == -1 && errno == EAGAIN) { g_debug ("No new data available on '%s'", sensor_device->name); return; } process_scan (*data, sensor_device); } static char * get_trigger_name (GUdevDevice *device) { GList *devices, *l; GUdevClient *client; gboolean has_trigger = FALSE; char *trigger_name; const gchar * const subsystems[] = { "iio", NULL }; client = g_udev_client_new (subsystems); devices = g_udev_client_query_by_subsystem (client, "iio"); /* Find the associated trigger */ trigger_name = g_strdup_printf ("accel_3d-dev%s", g_udev_device_get_number (device)); for (l = devices; l != NULL; l = l->next) { GUdevDevice *dev = l->data; if (g_strcmp0 (trigger_name, g_udev_device_get_sysfs_attr (dev, "name")) == 0) { g_debug ("Found associated trigger at %s", g_udev_device_get_sysfs_path (dev)); has_trigger = TRUE; break; } } g_list_free_full (devices, g_object_unref); g_clear_object (&client); if (has_trigger) return trigger_name; g_warning ("Could not find trigger name associated with %s", g_udev_device_get_sysfs_path (device)); g_free (trigger_name); return NULL; } static gboolean read_orientation (gpointer user_data) { SensorDevice *sensor_device = user_data; DrvData *drv_data = (DrvData *) sensor_device->priv; prepare_output (sensor_device, drv_data->buffer_data->dev_dir_name, drv_data->buffer_data->trigger_name); return G_SOURCE_CONTINUE; } static gboolean iio_buffer_accel_discover (GUdevDevice *device) { g_autofree char *trigger_name = NULL; if (!drv_check_udev_sensor_type (device, "iio-buffer-accel", NULL)) return FALSE; /* If we can't find an associated trigger, fallback to the iio-poll-accel driver */ trigger_name = get_trigger_name (device); if (!trigger_name) { g_debug ("Could not find trigger for %s", g_udev_device_get_sysfs_path (device)); return FALSE; } g_debug ("Found IIO buffer accelerometer at %s", g_udev_device_get_sysfs_path (device)); return TRUE; } static void iio_buffer_accel_set_polling (SensorDevice *sensor_device, gboolean state) { DrvData *drv_data = (DrvData *) sensor_device->priv; if (drv_data->timeout_id > 0 && state) return; if (drv_data->timeout_id == 0 && !state) return; if (drv_data->timeout_id) { g_source_remove (drv_data->timeout_id); drv_data->timeout_id = 0; } if (state) { drv_data->timeout_id = g_timeout_add (700, read_orientation, sensor_device); g_source_set_name_by_id (drv_data->timeout_id, "[iio_buffer_accel_set_polling] read_orientation"); } } static SensorDevice * iio_buffer_accel_open (GUdevDevice *device) { SensorDevice *sensor_device; DrvData *drv_data; g_autofree char *trigger_name = NULL; BufferDrvData *buffer_data; /* Get the trigger name, and build the channels from that */ trigger_name = get_trigger_name (device); if (!trigger_name) return NULL; buffer_data = buffer_drv_data_new (device, trigger_name); if (!buffer_data) return NULL; sensor_device = g_new0 (SensorDevice, 1); sensor_device->name = g_strdup (g_udev_device_get_property (device, "NAME")); if (!sensor_device->name) sensor_device->name = g_strdup (g_udev_device_get_name (device)); sensor_device->priv = g_new0 (DrvData, 1); drv_data = (DrvData *) sensor_device->priv; drv_data->buffer_data = buffer_data; drv_data->mount_matrix = setup_mount_matrix (device); drv_data->location = setup_accel_location (device); drv_data->dev = g_object_ref (device); drv_data->dev_path = get_device_file (device); return sensor_device; } static void iio_buffer_accel_close (SensorDevice *sensor_device) { DrvData *drv_data = (DrvData *) sensor_device->priv; g_clear_pointer (&drv_data->buffer_data, buffer_drv_data_free); g_clear_object (&drv_data->dev); g_clear_pointer (&drv_data->mount_matrix, g_free); g_clear_pointer (&drv_data->dev_path, g_free); g_clear_pointer (&sensor_device->priv, g_free); g_free (sensor_device); } SensorDriver iio_buffer_accel = { .driver_name = "IIO Buffer accelerometer", .type = DRIVER_TYPE_ACCEL, .discover = iio_buffer_accel_discover, .open = iio_buffer_accel_open, .set_polling = iio_buffer_accel_set_polling, .close = iio_buffer_accel_close, }; iio-sensor-proxy-3.5/src/drv-iio-buffer-compass.c000066400000000000000000000143001445770364600220630ustar00rootroot00000000000000/* * Copyright (c) 2014 Bastien Nocera * Copyright (c) 2015 Elad Alfassa * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. */ #include "drivers.h" #include "iio-buffer-utils.h" #include "utils.h" #include #include #include #include typedef struct { guint timeout_id; GUdevDevice *dev; const char *dev_path; int device_id; BufferDrvData *buffer_data; } DrvData; static int process_scan (IIOSensorData data, SensorDevice *sensor_device) { DrvData *drv_data = (DrvData *) sensor_device->priv; int i; int raw_heading; const char *channel_name = "in_rot_from_north_magnetic_tilt_comp"; gdouble scale; gboolean present_level; CompassReadings readings; if (data.read_size < 0) { g_warning ("Couldn't read from device '%s': %s", sensor_device->name, g_strerror (errno)); return 0; } /* Rather than read everything: * for (i = 0; i < data.read_size / drv_data->scan_size; i++)... * Just read the last one */ i = (data.read_size / drv_data->buffer_data->scan_size) - 1; if (i < 0) { g_debug ("Not enough data to read from '%s' (read_size: %d scan_size: %d)", sensor_device->name, (int) data.read_size, drv_data->buffer_data->scan_size); return 0; } process_scan_1 (data.data + drv_data->buffer_data->scan_size*i, drv_data->buffer_data, channel_name, &raw_heading, &scale, &present_level); readings.heading = raw_heading * scale; g_debug ("Heading read from IIO on '%s': %f (%d times %lf scale)", sensor_device->name, readings.heading, raw_heading, scale); //FIXME report errors sensor_device->callback_func (sensor_device, (gpointer) &readings, sensor_device->user_data); return 1; } static void prepare_output (SensorDevice *sensor_device, const char *dev_dir_name, const char *trigger_name) { DrvData *drv_data = (DrvData *) sensor_device->priv; g_autoptr(IIOSensorData) data = NULL; g_auto(IioFd) fp = -1; int buf_len = 127; data = iio_sensor_data_new (drv_data->buffer_data->scan_size * buf_len); /* Attempt to open non blocking to access dev */ fp = open (drv_data->dev_path, O_RDONLY | O_NONBLOCK); if (fp == -1) { /* If it isn't there make the node */ g_warning ("Failed to open '%s' at %s : %s", sensor_device->name, drv_data->dev_path, g_strerror (errno)); return; } /* Actually read the data */ data->read_size = read (fp, data->data, buf_len * drv_data->buffer_data->scan_size); if (data->read_size == -1 && errno == EAGAIN) { g_debug ("No new data available on '%s'", sensor_device->name); return; } process_scan (*data, sensor_device); } static char * get_trigger_name (GUdevDevice *device) { GList *devices, *l; GUdevClient *client; gboolean has_trigger = FALSE; char *trigger_name; const gchar * const subsystems[] = { "iio", NULL }; client = g_udev_client_new (subsystems); devices = g_udev_client_query_by_subsystem (client, "iio"); /* Find the associated trigger */ trigger_name = g_strdup_printf ("magn_3d-dev%s", g_udev_device_get_number (device)); for (l = devices; l != NULL; l = l->next) { GUdevDevice *dev = l->data; if (g_strcmp0 (trigger_name, g_udev_device_get_sysfs_attr (dev, "name")) == 0) { g_debug ("Found associated trigger at %s", g_udev_device_get_sysfs_path (dev)); has_trigger = TRUE; break; } } g_list_free_full (devices, g_object_unref); g_clear_object (&client); if (has_trigger) return trigger_name; g_warning ("Could not find trigger name associated with %s", g_udev_device_get_sysfs_path (device)); g_free (trigger_name); return NULL; } static gboolean read_heading (gpointer user_data) { SensorDevice *sensor_device = user_data; DrvData *drv_data = (DrvData *) sensor_device->priv; prepare_output (sensor_device, drv_data->buffer_data->dev_dir_name, drv_data->buffer_data->trigger_name); return G_SOURCE_CONTINUE; } static gboolean iio_buffer_compass_discover (GUdevDevice *device) { return drv_check_udev_sensor_type (device, "iio-buffer-compass", "IIO buffer compass"); } static SensorDevice * iio_buffer_compass_open (GUdevDevice *device) { SensorDevice *sensor_device; DrvData *drv_data; g_autofree char *trigger_name = NULL; BufferDrvData *buffer_data; /* Get the trigger name, and build the channels from that */ trigger_name = get_trigger_name (device); if (!trigger_name) { g_debug ("Could not find trigger for %s", g_udev_device_get_sysfs_path (device)); return NULL; } buffer_data = buffer_drv_data_new (device, trigger_name); if (!buffer_data) return NULL; sensor_device = g_new0 (SensorDevice, 1); sensor_device->name = g_strdup (g_udev_device_get_property (device, "NAME")); if (!sensor_device->name) sensor_device->name = g_strdup (g_udev_device_get_name (device)); sensor_device->priv = g_new0 (DrvData, 1); drv_data = (DrvData *) sensor_device->priv; drv_data->dev = g_object_ref (device); drv_data->buffer_data = buffer_data; drv_data->dev_path = g_udev_device_get_device_file (device); return sensor_device; } static void iio_buffer_compass_set_polling (SensorDevice *sensor_device, gboolean state) { DrvData *drv_data = (DrvData *) sensor_device->priv; if (drv_data->timeout_id > 0 && state) return; if (drv_data->timeout_id == 0 && !state) return; if (drv_data->timeout_id) { g_source_remove (drv_data->timeout_id); drv_data->timeout_id = 0; } if (state) { drv_data->timeout_id = g_timeout_add (700, read_heading, sensor_device); g_source_set_name_by_id (drv_data->timeout_id, "[iio_buffer_compass_set_polling] read_heading"); } } static void iio_buffer_compass_close (SensorDevice *sensor_device) { DrvData *drv_data = (DrvData *) sensor_device->priv; g_clear_pointer (&drv_data->buffer_data, buffer_drv_data_free); g_clear_object (&drv_data->dev); g_clear_pointer (&sensor_device->priv, g_free); g_free (sensor_device); } SensorDriver iio_buffer_compass = { .driver_name = "IIO Buffer Compass", .type = DRIVER_TYPE_COMPASS, .discover = iio_buffer_compass_discover, .open = iio_buffer_compass_open, .set_polling = iio_buffer_compass_set_polling, .close = iio_buffer_compass_close, }; iio-sensor-proxy-3.5/src/drv-iio-buffer-light.c000066400000000000000000000145051445770364600215340ustar00rootroot00000000000000/* * Copyright (c) 2014 Bastien Nocera * Copyright (c) 2015 Elad Alfassa * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. */ #include "drivers.h" #include "iio-buffer-utils.h" #include "utils.h" #include #include #include #include typedef struct { guint timeout_id; GUdevDevice *dev; const char *dev_path; int device_id; BufferDrvData *buffer_data; } DrvData; static int process_scan (IIOSensorData data, SensorDevice *sensor_device) { DrvData *drv_data = (DrvData *) sensor_device->priv; int i; int level = 0; gdouble scale; gboolean present_level; LightReadings readings; if (data.read_size < 0) { g_warning ("Couldn't read from device '%s': %s", sensor_device->name, g_strerror (errno)); return 0; } /* Rather than read everything: * for (i = 0; i < data.read_size / drv_data->scan_size; i++)... * Just read the last one */ i = (data.read_size / drv_data->buffer_data->scan_size) - 1; if (i < 0) { g_debug ("Not enough data to read from '%s' (read_size: %d scan_size: %d)", sensor_device->name, (int) data.read_size, drv_data->buffer_data->scan_size); return 0; } process_scan_1(data.data + drv_data->buffer_data->scan_size*i, drv_data->buffer_data, "in_intensity_both", &level, &scale, &present_level); g_debug ("Light read from IIO on '%s': %d (scale %lf) = %lf", sensor_device->name, level, scale, level * scale); readings.level = level * scale; /* Even though the IIO kernel API declares in_intensity* values as unitless, * we use Microsoft's hid-sensors-usages.docx which mentions that Windows 8 * compatible sensor proxies will be using Lux as the unit, and most sensors * will be Windows 8 compatible */ readings.uses_lux = TRUE; //FIXME report errors sensor_device->callback_func (sensor_device, (gpointer) &readings, sensor_device->user_data); return 1; } static void prepare_output (SensorDevice *sensor_device, const char *dev_dir_name, const char *trigger_name) { DrvData *drv_data = (DrvData *) sensor_device->priv; g_autoptr(IIOSensorData) data = NULL; g_auto(IioFd) fp = 1; int buf_len = 127; data = iio_sensor_data_new (drv_data->buffer_data->scan_size * buf_len); /* Attempt to open non blocking to access dev */ fp = open (drv_data->dev_path, O_RDONLY | O_NONBLOCK); if (fp == -1) { /* If it isn't there make the node */ g_warning ("Failed to open '%s' at %s : %s", sensor_device->name, drv_data->dev_path, g_strerror (errno)); return; } /* Actually read the data */ data->read_size = read (fp, data->data, buf_len * drv_data->buffer_data->scan_size); if (data->read_size == -1 && errno == EAGAIN) { g_debug ("No new data available on '%s'", sensor_device->name); return; } process_scan (*data, sensor_device); } static char * get_trigger_name (GUdevDevice *device) { GList *devices, *l; GUdevClient *client; gboolean has_trigger = FALSE; char *trigger_name; const gchar * const subsystems[] = { "iio", NULL }; client = g_udev_client_new (subsystems); devices = g_udev_client_query_by_subsystem (client, "iio"); /* Find the associated trigger */ trigger_name = g_strdup_printf ("als-dev%s", g_udev_device_get_number (device)); for (l = devices; l != NULL; l = l->next) { GUdevDevice *dev = l->data; if (g_strcmp0 (trigger_name, g_udev_device_get_sysfs_attr (dev, "name")) == 0) { g_debug ("Found associated trigger at %s", g_udev_device_get_sysfs_path (dev)); has_trigger = TRUE; break; } } g_list_free_full (devices, g_object_unref); g_clear_object (&client); if (has_trigger) return trigger_name; g_warning ("Could not find trigger name associated with %s", g_udev_device_get_sysfs_path (device)); g_free (trigger_name); return NULL; } static gboolean read_light (gpointer user_data) { SensorDevice *sensor_device = user_data; DrvData *drv_data = (DrvData *) sensor_device->priv; prepare_output (sensor_device, drv_data->buffer_data->dev_dir_name, drv_data->buffer_data->trigger_name); return G_SOURCE_CONTINUE; } static gboolean iio_buffer_light_discover (GUdevDevice *device) { return drv_check_udev_sensor_type (device, "iio-buffer-als", "IIO buffer ALS"); } static void iio_buffer_light_set_polling (SensorDevice *sensor_device, gboolean state) { DrvData *drv_data = (DrvData *) sensor_device->priv; if (drv_data->timeout_id > 0 && state) return; if (drv_data->timeout_id == 0 && !state) return; if (drv_data->timeout_id) { g_source_remove (drv_data->timeout_id); drv_data->timeout_id = 0; } if (state) { drv_data->timeout_id = g_timeout_add (700, read_light, sensor_device); g_source_set_name_by_id (drv_data->timeout_id, "[iio_buffer_light_set_polling] read_light"); } } static SensorDevice * iio_buffer_light_open (GUdevDevice *device) { SensorDevice *sensor_device; DrvData *drv_data; g_autofree char *trigger_name = NULL; BufferDrvData *buffer_data; /* Get the trigger name, and build the channels from that */ trigger_name = get_trigger_name (device); if (!trigger_name) { g_debug ("Could not find trigger for %s", g_udev_device_get_sysfs_path (device)); return NULL; } buffer_data = buffer_drv_data_new (device, trigger_name); if (!buffer_data) return NULL; sensor_device = g_new0 (SensorDevice, 1); sensor_device->name = g_strdup (g_udev_device_get_property (device, "NAME")); if (!sensor_device->name) sensor_device->name = g_strdup (g_udev_device_get_name (device)); sensor_device->priv = g_new0 (DrvData, 1); drv_data = (DrvData *) sensor_device->priv; drv_data->dev = g_object_ref (device); drv_data->buffer_data = buffer_data; drv_data->dev_path = g_udev_device_get_device_file (device); return sensor_device; } static void iio_buffer_light_close (SensorDevice *sensor_device) { DrvData *drv_data = (DrvData *) sensor_device->priv; g_clear_pointer (&drv_data->buffer_data, buffer_drv_data_free); g_clear_object (&drv_data->dev); g_clear_pointer (&sensor_device->priv, g_free); g_free (sensor_device); } SensorDriver iio_buffer_light = { .driver_name = "IIO Buffer Light sensor", .type = DRIVER_TYPE_LIGHT, .discover = iio_buffer_light_discover, .open = iio_buffer_light_open, .set_polling = iio_buffer_light_set_polling, .close = iio_buffer_light_close, }; iio-sensor-proxy-3.5/src/drv-iio-poll-accel.c000066400000000000000000000076601445770364600211750ustar00rootroot00000000000000/* * Copyright (c) 2014 Bastien Nocera * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. */ #include "drivers.h" #include "iio-buffer-utils.h" #include "accel-mount-matrix.h" #include #include #include #include #include #include typedef struct DrvData { guint timeout_id; GUdevDevice *dev; AccelVec3 *mount_matrix; AccelLocation location; AccelScale scale; } DrvData; static gboolean poll_orientation (gpointer user_data) { SensorDevice *sensor_device = user_data; DrvData *drv_data = (DrvData *) sensor_device->priv; int accel_x, accel_y, accel_z; AccelReadings readings; AccelVec3 tmp; accel_x = g_udev_device_get_sysfs_attr_as_int_uncached (drv_data->dev, "in_accel_x_raw"); accel_y = g_udev_device_get_sysfs_attr_as_int_uncached (drv_data->dev, "in_accel_y_raw"); accel_z = g_udev_device_get_sysfs_attr_as_int_uncached (drv_data->dev, "in_accel_z_raw"); copy_accel_scale (&readings.scale, drv_data->scale); g_debug ("Accel read from IIO on '%s': %d, %d, %d (scale %lf,%lf,%lf)", sensor_device->name, accel_x, accel_y, accel_z, drv_data->scale.x, drv_data->scale.y, drv_data->scale.z); tmp.x = accel_x; tmp.y = accel_y; tmp.z = accel_z; if (!apply_mount_matrix (drv_data->mount_matrix, &tmp)) g_warning ("Could not apply mount matrix"); //FIXME report errors readings.accel_x = tmp.x; readings.accel_y = tmp.y; readings.accel_z = tmp.z; sensor_device->callback_func (sensor_device, (gpointer) &readings, sensor_device->user_data); return G_SOURCE_CONTINUE; } static gboolean iio_poll_accel_discover (GUdevDevice *device) { /* We also handle devices with trigger buffers, but there's no trigger available on the system */ if (!drv_check_udev_sensor_type (device, "iio-poll-accel", NULL) && !drv_check_udev_sensor_type (device, "iio-buffer-accel", NULL)) return FALSE; g_debug ("Found IIO poll accelerometer at %s", g_udev_device_get_sysfs_path (device)); return TRUE; } static void iio_poll_accel_set_polling (SensorDevice *sensor_device, gboolean state) { DrvData *drv_data = (DrvData *) sensor_device->priv; if (drv_data->timeout_id > 0 && state) return; if (drv_data->timeout_id == 0 && !state) return; if (drv_data->timeout_id) { g_source_remove (drv_data->timeout_id); drv_data->timeout_id = 0; } if (state) { drv_data->timeout_id = g_timeout_add (700, poll_orientation, sensor_device); g_source_set_name_by_id (drv_data->timeout_id, "[iio_poll_accel_set_polling] poll_orientation"); } } static SensorDevice * iio_poll_accel_open (GUdevDevice *device) { SensorDevice *sensor_device; DrvData *drv_data; iio_fixup_sampling_frequency (device); sensor_device = g_new0 (SensorDevice, 1); sensor_device->name = g_strdup (g_udev_device_get_property (device, "NAME")); if (!sensor_device->name) sensor_device->name = g_strdup (g_udev_device_get_name (device)); sensor_device->priv = g_new0 (DrvData, 1); drv_data = (DrvData *) sensor_device->priv; drv_data->dev = g_object_ref (device); drv_data->mount_matrix = setup_mount_matrix (device); drv_data->location = setup_accel_location (device); if (!get_accel_scale (device, &drv_data->scale)) reset_accel_scale (&drv_data->scale); return sensor_device; } static void iio_poll_accel_close (SensorDevice *sensor_device) { DrvData *drv_data = (DrvData *) sensor_device->priv; g_clear_object (&drv_data->dev); g_clear_pointer (&drv_data->mount_matrix, g_free); g_clear_pointer (&sensor_device->priv, g_free); g_free (sensor_device); } SensorDriver iio_poll_accel = { .driver_name = "IIO Poll accelerometer", .type = DRIVER_TYPE_ACCEL, .discover = iio_poll_accel_discover, .open = iio_poll_accel_open, .set_polling = iio_poll_accel_set_polling, .close = iio_poll_accel_close, }; iio-sensor-proxy-3.5/src/drv-iio-poll-light.c000066400000000000000000000115541445770364600212320ustar00rootroot00000000000000/* * Copyright (c) 2014 Bastien Nocera * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. */ #include "drivers.h" #include "iio-buffer-utils.h" #include #include #include #include #include #define DEFAULT_POLL_TIME 0.8 typedef struct DrvData { char *input_path; guint interval; guint timeout_id; double scale; } DrvData; static gboolean iio_poll_light_discover (GUdevDevice *device) { return drv_check_udev_sensor_type (device, "iio-poll-als", "IIO poll als"); } static gboolean light_changed (gpointer user_data) { SensorDevice *sensor_device = user_data; DrvData *drv_data = (DrvData *) sensor_device->priv; LightReadings readings; gdouble level; char *contents; g_autoptr(GError) error = NULL; if (g_file_get_contents (drv_data->input_path, &contents, NULL, &error)) { level = g_ascii_strtod (contents, NULL); g_free (contents); } else { g_warning ("Failed to read input level from %s at %s: %s", sensor_device->name, drv_data->input_path, error->message); return G_SOURCE_CONTINUE; } readings.level = level * drv_data->scale; g_debug ("Light read from %s: %lf, (scale %lf)", sensor_device->name, level, drv_data->scale); /* Even though the IIO kernel API declares in_intensity* values as unitless, * we use Microsoft's hid-sensors-usages.docx which mentions that Windows 8 * compatible sensor proxies will be using Lux as the unit, and most sensors * will be Windows 8 compatible */ readings.uses_lux = TRUE; sensor_device->callback_func (sensor_device, (gpointer) &readings, sensor_device->user_data); return G_SOURCE_CONTINUE; } static char * get_illuminance_channel_path (GUdevDevice *device, const char *suffix) { const char *channels[] = { "in_illuminance", "in_illuminance0", "in_intensity_clear" }; char *path = NULL; guint i; for (i = 0; i < G_N_ELEMENTS (channels); i++) { path = g_strdup_printf ("%s/%s_%s", g_udev_device_get_sysfs_path (device), channels[i], suffix); if (g_file_test (path, G_FILE_TEST_EXISTS)) return path; g_clear_pointer (&path, g_free); } return NULL; } static guint get_interval (GUdevDevice *device) { gdouble time = DEFAULT_POLL_TIME; char *path, *contents; path = get_illuminance_channel_path (device, "integration_time"); if (!path) goto out; if (g_file_get_contents (path, &contents, NULL, NULL)) { time = g_ascii_strtod (contents, NULL); g_free (contents); } g_free (path); out: return (time * 1000); } static void iio_poll_light_set_polling (SensorDevice *sensor_device, gboolean state) { DrvData *drv_data = (DrvData *) sensor_device->priv; if (drv_data->timeout_id > 0 && state) return; if (drv_data->timeout_id == 0 && !state) return; if (drv_data->timeout_id) { g_source_remove (drv_data->timeout_id); drv_data->timeout_id = 0; } if (state) { drv_data->timeout_id = g_timeout_add (drv_data->interval, (GSourceFunc) light_changed, sensor_device); g_source_set_name_by_id (drv_data->timeout_id, "[iio_poll_light_set_polling] light_changed"); } } static SensorDevice * iio_poll_light_open (GUdevDevice *device) { SensorDevice *sensor_device; DrvData *drv_data; g_autofree char *input_path = NULL; iio_fixup_sampling_frequency (device); input_path = get_illuminance_channel_path (device, "input"); if (!input_path) input_path = get_illuminance_channel_path (device, "raw"); if (!input_path) return NULL; sensor_device = g_new0 (SensorDevice, 1); sensor_device->name = g_strdup (g_udev_device_get_property (device, "NAME")); if (!sensor_device->name) sensor_device->name = g_strdup (g_udev_device_get_name (device)); sensor_device->priv = g_new0 (DrvData, 1); drv_data = (DrvData *) sensor_device->priv; drv_data->interval = get_interval (device); if (g_str_has_prefix (input_path, "in_illuminance0")) { drv_data->scale = g_udev_device_get_sysfs_attr_as_double (device, "in_illuminance0_scale"); } else { drv_data->scale = g_udev_device_get_sysfs_attr_as_double (device, "in_illuminance_scale"); } if (drv_data->scale == 0.0) drv_data->scale = 1.0; drv_data->input_path = g_steal_pointer (&input_path); return sensor_device; } static void iio_poll_light_close (SensorDevice *sensor_device) { DrvData *drv_data = (DrvData *) sensor_device->priv; g_clear_pointer (&drv_data->input_path, g_free); g_clear_pointer (&sensor_device->priv, g_free); g_free (sensor_device); } SensorDriver iio_poll_light = { .driver_name = "IIO Polling Light sensor", .type = DRIVER_TYPE_LIGHT, .discover = iio_poll_light_discover, .open = iio_poll_light_open, .set_polling = iio_poll_light_set_polling, .close = iio_poll_light_close, }; iio-sensor-proxy-3.5/src/drv-iio-poll-proximity.c000066400000000000000000000076561445770364600221770ustar00rootroot00000000000000/* * Copyright (c) 2019 Purism SPC * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. */ #include "drivers.h" #include "iio-buffer-utils.h" #include #include #include #include #include #include #define PROXIMITY_NEAR_LEVEL "PROXIMITY_NEAR_LEVEL" #define PROXIMITY_WATER_MARK_LOW 0.9 #define PROXIMITY_WATER_MARK_HIGH 1.1 typedef struct DrvData { guint timeout_id; GUdevDevice *dev; gint near_level; gint last_level; } DrvData; static gboolean poll_proximity (gpointer user_data) { SensorDevice *sensor_device = user_data; DrvData *drv_data = (DrvData *) sensor_device->priv; ProximityReadings readings; gint prox; gdouble near_level = drv_data->near_level; /* g_udev_device_get_sysfs_attr_as_int does not update when there's no event */ prox = g_udev_device_get_sysfs_attr_as_int_uncached (drv_data->dev, "in_proximity_raw"); /* Use a margin so we don't trigger too often */ near_level *= (drv_data->last_level > near_level) ? PROXIMITY_WATER_MARK_LOW : PROXIMITY_WATER_MARK_HIGH; readings.is_near = (prox > near_level) ? PROXIMITY_NEAR_TRUE : PROXIMITY_NEAR_FALSE; g_debug ("Proximity read from IIO on '%s': %d/%f, near: %d", sensor_device->name, prox, near_level, readings.is_near); drv_data->last_level = prox; sensor_device->callback_func (sensor_device, (gpointer) &readings, sensor_device->user_data); return G_SOURCE_CONTINUE; } static gboolean iio_poll_proximity_discover (GUdevDevice *device) { return drv_check_udev_sensor_type (device, "iio-poll-proximity", "IIO poll proximity sensor"); } static void iio_poll_proximity_set_polling (SensorDevice *sensor_device, gboolean state) { DrvData *drv_data = (DrvData *) sensor_device->priv; if (drv_data->timeout_id > 0 && state) return; if (drv_data->timeout_id == 0 && !state) return; g_clear_handle_id (&drv_data->timeout_id, g_source_remove); if (state) { drv_data->timeout_id = g_timeout_add (700, poll_proximity, sensor_device); g_source_set_name_by_id (drv_data->timeout_id, "[iio_poll_proximity_set_polling] poll_proximity"); } } static gint get_near_level (GUdevDevice *device) { gint near_level; near_level = g_udev_device_get_property_as_int (device, PROXIMITY_NEAR_LEVEL); if (!near_level) near_level = g_udev_device_get_sysfs_attr_as_int (device, "in_proximity_nearlevel"); if (!near_level) { g_warning ("Found proximity sensor but no " PROXIMITY_NEAR_LEVEL " udev property"); g_warning ("See https://gitlab.freedesktop.org/hadess/iio-sensor-proxy/blob/master/README.md"); return 0; } g_debug ("Near level: %d", near_level); return near_level; } static SensorDevice * iio_poll_proximity_open (GUdevDevice *device) { SensorDevice *sensor_device; DrvData *drv_data; int near_level; iio_fixup_sampling_frequency (device); near_level = get_near_level (device); if (!near_level) return FALSE; sensor_device = g_new0 (SensorDevice, 1); sensor_device->name = g_strdup (g_udev_device_get_property (device, "NAME")); if (!sensor_device->name) sensor_device->name = g_strdup (g_udev_device_get_name (device)); sensor_device->priv = g_new0 (DrvData, 1); drv_data = (DrvData *) sensor_device->priv; drv_data->dev = g_object_ref (device); drv_data->near_level = near_level; return sensor_device; } static void iio_poll_proximity_close (SensorDevice *sensor_device) { DrvData *drv_data = (DrvData *) sensor_device->priv; g_clear_object (&drv_data->dev); g_clear_pointer (&sensor_device->priv, g_free); g_free (sensor_device); } SensorDriver iio_poll_proximity = { .driver_name = "IIO Poll proximity sensor", .type = DRIVER_TYPE_PROXIMITY, .discover = iio_poll_proximity_discover, .open = iio_poll_proximity_open, .set_polling = iio_poll_proximity_set_polling, .close = iio_poll_proximity_close, }; iio-sensor-proxy-3.5/src/drv-input-accel.c000066400000000000000000000163541445770364600206100ustar00rootroot00000000000000/* * Copyright (c) 2014 Bastien Nocera * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. */ #include "drivers.h" #include "accel-mount-matrix.h" #include "utils.h" #include #include #include #include #include #include typedef struct DrvData { guint timeout_id; GUdevClient *client; GUdevDevice *dev, *parent; const char *dev_path; AccelVec3 *mount_matrix; AccelLocation location; gboolean sends_kevent; } DrvData; static void input_accel_set_polling (SensorDevice *sensor_device, gboolean state); /* From src/linux/up-device-supply.c in UPower */ static GUdevDevice * get_sibling_with_subsystem (GUdevDevice *device, const char *subsystem) { GUdevDevice *parent; GUdevClient *client; GUdevDevice *sibling; const char * class[] = { NULL, NULL }; const char *parent_path; GList *devices, *l; g_return_val_if_fail (device != NULL, NULL); g_return_val_if_fail (subsystem != NULL, NULL); parent = g_udev_device_get_parent (device); if (!parent) return NULL; parent_path = g_udev_device_get_sysfs_path (parent); sibling = NULL; class[0] = subsystem; client = g_udev_client_new (class); devices = g_udev_client_query_by_subsystem (client, subsystem); for (l = devices; l != NULL && sibling == NULL; l = l->next) { GUdevDevice *d = l->data; GUdevDevice *p; const char *p_path; p = g_udev_device_get_parent (d); if (!p) continue; p_path = g_udev_device_get_sysfs_path (p); if (g_strcmp0 (p_path, parent_path) == 0) sibling = g_object_ref (d); g_object_unref (p); } g_list_free_full (devices, (GDestroyNotify) g_object_unref); g_object_unref (client); g_object_unref (parent); return sibling; } static gboolean is_part_of_joypad (GUdevDevice *device) { g_autoptr(GUdevDevice) sibling; sibling = get_sibling_with_subsystem (device, "input"); if (!sibling) return FALSE; return g_udev_device_get_property_as_boolean (sibling, "ID_INPUT_JOYSTICK"); } static gboolean input_accel_discover (GUdevDevice *device) { const char *path; g_autoptr(GUdevDevice) parent = NULL; if (!drv_check_udev_sensor_type (device, "input-accel", NULL)) return FALSE; path = g_udev_device_get_device_file (device); if (!path) return FALSE; if (strstr (path, "/event") == NULL) return FALSE; parent = g_udev_device_get_parent (device); if (parent && is_part_of_joypad (parent)) return FALSE; g_debug ("Found input accel at %s", g_udev_device_get_sysfs_path (device)); return TRUE; } #define READ_AXIS(axis, var) { memzero(&abs_info, sizeof(abs_info)); r = ioctl(fd, EVIOCGABS(axis), &abs_info); if (r < 0) return; var = abs_info.value; } #define memzero(x,l) (memset((x), 0, (l))) static void accelerometer_changed (gpointer user_data) { SensorDevice *sensor_device = user_data; DrvData *drv_data = (DrvData *) sensor_device->priv; struct input_absinfo abs_info; int accel_x = 0, accel_y = 0, accel_z = 0; g_auto(IioFd) fd = -1; int r; AccelReadings readings; AccelVec3 tmp; fd = open (drv_data->dev_path, O_RDONLY|O_CLOEXEC); if (fd < 0) { g_warning ("Could not open input accel '%s': %s", drv_data->dev_path, g_strerror (errno)); return; } READ_AXIS(ABS_X, accel_x); READ_AXIS(ABS_Y, accel_y); READ_AXIS(ABS_Z, accel_z); /* Scale from 1G ~= 256 to a value in m/s² */ set_accel_scale (&readings.scale, 1.0 / 256 * 9.81); g_debug ("Accel read from input on '%s': %d, %d, %d (scale %lf,%lf,%lf)", sensor_device->name, accel_x, accel_y, accel_z, readings.scale.x, readings.scale.y, readings.scale.z); tmp.x = accel_x; tmp.y = accel_y; tmp.z = accel_z; if (!apply_mount_matrix (drv_data->mount_matrix, &tmp)) g_warning ("Could not apply mount matrix"); readings.accel_x = tmp.x; readings.accel_y = tmp.y; readings.accel_z = tmp.z; sensor_device->callback_func (sensor_device, (gpointer) &readings, sensor_device->user_data); } static void uevent_received (GUdevClient *client, gchar *action, GUdevDevice *device, gpointer user_data) { SensorDevice *sensor_device = user_data; DrvData *drv_data = (DrvData *) sensor_device->priv; if (g_strcmp0 (action, "change") != 0) return; if (g_strcmp0 (g_udev_device_get_sysfs_path (device), g_udev_device_get_sysfs_path (drv_data->parent)) != 0) return; if (!drv_data->sends_kevent) { drv_data->sends_kevent = TRUE; g_debug ("Received kevent, let's stop polling for accelerometer data on %s", drv_data->dev_path); input_accel_set_polling (sensor_device, FALSE); } accelerometer_changed (sensor_device); } static gboolean first_values (gpointer user_data) { accelerometer_changed (user_data); return G_SOURCE_REMOVE; } static SensorDevice * input_accel_open (GUdevDevice *device) { const gchar * const subsystems[] = { "input", NULL }; SensorDevice *sensor_device; DrvData *drv_data; sensor_device = g_new0 (SensorDevice, 1); sensor_device->name = g_strdup (g_udev_device_get_property (device, "NAME")); if (!sensor_device->name) sensor_device->name = g_strdup (g_udev_device_get_name (device)); if (!sensor_device->name) sensor_device->name = g_strdup (g_udev_device_get_property (device, "ID_MODEL")); if (!sensor_device->name) { g_autoptr(GUdevDevice) parent = NULL; parent = g_udev_device_get_parent (device); sensor_device->name = g_strdup (g_udev_device_get_property (parent, "NAME")); } sensor_device->priv = g_new0 (DrvData, 1); drv_data = (DrvData *) sensor_device->priv; drv_data->dev = g_object_ref (device); drv_data->parent = g_udev_device_get_parent (drv_data->dev); drv_data->dev_path = g_udev_device_get_device_file (device); drv_data->client = g_udev_client_new (subsystems); drv_data->mount_matrix = setup_mount_matrix (device); drv_data->location = setup_accel_location (device); g_signal_connect (drv_data->client, "uevent", G_CALLBACK (uevent_received), sensor_device); g_idle_add (first_values, sensor_device); return sensor_device; } static gboolean read_accel_poll (gpointer user_data) { accelerometer_changed (user_data); return G_SOURCE_CONTINUE; } static void input_accel_set_polling (SensorDevice *sensor_device, gboolean state) { DrvData *drv_data = (DrvData *) sensor_device->priv; if (drv_data->timeout_id > 0 && state) return; if (drv_data->timeout_id == 0 && !state) return; if (drv_data->timeout_id) { g_source_remove (drv_data->timeout_id); drv_data->timeout_id = 0; } if (state && !drv_data->sends_kevent) { drv_data->timeout_id = g_timeout_add (700, read_accel_poll, sensor_device); g_source_set_name_by_id (drv_data->timeout_id, "[input_accel_set_polling] read_accel_poll"); } } static void input_accel_close (SensorDevice *sensor_device) { DrvData *drv_data = (DrvData *) sensor_device->priv; g_clear_object (&drv_data->client); g_clear_object (&drv_data->dev); g_clear_object (&drv_data->parent); g_clear_pointer (&drv_data->mount_matrix, g_free); g_clear_pointer (&sensor_device->priv, g_free); } SensorDriver input_accel = { .driver_name = "Input accelerometer", .type = DRIVER_TYPE_ACCEL, .discover = input_accel_discover, .open = input_accel_open, .set_polling = input_accel_set_polling, .close = input_accel_close, }; iio-sensor-proxy-3.5/src/fake-input-accelerometer.c000066400000000000000000000142451445770364600224630ustar00rootroot00000000000000/* * Copyright (c) 2011, 2014 Bastien Nocera * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include "uinput.h" #include "utils.h" #define ONEG 256 typedef struct { GMainLoop *loop; int uinput; int accel_x, accel_y, accel_z; GUdevClient *client; GUdevDevice *uinput_dev; struct termios old_tio; } OrientationData; static GUdevDevice * setup_uinput_udev (GUdevClient *client) { GList *devices, *l; GUdevDevice *ret = NULL; devices = g_udev_client_query_by_subsystem (client, "input"); for (l = devices; l != NULL; l = l->next) { GUdevDevice *dev = l->data; if (g_udev_device_get_property_as_boolean (dev, "ID_INPUT_ACCELEROMETER")) { ret = g_object_ref (dev); break; } } g_list_free_full (devices, g_object_unref); return ret; } static int write_sysfs_string (char *filename, char *basedir, char *val) { int ret = 0; g_autoptr(FILE) sysfsfp = NULL; g_autofree char *temp = NULL; temp = g_build_filename (basedir, filename, NULL); sysfsfp = fopen (temp, "w"); if (sysfsfp == NULL) { ret = -errno; return ret; } fprintf(sysfsfp, "%s", val); return ret; } static gboolean send_uinput_event (OrientationData *data) { struct uinput_event ev; memset(&ev, 0, sizeof(ev)); ev.type = EV_ABS; ev.code = ABS_X; ev.value = data->accel_x; (void) !write (data->uinput, &ev, sizeof(ev)); ev.code = ABS_Y; ev.value = data->accel_y; (void) !write (data->uinput, &ev, sizeof(ev)); ev.code = ABS_Z; ev.value = data->accel_z; (void) !write (data->uinput, &ev, sizeof(ev)); memset(&ev, 0, sizeof(ev)); gettimeofday(&ev.time, NULL); ev.type = EV_SYN; ev.code = SYN_REPORT; (void) !write (data->uinput, &ev, sizeof(ev)); if (!data->uinput_dev) data->uinput_dev = setup_uinput_udev (data->client); if (!data->uinput_dev) return FALSE; if (write_sysfs_string ((char *) "uevent", (char *) g_udev_device_get_sysfs_path (data->uinput_dev), (char *) "change") < 0) { g_warning ("Failed to write uevent"); return FALSE; } return TRUE; } static gboolean setup_uinput (OrientationData *data) { struct uinput_dev dev; g_auto(IioFd) fd = -1; fd = open("/dev/uinput", O_RDWR); if (fd < 0) { g_warning ("Could not open uinput"); return FALSE; } memset (&dev, 0, sizeof(dev)); snprintf (dev.name, sizeof (dev.name), "%s", "iio-sensor-proxy test application"); dev.id.bustype = BUS_VIRTUAL; dev.id.vendor = 0x00; dev.id.product = 0x00; /* 1G accel is reported as ~256, so clamp to 2G */ dev.absmin[ABS_X] = dev.absmin[ABS_Y] = dev.absmin[ABS_Z] = -512; dev.absmax[ABS_X] = dev.absmax[ABS_Y] = dev.absmax[ABS_Z] = 512; if (write (fd, &dev, sizeof(dev)) != sizeof(dev)) { g_warning ("Error creating uinput device"); return FALSE; } /* enabling key events */ if (ioctl (fd, UI_SET_EVBIT, EV_ABS) < 0) { g_warning ("Error enabling uinput absolute events"); return FALSE; } /* enabling keys */ if (ioctl (fd, UI_SET_ABSBIT, ABS_X) < 0 || ioctl (fd, UI_SET_ABSBIT, ABS_Y) < 0 || ioctl (fd, UI_SET_ABSBIT, ABS_Z) < 0) { g_warning ("Couldn't enable uinput axis"); return FALSE; } /* creating the device */ if (ioctl (fd, UI_DEV_CREATE) < 0) { g_warning ("Error creating uinput device"); return FALSE; } data->uinput = g_steal_fd (&fd); return TRUE; } static void keyboard_usage (void) { g_print ("Valid keys are: u (up), d (down), l (left), r (right), q/x (quit)\n"); } static gboolean check_keyboard (GIOChannel *source, GIOCondition condition, OrientationData *data) { GIOStatus status; char buf[1]; status = g_io_channel_read_chars (source, buf, 1, NULL, NULL); if (status == G_IO_STATUS_ERROR || status == G_IO_STATUS_EOF) { g_main_loop_quit (data->loop); return FALSE; } if (status == G_IO_STATUS_AGAIN) return TRUE; switch (buf[0]) { case 'u': data->accel_x = 0; data->accel_y = -ONEG; data->accel_z = 0; break; case 'd': data->accel_x = 0; data->accel_y = ONEG; data->accel_z = 0; break; case 'l': data->accel_x = ONEG; data->accel_y = 0; data->accel_z = 0; break; case 'r': data->accel_x = -ONEG; data->accel_y = 0; data->accel_z = 0; break; case 'q': case 'x': g_main_loop_quit (data->loop); return FALSE; default: keyboard_usage (); return TRUE; } send_uinput_event (data); return TRUE; } static gboolean setup_keyboard (OrientationData *data) { GIOChannel *channel; struct termios new_tio; tcgetattr(STDIN_FILENO, &data->old_tio); new_tio = data->old_tio; new_tio.c_lflag &=(~ICANON & ~ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &new_tio); channel = g_io_channel_unix_new (STDIN_FILENO); if (!channel) { g_warning ("Failed to open stdin"); return FALSE; } if (g_io_channel_set_encoding (channel, NULL, NULL) != G_IO_STATUS_NORMAL) { g_warning ("Failed to set stdin encoding to NULL"); return FALSE; } g_io_add_watch (channel, G_IO_IN, (GIOFunc) check_keyboard, data); return TRUE; } static void free_orientation_data (OrientationData *data) { if (data == NULL) return; tcsetattr(STDIN_FILENO, TCSANOW, &data->old_tio); if (data->uinput > 0) close (data->uinput); g_clear_object (&data->uinput_dev); g_clear_object (&data->client); g_clear_pointer (&data->loop, g_main_loop_unref); g_free (data); } int main (int argc, char **argv) { OrientationData *data; const gchar * const subsystems[] = { "input", NULL }; int ret = 0; data = g_new0 (OrientationData, 1); data->client = g_udev_client_new (subsystems); if (!setup_keyboard (data)) { g_warning ("Failed to setup keyboard capture"); ret = 1; goto out; } /* Set up uinput */ if (!setup_uinput (data)) { ret = 1; goto out; } /* Start with the 'normal' orientation */ data->accel_x = 0; data->accel_y = ONEG; data->accel_z = 0; send_uinput_event (data); keyboard_usage (); data->loop = g_main_loop_new (NULL, TRUE); g_main_loop_run (data->loop); out: free_orientation_data (data); return ret; } iio-sensor-proxy-3.5/src/iio-buffer-utils.c000066400000000000000000000544241445770364600210000ustar00rootroot00000000000000/* * Modified from industrialio buffer test code, and Lenovo Yoga (2 Pro) orientation helper * Copyright (c) 2008 Jonathan Cameron * Copyright (c) 2014 Peter F. Patel-Schneider * Copyright (c) 2014, 2021 Bastien Nocera * Copyright (c) 2015 Elad Alfassa * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. */ #include "iio-buffer-utils.h" #include "utils.h" #include #include #include #include #include #include #include #define IIO_MIN_SAMPLING_FREQUENCY 10 /* Hz */ /** * iio_channel_info - information about a given channel * @name: channel name * @generic_name: the generic name of the channel * @scale: scale factor to be applied for conversion to SI units * @offset: offset to be applied for conversion to SI units * @index: the channel index in the buffer output * @is_signed: signed or unsigned * @bits_used: number of valid bits of data * @bytes: number of bytes occupied in buffer output (bits_used + padding) * @shift: shift right by this before masking out bits_used * @mask: a bit mask for the raw output * @be: little or big endian * @enabled: is this channel enabled * @location: data offset for this channel inside the buffer (in bytes) **/ struct iio_channel_info { char *name; char *generic_name; float scale; float offset; unsigned index; unsigned is_signed; unsigned bits_used; unsigned bytes; unsigned shift; guint64 mask; unsigned be; unsigned enabled; unsigned location; }; static char * iioutils_break_up_name (const char *name) { g_auto(GStrv) items = NULL; guint i; items = g_strsplit (name, "_", -1); for (i = 0; items[i] != NULL; i++) { if (items[i + 1] == NULL) { g_clear_pointer (&items[i], g_free); break; } } return g_strjoinv ("_", items); } /** * iioutils_get_type() - find and process _type attribute data * @is_signed: output whether channel is signed * @bytes: output how many bytes the channel storage occupies * @mask: output a bit mask for the raw data * @be: big endian * @device_dir: the iio device directory * @name: the channel name * * See `struct iio_chan_spec` in the kernel headers. **/ static gboolean iioutils_get_type (unsigned *is_signed, unsigned *bytes, unsigned *bits_used, unsigned *shift, guint64 *mask, unsigned *be, const char *device_dir, const char *name, const char *generic_name) { int ret; g_autofree char *builtname = NULL; g_autofree char *filename = NULL; char signchar, endianchar; unsigned padint; g_autoptr(FILE) sysfsfp = NULL; builtname = g_strdup_printf ("%s_type", name); filename = g_build_filename (device_dir, "scan_elements", builtname, NULL); sysfsfp = fopen (filename, "r"); if (sysfsfp == NULL) { g_clear_pointer (&builtname, g_free); g_clear_pointer (&filename, g_free); builtname = g_strdup_printf ("%s_type", generic_name); filename = g_build_filename (device_dir, "scan_elements", builtname, NULL); sysfsfp = fopen (filename, "r"); if (sysfsfp == NULL) return FALSE; } /* See `iio_show_fixed_type()` in the IIO core for the format */ ret = fscanf (sysfsfp, "%ce:%c%u/%u>>%u", &endianchar, &signchar, bits_used, &padint, shift); if (ret < 0 || ret != 5) { g_warning ("Failed to pass scan type description for %s", filename); return FALSE; } *be = (endianchar == 'b'); *bytes = padint / 8; if (*bits_used == 64) *mask = ~G_GUINT64_CONSTANT(0); else *mask = (G_GUINT64_CONSTANT(1) << *bits_used) - G_GUINT64_CONSTANT(1); *is_signed = (signchar == 's'); g_debug ("Got type for %s: is signed: %d, bytes: %d, bits_used: %d, shift: %d, mask: 0x%" G_GUINT64_FORMAT ", be: %d", name, *is_signed, *bytes, *bits_used, *shift, *mask, *be); return TRUE; } static int iioutils_get_param_float (float *output, const char *param_name, const char *device_dir, const char *name, const char *generic_name) { g_autofree char *builtname = NULL; g_autofree char *filename = NULL; g_autofree char *contents = NULL; g_autoptr(GError) error = NULL; g_debug ("Trying to read '%s_%s' (name) from dir '%s'", name, param_name, device_dir); builtname = g_strdup_printf ("%s_%s", name, param_name); filename = g_build_filename (device_dir, builtname, NULL); if (g_file_get_contents (filename, &contents, NULL, &error)) { char *endptr; *output = g_ascii_strtod (contents, &endptr); if (*output != 0.0 || endptr != contents) return 0; g_warning ("Couldn't convert '%s' from %s to float", g_strchomp (contents), filename); } else { g_debug ("Failed to read float from %s: %s", filename, error->message); g_clear_error (&error); } g_debug ("Trying to read '%s_%s' (generic name) from dir '%s'", generic_name, param_name, device_dir); g_clear_pointer (&builtname, g_free); g_clear_pointer (&filename, g_free); builtname = g_strdup_printf ("%s_%s", generic_name, param_name); filename = g_build_filename (device_dir, builtname, NULL); g_clear_pointer (&contents, g_free); if (g_file_get_contents (filename, &contents, NULL, &error)) { char *endptr; *output = g_ascii_strtod (contents, &endptr); if (*output == 0.0 && endptr == contents) { g_warning ("Couldn't convert '%s' from %s to float", g_strchomp (contents), filename); return -EINVAL; } } else { if (g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { g_debug ("Failed to read float from non-existent %s", filename); return -ENOENT; } else { g_warning ("Failed to read float from %s: %s", filename, error->message); return -EINVAL; } } return 0; } static void channel_info_free (iio_channel_info *ci) { g_free (ci->name); g_free (ci->generic_name); g_free (ci); } static int compare_channel_index (gconstpointer a, gconstpointer b) { const iio_channel_info *info_1 = *(iio_channel_info **) a; const iio_channel_info *info_2 = *(iio_channel_info **) b; return (int) (info_1->index - info_2->index); } G_DEFINE_AUTOPTR_CLEANUP_FUNC(iio_channel_info, channel_info_free) /* build_channel_array() - function to figure out what channels are present */ static iio_channel_info ** build_channel_array (const char *device_dir, int *counter) { GDir *dp; int ret; const char *name; char *scan_el_dir; GPtrArray *array; iio_channel_info **ret_array; int i; *counter = 0; scan_el_dir = g_build_filename (device_dir, "scan_elements", NULL); dp = g_dir_open (scan_el_dir, 0, NULL); if (dp == NULL) { ret = -errno; g_debug ("Could not open scan_elements dir '%s'", scan_el_dir); g_free (scan_el_dir); return NULL; } array = g_ptr_array_new_full (0, (GDestroyNotify) channel_info_free); while ((name = g_dir_read_name (dp)) != NULL) { if (g_str_has_suffix (name, "_en")) { g_autoptr(FILE) sysfsfp = NULL; g_autofree char *filename = NULL; g_autofree char *index_name = NULL; g_autoptr(iio_channel_info) current = NULL; filename = g_build_filename (scan_el_dir, name, NULL); sysfsfp = fopen (filename, "r"); if (sysfsfp == NULL) { g_debug ("Could not open scan_elements file '%s'", filename); continue; } if (fscanf (sysfsfp, "%d", &ret) != 1) { g_debug ("Could not read from scan_elements file '%s'", filename); continue; } g_clear_pointer (&sysfsfp, fclose); current = g_new0 (iio_channel_info, 1); current->scale = 1.0; current->offset = 0; current->name = g_strndup (name, strlen(name) - strlen("_en")); current->generic_name = iioutils_break_up_name (current->name); if (g_strcmp0(current->generic_name, "in_rot_from_north_magnetic_tilt") == 0) { current->generic_name = g_strdup ("in_rot"); } index_name = g_strdup_printf ("%s_index", current->name); g_clear_pointer (&filename, g_free); filename = g_build_filename (scan_el_dir, index_name, NULL); sysfsfp = fopen (filename, "r"); if (sysfsfp == NULL) goto error; ret = fscanf (sysfsfp, "%u", ¤t->index); if (ret != 1) goto error; /* Find the scale */ ret = iioutils_get_param_float (¤t->scale, "scale", device_dir, current->name, current->generic_name); if ((ret < 0) && (ret != -ENOENT)) goto error; if (current->scale == 0.0) goto error; ret = iioutils_get_param_float (¤t->offset, "offset", device_dir, current->name, current->generic_name); if ((ret < 0) && (ret != -ENOENT)) goto error; ret = iioutils_get_type (¤t->is_signed, ¤t->bytes, ¤t->bits_used, ¤t->shift, ¤t->mask, ¤t->be, device_dir, current->name, current->generic_name); if (!ret) { g_warning ("Could not parse name %s, generic name %s", current->name, current->generic_name); } else { g_ptr_array_add (array, g_steal_pointer (¤t)); } } } g_dir_close (dp); g_free (scan_el_dir); g_ptr_array_sort (array, compare_channel_index); *counter = array->len; ret_array = (iio_channel_info **) g_ptr_array_free (array, FALSE); for (i = 0; i < *counter; i++) { iio_channel_info *ci = ret_array[i]; g_debug ("Built channel array for %s: index: %d, is signed: %d, bytes: %d, bits_used: %d, shift: %d, mask: 0x%" G_GUINT64_FORMAT ", be: %d", ci->name, ci->index, ci->is_signed, ci->bytes, ci->bits_used, ci->shift, ci->mask, ci->be); } return ret_array; error: g_ptr_array_free (array, TRUE); g_dir_close (dp); g_free (scan_el_dir); return NULL; } static int _write_sysfs_int (const char *filename, const char *basedir, int val, int verify, int type, int val2) { int ret = 0; g_autoptr(FILE) sysfsfp = NULL; int test; char *temp; temp = g_build_filename (basedir, filename, NULL); sysfsfp = fopen(temp, "w"); if (sysfsfp == NULL) { g_warning ("Could not open for write '%s'", temp); ret = -errno; goto error_free; } if (type) fprintf(sysfsfp, "%d %d", val, val2); else fprintf(sysfsfp, "%d", val); g_clear_pointer (&sysfsfp, fclose); if (verify) { sysfsfp = fopen(temp, "r"); if (sysfsfp == NULL) { g_warning ("Could not open for read '%s'", temp); ret = -errno; goto error_free; } if (fscanf(sysfsfp, "%d", &test) != 1 || test != val) { g_warning ("Possible failure in int write %d to %s", val, temp); ret = -1; } } error_free: g_free (temp); return ret; } static int write_sysfs_int(const char *filename, const char *basedir, int val) { return _write_sysfs_int(filename, basedir, val, 0, 0, 0); } static int write_sysfs_int_and_verify(const char *filename, const char *basedir, int val) { return _write_sysfs_int(filename, basedir, val, 1, 0, 0); } static int _write_sysfs_string (const char *filename, const char *basedir, const char *val, int verify) { g_autoptr(FILE) sysfsfp = NULL; g_autofree char *temp = NULL; g_autofree char *contents = NULL; temp = g_build_filename (basedir, filename, NULL); sysfsfp = fopen (temp, "w"); if (sysfsfp == NULL) { return -errno; } fprintf(sysfsfp, "%s", val); /* Verify? */ if (!verify) return 0; g_clear_pointer (&sysfsfp, fclose); if (!g_file_get_contents (temp, &contents, NULL, NULL) || g_strcmp0 (g_strchomp (contents), val) != 0) { g_warning ("Possible failure in string write of '%s' Should be '%s' written to %s\n", contents ?: "(null)", val, temp); return -1; } return 0; } /** * write_sysfs_string_and_verify() - string write, readback and verify * @filename: name of file to write to * @basedir: the sysfs directory in which the file is to be found * @val: the string to write **/ static int write_sysfs_string_and_verify(const char *filename, const char *basedir, const char *val) { return _write_sysfs_string(filename, basedir, val, 1); } static int write_sysfs_string(const char *filename, const char *basedir, const char *val) { return _write_sysfs_string(filename, basedir, val, 0); } /** * size_from_channelarray() - calculate the storage size of a scan * @channels: the channel info array * @num_channels: number of channels * * Has the side effect of filling the channels[i].location values used * in processing the buffer output. **/ static int size_from_channelarray (iio_channel_info **channels, int num_channels) { int bytes = 0; int i = 0; while (i < num_channels) { if (bytes % channels[i]->bytes == 0) channels[i]->location = bytes; else channels[i]->location = bytes - bytes % channels[i]->bytes + channels[i]->bytes; bytes = channels[i]->location + channels[i]->bytes; i++; } return bytes; } #define GUINT8_FROM_BE(x) (x) #define GUINT8_FROM_LE(x) (x) #define PROCESS_CHANNEL_BITS(bits) \ { \ guint##bits input; \ \ input = *(guint##bits *)(data + info->location); \ input = info->be ? GUINT##bits##_FROM_BE (input) : GUINT##bits##_FROM_LE (input);\ input >>= info->shift; \ input &= info->mask; \ if (info->is_signed) { \ gint##bits val = (gint##bits)(input << (bits - info->bits_used)) >> (bits - info->bits_used); \ val += info->offset; \ *ch_val = val; \ } else { \ *ch_val = input + info->offset; \ } \ } /** * process_scan_1() - get an integer value for a particular channel * @data: pointer to the start of the scan * @buffer_data: Buffer information * ch_name: name of channel to get * ch_val: value for the channel * ch_scale: scale for the channel * ch_present: whether the channel is present **/ void process_scan_1 (char *data, BufferDrvData *buffer_data, const char *ch_name, int *ch_val, gdouble *ch_scale, gboolean *ch_present) { int k; *ch_present = FALSE; for (k = 0; k < buffer_data->channels_count; k++) { struct iio_channel_info *info = buffer_data->channels[k]; if (strcmp (info->name, ch_name) != 0) continue; g_debug ("process_scan_1: channel_index: %d, chan_name: %s, channel_data_index: %d location: %d bytes: %d is_signed: %d be: %d shift: %d bits_used: %d", k, info->name, info->index, info->location, info->bytes, info->is_signed, info->be, info->shift, info->bits_used); switch (info->bytes) { case 1: #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wduplicated-branches" PROCESS_CHANNEL_BITS(8); #pragma GCC diagnostic pop break; case 2: PROCESS_CHANNEL_BITS(16); break; case 4: PROCESS_CHANNEL_BITS(32); break; case 8: PROCESS_CHANNEL_BITS(64); break; default: g_error ("Process %d bytes channels not supported", info->bytes); break; } *ch_scale = info->scale; *ch_present = TRUE; break; } if (!*ch_present) g_warning ("IIO channel '%s' could not be found", ch_name); } /** * iio_round_sampling_frequency: Round sampling_frequency to a supported value * @dev: the IIO device to round the sampling frequency for * @name: name of the *sampling_frequency attribute for which to round value * @desired: desired value * * Check "_available" and if that attribute exists round the desired * value to a supported value, rounding up where possible. */ static double iio_round_sampling_frequency (GUdevDevice *dev, const char *name, double desired) { g_autofree char *available_attr = NULL; const char *available_str; char *endptr; double closest_higher = DBL_MAX; double closest_lower = 0.0; double available; available_attr = g_strconcat (name, "_available", NULL); available_str = g_udev_device_get_sysfs_attr (dev, available_attr); if (!available_str) return desired; while (*available_str) { available = g_ascii_strtod (available_str, &endptr); if (available_str == endptr || (*endptr && !isspace (*endptr))) break; available_str = endptr; /* 0 disables sampling, skip */ if (available == 0.0) continue; if (available >= desired) { if (available < closest_higher) closest_higher = available; } else { if (available > closest_lower) closest_lower = available; } } /* Prefer higher values */ if (closest_higher != DBL_MAX) return closest_higher; if (closest_lower != 0.0) return closest_lower; return desired; } /** * iio_fixup_sampling_frequency: Fixup devices *sampling_frequency attributes * @dev: the IIO device to fix the sampling frequencies for * * Make sure devices with *sampling_frequency attributes are sampling at * 10Hz or more. This fixes 2 problems: * 1) Some buffered devices default their sampling_frequency to 0Hz and then * never produce any readings. * 2) Some polled devices default to 1Hz and wait for a fresh sample before * returning from sysfs *_raw reads, blocking all of iio-sensor-proxy for * multiple seconds **/ gboolean iio_fixup_sampling_frequency (GUdevDevice *dev) { GDir *dir; const char *device_dir; const char *name; g_autoptr(GError) error = NULL; char sample_freq_str[G_ASCII_DTOSTR_BUF_SIZE]; double sample_freq; device_dir = g_udev_device_get_sysfs_path (dev); dir = g_dir_open (g_udev_device_get_sysfs_path (dev), 0, &error); if (!dir) { g_warning ("Failed to open directory '%s': %s", device_dir, error->message); return FALSE; } while ((name = g_dir_read_name (dir))) { if (g_str_has_suffix (name, "sampling_frequency") == FALSE) continue; sample_freq = g_udev_device_get_sysfs_attr_as_double (dev, name); if (sample_freq >= IIO_MIN_SAMPLING_FREQUENCY) continue; /* Continue with pre-set sample freq. */ /* Sample freq too low, set it to a supported freq close to 10Hz */ sample_freq = iio_round_sampling_frequency (dev, name, IIO_MIN_SAMPLING_FREQUENCY); /* * Some sample_freq IIO sysfs attr only accept integers, most will * accept floats but not all. g_ascii_dtostr() uses the shortest * possible representation, omitting the "." for whole numbers. */ g_ascii_dtostr (sample_freq_str, G_ASCII_DTOSTR_BUF_SIZE, sample_freq); if (write_sysfs_string (name, device_dir, sample_freq_str) < 0) g_warning ("Could not fix sample-freq for %s/%s", device_dir, name); } g_dir_close (dir); return TRUE; } /** * enable_sensors: enable all the sensors in a device * @device_dir: the IIO device directory in sysfs * @ **/ static gboolean enable_sensors (GUdevDevice *dev, int enable) { GDir *dir; char *device_dir; const char *name; gboolean ret = FALSE; g_autoptr(GError) error = NULL; device_dir = g_build_filename (g_udev_device_get_sysfs_path (dev), "scan_elements", NULL); dir = g_dir_open (device_dir, 0, &error); if (!dir) { g_warning ("Failed to open directory '%s': %s", device_dir, error->message); g_free (device_dir); return FALSE; } while ((name = g_dir_read_name (dir))) { char *path; if (g_str_has_suffix (name, "_en") == FALSE) continue; /* Already enabled? */ path = g_strdup_printf ("scan_elements/%s", name); if (g_udev_device_get_sysfs_attr_as_boolean (dev, path)) { g_debug ("Already enabled sensor %s/%s", device_dir, name); ret = TRUE; g_free (path); continue; } g_free (path); /* Enable */ if (write_sysfs_int (name, device_dir, enable) < 0) { g_warning ("Could not enable sensor %s/%s", device_dir, name); continue; } ret = TRUE; g_debug ("Enabled sensor %s/%s", device_dir, name); } g_dir_close (dir); g_free (device_dir); if (!ret) { g_warning ("Failed to enable any sensors for device '%s'", g_udev_device_get_sysfs_path (dev)); } return ret; } static gboolean enable_ring_buffer (BufferDrvData *data) { int ret; /* Setup ring buffer parameters */ ret = write_sysfs_int("buffer/length", data->dev_dir_name, 128); if (ret < 0) { g_warning ("Failed to set ring buffer length for %s", data->dev_dir_name); return FALSE; } /* Enable the buffer */ ret = write_sysfs_int_and_verify("buffer/enable", data->dev_dir_name, 1); if (ret < 0) { g_warning ("Unable to enable ring buffer for %s", data->dev_dir_name); return FALSE; } return TRUE; } static void disable_ring_buffer (BufferDrvData *data) { /* Stop the buffer */ write_sysfs_int ("buffer/enable", data->dev_dir_name, 0); /* Disconnect the trigger - just write a dummy name. */ write_sysfs_string ("trigger/current_trigger", data->dev_dir_name, "NULL"); } static gboolean enable_trigger (BufferDrvData *data) { int ret; /* Set the device trigger to be the data ready trigger */ ret = write_sysfs_string_and_verify("trigger/current_trigger", data->dev_dir_name, data->trigger_name); if (ret < 0) { g_warning ("Failed to write current_trigger file %s", g_strerror(-ret)); return FALSE; } return TRUE; } static gboolean build_channels (BufferDrvData *data) { /* Parse the files in scan_elements to identify what channels are present */ data->channels = build_channel_array (data->dev_dir_name, &(data->channels_count)); if (data->channels == NULL) { g_warning ("Problem reading scan element information: %s", data->dev_dir_name); return FALSE; } data->scan_size = size_from_channelarray (data->channels, data->channels_count); return TRUE; } void buffer_drv_data_free (BufferDrvData *buffer_data) { int i; if (buffer_data == NULL) return; enable_sensors (buffer_data->device, 0); g_clear_object (&buffer_data->device); disable_ring_buffer (buffer_data); g_free (buffer_data->trigger_name); for (i = 0; i < buffer_data->channels_count; i++) channel_info_free (buffer_data->channels[i]); g_free (buffer_data->channels); g_free (buffer_data); } BufferDrvData * buffer_drv_data_new (GUdevDevice *device, const char *trigger_name) { BufferDrvData *buffer_data; buffer_data = g_new0 (BufferDrvData, 1); buffer_data->dev_dir_name = g_udev_device_get_sysfs_path (device); buffer_data->trigger_name = g_strdup (trigger_name); buffer_data->device = g_object_ref (device); if (!iio_fixup_sampling_frequency (device) || !enable_sensors (device, 1) || !enable_trigger (buffer_data) || !enable_ring_buffer (buffer_data) || !build_channels (buffer_data)) { buffer_drv_data_free (buffer_data); return NULL; } return buffer_data; } IIOSensorData * iio_sensor_data_new (gsize size) { IIOSensorData *data; g_return_val_if_fail (size != 0, NULL); data = g_new0 (IIOSensorData, 1); data->data = g_malloc0 (size); return data; } void iio_sensor_data_free (IIOSensorData *data) { if (data == NULL) return; g_free (data->data); g_free (data); } iio-sensor-proxy-3.5/src/iio-buffer-utils.h000066400000000000000000000030151445770364600207730ustar00rootroot00000000000000/* * Modified from industrialio buffer test code, and Lenovo Yoga (2 Pro) orientation helper * Copyright (c) 2008 Jonathan Cameron * Copyright (c) 2014 Peter F. Patel-Schneider * Copyright (c) 2014 Bastien Nocera * Copyright (c) 2015 Elad Alfassa * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. */ #include #include typedef struct iio_channel_info iio_channel_info; typedef struct { GUdevDevice *device; char *trigger_name; const char *dev_dir_name; int channels_count; iio_channel_info **channels; int scan_size; } BufferDrvData; typedef struct { ssize_t read_size; char *data; } IIOSensorData; IIOSensorData *iio_sensor_data_new (gsize size); void iio_sensor_data_free (IIOSensorData *data); G_DEFINE_AUTOPTR_CLEANUP_FUNC(IIOSensorData, iio_sensor_data_free) void process_scan_1 (char *data, BufferDrvData *buffer_data, const char *ch_name, int *ch_val, gdouble *ch_scale, gboolean *ch_present); gboolean iio_fixup_sampling_frequency (GUdevDevice *dev); void buffer_drv_data_free (BufferDrvData *buffer_data); BufferDrvData *buffer_drv_data_new (GUdevDevice *device, const char *trigger_name); iio-sensor-proxy-3.5/src/iio-sensor-proxy.c000066400000000000000000000676361445770364600210720ustar00rootroot00000000000000/* * Copyright (c) 2014-2016 Bastien Nocera * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. * */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "drivers.h" #include "orientation.h" #include "iio-sensor-proxy-resources.h" #define SENSOR_PROXY_DBUS_NAME "net.hadess.SensorProxy" #define SENSOR_PROXY_DBUS_PATH "/net/hadess/SensorProxy" #define SENSOR_PROXY_COMPASS_DBUS_PATH "/net/hadess/SensorProxy/Compass" #define SENSOR_PROXY_IFACE_NAME SENSOR_PROXY_DBUS_NAME #define SENSOR_PROXY_COMPASS_IFACE_NAME SENSOR_PROXY_DBUS_NAME ".Compass" #define NUM_SENSOR_TYPES DRIVER_TYPE_PROXIMITY + 1 typedef struct { GMainLoop *loop; GUdevClient *client; GDBusNodeInfo *introspection_data; GDBusConnection *connection; guint name_id; int ret; PolkitAuthority *auth; SensorDriver *drivers[NUM_SENSOR_TYPES]; SensorDevice *devices[NUM_SENSOR_TYPES]; GUdevDevice *udev_devices[NUM_SENSOR_TYPES]; GHashTable *clients[NUM_SENSOR_TYPES]; /* key = D-Bus name, value = watch ID */ /* Accelerometer */ OrientationUp previous_orientation; /* Light */ gdouble previous_level; gboolean uses_lux; /* Compass */ gdouble previous_heading; /* Proximity */ gboolean previous_prox_near; } SensorData; static const SensorDriver * const drivers[] = { &iio_buffer_accel, &iio_poll_accel, &input_accel, &iio_buffer_light, &iio_poll_light, &hwmon_light, &fake_compass, &fake_light, &iio_buffer_compass, &iio_poll_proximity, }; static ReadingsUpdateFunc driver_type_to_callback_func (DriverType type); static const char * driver_type_to_str (DriverType type) { switch (type) { case DRIVER_TYPE_ACCEL: return "accelerometer"; case DRIVER_TYPE_LIGHT: return "ambient light sensor"; case DRIVER_TYPE_COMPASS: return "compass"; case DRIVER_TYPE_PROXIMITY: return "proximity"; default: g_assert_not_reached (); } } #define DRIVER_FOR_TYPE(driver_type) data->drivers[driver_type] #define DEVICE_FOR_TYPE(driver_type) data->devices[driver_type] #define UDEV_DEVICE_FOR_TYPE(driver_type) data->udev_devices[driver_type] static void sensor_changes (GUdevClient *client, gchar *action, GUdevDevice *device, SensorData *data); static gboolean driver_type_exists (SensorData *data, DriverType driver_type) { return (DRIVER_FOR_TYPE(driver_type) != NULL); } static gboolean find_sensors (GUdevClient *client, SensorData *data) { GList *devices, *input, *platform, *l; gboolean found = FALSE; devices = g_udev_client_query_by_subsystem (client, "iio"); input = g_udev_client_query_by_subsystem (client, "input"); platform = g_udev_client_query_by_subsystem (client, "platform"); devices = g_list_concat (devices, input); devices = g_list_concat (devices, platform); /* Find the devices */ for (l = devices; l != NULL; l = l->next) { GUdevDevice *dev = l->data; guint i; for (i = 0; i < G_N_ELEMENTS(drivers); i++) { SensorDriver *driver = (SensorDriver *) drivers[i]; if (!driver_type_exists (data, driver->type) && driver_discover (driver, dev)) { g_debug ("Found device %s of type %s at %s", g_udev_device_get_sysfs_path (dev), driver_type_to_str (driver->type), driver->driver_name); UDEV_DEVICE_FOR_TYPE(driver->type) = g_object_ref (dev); DRIVER_FOR_TYPE(driver->type) = (SensorDriver *) driver; found = TRUE; } } if (driver_type_exists (data, DRIVER_TYPE_ACCEL) && driver_type_exists (data, DRIVER_TYPE_LIGHT) && driver_type_exists (data, DRIVER_TYPE_PROXIMITY) && driver_type_exists (data, DRIVER_TYPE_COMPASS)) break; } g_list_free_full (devices, g_object_unref); return found; } static void free_client_watch (gpointer data) { guint watch_id = GPOINTER_TO_UINT (data); if (watch_id == 0) return; g_bus_unwatch_name (watch_id); } static GHashTable * create_clients_hash_table (void) { return g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_client_watch); } typedef enum { PROP_HAS_ACCELEROMETER = 1 << 0, PROP_ACCELEROMETER_ORIENTATION = 1 << 1, PROP_HAS_AMBIENT_LIGHT = 1 << 2, PROP_LIGHT_LEVEL = 1 << 3, PROP_HAS_COMPASS = 1 << 4, PROP_COMPASS_HEADING = 1 << 5, PROP_HAS_PROXIMITY = 1 << 6, PROP_PROXIMITY_NEAR = 1 << 7, } PropertiesMask; #define PROP_ALL (PROP_HAS_ACCELEROMETER | \ PROP_ACCELEROMETER_ORIENTATION | \ PROP_HAS_AMBIENT_LIGHT | \ PROP_LIGHT_LEVEL | \ PROP_HAS_PROXIMITY | \ PROP_PROXIMITY_NEAR) #define PROP_ALL_COMPASS (PROP_HAS_COMPASS | \ PROP_COMPASS_HEADING) static PropertiesMask mask_for_sensor_type (DriverType sensor_type) { switch (sensor_type) { case DRIVER_TYPE_ACCEL: return PROP_HAS_ACCELEROMETER | PROP_ACCELEROMETER_ORIENTATION; case DRIVER_TYPE_LIGHT: return PROP_HAS_AMBIENT_LIGHT | PROP_LIGHT_LEVEL; case DRIVER_TYPE_COMPASS: return PROP_HAS_COMPASS | PROP_COMPASS_HEADING; case DRIVER_TYPE_PROXIMITY: return PROP_HAS_PROXIMITY | PROP_PROXIMITY_NEAR; default: g_assert_not_reached (); } } static void send_dbus_event_for_client (SensorData *data, const char *destination_bus_name, PropertiesMask mask) { GVariantBuilder props_builder; GVariant *props_changed = NULL; g_return_if_fail (destination_bus_name != NULL); g_variant_builder_init (&props_builder, G_VARIANT_TYPE ("a{sv}")); if (mask & PROP_HAS_ACCELEROMETER) { gboolean has_accel; has_accel = driver_type_exists (data, DRIVER_TYPE_ACCEL); g_variant_builder_add (&props_builder, "{sv}", "HasAccelerometer", g_variant_new_boolean (has_accel)); /* Send the orientation when the device appears */ if (has_accel) mask |= PROP_ACCELEROMETER_ORIENTATION; else data->previous_orientation = ORIENTATION_UNDEFINED; } if (mask & PROP_ACCELEROMETER_ORIENTATION) { g_variant_builder_add (&props_builder, "{sv}", "AccelerometerOrientation", g_variant_new_string (orientation_to_string (data->previous_orientation))); } if (mask & PROP_HAS_AMBIENT_LIGHT) { gboolean has_als; has_als = driver_type_exists (data, DRIVER_TYPE_LIGHT); g_variant_builder_add (&props_builder, "{sv}", "HasAmbientLight", g_variant_new_boolean (has_als)); /* Send the light level when the device appears */ if (has_als) mask |= PROP_LIGHT_LEVEL; } if (mask & PROP_LIGHT_LEVEL) { g_variant_builder_add (&props_builder, "{sv}", "LightLevelUnit", g_variant_new_string (data->uses_lux ? "lux" : "vendor")); g_variant_builder_add (&props_builder, "{sv}", "LightLevel", g_variant_new_double (data->previous_level)); } if (mask & PROP_HAS_COMPASS) { gboolean has_compass; has_compass = driver_type_exists (data, DRIVER_TYPE_COMPASS); g_variant_builder_add (&props_builder, "{sv}", "HasCompass", g_variant_new_boolean (has_compass)); /* Send the heading when the device appears */ if (has_compass) mask |= PROP_COMPASS_HEADING; } if (mask & PROP_COMPASS_HEADING) { g_variant_builder_add (&props_builder, "{sv}", "CompassHeading", g_variant_new_double (data->previous_heading)); } if (mask & PROP_HAS_PROXIMITY) { gboolean has_proximity; has_proximity = driver_type_exists (data, DRIVER_TYPE_PROXIMITY); g_variant_builder_add (&props_builder, "{sv}", "HasProximity", g_variant_new_boolean (has_proximity)); /* Send proximity information when the device appears */ if (has_proximity) mask |= PROP_PROXIMITY_NEAR; } if (mask & PROP_PROXIMITY_NEAR) { g_variant_builder_add (&props_builder, "{sv}", "ProximityNear", g_variant_new_boolean (data->previous_prox_near)); } props_changed = g_variant_new ("(s@a{sv}@as)", (mask & PROP_ALL) ? SENSOR_PROXY_IFACE_NAME : SENSOR_PROXY_COMPASS_IFACE_NAME, g_variant_builder_end (&props_builder), g_variant_new_strv (NULL, 0)); g_dbus_connection_emit_signal (data->connection, destination_bus_name, (mask & PROP_ALL) ? SENSOR_PROXY_DBUS_PATH : SENSOR_PROXY_COMPASS_DBUS_PATH, "org.freedesktop.DBus.Properties", "PropertiesChanged", props_changed, NULL); } static void send_dbus_event (SensorData *data, PropertiesMask mask) { GHashTable *ht; guint i; GHashTableIter iter; gpointer key, value; g_assert (mask != 0); g_assert (data->connection); g_assert ((mask & PROP_ALL) == 0 || (mask & PROP_ALL_COMPASS) == 0); /* Make a list of the events each client for each sensor * is interested in */ ht = g_hash_table_new (g_str_hash, g_str_equal); for (i = 0; i < NUM_SENSOR_TYPES; i++) { GList *clients, *l; clients = g_hash_table_get_keys (data->clients[i]); for (l = clients; l != NULL; l = l->next) { PropertiesMask m, new_mask; /* Already have a mask? */ m = GPOINTER_TO_UINT (g_hash_table_lookup (ht, l->data)); new_mask = mask & mask_for_sensor_type (i); m |= new_mask; g_hash_table_insert (ht, l->data, GUINT_TO_POINTER (m)); } } g_hash_table_iter_init (&iter, ht); while (g_hash_table_iter_next (&iter, &key, &value)) send_dbus_event_for_client (data, (const char *) key, GPOINTER_TO_UINT (value)); g_hash_table_destroy (ht); } static void send_driver_changed_dbus_event (SensorData *data, DriverType driver_type) { if (driver_type == DRIVER_TYPE_ACCEL) send_dbus_event (data, PROP_HAS_ACCELEROMETER); else if (driver_type == DRIVER_TYPE_LIGHT) send_dbus_event (data, PROP_HAS_AMBIENT_LIGHT); else if (driver_type == DRIVER_TYPE_PROXIMITY) send_dbus_event (data, PROP_HAS_PROXIMITY); else if (driver_type == DRIVER_TYPE_COMPASS) send_dbus_event (data, PROP_HAS_COMPASS); else g_assert_not_reached (); } static gboolean any_sensors_left (SensorData *data) { guint i; gboolean exists = FALSE; for (i = 0; i < NUM_SENSOR_TYPES; i++) { if (driver_type_exists (data, i)) { exists = TRUE; break; } } return exists; } static void client_release (SensorData *data, const char *sender, DriverType driver_type) { GHashTable *ht; guint watch_id; ht = data->clients[driver_type]; watch_id = GPOINTER_TO_UINT (g_hash_table_lookup (ht, sender)); if (watch_id == 0) { g_debug ("Sender '%s' already released device, no-op", sender); return; } g_hash_table_remove (ht, sender); if (driver_type_exists (data, driver_type) && g_hash_table_size (ht) == 0) { SensorDevice *sensor_device = DEVICE_FOR_TYPE(driver_type); driver_set_polling (sensor_device, FALSE); } } static void client_vanished_cb (GDBusConnection *connection, const gchar *name, gpointer user_data) { SensorData *data = user_data; guint i; char *sender; if (name == NULL) return; sender = g_strdup (name); for (i = 0; i < NUM_SENSOR_TYPES; i++) { GHashTable *ht; guint watch_id; ht = data->clients[i]; g_assert (ht); watch_id = GPOINTER_TO_UINT (g_hash_table_lookup (ht, sender)); if (watch_id > 0) client_release (data, sender, i); } g_free (sender); } static gboolean check_claim_permission (SensorData *data, const char *sender, GError **error) { g_autoptr(GError) local_error = NULL; g_autoptr(PolkitAuthorizationResult) result = NULL; g_autoptr(PolkitSubject) subject = NULL; subject = polkit_system_bus_name_new (sender); result = polkit_authority_check_authorization_sync (data->auth, subject, "net.hadess.SensorProxy.claim-sensor", NULL, POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE, NULL, &local_error); if (result == NULL || !polkit_authorization_result_get_is_authorized (result)) { g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "Not Authorized: %s", local_error ? local_error->message : "Sensor claim not allowed"); return FALSE; } return TRUE; } static void handle_generic_method_call (SensorData *data, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, DriverType driver_type) { GHashTable *ht; guint watch_id; g_debug ("Handling driver refcounting method '%s' for %s device", method_name, driver_type_to_str (driver_type)); ht = data->clients[driver_type]; if (g_str_has_prefix (method_name, "Claim")) { watch_id = GPOINTER_TO_UINT (g_hash_table_lookup (ht, sender)); if (watch_id > 0) { g_debug ("Sender '%s' already claimed device, no-op", sender); g_dbus_method_invocation_return_value (invocation, NULL); return; } /* No other clients for this sensor? Start it */ if (driver_type_exists (data, driver_type) && g_hash_table_size (ht) == 0) { SensorDevice *sensor_device = DEVICE_FOR_TYPE(driver_type); driver_set_polling (sensor_device, TRUE); } watch_id = g_bus_watch_name_on_connection (data->connection, sender, G_BUS_NAME_WATCHER_FLAGS_NONE, NULL, client_vanished_cb, data, NULL); g_hash_table_insert (ht, g_strdup (sender), GUINT_TO_POINTER (watch_id)); g_dbus_method_invocation_return_value (invocation, NULL); } else if (g_str_has_prefix (method_name, "Release")) { client_release (data, sender, driver_type); g_dbus_method_invocation_return_value (invocation, NULL); } } static void handle_method_call (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { SensorData *data = user_data; DriverType driver_type; g_autoptr(GError) error = NULL; if (g_strcmp0 (method_name, "ClaimAccelerometer") == 0 || g_strcmp0 (method_name, "ReleaseAccelerometer") == 0) driver_type = DRIVER_TYPE_ACCEL; else if (g_strcmp0 (method_name, "ClaimLight") == 0 || g_strcmp0 (method_name, "ReleaseLight") == 0) driver_type = DRIVER_TYPE_LIGHT; else if (g_strcmp0 (method_name, "ClaimProximity") == 0 || g_strcmp0 (method_name, "ReleaseProximity") == 0) driver_type = DRIVER_TYPE_PROXIMITY; else { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "Method '%s' does not exist on object %s", method_name, object_path); return; } if (!check_claim_permission (data, sender, &error)) { g_dbus_method_invocation_return_gerror (invocation, error); return; } handle_generic_method_call (data, sender, object_path, interface_name, method_name, parameters, invocation, driver_type); } static GVariant * handle_get_property (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GError **error, gpointer user_data) { SensorData *data = user_data; g_assert (data->connection); if (g_strcmp0 (property_name, "HasAccelerometer") == 0) return g_variant_new_boolean (driver_type_exists (data, DRIVER_TYPE_ACCEL)); if (g_strcmp0 (property_name, "AccelerometerOrientation") == 0) return g_variant_new_string (orientation_to_string (data->previous_orientation)); if (g_strcmp0 (property_name, "HasAmbientLight") == 0) return g_variant_new_boolean (driver_type_exists (data, DRIVER_TYPE_LIGHT)); if (g_strcmp0 (property_name, "LightLevelUnit") == 0) return g_variant_new_string (data->uses_lux ? "lux" : "vendor"); if (g_strcmp0 (property_name, "LightLevel") == 0) return g_variant_new_double (data->previous_level); if (g_strcmp0 (property_name, "HasProximity") == 0) return g_variant_new_boolean (driver_type_exists (data, DRIVER_TYPE_PROXIMITY)); if (g_strcmp0 (property_name, "ProximityNear") == 0) return g_variant_new_boolean (data->previous_prox_near); return NULL; } static const GDBusInterfaceVTable interface_vtable = { handle_method_call, handle_get_property, NULL }; static void handle_compass_method_call (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { SensorData *data = user_data; if (g_strcmp0 (method_name, "ClaimCompass") != 0 && g_strcmp0 (method_name, "ReleaseCompass") != 0) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "Method '%s' does not exist on object %s", method_name, object_path); return; } handle_generic_method_call (data, sender, object_path, interface_name, method_name, parameters, invocation, DRIVER_TYPE_COMPASS); } static GVariant * handle_compass_get_property (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GError **error, gpointer user_data) { SensorData *data = user_data; g_assert (data->connection); if (g_strcmp0 (property_name, "HasCompass") == 0) return g_variant_new_boolean (data->drivers[DRIVER_TYPE_COMPASS] != NULL); if (g_strcmp0 (property_name, "CompassHeading") == 0) return g_variant_new_double (data->previous_heading); return NULL; } static const GDBusInterfaceVTable compass_interface_vtable = { handle_compass_method_call, handle_compass_get_property, NULL }; static void name_lost_handler (GDBusConnection *connection, const gchar *name, gpointer user_data) { g_debug ("iio-sensor-proxy is already running, or it cannot own its D-Bus name. Verify installation."); exit (0); } static void bus_acquired_handler (GDBusConnection *connection, const gchar *name, gpointer user_data) { SensorData *data = user_data; g_dbus_connection_register_object (connection, SENSOR_PROXY_DBUS_PATH, data->introspection_data->interfaces[0], &interface_vtable, data, NULL, NULL); g_dbus_connection_register_object (connection, SENSOR_PROXY_COMPASS_DBUS_PATH, data->introspection_data->interfaces[1], &compass_interface_vtable, data, NULL, NULL); data->connection = g_object_ref (connection); } static void name_acquired_handler (GDBusConnection *connection, const gchar *name, gpointer user_data) { SensorData *data = user_data; const gchar * const subsystems[] = { "iio", "input", "platform", NULL }; guint i; data->client = g_udev_client_new (subsystems); if (!find_sensors (data->client, data)) goto bail; g_signal_connect (G_OBJECT (data->client), "uevent", G_CALLBACK (sensor_changes), data); for (i = 0; i < NUM_SENSOR_TYPES; i++) { SensorDevice *sensor_device; data->clients[i] = create_clients_hash_table (); if (!driver_type_exists (data, i)) continue; sensor_device = driver_open (DRIVER_FOR_TYPE(i), UDEV_DEVICE_FOR_TYPE(i), driver_type_to_callback_func (data->drivers[i]->type), data); if (!sensor_device) { DRIVER_FOR_TYPE(i) = NULL; g_clear_object (&UDEV_DEVICE_FOR_TYPE(i)); continue; } DEVICE_FOR_TYPE(i) = sensor_device; } if (!any_sensors_left (data)) goto bail; send_dbus_event (data, PROP_ALL); send_dbus_event (data, PROP_ALL_COMPASS); return; bail: data->ret = 0; g_debug ("No sensors or missing kernel drivers for the sensors"); g_main_loop_quit (data->loop); } static gboolean setup_dbus (SensorData *data, gboolean replace) { GBytes *bytes; GBusNameOwnerFlags flags; bytes = g_resources_lookup_data ("/net/hadess/SensorProxy/net.hadess.SensorProxy.xml", G_RESOURCE_LOOKUP_FLAGS_NONE, NULL); data->introspection_data = g_dbus_node_info_new_for_xml (g_bytes_get_data (bytes, NULL), NULL); g_bytes_unref (bytes); g_assert (data->introspection_data != NULL); flags = G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT; if (replace) flags |= G_BUS_NAME_OWNER_FLAGS_REPLACE; data->name_id = g_bus_own_name (G_BUS_TYPE_SYSTEM, SENSOR_PROXY_DBUS_NAME, flags, bus_acquired_handler, name_acquired_handler, name_lost_handler, data, NULL); return TRUE; } static void accel_changed_func (SensorDevice *sensor_device, gpointer readings_data, gpointer user_data) { SensorData *data = user_data; AccelReadings *readings = (AccelReadings *) readings_data; OrientationUp orientation; //FIXME handle errors g_debug ("Accel sent by driver (quirk applied): %d, %d, %d (scale: %lf,%lf,%lf)", readings->accel_x, readings->accel_y, readings->accel_z, readings->scale.x, readings->scale.y, readings->scale.z); orientation = orientation_calc (data->previous_orientation, readings->accel_x, readings->accel_y, readings->accel_z, readings->scale); if (data->previous_orientation != orientation) { OrientationUp tmp; tmp = data->previous_orientation; data->previous_orientation = orientation; send_dbus_event (data, PROP_ACCELEROMETER_ORIENTATION); g_debug ("Emitted orientation changed: from %s to %s", orientation_to_string (tmp), orientation_to_string (data->previous_orientation)); } } static void light_changed_func (SensorDevice *sensor_device, gpointer readings_data, gpointer user_data) { SensorData *data = user_data; LightReadings *readings = (LightReadings *) readings_data; //FIXME handle errors g_debug ("Light level sent by driver (quirk applied): %lf (unit: %s)", readings->level, data->uses_lux ? "lux" : "vendor"); if (data->previous_level != readings->level || data->uses_lux != readings->uses_lux) { gdouble tmp; tmp = data->previous_level; data->previous_level = readings->level; data->uses_lux = readings->uses_lux; send_dbus_event (data, PROP_LIGHT_LEVEL); g_debug ("Emitted light changed: from %lf to %lf", tmp, data->previous_level); } } static void compass_changed_func (SensorDevice *sensor_device, gpointer readings_data, gpointer user_data) { SensorData *data = user_data; CompassReadings *readings = (CompassReadings *) readings_data; //FIXME handle errors g_debug ("Heading sent by driver (quirk applied): %lf degrees", readings->heading); if (data->previous_heading != readings->heading) { gdouble tmp; tmp = data->previous_heading; data->previous_heading = readings->heading; send_dbus_event (data, PROP_COMPASS_HEADING); g_debug ("Emitted heading changed: from %lf to %lf", tmp, data->previous_heading); } } static void proximity_changed_func (SensorDevice *sensor_device, gpointer readings_data, gpointer user_data) { SensorData *data = user_data; ProximityReadings *readings = (ProximityReadings *) readings_data; gboolean near; //FIXME handle errors g_debug ("Proximity sent by driver: %d", readings->is_near); near = readings->is_near > 0; if (data->previous_prox_near != near) { ProximityNear tmp; tmp = data->previous_prox_near; data->previous_prox_near = near; send_dbus_event (data, PROP_PROXIMITY_NEAR); g_debug ("Emitted proximity changed: from %d to %d", tmp, near); } } static ReadingsUpdateFunc driver_type_to_callback_func (DriverType type) { switch (type) { case DRIVER_TYPE_ACCEL: return accel_changed_func; case DRIVER_TYPE_LIGHT: return light_changed_func; case DRIVER_TYPE_COMPASS: return compass_changed_func; case DRIVER_TYPE_PROXIMITY: return proximity_changed_func; default: g_assert_not_reached (); } } static void free_sensor_data (SensorData *data) { guint i; if (data == NULL) return; if (data->name_id != 0) { g_bus_unown_name (data->name_id); data->name_id = 0; } for (i = 0; i < NUM_SENSOR_TYPES; i++) { if (driver_type_exists (data, i)) driver_close (DEVICE_FOR_TYPE(i)); g_clear_object (&UDEV_DEVICE_FOR_TYPE(i)); g_clear_pointer (&data->clients[i], g_hash_table_unref); } g_clear_object (&data->auth); g_clear_pointer (&data->introspection_data, g_dbus_node_info_unref); g_clear_object (&data->connection); g_clear_object (&data->client); g_clear_pointer (&data->loop, g_main_loop_unref); g_free (data); } static void sensor_changes (GUdevClient *client, gchar *action, GUdevDevice *device, SensorData *data) { guint i; g_debug ("Sensor changes: action = %s, device = %s", action, g_udev_device_get_sysfs_path (device)); if (g_strcmp0 (action, "remove") == 0) { for (i = 0; i < NUM_SENSOR_TYPES; i++) { GUdevDevice *dev = UDEV_DEVICE_FOR_TYPE(i); if (!dev) continue; if (g_strcmp0 (g_udev_device_get_sysfs_path (device), g_udev_device_get_sysfs_path (dev)) == 0) { g_debug ("Sensor type %s got removed (%s)", driver_type_to_str (i), g_udev_device_get_sysfs_path (dev)); g_clear_object (&UDEV_DEVICE_FOR_TYPE(i)); driver_close (DEVICE_FOR_TYPE(i)); DEVICE_FOR_TYPE(i) = NULL; DRIVER_FOR_TYPE(i) = NULL; g_clear_pointer (&data->clients[i], g_hash_table_unref); data->clients[i] = create_clients_hash_table (); send_driver_changed_dbus_event (data, i); } } if (!any_sensors_left (data)) g_main_loop_quit (data->loop); } else if (g_strcmp0 (action, "add") == 0) { for (i = 0; i < G_N_ELEMENTS(drivers); i++) { SensorDriver *driver = (SensorDriver *) drivers[i]; if (!driver_type_exists (data, driver->type) && driver_discover (driver, device)) { SensorDevice *sensor_device = NULL; g_debug ("Found hotplugged device %s of type %s at %s", g_udev_device_get_sysfs_path (device), driver_type_to_str (driver->type), driver->driver_name); sensor_device = driver_open (driver, device, driver_type_to_callback_func (driver->type), data); if (sensor_device) { GHashTable *ht; UDEV_DEVICE_FOR_TYPE(driver->type) = g_object_ref (device); DEVICE_FOR_TYPE(driver->type) = sensor_device; DRIVER_FOR_TYPE(driver->type) = (SensorDriver *) driver; send_driver_changed_dbus_event (data, driver->type); ht = data->clients[driver->type]; if (g_hash_table_size (ht) > 0) { SensorDevice *sensor_device = DEVICE_FOR_TYPE(driver->type); driver_set_polling (sensor_device, TRUE); } } break; } } } } int main (int argc, char **argv) { SensorData *data; g_autoptr(GOptionContext) option_context = NULL; g_autoptr(GError) error = NULL; gboolean verbose = FALSE; gboolean replace = FALSE; const GOptionEntry options[] = { { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Show extra debugging information", NULL }, { "replace", 'r', 0, G_OPTION_ARG_NONE, &replace, "Replace the running instance of iio-sensor-proxy", NULL }, { NULL} }; int ret = 0; setlocale (LC_ALL, ""); option_context = g_option_context_new (""); g_option_context_add_main_entries (option_context, options, NULL); ret = g_option_context_parse (option_context, &argc, &argv, &error); if (!ret) { g_print ("Failed to parse arguments: %s\n", error->message); return EXIT_FAILURE; } if (verbose) { if (!g_setenv ("G_MESSAGES_DEBUG", "all", TRUE)) { g_warning ("Failed to enable debug"); return EXIT_FAILURE; } g_debug ("Starting iio-sensor-proxy version "VERSION); } data = g_new0 (SensorData, 1); data->previous_orientation = ORIENTATION_UNDEFINED; data->uses_lux = TRUE; /* Set up D-Bus */ setup_dbus (data, replace); data->auth = polkit_authority_get_sync (NULL, NULL); data->loop = g_main_loop_new (NULL, TRUE); g_main_loop_run (data->loop); ret = data->ret; free_sensor_data (data); return ret; } iio-sensor-proxy-3.5/src/iio-sensor-proxy.gresource.xml000066400000000000000000000003061445770364600234220ustar00rootroot00000000000000 net.hadess.SensorProxy.xml iio-sensor-proxy-3.5/src/meson.build000066400000000000000000000034661445770364600176110ustar00rootroot00000000000000config_h = configuration_data() config_h.set_quoted('VERSION', meson.project_version()) config_h_files = configure_file( output: 'config.h', configuration: config_h ) deps = [ gio_dep, gudev_dep, mathlib_dep, polkit_gobject_dep ] resources = gnome.compile_resources( 'iio-sensor-proxy-resources', 'iio-sensor-proxy.gresource.xml', c_name: 'iio_sensor_proxy', source_dir: '.', export: true ) sources = [ 'iio-sensor-proxy.c', 'drivers.c', 'orientation.c', 'drv-iio-buffer-accel.c', 'drv-iio-poll-accel.c', 'drv-input-accel.c', 'drv-fake-compass.c', 'drv-fake-light.c', 'drv-iio-poll-light.c', 'drv-hwmon-light.c', 'drv-iio-buffer-light.c', 'drv-iio-buffer-compass.c', 'drv-iio-poll-proximity.c', 'iio-buffer-utils.c', 'accel-mount-matrix.c', 'accel-scale.c', 'accel-attributes.c', 'utils.c', resources, config_h_files, ] executable('iio-sensor-proxy', sources, dependencies: deps, install: true, install_dir: libexecdir ) executable('fake-input-accelerometer', [ 'fake-input-accelerometer.c' ], dependencies: deps, install: false ) test_mount_matrix = executable('test-mount-matrix', [ 'test-mount-matrix.c', 'accel-mount-matrix.c' ], dependencies: deps, install: false ) test('test-mount-matrix', test_mount_matrix) test_orientation = executable('test-orientation', [ 'test-orientation.c', 'orientation.c', 'accel-mount-matrix.c', 'accel-scale.c' ], dependencies: deps, install: false ) test('test-orientation', test_orientation) if get_option('gtk-tests') executable('test-orientation-gtk', [ 'test-orientation-gtk.c', 'orientation.c', 'accel-scale.c' ], dependencies: [ deps, gtk_dep ], install: false ) endif executable('monitor-sensor', [ 'monitor-sensor.c' ], dependencies: gio_dep, install: true, install_dir: bindir ) iio-sensor-proxy-3.5/src/monitor-sensor.c000066400000000000000000000236551445770364600206130ustar00rootroot00000000000000/* * Copyright (c) 2015 Bastien Nocera * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. * */ #include #include static GMainLoop *loop; static guint watch_id; static GDBusProxy *iio_proxy, *iio_proxy_compass; static gboolean watch_accel = FALSE; static gboolean watch_prox = FALSE; static gboolean watch_compass = FALSE; static gboolean watch_light = FALSE; static void properties_changed (GDBusProxy *proxy, GVariant *changed_properties, GStrv invalidated_properties, gpointer user_data) { GVariant *v; GVariantDict dict; g_variant_dict_init (&dict, changed_properties); if (g_variant_dict_contains (&dict, "HasAccelerometer")) { v = g_dbus_proxy_get_cached_property (iio_proxy, "HasAccelerometer"); if (g_variant_get_boolean (v)) g_print ("+++ Accelerometer appeared\n"); else g_print ("--- Accelerometer disappeared\n"); g_variant_unref (v); } if (g_variant_dict_contains (&dict, "AccelerometerOrientation")) { v = g_dbus_proxy_get_cached_property (iio_proxy, "AccelerometerOrientation"); g_print (" Accelerometer orientation changed: %s\n", g_variant_get_string (v, NULL)); g_variant_unref (v); } if (g_variant_dict_contains (&dict, "HasAmbientLight")) { v = g_dbus_proxy_get_cached_property (iio_proxy, "HasAmbientLight"); if (g_variant_get_boolean (v)) g_print ("+++ Light sensor appeared\n"); else g_print ("--- Light sensor disappeared\n"); g_variant_unref (v); } if (g_variant_dict_contains (&dict, "LightLevel")) { GVariant *unit; v = g_dbus_proxy_get_cached_property (iio_proxy, "LightLevel"); unit = g_dbus_proxy_get_cached_property (iio_proxy, "LightLevelUnit"); g_print (" Light changed: %lf (%s)\n", g_variant_get_double (v), g_variant_get_string (unit, NULL)); g_variant_unref (v); g_variant_unref (unit); } if (g_variant_dict_contains (&dict, "HasProximity")) { v = g_dbus_proxy_get_cached_property (iio_proxy, "HasProximity"); if (g_variant_get_boolean (v)) g_print ("+++ Proximity sensor appeared\n"); else g_print ("--- Proximity sensor disappeared\n"); g_variant_unref (v); } if (g_variant_dict_contains (&dict, "ProximityNear")) { v = g_dbus_proxy_get_cached_property (iio_proxy, "ProximityNear"); g_print (" Proximity value changed: %d\n", g_variant_get_boolean (v)); g_variant_unref (v); } if (g_variant_dict_contains (&dict, "HasCompass")) { v = g_dbus_proxy_get_cached_property (iio_proxy_compass, "HasCompass"); if (g_variant_get_boolean (v)) g_print ("+++ Compass appeared\n"); else g_print ("--- Compass disappeared\n"); g_variant_unref (v); } if (g_variant_dict_contains (&dict, "CompassHeading")) { v = g_dbus_proxy_get_cached_property (iio_proxy_compass, "CompassHeading"); g_print (" Compass heading changed: %lf\n", g_variant_get_double (v)); g_variant_unref (v); } g_variant_dict_clear (&dict); } static void print_initial_values (void) { GVariant *v; if (watch_accel) { v = g_dbus_proxy_get_cached_property (iio_proxy, "HasAccelerometer"); if (g_variant_get_boolean (v)) { g_variant_unref (v); v = g_dbus_proxy_get_cached_property (iio_proxy, "AccelerometerOrientation"); g_print ("=== Has accelerometer (orientation: %s)\n", g_variant_get_string (v, NULL)); } else { g_print ("=== No accelerometer\n"); } g_variant_unref (v); } if (watch_light) { v = g_dbus_proxy_get_cached_property (iio_proxy, "HasAmbientLight"); if (g_variant_get_boolean (v)) { GVariant *unit; g_variant_unref (v); v = g_dbus_proxy_get_cached_property (iio_proxy, "LightLevel"); unit = g_dbus_proxy_get_cached_property (iio_proxy, "LightLevelUnit"); g_print ("=== Has ambient light sensor (value: %lf, unit: %s)\n", g_variant_get_double (v), g_variant_get_string (unit, NULL)); g_variant_unref (unit); } else { g_print ("=== No ambient light sensor\n"); } g_variant_unref (v); } if (watch_prox) { v = g_dbus_proxy_get_cached_property (iio_proxy, "HasProximity"); if (g_variant_get_boolean (v)) { g_variant_unref (v); v = g_dbus_proxy_get_cached_property (iio_proxy, "ProximityNear"); g_print ("=== Has proximity sensor (near: %d)\n", g_variant_get_boolean (v)); } else { g_print ("=== No proximity sensor\n"); } g_variant_unref (v); } if (!iio_proxy_compass) return; v = g_dbus_proxy_get_cached_property (iio_proxy_compass, "HasCompass"); if (g_variant_get_boolean (v)) { g_variant_unref (v); v = g_dbus_proxy_get_cached_property (iio_proxy, "CompassHeading"); if (v) { g_print ("=== Has compass (heading: %lf)\n", g_variant_get_double (v)); g_variant_unref (v); } else { g_print ("=== Has compass (heading: unset)\n"); } } else { g_print ("=== No compass\n"); } g_clear_pointer (&v, g_variant_unref); } static void appeared_cb (GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer user_data) { GError *error = NULL; GVariant *ret = NULL; g_print ("+++ iio-sensor-proxy appeared\n"); iio_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, "net.hadess.SensorProxy", "/net/hadess/SensorProxy", "net.hadess.SensorProxy", NULL, NULL); g_signal_connect (G_OBJECT (iio_proxy), "g-properties-changed", G_CALLBACK (properties_changed), NULL); if (watch_compass) { iio_proxy_compass = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, "net.hadess.SensorProxy", "/net/hadess/SensorProxy/Compass", "net.hadess.SensorProxy.Compass", NULL, NULL); g_signal_connect (G_OBJECT (iio_proxy_compass), "g-properties-changed", G_CALLBACK (properties_changed), NULL); } /* Accelerometer */ if (watch_accel) { ret = g_dbus_proxy_call_sync (iio_proxy, "ClaimAccelerometer", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (!ret) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning ("Failed to claim accelerometer: %s", error->message); g_main_loop_quit (loop); return; } g_clear_pointer (&ret, g_variant_unref); } /* ALS */ if (watch_light) { ret = g_dbus_proxy_call_sync (iio_proxy, "ClaimLight", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (!ret) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning ("Failed to claim light sensor: %s", error->message); g_main_loop_quit (loop); return; } g_clear_pointer (&ret, g_variant_unref); } /* Proximity sensor */ if (watch_prox) { ret = g_dbus_proxy_call_sync (iio_proxy, "ClaimProximity", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (!ret) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning ("Failed to claim proximity sensor: %s", error->message); g_main_loop_quit (loop); return; } g_clear_pointer (&ret, g_variant_unref); } /* Compass */ if (watch_compass) { ret = g_dbus_proxy_call_sync (iio_proxy_compass, "ClaimCompass", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (!ret) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning ("Failed to claim light sensor: %s", error->message); g_main_loop_quit (loop); return; } g_clear_pointer (&ret, g_variant_unref); } print_initial_values (); } static void vanished_cb (GDBusConnection *connection, const gchar *name, gpointer user_data) { if (iio_proxy || iio_proxy_compass) { g_clear_object (&iio_proxy); g_clear_object (&iio_proxy_compass); g_print ("--- iio-sensor-proxy vanished, waiting for it to appear\n"); } } int main (int argc, char **argv) { g_autoptr(GOptionContext) option_context = NULL; g_autoptr(GError) error = NULL; gboolean opt_watch_accel = FALSE; gboolean opt_watch_prox = FALSE; gboolean opt_watch_compass = FALSE; gboolean opt_watch_light = FALSE; gboolean opt_all = FALSE; const GOptionEntry options[] = { { "all", 'a', 0, G_OPTION_ARG_NONE, &opt_all, "Monitor all the sensor changes", NULL }, { "accel", 0, 0, G_OPTION_ARG_NONE, &opt_watch_accel, "Monitor accelerometer changes", NULL }, { "proximity", 0, 0, G_OPTION_ARG_NONE, &opt_watch_prox, "Monitor proximity sensor changes", NULL }, { "compass", 0, 0, G_OPTION_ARG_NONE, &opt_watch_compass, "Monitor compass changes", NULL }, { "light", 0, 0, G_OPTION_ARG_NONE, &opt_watch_light, "Monitor light changes changes", NULL }, { NULL} }; int ret = 0; setlocale (LC_ALL, ""); option_context = g_option_context_new (""); g_option_context_add_main_entries (option_context, options, NULL); ret = g_option_context_parse (option_context, &argc, &argv, &error); if (!ret) { g_print ("Failed to parse arguments: %s\n", error->message); return EXIT_FAILURE; } if (opt_watch_compass && g_strcmp0 (g_get_user_name (), "geoclue") != 0) { g_print ("Can't monitor compass as a user other than \"geoclue\"\n"); return EXIT_FAILURE; } if ((!opt_watch_accel && !opt_watch_prox && !opt_watch_compass && !opt_watch_light) || opt_all) { opt_watch_accel = opt_watch_prox = opt_watch_light = TRUE; opt_watch_compass = g_strcmp0 (g_get_user_name (), "geoclue") == 0; } watch_accel = opt_watch_accel; watch_prox = opt_watch_prox; watch_compass = opt_watch_compass; watch_light = opt_watch_light; watch_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM, "net.hadess.SensorProxy", G_BUS_NAME_WATCHER_FLAGS_NONE, appeared_cb, vanished_cb, NULL, NULL); g_print (" Waiting for iio-sensor-proxy to appear\n"); loop = g_main_loop_new (NULL, TRUE); g_main_loop_run (loop); return 0; } iio-sensor-proxy-3.5/src/net.hadess.SensorProxy.xml000066400000000000000000000174761445770364600225450ustar00rootroot00000000000000 iio-sensor-proxy-3.5/src/orientation.c000066400000000000000000000065231445770364600201430ustar00rootroot00000000000000/* * Copyright (c) 2011, 2014 Bastien Nocera * * orientation_calc() from the sensorfw package * Copyright (C) 2009-2010 Nokia Corporation * Authors: * Üstün Ergenoglu * Timo Rongas * Lihan Guo * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. * */ #include #include #include #include #include "orientation.h" static const char *orientations[] = { "undefined", "normal", "bottom-up", "left-up", "right-up", NULL }; const char * orientation_to_string (OrientationUp o) { return orientations[o]; } OrientationUp string_to_orientation (const char *orientation) { int i; if (orientation == NULL) return ORIENTATION_UNDEFINED; for (i = 0; orientations[i] != NULL; i++) { if (g_str_equal (orientation, orientations[i])) return i; } return ORIENTATION_UNDEFINED; } #define RADIANS_TO_DEGREES 180.0/M_PI #define SAME_AXIS_LIMIT 5 #define THRESHOLD_LANDSCAPE 35 #define THRESHOLD_PORTRAIT 35 /* First apply scale to get m/s², then * convert to 1G ~= 256 as the code expects */ #define SCALE(a) ((int) ((double) in_##a * scale.a * 256.0 / 9.81)) OrientationUp orientation_calc (OrientationUp prev, int in_x, int in_y, int in_z, AccelScale scale) { OrientationUp ret = prev; int x, y, z; int portrait_rotation; int landscape_rotation; /* this code expects 1G ~= 256 */ x = SCALE(x); y = SCALE(y); z = SCALE(z); portrait_rotation = round(atan2(x, sqrt(y * y + z * z)) * RADIANS_TO_DEGREES); landscape_rotation = round(atan2(y, sqrt(x * x + z * z)) * RADIANS_TO_DEGREES); /* Don't change orientation if we are on the common border of two thresholds */ if (abs(portrait_rotation) > THRESHOLD_PORTRAIT && abs(landscape_rotation) > THRESHOLD_LANDSCAPE) return prev; /* Portrait check */ if (abs(portrait_rotation) > THRESHOLD_PORTRAIT) { ret = (portrait_rotation > 0) ? ORIENTATION_LEFT_UP : ORIENTATION_RIGHT_UP; /* Some threshold to switching between portrait modes */ if (prev == ORIENTATION_LEFT_UP || prev == ORIENTATION_RIGHT_UP) { if (abs(portrait_rotation) < SAME_AXIS_LIMIT) { ret = prev; } } } else { /* Landscape check */ if (abs(landscape_rotation) > THRESHOLD_LANDSCAPE) { ret = (landscape_rotation > 0) ? ORIENTATION_BOTTOM_UP : ORIENTATION_NORMAL; /* Some threshold to switching between landscape modes */ if (prev == ORIENTATION_BOTTOM_UP || prev == ORIENTATION_NORMAL) { if (abs(landscape_rotation) < SAME_AXIS_LIMIT) { ret = prev; } } } } return ret; } iio-sensor-proxy-3.5/src/orientation.h000066400000000000000000000020401445770364600201360ustar00rootroot00000000000000/* * Copyright (c) 2011, 2014 Bastien Nocera * * orientation_calc() from the sensorfw package * Copyright (C) 2009-2010 Nokia Corporation * Authors: * Üstün Ergenoglu * Timo Rongas * Lihan Guo * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. * */ #include "accel-scale.h" typedef enum { ORIENTATION_UNDEFINED, ORIENTATION_NORMAL, ORIENTATION_BOTTOM_UP, ORIENTATION_LEFT_UP, ORIENTATION_RIGHT_UP } OrientationUp; #define ORIENTATION_UP_UP ORIENTATION_NORMAL const char *orientation_to_string (OrientationUp o); OrientationUp string_to_orientation (const char *orientation); OrientationUp orientation_calc (OrientationUp prev, int x, int y, int z, AccelScale scale); iio-sensor-proxy-3.5/src/test-accel-location.c000066400000000000000000000040651445770364600214410ustar00rootroot00000000000000/* * Copyright (c) 2019 Luís Ferreira * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. * */ #include #include "accel-attributes.h" #define VALID_DISPLAY_LOCATION "display" #define VALID_BASE_LOCATION "base" #define INVALID_LOCATION "invalid" #define VALID_DISPLAY_LOCATION_LABEL "accel-display" #define VALID_BASE_LOCATION_LABEL "accel-base" #define INVALID_LOCATION_LABEL "proximity-foo-bar" static void test_accel_label (void) { AccelLocation location; /* display location */ g_assert_true (parse_accel_label (VALID_DISPLAY_LOCATION_LABEL, &location)); g_assert_true (location == ACCEL_LOCATION_DISPLAY); /* base location */ g_assert_true (parse_accel_label (VALID_BASE_LOCATION_LABEL, &location)); g_assert_true (location == ACCEL_LOCATION_BASE); /* invalid label */ g_assert_false (parse_accel_location (NULL, &location)); g_assert_false (parse_accel_location (INVALID_LOCATION_LABEL, &location)); } static void test_accel_location (void) { AccelLocation location; /* display location */ g_assert_true (parse_accel_location (VALID_DISPLAY_LOCATION, &location)); g_assert_true (location == ACCEL_LOCATION_DISPLAY); /* base location */ g_assert_true (parse_accel_location (VALID_BASE_LOCATION, &location)); g_assert_true (location == ACCEL_LOCATION_BASE); /* default location (display) */ g_assert_true (parse_accel_location ("", &location)); g_assert_true (location == ACCEL_LOCATION_DISPLAY); /* Invalid matrix */ g_test_expect_message (NULL, G_LOG_LEVEL_WARNING, "Failed to parse 'invalid' as a location"); g_assert_false (parse_accel_location (INVALID_LOCATION, &location)); g_test_assert_expected_messages (); } int main (int argc, char **argv) { setlocale(LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_test_add_func ("/iio-sensor-proxy/accel-location", test_accel_location); g_test_add_func ("/iio-sensor-proxy/accel-label", test_accel_label); return g_test_run (); } iio-sensor-proxy-3.5/src/test-mount-matrix.c000066400000000000000000000045241445770364600212300ustar00rootroot00000000000000/* * Copyright (c) 2014-2016 Bastien Nocera * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. * */ #include #include "accel-mount-matrix.h" #define SWAP_Y_Z_MATRIX "1, 0, 0; 0, 0, 1; 0, 1, 0" #define INVALID_MATRIX "0, 1, 0; 1, 0, 0; 0, 0, 0" #define DEFAULT_MATRIX "1, 0, 0; 0, 1, 0; 0, 0, 1" static void print_vecs (AccelVec3 vecs[3]) { g_test_message ("%f, %f, %f; %f, %f, %f; %f, %f, %f", (double) vecs[0].x, (double) vecs[0].y, (double) vecs[0].z, (double) vecs[1].x, (double) vecs[1].y, (double) vecs[1].z, (double) vecs[2].x, (double) vecs[2].y, (double) vecs[2].z); } static void test_mount_matrix (void) { AccelVec3 *vecs; AccelVec3 test; /* Swap Y/Z matrix */ g_assert_true (parse_mount_matrix (SWAP_Y_Z_MATRIX, &vecs)); print_vecs (vecs); test.x = test.z = 0.0; test.y = -256.0; g_assert_true (apply_mount_matrix (vecs, &test)); g_assert_cmpfloat (test.x, ==, 0.0); g_assert_cmpfloat (test.z, ==, -256.0); g_assert_cmpfloat (test.y, ==, 0.0); g_free (vecs); /* Identity matrix */ g_assert_true (parse_mount_matrix ("", &vecs)); print_vecs (vecs); test.x = test.z = 0.0; test.y = -256.0; g_assert_true (apply_mount_matrix (vecs, &test)); g_assert_cmpfloat (test.x, ==, 0.0); g_assert_cmpfloat (test.z, ==, 0.0); g_assert_cmpfloat (test.y, ==, -256.0); g_free (vecs); /* Invalid matrix */ g_test_expect_message (NULL, G_LOG_LEVEL_WARNING, "In mount matrix '0, 1, 0; 1, 0, 0; 0, 0, 0', axis z is all zeroes, which is invalid"); g_assert_false (parse_mount_matrix (INVALID_MATRIX, &vecs)); g_test_assert_expected_messages (); } static void test_comma_decimal_separator (void) { char *old_locale; AccelVec3 *vecs; old_locale = setlocale (LC_ALL, "fr_FR.UTF-8"); /* French locale not available? */ g_assert_nonnull (old_locale); /* Default matrix */ g_assert_true (parse_mount_matrix (DEFAULT_MATRIX, &vecs)); g_free (vecs); setlocale (LC_ALL, old_locale); } int main (int argc, char **argv) { setlocale(LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_test_add_func ("/iio-sensor-proxy/mount-matrix", test_mount_matrix); g_test_add_func ("/iio-sensor-proxy/comma-decimal-separator", test_comma_decimal_separator); return g_test_run (); } iio-sensor-proxy-3.5/src/test-orientation-gtk.c000066400000000000000000000047721445770364600217070ustar00rootroot00000000000000/* * Copyright (c) 2014-2016 Bastien Nocera * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. * */ #include #include #include "orientation.h" static GtkWidget *scale_x, *scale_y, *scale_z; static GtkWidget *label; #define ONEG 256 static void value_changed (GtkSpinButton *spin_button, gpointer user_data) { int x, y, z; OrientationUp o; AccelScale scale; x = gtk_spin_button_get_value (GTK_SPIN_BUTTON (scale_x)); y = gtk_spin_button_get_value (GTK_SPIN_BUTTON (scale_y)); z = gtk_spin_button_get_value (GTK_SPIN_BUTTON (scale_z)); set_accel_scale (&scale, 9.81 / ONEG); o = orientation_calc (ORIENTATION_UNDEFINED, x, y, z, scale); gtk_label_set_text (GTK_LABEL (label), orientation_to_string (o)); } int main (int argc, char **argv) { GtkWidget *window; GtkWidget *grid; setlocale(LC_ALL, ""); gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); scale_x = gtk_spin_button_new_with_range (-ONEG, ONEG, 1); scale_y = gtk_spin_button_new_with_range (-ONEG, ONEG, 1); scale_z = gtk_spin_button_new_with_range (-ONEG, ONEG, 1); /* Set default values to "up" orientation */ gtk_spin_button_set_value (GTK_SPIN_BUTTON (scale_x), 0.0); gtk_spin_button_set_value (GTK_SPIN_BUTTON (scale_y), ONEG); gtk_spin_button_set_value (GTK_SPIN_BUTTON (scale_z), 0.0); grid = gtk_grid_new (); g_object_set (G_OBJECT (grid), "column-spacing", 12, "row-spacing", 12, NULL); gtk_container_add (GTK_CONTAINER (window), grid); gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("X:"), 0, 0, 1, 1); gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Y:"), 0, 1, 1, 1); gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Z:"), 0, 2, 1, 1); gtk_grid_attach (GTK_GRID (grid), scale_x, 1, 0, 1, 1); gtk_grid_attach (GTK_GRID (grid), scale_y, 1, 1, 1, 1); gtk_grid_attach (GTK_GRID (grid), scale_z, 1, 2, 1, 1); g_signal_connect (G_OBJECT (scale_x), "value-changed", G_CALLBACK (value_changed), NULL); g_signal_connect (G_OBJECT (scale_y), "value-changed", G_CALLBACK (value_changed), NULL); g_signal_connect (G_OBJECT (scale_z), "value-changed", G_CALLBACK (value_changed), NULL); label = gtk_label_new (""); gtk_grid_attach (GTK_GRID (grid), label, 0, 3, 2, 1); value_changed (NULL, NULL); gtk_widget_show_all (window); gtk_main (); return 0; } iio-sensor-proxy-3.5/src/test-orientation.c000066400000000000000000000132311445770364600211120ustar00rootroot00000000000000/* * Copyright (c) 2016 Bastien Nocera * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. * */ #include #include #include #include "orientation.h" #include "accel-mount-matrix.h" #define ONEG 256 static void test_orientation (void) { static struct { int x; int y; int z; OrientationUp expected; } orientations[] = { { 0, -ONEG, 0, ORIENTATION_NORMAL }, { -ONEG, 0, 0, ORIENTATION_RIGHT_UP }, { ONEG, 0, 0, ORIENTATION_LEFT_UP }, { 0, ONEG, 0, ORIENTATION_BOTTOM_UP } }; guint i, num_failures; num_failures = 0; for (i = 0; i < G_N_ELEMENTS (orientations); i++) { OrientationUp o; const char *expected, *result; AccelScale scale; set_accel_scale (&scale, 9.81 / ONEG); o = orientation_calc (ORIENTATION_UNDEFINED, orientations[i].x, orientations[i].y, orientations[i].z, scale); result = orientation_to_string (o); expected = orientation_to_string (orientations[i].expected); /* Fail straight away when not verbose */ if (g_test_verbose ()) { if (g_strcmp0 (result, expected) != 0) { g_test_message ("Expected %s, got %s", expected, result); num_failures++; } } else { g_assert_cmpstr (result, ==, expected); } } if (num_failures > 0) g_test_fail (); } static void test_orientation_threshold (void) { static struct { int x; int y; int z; OrientationUp expected; } orientations[] = { { 0, -ONEG, 0, ORIENTATION_NORMAL }, { 183, -ONEG, 0, ORIENTATION_NORMAL }, { 176, -ONEG, 0, ORIENTATION_NORMAL }, { 183, -ONEG, 0, ORIENTATION_NORMAL }, }; guint i, num_failures; OrientationUp prev; num_failures = 0; prev = ORIENTATION_UNDEFINED; for (i = 0; i < G_N_ELEMENTS (orientations); i++) { OrientationUp o; const char *expected, *result; AccelScale scale; set_accel_scale (&scale, 9.81 / ONEG); o = orientation_calc (prev, orientations[i].x, orientations[i].y, orientations[i].z, scale); result = orientation_to_string (o); expected = orientation_to_string (orientations[i].expected); /* Fail straight away when not verbose */ if (g_test_verbose ()) { if (g_strcmp0 (result, expected) != 0) { g_test_message ("Expected %s, got %s (orientation #%d)", expected, result, i); num_failures++; } } else { g_assert_cmpstr (result, ==, expected); } g_assert_cmpstr (result, ==, expected); prev = orientations[i].expected; } if (num_failures > 0) g_test_fail (); } static void test_mount_matrix_orientation (void) { guint i; struct { AccelVec3 readings; gdouble scale; const char *mount_matrix; OrientationUp expected; } tests[] = { /* Onda v975 quirking */ { { 523, 13, 5 }, 0.019163, "0, -1, 0; -1, 0, 0; 0, 0, 1", ORIENTATION_NORMAL }, { { 8, 521, -67 }, 0.019163, "0, -1, 0; -1, 0, 0; 0, 0, 1", ORIENTATION_RIGHT_UP }, /* Winbook TW100 quirking */ { { 24, 0, -21 }, 0.306457, "0, -1, 0; -1, 0, 0; 0, 0, 1", ORIENTATION_NORMAL }, { { 15, -25, -14 }, 0.306457, "0, -1, 0; -1, 0, 0; 0, 0, 1", ORIENTATION_LEFT_UP } }; for (i = 0; i < G_N_ELEMENTS (tests); i++) { AccelVec3 *vecs; const char *result, *expected; OrientationUp o; AccelScale scale; g_assert_true (parse_mount_matrix (tests[i].mount_matrix, &vecs)); g_assert_true (apply_mount_matrix (vecs, &tests[i].readings)); set_accel_scale (&scale, tests[i].scale); o = orientation_calc (ORIENTATION_UNDEFINED, tests[i].readings.x, tests[i].readings.y, tests[i].readings.z, scale); result = orientation_to_string (o); expected = orientation_to_string (tests[i].expected); g_assert_cmpstr (result, ==, expected); g_free (vecs); } } static gboolean print_orientation (const char *x_str, const char *y_str, const char *z_str, const char *scale_str, const char *mount_matrix) { int x, y, z; gdouble scale; OrientationUp o; AccelScale scale_vec; if (scale_str == NULL) scale = 1.0; else scale = g_strtod (scale_str, NULL); x = atoi (x_str); y = atoi (y_str); z = atoi (z_str); if (mount_matrix) { AccelVec3 *vecs; AccelVec3 readings; if (!parse_mount_matrix (mount_matrix, &vecs)) { g_printerr ("Could not parse mount matrix '%s'\n", mount_matrix); return FALSE; } readings.x = x; readings.y = y; readings.z = z; if (!apply_mount_matrix (vecs, &readings)) { g_printerr ("Could not apply mount matrix '%s'\n", mount_matrix); return FALSE; } x = readings.x; y = readings.y; z = readings.z; g_free (vecs); } set_accel_scale (&scale_vec, scale); o = orientation_calc (ORIENTATION_UNDEFINED, x, y, z, scale_vec); g_print ("Orientation for %d,%d,%d (scale: %lf) is '%s'\n", x, y, z, scale, orientation_to_string (o)); return TRUE; } int main (int argc, char **argv) { setlocale(LC_ALL, ""); g_test_init (&argc, &argv, NULL); if (argc > 1) { gboolean ret; if (argc == 6) { ret = print_orientation (argv[1], argv[2], argv[3], argv[4], argv[5]); } else if (argc == 5) { ret = print_orientation (argv[1], argv[2], argv[3], argv[4], NULL); } else if (argc == 4) { ret = print_orientation (argv[1], argv[2], argv[3], "1.0", NULL); } else { g_printerr ("Usage: %s X Y Z [scale] [mount-matrix]\n", argv[0]); ret = FALSE; } return (ret == FALSE ? 1 : 0); } g_test_add_func ("/iio-sensor-proxy/orientation", test_orientation); g_test_add_func ("/iio-sensor-proxy/quirking", test_mount_matrix_orientation); g_test_add_func ("/iio-sensor-proxy/threshold", test_orientation_threshold); return g_test_run (); } iio-sensor-proxy-3.5/src/uinput.h000066400000000000000000000437331445770364600171450ustar00rootroot00000000000000/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2003-2010 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef __UINPUT_H #define __UINPUT_H #ifdef __cplusplus extern "C" { #endif #include #include #include /* Events */ #define EV_SYN 0x00 #define EV_KEY 0x01 #define EV_REL 0x02 #define EV_ABS 0x03 #define EV_MSC 0x04 #define EV_LED 0x11 #define EV_SND 0x12 #define EV_REP 0x14 #define EV_FF 0x15 #define EV_PWR 0x16 #define EV_FF_STATUS 0x17 #define EV_MAX 0x1f /* Synchronization events */ #define SYN_REPORT 0 #define SYN_CONFIG 1 /* * Keys and buttons * * Most of the keys/buttons are modelled after USB HUT 1.12 * (see http://www.usb.org/developers/hidpage). * Abbreviations in the comments: * AC - Application Control * AL - Application Launch Button * SC - System Control */ #define KEY_RESERVED 0 #define KEY_ESC 1 #define KEY_1 2 #define KEY_2 3 #define KEY_3 4 #define KEY_4 5 #define KEY_5 6 #define KEY_6 7 #define KEY_7 8 #define KEY_8 9 #define KEY_9 10 #define KEY_0 11 #define KEY_MINUS 12 #define KEY_EQUAL 13 #define KEY_BACKSPACE 14 #define KEY_TAB 15 #define KEY_Q 16 #define KEY_W 17 #define KEY_E 18 #define KEY_R 19 #define KEY_T 20 #define KEY_Y 21 #define KEY_U 22 #define KEY_I 23 #define KEY_O 24 #define KEY_P 25 #define KEY_LEFTBRACE 26 #define KEY_RIGHTBRACE 27 #define KEY_ENTER 28 #define KEY_LEFTCTRL 29 #define KEY_A 30 #define KEY_S 31 #define KEY_D 32 #define KEY_F 33 #define KEY_G 34 #define KEY_H 35 #define KEY_J 36 #define KEY_K 37 #define KEY_L 38 #define KEY_SEMICOLON 39 #define KEY_APOSTROPHE 40 #define KEY_GRAVE 41 #define KEY_LEFTSHIFT 42 #define KEY_BACKSLASH 43 #define KEY_Z 44 #define KEY_X 45 #define KEY_C 46 #define KEY_V 47 #define KEY_B 48 #define KEY_N 49 #define KEY_M 50 #define KEY_COMMA 51 #define KEY_DOT 52 #define KEY_SLASH 53 #define KEY_RIGHTSHIFT 54 #define KEY_KPASTERISK 55 #define KEY_LEFTALT 56 #define KEY_SPACE 57 #define KEY_CAPSLOCK 58 #define KEY_F1 59 #define KEY_F2 60 #define KEY_F3 61 #define KEY_F4 62 #define KEY_F5 63 #define KEY_F6 64 #define KEY_F7 65 #define KEY_F8 66 #define KEY_F9 67 #define KEY_F10 68 #define KEY_NUMLOCK 69 #define KEY_SCROLLLOCK 70 #define KEY_KP7 71 #define KEY_KP8 72 #define KEY_KP9 73 #define KEY_KPMINUS 74 #define KEY_KP4 75 #define KEY_KP5 76 #define KEY_KP6 77 #define KEY_KPPLUS 78 #define KEY_KP1 79 #define KEY_KP2 80 #define KEY_KP3 81 #define KEY_KP0 82 #define KEY_KPDOT 83 #define KEY_ZENKAKUHANKAKU 85 #define KEY_102ND 86 #define KEY_F11 87 #define KEY_F12 88 #define KEY_RO 89 #define KEY_KATAKANA 90 #define KEY_HIRAGANA 91 #define KEY_HENKAN 92 #define KEY_KATAKANAHIRAGANA 93 #define KEY_MUHENKAN 94 #define KEY_KPJPCOMMA 95 #define KEY_KPENTER 96 #define KEY_RIGHTCTRL 97 #define KEY_KPSLASH 98 #define KEY_SYSRQ 99 #define KEY_RIGHTALT 100 #define KEY_LINEFEED 101 #define KEY_HOME 102 #define KEY_UP 103 #define KEY_PAGEUP 104 #define KEY_LEFT 105 #define KEY_RIGHT 106 #define KEY_END 107 #define KEY_DOWN 108 #define KEY_PAGEDOWN 109 #define KEY_INSERT 110 #define KEY_DELETE 111 #define KEY_MACRO 112 #define KEY_MUTE 113 #define KEY_VOLUMEDOWN 114 #define KEY_VOLUMEUP 115 #define KEY_POWER 116 /* SC System Power Down */ #define KEY_KPEQUAL 117 #define KEY_KPPLUSMINUS 118 #define KEY_PAUSE 119 #define KEY_KPCOMMA 121 #define KEY_HANGEUL 122 #define KEY_HANGUEL KEY_HANGEUL #define KEY_HANJA 123 #define KEY_YEN 124 #define KEY_LEFTMETA 125 #define KEY_RIGHTMETA 126 #define KEY_COMPOSE 127 #define KEY_STOP 128 /* AC Stop */ #define KEY_AGAIN 129 #define KEY_PROPS 130 /* AC Properties */ #define KEY_UNDO 131 /* AC Undo */ #define KEY_FRONT 132 #define KEY_COPY 133 /* AC Copy */ #define KEY_OPEN 134 /* AC Open */ #define KEY_PASTE 135 /* AC Paste */ #define KEY_FIND 136 /* AC Search */ #define KEY_CUT 137 /* AC Cut */ #define KEY_HELP 138 /* AL Integrated Help Center */ #define KEY_MENU 139 /* Menu (show menu) */ #define KEY_CALC 140 /* AL Calculator */ #define KEY_SETUP 141 #define KEY_SLEEP 142 /* SC System Sleep */ #define KEY_WAKEUP 143 /* System Wake Up */ #define KEY_FILE 144 /* AL Local Machine Browser */ #define KEY_SENDFILE 145 #define KEY_DELETEFILE 146 #define KEY_XFER 147 #define KEY_PROG1 148 #define KEY_PROG2 149 #define KEY_WWW 150 /* AL Internet Browser */ #define KEY_MSDOS 151 #define KEY_COFFEE 152 /* AL Terminal Lock/Screensaver */ #define KEY_SCREENLOCK KEY_COFFEE #define KEY_DIRECTION 153 #define KEY_CYCLEWINDOWS 154 #define KEY_MAIL 155 #define KEY_BOOKMARKS 156 /* AC Bookmarks */ #define KEY_COMPUTER 157 #define KEY_BACK 158 /* AC Back */ #define KEY_FORWARD 159 /* AC Forward */ #define KEY_CLOSECD 160 #define KEY_EJECTCD 161 #define KEY_EJECTCLOSECD 162 #define KEY_NEXTSONG 163 #define KEY_PLAYPAUSE 164 #define KEY_PREVIOUSSONG 165 #define KEY_STOPCD 166 #define KEY_RECORD 167 #define KEY_REWIND 168 #define KEY_PHONE 169 /* Media Select Telephone */ #define KEY_ISO 170 #define KEY_CONFIG 171 /* AL Consumer Control Configuration */ #define KEY_HOMEPAGE 172 /* AC Home */ #define KEY_REFRESH 173 /* AC Refresh */ #define KEY_EXIT 174 /* AC Exit */ #define KEY_MOVE 175 #define KEY_EDIT 176 #define KEY_SCROLLUP 177 #define KEY_SCROLLDOWN 178 #define KEY_KPLEFTPAREN 179 #define KEY_KPRIGHTPAREN 180 #define KEY_NEW 181 /* AC New */ #define KEY_REDO 182 /* AC Redo/Repeat */ #define KEY_F13 183 #define KEY_F14 184 #define KEY_F15 185 #define KEY_F16 186 #define KEY_F17 187 #define KEY_F18 188 #define KEY_F19 189 #define KEY_F20 190 #define KEY_F21 191 #define KEY_F22 192 #define KEY_F23 193 #define KEY_F24 194 #define KEY_PLAYCD 200 #define KEY_PAUSECD 201 #define KEY_PROG3 202 #define KEY_PROG4 203 #define KEY_SUSPEND 205 #define KEY_CLOSE 206 /* AC Close */ #define KEY_PLAY 207 #define KEY_FASTFORWARD 208 #define KEY_BASSBOOST 209 #define KEY_PRINT 210 /* AC Print */ #define KEY_HP 211 #define KEY_CAMERA 212 #define KEY_SOUND 213 #define KEY_QUESTION 214 #define KEY_EMAIL 215 #define KEY_CHAT 216 #define KEY_SEARCH 217 #define KEY_CONNECT 218 #define KEY_FINANCE 219 /* AL Checkbook/Finance */ #define KEY_SPORT 220 #define KEY_SHOP 221 #define KEY_ALTERASE 222 #define KEY_CANCEL 223 /* AC Cancel */ #define KEY_BRIGHTNESSDOWN 224 #define KEY_BRIGHTNESSUP 225 #define KEY_MEDIA 226 #define KEY_SWITCHVIDEOMODE 227 /* Cycle between available video outputs (Monitor/LCD/TV-out/etc) */ #define KEY_KBDILLUMTOGGLE 228 #define KEY_KBDILLUMDOWN 229 #define KEY_KBDILLUMUP 230 #define KEY_SEND 231 /* AC Send */ #define KEY_REPLY 232 /* AC Reply */ #define KEY_FORWARDMAIL 233 /* AC Forward Msg */ #define KEY_SAVE 234 /* AC Save */ #define KEY_DOCUMENTS 235 #define KEY_BATTERY 236 #define KEY_BLUETOOTH 237 #define KEY_WLAN 238 #define KEY_UWB 239 #define KEY_UNKNOWN 240 #define KEY_VIDEO_NEXT 241 /* drive next video source */ #define KEY_VIDEO_PREV 242 /* drive previous video source */ #define KEY_BRIGHTNESS_CYCLE 243 /* brightness up, after max is min */ #define KEY_BRIGHTNESS_ZERO 244 /* brightness off, use ambient */ #define KEY_DISPLAY_OFF 245 /* display device to off state */ #define KEY_WIMAX 246 /* Range 248 - 255 is reserved for special needs of AT keyboard driver */ #define BTN_MISC 0x100 #define BTN_0 0x100 #define BTN_1 0x101 #define BTN_2 0x102 #define BTN_3 0x103 #define BTN_4 0x104 #define BTN_5 0x105 #define BTN_6 0x106 #define BTN_7 0x107 #define BTN_8 0x108 #define BTN_9 0x109 #define BTN_MOUSE 0x110 #define BTN_LEFT 0x110 #define BTN_RIGHT 0x111 #define BTN_MIDDLE 0x112 #define BTN_SIDE 0x113 #define BTN_EXTRA 0x114 #define BTN_FORWARD 0x115 #define BTN_BACK 0x116 #define BTN_TASK 0x117 #define BTN_JOYSTICK 0x120 #define BTN_TRIGGER 0x120 #define BTN_THUMB 0x121 #define BTN_THUMB2 0x122 #define BTN_TOP 0x123 #define BTN_TOP2 0x124 #define BTN_PINKIE 0x125 #define BTN_BASE 0x126 #define BTN_BASE2 0x127 #define BTN_BASE3 0x128 #define BTN_BASE4 0x129 #define BTN_BASE5 0x12a #define BTN_BASE6 0x12b #define BTN_DEAD 0x12f #define BTN_GAMEPAD 0x130 #define BTN_A 0x130 #define BTN_B 0x131 #define BTN_C 0x132 #define BTN_X 0x133 #define BTN_Y 0x134 #define BTN_Z 0x135 #define BTN_TL 0x136 #define BTN_TR 0x137 #define BTN_TL2 0x138 #define BTN_TR2 0x139 #define BTN_SELECT 0x13a #define BTN_START 0x13b #define BTN_MODE 0x13c #define BTN_THUMBL 0x13d #define BTN_THUMBR 0x13e #define BTN_DIGI 0x140 #define BTN_TOOL_PEN 0x140 #define BTN_TOOL_RUBBER 0x141 #define BTN_TOOL_BRUSH 0x142 #define BTN_TOOL_PENCIL 0x143 #define BTN_TOOL_AIRBRUSH 0x144 #define BTN_TOOL_FINGER 0x145 #define BTN_TOOL_MOUSE 0x146 #define BTN_TOOL_LENS 0x147 #define BTN_TOUCH 0x14a #define BTN_STYLUS 0x14b #define BTN_STYLUS2 0x14c #define BTN_TOOL_DOUBLETAP 0x14d #define BTN_TOOL_TRIPLETAP 0x14e #define BTN_WHEEL 0x150 #define BTN_GEAR_DOWN 0x150 #define BTN_GEAR_UP 0x151 #define KEY_OK 0x160 #define KEY_SELECT 0x161 #define KEY_GOTO 0x162 #define KEY_CLEAR 0x163 #define KEY_POWER2 0x164 #define KEY_OPTION 0x165 #define KEY_INFO 0x166 /* AL OEM Features/Tips/Tutorial */ #define KEY_TIME 0x167 #define KEY_VENDOR 0x168 #define KEY_ARCHIVE 0x169 #define KEY_PROGRAM 0x16a /* Media Select Program Guide */ #define KEY_CHANNEL 0x16b #define KEY_FAVORITES 0x16c #define KEY_EPG 0x16d #define KEY_PVR 0x16e /* Media Select Home */ #define KEY_MHP 0x16f #define KEY_LANGUAGE 0x170 #define KEY_TITLE 0x171 #define KEY_SUBTITLE 0x172 #define KEY_ANGLE 0x173 #define KEY_ZOOM 0x174 #define KEY_MODE 0x175 #define KEY_KEYBOARD 0x176 #define KEY_SCREEN 0x177 #define KEY_PC 0x178 /* Media Select Computer */ #define KEY_TV 0x179 /* Media Select TV */ #define KEY_TV2 0x17a /* Media Select Cable */ #define KEY_VCR 0x17b /* Media Select VCR */ #define KEY_VCR2 0x17c /* VCR Plus */ #define KEY_SAT 0x17d /* Media Select Satellite */ #define KEY_SAT2 0x17e #define KEY_CD 0x17f /* Media Select CD */ #define KEY_TAPE 0x180 /* Media Select Tape */ #define KEY_RADIO 0x181 #define KEY_TUNER 0x182 /* Media Select Tuner */ #define KEY_PLAYER 0x183 #define KEY_TEXT 0x184 #define KEY_DVD 0x185 /* Media Select DVD */ #define KEY_AUX 0x186 #define KEY_MP3 0x187 #define KEY_AUDIO 0x188 #define KEY_VIDEO 0x189 #define KEY_DIRECTORY 0x18a #define KEY_LIST 0x18b #define KEY_MEMO 0x18c /* Media Select Messages */ #define KEY_CALENDAR 0x18d #define KEY_RED 0x18e #define KEY_GREEN 0x18f #define KEY_YELLOW 0x190 #define KEY_BLUE 0x191 #define KEY_CHANNELUP 0x192 /* Channel Increment */ #define KEY_CHANNELDOWN 0x193 /* Channel Decrement */ #define KEY_FIRST 0x194 #define KEY_LAST 0x195 /* Recall Last */ #define KEY_AB 0x196 #define KEY_NEXT 0x197 #define KEY_RESTART 0x198 #define KEY_SLOW 0x199 #define KEY_SHUFFLE 0x19a #define KEY_BREAK 0x19b #define KEY_PREVIOUS 0x19c #define KEY_DIGITS 0x19d #define KEY_TEEN 0x19e #define KEY_TWEN 0x19f #define KEY_VIDEOPHONE 0x1a0 /* Media Select Video Phone */ #define KEY_GAMES 0x1a1 /* Media Select Games */ #define KEY_ZOOMIN 0x1a2 /* AC Zoom In */ #define KEY_ZOOMOUT 0x1a3 /* AC Zoom Out */ #define KEY_ZOOMRESET 0x1a4 /* AC Zoom */ #define KEY_WORDPROCESSOR 0x1a5 /* AL Word Processor */ #define KEY_EDITOR 0x1a6 /* AL Text Editor */ #define KEY_SPREADSHEET 0x1a7 /* AL Spreadsheet */ #define KEY_GRAPHICSEDITOR 0x1a8 /* AL Graphics Editor */ #define KEY_PRESENTATION 0x1a9 /* AL Presentation App */ #define KEY_DATABASE 0x1aa /* AL Database App */ #define KEY_NEWS 0x1ab /* AL Newsreader */ #define KEY_VOICEMAIL 0x1ac /* AL Voicemail */ #define KEY_ADDRESSBOOK 0x1ad /* AL Contacts/Address Book */ #define KEY_MESSENGER 0x1ae /* AL Instant Messaging */ #define KEY_DISPLAYTOGGLE 0x1af /* Turn display (LCD) on and off */ #define KEY_SPELLCHECK 0x1b0 /* AL Spell Check */ #define KEY_LOGOFF 0x1b1 /* AL Logoff */ #define KEY_DOLLAR 0x1b2 #define KEY_EURO 0x1b3 #define KEY_FRAMEBACK 0x1b4 /* Consumer - transport controls */ #define KEY_FRAMEFORWARD 0x1b5 #define KEY_CONTEXT_MENU 0x1b6 /* GenDesc - system context menu */ #define KEY_MEDIA_REPEAT 0x1b7 /* Consumer - transport control */ #define KEY_DEL_EOL 0x1c0 #define KEY_DEL_EOS 0x1c1 #define KEY_INS_LINE 0x1c2 #define KEY_DEL_LINE 0x1c3 #define KEY_FN 0x1d0 #define KEY_FN_ESC 0x1d1 #define KEY_FN_F1 0x1d2 #define KEY_FN_F2 0x1d3 #define KEY_FN_F3 0x1d4 #define KEY_FN_F4 0x1d5 #define KEY_FN_F5 0x1d6 #define KEY_FN_F6 0x1d7 #define KEY_FN_F7 0x1d8 #define KEY_FN_F8 0x1d9 #define KEY_FN_F9 0x1da #define KEY_FN_F10 0x1db #define KEY_FN_F11 0x1dc #define KEY_FN_F12 0x1dd #define KEY_FN_1 0x1de #define KEY_FN_2 0x1df #define KEY_FN_D 0x1e0 #define KEY_FN_E 0x1e1 #define KEY_FN_F 0x1e2 #define KEY_FN_S 0x1e3 #define KEY_FN_B 0x1e4 #define KEY_BRL_DOT1 0x1f1 #define KEY_BRL_DOT2 0x1f2 #define KEY_BRL_DOT3 0x1f3 #define KEY_BRL_DOT4 0x1f4 #define KEY_BRL_DOT5 0x1f5 #define KEY_BRL_DOT6 0x1f6 #define KEY_BRL_DOT7 0x1f7 #define KEY_BRL_DOT8 0x1f8 #define KEY_BRL_DOT9 0x1f9 #define KEY_BRL_DOT10 0x1fa /* We avoid low common keys in module aliases so they don't get huge. */ #define KEY_MIN_INTERESTING KEY_MUTE #define KEY_MAX 0x1ff #define KEY_CNT (KEY_MAX+1) /* * Relative axes */ #define REL_X 0x00 #define REL_Y 0x01 #define REL_Z 0x02 #define REL_RX 0x03 #define REL_RY 0x04 #define REL_RZ 0x05 #define REL_HWHEEL 0x06 #define REL_DIAL 0x07 #define REL_WHEEL 0x08 #define REL_MISC 0x09 #define REL_MAX 0x0f #define REL_CNT (REL_MAX+1) /* * Absolute axes */ #define ABS_X 0x00 #define ABS_Y 0x01 #define ABS_Z 0x02 #define ABS_RX 0x03 #define ABS_RY 0x04 #define ABS_RZ 0x05 #define ABS_THROTTLE 0x06 #define ABS_RUDDER 0x07 #define ABS_WHEEL 0x08 #define ABS_GAS 0x09 #define ABS_BRAKE 0x0a #define ABS_HAT0X 0x10 #define ABS_HAT0Y 0x11 #define ABS_HAT1X 0x12 #define ABS_HAT1Y 0x13 #define ABS_HAT2X 0x14 #define ABS_HAT2Y 0x15 #define ABS_HAT3X 0x16 #define ABS_HAT3Y 0x17 #define ABS_PRESSURE 0x18 #define ABS_DISTANCE 0x19 #define ABS_TILT_X 0x1a #define ABS_TILT_Y 0x1b #define ABS_TOOL_WIDTH 0x1c #define ABS_VOLUME 0x20 #define ABS_MISC 0x28 #define ABS_MAX 0x3f #define ABS_CNT (ABS_MAX+1) /* * Switch events */ #define SW_LID 0x00 /* set = lid shut */ #define SW_TABLET_MODE 0x01 /* set = tablet mode */ #define SW_HEADPHONE_INSERT 0x02 /* set = inserted */ #define SW_RFKILL_ALL 0x03 /* rfkill master switch, type "any" set = radio enabled */ #define SW_RADIO SW_RFKILL_ALL /* deprecated */ #define SW_MICROPHONE_INSERT 0x04 /* set = inserted */ #define SW_DOCK 0x05 /* set = plugged into dock */ #define SW_MAX 0x0f #define SW_CNT (SW_MAX+1) /* * Misc events */ #define MSC_SERIAL 0x00 #define MSC_PULSELED 0x01 #define MSC_GESTURE 0x02 #define MSC_RAW 0x03 #define MSC_SCAN 0x04 #define MSC_MAX 0x07 #define MSC_CNT (MSC_MAX+1) /* * LEDs */ #define LED_NUML 0x00 #define LED_CAPSL 0x01 #define LED_SCROLLL 0x02 #define LED_COMPOSE 0x03 #define LED_KANA 0x04 #define LED_SLEEP 0x05 #define LED_SUSPEND 0x06 #define LED_MUTE 0x07 #define LED_MISC 0x08 #define LED_MAIL 0x09 #define LED_CHARGING 0x0a #define LED_MAX 0x0f #define LED_CNT (LED_MAX+1) /* * Autorepeat values */ #define REP_DELAY 0x00 #define REP_PERIOD 0x01 #define REP_MAX 0x01 /* * Sounds */ #define SND_CLICK 0x00 #define SND_BELL 0x01 #define SND_TONE 0x02 #define SND_MAX 0x07 #define SND_CNT (SND_MAX+1) /* * IDs. */ #define ID_BUS 0 #define ID_VENDOR 1 #define ID_PRODUCT 2 #define ID_VERSION 3 #define BUS_PCI 0x01 #define BUS_ISAPNP 0x02 #define BUS_USB 0x03 #define BUS_HIL 0x04 #define BUS_BLUETOOTH 0x05 #define BUS_VIRTUAL 0x06 #define BUS_ISA 0x10 #define BUS_I8042 0x11 #define BUS_XTKBD 0x12 #define BUS_RS232 0x13 #define BUS_GAMEPORT 0x14 #define BUS_PARPORT 0x15 #define BUS_AMIGA 0x16 #define BUS_ADB 0x17 #define BUS_I2C 0x18 #define BUS_HOST 0x19 #define BUS_GSC 0x1A #define BUS_ATARI 0x1B /* User input interface */ #define UINPUT_IOCTL_BASE 'U' #define UI_DEV_CREATE _IO(UINPUT_IOCTL_BASE, 1) #define UI_DEV_DESTROY _IO(UINPUT_IOCTL_BASE, 2) #define UI_SET_EVBIT _IOW(UINPUT_IOCTL_BASE, 100, int) #define UI_SET_KEYBIT _IOW(UINPUT_IOCTL_BASE, 101, int) #define UI_SET_RELBIT _IOW(UINPUT_IOCTL_BASE, 102, int) #define UI_SET_ABSBIT _IOW(UINPUT_IOCTL_BASE, 103, int) #define UI_SET_MSCBIT _IOW(UINPUT_IOCTL_BASE, 104, int) #define UI_SET_LEDBIT _IOW(UINPUT_IOCTL_BASE, 105, int) #define UI_SET_SNDBIT _IOW(UINPUT_IOCTL_BASE, 106, int) #define UI_SET_FFBIT _IOW(UINPUT_IOCTL_BASE, 107, int) #define UI_SET_PHYS _IOW(UINPUT_IOCTL_BASE, 108, char*) #define UI_SET_SWBIT _IOW(UINPUT_IOCTL_BASE, 109, int) #ifndef NBITS #define NBITS(x) ((((x) - 1) / (sizeof(long) * 8)) + 1) #endif #define UINPUT_MAX_NAME_SIZE 80 struct uinput_id { uint16_t bustype; uint16_t vendor; uint16_t product; uint16_t version; }; struct uinput_dev { char name[UINPUT_MAX_NAME_SIZE]; struct uinput_id id; int ff_effects_max; int absmax[ABS_MAX + 1]; int absmin[ABS_MAX + 1]; int absfuzz[ABS_MAX + 1]; int absflat[ABS_MAX + 1]; }; struct uinput_event { struct timeval time; uint16_t type; uint16_t code; int32_t value; }; #ifdef __cplusplus } #endif #endif /* __UINPUT_H */ iio-sensor-proxy-3.5/src/utils.c000066400000000000000000000007551445770364600167510ustar00rootroot00000000000000/* * Copyright (c) 2021 Bastien Nocera * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. * */ #include "utils.h" char * get_device_file (GUdevDevice *device) { if (!IS_TEST) return g_strdup (g_udev_device_get_device_file (device)); return g_build_filename (g_getenv ("UMOCKDEV_DIR"), "iio-dev-data.bin", NULL); } iio-sensor-proxy-3.5/src/utils.h000066400000000000000000000010701445770364600167450ustar00rootroot00000000000000/* * Copyright (c) 2021 Bastien Nocera * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. * */ #pragma once #include #include #include #include #define IS_TEST (g_getenv ("UMOCKDEV_DIR") != NULL) typedef int IioFd; G_DEFINE_AUTO_CLEANUP_FREE_FUNC(IioFd, close, -1) G_DEFINE_AUTOPTR_CLEANUP_FUNC(FILE, fclose) char *get_device_file (GUdevDevice *device); iio-sensor-proxy-3.5/tests/000077500000000000000000000000001445770364600160115ustar00rootroot00000000000000iio-sensor-proxy-3.5/tests/iio-buffer-accel-data/000077500000000000000000000000001445770364600220145ustar00rootroot00000000000000iio-sensor-proxy-3.5/tests/iio-buffer-accel-data/orientation-left-up.bin000066400000000000000000000005501445770364600264130ustar00rootroot00000000000000o"H [iiDDoI []]K [<,lN [Rff||m5P [vqS [< U [11@uW [kkaAY [V,,N[ [tt5O\ [jj{^ []a [~zi [H77m [iio-sensor-proxy-3.5/tests/iio-buffer-accel-data/orientation-normal.bin000066400000000000000000000000301445770364600263200ustar00rootroot00000000000000gii΁tE [iio-sensor-proxy-3.5/tests/input-accel-capture.ioctl000066400000000000000000000004241445770364600227120ustar00rootroot00000000000000@DEV /dev/input/event4 EVIOCGABS 0 0000000000FEFFFF00020000000000000000000000000000 EVIOCGABS(1) 0 00FFFFFF00FEFFFF00020000000000000000000000000000 EVIOCGABS(2) 0 0000000000FEFFFF00020000000000000000000000000000 EVIOCGABS(1) 0 0001000000FEFFFF00020000000000000000000000000000 iio-sensor-proxy-3.5/tests/input-accel-device000066400000000000000000000024331445770364600213770ustar00rootroot00000000000000P: /devices/virtual/input/input159/event4 N: input/event4 E: DEVNAME=/dev/input/event4 E: MAJOR=13 E: MINOR=68 E: SUBSYSTEM=input E: ID_INPUT=1 E: ID_INPUT_ACCELEROMETER=1 E: IIO_SENSOR_PROXY_TYPE=input-accel E: SYSTEMD_WANTS=iio-sensor-proxy.service E: TAGS=:systemd: A: dev=13:68 L: device=../../input159 A: power/control=auto A: power/runtime_active_time=0 A: power/runtime_status=unsupported A: power/runtime_suspended_time=0 P: /devices/virtual/input/input159 E: PRODUCT=6/0/0/0 E: NAME="iio-sensor-proxy test application" E: PROP=0 E: EV=9 E: ABS=7 E: MODALIAS=input:b0006v0000p0000e0000-e0,3,kra0,1,2,mlsfw E: SUBSYSTEM=input E: ID_INPUT=1 E: ID_INPUT_ACCELEROMETER=1 E: IIO_SENSOR_PROXY_TYPE=input-accel E: SYSTEMD_WANTS=iio-sensor-proxy.service E: TAGS=:systemd:seat: A: capabilities/abs=7 A: capabilities/ev=9 A: capabilities/ff=0 A: capabilities/key=0 A: capabilities/led=0 A: capabilities/msc=0 A: capabilities/rel=0 A: capabilities/snd=0 A: capabilities/sw=0 A: id/bustype=0006 A: id/product=0000 A: id/vendor=0000 A: id/version=0000 A: modalias=input:b0006v0000p0000e0000-e0,3,kra0,1,2,mlsfw A: name=iio-sensor-proxy test application A: phys= A: power/control=auto A: power/runtime_active_time=0 A: power/runtime_status=unsupported A: power/runtime_suspended_time=0 A: properties=0 A: uniq= iio-sensor-proxy-3.5/tests/integration-test.py000077500000000000000000000721151445770364600216740ustar00rootroot00000000000000#!/usr/bin/python3 # iio-sensor-proxy integration test suite # # Run in built tree to test local built binaries, or from anywhere else to test # system installed binaries. # # Copyright: (C) 2011 Martin Pitt # (C) 2021 Bastien Nocera # # 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. import os import sys import dbus import dbusmock import gi import tempfile import psutil import subprocess import unittest import time try: from gi.repository import GLib from gi.repository import Gio except ImportError as e: sys.stderr.write('PyGobject not available for Python 3, or missing GI typelibs: %s\n' % str(e)) sys.exit(1) try: gi.require_version('UMockdev', '1.0') from gi.repository import UMockdev except ImportError: sys.stderr.write('umockdev not available (https://github.com/martinpitt/umockdev)\n') sys.exit(1) SP = 'net.hadess.SensorProxy' SP_PATH = '/net/hadess/SensorProxy' SP_COMPASS = 'net.hadess.SensorProxy.Compass' SP_COMPASS_PATH = '/net/hadess/SensorProxy/Compass' class Tests(dbusmock.DBusTestCase): @classmethod def setUpClass(cls): # run from local build tree if we are in one, otherwise use system instance builddir = os.getenv('top_builddir', '.') if os.access(os.path.join(builddir, 'src', 'iio-sensor-proxy'), os.X_OK): cls.daemon_path = os.path.join(builddir, 'src', 'iio-sensor-proxy') cls.monitor_sensor_path = os.path.join(builddir, 'src', 'monitor-sensor') print('Testing binaries from local build tree (%s)' % cls.daemon_path) elif os.environ.get('UNDER_JHBUILD', False): jhbuild_prefix = os.environ['JHBUILD_PREFIX'] cls.daemon_path = os.path.join(jhbuild_prefix, 'libexec', 'iio-sensor-proxy') cls.monitor_sensor_path = os.path.join(jhbuild_prefix, 'bin', 'monitor-sensor') print('Testing binaries from JHBuild (%s)' % cls.daemon_path) else: cls.daemon_path = None with open('/usr/lib/systemd/system/iio-sensor-proxy.service') as f: for line in f: if line.startswith('ExecStart='): cls.daemon_path = line.split('=', 1)[1].strip() break assert cls.daemon_path, 'could not determine daemon path from systemd .service file' cls.monitor_sensor_path = '/usr/bin/monitor-sensor' print('Testing installed system binary (%s)' % cls.daemon_path) # fail on CRITICALs on client and server side GLib.log_set_always_fatal(GLib.LogLevelFlags.LEVEL_WARNING | GLib.LogLevelFlags.LEVEL_ERROR | GLib.LogLevelFlags.LEVEL_CRITICAL) os.environ['G_DEBUG'] = 'fatal_warnings' # set up a fake system D-BUS cls.test_bus = Gio.TestDBus.new(Gio.TestDBusFlags.NONE) cls.test_bus.up() try: del os.environ['DBUS_SESSION_BUS_ADDRESS'] except KeyError: pass os.environ['DBUS_SYSTEM_BUS_ADDRESS'] = cls.test_bus.get_bus_address() cls.dbus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) cls.dbus_con = cls.get_dbus(True) @classmethod def tearDownClass(cls): cls.test_bus.down() dbusmock.DBusTestCase.tearDownClass() def setUp(self): '''Set up a local umockdev testbed. The testbed is initially empty. ''' self.testbed = UMockdev.Testbed.new() self.polkitd, obj_polkit = self.spawn_server_template( 'polkitd', {}, stdout=subprocess.PIPE) obj_polkit.SetAllowed(['net.hadess.SensorProxy.claim-sensor']) self.proxy = None self.log = None self.daemon = None def run(self, result=None): super(Tests, self).run(result) if result and len(result.errors) + len(result.failures) > 0 and self.log: with open(self.log.name) as f: sys.stderr.write('\n-------------- daemon log: ----------------\n') sys.stderr.write(f.read()) sys.stderr.write('------------------------------\n') def tearDown(self): del self.testbed self.stop_daemon() if self.polkitd: try: self.polkitd.kill() except OSError: pass self.polkitd.wait() self.polkitd = None # # Daemon control and D-BUS I/O # def start_daemon(self, env = None, wrapper = None): '''Start daemon and create DBus proxy. When done, this sets self.proxy as the Gio.DBusProxy for power-profiles-daemon. ''' if not env: env = os.environ.copy() env['G_DEBUG'] = 'fatal-criticals' env['G_MESSAGES_DEBUG'] = 'all' env['UMOCKDEV_DEBUG'] = 'all' # note: Python doesn't propagate the setenv from Testbed.new(), so we # have to do that ourselves env['UMOCKDEV_DIR'] = self.testbed.get_root_dir() self.log = tempfile.NamedTemporaryFile() timeout_multiplier = 1 if wrapper: daemon_path = wrapper + [ self.daemon_path ] else: daemon_path = [ self.daemon_path ] if os.getenv('VALGRIND') != None: daemon_path = ['valgrind'] + daemon_path + ['-v'] timeout_multiplier = 10 else: daemon_path = daemon_path + ['-v'] self.daemon = subprocess.Popen(daemon_path, env=env, stdout=self.log, stderr=subprocess.STDOUT) # wait until the daemon gets online timeout = 100 * timeout_multiplier while timeout > 0: time.sleep(0.1) timeout -= 1 try: self.get_dbus_property('HasAccelerometer') break except GLib.GError: pass else: self.fail('daemon did not start in 10 seconds') self.proxy = Gio.DBusProxy.new_sync( self.dbus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None, SP, SP_PATH, SP, None) self.assertEqual(self.daemon.poll(), None, 'daemon crashed') def stop_daemon(self): '''Stop the daemon if it is running.''' if self.daemon: try: for child in psutil.Process(self.daemon.pid).children(recursive=True): child.kill() self.daemon.kill() except OSError: pass self.daemon.wait() self.daemon = None self.proxy = None def get_dbus_property(self, name): '''Get property value from daemon D-Bus interface.''' proxy = Gio.DBusProxy.new_sync( self.dbus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None, SP, SP_PATH, 'org.freedesktop.DBus.Properties', None) return proxy.Get('(ss)', SP, name) def get_compass_dbus_property(self, name): '''Get property value from daemon compass D-Bus interface.''' proxy = Gio.DBusProxy.new_sync( self.dbus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None, SP, SP_COMPASS_PATH, 'org.freedesktop.DBus.Properties', None) return proxy.Get('(ss)', SP_COMPASS, name) def have_text_in_log(self, text): return self.count_text_in_log(text) > 0 def count_text_in_log(self, text): with open(self.log.name) as f: return f.read().count(text) def read_sysfs_attr(self, device, attribute): with open(os.path.join(self.testbed.get_root_dir() + device, attribute), 'rb') as f: return f.read() return None def read_file(self, path): with open(path, 'rb') as f: return f.read() return None def assertEventually(self, condition, message=None, timeout=50): '''Assert that condition function eventually returns True. Timeout is in deciseconds, defaulting to 50 (5 seconds). message is printed on failure. ''' while timeout >= 0: context = GLib.MainContext.default() while context.iteration(False): pass if condition(): break timeout -= 1 time.sleep(0.1) else: self.fail(message or 'timed out waiting for ' + str(condition)) # # Actual test cases # def test_hwmon_light(self): '''hwmon light''' hwmon = self.testbed.add_device('platform', 'hwmon-als', None, ['light', '(128,128)'], ['IIO_SENSOR_PROXY_TYPE', 'hwmon-als'] ) self.start_daemon() self.assertEqual(self.get_dbus_property('HasAmbientLight'), True) self.assertEqual(self.get_dbus_property('HasAccelerometer'), False) # Default values self.assertEqual(self.get_dbus_property('LightLevelUnit'), 'lux') self.assertEqual(self.get_dbus_property('LightLevel'), 0) self.proxy.ClaimLight() self.assertEventually(lambda: int(self.get_dbus_property('LightLevel')) == 50) self.assertEqual(self.get_dbus_property('LightLevelUnit'), 'vendor') self.testbed.set_attribute(hwmon, 'light', '(255,255)') # DEFAULT_POLL_TIME time.sleep(0.5) self.assertEventually(lambda: self.get_dbus_property('LightLevel') == 100) # process = subprocess.Popen(['gdbus', 'introspect', '--system', '--dest', 'net.hadess.SensorProxy', '--object-path', '/net/hadess/SensorProxy']) # print (self.get_dbus_property('Foo')) self.stop_daemon() def test_fake_light(self): '''fake light''' self.testbed.add_device('input', 'fake-light', None, [], ['NAME', '"Power Button"'] ) env = os.environ.copy() env['FAKE_LIGHT_SENSOR'] = '1' self.start_daemon(env) self.assertEqual(self.get_dbus_property('HasAmbientLight'), True) self.assertEqual(self.get_dbus_property('HasAccelerometer'), False) # Default values self.assertEqual(self.get_dbus_property('LightLevelUnit'), 'lux') self.assertEqual(self.get_dbus_property('LightLevel'), 0) self.proxy.ClaimLight() self.assertEventually(lambda: int(self.get_dbus_property('LightLevel')) == 1.0) self.assertEqual(self.get_dbus_property('LightLevelUnit'), 'lux') self.assertEventually(lambda: int(self.get_dbus_property('LightLevel')) == 2.0) self.assertEqual(self.get_dbus_property('LightLevelUnit'), 'lux') # process = subprocess.Popen(['gdbus', 'introspect', '--system', '--dest', 'net.hadess.SensorProxy', '--object-path', '/net/hadess/SensorProxy']) # print (self.get_dbus_property('Foo')) self.stop_daemon() def test_fake_compass(self): '''fake compass''' self.testbed.add_device('input', 'fake-compass', None, [], ['NAME', '"Power Button"'] ) env = os.environ.copy() env['FAKE_COMPASS'] = '1' self.start_daemon(env) self.assertEqual(self.get_dbus_property('HasAmbientLight'), False) self.assertEqual(self.get_compass_dbus_property('HasCompass'), True) # Default values compass_proxy = Gio.DBusProxy.new_sync( self.dbus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None, SP, SP_COMPASS_PATH, SP_COMPASS, None) compass_proxy.ClaimCompass() self.assertEventually(lambda: int(self.get_compass_dbus_property('CompassHeading')) == 10) self.assertEventually(lambda: int(self.get_compass_dbus_property('CompassHeading')) == 20) self.stop_daemon() def test_iio_accel_base_location(self): '''iio accel base location''' self.testbed.add_device('iio', 'iio-accel', None, ['in_accel_x_raw', '0', 'in_accel_y_raw', '-25.6', 'in_accel_z_raw', '0', 'in_accel_scale', '10', 'label', 'accel-base', 'sampling_frequency', '5.2', 'name', 'IIO Test Accelerometer'], ['NAME', '"IIO Accelerometer"', 'IIO_SENSOR_PROXY_TYPE', 'iio-poll-accel'] ) self.testbed.add_device('input', 'fake-compass', None, [], ['NAME', '"Power Button"'] ) env = os.environ.copy() env['FAKE_COMPASS'] = '1' self.start_daemon(env) self.assertEqual(self.get_compass_dbus_property('HasCompass'), True) self.assertEqual(self.get_dbus_property('HasAccelerometer'), False) self.stop_daemon() def test_iio_poll_accel(self): '''iio poll accel''' accel = self.testbed.add_device('iio', 'iio-accel', None, ['in_accel_x_raw', '0', 'in_accel_y_raw', '-25.6', 'in_accel_z_raw', '0', 'in_accel_scale', '10', 'sampling_frequency', '5.2', 'name', 'IIO Test Accelerometer'], ['NAME', '"IIO Accelerometer"', 'IIO_SENSOR_PROXY_TYPE', 'iio-poll-accel'] ) self.start_daemon() self.assertEqual(self.get_dbus_property('HasAmbientLight'), False) self.assertEqual(self.get_dbus_property('HasAccelerometer'), True) # Default values self.assertEqual(self.get_dbus_property('AccelerometerOrientation'), 'undefined') self.assertEqual(self.read_sysfs_attr(accel, 'sampling_frequency'), b'10') self.proxy.ClaimAccelerometer() self.assertEventually(lambda: self.get_dbus_property('AccelerometerOrientation') == 'normal') self.testbed.set_attribute(accel, 'in_accel_x_raw', '-25.6') self.testbed.set_attribute(accel, 'in_accel_y_raw', '0') self.assertEventually(lambda: self.get_dbus_property('AccelerometerOrientation') == 'right-up') self.stop_daemon() def test_iio_poll_light(self): '''iio poll light''' iio = self.testbed.add_device('iio', 'iio-als', None, ['integration_time', '1', 'in_illuminance_input', '10', 'in_illuminance_scale', '1.0'], ['NAME', '"IIO Light Sensor"', 'IIO_SENSOR_PROXY_TYPE', 'iio-poll-als'] ) self.start_daemon() self.assertEqual(self.get_dbus_property('HasAmbientLight'), True) self.assertEqual(self.get_dbus_property('HasAccelerometer'), False) # Default values self.assertEqual(self.get_dbus_property('LightLevelUnit'), 'lux') self.assertEqual(self.get_dbus_property('LightLevel'), 0) self.proxy.ClaimLight() self.assertEventually(lambda: int(self.get_dbus_property('LightLevel')) == 10) self.assertEqual(self.get_dbus_property('LightLevelUnit'), 'lux') self.testbed.set_attribute(iio, 'in_illuminance_input', '30') self.assertEventually(lambda: self.get_dbus_property('LightLevel') == 30) self.stop_daemon() def test_iio_poll_proximity(self): '''iio poll proximity''' prox = self.testbed.add_device('iio', 'iio-proximity', None, ['in_proximity_nearlevel', '128', 'in_proximity_raw', '256', 'name', 'IIO Test Proximity Sensor'], ['NAME', '"IIO Proximity Sensor"', 'IIO_SENSOR_PROXY_TYPE', 'iio-poll-proximity'] ) self.start_daemon() self.assertEqual(self.get_dbus_property('HasAmbientLight'), False) self.assertEqual(self.get_dbus_property('HasAccelerometer'), False) self.assertEqual(self.get_dbus_property('HasProximity'), True) # Default values self.assertEqual(self.get_dbus_property('ProximityNear'), False) self.proxy.ClaimProximity() self.assertEventually(lambda: self.get_dbus_property('ProximityNear') == True) self.testbed.set_attribute(prox, 'in_proximity_raw', '0') self.assertEventually(lambda: self.get_dbus_property('ProximityNear') == False) self.testbed.set_attribute(prox, 'in_proximity_raw', '129') self.assertEventually(lambda: self.get_dbus_property('ProximityNear') == True) # Test margin self.testbed.set_attribute(prox, 'in_proximity_raw', '127') self.assertEventually(lambda: self.get_dbus_property('ProximityNear') == True) self.stop_daemon() def test_input_accel(self): '''input accelerometer''' top_srcdir = os.getenv('top_srcdir', '.') script = ['umockdev-run', '-d', top_srcdir + '/tests/input-accel-device', '-i', '/dev/input/event4=%s/tests/input-accel-capture.ioctl' % (top_srcdir), '--'] self.start_daemon(wrapper=script) self.assertEqual(self.get_dbus_property('HasAccelerometer'), True) self.assertEqual(self.get_dbus_property('AccelerometerOrientation'), 'normal') self.stop_daemon() def test_iio_buffer_accel(self): '''iio buffer accel''' top_srcdir = os.getenv('top_srcdir', '.') mock_dev_data = self.testbed.get_root_dir() + '/iio-dev-data.bin' accel = self.testbed.add_device('iio', 'iio-buffer-accel0', None, ['name', 'IIO Test Accelerometer', 'buffer/enable', '0', 'trigger/current_trigger', '', 'scan_elements/in_accel_x_en', '0', 'scan_elements/in_accel_x_index', '0', 'scan_elements/in_accel_x_type', 'le:s16/32>>0', 'scan_elements/in_accel_y_en', '0', 'scan_elements/in_accel_y_index', '1', 'scan_elements/in_accel_y_type', 'le:s16/32>>0', 'scan_elements/in_accel_z_en', '0', 'scan_elements/in_accel_z_index', '2', 'scan_elements/in_accel_z_type', 'le:s16/32>>0', 'scan_elements/in_timestamp_en', '1', 'scan_elements/in_timestamp_index', '3', 'scan_elements/in_timestamp_type', 'le:s64/64>>0'], ['NAME', '"IIO Accelerometer"', 'DEVNAME', '/dev/iio-buffer-accel-test', 'IIO_SENSOR_PROXY_TYPE', 'iio-buffer-accel'] ) trigger = self.testbed.add_device('iio', 'trigger0', None, ['name', 'accel_3d-dev0'], [] ) self.start_daemon() self.assertEqual(self.get_dbus_property('HasAmbientLight'), False) self.assertEqual(self.get_dbus_property('HasAccelerometer'), True) # Default values self.assertEqual(self.get_dbus_property('AccelerometerOrientation'), 'undefined') self.assertEqual(self.read_sysfs_attr(accel, 'buffer/enable'), b'1') self.assertEqual(self.read_sysfs_attr(accel, 'scan_elements/in_accel_x_en'), b'1') self.assertEqual(self.read_sysfs_attr(accel, 'scan_elements/in_accel_y_en'), b'1') self.assertEqual(self.read_sysfs_attr(accel, 'scan_elements/in_accel_z_en'), b'1') data = self.read_file(top_srcdir + '/tests/iio-buffer-accel-data/orientation-normal.bin') with open(mock_dev_data,'wb') as mock_file: mock_file.write(data) self.proxy.ClaimAccelerometer() time.sleep(1) self.assertEqual(self.get_dbus_property('AccelerometerOrientation'), 'normal') os.remove(mock_dev_data) time.sleep(1) self.assertEqual(self.get_dbus_property('AccelerometerOrientation'), 'normal') data = self.read_file(top_srcdir + '/tests/iio-buffer-accel-data/orientation-left-up.bin') with open(mock_dev_data,'wb') as mock_file: mock_file.write(data) time.sleep(1) self.assertEqual(self.get_dbus_property('AccelerometerOrientation'), 'left-up') os.remove(mock_dev_data) data = self.read_file(top_srcdir + '/tests/iio-buffer-accel-data/orientation-normal.bin') with open(mock_dev_data,'wb') as mock_file: mock_file.write(data) time.sleep(1) self.assertEqual(self.get_dbus_property('AccelerometerOrientation'), 'normal') os.remove(mock_dev_data) self.stop_daemon() def test_unrequested_readings(self): '''unrequested property updates''' self.testbed.add_device('input', 'fake-light', None, [], ['NAME', '"Power Button"'] ) env = os.environ.copy() env['FAKE_LIGHT_SENSOR'] = '1' self.start_daemon(env) self.assertEqual(self.get_dbus_property('HasAmbientLight'), True) ctx = GLib.main_context_default() self.proxy.ClaimLight() self.assertEventually(lambda: int(self.get_dbus_property('LightLevel')) == 1.0) self.proxy.ReleaseLight() self.assertEqual(self.get_dbus_property('LightLevel'), 1.0) monitor = subprocess.Popen(self.monitor_sensor_path) self.assertEventually(lambda: int(self.get_dbus_property('LightLevel')) == 2.0) monitor.kill() # Catch up with main loop signals while ctx.pending(): ctx.iteration(True) # We shouldn't have received light level properties updates res = self.proxy.get_cached_property('LightLevel') self.assertEqual(res.unpack(), 1.0) self.assertTrue(int(self.get_dbus_property('LightLevel') > 1.0)) self.stop_daemon() def test_iio_scale_decimal_separator(self): '''scale decimal separator''' top_srcdir = os.getenv('top_srcdir', '.') mock_dev_data = self.testbed.get_root_dir() + '/iio-dev-data.bin' accel = self.testbed.add_device('iio', 'iio-buffer-accel0', None, ['name', 'IIO Test Accelerometer', 'buffer/enable', '0', 'trigger/current_trigger', '', 'in_accel_scale', '0.000010\n', 'in_accel_offset', '0.0\n', 'in_accel_mount_matrix', '1, 0, 0; 0, 1, 0; 0, 0, 1\n', 'scan_elements/in_accel_x_en', '0', 'scan_elements/in_accel_x_index', '0', 'scan_elements/in_accel_x_type', 'le:s16/32>>0', 'scan_elements/in_accel_y_en', '0', 'scan_elements/in_accel_y_index', '1', 'scan_elements/in_accel_y_type', 'le:s16/32>>0', 'scan_elements/in_accel_z_en', '0', 'scan_elements/in_accel_z_index', '2', 'scan_elements/in_accel_z_type', 'le:s16/32>>0', 'scan_elements/in_timestamp_en', '1', 'scan_elements/in_timestamp_index', '3', 'scan_elements/in_timestamp_type', 'le:s64/64>>0'], ['NAME', '"IIO Accelerometer"', 'DEVNAME', '/dev/iio-buffer-accel-test', 'IIO_SENSOR_PROXY_TYPE', 'iio-buffer-accel'] ) trigger = self.testbed.add_device('iio', 'trigger0', None, ['name', 'accel_3d-dev0'], [] ) env = os.environ.copy() env['LC_NUMERIC'] = 'fr_FR.UTF-8' self.start_daemon(env=env) self.assertEqual(self.get_dbus_property('HasAccelerometer'), True) data = self.read_file(top_srcdir + '/tests/iio-buffer-accel-data/orientation-normal.bin') with open(mock_dev_data,'wb') as mock_file: mock_file.write(data) self.proxy.ClaimAccelerometer() self.assertEventually(lambda: self.have_text_in_log('Accel sent by driver')) # If the 2nd test fails, it's likely that fr_FR.UTF-8 locale isn't supported self.assertEqual(self.have_text_in_log('scale: 0,000000,0,000000,0,000000'), False) self.assertEqual(self.have_text_in_log('scale: 0,000010,0,000010,0,000010'), True) self.stop_daemon() def test_iio_scale_decimal_separator_offset(self): '''scale decimal separator with specific offset''' top_srcdir = os.getenv('top_srcdir', '.') mock_dev_data = self.testbed.get_root_dir() + '/iio-dev-data.bin' accel = self.testbed.add_device('iio', 'iio-buffer-accel0', None, ['name', 'IIO Test Accelerometer', 'buffer/enable', '0', 'trigger/current_trigger', '', 'in_accel_scale', '0.000010\n', 'in_accel_x_offset', '0.0\n', 'in_accel_mount_matrix', '1, 0, 0; 0, 1, 0; 0, 0, 1\n', 'scan_elements/in_accel_x_en', '0', 'scan_elements/in_accel_x_index', '0', 'scan_elements/in_accel_x_type', 'le:s16/32>>0', 'scan_elements/in_accel_y_en', '0', 'scan_elements/in_accel_y_index', '1', 'scan_elements/in_accel_y_type', 'le:s16/32>>0', 'scan_elements/in_accel_z_en', '0', 'scan_elements/in_accel_z_index', '2', 'scan_elements/in_accel_z_type', 'le:s16/32>>0', 'scan_elements/in_timestamp_en', '1', 'scan_elements/in_timestamp_index', '3', 'scan_elements/in_timestamp_type', 'le:s64/64>>0'], ['NAME', '"IIO Accelerometer"', 'DEVNAME', '/dev/iio-buffer-accel-test', 'IIO_SENSOR_PROXY_TYPE', 'iio-buffer-accel'] ) trigger = self.testbed.add_device('iio', 'trigger0', None, ['name', 'accel_3d-dev0'], [] ) env = os.environ.copy() env['LC_NUMERIC'] = 'fr_FR.UTF-8' self.start_daemon(env=env) self.assertEqual(self.get_dbus_property('HasAccelerometer'), True) self.stop_daemon() def test_sampling_freq_available_integer(self): 'Check whether the sampling frequency gets corrected to >= 10Hz, integer numbers sorted low to high' accel = self.testbed.add_device('iio', 'iio-accel', None, ['in_accel_x_raw', '0', 'in_accel_y_raw', '-256000000', 'in_accel_z_raw', '0', 'in_accel_scale', '0.000001', 'sampling_frequency', '5.2', # Real world example taken from LSM303DA accelerometer 'sampling_frequency_available', '3 6 12 25 50 100 200 400 800 1600', 'name', 'IIO Test Accelerometer'], ['NAME', '"IIO Accelerometer"', 'IIO_SENSOR_PROXY_TYPE', 'iio-poll-accel'] ) self.start_daemon() self.assertEqual(self.get_dbus_property('HasAccelerometer'), True) self.assertEqual(self.read_sysfs_attr(accel, 'sampling_frequency'), b'12') self.stop_daemon() def test_sampling_freq_available_double(self): 'Check whether the sampling frequency gets corrected to >= 10Hz, double numbers sorted high to low' accel = self.testbed.add_device('iio', 'iio-accel', None, ['in_accel_x_raw', '0', 'in_accel_y_raw', '-256000000', 'in_accel_z_raw', '0', 'in_accel_scale', '0.000001', 'sampling_frequency', '5.2', # Real world example taken from drivers/iio/accel/mma8452.c 'sampling_frequency_available', '800.000000 400.000000 200.000000 100.000000 50.000000 12.500000 6.250000 1.560000', 'name', 'IIO Test Accelerometer'], ['NAME', '"IIO Accelerometer"', 'IIO_SENSOR_PROXY_TYPE', 'iio-poll-accel'] ) self.start_daemon() self.assertEqual(self.get_dbus_property('HasAccelerometer'), True) self.assertEqual(self.read_sysfs_attr(accel, 'sampling_frequency'), b'12.5') self.stop_daemon() def test_iio_scale_decimal_separator2(self): '''scale decimal separator polling''' accel = self.testbed.add_device('iio', 'iio-accel', None, ['in_accel_x_raw', '0', 'in_accel_y_raw', '-256000000', 'in_accel_z_raw', '0', 'in_accel_scale', '0.000001', 'sampling_frequency', '5.2', 'name', 'IIO Test Accelerometer'], ['NAME', '"IIO Accelerometer"', 'IIO_SENSOR_PROXY_TYPE', 'iio-poll-accel'] ) env = os.environ.copy() env['LC_NUMERIC'] = 'fr_FR.UTF-8' self.start_daemon(env=env) self.proxy.ClaimAccelerometer() self.assertEventually(lambda: self.have_text_in_log('Accel read from IIO on')) # If the 2nd test fails, it's likely that fr_FR.UTF-8 locale isn't supported self.assertEqual(self.have_text_in_log('scale 1,000000,1,000000,1,000000'), False) self.assertEqual(self.have_text_in_log('scale 0,000001,0,000001,0,000001'), True) self.assertEventually(lambda: self.get_dbus_property('AccelerometerOrientation') == 'normal') self.testbed.set_attribute(accel, 'in_accel_x_raw', '-256000000') self.testbed.set_attribute(accel, 'in_accel_y_raw', '0') self.assertEventually(lambda: self.get_dbus_property('AccelerometerOrientation') == 'right-up') self.stop_daemon() # # Helper methods # @classmethod def _props_to_str(cls, properties): '''Convert a properties dictionary to uevent text representation.''' prop_str = '' if properties: for k, v in properties.items(): prop_str += '%s=%s\n' % (k, v) return prop_str if __name__ == '__main__': # run ourselves under umockdev if 'umockdev' not in os.environ.get('LD_PRELOAD', ''): os.execvp('umockdev-wrapper', ['umockdev-wrapper'] + sys.argv) unittest.main() iio-sensor-proxy-3.5/tests/meson.build000066400000000000000000000007761445770364600201650ustar00rootroot00000000000000envs = environment() envs.set ('top_builddir', meson.build_root()) envs.set ('top_srcdir', meson.source_root()) python3 = find_program('python3') unittest_inspector = find_program('unittest_inspector.py') r = run_command(unittest_inspector, files('integration-test.py'), check: true) unit_tests = r.stdout().strip().split('\n') foreach ut: unit_tests ut_args = files('integration-test.py') ut_args += ut test(ut, python3, args: ut_args, env: envs, ) endforeach iio-sensor-proxy-3.5/tests/unittest_inspector.py000077500000000000000000000031661445770364600223410ustar00rootroot00000000000000#! /usr/bin/env python3 # Copyright © 2020, Canonical Ltd # # This program 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 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 # 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, see . # Authors: # Marco Trevisan import argparse import importlib.util import inspect import os import unittest def list_tests(module): tests = [] for name, obj in inspect.getmembers(module): if inspect.isclass(obj) and issubclass(obj, unittest.TestCase): cases = unittest.defaultTestLoader.getTestCaseNames(obj) tests += [ (obj, '{}.{}'.format(name, t)) for t in cases ] return tests if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('unittest_source', type=argparse.FileType('r')) args = parser.parse_args() source_path = args.unittest_source.name spec = importlib.util.spec_from_file_location( os.path.basename(source_path), source_path) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) for machine, human in list_tests(module): print(human)