diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..4362b491 --- /dev/null +++ b/COPYING @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 00000000..27d5db90 --- /dev/null +++ b/Doxyfile @@ -0,0 +1,1514 @@ +# Doxyfile 1.5.9 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = "MCE (Internals)" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 1.10.x + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = NO + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it parses. +# With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this tag. +# The format is ext=language, where ext is a file extension, and language is one of +# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, +# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen to replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penality. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will rougly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by +# doxygen. The layout file controls the global structure of the generated output files +# in an output format independent way. The create the layout file that represents +# doxygen's defaults, run doxygen with the -l option. You can optionally specify a +# file name after the option, if omitted DoxygenLayout.xml will be used as the name +# of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = bme-dbus-names.h build-tests systemui + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = */search*.* + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER +# are set, an additional index file will be generated that can be used as input for +# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated +# HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. +# For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's +# filter section matches. +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 1 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to FRAME, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. Other possible values +# for this tag are: HIERARCHIES, which will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list; +# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which +# disables this behavior completely. For backwards compatibility with previous +# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE +# respectively. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# By default doxygen will write a font called FreeSans.ttf to the output +# directory and reference it in all dot files that doxygen generates. This +# font does not include all possible unicode characters however, so when you need +# these (or just want a differently looking font) you can specify the font name +# using DOT_FONTNAME. You need need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = FreeSans + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Options related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..a981fc32 --- /dev/null +++ b/Makefile @@ -0,0 +1,157 @@ +# Makefile for MCE +# Copyright © 2004-2010 Nokia Corporation. +# Written by David Weinehall +# Modified by Tuomo Tanskanen + +VERSION := 1.10.88 + +INSTALL := install -o root -g root --mode=755 +INSTALL_DIR := install -d +INSTALL_DATA := install -o root -g root --mode=644 + +DOXYGEN := doxygen + +VARDIR := $(DESTDIR)/var/lib/mce +RUNDIR := $(DESTDIR)/var/run/mce +CONFDIR := /etc/mce +CONFINSTDIR := $(DESTDIR)$(CONFDIR) +SBINDIR := $(DESTDIR)/sbin +MODULEDIR := $(DESTDIR)/usr/lib/mce/modules +DBUSDIR := $(DESTDIR)/etc/dbus-1/system.d +LOCALEDIR := $(DESTDIR)/usr/share/locale +GCONFSCHEMADIR := $(DESTDIR)/etc/gconf/schemas +BACKUPCONFDIR := $(DESTDIR)/usr/share/backup-framework/applications +BACKUPSCRIPTDIR := $(DESTDIR)/usr/share/mce + +TOPDIR := . +DOCDIR := $(TOPDIR)/doc +TOOLDIR := $(TOPDIR)/tools +TESTSDIR := $(TOPDIR)/tests +MODULE_DIR := $(TOPDIR)/modules + +TOOLS := \ + $(TOOLDIR)/mcetool +TESTS := \ + $(TESTSDIR)/mcetorture +TARGETS := \ + mce +MODULES := \ + $(MODULE_DIR)/libradiostates.so \ + $(MODULE_DIR)/libfilter-brightness-als.so \ + $(MODULE_DIR)/libfilter-brightness-simple.so \ + $(MODULE_DIR)/libproximity.so \ + $(MODULE_DIR)/libkeypad.so \ + $(MODULE_DIR)/libinactivity.so \ + $(MODULE_DIR)/libcamera.so \ + $(MODULE_DIR)/libalarm.so \ + $(MODULE_DIR)/libbattery.so \ + $(MODULE_DIR)/libdisplay.so \ + $(MODULE_DIR)/libled.so \ + $(MODULE_DIR)/libcallstate.so \ + $(MODULE_DIR)/libaudiorouting.so \ + $(MODULE_DIR)/libpowersavemode.so +ONLINERADIOSTATESFILE := radio_states.online +OFFLINERADIOSTATESFILE := radio_states.offline +CONFFILE := mce.ini +DBUSCONF := mce.conf mcetool.conf +GCONFSCHEMAS := display.schemas energymanagement.schemas +BACKUPCONF := mcebackup.conf +BACKUPSCRIPTS := mce-backup mce-restore + +WARNINGS := -Wextra -Wall -Wpointer-arith -Wundef -Wcast-align -Wshadow +WARNINGS += -Wbad-function-cast -Wwrite-strings -Wsign-compare +WARNINGS += -Waggregate-return -Wmissing-noreturn -Wnested-externs +WARNINGS += -Wchar-subscripts -Wmissing-prototypes -Wformat-security +WARNINGS += -Wformat=2 -Wformat-nonliteral -Winit-self +WARNINGS += -Wswitch-default -Wstrict-prototypes +WARNINGS += -Wdeclaration-after-statement +WARNINGS += -Wold-style-definition -Wmissing-declarations +WARNINGS += -Wmissing-include-dirs -Wstrict-aliasing=2 +WARNINGS += -Wunsafe-loop-optimizations -Winvalid-pch +WARNINGS += -Waddress -Wvolatile-register-var +WARNINGS += -Wmissing-format-attribute +#WARNINGS += -Wswitch-enum -Wunreachable-code +WARNINGS += -Wstack-protector +WARNINGS += -Werror # -std=c99 +WARNINGS += -Wno-declaration-after-statement + +COMMON_CFLAGS := -D_GNU_SOURCE +COMMON_CFLAGS += -I. $(WARNINGS) +COMMON_CFLAGS += -DG_DISABLE_DEPRECATED +COMMON_CFLAGS += -DOSSOLOG_COMPILE +COMMON_CFLAGS += -DMCE_VAR_DIR=$(VARDIR) -DMCE_RUN_DIR=$(RUNDIR) +COMMON_CFLAGS += -DPRG_VERSION=$(VERSION) +#COMMON_CFLAGS += -funit-at-a-time -fwhole-program -combine +#COMMON_CFLAGS += -fstack-protector + +MCE_CFLAGS := $(COMMON_CFLAGS) +MCE_CFLAGS += -DMCE_CONF_FILE=$(CONFDIR)/$(CONFFILE) +MCE_CFLAGS += $$(pkg-config gobject-2.0 glib-2.0 gio-2.0 gmodule-2.0 dbus-1 dbus-glib-1 gconf-2.0 conic sysinfo --cflags) +MCE_LDFLAGS := $$(pkg-config gobject-2.0 glib-2.0 gio-2.0 gmodule-2.0 dbus-1 dbus-glib-1 gconf-2.0 conic dsme sysinfo --libs) +LIBS := tklock.c modetransition.c powerkey.c connectivity.c mce-dbus.c mce-dsme.c mce-gconf.c event-input.c event-switches.c mce-hal.c mce-log.c mce-conf.c datapipe.c mce-modules.c mce-io.c mce-lib.c +HEADERS := tklock.h modetransition.h powerkey.h connectivity.h mce.h mce-dbus.h mce-dsme.h mce-gconf.h event-input.h event-switches.h mce-hal.h mce-log.h mce-conf.h datapipe.h mce-modules.h mce-io.h mce-lib.h + +MODULE_CFLAGS := $(COMMON_CFLAGS) +MODULE_CFLAGS += -fPIC -shared +MODULE_CFLAGS += -I. +MODULE_CFLAGS += $$(pkg-config gobject-2.0 glib-2.0 gmodule-2.0 dbus-1 dbus-glib-1 gconf-2.0 --cflags) +MODULE_LDFLAGS := $$(pkg-config gobject-2.0 glib-2.0 gmodule-2.0 dbus-1 dbus-glib-1 gconf-2.0 libcal --libs) +MODULE_LIBS := datapipe.c mce-hal.c mce-log.c mce-dbus.c mce-conf.c mce-gconf.c median_filter.c mce-lib.c +MODULE_HEADERS := datapipe.h mce-hal.h mce-log.h mce-dbus.h mce-conf.h mce-gconf.h mce.h median_filter.h mce-lib.h + +TOOLS_CFLAGS := $(COMMON_CFLAGS) +TOOLS_CFLAGS += -I. +TOOLS_CFLAGS += $$(pkg-config gobject-2.0 glib-2.0 dbus-1 gconf-2.0 --cflags) +TOOLS_LDFLAGS := $$(pkg-config gobject-2.0 glib-2.0 dbus-1 gconf-2.0 --libs) +TOOLS_HEADERS := tklock.h mce-dsme.h tools/mcetool.h + +.PHONY: all +all: $(TARGETS) $(MODULES) $(TOOLS) + +$(TARGETS): %: %.c $(HEADERS) $(LIBS) + @$(CC) $(CFLAGS) $(MCE_CFLAGS) -o $@ $< $(LIBS) $(LDFLAGS) $(MCE_LDFLAGS) + +$(MODULES): $(MODULE_DIR)/lib%.so: $(MODULE_DIR)/%.c $(MODULE_HEADERS) $(MODULE_LIBS) + @$(CC) $(CFLAGS) $(MODULE_CFLAGS) -o $@ $< $(MODULE_LIBS) $(LDFLAGS) $(MODULE_LDFLAGS) + +$(TOOLS): %: %.c $(TOOLS_HEADERS) + @$(CC) $(CFLAGS) $(TOOLS_CFLAGS) -o $@ $< $(LDFLAGS) mce-log.c $(TOOLS_LDFLAGS) + +.PHONY: tags +tags: + @find . $(MODULE_DIR) -maxdepth 1 -type f -name '*.[ch]' | xargs ctags -a --extra=+f + +.PHONY: doc +doc: + @$(DOXYGEN) 2> $(DOCDIR)/warnings > /dev/null + +.PHONY: install +install: all + $(INSTALL_DIR) $(SBINDIR) $(DBUSDIR) $(VARDIR) $(MODULEDIR) &&\ + $(INSTALL_DIR) $(RUNDIR) $(CONFINSTDIR) $(GCONFSCHEMADIR) &&\ + $(INSTALL_DIR) $(BACKUPCONFDIR) $(BACKUPSCRIPTDIR) &&\ + $(INSTALL) $(TARGETS) $(SBINDIR) &&\ + $(INSTALL) $(TOOLS) $(TESTS) $(SBINDIR) &&\ + $(INSTALL) $(MODULES) $(MODULEDIR) &&\ + $(INSTALL) $(BACKUPSCRIPTS) $(BACKUPSCRIPTDIR) &&\ + $(INSTALL_DATA) $(ONLINERADIOSTATESFILE) $(VARDIR) &&\ + $(INSTALL_DATA) $(OFFLINERADIOSTATESFILE) $(VARDIR) &&\ + $(INSTALL_DATA) $(CONFFILE) $(CONFINSTDIR) &&\ + $(INSTALL_DATA) $(GCONFSCHEMAS) $(GCONFSCHEMADIR) &&\ + $(INSTALL_DATA) $(DBUSCONF) $(DBUSDIR) &&\ + $(INSTALL_DATA) $(BACKUPCONF) $(BACKUPCONFDIR) + +.PHONY: fixme +fixme: + @find . -type f -name "*.[ch]" | xargs grep -E "FIXME|XXX|TODO" + +.PHONY: clean +clean: + @rm -f $(TARGETS) $(TOOLS) $(MODULES) + @if [ x"$(DOCDIR)" != x"" ] && [ -d "$(DOCDIR)" ]; then \ + rm -rf $(DOCDIR)/*; \ + fi + +.PHONY: distclean +distclean: clean + @rm -f tags diff --git a/TODO b/TODO new file mode 100644 index 00000000..9c7cc9fc --- /dev/null +++ b/TODO @@ -0,0 +1,107 @@ +GENERIC: + - stay-on doesn't seem to work?! + + - Handle display type = 0 properly! + + - Make ALS setup a 0-timeout read instead of using + als_read_value_filtered() + + - Replace use of *_submode_int32() with datapipe if feasible + + - Merge ALS and proximity sensor into modules/sensors.c + + - Fix event-input.c to use + /dev/input/{ts,keypad,jack,gpio-keys,pwr_button,mice} + where possible + + - Use g_try_malloc() instead of g_malloc() + + - Add support for new functionality to mcetorture + | power save mode + + - Add function to mce-hal to enumerate features for different + products (flicker key, home key, keyboard, etc.) + + - Add code to datapipe.c to ensure that datapipes never end up + called recursively + + - Audit the code to ensure that no datapipe triggers or filters + execute datapipes directly; if this cannot be avoided, it needs + to be verified to be safe + + - Use string pointer comparisons instead of strcmp() wherever + possible; be sure to internalise strings to enable this + + - Add option for proximity sensor policy both for incoming calls + and calls in progress + + - Add option to not use systemui tklock if kp + ts irqs are + disabled and immediate blanking is enabled + + - Before ALS is fully tuned or if brightness setting isn't + available, use 40% + + - Turn powerkey.c, tklock.c into proper modules + + - Unify lens cover and camera popout code + | Move camera popout detection to event-switches, + | and overload the lens cover pipe for this, thereby + | ensuring consistent behaviour across hardware. + | Also rename the configuration option CameraPopoutUnlock + | in mce.ini to something more sensible, and enable it + | by default + | keep the camera active code in the camera module + + - Update mce manual for GConf keys + + - Fix maximum_display_brightness in the filters + | Make it possible to use range 1- + | if the ALS has been disabled + || Get brightness range from GConf, convert to percentage, + || use in filter_brightness_simple + || Non-linearity can then be achieved simply by using a 1-100% + || range and only providing discrete steps in the app + + - Make minimum brightness for RX34 2% of maximum? + + - Add option to allow the keyboard/keypad backlight stay on as + long as the keyboard is open and the display is on + + - Find some way to make sure audio etc. is disabled + | maybe save old profile and request silent profile, + | then restore? + + +FIXME: + - mce-modules needs to make use of the module_info + + +ACTIVITY: + - Make activity sources configurable with path + values + instead of hardcoding them + + +HACKS: + - "Silent" tklock; no tklock UI, only disable touchscreen + and keypad (to allow mediaplayer to continue playing) + + +CONFIGURABILITY: + - Check ranges for return values + + +RELIABILITY: + - Check for superfluous D-Bus arguments + - Return proper D-Bus errors + + +DOCUMENTATION: + - Manual page for mce.ini + + +CLEANUP: + +OPEN ISSUES: + - Should we unref dbus-messages or not after an OOM? + + - Failure cases for D-Bus, GConf, etc.? diff --git a/bme-dbus-names.h b/bme-dbus-names.h new file mode 100644 index 00000000..1ccccec3 --- /dev/null +++ b/bme-dbus-names.h @@ -0,0 +1,37 @@ +/** + * @file bme-dbus-names.h + */ + +#ifndef _BME_DBUS_NAMES_H_ +#define _BME_DBUS_NAMES_H_ + +#define DBUS_BROADCAST "org.freedesktop.DBus.Broadcast" + +#define BME_SERVICE "com.nokia.bme" + +#define BME_REQUEST_IF "com.nokia.bme.request" +#define BME_REQUEST_PATH "/com/nokia/bme/request" + +#define BME_SIGNAL_IF "com.nokia.bme.signal" +#define BME_SIGNAL_PATH "/com/nokia/bme/signal" + +#define BME_ERROR_FATAL "com.nokia.bme.error.fatal" +#define BME_ERROR_INVALID_ARGS "org.freedesktop.DBus.Error.InvalidArgs" + +#define BME_BATTERY_STATE_UPDATE "battery_state_changed" +#define BME_BATTERY_FULL "battery_full" +#define BME_BATTERY_OK "battery_ok" +#define BME_BATTERY_LOW "battery_low" +#define BME_BATTERY_EMPTY "battery_empty" +#define BME_BATTERY_TIMELEFT "battery_timeleft" + +#define BME_CHARGER_CONNECTED "charger_connected" +#define BME_CHARGER_DISCONNECTED "charger_disconnected" +#define BME_CHARGER_CHARGING_ON "charger_charging_on" +#define BME_CHARGER_CHARGING_OFF "charger_charging_off" +#define BME_CHARGER_CHARGING_FAILED "charger_charging_failed" + +#define BME_STATUS_INFO_REQ "status_info_req" +#define BME_TIMELEFT_INFO_REQ "timeleft_info_req" + +#endif /*__BME_DBUS_NAMES_H_ */ diff --git a/connectivity.c b/connectivity.c new file mode 100644 index 00000000..f08ab90c --- /dev/null +++ b/connectivity.c @@ -0,0 +1,117 @@ +/** + * @file connectivity.c + * Connectivity logic for the Mode Control Entity + *

+ * Copyright © 2004-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include +#include /* g_object_set(), + * g_signal_connect(), + * G_OBJECT(), + * G_CALLBACK() + */ + +#include /* con_ic_connection_new(), + * con_ic_connection_event_get_status(), + * ConIcConnection, + * ConIcConnectionEvent, + * ConIcConnectionStatus, + * CON_IC_STATUS_CONNECTED + */ + +#include "connectivity.h" + +/** The connection object */ +static ConIcConnection *connection_object = NULL; + +/** The handler ID for the connection event signal handler */ +static gulong connection_event_handler_id = 0; + +/** Is there an open connection or not? */ +static gboolean connected = FALSE; + +/** + * Connection info handler + * + * @param connection Unused + * @param event The connection event + * @param user_data Unused + */ +static void connection_event_cb(ConIcConnection *connection, + ConIcConnectionEvent *event, + gpointer user_data) +{ + ConIcConnectionStatus status; + + (void)connection; + (void)user_data; + + status = con_ic_connection_event_get_status(event); + + connected = (status == CON_IC_STATUS_CONNECTED) ? TRUE : FALSE; +} + +/** + * Check connectivity status + * + * @return TRUE if there's an open connection, + * FALSE if there's no open connection + */ +gboolean get_connectivity_status(void) G_GNUC_PURE; +gboolean get_connectivity_status(void) +{ + return connected; +} + +/** + * Init function for the connectivity component + * + * @return TRUE on success, FALSE on failure + */ +gboolean mce_connectivity_init(void) +{ + /* Create connection object */ + connection_object = con_ic_connection_new(); + + /* Connect signal to receive connection events */ + connection_event_handler_id = + g_signal_connect(G_OBJECT(connection_object), + "connection-event", + G_CALLBACK(connection_event_cb), NULL); + + /* Set automatic events */ + g_object_set(G_OBJECT(connection_object), + "automatic-connection-events", TRUE, NULL); + + return TRUE; +} + +/** + * Exit function for the connectivity component + * + * @todo Unregister the connection events, etc. + */ +void mce_connectivity_exit(void) +{ + if (connection_event_handler_id != 0) { + g_signal_handler_disconnect(G_OBJECT(connection_object), + connection_event_handler_id); + connection_event_handler_id = 0; + } + + return; +} diff --git a/connectivity.h b/connectivity.h new file mode 100644 index 00000000..19b6e901 --- /dev/null +++ b/connectivity.h @@ -0,0 +1,31 @@ +/** + * @file connectivity.h + * Headers for the connectivity logic for the Mode Control Entity + *

+ * Copyright © 2007 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _CONNECTIVITY_H_ +#define _CONNECTIVITY_H_ + +#include + +gboolean get_connectivity_status(void); + +gboolean mce_connectivity_init(void); +void mce_connectivity_exit(void); + +#endif /* _CONNECTIVITY_H_ */ diff --git a/datapipe.c b/datapipe.c new file mode 100644 index 00000000..68c32e5f --- /dev/null +++ b/datapipe.c @@ -0,0 +1,615 @@ +/** + * @file datapipe.c + * This file implements the sinmple datapipe framework; + * this can be used to filter data and to setup data triggers + *

+ * Copyright © 2007-2008 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include + +#include "datapipe.h" + +#include "mce-log.h" /* mce_log(), LL_* */ + +/** + * Execute the input triggers of a datapipe + * + * @param datapipe The datapipe to execute + * @param indata The input data to run through the datapipe + * @param use_cache USE_CACHE to use data from cache, + * USE_INDATA to use indata + * @param cache_indata CACHE_INDATA to cache the indata, + * DONT_CACHE_INDATA to keep the old data + */ +void execute_datapipe_input_triggers(datapipe_struct *const datapipe, + gpointer const indata, + const data_source_t use_cache, + const caching_policy_t cache_indata) +{ + void (*trigger)(gconstpointer const input); + gpointer data; + gint i; + + if (datapipe == NULL) { + /* Potential memory leak! */ + mce_log(LL_ERR, + "execute_datapipe_input_triggers() called " + "without a valid datapipe"); + goto EXIT; + } + + data = (use_cache == USE_CACHE) ? datapipe->cached_data : indata; + + if (cache_indata == CACHE_INDATA) { + if (use_cache == USE_INDATA) { + if (datapipe->free_cache == FREE_CACHE) + g_free(datapipe->cached_data); + + datapipe->cached_data = data; + } + } + + for (i = 0; (trigger = g_slist_nth_data(datapipe->input_triggers, + i)) != NULL; i++) { + trigger(data); + } + +EXIT: + return; +} + +/** + * Execute the filters of a datapipe + * + * @param datapipe The datapipe to execute + * @param indata The input data to run through the datapipe + * @param use_cache USE_CACHE to use data from cache, + * USE_INDATA to use indata + * @return The processed data + */ +gconstpointer execute_datapipe_filters(datapipe_struct *const datapipe, + gpointer indata, + const data_source_t use_cache) +{ + gpointer (*filter)(gpointer input); + gpointer data; + gconstpointer retval = NULL; + gint i; + + if (datapipe == NULL) { + mce_log(LL_ERR, + "execute_datapipe_filters() called " + "without a valid datapipe"); + goto EXIT; + } + + data = (use_cache == USE_CACHE) ? datapipe->cached_data : indata; + + for (i = 0; (filter = g_slist_nth_data(datapipe->filters, + i)) != NULL; i++) { + gpointer tmp = filter(data); + + /* If the data needs to be freed, and this isn't the indata, + * or if we're not using the cache, then free the data + */ + if ((datapipe->free_cache == FREE_CACHE) && + ((i > 0) || (use_cache == USE_INDATA))) + g_free(data); + + data = tmp; + } + + retval = data; + +EXIT: + return retval; +} + +/** + * Execute the output triggers of a datapipe + * + * @param datapipe The datapipe to execute + * @param indata The input data to run through the datapipe + * @param use_cache USE_CACHE to use data from cache, + * USE_INDATA to use indata + */ +void execute_datapipe_output_triggers(const datapipe_struct *const datapipe, + gconstpointer indata, + const data_source_t use_cache) +{ + void (*trigger)(gconstpointer input); + gconstpointer data; + gint i; + + if (datapipe == NULL) { + mce_log(LL_ERR, + "execute_datapipe_output_triggers() called " + "without a valid datapipe"); + goto EXIT; + } + + data = (use_cache == USE_CACHE) ? datapipe->cached_data : indata; + + for (i = 0; (trigger = g_slist_nth_data(datapipe->output_triggers, + i)) != NULL; i++) { + trigger(data); + } + +EXIT: + return; +} + +/** + * Execute the datapipe + * + * @param datapipe The datapipe to execute + * @param indata The input data to run through the datapipe + * @param use_cache USE_CACHE to use data from cache, + * USE_INDATA to use indata + * @param cache_indata CACHE_INDATA to cache the indata, + * DONT_CACHE_INDATA to keep the old data + * @return The processed data + */ +gconstpointer execute_datapipe(datapipe_struct *const datapipe, + gpointer indata, + const data_source_t use_cache, + const caching_policy_t cache_indata) +{ + gconstpointer data = NULL; + + if (datapipe == NULL) { + mce_log(LL_ERR, + "execute_datapipe() called " + "without a valid datapipe"); + goto EXIT; + } + + execute_datapipe_input_triggers(datapipe, indata, use_cache, + cache_indata); + + if (datapipe->read_only == READ_ONLY) { + data = indata; + } else { + data = execute_datapipe_filters(datapipe, indata, use_cache); + } + + execute_datapipe_output_triggers(datapipe, data, USE_INDATA); + +EXIT: + return data; +} + +/** + * Append a filter to an existing datapipe + * + * @param datapipe The datapipe to manipulate + * @param filter The filter to add to the datapipe + */ +void append_filter_to_datapipe(datapipe_struct *const datapipe, + gpointer (*filter)(gpointer data)) +{ + void (*refcount_trigger)(void); + gint i; + + if (datapipe == NULL) { + mce_log(LL_ERR, + "append_filter_to_datapipe() called " + "without a valid datapipe"); + goto EXIT; + } + + if (filter == NULL) { + mce_log(LL_ERR, + "append_filter_to_datapipe() called " + "without a valid filter"); + goto EXIT; + } + + if (datapipe->read_only == READ_ONLY) { + mce_log(LL_ERR, + "append_filter_to_datapipe() called " + "on read only datapipe"); + goto EXIT; + } + + datapipe->filters = g_slist_append(datapipe->filters, filter); + + for (i = 0; (refcount_trigger = g_slist_nth_data(datapipe->refcount_triggers, i)) != NULL; i++) { + refcount_trigger(); + } + +EXIT: + return; +} + +/** + * Remove a filter from an existing datapipe + * Non-existing filters are ignored + * + * @param datapipe The datapipe to manipulate + * @param filter The filter to remove from the datapipe + */ +void remove_filter_from_datapipe(datapipe_struct *const datapipe, + gpointer (*filter)(gpointer data)) +{ + void (*refcount_trigger)(void); + guint oldlen; + gint i; + + if (datapipe == NULL) { + mce_log(LL_ERR, + "remove_filter_from_datapipe() called " + "without a valid datapipe"); + goto EXIT; + } + + if (filter == NULL) { + mce_log(LL_ERR, + "remove_filter_from_datapipe() called " + "without a valid filter"); + goto EXIT; + } + + if (datapipe->read_only == READ_ONLY) { + mce_log(LL_ERR, + "remove_filter_from_datapipe() called " + "on read only datapipe"); + goto EXIT; + } + + oldlen = g_slist_length(datapipe->filters); + + datapipe->filters = g_slist_remove(datapipe->filters, filter); + + /* Did we remove any entry? */ + if (oldlen == g_slist_length(datapipe->filters)) { + mce_log(LL_DEBUG, + "Trying to remove non-existing filter"); + goto EXIT; + } + + for (i = 0; (refcount_trigger = g_slist_nth_data(datapipe->refcount_triggers, i)) != NULL; i++) { + refcount_trigger(); + } + +EXIT: + return; +} + +/** + * Append an input trigger to an existing datapipe + * + * @param datapipe The datapipe to manipulate + * @param trigger The trigger to add to the datapipe + */ +void append_input_trigger_to_datapipe(datapipe_struct *const datapipe, + void (*trigger)(gconstpointer data)) +{ + void (*refcount_trigger)(void); + gint i; + + if (datapipe == NULL) { + mce_log(LL_ERR, + "append_input_trigger_to_datapipe() called " + "without a valid datapipe"); + goto EXIT; + } + + if (trigger == NULL) { + mce_log(LL_ERR, + "append_input_trigger_to_datapipe() called " + "without a valid trigger"); + goto EXIT; + } + + datapipe->input_triggers = g_slist_append(datapipe->input_triggers, + trigger); + + for (i = 0; (refcount_trigger = g_slist_nth_data(datapipe->refcount_triggers, i)) != NULL; i++) { + refcount_trigger(); + } + +EXIT: + return; +} + +/** + * Remove an input trigger from an existing datapipe + * Non-existing triggers are ignored + * + * @param datapipe The datapipe to manipulate + * @param trigger The trigger to remove from the datapipe + */ +void remove_input_trigger_from_datapipe(datapipe_struct *const datapipe, + void (*trigger)(gconstpointer data)) +{ + void (*refcount_trigger)(void); + guint oldlen; + gint i; + + if (datapipe == NULL) { + mce_log(LL_ERR, + "remove_input_trigger_from_datapipe() called " + "without a valid datapipe"); + goto EXIT; + } + + if (trigger == NULL) { + mce_log(LL_ERR, + "remove_input_trigger_from_datapipe() called " + "without a valid trigger"); + goto EXIT; + } + + oldlen = g_slist_length(datapipe->input_triggers); + + datapipe->input_triggers = g_slist_remove(datapipe->input_triggers, + trigger); + + /* Did we remove any entry? */ + if (oldlen == g_slist_length(datapipe->input_triggers)) { + mce_log(LL_DEBUG, + "Trying to remove non-existing input trigger"); + goto EXIT; + } + + for (i = 0; (refcount_trigger = g_slist_nth_data(datapipe->refcount_triggers, i)) != NULL; i++) { + refcount_trigger(); + } + +EXIT: + return; +} + +/** + * Append an output trigger to an existing datapipe + * + * @param datapipe The datapipe to manipulate + * @param trigger The trigger to add to the datapipe + */ +void append_output_trigger_to_datapipe(datapipe_struct *const datapipe, + void (*trigger)(gconstpointer data)) +{ + void (*refcount_trigger)(void); + gint i; + + if (datapipe == NULL) { + mce_log(LL_ERR, + "append_output_trigger_to_datapipe() called " + "without a valid datapipe"); + goto EXIT; + } + + if (trigger == NULL) { + mce_log(LL_ERR, + "append_output_trigger_to_datapipe() called " + "without a valid trigger"); + goto EXIT; + } + + datapipe->output_triggers = g_slist_append(datapipe->output_triggers, + trigger); + + for (i = 0; (refcount_trigger = g_slist_nth_data(datapipe->refcount_triggers, i)) != NULL; i++) { + refcount_trigger(); + } + +EXIT: + return; +} + +/** + * Remove an output trigger from an existing datapipe + * Non-existing triggers are ignored + * + * @param datapipe The datapipe to manipulate + * @param trigger The trigger to remove from the datapipe + */ +void remove_output_trigger_from_datapipe(datapipe_struct *const datapipe, + void (*trigger)(gconstpointer data)) +{ + void (*refcount_trigger)(void); + guint oldlen; + gint i; + + if (datapipe == NULL) { + mce_log(LL_ERR, + "remove_output_trigger_from_datapipe() called " + "without a valid datapipe"); + goto EXIT; + } + + if (trigger == NULL) { + mce_log(LL_ERR, + "remove_output_trigger_from_datapipe() called " + "without a valid trigger"); + goto EXIT; + } + + oldlen = g_slist_length(datapipe->output_triggers); + + datapipe->output_triggers = g_slist_remove(datapipe->output_triggers, + trigger); + + /* Did we remove any entry? */ + if (oldlen == g_slist_length(datapipe->output_triggers)) { + mce_log(LL_DEBUG, + "Trying to remove non-existing output trigger"); + goto EXIT; + } + + for (i = 0; (refcount_trigger = g_slist_nth_data(datapipe->refcount_triggers, i)) != NULL; i++) { + refcount_trigger(); + } + +EXIT: + return; +} + +/** + * Append a reference count trigger to an existing datapipe + * + * @param datapipe The datapipe to manipulate + * @param trigger The trigger to add to the datapipe + */ +void append_refcount_trigger_to_datapipe(datapipe_struct *const datapipe, + void (*trigger)(void)) +{ + if (datapipe == NULL) { + mce_log(LL_ERR, + "append_refcount_trigger_to_datapipe() called " + "without a valid datapipe"); + goto EXIT; + } + + if (trigger == NULL) { + mce_log(LL_ERR, + "append_refcount_trigger_to_datapipe() called " + "without a valid trigger"); + goto EXIT; + } + + datapipe->refcount_triggers = g_slist_append(datapipe->refcount_triggers, trigger); + +EXIT: + return; +} + +/** + * Remove a reference count trigger from an existing datapipe + * Non-existing triggers are ignored + * + * @param datapipe The datapipe to manipulate + * @param trigger The trigger to remove from the datapipe + */ +void remove_refcount_trigger_from_datapipe(datapipe_struct *const datapipe, + void (*trigger)(void)) +{ + guint oldlen; + + if (datapipe == NULL) { + mce_log(LL_ERR, + "remove_refcount_trigger_from_datapipe() called " + "without a valid datapipe"); + goto EXIT; + } + + if (trigger == NULL) { + mce_log(LL_ERR, + "remove_refcount_trigger_from_datapipe() called " + "without a valid trigger"); + goto EXIT; + } + + oldlen = g_slist_length(datapipe->refcount_triggers); + + datapipe->refcount_triggers = g_slist_remove(datapipe->refcount_triggers, trigger); + + /* Did we remove any entry? */ + if (oldlen == g_slist_length(datapipe->refcount_triggers)) { + mce_log(LL_DEBUG, + "Trying to remove non-existing refcount trigger"); + goto EXIT; + } + +EXIT: + return; +} + +/** + * Initialise a datapipe + * + * @param datapipe The datapipe to manipulate + * @param read_only READ_ONLY if the datapipe is read only, + * READ_WRITE if it's read/write + * @param free_cache FREE_CACHE if the cached data needs to be freed, + * DONT_FREE_CACHE if the cache data should not be freed + * @param datasize Pass size of memory to copy, + * or 0 if only passing pointers or data as pointers + * @param initial_data Initial cache content + */ +void setup_datapipe(datapipe_struct *const datapipe, + const read_only_policy_t read_only, + const cache_free_policy_t free_cache, + const gsize datasize, gpointer initial_data) +{ + if (datapipe == NULL) { + mce_log(LL_ERR, + "setup_datapipe() called " + "without a valid datapipe"); + goto EXIT; + } + + datapipe->filters = NULL; + datapipe->input_triggers = NULL; + datapipe->output_triggers = NULL; + datapipe->refcount_triggers = NULL; + datapipe->datasize = datasize; + datapipe->read_only = read_only; + datapipe->free_cache = free_cache; + datapipe->cached_data = initial_data; + +EXIT: + return; +} + +/** + * Deinitialize a datapipe + * + * @param datapipe The datapipe to manipulate + */ +void free_datapipe(datapipe_struct *const datapipe) +{ + if (datapipe == NULL) { + mce_log(LL_ERR, + "free_datapipe() called " + "without a valid datapipe"); + goto EXIT; + } + + /* Warn about still registered filters/triggers */ + if (datapipe->filters != NULL) { + mce_log(LL_INFO, + "free_datapipe() called on a datapipe that " + "still has registered filter(s)"); + } + + if (datapipe->input_triggers != NULL) { + mce_log(LL_INFO, + "free_datapipe() called on a datapipe that " + "still has registered input_trigger(s)"); + } + + if (datapipe->output_triggers != NULL) { + mce_log(LL_INFO, + "free_datapipe() called on a datapipe that " + "still has registered output_trigger(s)"); + } + + if (datapipe->refcount_triggers != NULL) { + mce_log(LL_INFO, + "free_datapipe() called on a datapipe that " + "still has registered refcount_trigger(s)"); + } + + if (datapipe->free_cache == FREE_CACHE) { + g_free(datapipe->cached_data); + } + +EXIT: + return; +} diff --git a/datapipe.h b/datapipe.h new file mode 100644 index 00000000..64931980 --- /dev/null +++ b/datapipe.h @@ -0,0 +1,144 @@ +/** + * @file datapipe.h + * Headers for the simple filter framework + *

+ * Copyright © 2007 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _DATAPIPE_H_ +#define _DATAPIPE_H_ + +#include + +/** + * Datapipe structure + * + * Only access this struct through the functions + */ +typedef struct { + GSList *filters; /**< The filters */ + GSList *input_triggers; /**< Triggers called on indata */ + GSList *output_triggers; /**< Triggers called on outdata */ + GSList *refcount_triggers; /**< Triggers called on + * reference count changes + */ + gpointer cached_data; /**< Latest cached data */ + gsize datasize; /**< Size of data; NULL == automagic */ + gboolean free_cache; /**< Free the cache? */ + gboolean read_only; /**< Datapipe is read only */ +} datapipe_struct; + +/** + * Read only policy type + */ +typedef enum { + READ_WRITE = FALSE, /**< The pipe is read/write */ + READ_ONLY = TRUE /**< The pipe is read only */ +} read_only_policy_t; + +/** + * Policy used for the cache when freeing a datapipe + */ +typedef enum { + DONT_FREE_CACHE = FALSE, /**< Don't free the cache */ + FREE_CACHE = TRUE /**< Free the cache */ +} cache_free_policy_t; + +/** + * Policy for the data source + */ +typedef enum { + USE_INDATA = FALSE, /**< Use the indata as data source */ + USE_CACHE = TRUE /**< Use the cache as data source */ +} data_source_t; + +/** + * Policy used for caching indata + */ +typedef enum { + DONT_CACHE_INDATA = FALSE, /**< Do not cache the indata */ + CACHE_INDATA = TRUE /**< Cache the indata */ +} caching_policy_t; + +/* Data retrieval */ + +/** Retrieve a gboolean from a datapipe */ +#define datapipe_get_gbool(_datapipe) (GPOINTER_TO_INT((_datapipe).cached_data)) +/** Retrieve a gint from a datapipe */ +#define datapipe_get_gint(_datapipe) (GPOINTER_TO_INT((_datapipe).cached_data)) +/** Retrieve a guint from a datapipe */ +#define datapipe_get_guint(_datapipe) (GPOINTER_TO_UINT((_datapipe).cached_data)) +/** Retrieve a gsize from a datapipe */ +#define datapipe_get_gsize(_datapipe) (GPOINTER_TO_SIZE((_datapipe).cached_data)) +/** Retrieve a gpointer from a datapipe */ +#define datapipe_get_gpointer(_datapipe) ((_datapipe).cached_data) + +/* Reference count */ + +/** Retrieve the filter reference count from a datapipe */ +#define datapipe_get_filter_refcount(_datapipe) (g_slist_length((_datapipe).filters)) +/** Retrieve the input trigger reference count from a datapipe */ +#define datapipe_get_input_trigger_refcount(_datapipe) (g_slist_length((_datapipe).input_triggers)) +/** Retrieve the output trigger reference count from a datapipe */ +#define datapipe_get_output_trigger_refcount(_datapipe) (g_slist_length((_datapipe).output_triggers)) + +/* Datapipe execution */ +void execute_datapipe_input_triggers(datapipe_struct *const datapipe, + gpointer const indata, + const data_source_t use_cache, + const caching_policy_t cache_indata); +gconstpointer execute_datapipe_filters(datapipe_struct *const datapipe, + gpointer indata, + const data_source_t use_cache); +void execute_datapipe_output_triggers(const datapipe_struct *const datapipe, + gconstpointer indata, + const data_source_t use_cache); +gconstpointer execute_datapipe(datapipe_struct *const datapipe, + gpointer indata, + const data_source_t use_cache, + const caching_policy_t cache_indata); + +/* Filters */ +void append_filter_to_datapipe(datapipe_struct *const datapipe, + gpointer (*filter)(gpointer data)); +void remove_filter_from_datapipe(datapipe_struct *const datapipe, + gpointer (*filter)(gpointer data)); + +/* Input triggers */ +void append_input_trigger_to_datapipe(datapipe_struct *const datapipe, + void (*trigger)(gconstpointer data)); +void remove_input_trigger_from_datapipe(datapipe_struct *const datapipe, + void (*trigger)(gconstpointer data)); + +/* Output triggers */ +void append_output_trigger_to_datapipe(datapipe_struct *const datapipe, + void (*trigger)(gconstpointer data)); +void remove_output_trigger_from_datapipe(datapipe_struct *const datapipe, + void (*trigger)(gconstpointer data)); + +/* Reference count triggers */ +void append_refcount_trigger_to_datapipe(datapipe_struct *const datapipe, + void (*trigger)(void)); +void remove_refcount_trigger_from_datapipe(datapipe_struct *const datapipe, + void (*trigger)(void)); + +void setup_datapipe(datapipe_struct *const datapipe, + const read_only_policy_t read_only, + const cache_free_policy_t free_cache, + const gsize datasize, gpointer initial_data); +void free_datapipe(datapipe_struct *const datapipe); + +#endif /* _DATAPIPE_H_ */ diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 00000000..62954309 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,4040 @@ +mce (1.10.88) unstable; urgency=low + + * Added backup/restore support for the radio state files + (Fixes: NB#190561) + * Disabled triggering of unlock screen with volume keys by default + (Fixes: NB#209963) + + -- David Weinehall Thu, 16 Dec 2010 08:30:17 +0200 + +mce (1.10.87) unstable; urgency=low + + * Modified audiorouting.c to support "headsetforcall" + (Fixes: NB#207998) + + -- David Weinehall Tue, 30 Nov 2010 11:31:20 +0200 + +mce (1.10.86) unstable; urgency=low + + * Improved the proximity sensor related tklock logic + (Fixes: NB#207731, NB#207771) + * Updated debian/copyright. We're finally going LGPL. Yay! + + -- David Weinehall Mon, 29 Nov 2010 15:39:11 +0200 + +mce (1.10.85) unstable; urgency=low + + * Request needed credentials to be able to inhibit the DeviceLock + and be more picky about the D-Bus interface (Fixes: NB#207449) + * Disabled [power] button completely when the alarm UI is visible + (Fixes: NB#206536) + + -- David Weinehall Fri, 26 Nov 2010 11:34:05 +0200 + +mce (1.10.84) unstable; urgency=low + + * Free the LED control paths after disabling the LED, + since these paths are needed when disabling (Fixes: NB#205024) + * Remove dependency on system-services due to broken system-wide + policy to treat system-services as a base package even though it isn't + (Fixes: NB#207286) + * Added --no-logging option to mcetorture + * Modified mcetorture to fallback to /root as logdir + if /media/mmc1 doesn't exist + + -- David Weinehall Thu, 25 Nov 2010 11:15:37 +0200 + +mce (1.10.83) unstable; urgency=low + + * Properly define SensorControl token + + -- David Weinehall Fri, 19 Nov 2010 16:39:32 +0200 + +mce (1.10.82) unstable; urgency=low + + * Corrected incorrect logic for the [power] button / alarm UI logic + (Fixes: NB#204426, NB#204770) + * Added new input layer name for the jack sense (Fixes: NB#205555) + + -- David Weinehall Thu, 18 Nov 2010 15:10:08 +0200 + +mce (1.10.81) unstable; urgency=low + + * Added internal API (for SystemSW use only) for enabling/disabling + proximity sensor (Fixes: NB#200345) + + -- David Weinehall Wed, 17 Nov 2010 15:43:58 +0200 + +mce (1.10.80) unstable; urgency=low + + * Do not register match rules for method calls (Fixes: NB#204093) + + -- David Weinehall Fri, 12 Nov 2010 18:17:19 +0200 + +mce (1.10.79) unstable; urgency=low + + * Don't use tklock UI for proximity locking (Fixes: NB#200814) + * Fixed missing Doxygen documentation + + -- David Weinehall Tue, 02 Nov 2010 12:16:50 +0200 + +mce (1.10.78) unstable; urgency=low + + * Request cal credentials for mce + + -- David Weinehall Mon, 01 Nov 2010 12:09:06 +0200 + +mce (1.10.77) unstable; urgency=low + + * Disabled [power] button shutdown when the alarm UI is visible + * Added support for adaptive dimming to mcetool and mcetorture + * Removed support for the deprecated device mode interface + from mce, mcetool and mcetorture + * ALS profile adjustments + + -- David Weinehall Mon, 25 Oct 2010 11:50:08 +0300 + +mce (1.10.76) unstable; urgency=low + + * Do not generate activity if tklock or event eater fails to open + (Fixes: NB#192925, NB#195008, NB#196871) + + -- David Weinehall Thu, 21 Oct 2010 06:37:05 +0300 + +mce (1.10.75) unstable; urgency=low + + * Don't disable the charging and battery full patterns on bootup + from acting dead (Fixes: NB#196840) + * Fixed disable_led() to work for the monochrome NJoy LED case too + * Fixed led_update_active_pattern() to handle patterns with policy 5 + in a correct manner + * Request I2C credentials for mce, to ensure that we can interface with Reno + * Fixed stupid typo in disable_reno() + * Don't install all of /etc recursively to the mce package, + only install /etc/mce and /etc/gconf + + -- David Weinehall Wed, 20 Oct 2010 03:34:29 +0300 + +mce (1.10.74) unstable; urgency=low + + * Do not blank unconditionally when switching to acting dead, + since the shutdown splashscreen needs to be shown first; + the splashscreen is then responsible for dimming the screen + (Fixes: NB#190207, NB#192008) + * If the screen is blank when there's a display dim request, + set the dim brightness immediately instead of fading + + -- David Weinehall Tue, 19 Oct 2010 00:59:00 +0300 + +mce (1.10.73) unstable; urgency=low + + * Fixed ALS thresholds to work properly even on devices that lack + keyboard backlight or LEDs + * Fixed inverted values in proximity sensor code for RM-696 + * Adjusted the ALS steps a bit for RM-696 + * Ignore tuning for the highest brightness setting and simply + use full brightness instead + * Handle saturated ALS reads in a better manner + * Enabled the volume key trigger option + * Modified mce-hal to use libsysinfo instead of /proc/component_version + to get the product ID, since the latter is deprecated + * Removed useless call to get_ps_type() + + -- David Weinehall Fri, 15 Oct 2010 16:06:36 +0300 + +mce (1.10.72) unstable; urgency=low + + * Corrected the default PSM threshold (Fixes: NB#194693) + * Added Aegis manifest for mcetools (Fixes: NB#195058) + | Thanks to Sergey Yakubenko + * Fixed an issue with the mce Aegis manifest (Fixes: NB#195912) + * Added an option to have the volume keys trigger the visual tklock + + -- David Weinehall Tue, 05 Oct 2010 13:38:11 +0300 + +mce (1.10.71) unstable; urgency=low + + * Acknowledge NMU + * Fixed another radio states related issue (Fixes: NB#192569) + + -- David Weinehall Wed, 29 Sep 2010 13:04:28 +0300 + +mce (1.10.70+nmu1) unstable; urgency=low + + * Implemented preliminary Avago ALS and Proximity support for MCE + + -- Tuomo Tanskanen Wed, 22 Sep 2010 18:03:15 +0300 + +mce (1.10.70) unstable; urgency=low + + * Make sure we always synthesise activity when disabling the tklock + and synthesise inactivity when enabling the tklock (Fixes: NB#190599) + * Added reply handlers for device lock inhibit and tklock + * Always disable the tklock if communication with tklock fails + * Always request the tklock UI to be opened on unblank if the tklock + is active; if it's already open and working this will be a no-op + * Properly consume all arguments for battery_state_changed in fakecharger + * Modified the makefile to use "." instead of "$(shell /bin/pwd)" + as $(TOPDIR); this prevents the build-directory from cluttering the + log messages from modules + + -- David Weinehall Fri, 10 Sep 2010 10:04:30 +0300 + +mce (1.10.69) unstable; urgency=low + + * Hopefully finally fixed the radio states bugs for real + (Fixes: NB#181863, NB#183833) + * Added support for ACPI-controlled displays, to ease testing on regular PCs + * Removed unused GConf key from display.schemas + + -- David Weinehall Tue, 31 Aug 2010 19:25:57 +0300 + +mce (1.10.68) unstable; urgency=low + + * Modified the first dim on bootup to rely on the desktop_visible + signal from system-services instead of the readyNotifier from + duihome (Fixes: NB#186466) + * Depends: system-services (>= 0.123) + * debian/control: Bumped Standards-Version to 3.8.4 + | Don't install /var/run/mce; it's dynamically created by the mce + | upstart script in system-services + * Added support for new display driver sysfs path + * Fixed dim timeouts to add the additional bootup dim timeout + also when adaptive dim timeouts are in use + * Ignore requests to enable tklock during bootup + + -- David Weinehall Thu, 26 Aug 2010 17:06:24 +0300 + +mce (1.10.67) unstable; urgency=low + + * Corrected the default for proximity lock when ringing + and added workaround for incorrect call-ui behaviour + (Fixes: NB#179569, NB#187027, NB#183740) + * Don't filter out keyboard backlight ALS changes + if the keyboard backlight is disabled; we still need to change + the ALS thresholds and backlight values, since it might be that + the backlight is disabled due to the light conditions rather than + the keyboard state + * If the ALS threshold values have never been set before, + don't filter out identical reads + * Fixed various issues found by coverity + + -- David Weinehall Tue, 24 Aug 2010 17:08:44 +0300 + +mce (1.10.66) unstable; urgency=low + + * Fixed copy'n'paste error in the ALS code + + -- David Weinehall Fri, 20 Aug 2010 13:06:43 +0300 + +mce (1.10.65) unstable; urgency=low + + * Adjusted some of the ALS behaviour: + - Only update the threshold values once every interrupt, + not once per user of the ALS reading (display, LED, keyboard backlight) + - Only adjust the threshold values if we have something registered to + monitor/poll the reads + + -- David Weinehall Thu, 19 Aug 2010 15:32:09 +0300 + +mce (1.10.64) unstable; urgency=low + + * Added default permissive rule to the Aegis manifest (Fixes: NB#185303) + * Store both the set of active radio states and the online radio states + (Fixes: NB#183833) + * Fixed the enabling/disabling of individual radios when the master + radio isn't altered + * Regard the device as inactive signal as soon as the tklock is enabled + | This might need some tuning + * Disabled [power] double click to unlock + * Added support to send a D-Bus signal as [power] action + + -- David Weinehall Wed, 18 Aug 2010 13:03:17 +0300 + +mce (1.10.63) unstable; urgency=low + + * Call the device lock inhibit function whenever display blanking + is inhibited from another process + + -- David Weinehall Thu, 22 Jul 2010 14:37:29 +0300 + +mce (1.10.62) unstable; urgency=low + + * Default WLAN radio to enabled (Fixes: NB#181254) + * Only trigger visual tklock when pressing [power] in user state + or when an alarm dialog is visible + * Setup visual tklock timeout also when calling unblank when the tklock + is active + * Added dbus_get_pid_from_bus_name(); this function can be used when + debugging to find out what process has made a particular D-Bus request + * Fixed some doxygen comments + + -- David Weinehall Thu, 22 Jul 2010 13:55:38 +0300 + +mce (1.10.61) unstable; urgency=low + + * Default cellular radio to enabled (Fixes: NB#180894) + * Fixed radio states mask handling to work properly + * Fixed copy'n'paste bug in mcetool + + -- David Weinehall Tue, 20 Jul 2010 15:38:32 +0300 + +mce (1.10.60) unstable; urgency=low + + * Added Aegis manifest file + * Modified the radio states behaviour to allow changing + individual radio states even when the master radio is off + * Updated the radio states module and mcetool + for the revised radio states interface + | Build-Depends: mce-dev (>= 1.10.21) + * Changed the default radio_states file to only have the master radio + enabled + * Fixed incorrect error check in mcetool + * Removed the no_reply flag from mcetool_dbus_call_bool() + and mcetool_dbus_call_uint(), since the reply is always used + * Added configuration option to allow proximity based locking + to take place even when the keyboard slide is open + * Fixed fakealarm to immitate reminder service instead of the old + system ui alarm + * Fixed fakecharger to use the proper types for battery_state_changed + * debian/control: Bumped Standards-Version to 3.8.0 + | No changes required + + -- David Weinehall Fri, 16 Jul 2010 14:53:33 +0300 + +mce (1.10.59) unstable; urgency=low + + * Changed default CABC-mode to "ui" (Fixes: NB#169432) + * Only generate activity when opening the keyboard slide + and lens cover, not when closing them (Fixes: NB#176362) + * Keep the display off when we're in acting dead and no alarm + is visible (Fixes: NB#177690) + * Fixed memory leaks in dir_changed_cb() + * Fixed memory leaks in two error paths of io_chunk_cb() + * Added check for proper termination of ALS profiles in filter_data() + | Found by coverity + * Added proper error handling in disable_reno() + | Found by coverity + * Added proper error handling in backlight_ioctl() + * Trigger visual tklock screen on jack sense and on + connecting/disconnecting a usb-cable + * Fixed the keyboard backlight fade-out to work properly + * Added keyboard backlight fade-in + * Don't try to read from the old proximity sensor state path + if it doesn't exist + * Made update_proximity_monitor() exit if the proximity sensor type + is PS_TYPE_NONE + * Major rewrite of the LED module + - Remove support for old LP5521 and LP5523 drivers + (for RX-44 and RX-51, respectively); initial support has been added + to support them with the new driver instead, but that obviously + necessitates a backport of the new kernel driver + - Support the monochrome LED in RM-696 + - Removed some of the define-madness from modules/led.h + by generating the paths runtime instead + - Removed model name LED support + * Remove unused code for acting dead screen and splashscreens + * Added configuration option to always enable tklock when keyboard slide + is closed + * Lots of improvements to mcetorture: + - Added support for cancel blank prevent + - Improved the dbus-errors testcase + - Fixed the battery testcase + - Fixed some incorrect event codes + (counting in octal has never been my cup of tea) + - Properly handle the case where mce is stopped/crashes + without being restarted + - Removed event-errors testcase since it was useless + - Adjusted sleeps in various testcases to avoid false positives + * Updated mcetorture manual page + + -- David Weinehall Mon, 12 Jul 2010 16:13:06 +0300 + +mce (1.10.58) unstable; urgency=low + + * Updated alarm module to get alarm status from reminder service + | Partial fix for NB#176632; the rest is done elsewhere + * Modified alarm testcase in mcetorture to imitate reminder service + + -- David Weinehall Wed, 07 Jul 2010 14:11:45 +0300 + +mce (1.10.57) unstable; urgency=low + + * Disable Reno-based LED-control on startup, to avoid conflicts with Lysti + + -- David Weinehall Wed, 16 Jun 2010 21:05:03 +0300 + +mce (1.10.56) unstable; urgency=low + + * Fixed botched use of mce_close_file() in + mce_write_number_string_to_file_atomic() + * Worked around issue where tklock doesn't always close + when the keyboard is opened (Fixes: NB#175119) + * Removed the "-dialog" options from the help-text in mcetool + + -- David Weinehall Wed, 16 Jun 2010 19:14:52 +0300 + +mce (1.10.55) unstable; urgency=low + + * Added mce_close_file() which abstracts file closing with proper + error messages, NULL-check, etc. + * Use mce_close_file() for closing files where appropriate; + fixes some issues when shutting down mce + * Added option to mcetool to cancel blank prevent request + * Added option to mce to show module information + * Updated mcetool manual page + * Updated mce manual page + + -- David Weinehall Wed, 16 Jun 2010 07:36:53 +0300 + +mce (1.10.54) unstable; urgency=low + + * Tuned the keyboard backlight ALS profile for RM-680 (Fixes: NB#174192) + * Made tklock show up immediately when pressing [power] (Fixes: NB#173595) + * Close file on error in mce_read_number_string_from_file() + | Found by coverity + * Close file on error in mce_write_number_string_to_file() + | Found by coverity + * Fixed a memory leak in mce-io.c + * Fixed a memory leak in event-input.c + * Fixed a memory leak in powerkey.c + * Added support for ALS threshold ranges to minimise wakeups + * Added mce_write_number_string_to_file_atomic() to mce-io + * Modified the radio state module to use + mce_write_number_string_to_file_atomic() for writing the radio states + * Shutdown I/O channel for dsmesock when exiting + * Allow explicit D-Bus unblank/dim requests even when the tklock is active + * Only unblank on device activity when the tklock is disabled + * Always use explicit unblanking when the tklock is active + * Made req_trigger_powerkey_event optionally accept dbus_uint32_t + instead of dbus_bool_t, to allow triggering doublepress events + * Added mcetool support for doublepress events + * Lots of improvements to mcetorture: + - Properly restore the blanking inhibit value + - Fixed bug in injection functions + - Added radio states test + - Added battery level test + - Added charger state test + - Added injection based short [power] key test + - Added injection based double [power] key test + - Added injection based long [power] key test + - Added injection based gpio-keys keyboard-slide test + - Added injection based touchscreen test + - Added D-Bus based double [power] key test + - Added `--verbose' option + - Renamed the D-Bus based short [power] key test to powershort-dbus + - Renamed the D-Bus based long [power] key test to powerlong-dbus + - Removed [home] key related tests from the default testcases + - Removed tests for long-gone alarm state D-Bus interface + - Various cleanup + * Updated mcetool manual page + * Updated mcetorture manual page + * Improved mce manual page + + -- David Weinehall Tue, 15 Jun 2010 20:39:01 +0300 + +mce (1.10.53) unstable; urgency=low + + * Provide backwards compatibility with the old power saving mode API + until its users have transitioned to the new API + * Removed the build-tests, since they're totally outdated + * Reverted mce-hal change + + -- David Weinehall Fri, 04 Jun 2010 15:52:58 +0300 + +mce (1.10.52) unstable; urgency=low + + * Send the new radio state as a signal rather than a reply + * Fixed regression in modetransition backwards compatibility + * Install a default radio_states file + * Converted mce-hal to use mce_translate_string_to_int_with_default() + * Added keyboard backlight status info to mcetool + * Removed powerkey menu testing from mcetorture + * Updated mcetorture manual page accordingly + * Removed unused files + * Cleanup + + -- David Weinehall Thu, 03 Jun 2010 14:49:54 +0300 + +mce (1.10.51) unstable; urgency=low + + * Fixed several brown paper bag bugs introduced by the + radiostates/modetransition rewrite + * Fixed typo in mce.ini + * Fixed indentation in mcetool + * Removed unused code + + -- David Weinehall Wed, 02 Jun 2010 15:52:45 +0300 + +mce (1.10.50) unstable; urgency=low + + * Temporarily add all D-Bus methods to the default context in mce.conf + (Fixes: NB#162564) + * Added new radio state interface; the device mode interface + is now deprecated. The old behaviour can be emulated by using + the radio master switch + * Added support for radio states to mcetool + * Updated mcetool manual page to cover radio states + * Removed device menu support; the concept might be reintroduced + at some point, but the current implementation was too ugly + * Removed mode change confirmation support; the concept might be + reintroduced at some point, but the current implementation was too ugly + * Updated tests/fakealarm to support all three alarm UI states + * Fixed missing Doxygen documentation + + -- David Weinehall Wed, 02 Jun 2010 05:38:27 +0300 + +mce (1.10.49) unstable; urgency=low + + * Additional fix for mce.conf (Fixes: NB#170827) + + -- David Weinehall Thu, 27 May 2010 11:06:27 +0300 + +mce (1.10.48) unstable; urgency=low + + * Modified the logic for double [power] press (Fixes: NB#169842) + + -- David Weinehall Fri, 21 May 2010 11:11:55 +0300 + +mce (1.10.47) unstable; urgency=low + + * Always call tklock with TKLOCK_ENABLE_VISUAL when enabling the tklock + * New display brightness adjustment policies + * Made a few rarely used timeouts use g_timeout_add_seconds() + * Implemented: MaSSW-884, MaSSW-1762, MaSSW-1763, MaSSW-1764 + + -- David Weinehall Mon, 17 May 2010 17:25:47 +0300 + +mce (1.10.46) unstable; urgency=low + + * Disable the gpio-key for camera focus when tklock is active + (Fixes: NB#166942) + * Added set_bit(), clear_bit(), string_to_bitfield() + and bitfield_to_string() to mce-lib.c + * Removed everything device lock related from mce; device lock is now + a completely separate component + * Updated mce, mcetool and mcetorture manual pages + + -- David Weinehall Thu, 13 May 2010 02:00:08 +0300 + +mce (1.10.45) unstable; urgency=low + + * Do not attempt to call fclose() unless the file is actually open + (Fixes: NB#168416) + + -- David Weinehall Tue, 11 May 2010 22:14:12 +0300 + +mce (1.10.44) unstable; urgency=low + + * Modified filter-brightness-als to handle ALS profiles with more + lux ranges + * Created ALS profiles for RM-680 + * Lowered the LED current for the model name LED and modified the + handling to cover the fact there are two model name LEDs + * Fixed mcetool_dbus_call_bool() to handle the return value correctly + * Fixed regression for ``--mode'' option in mcetool + * Added complete support for the power saving mode to mcetool + * Added support for enabling/disabling automatic power saving mode, + forcing the power saving mode, and setting the PSM threshold + + -- David Weinehall Mon, 10 May 2010 13:32:12 +0300 + +mce (1.10.43) unstable; urgency=low + + * Added support to mcetool for setting the display backlight level + * Updated the mcetool manual page accordingly + * Modified mcetorture to test all display blanking inhibit settings + * Added support for CABC-mode to mcetorture + * Refer to /dev/input/keypad and /dev/input/pwrbutton in mcetorture + instead of hardcoding paths to /dev/input/eventX + * Fixed missing Doxygen documentation + * Improved the description of the display blanking inhibit GConf + key in display.schemas + + -- David Weinehall Wed, 05 May 2010 08:14:39 +0300 + +mce (1.10.42) unstable; urgency=low + + * Corrected proximity sensor logic and modified threshold a bit + (Fixes: NB#164007) + * Added hysteresis to proximity sensor logic + * Fixed incorrect error-check in mce_read_number_string_from_file() + * Improved mce_write_number_string_to_file() + * Fixed update_proximity_monitor in event-switches to disable + proximity interrupts if there actually is a way to disable them + * Fixed the submode trigger in event-switches to disable + camera focus and camera launch interrupts if we're monitoring them, + and only if there actually is a way to disable them + * Added partial support for the power saving mode to mcetool + * Made mcetool default to not show the status information when executing + a command, and changed the `--no-status' option into `--status' + to provide a means to achieve the old behaviour + * Added blanking inhibit support to mcetool + * Fixed the CABC-mode support in mcetool to actually change the CABC-mode + * Updated the mcetool manual page and mcetorture accordingly + * Fixed a bug in mcetorture in the dim test + * Updated mcetorture for the new mcetool behaviour + + -- David Weinehall Tue, 04 May 2010 08:45:39 +0300 + +mce (1.10.41) unstable; urgency=low + + * Made [power] blank the display if pressed when the + visual tklock screen is already active (Fixes: NB#166247) + * Made double [power] press unlock the tklock + * Changed the default double click timeout to 500 ms + * Reset the display blanking timeout on bootup after updating the inhibit + instead of before, to give us a bit more time after the desktop ready + signal arrives (Fixes: NB#165966) + * Fixed ALS code to initialise the median filter *after* we know the + ALS type + * Made the visual tklock timeout 3 seconds instead of 5 seconds + * Changed default CABC-mode to "moving-image" again + * Fixed possible resource leak in mce_read_number_string_from_file() + * Added support for CABC-mode to mcetool + * Added support for blocking to mcetool + * Nicer handling of unset GConf keys in mcetool + * Added help to the verifybacklight script + + -- David Weinehall Fri, 30 Apr 2010 13:00:39 +0300 + +mce (1.10.40) unstable; urgency=low + + * Preliminary adjustments of the ALS profile for the LED + * Do not use a median filter for the ALS when the kernel driver + supports thresholds and polling + + -- David Weinehall Tue, 27 Apr 2010 22:59:22 +0300 + +mce (1.10.39) unstable; urgency=low + + * Trigger visual tklock when the camera button is pressed + (Fixes: NB#165453) + + -- David Weinehall Mon, 26 Apr 2010 01:28:44 +0300 + +mce (1.10.38) unstable; urgency=low + + * Fixed the keyboard backlight LED current + * Avoid doing unnecessary updates if the proximity state hasn't changed + * Fixed the --disable-pm-debug option in the verifybacklight test-script + + -- David Weinehall Fri, 23 Apr 2010 17:20:47 +0300 + +mce (1.10.37) unstable; urgency=low + + * Filter hotplugged /dev/input files using the same criteria + as when we first register them (Fixes: NB#164076) + + -- David Weinehall Tue, 20 Apr 2010 15:36:46 +0300 + +mce (1.10.36) unstable; urgency=low + + * Read the states of the input switches we care about on startup + (Fixes: NB#164471) + * Use debhelper compat level 5 + + -- David Weinehall Tue, 20 Apr 2010 02:56:33 +0300 + +mce (1.10.35) unstable; urgency=low + + * Added support for cancelling blank prevention requests + | Build-Depends: mce-dev (>= 1.10.15) + + -- David Weinehall Mon, 19 Apr 2010 13:21:43 +0300 + +mce (1.10.34) unstable; urgency=low + + * Only generate activity from the charger_connected/charger_disconnected + signals if the state changes (Fixes: NB#161579) + * Fixed path to proximity sensor calibration point + * Avoid doing unnecessary updates if the display state hasn't changed + * Avoid doing unnecessary submode updates + * Added support for battery_state_changed to the fakecharger test script + + -- David Weinehall Fri, 16 Apr 2010 22:22:52 +0300 + +mce (1.10.33) unstable; urgency=low + + * Fixed led_enable() and led_disable() to work properly (Fixes: NB#164169) + * Update the active LED pattern when a pattern timeout expires; + don't disable the LED unconditionally + + -- David Weinehall Fri, 16 Apr 2010 08:39:18 +0300 + +mce (1.10.32) unstable; urgency=low + + * Updated the list of touchscreen drivers (Fixes: NB#164004) + * Use a mapping from a percentage level to battery bars left + as the threshold for the power saving mode instead of the + battery low signal + * Modified the verifybacklight test script to allow disabling of the + sleep indication on RM-680 and to use the new LED interface + + -- David Weinehall Thu, 15 Apr 2010 13:05:55 +0300 + +mce (1.10.31) unstable; urgency=low + + * Added support for disabling touchscreen interrupts on RM-680 + (Fixes: NB#161334) + * Transitioned the LEDs to use the new sysfs entries + * Fixed LED patterns for communication events + * Fixed the connectivity component to not disconnect g_signal_handlers + that have not been connected + + -- David Weinehall Thu, 08 Apr 2010 03:45:34 +0300 + +mce (1.10.30) unstable; urgency=low + + * Improved mce_read_number_string_from_file() + * Made modules/filter-brightness-als.c keep an open file handle + for the ALS instead of re-opening it over and over + | Only for sensors that we monitor with recurring timers + * Added a D-Bus method for getting the key backlight state + * Fixed a typo in mcetool.sv.8 + + -- David Weinehall Mon, 29 Mar 2010 16:04:36 +0300 + +mce (1.10.29) unstable; urgency=low + + * More fixes to the adaptive display dimming + * Make the D-Bus policy a bit more lenient; do not require root for: + - get_device_mode + - get_call_state + - get_devicelock_mode + - get_tklock_mode + - get_display_status + - get_cabc_mode + - get_psm_mode + - get_inactivity_status + - get_version + - req_display_state_on + - req_display_state_dim + - req_display_state_off + - req_display_blanking_pause + * Improved mce_read_chunk_from_file() slightly + + -- David Weinehall Wed, 24 Mar 2010 13:06:13 +0200 + +mce (1.10.28) unstable; urgency=low + + * Modified the behaviour of the adaptive display dimming; + instead of using multipliers, we simply pick the next step up in + the list of possible dimming timeouts (if there are any higher ones) + * Updated the list of possible dim timeouts + + -- David Weinehall Tue, 23 Mar 2010 18:28:03 +0200 + +mce (1.10.27) unstable; urgency=low + + * Fixed incorrect handling of integer lists in mce-gconf + * Fixed the logic for the adaptive display dimming a bit + * Fixed incorrect error handling in mce_read_chunk_from_file() + | Found by coverity + + -- David Weinehall Tue, 23 Mar 2010 11:59:51 +0200 + +mce (1.10.26) unstable; urgency=low + + * Enabled adaptive display dimming by default + * Added mcetool.conf to allow mcetool to send on the system bus + * Removed the keypress event sending interface again; + the same information can now be gotten from QmKeys + + -- David Weinehall Mon, 22 Mar 2010 14:49:33 +0200 + +mce (1.10.25) unstable; urgency=low + + * Added support for combination rules for LED patterns + * Don't re-initialise the ALS median filter on transition from + undefined display state to on/dim; we already initialise + the median filter when we initialise the ALS module + * Don't reprogram the ALS I/O-monitor if we already have one active + * Use FB_BLANK_* instead of the deprecated VESA_* defines + for controlling the display power saving + * Fix encoding of man/mcetool.sv.8 + * Only setup the doublepress timeout when the tklock isn't active + and only if there is a doublepress action defined + + -- David Weinehall Thu, 18 Mar 2010 16:58:00 +0200 + +mce (1.10.24) unstable; urgency=low + + * Made the initial reads from the ambient light sensor + and proximity sensor blocking (Fixes: NB#160313) + * Fixed the special key/switch signal + * Fixed the path to calibration point for the DIPRO ambient light sensor + * Added calibration support for proximity sensors + * Made mce_read_chunk_from_file() retry a few times on + EAGAIN/EWOULDBLOCK before giving up + * Added an extra parameter to mce_read_chunk_from_file(), to allow for + passing of extra flags to open() + + -- David Weinehall Thu, 11 Mar 2010 13:43:42 +0200 + +mce (1.10.23) unstable; urgency=low + + * Fixed crash in modules/proximity.c + + -- David Weinehall Mon, 08 Mar 2010 15:04:42 +0200 + +mce (1.10.22) unstable; urgency=low + + * Added a replacement for the old keypress event sending interface; + the new, more reasonable, interface only sends special keys and switches + | Build-Depends: mce-dev (>= 1.10.12) + * Removed homekey module; this is handled by event-input.c for now + * Removed broken mce-cabc tool + * Fixed mce.conf to work with new, more strict, D-Bus policy + (Fixes: NB#159430) + | Thanks to Marius Vollmer + * Added test_bit() function to mce-lib.c + * Properly set display type for Himalaya + + -- David Weinehall Fri, 05 Mar 2010 15:51:57 +0200 + +mce (1.10.21) unstable; urgency=low + + * Properly send the active power saving mode instead of the user setting + * Added support for forcing power saving mode to always be on, even + when battery is not empty + * Moved the power saving mode related GConf keys to their own schemas-file + * Added support for reading lists of integers to mce-gconf + * Fixed minor memory leaks in mce-gconf + * Modified the logic for the power saving mode + * Modified mce-log to make syslog and stderr logging more consistent + with each other + * Don't forget gobject-2.0 in the list of packages to get pkg-config + information from; both GConf and Conic uses GObject + * Added module info for the powersavemode module + * Disconnect signal handlers when we no longer use them + + -- David Weinehall Mon, 01 Mar 2010 15:07:30 +0200 + +mce (1.10.20) unstable; urgency=low + + * Tuned LED patterns for RM-680 + * Removed the vibrator test from mcetorture + + -- David Weinehall Thu, 25 Feb 2010 12:21:30 +0200 + +mce (1.10.19) unstable; urgency=low + + * Allocate the proper amount of memory for the adaptive dimming values + * Fixed double allocation of DBusMessage in send_power_save_mode() + * Added -Wl,--as-needed to the LDFLAGS + * Added linux-libc-dev (>= 2.6.32) as an alternative build dependency + instead of linux-kernel-headers, and restored the workaround for + older kernel versions + | These two changes together allows building mce on other platforms + * Change the default for the power saving mode from true to false, + at least until hald-addon-bme proxies the battery_ok signal + * Added battery_ok to tests/fakecharger + + -- David Weinehall Wed, 24 Feb 2010 15:02:26 +0200 + +mce (1.10.18) unstable; urgency=low + + * Crapectomy: removed the code in keypad.c that + sent all keypresses as D-Bus signals (!) + * Added a power saving mode module + | Build-Depends: mce-dev (>= 1.10.11) + | Right now it kicks in when the battery low signal is received, + | but this might change in the future + * Added partial support for adaptive display dim timeouts + * Added BATTERY_OK to the battery signals from bme + * Tightened build-dependency on linux-kernel-headers + * Removed workaround for older kernel versions + * Always check return value from mcetool_dbus_call() in mcetool + | Found by coverity + * Updated the Doxyfile to the Doxygen 1.5.9 format + * Changed default CABC-mode to "ui" + + -- David Weinehall Tue, 23 Feb 2010 17:11:46 +0200 + +mce (1.10.17) unstable; urgency=low + + * Added support for the TWL4030 powerbutton (Fixes: NB#156762) + * Added support for the Atmel QT602240 touchscreen + * Added support for the TWL4030 jack sense + * Added support for the model name LED in the RM-680 + + -- David Weinehall Wed, 17 Feb 2010 17:25:32 +0200 + +mce (1.10.16) unstable; urgency=low + + * Added support for the DIPRO proximity sensor + * Added support for the DIPRO ambient light sensor + * Added support for the BH1780GLI ambient light sensor + * Added support for the monochrome LED in the RM-680 + | Patterns still need to be tuned + * Added keyboard backlight support for RM-680 + * Corrected inverted input-based cover switches + * Added mce_read_chunk_from_file() to mce-io + * Removed the mce-tests package; it interferes with the test reports + and is basically useless anyway + * Modified mce_log(); All log messages now have "file:function(): " + as a prefix + * Added mce_log_raw() for cases where the "file:function(): " + prefix isn't wanted + + -- David Weinehall Wed, 10 Feb 2010 00:23:58 +0200 + +mce (1.10.15) unstable; urgency=low + + * Added support for new input events to accommodate for newer kernels + that no longer support gpio-switch (Fixes: NB#153001) + * Added missing Build-Depends on linux-kernel-headers + * Added support for the Himalaya display controller + * Added support for the Synaptic TM12xx touchscreen input event device + * Added support for the new name of the keypad input event device + * Added support for the gpio-keys input event device + * Blacklisted the input drivers for (known) accelerometer, + magnetometer and vibrator input event devices + * Temporarily work around too old linux-kernel-headers by defining the + needed values if needed + + -- David Weinehall Tue, 26 Jan 2010 11:11:49 +0200 + +mce (1.10.14) unstable; urgency=low + + * Fixed the input tracking that was broken by the fix for the new + kernel poll semantics; only the semantics for sysfs changed, + not for everything else + * Removed some dead code + + -- David Weinehall Fri, 15 Jan 2010 10:00:16 +0200 + +mce (1.10.13) unstable; urgency=low + + * Corrected the paths for the Taal display controller + * Fixed FTBFS with new toolchain + * Use new desktop startup signal (Fixes: NB#126872) + + -- David Weinehall Fri, 08 Jan 2010 21:37:42 +0200 + +mce (1.10.12) unstable; urgency=low + + * Adapted mce-io to new kernel poll semantics for sysfs + (Fixes: NB#150979) + + -- David Weinehall Wed, 16 Dec 2009 15:28:59 +0200 + +mce (1.10.11) unstable; urgency=low + + * Modified [power] button behaviour + - Activate tklock on short-press + - No longer activate tklock on double-press + * Forward ported additional fixes for the error flood problem + + -- David Weinehall Mon, 14 Dec 2009 16:48:11 +0200 + +mce (1.10.10) unstable; urgency=low + + [ Tuomo Tanskanen ] + * Use devicelock packages headers + - Old SystemUI attic devlock removed + - All devlock D-Bus calls etc. converted to use new interface + * Implemented required D-Bus interface for setting device lock state + (Fixes: NB#148699) + + [ David Weinehall ] + * Forward ported changes from Fremantle + * Added Tuomo to Uploaders + * Removed out of date Conflicts + * Removed mce.zzinitdone.init + * Removed mce.upstart and mce.devlock-blocker.xsession + | This is now in the system-services package + * Removed build-dependency on upstart-dev + * Removed the accelerometer module + * Updated mcetool and its manual page to reflect the removed + vibrator and accelerometer support + * Added GConf schemas for the display settings (Fixes: NB#148537) + + -- David Weinehall Wed, 02 Dec 2009 16:41:33 +0200 + +mce (1.10.9+nmu3) unstable; urgency=low + + * Fixed D-Bus interface to return the new CABC mode + (Fixes: NB#147731) + * Removed mce.mce.init, as that init stuff is not used anymore + | Init files have not been parsed by upstart for a long time + | and the new upstart was giving problems with this obsolete file + + -- Tuomo Tanskanen Fri, 27 Nov 2009 10:08:20 +0200 + +mce (1.10.9+nmu2) unstable; urgency=low + + * Added shebang and svn:executable to runtests.sh + + -- Tuomo Tanskanen Thu, 05 Nov 2009 09:13:35 +0200 + +mce (1.10.9+nmu1) unstable; urgency=low + + * Fremantle MCE port to Harmattan + - Added SystemUI attic + - Added Cita tests + - Added mce-cabc tool + - Dropped Build dependencies on SystemUI and BME + - Added BME header + - Fixed modules related to SystemUI and BME + - Dropped useless linking to libcrypt for mce and libdbus-glib for mcetool + - Added support for Taal display + - Added Hardware keys support + - Removed vibrator module and related entries in mce.ini + (Fixes: NB#144584) + - Fixed Makefile + + -- Tuomo Tanskanen Tue, 03 Nov 2009 15:09:46 +0200 + +mce (1.8.98) unstable; urgency=low + + * Really fix the audio routing for ihfandheadset (Fixes: NB#139253) + * Disable charger based blank prevention if charging stops or fails + (Fixes: NB#139410) + * Disable the tklock before unblanking when interacting with + the proximity sensor (Fixes: NB#139823) + * Always log the reason for shutdown requests originating from mce + + -- David Weinehall Mon, 26 Oct 2009 14:56:44 +0200 + +mce (1.8.97) unstable; urgency=low + + * Connecting USB cable opens tklock and lits display + (Fixes: NB#143099) + + -- Pekka Lundström Wed, 21 Oct 2009 13:15:55 +0300 + +mce (1.8.96) unstable; urgency=low + + * Fixed tklock case when alarm comes on during the call + (Fixes: NB#138435) + + -- Pekka Lundström Tue, 20 Oct 2009 14:20:45 +0300 + +mce (1.8.95) unstable; urgency=low + + * Applied patch to audio routing signal handling (Partial fix for NB#143242) + | Patch provided by Janos Kovacs + + -- Pekka Lundström Fri, 16 Oct 2009 15:15:20 +0300 + +mce (1.8.94) unstable; urgency=low + + [ Pekka Lundström ] + * When tklock is enabled, keypad interrupts are disabled but not + during active call; volume keys must work during the call even + when the proximity sensor has blanked the display + (Fixes: NB#138475) + + [ Tuomo Tanskanen ] + * Re-enable visual unlock during calls, since it now works properly + (Fixes: NB#141758) + + -- Tuomo Tanskanen Mon, 05 Oct 2009 17:09:06 +0300 + +mce (1.8.93) unstable; urgency=low + + * Added new vibra pattern "PatternChatAndEmail" + (Fixes: NB#141599) + + -- Pekka Lundström Mon, 5 Oct 2009 9:46:46 +0300 + +mce (1.8.92+dw1) unstable; urgency=low + + * Fixed audio routing for ihfandheadset (Fixes: NB#139253) + * Removed unused code for deprecated call states + + -- David Weinehall Thu, 01 Oct 2009 13:14:51 +0300 + +mce (1.8.92) unstable; urgency=low + + * Added a workaround for a very nasty case where the display state trigger + is called recursively by the accelerometer module; the bug is not properly + fixed, but there are no infinite loops any more (Fixes: NB#141040) + + -- David Weinehall Wed, 30 Sep 2009 02:33:44 +0300 + +mce (1.8.91) unstable; urgency=low + + * Fine-tuning for touchscreen vibra (start 23ms, stop 15ms) + (Fixes: NB#141080) + + -- Pekka Lundström Tue, 29 Sep 2009 16:08:03 +0300 + +mce (1.8.90) unstable; urgency=low + + * Removed the CABC hack once more (hopefully for good this time) + and added support for a GConf setting that enables/disables CABC + completely instead (Fixes: NB#139411) + * Another fix to the error policy in mce-io.c + | Found by coverity + + -- David Weinehall Fri, 25 Sep 2009 15:22:52 +0300 + +mce (1.8.89) unstable; urgency=low + + * Added accelerometer enable/disable request handling + (Fixes: NB#137886, NB#138981) + | Build-Depends: mce-dev (>= 1.8.14) + * Fine-tuned orientation decision points + (Fixes: NB#128678, NB#135660) + + -- Pekka Lundström Fri, 18 Sep 2009 10:48:01 +0300 + +mce (1.8.88) unstable; urgency=low + + * Changed CABC default to "moving-image" (Fixes: NB#137988) + * Made tklock the default for [power] double press + * Properly disable the automagic relocking when there's an external + tklock unlock request (Fixes: NB#138615) + * Cancel all tklock related timeouts when disabling the tklock + (Fixes: NB#137380) + * Don't update the saved submode if there's already an active call + * Disabling of temporary tklock when a call ends should be silent + * Minor fixes to error messages from mce-io + + -- David Weinehall Wed, 09 Sep 2009 14:34:00 +0300 + +mce (1.8.87) unstable; urgency=low + + * Don't filter inactivity, only activity when touchscreen/keypad + is locked (Fixes: NB#137568) + * Added a few more NULL-checks and improved some error messages + in datapipe.c (Fixes: NB#136452) + * Inhibit automatic tklock during bootup (Fixes: NB#137257) + * Modified autorelock not to take the camera button into consideration + * Added option to enable tklock by double pressing [power] + + -- David Weinehall Mon, 07 Sep 2009 23:10:36 +0300 + +mce (1.8.86) unstable; urgency=low + + * Vibra uses new interface to driver; all timings are handled in kernel + (Fixes: NB#132957) + + -- Pekka Lundström Mon, 7 Sep 2009 10:15:11 +0300 + +mce (1.8.85) unstable; urgency=low + + * Disable interrupts from camera focus/launch when enabling the + touchscreen/keypad lock (Fixes: NB#135706) + * Tuned the DeviceOn LED pattern a bit further (Fixes: NB#135822) + | Thanks to Pekka Lundström + * Fixed corner case where the proximity sensor is ignored on incoming + calls if there's an alarm active but the display has blanked + * Don't suspend the proximity sensor I/O monitor when the interrupts + are disabled; there won't be any activity anyway, so it shouldn't + make a difference that it's active, and keeping it active + simplifies things a bit + + -- David Weinehall Fri, 28 Aug 2009 12:11:14 +0300 + +mce (1.8.84) unstable; urgency=low + + * Change default for autolock back to not activate when the keyboard + slide is open (Fixes: NB#135171) + | Sigh... I don't mind this behaviour, but I wish people could + | make up their bloody minds... + * Properly handle the boot-time display dimming/blanking/inactivity timeout + (Fixes: NB#134904) + * Revert the policy for the touchscreen vibrator pattern, since disabling + vibra using the D-Bus interface should affect *everything* except the + poweroff pattern (otherwise it won't disable the vibra during calls + and when using camera where the vibrations might disrupt the main + use-case) (Fixes: NB#133592) + * Made the lens cover triggered unlocking of the tklock configurable + through mce.ini + + -- David Weinehall Wed, 26 Aug 2009 16:56:10 +0300 + +mce (1.8.83) unstable; urgency=low + + * Adjusted the various LED patterns related to notifications + (Fixes: NB#134101) + * Change policy for the touchscreen vibrator pattern to make it play + even if vibrator alarms/ringtones are disabled + * Fix call state bug + + -- David Weinehall Mon, 24 Aug 2009 18:27:54 +0300 + +mce (1.8.82) unstable; urgency=low + + * Against my better judgement, reintroduce the fugly CABC hack once + more, after much pressure from product management... Mark my words, + nothing good will come of this (Fixes: NB#133710) + * Disable proximity sensor interrupts and suspend the I/O monitor + when the sensor is not in use + * Send the call state over D-Bus before we send it internally, + instead of after, to help the camera avoid grabbing the audio resources + when there are incoming calls (partial fix for NB#130535) + * Don't dereference iomon if it's NULL + | Found by coverity + * All functions, typedefs and global variables should now have full + Doxygen coverage + * Acknowledge NMU + | Thanks to Pekka Lundström + + -- David Weinehall Mon, 24 Aug 2009 14:42:10 +0300 + +mce (1.8.81+nmu1) unstable; urgency=low + + * Added start and stop pulses to vibrator patterns to make + it better suitable for touchscreen (Fixes: NB#132957) + + -- Pekka Lundström Fri, 21 Aug 2009 13:52:55 +0300 + +mce (1.8.81) unstable; urgency=low + + * Unblank at the end of a call unless the tklock is active + (Fixes: NB#131003) + * Only reprogram ALS timeout if it's different from the current timeout + (Fixes: NB#125933) + * Same fix for the accelerometer + * Minor ALS profile fixes + * Define _GNU_SOURCE directly in COMMON_CFLAGS to enable compiling with + newer libc where ucred is hidden behind __USE_GNU + * Fixed some Doxygen comments + + -- David Weinehall Mon, 17 Aug 2009 17:44:36 +0300 + +mce (1.8.80) unstable; urgency=low + + * Properly reset the proximity tklock flag when leaving call / alarm state + (Partial fix for NB#131003) + * Removed various unnecessary D-Bus calls when mce is shutting down + and powering up from acting dead + | This also fixes a warning when running "mce --version" / "mce --help" + * Fixed LED pattern when powering up from acting dead + * Fixed proximity sensor inhibiting to last the entire call + * Allow the owner of the call state to make a transition from + "active" to "ringing", to allow for an incoming call when there's + already an active call + * Don't prevent disabling of the touchscreen if the alarm UI is visible + when the user activates the tklock + * Acknowledge NMUs + | Thanks to Pekka Lundström + + -- David Weinehall Fri, 14 Aug 2009 16:48:22 +0300 + +mce (1.8.79+nmu3) unstable; urgency=low + + * Added vibrator pattern for touchscreen (Fixes: NB#132680) + + -- Pekka Lundström Fri, 14 Aug 2009 09:36:35 +0300 + +mce (1.8.79+nmu2) unstable; urgency=low + + * Modified display and keyboard brightness (Partial fix for NB#125933) + + -- Pekka Lundström Thu, 13 Aug 2009 11:01:20 +0300 + +mce (1.8.79+nmu1) unstable; urgency=low + + * Modified PatternDeviceOn to pulsate slower (Fixes: NB#130960) + + -- Pekka Lundström Mon, 10 Aug 2009 12:55:59 +0300 + +mce (1.8.79) unstable; urgency=low + + * Disable event eater before we open the tklock UI when alarm dialog + status changes to not visible (Fixes: NB#130714, NB#129598) + * Modified the behaviour for proximity sensor inhibiting; always + inhibit the proximity sensor if using the flicker key during a call + (Fixes: NB#130657) + * More fixes for the error handling policies + * Made unset GConf keys generate log messages at loglevel LL_INFO + rather than LL_WARN, since it's not harmful as there's default values + to fall back on + + -- David Weinehall Fri, 31 Jul 2009 10:26:35 +0300 + +mce (1.8.78) unstable; urgency=low + + * Use new osso-systemui-devlock interface to only show the + "Device locked" when locking the device (Fixes: NB#119441) + * Improved error handling policies for the mce_register_io_monitor*() + class of functions + + -- David Weinehall Thu, 30 Jul 2009 12:36:56 +0300 + +mce (1.8.77) unstable; urgency=low + + * Don't disable accelerometer reads when the display goes blank if + the alarm UI is visible, otherwise flip-snoozing won't work + * Improved fix for the tklock <-> alarm interaction + + -- David Weinehall Tue, 28 Jul 2009 22:08:52 +0300 + +mce (1.8.76) unstable; urgency=low + + * Don't disable the device lock and confirmation dialog on incoming call + (Fixes: NB#124743) + * Don't activate the touchscreen/keypad autolock if there's + an alarm dialog visible (Fixes: NB#129609) + * Adjusted LED pattern for application triggered patterns + (Fixes: NB#128358) + * Stop-gap ALS adjustment; will be properly tuned later on + * Don't disable autorelock on keypress/touchscreen tap + if the tklock UI isn't visible (Fixes: NB#129614) + * Adjusted PowerOn LED behaviour when powering up from acting dead + when charging + + -- David Weinehall Thu, 23 Jul 2009 17:32:19 +0300 + +mce (1.8.75) unstable; urgency=low + + * Fix issue where event eater could remain active even when it should + be disabled + + -- David Weinehall Mon, 20 Jul 2009 20:38:16 +0300 + +mce (1.8.74) unstable; urgency=low + + * Updated mcetorture to test new alarm dialog status interface + * Fixed a regression caused by the fix for NB#126598 + * Fixed a potential issue in call state monitoring, to avoid vetoing + call state changes if the requesting application exits before we've + registered the dbus monitoring + + -- David Weinehall Sun, 19 Jul 2009 03:30:43 +0300 + +mce (1.8.73) unstable; urgency=low + + * Don't setup a dim timeout when locking if the display is already + blank (Fixes: NB#126340) + * Made the Build-Depends on osso-systemui-dbus-dev (>> 0.1.2) instead, + to make sure we actually get the right package + * Tuned the accelerometer a bit further, to be less eager to report + portrait mode (Fixes: NB#122546) + + -- David Weinehall Thu, 16 Jul 2009 13:41:15 +0300 + +mce (1.8.72) unstable; urgency=low + + * Handle the new alarm dialog status signal from osso-systemui-alarm + (Fixes: NB#118907, NB#121457, NB#116721, NB#126243, NB#126598) + | Build-Depends: osso-systemui-dbus-dev (>= 0.1.2) + | Conflicts: osso-systemui-alarm (<< 0.2.1.10) + + -- David Weinehall Fri, 10 Jul 2009 03:36:41 +0300 + +mce (1.8.71) unstable; urgency=low + + * Use GFileMonitor to track addition/removal of files in /dev/input + rather than relying on kevents; this makes hotplugged input devices + properly generate activity (Fixes: NB#126131) + * Bumped dependency on glib to 2.18 or greater, since we're using GIO + * Filter out repeat keypresses and only send activity from them + once a second (Fixes: NB#125479) + * Don't forget to send event on keypress release if the event eater is + active, otherwise the first event after pressing [power] will get lost + | Partial fix for NB#126486 + * Don't enable device lock when transitioning from acting dead to + user state; this only happens on power up from acting dead, + and in this case we go through a reboot anyway (Fixes: NB#124756) + + -- David Weinehall Tue, 07 Jul 2009 05:24:55 +0300 + +mce (1.8.70) unstable; urgency=low + + * Changed default for autolock; make autolock activate even when the + keyboard slide is open (Fixes: NB#124836) + + -- David Weinehall Thu, 25 Jun 2009 15:23:21 +0300 + +mce (1.8.69) unstable; urgency=low + + * Further updates to locking logic (Fixes: NB#121943) + * Use the proximity sensor for the headset too + * Make use of the flicker key while using the headset inhibits + the proximity sensor + * Don't use slide unlock UI during calls + * Made autolock with open keyboard slide configurable, + since the UI spec team warned that the default is likely to change + + -- David Weinehall Tue, 23 Jun 2009 16:26:09 +0300 + +mce (1.8.68) unstable; urgency=low + + * Use the normal blanking delay for handset too + + -- David Weinehall Thu, 18 Jun 2009 12:48:22 +0300 + +mce (1.8.67) unstable; urgency=low + + * Made the accelerometer return MCE_ORIENTATION_UNKNOWN + as rotation if we cannot discern it (Fixes: NB#122527) + * Handle handset vs headset vs speaker in a better manner + (Fixes: NB#121943, NB#121984) + * Removed caching of old datapipe values, since using the cached values + was only safe under certain conditions; replaced with static variables + in the functions that need it instead + * Suspend the touchscreen I/O monitor from visual tklock too + (Fixes: NB#121897) + * Keep the display blank when closing the keyboard, + and trigger the touchscreen/keypad autolock (Fixes: NB#107218) + * Re-enable the proximity lock if the keyboard is closed, + and there's a call on-going (Fixes: NB#122001) + * Don't inhibit blanking in during an on-going call when using + headset or speaker (Fixes: NB#121943) + * Don't trigger visual tklock during bootup + * Only trigger visual tklock with [power] button + * Fixed a potential memory leak in the datapipe code + + -- David Weinehall Thu, 18 Jun 2009 01:27:57 +0300 + +mce (1.8.66) unstable; urgency=low + + * Ignore INHIBIT_STAY_ON and INHIBIT_STAY_ON_WITH_CHARGER + in acting dead mode + * Always reset dimming inhibiting properly + * If we're in acting dead and no alarm is visible, + don't unblank the display on activity + * If the charger state changes, unblank the display + * Don't enable the event eater when the charging UI + is visible in acting dead + * Use a 5 second dim timeout in acting dead + | The above fixes taken together solves the problem with the display + | remaining on indefinitely in acting dead and the bugs hidden + | behind this behaviour (Fixes: NB#121664) + * Always enable the keyboard backlight on activity if the slide is open, + not just when a key is pressed (this makes it possible to tap the display + to light the keypad, for instance) + + -- David Weinehall Thu, 11 Jun 2009 20:01:42 +0300 + +mce (1.8.65) unstable; urgency=low + + * Ignore earlier tuning for the highest brightness setting and simply + use full brightness instead. *sigh* (Fixes: NB#120102) + + -- David Weinehall Tue, 09 Jun 2009 12:35:24 +0300 + +mce (1.8.64) unstable; urgency=low + + * Made landscape/portrait orientation changes generate activity, + but only if the display is on (Fixes: NB#117305) + * Added functions to mce-dbus for tracking NameOwnerChange + * Keep track of NameOwnerChange for call state, and restore call state + to none:normal when the application quits/crashes, to avoid getting + stuck in call state + * Keep track of NameOwnerChange for display blanking pauses, + and resume normal behaviour immediately when the application + quits/crashes, to avoid preventing blanking for up to 60 seconds extra + * Added driver blacklist to the event-input monitoring, + and exclude the high-resolution accelerometer + * Fixed event eating for the [power] button + * Rewrite some logic regarding to acting dead and splashscreens + * Do not use delayed unlocking of the touchscreen/keypad lock + for the proximity sensor (Fixes: NB#120601) + * Be sure to follow the keyboard interrupt policy when the + touchscreen/keypad autolock kicks in too, not just when enabling + it manually (Fixes: NB#116977) + * On startup, fade to the target brightness from the current brightness + * Fixed incorrect example pattern + + -- David Weinehall Thu, 04 Jun 2009 03:24:56 +0300 + +mce (1.8.63) unstable; urgency=low + + * Another locking fix (Fixes: NB#118517) + * Don't call mce_log() from signal handler (Fixes: NB#119383) + * Reset errno if sscanf fails and log a proper error message + + -- David Weinehall Thu, 28 May 2009 16:48:20 +0300 + +mce (1.8.62) unstable; urgency=low + + * Don't show device lock again if SystemUI restarts (Fixes: NB#115467) + * Accept "ihf" as sink when determining if the call is handheld + when call state is ringing (Fixes: NB#116980, NB#109779) + * Further accelerometer tuning + | Thanks to Pekka Lundström + * Fixed regressions in device lock introduced in mce 1.8.61 + * Fixed regression with lens cover based unlocking of tklock + introduced in mce 1.8.61 + | Thanks to Chetan Gopal for finding these regressions + * Fixed possible NULL-pointer dereference in tklock closing code + * Corrected the default timeout for automatic device lock + * Turn the display state trigger in the accelerometer code into + an input trigger instead, to allow for sending the new orientation + before the display turns on + | This is unlikely to matter in practise, since the D-Bus latency + | is presumably higher than the internal latencies in MCE, but it's + | the best we can do + * Don't restart the dim/blank timeouts when updating blanking inhibit + if the display is already dimmed/blanked (Fixes: NB#115671) + * Always unblank the display on incoming call based on the proximity + sensor, even if the tklock isn't active + * Don't disable visual tklock when alarm UI is visible + (Fixes: NB#118105) + * Various tklock fixes (Fixes: NB#116176) + * Simplified logic for visual tklock + * Improved D-Bus error messages + + -- David Weinehall Thu, 28 May 2009 03:10:07 +0300 + +mce (1.8.61) unstable; urgency=low + + * Improved logic for proximity autorelock + (Fixes: NB#117156) + * Unblank immediately, don't fade (Fixes: NB#116656) + * When the device is ringing, don't dim/blank the display + (Fixes: NB#116953) + * When a call is ongoing, only dim, don't blank + * If the device is in autorelock submode, don't open the visual + tklock when a key is pressed + * Tune the accelerometer values for face down + | Thanks to Pekka Lundström + * Added a new D-Bus helper to mce-dbus, for sending D-Bus messages + and blocking for the reply (use with care!) + * Use the new D-Bus helper for all interaction with SystemUI + * Reorganise the display handling a bit to decrease the number + of writes to the display controller + * The CABC mode doesn't need to be reprogrammed after a display off/on + cycle; this was just a kernel bug + + -- David Weinehall Tue, 26 May 2009 19:07:42 +0300 + +mce (1.8.60) unstable; urgency=low + + * Use a 5 second timeout for visual touchscreen/keypad lock, + then blank immediately (Fixes: NB#118092) + * Fixed some issues when interacting with the visual + touchscreen/keypad lock (Fixes: NB#118088) + * Fixed the inactivity filtering that was broken by the change in mce 1.8.59 + and fix the interaction with the visual tklock + * Modify brightness according to what LEDs we are driving and add + a new pattern for charging (Fixes: NB#111247, NB#115892) + | Thanks to Pekka Lundström + * If the display is blank but the tklock isn't active, + don't unblank if the user activates the lock with the flicker key + * Don't generate activity from the flicker key by default + * Synthesise activity if the tklock is unlocked with the flicker key + + -- David Weinehall Wed, 20 May 2009 18:54:47 +0300 + +mce (1.8.59) unstable; urgency=low + + * The new desktop readiness signal is finally available, + so use it (Fixes: NB#107958, NB#113753) + * Minor fix for the audio routing interaction with tklock + * Don't generate activity or turn on the keyboard backlight + if the tklock is active + * Timeout for keyboard backlight should be 30s, not 10s (Fixes: NB#116747) + + -- David Weinehall Tue, 19 May 2009 16:42:56 +0300 + +mce (1.8.58) unstable; urgency=low + + * Disable the device autolock when the display is kept awake through + blank prevent requests (Fixes: NB#115637) + * Don't ignore [power] during bootup, the user might want to + shutdown from the configuration wizard or during the pin code + entry dialog (Fixes: NB#111613) + * Properly handle G_IO_STATUS_AGAIN if returned by g_io_channel_read_chars() + | Found by coverity + + -- David Weinehall Tue, 12 May 2009 16:59:14 +0300 + +mce (1.8.57) unstable; urgency=low + + * Improved orientation code + | Thanks to Pekka Lundström + * Added support for tracking of audio routing + * Use the audio routing information as additional policy to decide whether + or not to take the proximity sensor into account during calls + (Fixes: NB#109779) + * Do not rescan the entire input device list when there's a hotplug event, + only update the relevant entry; this solves various race conditions + when devices are hotplugged (Fixes: NB#112530) + | It seems as though the netlink event for a hotplugged device can + | arrive *before* the device entry has actually appeared in /dev/input; + | this means that mce might fail to open these devices. This needs to + | be investigated further -- having to busy wait is not a particularly + | good idea -- after all, the netlink events are meant to avoid having + | to do things like that... + * Use LL_WARN instead of LL_ERR for error messages + when the error policy in mce-io is to ignore errors + * Apparently the kernel emits POLLERR when new data is available (!), + so use POLLERR as an indication that data needs to be read instead + of as indication that something went wrong + | Removes a lot of useless warnings, but also leaves me with a + | sense of discomfort + + -- David Weinehall Mon, 11 May 2009 15:19:12 +0300 + +mce (1.8.56) unstable; urgency=low + + * Do not adhere to display on/display dim requests if the tklock is active + (Fixes: NB#111649) + * If there's an incoming call or alarm, automagically unlock the tklock, + but *only* if the proximity sensor doesn't indicate proximity + (IOW, if the device is in a pocket, bag or similar, don't unlock); + if so the device will instead unlock when the proximity changes state + (Fixes: NB#113451) + * Hysteresis adjustment in ALS profiles for the RX-51 + * Updated help text in mcetool + + -- David Weinehall Thu, 30 Apr 2009 11:04:02 +0300 + +mce (1.8.55) unstable; urgency=low + + * Check if tklock is enabled on unblank before attempting to disable + the event eater, since both share the same code; otherwise the + unblanking done when we unlock the tklock will trigger relock again + on toggle in corner conditions where both tklock and event eater + are active at the same time (Fixes: NB#110951, NB#112447) + * The tklock should also autorelock if the lens cover is opened and then + closed without any input in between (Fixes: NB111999, NB#112526) + * If tklock is activated manually during a call, + don't use the proximity sensor to lock/unlock (Fixes: NB#112777) + * Fixed bug in blanking inhibit code (Fixes: NB#93557, NB#111158, NB#112401) + * If the restored device mode is invalid, properly default to INVALID + (Fixes: NB#111716) + * Tune accelerometer to fix unexpected switching between portrait + and landscape (Fixes: NB#112741) + | Patch by Matti Halme + * Tune the ALS profiles for the RX-51 + * Remove the CABC hack again; always use the CABC even for the + highest brightness setting + * Remove Workaround for the TKLOCK_ENABLE_VISUAL issue; this should + work properly now + + -- David Weinehall Mon, 27 Apr 2009 17:01:49 +0300 + +mce (1.8.54) unstable; urgency=low + + * Read the ALS calibration data from the correct CAL section + and fix the size check (solves the remainder of NB#99119) + * Don't display the confirmation dialog on when online mode is chosen + from the device menu (Fixes: NB#112239) + * Made the verifybacklight and verifyled scripts use printf instead + of echo, to ensure that we're getting the same result as mce would + get (since mce does not include any newline) + * Don't warn about non-seekable file if the rewind policy is FALSE + + -- David Weinehall Tue, 21 Apr 2009 17:32:27 +0300 + +mce (1.8.53) unstable; urgency=low + + * Introduce mce_get_io_monitor_fd() and use it to get the file descriptor + monitored by event-input.c, and close them, to avoid leaks + (Fixes: NB#98013) + + -- David Weinehall Thu, 09 Apr 2009 17:29:33 +0300 + +mce (1.8.52) unstable; urgency=low + + * Work around issue in tklock by closing and reopening tklock UI + when changing to TKLOCK_ENABLE_VISUAL + * Track camera button launch, +/- and power key presses to trigger + TKLOCK_ENABLE_VISUAL + * Added new option for keyboard interrupt disabling to mce.ini, + and use it by default + * Setup a dim timeout for the tklock immediately after the alarm UI + closes + * Inhibit screen blanking (but not dimming) when charging in acting dead + (Fixes: NB#99790) + * Don't generate activity from touchscreen and/or keypad when + the event eater is active; generate it when it's closed instead + | Conflicts: osso-systemui-tklock (<< 0.1.8.10) + * Fixed memory leak found by coverity (Fixes: NB#109692) + * Don't suspend the touchscreen I/O monitor when autorelock is active + (Fixes: NB#107435) + * Enable event eater already at dim + * Corrected an issue with CABC state on bootup + * Don't make activity trigger display unblank if the touchscreen/keypad + lock is enabled (Fixes: NB#110068) + | In the cases where the display does need to be enabled, + | we do this explicitly in the tklock module + * Better error handling; in case osso-systemui-tklock fails, + don't enable tklock internally, since we'll end up in a horrible mess + * Gah! Use /var/run, not /var/lib for the call state file, otherwise + the transition handling on bootup won't have a chance to work properly + + -- David Weinehall Tue, 07 Apr 2009 16:50:49 +0300 + +mce (1.8.51) unstable; urgency=low + + * Set verify submode when device lock UI is requested, + to avoid a race condition that would allow device menu to be opened + on top of the device lock (Fixes: NB#102646) + * Re-program CABC every time we unblank the display, + since the CABC driver seems to forget its setting when the display blanks + * Disable CABC when using maximum brightness level + * Added support for using keyboard interrupt disabling on the RX-51 + * Unified activity callbacks in event-switches + + -- David Weinehall Mon, 30 Mar 2009 19:01:33 +0300 + +mce (1.8.50) unstable; urgency=low + + * Be sure to set the cached call state in the tklock even when the call + is initiated on our end (Fixes: NB#107278) + * Handle the case where the proximity sensor reports proximity + at the point when the device starts ringing (Fixes: NB#92896) + * Properly restore the tklock after a call (Fixes: NB#102681) + * Implemented support for the tristate camera button (Fixes: NB#101348) + * Execute the touchscreen pipe both on press and release + (Fixes: NB#103170) + * Removed camera button input layer support; this is no longer available + since the button is now tristate + * Proper fix for NB#104372 + + -- David Weinehall Wed, 25 Mar 2009 09:43:44 +0200 + +mce (1.8.49) unstable; urgency=low + + * Close the device lock code verification dialog when the call state + changes to "ringing" or "active" (Fixes: NB#104372) + + -- David Weinehall Mon, 23 Mar 2009 21:16:57 +0200 + +mce (1.8.48) unstable; urgency=low + + * Setup the device lock delay before updating the counters + (Fixes: NB#104582) + * Added I/O monitor for mmc0/battery cover for the RX-51 + * Removed activity I/O monitor and changed remaining activity monitor + users over to use string monitors instead; the activity monitors + were never working correctly + + -- David Weinehall Mon, 23 Mar 2009 15:29:22 +0200 + +mce (1.8.47) unstable; urgency=low + + * Don't setup the device autolock unless we're in USER state + (Fixes: NB#101091) + * Updated mcetool manual page + * Don't display mode confirmation dialog when requesting + offline from the device menu + + -- David Weinehall Tue, 17 Mar 2009 18:05:21 +0200 + +mce (1.8.46) unstable; urgency=low + + * Fixed copy'n'paste error in tests/verifyled + * Disable the timeout at the "shutdown device?" dialog when pressing + cancel in the device lock (this behaviour can be re-enabled + using mce.ini) (Fixes: NB#103552) + * Fixed bug where the shutdown timeout wouldn't get re-enabled + after a call + * In case charging signals arrive in the wrong order or if some + signal gets lost, disable the charging and battery full pattern + when charger is disconnected (Fixes: NB#103622) + * Minor improvements of the fakecharger script + * Modified build-time version check to allow for binary NMU's + + -- David Weinehall Fri, 13 Mar 2009 06:02:37 +0200 + +mce (1.8.45) unstable; urgency=low + + * Read misc input events before suspending again; + otherwise they will trigger activity over and over + (Fixes: NB#99351, NB#102601) + * Disable the headphone switch handler + + -- David Weinehall Mon, 09 Mar 2009 17:06:50 +0200 + +mce (1.8.44) unstable; urgency=low + + * Allow call state transition from "ringing" to "active" + + -- David Weinehall Tue, 03 Mar 2009 16:38:50 +0200 + +mce (1.8.43) unstable; urgency=low + + * Set CABC mode to UI instead of still image (Fixes: NB#101292) + * Changed the polling interval for the accelerometer to 500 ms + * Added an error message if mce cannot read the max_brightness file + from sysfs + + -- David Weinehall Tue, 03 Mar 2009 13:25:29 +0200 + +mce (1.8.42) unstable; urgency=low + + * Another doh! Fixed the name of the Xsession file + + -- David Weinehall Thu, 26 Feb 2009 19:56:28 +0200 + +mce (1.8.41) unstable; urgency=low + + * Start the device lock blocker from Xsession (Fixes: NB#103361) + + -- David Weinehall Wed, 25 Feb 2009 18:20:46 +0200 + +mce (1.8.40) unstable; urgency=low + + * Doh! Handle the new "ringing" call state properly + + -- David Weinehall Wed, 25 Feb 2009 17:03:18 +0200 + +mce (1.8.39) unstable; urgency=low + + * Doh! Fixed libcal build-dependency + + -- David Weinehall Tue, 24 Feb 2009 17:53:25 +0200 + +mce (1.8.38) unstable; urgency=low + + * Added "ringing" call state + | Build-Depends (mce-dev >= 1.8.9) + * Disable the vibrator during an active call, but not when ringing + (Fixes: NB#101572) + + -- David Weinehall Tue, 24 Feb 2009 17:35:04 +0200 + +mce (1.8.37) unstable; urgency=low + + * Improved message handler in mce-dbus.c + | Thanks to Simo Piiroinen + * Improved the mce.conf D-Bus configuration + * Fixed coverity warnings in modules/led.c + * Fixed coverity warning in modules/display.c + * Don't disable the battery low pattern when charging, + since we never ever enable it (and it's no longer defined in mce.ini) + * Read the ALS tuning values from CAL and use them + to initialise the ALS driver (Fixes: NB#99119) + * MCE once more Build-Depends on libcal1, + since we need it to read the ALS tuning values + * Improved verifyled script + * Minor code cleanup + + -- David Weinehall Mon, 23 Feb 2009 13:59:46 +0200 + +mce (1.8.36) unstable; urgency=low + + * Remove handler from dbus_handlers *before* freeing it + * Don't try to do close the filedescriptor in rescan_inputdevices() + if we don't have a valid filedescriptor + * Fixed coverity warnings in test_mce-log.c + * Made keypad component a module + * Send the correct result from mce_set_device_mode_int32() + * Fixed bug in connectivity code that caused connected status to + remain as TRUE even after disconnect + + -- David Weinehall Thu, 05 Feb 2009 13:10:42 +0200 + +mce (1.8.35) unstable; urgency=low + + * Added the confirmation dialog support for mode changes + (Fixes: NB#99661) + * Updated mcetool to support the new mode change API + * Swapped portrait and portrait (inverted) + + -- David Weinehall Wed, 04 Feb 2009 09:14:00 +0200 + +mce (1.8.34) unstable; urgency=low + + * Added support for CABC to the display code + * Added strstr_delim() to mce-lib to allow finding substrings + that are token separated (without the lurking issues that strtok and + its ilk comes with) + * Removed direct reference to dsm.state + * Fixed bug that caused mce to misbehave if restarted + (MCE would remain indefinitely in transition submode) + + -- David Weinehall Tue, 03 Feb 2009 13:47:15 +0200 + +mce (1.8.33) unstable; urgency=low + + * Improved the error handling in mce-io.c when NULL-parameters are passed, + to ease debugging, and avoid crashes caused by, issues such as + NB#99351 and NB#98013 + * Added stubs to accept mode change with confirmation dialog requests; + for now no dialog is displayed, but the requests are at least handled + * Implemented memory for the orientation; if the orientation cannot be + discerned (device lies flat on a table), the last used orientation + will be reported + * Added reporting of inverted orientations + * Use mce_translate_int_to_string() in modetransition.c too, + to allow aliases to work properly + * Modify display blank behaviour to really do immediate blanking + (Fixes: NB#99066) + + -- David Weinehall Mon, 02 Feb 2009 18:03:45 +0200 + +mce (1.8.32) unstable; urgency=medium + + * Fixed incorrect reply handling in call state code + + -- David Weinehall Thu, 29 Jan 2009 20:14:19 +0200 + +mce (1.8.31) unstable; urgency=low + + * Cancel I/O monitor timeouts after an input-device rescan + (Fixes: NB#99351) + * Fixed policy 5 for LED and vibrator patterns + + -- David Weinehall Wed, 28 Jan 2009 19:46:26 +0200 + +mce (1.8.30) unstable; urgency=low + + * Added limited backwards compatibility for the call states; will be removed + as soon as all relevant services have been migrated to the new ABI/API + + -- David Weinehall Tue, 27 Jan 2009 17:32:44 +0200 + +mce (1.8.29) unstable; urgency=low + + * Call states changes are made atomic by means of only allowing + changes to/from the "none" state (an exception is made for emergency + calls) + | This means both an API and an ABI break, + | since the return type of the req_call_state_change D-Bus method + | is changed to a dbus_bool_t that indicates if the change + | was permitted or vetoed + * Call states are now "active"/"none"/"service" + | Use "service" for system services that need to block calls + | Note: unlike "active", the "service" call state does not alter + | the device lock and touchscreen/keypad lock behaviour + | Also note that emergency calls are still permitted even when + | the call state is "service" + * Build-Depends (mce-dev >= 1.8.6) for new call state API + * Added recommends on alarmd + + -- David Weinehall Tue, 27 Jan 2009 13:42:33 +0200 + +mce (1.8.28) unstable; urgency=medium + + * Fixed typo in verifybacklight + * Fixed possible NULL-pointer dereference in event-input.c + device rescanning code (Fixes: NB#98013) + | Thanks to Dafydd Harries + + -- David Weinehall Sun, 25 Jan 2009 18:13:30 +0200 + +mce (1.8.27) unstable; urgency=low + + * Adjusted the ALS-profile for the RGB LED + * Bumped the LED current for the backlight LEDs + * Simplified ALS-code a bit + * Fixed verifyled to set LED current + * Added verifybacklight script + * Removed call to "make test" from debian/rules, + since it does not behave as intended (Fixes: NB#99149) + * Increased low brightness for keyboard backlight a bit + * Fixed inverted check for alarm UI state in tklock keypress trigger + (Fixes: NB#99103) + + -- David Weinehall Fri, 23 Jan 2009 16:50:48 +0200 + +mce (1.8.26) unstable; urgency=low + + * Always generate activity *before* sending specific actions + for events; this should prevent activity policy from interfering + with the event policy + * Only generate activity from flicker key on slide, not on release + + -- David Weinehall Thu, 22 Jan 2009 16:15:39 +0200 + +mce (1.8.25) unstable; urgency=low + + * Improved tklock behaviour in interaction with call state + * Removed visual tklock mode again, since it's not used + + -- David Weinehall Wed, 21 Jan 2009 16:44:24 +0200 + +mce (1.8.24) unstable; urgency=low + + * Blank screen immediately when the touchscreen/keypad lock is enabled + * Send the updated orientation when display turns on if the orientation + has changed (Fixes: NB#97918) + * Fixed call state tests in mcetorture + + -- David Weinehall Wed, 14 Jan 2009 16:30:44 +0200 + +mce (1.8.23) unstable; urgency=low + + * Disable device autolock during calls even when the device is + otherwise idle (Fixes: NB#97627) + * Do not open device menu if the alarm UI is visible + (Fixes: NB#93776) + * Properly set LED current for keyboard backlight on the RX-51 + * Disable tklock temporarily when the alarm UI is shown + * Fixed initial accelerometer poll delay + * Fixed splashscreen timeout + * Changed modules/alarm.c to listen to alarmd signals to obtain + the alarm ui status instead of using a callback into MCE + * Removed device lock code tests from mcetorture + * Restored accidentally removed --set-mode help-text in mcetool + * Updated the manual page for mcetool according to the crapectomy + * Code cleanup + * Since the vibrator pattern for incoming calls/messages are overloaded + for other use, configure them to play even in acting dead + | We don't receive calls/messages in acting dead, so for their main + | use case there's no change in behaviour + + -- David Weinehall Tue, 13 Jan 2009 12:10:21 +0200 + +mce (1.8.22) unstable; urgency=low + + * Remove device autolock timeout on call and restore it again + when the call ends + * New path for vibra again + * Crapectomy: No more VoIP-mode remnants. Yay! + * Crapectomy: purge all SystemUI related options from + mcetool, and all corresponding torture tests from mcetorture + + -- David Weinehall Tue, 16 Dec 2008 18:29:33 +0200 + +mce (1.8.21) unstable; urgency=low + + * Build-Depend on a version of upstart-dev that contains + the fixed postinst script + * Added missing dh_installdirs call to debian/rules + * Added GConf schema for devicelock count + * Added script to be used for landscape/portrait testing + * Simplified mcetorture + + -- David Weinehall Fri, 05 Dec 2008 13:10:06 +0200 + +mce (1.8.20) unstable; urgency=low + + * Adapted Lysti support to new interface (Fixes: NB#92847) + * Implemented device autolock functionality (Fixes: NB#92659) + * Added Conflicts: libcodelockui1 (<< 1:1.5.5) to mce, + to properly take care of the transition to libdevlock + * Commented out MUSB cable detection for now, since it causes issues + * Added event-file for upstart + | Thanks to Philippe De Swert and Riku Voipio + * Code cleanup + + -- David Weinehall Wed, 26 Nov 2008 13:55:35 +0200 + +mce (1.8.19) unstable; urgency=low + + * New path for vibra + * Added script to be used for proximity sensor testing + * Removed device lock code related code and dependencies + + -- David Weinehall Mon, 17 Nov 2008 12:53:45 +0200 + +mce (1.8.18) unstable; urgency=low + + * Removed alarm features from mcetool, since the same functionality, + only better working, can be had from alarmtool + * Forward ported fix from Diablo for bug in filter-brightness-als + that caused the ALS to always be disabled by default + * Modified the tklock to use TKLOCK_ENABLE_VISUAL if there's a call + when tklock is active + | Conflicts: osso-systemui-tklock (<< 0.1.9.0) + | Build-Depends: osso-systemui-tklock-dev (>= 0.1.9.0) + * Added support for visual tklock mode to mcetool + * Bumped Build-Depends on osso-systemui-tklock-dev to 0.1.7.0 + * Added versioned Build-Depends on libglib2.0-dev (>= 2.14.0) for + g_timeout_add_seconds() + + -- David Weinehall Thu, 06 Nov 2008 16:37:21 +0200 + +mce (1.8.17) unstable; urgency=low + + * Update mce.ini for the new vibrator patterns + * Don't attempt to seek on non-seekable I/O channels + * Fixed vibrator pattern NULL-check + + -- David Weinehall Wed, 05 Nov 2008 14:18:03 +0200 + +mce (1.8.16) unstable; urgency=low + + * Actually register inactivity (Doh!) + * Initialise the inactivity timeout from modules/display.c + * Turned inactivity timeout into a READ/WRITE datapipe + * Added an inactivity_timeout_trigger to the inactivity module + | (The above four fixes together Fixes: NB#88928) + * Added test for visual tklock mode to mcetorture + | Not yet integrated in mce + tklock + * Removed PatternClockAlarm and PatternCalendarAlarm + * Added PatternPowerKeyPress + * Use PatternPowerKeyPress on power up from acting dead + * Fixed a bug in vibrator code that could lead to lower priority patterns + not being played when higher priority patterns timeout + * Added option to Vibrator patterns to play patterns even when + the vibrator is disabled by policy + * Added pattern count to Vibrator patterns + * Tuned the vibrator patterns + * Blank timeout should be 3, not 60 seconds + * Properly initialise inactivity timeout datapipe + * Remove unused blanking inhibit datapipe + * Made append_filter_to_datapipe() log an error message + if an attempt to append a filter to a READ ONLY pipe is made + * Made remove_filter_from_datapipe() log an error message + if an attempt to remove a filter from a READ ONLY pipe is made + * Removed the alarm submode and use the alarm ui state pipe instead + * Minor cleanup + + -- David Weinehall Wed, 05 Nov 2008 09:41:44 +0200 + +mce (1.8.15) unstable; urgency=low + + * Merged event-keyboard and event-touchscreen into event-input + * Added support for rescanning of /dev/input when devices are hotplugged + * Added inactivity tracking for non-keyboard/touchscreen /dev/input devices + * Added inactivity tracking for USB-cable + * Treat the camera button as a key as well + * Added mce_suspend_io_monitor and mce_resume_io_monitor to mce-io + * Modified the vibrator logic to ensure that the vibrator isn't + enabled during a call + * Enabled the vibrator by default + * Removed unnecessary includes (and added a missing one) + in connectivity.c, and added some comments + * Updated TODO-list (removed outdated and unneeded items) + + -- David Weinehall Thu, 30 Oct 2008 14:05:06 +0200 + +mce (1.8.14) unstable; urgency=low + + * Do not enable automatic touchscreen/keypad lock + if the keyboard slide is open + * Fixed inhibit code to behave properly in certain corner cases + * Added code to handle the new display dimming/blanking inhibit modes + * Added conflict against osso-applet-display (<< 1.4.2), since the + type and of the blanking inhibit GConf key has changed + * Added tests/inhibit to simplify testing of blank/dim inhibit + * Added Doxygen documentation + + -- David Weinehall Tue, 21 Oct 2008 03:45:22 +0300 + +mce (1.8.13) unstable; urgency=low + + * Fixed massive memory leak in modules/accelerometer.c + (Fixes: NB#89955) + * Added a GConf entry for disabling the ALS + * Only change tklock status based on proximity sensor + if there's a call on-going + * Added call state/type test to mcetorture + * Don't perform new VESA ioctl()'s every time we change brightness, + only do it when we turn display on/off + + -- David Weinehall Thu, 16 Oct 2008 22:12:32 +0300 + +mce (1.8.12) unstable; urgency=low + + * Added missing call state signalling + * Fixed accelerometer timeout function to always return true + * Fixed vibrator patterns to loop properly (Fixes: NB#89170) + * Made it possible to restart vibrator patterns after a timeout + * Made it possible to restart LED patterns after a timeout + * Fixed incorrect use of g_free() instead of g_strfreev() in module/led.c + * Always update device orientation when the orientation is requested + * Tune the accelerometer values for portrait/landscape a bit + * Reinstate missing transition code + + -- David Weinehall Tue, 07 Oct 2008 16:24:20 +0300 + +mce (1.8.11) unstable; urgency=low + + * Implemented: NR#190168 + * Modified mcetool to show device orientation + * Added full call state/type support to mcetool + * Fixed initial ALS timeout + * Fixed minor memory leak + * Made all users of g_timeout_add() with second resolution + use g_timeout_add_seconds() instead + * Enabled more compile time warnings + * Made mce_log use __attribute__((format(printf, 2, 3))) + * Declared a few functions as pure + + -- David Weinehall Thu, 02 Oct 2008 15:59:41 +0300 + +mce (1.8.10) unstable; urgency=low + + * Implemented: NR#192160 + * Handle notifications about unset GConf keys properly + * Restored battery/charger functionality + * Removed (possible) infinite loop in display blanking inhibit code + * Made charger connect/disconnect generate activity + * Changed path for vibrator for RX-51 + * Added vibrator support to mcetool + * Added Vibrator test to mcetorture + * Added code to test that mce copes with unsetting a GConf key + * Added code to test that mce copes with setting an unset GConf key + * Added test to mcetorture to test that mce is running and hasn't crashed + + -- David Weinehall Fri, 12 Sep 2008 16:43:44 +0300 + +mce (1.8.9) unstable; urgency=low + + * Reactivate the Lysti engines in reverse order + + -- David Weinehall Thu, 04 Sep 2008 16:17:52 +0300 + +mce (1.8.8) unstable; urgency=low + + * Touchscreen I/O monitoring + + -- David Weinehall Tue, 02 Sep 2008 16:58:31 +0300 + +mce (1.8.7) unstable; urgency=low + + * Implemented: NR#192335 + * Improved error handling/reporting in mce-io + * Fixed a bug in mce_translate_int_to_string_with_default() + * Added code to enable/disable LED pattern for soft poweroff + * Added build-tests for the *_with_default translation functions + * Disable old Lysti patterns before programming new ones + * Added full support for the lens cover + * Added input event support for TSC2005 + * Added missing LED patterns for RX-51 + * Fixed path to RX-51 ALS + * Made the mce-conf build-test verify that mce.ini is a valid conf-file + * Comment improvements + * Minor cleanup + + -- David Weinehall Tue, 02 Sep 2008 05:29:24 +0300 + +mce (1.8.6) unstable; urgency=low + + * Implemented: NR#185610 + * Disable vibrator during on-going call + * Added mce_translate_int_to_string_with_default(), and + mce_translate_string_to_int_with_default() to mce-lib + * Modified soft poweroff policy parsing to use the translation functions + * Added LED pattern for soft poweroff + * Added option to wake up from soft poweroff when charger is connected + * Added partial support for call state/type to mcetool + * Added RX-51 keyboard backlight support + * Added Lysti LED support for the RX-51 + | Patterns still need to be converted to Lysti format though + + -- David Weinehall Tue, 19 Aug 2008 14:14:14 +0300 + +mce (1.8.5) unstable; urgency=low + + * Implemented: NR#190169 + * Added accelerometer D-Bus API + | Still just stub functions behind it though, so the returned values + | will only be dummy ones (valid, but not reflecting reality) + * Fixed various incorrect comments + + -- David Weinehall Wed, 02 Jul 2008 13:38:02 +0300 + +mce (1.8.4) unstable; urgency=low + + * Implemented: NR#191055 + * Implemented: NR#185609 + * Added a call state module + * Added RX-51 support to filter-brightness-als + * Added missing "Depends: dbus" to mce and mcetools + | Thanks to Guillem Jover for pointing this out + * Created int<->string translation functions and modified + modetransition.c and callstate.c to use those + * Added a build-test for the translator functions + + -- David Weinehall Tue, 01 Jul 2008 15:43:55 +0300 + +mce (1.8.3) unstable; urgency=low + + * Build-Depends: mce-dev (>= 1.8.1) + * Removed reference to MCE_VOIP_MODE + | This is just to make mce build with mce-dev (>= 1.8.1); + | the real call API will be implemented later on + * Remove duplicate charger state trigger from modules/display.c + + -- David Weinehall Tue, 27 May 2008 14:37:01 +0300 + +mce (1.8.2) unstable; urgency=low + + * Partial: NR#192160 + * Added a vibrator module + * Added a stub accelerometer module + * Move the median-filter code into a file of its own, + since it will probably be used from several places + * Added a display module + | For brightness, dimming, and blanking logic (Fixes: NB#74806) + * Updated inactivity module to handle inactivity + * Made LED component a proper module + * Ripped out a lot of code from mce-dsme that is handled by DSME now + (bye, bye D-Bus proxying!) or isn't needed because the display + and inactivity handling was moved to MCE + * Depend on mce-dev >= 1.8.0 for new D-Bus interfaces + * Minor work on mce-module + * Added support for new keyboard + * Added stub support for the shutter button + * Added stub support for lens cover + * Shutdown on long [power] press even when dsme reports UNDEF state + + -- David Weinehall Mon, 19 May 2008 20:21:19 +0300 + +mce (1.8.1) unstable; urgency=low + + * cal has a .pc file again; libcal.pc + * Forward-port alarm fix from mce 1.7.19 + + -- David Weinehall Thu, 03 Apr 2008 09:33:09 +0300 + +mce (1.8.0) unstable; urgency=low + + * Partial: NR#190177 + * libcal has been split out to its own package; + change the build-dependency accordingly + * Temporarily work around missing cal.pc; hopefully it will be restored + in the next version of libcal-dev + * Build-Depend on libdsme0.2.0-dev instead of libdsme0-dev + * Adapt mce-dsme to new API + | Thanks to Semi Malinen + * Added RX-51 support to led.c + * Added proximity sensor support to event-switches.c + * Bumped the version# in the Doxyfile to 1.8.x + * Added support for Triton 2 powerbutton + * Disabled/removed code related to bme-dbus-proxy; + this needs to be adapted to use HAL + * Moved product ID check to a separate file, as the first step + to abstract hardware a bit more + + -- David Weinehall Tue, 01 Apr 2008 18:27:13 +0300 + +mce (1.7.18) unstable; urgency=low + + * Do not send alarm state change to DSME if the alarm UI state changes; + hopefully this fixes: NB#80520 + * More code cleanup + * Corrected various dependency and section information + + -- David Weinehall Wed, 26 Mar 2008 15:47:22 +0200 + +mce (1.7.17) unstable; urgency=low + + * Fixed various warnings, comments, and added a missing include + * syslog is now located where it should be (/var/log), + fix mcetorture accordingly + * Fixed mcetorture to be able to set unset GConf keys + * Turn the warnings about attempts to remove non-existing triggers + into informational messages instead, since such attempts are ignored + anyway, and since the only remaining such warning is a false positive + + -- David Weinehall Fri, 14 Mar 2008 23:01:34 +0200 + +mce (1.7.16) unstable; urgency=low + + * Fixed a bug in datapipe.c with reference counting + + -- David Weinehall Fri, 14 Mar 2008 17:05:12 +0200 + +mce (1.7.15) unstable; urgency=low + + * Rewrote the connection status tracking further; use libconic instead + of the BlueZ and WLANCond D-Bus interfaces; this way we also get + information about open WiMAX connections (Fixes: NB#80813) + + -- David Weinehall Wed, 12 Mar 2008 15:41:50 +0200 + +mce (1.7.14) unstable; urgency=low + + * Since mce.init uses files from /usr, we need $remote_fs, not $local_fs + * Removed unused function from modules/battery.c + + -- David Weinehall Tue, 26 Feb 2008 13:44:47 +0200 + +mce (1.7.13) unstable; urgency=low + + * Split out the battery handling to a module, as yet another step + in the code cleanup + * Made adjustments to the DeviceOn pattern for RX-48 + * Made the PowerOn and PowerOff patterns for RX-48 orange as well + + -- David Weinehall Fri, 22 Feb 2008 12:32:16 +0200 + +mce (1.7.12) unstable; urgency=low + + * Made configuration file handling more error resilient; + now the default values will be used if the configuration file + is invalid or non-existing + * Bumped the version# in the Doxyfile to 1.7.x + * Minor cleanup + + -- David Weinehall Mon, 18 Feb 2008 16:25:27 +0200 + +mce (1.7.11) unstable; urgency=low + + * Removed build-dependency on osso-gwconnect-dev + + -- David Weinehall Fri, 08 Feb 2008 10:29:56 +0200 + +mce (1.7.10) unstable; urgency=low + + * Use BlueZ to get connection status for BT instead of BTCond + (Fixes: NB#78985) + * Unblank screen when lock key is pressed + (Fixes: NB#78446) + + -- David Weinehall Thu, 07 Feb 2008 17:31:17 +0200 + +mce (1.7.9) unstable; urgency=low + + * Modified tklock to make sure that the touchscreen/keypad lock isn't + deactivated by the initial touchscreen tap when enabling the lock + (Fixes: NB#80164) + * Made /etc/mce/mce.init report status in a working way + (Fixes: NB#79379) + + -- David Weinehall Wed, 06 Feb 2008 18:18:13 +0200 + +mce (1.7.8) unstable; urgency=low + + * Fixed a copy'n'paste error introduced in mce 1.7.6, + that caused mono-patterns not to work. + + -- David Weinehall Fri, 01 Feb 2008 16:06:35 +0200 + +mce (1.7.7) unstable; urgency=low + + * debian/control: Bumped Standards-Version to 3.7.3 + | No changes required + * Fixed a few Lintian warnings + + -- David Weinehall Fri, 01 Feb 2008 12:04:19 +0200 + +mce (1.7.6) unstable; urgency=low + + * Made it possible to have different LED patterns for different products + * Made the DeviceOn pattern for the RX-48 orange + * Disable touchscreen and keypad events on soft poweroff + (Fixes: MB#2400) + + -- David Weinehall Wed, 30 Jan 2008 09:33:50 +0200 + +mce (1.7.5) unstable; urgency=low + + * Bumped channel size for NJoy patterns to properly fit + 16 commands / channel + * Send a PERIPHERAL_ACTIVITY message to DSME on lockkey trigger + (Fixes: NB#76607) + + -- David Weinehall Mon, 26 Nov 2007 08:34:26 +0000 + +mce (1.7.4) unstable; urgency=low + + * Properly unreference resources in mce_unregister_io_monitor + * Fixed broken test in mcetorture + + -- David Weinehall Tue, 13 Nov 2007 12:01:38 +0200 + +mce (1.7.3) unstable; urgency=low + + * Only allow display dim/blank when in normal user mode + (Fixes: NB#72832) + * Added reference count callback functionality to datapipes + * Made event-touchscreen use the new functionality to disable + the touchscreen monitoring when no triggers/filters are registered + * Don't forget to send shutdown indication when going to + ACTDEAD state from USER state + * Enabled more warnings at build-time + * Fixed a (harmless) warning in mce-io + + -- David Weinehall Thu, 01 Nov 2007 15:22:30 +0200 + +mce (1.7.2) unstable; urgency=low + + * Made keyboard backlight timeout and fade time configurable + + -- David Weinehall Sat, 20 Oct 2007 06:57:14 +0300 + +mce (1.7.1) unstable; urgency=low + + * The LED framework keeps track of the display state itself; + do not enable/disable patterns based on display state from other places + * Improved logging for cases when we get un-expected states from DSME + * Improved the error handling in daemonize() + * Code cleanup + + -- David Weinehall Sat, 20 Oct 2007 06:10:22 +0300 + +mce (1.7.0) unstable; urgency=low + + * Updated manual pages to cover new features of mcetool and mcetorture + * Fixed lintian complaints about (correctly used) hyphens in the manual + pages by explicity using the escape codes instead + * Changed deprecated substvar Source-Version in debian/control to + binary:Version instead + * Do not send BLANK_ALLOW immediately on charger disconnect or change of + the "keep display on with charger" option if there is a timeout + in place (NB#72905) + * Disable [power] until power on transition has finished (NB#52440) + * Do not send BLANK_ALLOW to DSME if the charger is connected and the + "keep display on with charger" option is set (NB#72492) + * Fixed vararg leaks in error paths of mcetool and mce-dbus + * Fixed possible return of uninitialised value from + mce_read_number_string_from_file() + * Made mce_read_number_string_from_file() only assign a value + if the read and conversion was successful; otherwise only + return FALSE; this way the caller can pass a default value + into the function without having it modified if the conversion fails + * Fixed possible leak in change_devlock_code() + * Fixed possible overrun of static array in validate_devlock_code() + * dbus_connection_send_with_reply() can return pending_return == NULL + if the D-Bus connection has been disconnected; handle this case properly + * Use a longer timeout and fadeout time for the keyboard backlight + * Fixed incorrect default timeout used for device lock shutdown + + -- David Weinehall Sat, 20 Oct 2007 06:07:11 +0300 + +mce (1.6.41) unstable; urgency=low + + * Added support for solid light for the mono-LED patterns, + to properly fix PatternBatteryFull + * Adjusted PatternDeviceOn to consume less power + * Fixed GConf defaults in mcetool to display sane data if keys are unset + * Added the missing PatternCommonNotification to mce.ini + * Fixed name of connectivity soft poweron policy in mce.ini + * Added GConf brightness testcase to mcetorture + * Added GConf timeouts testcase to mcetorture + * Added GConf LED testcase to mcetorture + * Added alarm state testcase to mcetorture + * Added error injections (invalid number of arguments, invalid data, + invalid type) for D-Bus to mcetorture + * Made notes in mcetorture of what tests only torture SystemUI + rather than mce and added an option to disable those + + -- David Weinehall Mon, 01 Oct 2007 00:20:44 +0300 + +mce (1.6.40) unstable; urgency=low + + * Made [home] trigger on input instead of output + (Fixes: NB#70919) + * Made [power] trigger on input instead of output + * One more error path memory leak fix for device lock and device menu UI + reply handler + | Phew, this is getting repetitive... + * Fixed mono-LED patterns for DeviceOn, PowerOn, PowerOff, and BatteryFull + * Fixed name of powerkeymenu-ui testcase in mcetorture + * Made the powershort and powerlong mcetorture testcases use the + D-Bus interface instead + * Improved mcetorture abort-logging a bit + * Added a bit of input torture to mcetorture (error-events) which + injects incorrect data into /dev/input/eventX + + -- David Weinehall Tue, 25 Sep 2007 16:44:41 +0300 + +mce (1.6.39) unstable; urgency=low + + * Adjusted webcam pattern (Fixes: NB#65756) + * Fixed visibility settings for mono-LED Communication-patterns + (Fixes: NB#70645) + + -- David Weinehall Mon, 24 Sep 2007 15:33:15 +0300 + +mce (1.6.38) unstable; urgency=low + + * Fixed leaks in error paths for device lock and device menu UI reply + handling + + -- David Weinehall Sun, 23 Sep 2007 03:21:42 +0300 + +mce (1.6.37) unstable; urgency=low + + * Fixed memory leaks in device lock and device menu UI reply handling + * Modified datapipes to handle the cache better + | This fixes a problem where restarts of systemui could trigger + | the splashscreen + * Fixed typo in debian/mce.zzinitdone.init + * Removed network_state_pipe since it was unused + * Reprogram timer for long [home] key press timeout if we receive another + down event without an up-event between + | The same change was made earlier for [power], but somehow I forgot to + | fix [home] too, while at it + * Made mce-io a bit more resilient to errors, and made it report + in a better way what's gone wrong if something goes haywire + * Added options to continue on error, + and to disable leak checking completely, to mcetorture + * Added a simple, injection based, test of the powerkey menu to mcetorture + * Added injection of short and long [home] and [power] keypresses + into the event files to the testcases in mcetorture + * Fixed the error message printed by the abort function in mcetorture + * Better handling of the case where both the powerbutton and home key + are provided from the same event-file + * Code simplification in event-touchscreen and event-keypress + + -- David Weinehall Thu, 20 Sep 2007 20:55:27 +0300 + +mce (1.6.36) unstable; urgency=low + + * Fixed mistakenly trunkated PowerOff pattern + * Allow the NJoy LED brightness to be in the range from 0-3 instead of 1-3 + * Modify the brightness profiles, to make the LED to be dimmer in darkness + (Fixes: NB#69504) + * Doh! Missed the stop script for the most important runlevel in the kludge + + -- David Weinehall Mon, 17 Sep 2007 15:58:42 +0300 + +mce (1.6.35) unstable; urgency=low + + * Fixed segfault when unregistering an I/O monitor + * Modified error-handling for enabling/disabling touchscreen/keypad; + this should avoid further bugs like NB#69425 + * Minor cleanup + * Shortened PatternPsychedelia; one of the channels contained too many + instructions + * Added an ugly kludge to work around the lack of an init system with + startup notifications (Fixes: NB#62729) + * Fixed bugs in fakecharger test script + + -- David Weinehall Thu, 13 Sep 2007 17:35:24 +0300 + +mce (1.6.34) unstable; urgency=low + + * Added code to flush the median filter and re-read the ALS + after a screen-blank + | compile-time option + * Added module tests for mce-io + | Only partial coverage for now + * Fixed a few issues found in mce_read_number_string_from_file() + * Remove transition timeout properly on exit + * Tuned PowerOn and PowerOff patterns (partial fix for NB#65756) + * Fixed file descriptor leaks in mce_keypress_exit and mce_touchscreen_exit + + -- David Weinehall Tue, 11 Sep 2007 19:16:02 +0300 + +mce (1.6.33) unstable; urgency=low + + * Fixed return value of tklock_mode_change_req_dbus_cb when no_reply is set + * Fixed memory leaks in mce-io.c (Fixes: NB#65714) + * Fixed memory leak in led.c and made the strncpy handling a bit safer + | Thanks to Semi Malinen for finding + * Fixed memory leak in mce-dbus.c + | Thanks to Semi Malinen for finding + * LED patterns tuned (partial fix for NB#65756) + * Modified the D-Bus msg handler to process all handlers for a signal, + not just the first one, mainly to allow modules to listen to the same + signals as the main process does + * Added support for new keyboard interrupt disabling sysfs path + * Fixed two memory leaks on failed D-Bus startup attempts; these have no + practical significance, since MCE will abort if D-Bus startup fails, + but correctness is nice anyway + * Fixed crash in mcetool when using `--set-tklock-mode' + * Fixed crash in mcetool when using `--alarm=switchon' + * Added ctags support to the Makefile + * Removed unnecessary error-message from the same function + * Added support for silent unlocking of tklock + * Added a tklock test to mcetorture + * Added a LED test to mcetorture + * Added a voip mode test to mcetorture + * Improved various mcetorture tests + * Updated mcetorture manual page + * Added a `--dont-block' option to mcetool, that causes options + that normally blocks to return directly, to allow for more torture tests + | Note: this means that MCE will fail to send the reply to mcetool, + | since mcetool will no longer be running by the time MCE sends the reply + * Added LED testing support to mcetool + * Cleaned up the D-Bus error-handling in mcetool a little + * Updated the mcetool manual page + * Removed the ifdef's for MCE_VERSION_GET from mcetool; + MCE_VERSION_GET has been in MCE since v1.1.6... + * Fixed warnings in test_mce-log.c found with gcc 4.2.1 + + -- David Weinehall Fri, 07 Sep 2007 14:09:00 +0300 + +mce (1.6.32) unstable; urgency=low + + * Fixed debug/regular packages to be built with -O2 and -g (Fixes: NB#65718) + + -- Philippe De Swert Thu, 30 Aug 2007 14:04:09 +0300 + +mce (1.6.31) unstable; urgency=low + + * Fixed state of lockkey (Fixes: NB#66674) + + -- Ismo Laitinen Mon, 27 Aug 2007 09:30:27 +0300 + +mce (1.6.30) unstable; urgency=low + + * Added debug packages (Fixes: NB#65718) + + -- Philippe De Swert Fri, 17 Aug 2007 13:29:46 +0300 + +mce (1.6.29) unstable; urgency=low + + * Use charger_connected/charger_disconnected rather than + charger_charging_on charger_charging_off for display inhibit + (Fixes: NB#65061) + * Ignore shutdown requests and soft poweroff requests when + the touchscreen/keypad lock is active (Fixes: NB#63565) + * Made the delay before dimming on tklock configurable + and increased the timeout + * Fixed the tklock code to disable the dim timeout when unlocking + * Initialise the cover state on mce startup (Fixes: NB#64934) + + -- David Weinehall Tue, 07 Aug 2007 13:27:32 +0300 + +mce (1.6.28) unstable; urgency=low + + * Added an option to mce_register_io_monitor* to choose seek behaviour + * Made monitors from streams not seek to the beginning before reading; + this fixes issues with empty reads + + -- David Weinehall Wed, 01 Aug 2007 17:10:30 +0300 + +mce (1.6.27) unstable; urgency=low + + * Do not perform ALS readings when the display is blanked + * ALS profile adjustments + * Minor cleanup + + -- David Weinehall Tue, 31 Jul 2007 12:15:43 +0300 + +mce (1.6.26) unstable; urgency=low + + * Do not enable keyboard backlight if we're in other states than USER, + unless the alarm dialog is visible + * Disable the keyboard backlight when system state changes to other + states than USER + * Made the version# check a tiny bit smarter + * Reprogram timer for long [power] key press timeout if we receive another + down event without an up-event between + * Fixed two cases of incorrect usage of datapipe_get.*() macros + in tklock.c + * Added () in the right places to the datapipe_get.*() macros + to avoid future problems of the same kind + + -- David Weinehall Wed, 25 Jul 2007 14:34:40 +0300 + +mce (1.6.25) unstable; urgency=low + + * Send alarm invisible signal to DSME before shutdown on long [power] + keypress when alarm UI visible (Fixes: NB#51321) + + -- David Weinehall Tue, 17 Jul 2007 16:52:40 +0300 + +mce (1.6.24) unstable; urgency=low + + * Added module test for input_event; unfortunately this test + needs to be run as root, so it cannot be enabled by default; + also, it's platform specific for now; it will only work on + a standard PC + * Moved the modules to a sub-directory of its own in the source + * Doh! Fixed names of two keys in mce.ini + * Added LL_NONE, to make it possible to completely disable logging + * Fixed double free in mce-dsme.c + + -- David Weinehall Tue, 17 Jul 2007 16:05:43 +0300 + +mce (1.6.23) unstable; urgency=low + + * Added module test for mce-log + + -- David Weinehall Mon, 16 Jul 2007 23:08:39 +0300 + +mce (1.6.22) unstable; urgency=low + + * Modified the device lock code to listen for the reply and only + activate the lock when locking actually takes place; + this is to handle the lock code verification sequence properly + * Added (disabled by default) support for double presses of [power] + * Soft poweroff fixes + * Fixed typo in mce.ini + * Initialise system_state_pipe to MCE_STATE_UNDEF + + -- David Weinehall Thu, 12 Jul 2007 18:21:48 +0300 + +mce (1.6.21) unstable; urgency=low + + * Fixed error handling in mce-conf + * Begun work adding module tests; if any of the module tests fail, + building the Debian package will abort but the rest will still build, + which allows crap to be tested, but not shipped... + * Disabled the I/O monitoring for event-switches that we only track the + state of for inactivity + * Further adjustments of the PowerOn and PowerOff RGB patterns + * Added METHOD_RETURN handler to mce-dbus; nothing uses it yet though + * Execute the system state pipe on UI startup, to make the device lock + work on bootup again + + -- David Weinehall Tue, 10 Jul 2007 16:00:25 +0300 + +mce (1.6.20) unstable; urgency=low + + * The "shiniest Mode Controller Ever" release + * Converted event-touchscreen and event-keypress to use the new + I/O monitor interface for binary chunks + * Added headphone jack sense, MMC cover, device cover, battery cover, + and USB cable activity tracking + * Various minor cleanup + * Added D-Bus interfaces for requesting display dimming and display blanking + * Bumped build-dependency on mce-dev + * Added mcetool options to dim and blank display + * Added mcetorture tests for dim and blank display + * Introduced a datapipe for alarm state + * Moved (part of) the alarm handling into a separate file + * Made the tklock handling properly trigger on display + * Modified the tklock logic to re-disable touchscreen and keypresses + when the alarm is snoozed or acknowledged and the tklock is active + * Moved all logic for the touchscreen and keypress lock to tklock.[ch] + * Moved all logic for the device lock to devlock.[ch] + * Introduced a simplified I/O monitor for file changes + + -- David Weinehall Tue, 03 Jul 2007 18:54:59 +0300 + +mce (1.6.19) unstable; urgency=low + + * Added missing brackets to free_datapipe (Fixes: NB#61744) + * Adjusted more patterns + * Adjusted logic to enable keyboard backlight if the display state changes + from off to on and the slide is open + | This is not perfect, since unblanking can be triggered + | by a D-Bus request from an application too, but for now it's good enough + * Moved keypress event handling from powerkey.[ch] and keypad.[ch] to + event-keypress.[ch] + * Introduced event-touchscreen.[ch] for touchscreen events + | Right now only taps are monitored + * Introduced event-switches.[ch] for switch events + | Right now only the keyboard slide is supported + * Modified logic to disable auto-relock if the touchscreen + is tapped when the slide is open + * Moved [home] handling into a separate module + * Began adding inactivity tracking + * Fixed memory leak in mce-io.c + + -- David Weinehall Wed, 27 Jun 2007 23:36:24 +0300 + +mce (1.6.18) unstable; urgency=low + + * Added a trigger that unblanks the screen when the slide status changes + (Fixes: NB#61596) + * Added proper DBusError initialisation to connectivity status request + * Fixed bug in autorelock logic + * Adjusted PowerOff pattern (Fixes: NB#61396) + * Fixed really stupid init-script bug that could be triggered if + /var/run/mce existed but was a file rather than a directory + * Modified debian/rules not to ignore make clean errors + + -- David Weinehall Mon, 25 Jun 2007 19:39:34 +0300 + +mce (1.6.17) unstable; urgency=low + + * Fixed D-Bus handler to make sure we don't eat signals; only errors + and method calls that are addressed to us should be marked as handled + + -- David Weinehall Thu, 21 Jun 2007 16:08:46 +0300 + +mce (1.6.16) unstable; urgency=low + + * Corrected the name of the filter-brightness-als module in mce.ini + * Fixed cooking of uncooked brightness values in mce-dsme + * Simplified filter-brightness-simple a bit further + + -- David Weinehall Thu, 21 Jun 2007 15:53:48 +0300 + +mce (1.6.15) unstable; urgency=low + + * Fixed incorrect calls to dbus_message_get_args in powerkey.c, + tklock.c, devlock.c, and tools/mcetool.c + * Fixed an incorrect error-message + * Improved the error reporting when failing to get D-Bus arguments + * Updated front key backlight path + * Fixed LED code to properly initialise LED brightness pipe + * Minor cleanup + + -- David Weinehall Mon, 18 Jun 2007 18:53:07 +0300 + +mce (1.6.14) unstable; urgency=low + + * Made the submode-operations use the submode pipe + * Removed data-retrieval safeguards again; they caused more problems + than they solved + * Added fugly hack for mode tracking (Fixes: NB#59872) + * Made the camera popout unlock behaviour a configuration option + * Modified the tklock code to use silent mode if the same submode + is activated twice + + -- David Weinehall Thu, 07 Jun 2007 16:44:56 +0300 + +mce (1.6.13) unstable; urgency=low + + * Added support for ALS-controlled LED brightness + * Turned LED pattern enabling/disabling into datapipes, + to allow for easy use from modules; this also means that the LED code + should be possibly to turn into a module + * Added PowerOn/PowerOff patterns for NJoy + * Added support for new input device names for keyboard/keypad + * Added a camera module to control camera pattern + * Added code to disable the tklock when camera is popped out on the N800 + * Removed a workaround from mce-dsme + * Refactored more code to make use of the state_pipe; + as a result set_dsmestate/get_dsmestate has been eliminated + * Added code to query DSME-state on startup + to make sure that MCE has the correct state information + * Added safeguards to data-retrieval macros + + -- David Weinehall Tue, 05 Jun 2007 19:02:52 +0300 + +mce (1.6.12) unstable; urgency=low + + * Modified mce_register_io_monitor_string to always return NULL + if setting up a monitor fails + * Re-enabled mono-patterns + + -- David Weinehall Thu, 31 May 2007 16:30:29 +0300 + +mce (1.6.11) unstable; urgency=low + + * Added median-filter to ALS filter + * Added error-handling for incorrect number of fields in a LED pattern + | Thanks to Amit Kucheria + * Fixed typo in example pattern + * More abstraction using datapipes + + -- David Weinehall Wed, 30 May 2007 14:09:37 +0300 + +mce (1.6.10) unstable; urgency=low + + * Modified the ALS module to only poll every 60 seconds when screen + is blank (for adjustment of LED brightness; this is not done yet though) + * Only update display brightness if the display is on + * Only update the keyboard backlight brightness if it changed + and the display is on (Fixes: NB#58326) + * Modified backlight code to disable keyboard backlight + when the screen dims or blanks + * Various minor optimisations + * Added another simple test script, this one to test charger related + events + * Made minor correctness fixes to mcetorture + + -- David Weinehall Fri, 25 May 2007 15:48:45 +0300 + +mce (1.6.9) unstable; urgency=low + + * Fixed logic for automagic relocking (Fixes: NB#57217) + + -- David Weinehall Tue, 22 May 2007 15:45:37 +0300 + +mce (1.6.8) unstable; urgency=low + + * Modified devlock to close the devlock UI when VoIP-mode is enabled + * Modified the pattern behaviour to allow for some patterns to be + visible even in acting dead mode, and made the charging + battery full + patterns use this new functionality (Fixes: NB#57376) + * Added further adjustments to dsme_set_disp_brightness + + -- David Weinehall Mon, 21 May 2007 14:54:56 +0300 + +mce (1.6.7) unstable; urgency=low + + * Added LSB-section to the init script + * Added code to adjust brightness if it arrives raw + to dsme_set_disp_brightness (Fixes: NB#57668) + * Disabled mono-patterns for now, to work around a kernel oops + + -- David Weinehall Mon, 21 May 2007 12:33:17 +0300 + +mce (1.6.6) unstable; urgency=low + + * Modified filter-als to use a 5-step brightness scale for the display, + and map the ALS brightness profiles to these steps + * Modified mce-gconf to handle unset values in a proper way + (Fixes: NB#57030) + + -- David Weinehall Tue, 15 May 2007 16:41:24 +0300 + +mce (1.6.5) unstable; urgency=low + + * Build-Depend on libdsme0-dev (>= 0.57) for new brightness interface + * Abstracted out some more I/O functions to mce-io.[ch] + * Fixed a tiny memory leak + * Modified the device menu to reopen if the system mode changes + (Fixes: NB#53241) + * Fixed typo in usage information + * Implemented ALS filtering and added ALS-policies + for the display and keyboard backlight + * Fixed backlight bug on N800 hardware (Fixes: NB#56253) + + -- David Weinehall Mon, 14 May 2007 22:39:49 +0300 + +mce (1.6.4) unstable; urgency=low + + * Modified the LED section of mce.ini to better match new LED code + * Added support for RGB-patterns + * Modified the tklock to use a different tklock message + if the device has a flicker key + + -- David Weinehall Tue, 08 May 2007 17:01:14 +0300 + +mce (1.6.3) unstable; urgency=low + + * Added English and Swedish manual pages for mcetorture + * Various minor manpage fixes + * Rewrote the filter system to be more generic; it can now take + triggers as well as filters, and other types than gint + * MCE now has proper support for module loading and unloading; + the list of, and path to, the modules to load is in /etc/mce/mce.ini + * Inactivity handling is now a real module + * Fixed a display brightness issue on startup + * Modified the pattern list to Use memory slices + * Abstracted out some I/O functions to mce-io.[ch] + * Added support for keyboard/keypad backlight + * Added support for keyboard slide + * Added support for lock flicker key + * Various code refactoring + + -- David Weinehall Thu, 26 Apr 2007 14:25:50 +0300 + +mce (1.6.2) unstable; urgency=low + + * Initial support for lock key + * Added more options for touchscreen/keypad lock behaviour + * Renamed the group for tklock related configuration options + from "Device Menu" to "TKLock" in mce.ini + | We might want to add back a "Device Menu" group some time in + | the future, but for now it's unused, and thus removed + * Added options to force logging to stderr or syslog + * Added a filter system; filter chains can now be registered, + to allow for fun stuff such as ambient light sensors + * Added module loading + * Added an ALS filter plugin (for now just a stub) + + -- David Weinehall Thu, 12 Apr 2007 10:56:16 +0300 + +mce (1.6.1) unstable; urgency=low + + * Corrected blanking inhibit + * Added missing #include + * Cleanup + * Removed "-DDBUS_API_SUBJECT_TO_CHANGE"; we're now using D-Bus 1.0! + + -- David Weinehall Tue, 13 Mar 2007 14:40:54 +0200 + +mce (1.6.0) unstable; urgency=low + + * Do not return FALSE from mce_set_mode if mode is same as old mode + (Fixes: NB#51098) + * Removed inactivity state workaround + | API-break! + * Install mcetorture as part of mcetools + * Added `--no-status' option to mcetool + * Fixed a memory leak in get_version + * Added support for keeping the display on when the charger is connected + * Improved mce-log to support logging to stderr + * Modified MCE to log to stderr if `--daemonflag' isn't specified + * Do not close shared D-Bus connections + * Readded testmode (renamed to `--debug-mode') option, + to allow running MCE when DSME isn't present + * Split {kp,ts}_{en,dis}able into separate functions + * debian/control: Bumped Standards-Version to 3.7.2 + | No changes required + + -- David Weinehall Wed, 7 Mar 2007 09:21:12 +0200 + +mce (1.5.25) unstable; urgency=low + + * Blank immediately when user activates the tklock manually + | The old behaviour is still available as a configation option + | in mce.ini (Fixes: NB#50627) + * Cleanup + + -- David Weinehall Fri, 19 Jan 2007 14:32:17 +0200 + +mce (1.5.24) unstable; urgency=low + + * Export is_eveater_enabled + * Always enable touchscreen/keypad on unblank in user state + * Disable event eater when screen is unblanked + (Fixes: NB#49404) + + -- David Weinehall Thu, 14 Dec 2006 12:38:55 +0200 + +mce (1.5.23) unstable; urgency=low + + * Added support for DSM_MSGTYPE_STATE_ACTIVE_IND (Fixes: NB#48722) + + -- David Weinehall Thu, 30 Nov 2006 14:08:10 +0200 + +mce (1.5.22) unstable; urgency=low + + * Never allow device autolock when in VOIP-mode; should simplify the + device autolock logic (Fixes: NB#48329) + * Delay for 10 seconds after a modetransition has begun before allowing + long powerkey press (Fixes: NB#47594, NB#47797) + + -- David Weinehall Wed, 29 Nov 2006 14:07:21 +0200 + +mce (1.5.21) unstable; urgency=low + + * Modify LED framework to recompute the LED stack on display state changes + (Fixes: NB#47116) + * Re-enable touchscreen/keypad when unlock is requested (Fixes: NB#47745) + * Added D-Bus signal and method call for display state + * Cleanup + + -- David Weinehall Thu, 23 Nov 2006 12:53:02 +0200 + +mce (1.5.20) unstable; urgency=low + + * Added screen_on argument to LED patterns (Fixes: NB#44276) + * Use new LED framework interface, to allow idle retention + * Minor manpage fixes + * Do not activate the PowerOn and PowerOff patterns by default + * Disable charging pattern when battery is full (Fixes: NB#45415) + + -- David Weinehall Mon, 30 Oct 2006 13:42:07 +0200 + +mce (1.5.19) unstable; urgency=low + + * Added PatternCommunicationEvent to the list of configured patterns + (Fixes: NB#43780) + * Do not disable the LED if we're exiting MCE because of shutdown + (Fixes: NB#43783) + * Disable PowerOn-pattern when desktop startup signal arrives + (Fixes: NB#43784) + + -- David Weinehall Thu, 19 Oct 2006 16:26:41 +0300 + +mce (1.5.18) unstable; urgency=low + + * Fixed typo in mce.8 + * Base BOOTUP-flag on /var/run/mce/call, not on /var/run/mce.pid, + since the latter does not exist when we're started using dsmetool + (Fixes: NB#42667) + * Fixed init-script bug + + -- David Weinehall Tue, 10 Oct 2006 11:58:40 +0300 + +mce (1.5.17) unstable; urgency=low + + * Display LED patterns for poweron and poweroff (Fixes: NB#41214) + * Moved /var/lib/mce/call to /var/run/mce/call since this state file + does not need to be preserved across boots (Fixes: NB#41357) + * Updated LED patterns + + -- David Weinehall Wed, 27 Sep 2006 15:49:20 +0300 + +mce (1.5.16) unstable; urgency=low + + * Modified to build against the new osso-systemui-dbus-dev package + + -- David Weinehall Fri, 22 Sep 2006 14:20:04 +0300 + +mce (1.5.15) unstable; urgency=low + + * Activate Battery LED patterns when needed + | Thanks to Ismo Laitinen + * Listen to BME signals not method calls + | Thanks to Ismo Laitinen + + -- David Weinehall Tue, 19 Sep 2006 14:43:46 +0300 + +mce (1.5.14) unstable; urgency=low + + * Check if the powerkey_timeout_cb_id is set before removing it + (Fixes: NB#39424) + * Adapt MCE to the new touchscreen disable sysfs-interface. The value + written is also reversed. (Fixes: NB#40600) + + -- Ismo Laitinen Mon, 18 Sep 2006 11:47:08 +0300 + +mce (1.5.13) unstable; urgency=low + + * Added more charging patterns + * Show the DeviceOn pattern whenever the screen is blanked; + this will definitely make our usetime targets impossible to meet + * Use G_STRINGIFY instead of own implementation + + -- David Weinehall Wed, 30 Aug 2006 17:17:30 +0300 + +mce (1.5.12) unstable; urgency=low + + * Send thermal_shutdown_ind signal if the device needs to shut down + due to thermal constraints + * Only write the mode to /var/lib/mce/mode if the mode is actually + different from the old mode (Fixes: NB#38052) + + -- David Weinehall Tue, 22 Aug 2006 14:19:05 +0300 + +mce (1.5.11) unstable; urgency=low + + * Return type from query_event is DBUS_TYPE_INT32 not DBUS_TYPE_INT64 + + -- David Weinehall Wed, 16 Aug 2006 12:15:39 +0300 + +mce (1.5.10) unstable; urgency=low + + * Added support for querying pending alarms from alarmd + * Shutdown instead of powerup on long power press in acting dead mode + alarm submode (Fixes: NB#37807) + * Bump build-dependency on libalarm-dev to 0.2.8, to make sure we get + the D-Bus related header-file + + -- David Weinehall Tue, 15 Aug 2006 14:33:55 +0300 + +mce (1.5.9) unstable; urgency=low + + * Fixed LED pattern deactivation + * Don't forget to program the LED brightness when the pattern is activated + * Modified ledtest.sh to test things more sensibly + * Fixed more memory leaks + + -- David Weinehall Mon, 14 Aug 2006 19:12:07 +0300 + +mce (1.5.8) unstable; urgency=low + + * Lots of g_clear_error sprinkling (Fixes: NB#37218) + * Fixed possible memory leaks in error cases when using g_io_channel + * Added an example pattern to mce.ini + * Don't wake up to reprogram the LED timer unless it's needed + + -- David Weinehall Thu, 10 Aug 2006 11:18:53 +0300 + +mce (1.5.7) unstable; urgency=low + + * Fixed the example pattern for Poweron + * Improved LED tests + + -- David Weinehall Tue, 1 Aug 2006 17:28:13 +0300 + +mce (1.5.6) unstable; urgency=low + + * Remove unused pkg-config entry + + -- David Weinehall Wed, 26 Jul 2006 15:01:21 +0300 + +mce (1.5.5) unstable; urgency=low + + * The "Doh! Bump the build dependencies!" release + + -- David Weinehall Wed, 26 Jul 2006 14:39:23 +0300 + +mce (1.5.4) unstable; urgency=low + + * Added the kernel interface part for the LED code + * Added support for this new interface to mcetool + * Various minor fixes to mcetool + * use_led is a boolean, not an integer + * Use new touchscreen disabling interface + + -- David Weinehall Wed, 26 Jul 2006 11:33:31 +0300 + +mce (1.5.3) unstable; urgency=low + + * Close the D-Bus connection properly in mcetool too + + -- David Weinehall Thu, 15 Jun 2006 12:02:19 +0300 + +mce (1.5.2) unstable; urgency=low + + * Fixed shadowing of length variable in led.c + * It seems dbus_connection_close() is needed before + dbus_connection_unref() after all, let's hope this doesn't + trigger any new bugs (Fixes: NB#32585) + * Made the snooze delay 2 minutes instead of 5 minutes + * Added new method call: get_inactivity_status + * Minor cleanups + + -- David Weinehall Wed, 14 Jun 2006 15:50:25 +0300 + +mce (1.5.1) unstable; urgency=low + + * Fixed stupid error in device lock code changing D-Bus reply handling + + -- David Weinehall Wed, 31 May 2006 17:13:12 +0300 + +mce (1.5.0) unstable; urgency=low + + * Added interface for changing the device lock code + * Generate thumb code + * Added support for LED patterns + * Minor cleanup + + -- David Weinehall Wed, 31 May 2006 09:46:43 +0300 + +mce (1.4.15) unstable; urgency=low + + * Set dim/blank timeouts both when booting to acting dead + and when the automatic device lock is enabled (Fixes: N#25386, N#26648) + * Do not react to short [power] press when the alarm systemui is shown + (Fixes: N#26859) + * Set alarm state to off if the device is shutdown when the alarm + systemUI is visible + * Added "silent-locked", "locked-dim", and "silent-locked-dim" + to the tklock modes that can be requested over D-Bus + + -- David Weinehall Fri, 21 Apr 2006 17:53:42 +0300 + +mce (1.4.14) unstable; urgency=low + + * Code abstraction (slow steps towards true modularity) + * Renamed some functions for consistency + * Doxygen tags for the final few things in mcetool + * Made lock delays and shutdown query timeout configurable + * Doh! KEY_F8 is *not* a good fallback for the [POWER] key, + since it's already use for the [DECREASE] key; use KEY_F9 instead + * More changes for the dain-bramaged alternate touchscreen event + disabling interface + + -- David Weinehall Mon, 27 Mar 2006 14:39:15 +0300 + +mce (1.4.13) unstable; urgency=low + + * Seems it should be omap_uwire, not omap-uwire; + ahhh, lovely inconsistency =/ + * Fixed initscript to make stop action work properly with dsmetool + * More Doxygen updates for mcetool; still not finished + + -- David Weinehall Wed, 22 Mar 2006 13:13:48 +0200 + +mce (1.4.12) unstable; urgency=medium + + * Fixed bug in powerkey logic, with a combination of the modechange + dialog and the automatic tklock, you could end up in a situation + where you couldn't unlock the devicelock with the normal combination + (Fixes: N#24500) + * Added mce-conf; MCE can now take configuration options + from a configuration file (/etc/mce/mce.ini) + + -- David Weinehall Mon, 20 Mar 2006 20:07:45 +0200 + +mce (1.4.11) unstable; urgency=low + + * The "Documentation is good, more documentation is better!" + * Added doc target to Makefile + * Fixed various Doxygen tag bugs + * Added comments to various places + * Use DBUS_SERVICE_DBUS instead of "org.freedesktop.DBus" directly + * Added a wrapper function for the logging, mce_log, and convert all + parts of mce to use it + * Remove build-dependency on libosso (since we do logging ourselves now) + * Added `--verbose' and `--quiet' options, to increase and decrease + verbosity, respectively + + -- David Weinehall Tue, 14 Mar 2006 15:04:03 +0200 + +mce (1.4.10) unstable; urgency=low + + * Support alternate touchscreen event disabling interface + * Added support to mcetool for changing and displaying + the touchscreen/keypad lock mode + * Fixed display of touchscreen/keypad autolock status in mcetool + * Reorder entries in mcetool status display a bit + * Fixed incorrect pre-processor directive in mce-dsme.c + + -- David Weinehall Tue, 7 Mar 2006 09:17:12 +0200 + +mce (1.4.9) unstable; urgency=low + + * Support running mce on x86 (add fallbacks for [HOME] and [POWER]) + * Support alternate name for keypad event device + * Still start even if the [HOME] and/or [POWER] devices does not exist + * Don't dereference h->name after it's been freed + * Fixed incorrect debug message + * Re-enabled alarm support + * Added support for alarm icon + message to mcetool + (only works with osso-systemui-alarm >= 0.1.5.1) + * Bumped Recommends, readd Build-Depends for alarm + + -- David Weinehall Tue, 28 Feb 2006 15:45:49 +0200 + +mce (1.4.8.1) unstable; urgency=low + + * Workaround for stupid bug in libdbus-glib-1-2 *grumble* + + -- David Weinehall Tue, 14 Feb 2006 16:11:36 +0200 + +mce (1.4.8) unstable; urgency=low + + * Added debugging information to keypress_event.c + + -- David Weinehall Mon, 13 Feb 2006 14:24:47 +0200 + +mce (1.4.7) unstable; urgency=low + + * Send tklock mode change signals + * Build-Depends: osso-systemui-powerkeymenu-dev (>= 0.1.4.7) + to get the latest return value defines + * Implemented soft poweroff support + + -- David Weinehall Fri, 10 Feb 2006 14:51:11 +0200 + +mce (1.4.6) unstable; urgency=low + + * Make sure that the device autolock causes devicelock to be enabled + on bootup from acting dead (Fixes: N#22619) + * Dim the device lock menu entry in "voip"-mode + * Send "normal" instead of "voip" + * Fixed memory leak and invalid pointer dereference in mcetool + + -- David Weinehall Tue, 7 Feb 2006 12:20:41 +0200 + +mce (1.4.5) unstable; urgency=low + + * Initial "voip"-mode support + * Added new D-Bus API's: "get_tklock_mode" and "req_tklock_mode_change" + * Bump build-dependency on mce-dev, to get a version that supports the + new API + + -- David Weinehall Wed, 1 Feb 2006 13:57:58 +0200 + +mce (1.4.4) unstable; urgency=low + + * Improved D-Bus matching + * Changelog cleanup + * Added bme-dbus-proxy to mce's Recommends, + and bumped the recommended version of mce for mcetools + + -- David Weinehall Wed, 1 Feb 2006 11:23:38 +0200 + +mce (1.4.3) unstable; urgency=high + + * Modified mce_dbus_handler_add and mce_dbus_handler_remove to handle + a rules argument, for adding extra matching rules + * Made MCE wait for desktop startup before setting initial display settings + * Fixed stupid cut'n'paste bug in mce-dsme.c (Fixes: N#22517) + + -- David Weinehall Mon, 30 Jan 2006 16:14:43 +0200 + +mce (1.4.2) unstable; urgency=high + + * Fixed broken device lock logic + * Reverted 1.4.1 change; use a normal connection, + but only unreference it instead; never close it + + -- David Weinehall Fri, 27 Jan 2006 17:30:59 +0200 + +mce (1.4.1) unstable; urgency=low + + * Open a private bus connection, to get a sensible interface when + closing the bus (the close/unref mess is just sooo messed up) + * Improved D-Bus error reporting + + -- David Weinehall Wed, 25 Jan 2006 19:01:11 +0200 + +mce (1.4.0) unstable; urgency=low + + * Refactored dbus handling + * Use g_slist_prepend instead of g_slist_append since it's more efficient + | Granted, the cases were we use it are not really performance + | critical, but let's promote good programming + * Added more comments + * Various minor fixes + + -- David Weinehall Mon, 23 Jan 2006 18:20:32 +0200 + +mce (1.3.5) unstable; urgency=medium + + * Doh! waitdbus is in /usr/sbin, not in /usr/bin (Fixes: N#22254) + + -- David Weinehall Mon, 9 Jan 2006 16:39:22 +0200 + +mce (1.3.4) unstable; urgency=low + + * Enable debugging + + -- David Weinehall Thu, 5 Jan 2006 16:44:39 +0200 + +mce (1.3.3) unstable; urgency=low + + * Remove local re-defines of values provided by linux/input.h + + -- David Weinehall Wed, 4 Jan 2006 12:39:31 +0200 + +mce (1.3.2) unstable; urgency=low + + * Disable alarm-support for now + + -- David Weinehall Tue, 3 Jan 2006 17:14:12 +0200 + +mce (1.3.1) unstable; urgency=low + + * Make sure the devicelock status is saved as soon as we enable it + * More D-Bus 0.50 API migration; dbus_message_append_args_valist + requires its arguments to be pass-by-reference in the new API + | Unless there are some undocumented changes between 0.50 and 0.60, + | this version of MCE *should* also work with D-Bus 0.60 + * Do not unreference dbus-message in dbus_send, + it's unreferenced by dbus_send_message + + -- David Weinehall Wed, 21 Dec 2005 13:44:08 +0200 + +mce (1.3.0) unstable; urgency=low + + * The "And now for some real fun" release + * Migrate to D-Bus 0.50 API + + -- David Weinehall Thu, 1 Dec 2005 16:22:25 +0200 + +mce (1.2.3) unstable; urgency=low + + * Fixed error handling in mcetool (several incorrect uses of dbus_error) + + -- David Weinehall Thu, 24 Nov 2005 14:55:01 +0200 + +mce (1.2.2) unstable; urgency=low + + * Made the [HOME] delay 0.8s instead of 1.5s (Fixes: N#21540) + + -- David Weinehall Thu, 24 Nov 2005 14:32:33 +0200 + +mce (1.2.1) unstable; urgency=low + + * Added mcetorture2.sh + * Clean up source code a little (i.e. more comments) + + -- David Weinehall Mon, 7 Nov 2005 16:16:10 +0200 + +mce (1.2.0) unstable; urgency=low + + * Split mce-dev into separate package, to remove circular build-depends + * Start MCE earlier and wait until D-Bus system bus is up + (Fixes: N#21069) + + -- David Weinehall Mon, 7 Nov 2005 00:07:01 +0200 + +mce (1.1.6) unstable; urgency=low + + * Added D-Bus method_call to get the MCE-version + * Made mce_gconf_set_int() always suggest sync, + to minimise risk for dataloss (Hopefully fixes: N#20980) + + -- David Weinehall Mon, 31 Oct 2005 16:31:00 +0200 + +mce (1.1.5) unstable; urgency=low + + * Show tklock instructions on long [POWER] keypress too + (Fixes: N#20932) + + -- David Weinehall Wed, 26 Oct 2005 14:48:49 +0300 + +mce (1.1.4) unstable; urgency=low + + * Added torture test script + * Added missing build-dependency on osso-systemui-alarm-dev + + -- David Weinehall Wed, 26 Oct 2005 12:32:31 +0300 + +mce (1.1.3) unstable; urgency=low + + * Added missing include to mce.c + * Abstracted out dsmesock_send() + + -- David Weinehall Mon, 17 Oct 2005 19:17:52 +0300 + +mce (1.1.2) unstable; urgency=low + + * Fixed a nasty bug in the exit-function for mce-dsme + | Thanks to Devesh Kothari + + -- David Weinehall Mon, 17 Oct 2005 17:42:44 +0300 + +mce (1.1.1) unstable; urgency=low + + * Forward ported mcetool functionality from mce 1.0.5 (Fixes N#19185): + o Added functionality to mcetool to open/close the alarm dialog + and to set and disable the alarm clock + o Added functionality to mcetool to open/close the tklock + o Updated mcetool manual accordingly + + -- David Weinehall Tue, 11 Oct 2005 20:43:19 +0300 + +mce (1.1.0) unstable; urgency=low + + * Eat the first press of [HOME] and [POWER] when the screen is blank + (Fixes: N#19968, N#19973) + + -- David Weinehall Fri, 7 Oct 2005 19:04:16 +0300 + +mce (1.0.4) unstable; urgency=low + + * Added functionality to mcetool to open/close the devicelock dialog + * Updated mcetool manual accordingly + * Source code cleanup + | Only comments modified + * Made enable_eventeater() static + + -- David Weinehall Fri, 7 Oct 2005 18:08:32 +0300 + +mce (1.0.3) unstable; urgency=low + + * Added English and Swedish manual pages for mce + * Fixed several manpage typos + * Move SYSTEMUI_GCONF_DEVICE_AUTOLOCK_ENABLED_PATH + to mcetool.h instead, since it's not used by MCE itself + | Preferably this should be taken from some -dev package instead, + | but... + * Added functionality to mcetool to open/close startup and shutdown + splashscreens + * Added functionality to mcetool to open/close the modechange + confirmation dialog + * Added functionality to mcetool to open/close the powerkey menu + * Added functionality to mcetool to open/close the actingdead UI + + -- David Weinehall Wed, 5 Oct 2005 17:59:08 +0300 + +mce (1.0.2) unstable; urgency=low + + * Init processwd in mce_init() and exit processwd in mce_exit() + * Added English and Swedish manual pages for mcetool + * Fixed minor typos in the devlock-blocker manual pages + + -- David Weinehall Mon, 19 Sep 2005 14:18:07 +0300 + +mce (1.0.1) unstable; urgency=low + + * Improved mcetool; added support for: + * blank prevent request + * unblank screen request + * powerup request + * reboot request + * shutdown request + * Added English and Swedish manual pages for devlock-blocker + + -- David Weinehall Sat, 17 Sep 2005 15:57:45 +0300 + +mce (1.0.0) unstable; urgency=low + + * The "I expect the first brown paper bag within a minute" release + * Boldly declare this as 1.0.0 + * Enable tklock silently when autolocking and for event eater + (Fixes: N#17757) + * Do not cancel shutdown if the charger is reconnected if battery is empty + * Shutdown 5 seconds after battery empty + * Simplify pkg-config flags + | Thanks to Eero Tamminen + + -- David Weinehall Tue, 13 Sep 2005 19:51:13 +0300 + +mce (0.9.18) unstable; urgency=low + + * Special handling for blank vs dim timeout overlaps + * Added support for DSME watchdog + + -- David Weinehall Sat, 10 Sep 2005 17:30:56 +0300 + +mce (0.9.17) unstable; urgency=low + + * Delay for 10 seconds on battery empty message before requesting + shutdown, to make sure that the user has an opportunity + to connect the charger + * If charger is connected during the 10 second delay, cancel shutdown + * Disable all timers on shutdown + * Use the new rtc_alarm_time GConf entry to get the alarm + + -- David Weinehall Wed, 31 Aug 2005 23:22:45 +0300 + +mce (0.9.16) unstable; urgency=medium + + * Doh! Restore value for total failures to device_lock_total_failed, + NOT to device_lock_failed, since the alternative causes lock code + to be asked even when the last code entry was indeed correct + (Fixes: N#18250) + + -- David Weinehall Mon, 29 Aug 2005 15:32:32 +0300 + +mce (0.9.15) unstable; urgency=low + + * Print debug message about DSME message type in hex instead of decimal + * Modify splash behaviour a bit when shutting down to acting dead, + to avoid a visual glitch + * Modify tklock behaviour to handle corner cases + * Disable devicelock autolock timeout in the correct way + + -- David Weinehall Wed, 24 Aug 2005 14:09:01 +0300 + +mce (0.9.14) unstable; urgency=low + + * Send replies to all method_calls that expect them + * Set no_reply for the calls to SystemUI + * More codingstyle fixes + * Revert flight mode change from 0.9.12 + + -- David Weinehall Fri, 19 Aug 2005 15:52:50 +0300 + +mce (0.9.13) unstable; urgency=low + + * Implemented support for proper keypad and touchscreen lock + (disabling of those events on a kernel level, through sysfs) + (Fixes: N#15142) + * Also disable keypad and touchscreen in acting dead state + + -- David Weinehall Thu, 18 Aug 2005 14:40:38 +0300 + +mce (0.9.12) unstable; urgency=high + + * Always report flight-mode when state != DSME_STATE_USER + * Assume failed D-Bus calls to wlancond/btcond to mean open connections; + while this is highly improbable (it's more likely that this means that + networking isn't functional), it's better to ask and be safe, than + not ask and be sorry... This makes MCE survive unavailability of + wlancond/btcond, thus high urgency, since that situation is + triggerable in the wild (Fixes: N#17332) + * Fixed alarm state setting code (Fixes: N#16057) + * First close powerkey menu, *then* perform action + + -- David Weinehall Tue, 16 Aug 2005 13:26:08 +0300 + +mce (0.9.11) unstable; urgency=low + + * Made sure that we don't send both long and short keypress event for [HOME] + * Minor cleanup + * Removed testmode + + -- David Weinehall Mon, 8 Aug 2005 15:05:08 +0300 + +mce (0.9.10) unstable; urgency=low + + * Added D-Bus interface for alarm mode changes (Fixes: N#15736) + | Thanks to Ismo Laitinen for patch + * Fixed debugging statement in mcetool + | Thanks to Ismo Laitinen for reporting the bug + * Modify Makefile to use pkg-config also for libdsme and libcal + * Build-Depends: libdsme0-dev (>= 0.29) + * Implement devicelock code validation properly + + -- David Weinehall Fri, 22 Jul 2005 08:09:26 +0300 + +mce (0.9.9) unstable; urgency=low + + * Doh! Restore code to enable autokeylock... (Fixes: N#15576) + * As per request, modify cover open tklock event to show infoprint + (Fixes: N#15353) + * Added stub code for devicelock code validation + + -- David Weinehall Fri, 15 Jul 2005 11:19:18 +0300 + +mce (0.9.8) unstable; urgency=low + + * Only send inactivity D-Bus signal if the state has changed + (Fixes: N#15114) + + -- David Weinehall Wed, 29 Jun 2005 16:15:19 +0300 + +mce (0.9.7) unstable; urgency=low + + * Always close powerkeymenu after callback is called + * Better handling of osso-systemui restarts + * Fixed stupid typo wrt inactivity handling + + -- David Weinehall Tue, 28 Jun 2005 13:29:25 +0300 + +mce (0.9.6) unstable; urgency=low + + * Do not react on [POWER] if the modechange confirmation + dialogue is shown (Fixes: N#14008) + * Send D-Bus message when system changes from inactive to active + * Fixed incorrect handling of device autolock timeout + (Fixes: N#14112, N#14439) + * Handle [HOME] in a more correct manner (Fixes: N#14285) + + -- David Weinehall Mon, 13 Jun 2005 16:39:04 +0300 + +mce (0.9.5) unstable; urgency=low + + * Fixed argument types used with D-Bus + (dbus_bool_t instead of gboolean, dbus_uint32_t instead of guint32, etc.) + * Made snooze on shutdown work properly + * Code cleanup for D-Bus code + * Fixed stupid display blank vs dim timeout bug (Fixes: N#13687) + + -- David Weinehall Wed, 1 Jun 2005 17:22:31 +0300 + +mce (0.9.4) unstable; urgency=low + + * Added `--reset-passwd' to mcetool + + -- David Weinehall Mon, 30 May 2005 13:39:26 +0300 + +mce (0.9.3) unstable; urgency=low + + * Added a boolean argument to inactivity signal; + TRUE means inactivity, FALSE means activity + * Begun restructuring of the code + * Readded devlock-blocker (Fixes: N#10510) + * Home key has moved to KEY_F5 now, it seems (Fixes: N#13151) + + -- David Weinehall Fri, 27 May 2005 11:19:51 +0300 + +mce (0.9.2) unstable; urgency=low + + * Fixed dependencies for tools + * Added reading of gconf values to mcetool + + -- David Weinehall Fri, 20 May 2005 13:22:57 +0300 + +mce (0.9.1) unstable; urgency=low + + * Added support for TKLOCK_TIMEOUT + * Use pkg-config also for osso-clock + + -- David Weinehall Tue, 17 May 2005 14:33:52 +0300 + +mce (0.9.0) unstable; urgency=low + + * New package: mcetools + | For now it only provides one program; mcetool + * Fixed memory leak; free string array from BTCOND_GET_BDA_LIST_REQ + * Removed the devlock-blocker + * Always send mode names based on lookup table, + to avoid confusion because of aliases + + -- David Weinehall Mon, 16 May 2005 18:32:19 +0300 + +mce (0.8.8) unstable; urgency=low + + * Bluetooth support for connection checking + * Don't show splashscreen on bootup, + only when changing from acting dead to normal state + * Use a nice value of -1 when starting MCE using dsmetool + + -- David Weinehall Fri, 13 May 2005 14:52:03 +0300 + +mce (0.8.7) unstable; urgency=low + + * Only ask about for confirmation about flight-mode + if there are open connections + | bluetooth is not checked at the moment + * Made the device lock delays 0, 1, 1, 5 instead of 0, 1, 30, 300 + * Removed osso-log.h; use the one from libosso + + -- David Weinehall Fri, 13 May 2005 00:03:54 +0300 + +mce (0.8.6) unstable; urgency=low + + * Added event eating on inactivity + * Send data save event on battery low + * Play sound on shutdown from user state + * open/close tklock without infoprints if we're not in USER state + and when auto-locking/auto-unlocking + + -- David Weinehall Tue, 10 May 2005 19:42:56 +0300 + +mce (0.8.5) unstable; urgency=low + + * Remove shutdown query timeout when doing a shutdown + from device lock shutdown query + * Added a Recommends: dsme, for the DSME lifeguard + * Set default brightness to 5 instead of 9 + * Added dialogue that asks for confirmation + before changing to/from flight mode + * Build-Depends: osso-systemui-modechange + * Cleanup + * Fixed (extremely) theoretical loop-around bug with device lock + + -- David Weinehall Mon, 2 May 2005 08:44:00 +0300 + +mce (0.8.4) unstable; urgency=low + + * On popular request, add pkg-config file + * Added version number check + * Added 30 second timeout in shutdown dialogue from device lock + * Always offer shutdown when cancel is pressed from device lock + + -- David Weinehall Wed, 27 Apr 2005 14:45:52 +0300 + +mce (0.8.3) unstable; urgency=low + + * Doh! close() pidfile... + * Detect if this is the first time we start MCE or if it's been restarted + * Only show shutdown question from devicelock on bootup + * Clear input on press of cancel in devicelock mode, *except* + when input has been disabled during timeout + * Added (disabled for now) feature to bootup directly to flight mode + * Made the delay slightly shorter when powering up from acting dead + + -- David Weinehall Fri, 22 Apr 2005 17:58:10 +0300 + +mce (0.8.2) unstable; urgency=low + + * Show shutdown question from devicelock + * Make sure powerkeymenu is closed on shutdown + * Close down powerkeymenu and tklock if MCE is restarted, + to ensure that MCE doesn't end up confused about the mode it's in + * Don't install devlock-blocker by default; it's not needed + Keep it for now in the source tree, though + + -- David Weinehall Thu, 21 Apr 2005 23:31:42 +0300 + +mce (0.8.1) unstable; urgency=low + + * Moved call to g_type_init to mce-gconf, since that file is the + only user of GType + * Added devlock-blocker + | This program will block if loaded when the device is locked + | and exits as soon as the device is unlocked again + * Removed unused defines from dbus-names.h + * Start MCE using dsmetool (if available) unless USE_DSMETOOL=no + * devicelock on boot if shutdown from devicelock + or if device autolock is enabled + * Don't show devicelock in acting dead state + + -- David Weinehall Tue, 12 Apr 2005 13:17:04 +0300 + +mce (0.8.0) unstable; urgency=low + + * Before requesting a shutdown from DSME, update the alarm state, + to make sure that no alarms fire during shutdown phase + (If there's pending alarms, DSME will go to acting dead state instead) + * Change offline back to flight... + * Fixed wakeup from acting dead and reboot request to work properly + * Added D-Bus method and signal for information about device lock mode + * Refactored some code + + -- David Weinehall Tue, 29 Mar 2005 19:46:09 +0300 + +mce (0.7.7) unstable; urgency=low + + * const sprinkling + * Fixed bugs in error callbacks + + -- David Weinehall Fri, 4 Mar 2005 11:22:54 +0200 + +mce (0.7.6) unstable; urgency=low + + * Change the Depends: osso-systemui-tklock (>= 0.0.4.6) + to a Conflicts: osso-systemui-tklock (<< 0.0.4.6), since we really + don't want MCE to depend directly on systemui-components + * Bump the Build-Depends on osso-systemui-tklock-dev to match + version of the Conflicts on osso-systemui-tklock + + -- David Weinehall Tue, 1 Mar 2005 10:27:39 +0200 + +mce (0.7.5) unstable; urgency=low + + * Auto enabling/disabling of locks should only happen in user state + + -- David Weinehall Tue, 22 Feb 2005 16:38:09 +0200 + +mce (0.7.4) unstable; urgency=low + + * Added timeout between tklock enable and dim request + * Added timeout between cover open and tklock disable + * Re-enable device autolock + (depends on device autolock module in DSME) + * Added missing break to tklock unlock code + + -- David Weinehall Tue, 22 Feb 2005 16:13:48 +0200 + +mce (0.7.3) unstable; urgency=low + + * Flight mode --> Offline mode, as per ideas from above + * Re-enabled tklock + + -- David Weinehall Fri, 18 Feb 2005 14:05:53 +0200 + +mce (0.7.2) unstable; urgency=low + + * Create /var/lib/mce too, and install a mode-file in it... + | Thanks to Johan Hedberg for finding it + + -- David Weinehall Wed, 16 Feb 2005 12:39:30 +0200 + +mce (0.7.1) unstable; urgency=low + + * Disabled tklock (except for with cover) and automatic devicelock, + since the functionality in systemui-tklock and DSME they depend on + doesn't work properly + + -- David Weinehall Tue, 15 Feb 2005 16:58:35 +0200 + +mce (0.7.0) unstable; urgency=low + + * Added build-depends on osso-systemui-dev, osso-systemui-powerkeymenu-dev, + osso-systemui-tklock-dev, osso-systemui-devlock-dev, + osso-systemui-splashscreen-dev, and osso-systemui-actingdead-dev + * Implemented logic for touchscreen/keypad and device locks + * Enable touchscreen/keypad lock and device lock when closing the cover, + disable touchscreen/keypad lock when opening the cover + * Fixed bug in logic for [POWER] wrt short keypress + | Thanks to Ismo Laitinen for reporting the bug + * Doh! Fixed really stupid bug in mce_get_mode_string and mce_set_mode + | Thanks to Ismo Laitinen for reporting the bug + * MCE now stores the mode in /var/lib/mce/mode + + -- David Weinehall Sun, 13 Feb 2005 17:43:13 +0200 + +mce (0.6.1) unstable; urgency=low + + * Install at init level 25 instead of 30 to make sure we're already + running when btcond is started + | Requested by Johan Hedberg + * Added build-depends on libgconf2-dev + + -- David Weinehall Thu, 3 Feb 2005 14:40:14 +0200 + +mce (0.6) unstable; urgency=low + + * Call the SystemUI powerkey menu when [POWER] is pressed (short press) + | Handle shutdown, flight mode, and normal mode for now; + | touchscreen/keypad and device locks has not been implemented yet + * Call SystemUI shutdown splashscreen when shutting down or rebooting + * Call SystemUI actdead when changing to acting dead state + * Fixed missing build-depends on dbus-1-dev and dbus-glib-1-dev + + -- David Weinehall Wed, 2 Feb 2005 09:32:06 +0200 + +mce (0.5.5) unstable; urgency=low + + * Added new D-Bus method; req_reboot + + -- David Weinehall Fri, 21 Jan 2005 10:26:17 +0200 + +mce (0.5.4) unstable; urgency=low + + * Moved mode-names.h to mce-dev + + -- David Weinehall Wed, 19 Jan 2005 15:11:54 +0200 + +mce (0.5.3) unstable; urgency=low + + * Implemented get_device_mode + + -- David Weinehall Wed, 19 Jan 2005 15:00:53 +0200 + +mce (0.5.2) unstable; urgency=low + + * Set defaults for values from gconf if those cannot be read + + -- David Weinehall Tue, 18 Jan 2005 16:26:16 +0200 + +mce (0.5.1) unstable; urgency=low + + * bme-dbus-proxy changed back to bme_empty from bme_recharge + + -- David Weinehall Tue, 18 Jan 2005 15:16:11 +0200 + +mce (0.5) unstable; urgency=low + + * Implemented sig_device_mode_ind and req_device_mode_change + | This means that MCE now handles changes to/from flight mode + * Various cleanup + * Added gconf support + * Implemented emergy shutdown on battery empty + + -- David Weinehall Tue, 18 Jan 2005 08:25:56 +0200 + +mce (0.4.1) unstable; urgency=low + + * Doh... 2 seconds, not 2 milliseconds + * Oh, and the pidfile should be mce.pid, nothing else... + + -- David Weinehall Wed, 1 Dec 2004 09:20:50 +0200 + +mce (0.4) unstable; urgency=low + + * Removed -n from dh_installinit + * Only send a shutdown request to DSME if we're in user state; + if we're acting dead, send a power-up request instead + * Scan for the correct /dev/input/event-files, don't just assume + that the keypad is event0 and [POWER] is event2 + * Added logging + * Added improved debugging functionality + + -- David Weinehall Tue, 30 Nov 2004 14:12:05 +0200 + +mce (0.3) unstable; urgency=low + + * Use the GMainLoop; hook up dsmesock and powerkey to it + * Added D-Bus interface; most of the code is a cleaned up version + of dsme-dbus-proxy + * Added init-file + * Added framework for checking [HOME] as well + + -- David Weinehall Tue, 16 Nov 2004 18:00:09 +0200 + +mce (0.2) unstable; urgency=low + + * Depend on at least libdsme0-dev (>= 0.12) + + -- David Weinehall Tue, 2 Nov 2004 18:16:52 +0200 + +mce (0.1) unstable; urgency=low + + * Initial Release + + -- David Weinehall Mon, 1 Nov 2004 14:11:16 +0200 diff --git a/debian/compat b/debian/compat new file mode 100644 index 00000000..7ed6ff82 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +5 diff --git a/debian/control b/debian/control new file mode 100644 index 00000000..0b3f2957 --- /dev/null +++ b/debian/control @@ -0,0 +1,44 @@ +Source: mce +Section: misc +Priority: optional +Maintainer: David Weinehall +Uploaders: Philippe De Swert , Pekka Lundström , Tuomo Tanskanen +Build-Depends: debhelper (>= 5.0.0), mce-dev (>= 1.10.21), pkg-config, libglib2.0-dev (>= 2.18.0), libdbus-1-dev (>= 1.0.2), libdbus-glib-1-dev, libdsme0.2.0-dev (>= 0.58), libgconf2-dev, linux-kernel-headers (>= 2.6.32-20100102) | linux-libc-dev (>= 2.6.32), libconic0-dev (>= 0.15-0.1), dpkg-dev (>= 1.13.19), libcal-dev (>= 0.2.4), libi2c-dev, aegis-builder (>= 1.4), libsysinfo-dev +Standards-Version: 3.8.4 + +Package: mce +Architecture: any +Depends: dbus, ${shlibs:Depends}, ${misc:Depends} +Recommends: dsme (>= 0.30.11), devicelock (>= 0.3.3) +Description: the Mode Control Entity for Nokia mobile computers + This package contains the Mode Control Entity which provides + mode management features. This is a daemon that is the backend + for many features on Nokia's mobile computers. + +Package: mce-dbg +Section: devel +Priority: extra +Architecture: any +Depends: mce (= ${binary:Version}), ${misc:Depends} +Description: debug symbols for mce + This package contains the debug symbols for the Mode Control Entity. + +Package: mcetools +Section: utils +Architecture: any +Depends: dbus, ${shlibs:Depends}, ${misc:Depends} +Recommends: mce (>= 1.3.1) +Description: tools for interacting with mce + This package contains tools that can be used to interact with + the Mode Control Entity and to get mode information. + +Package: mcetools-dbg +Section: devel +Priority: extra +Architecture: any +Depends: mcetools (= ${binary:Version}), ${misc:Depends} +Recommends: mce (>= 1.3.1) +Description: debug symbols for mcetools + This package contains tools that can be used to interact with + the Mode Control Entity and to get mode information with all + debugging information. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 00000000..e657c5fd --- /dev/null +++ b/debian/copyright @@ -0,0 +1,24 @@ +This package was debianized by David Weinehall on +Mon, 1 Nov 2004 13:46:42 +0300. + +Upstream Author: David Weinehall + +Copyright © 2004-2010 Nokia Corporation and/or its subsidiary(-ies) + + +License: + + mce is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License version 2.1 + as published by the Free Software Foundation. + + mce 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 mce. If not, see . + +On Debian systems, the complete text of the GNU Lesser General +Public License can be found in `/usr/share/common-licenses/LGPL-2.1'. diff --git a/debian/mce.aegis b/debian/mce.aegis new file mode 100644 index 00000000..f5a500a8 --- /dev/null +++ b/debian/mce.aegis @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/debian/mce.dirs b/debian/mce.dirs new file mode 100644 index 00000000..9e9b3ba0 --- /dev/null +++ b/debian/mce.dirs @@ -0,0 +1,7 @@ +sbin +etc/mce +etc/gconf/schemas +var/lib/mce +usr/lib/mce +usr/share/mce +usr/share/backup-framework/applications diff --git a/debian/mce.install b/debian/mce.install new file mode 100644 index 00000000..22851f6e --- /dev/null +++ b/debian/mce.install @@ -0,0 +1,7 @@ +debian/tmp/etc/mce +debian/tmp/etc/gconf +debian/tmp/var +debian/tmp/sbin/mce +debian/tmp/usr/lib/mce +debian/tmp/usr/share/mce +debian/tmp/usr/share/backup-framework/applications diff --git a/debian/mce.manpages b/debian/mce.manpages new file mode 100644 index 00000000..18568347 --- /dev/null +++ b/debian/mce.manpages @@ -0,0 +1,2 @@ +man/mce.8 +man/mce.sv.8 diff --git a/debian/mce.postinst b/debian/mce.postinst new file mode 100644 index 00000000..cfcda36a --- /dev/null +++ b/debian/mce.postinst @@ -0,0 +1,6 @@ +#! /bin/sh -e + +#DEBHELPER# + +# Remove old D-Bus configuration +rm -f /etc/dbus-1/system.d/mce.conf diff --git a/debian/mcetools.aegis b/debian/mcetools.aegis new file mode 100644 index 00000000..09447236 --- /dev/null +++ b/debian/mcetools.aegis @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/debian/mcetools.dirs b/debian/mcetools.dirs new file mode 100644 index 00000000..e1ae85e0 --- /dev/null +++ b/debian/mcetools.dirs @@ -0,0 +1 @@ +sbin diff --git a/debian/mcetools.install b/debian/mcetools.install new file mode 100644 index 00000000..b677d62d --- /dev/null +++ b/debian/mcetools.install @@ -0,0 +1,2 @@ +debian/tmp/sbin/mcetool +debian/tmp/sbin/mcetorture diff --git a/debian/mcetools.manpages b/debian/mcetools.manpages new file mode 100644 index 00000000..74769aaf --- /dev/null +++ b/debian/mcetools.manpages @@ -0,0 +1,4 @@ +man/mcetool.8 +man/mcetool.sv.8 +man/mcetorture.8 +man/mcetorture.sv.8 diff --git a/debian/mcetools.postinst b/debian/mcetools.postinst new file mode 100644 index 00000000..6dc5dbfb --- /dev/null +++ b/debian/mcetools.postinst @@ -0,0 +1,6 @@ +#! /bin/sh -e + +#DEBHELPER# + +# Remove old D-Bus configuration +rm -f /etc/dbus-1/system.d/mcetool.conf diff --git a/debian/rules b/debian/rules new file mode 100755 index 00000000..98fcec3a --- /dev/null +++ b/debian/rules @@ -0,0 +1,95 @@ +#!/usr/bin/make -f +# debian/rules for mce using debhelper. +# Copyright © 2004-2010 Nokia Corporation and/or its subsidiary(-ies). + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +# This has to be exported to make some magic below work. +export DH_OPTIONS + +pkgdir = $(CURDIR)/debian/tmp + +makefileversion := $$(grep -m1 '^VERSION' Makefile | sed -e 's/VERSION *:= *//') +changelogversion := $$(grep -m1 'mce (' debian/changelog | sed -e 's/mce (\(.*\)).*/\1/;s/\(^'$(makefileversion)'\).*/\1/') + +CC = gcc +CFLAGS = -Wall -g +LDFLAGS = -Wl,--as-needed + +ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) + CFLAGS += -O0 +else + CFLAGS += -O2 +endif + +# Use soft-float and thumb mode if enabled +ifneq (,$(findstring thumb,$(DEB_BUILD_OPTIONS))) + CFLAGS += -mthumb +endif + +.PHONY: clean +clean: + dh_testdir + dh_testroot + rm -f build-stamp + + $(MAKE) distclean + + dh_clean + +.PHONY: check +check: + @if [ x"$(makefileversion)" != x"$(changelogversion)" ]; then \ + printf "error: version-number mismatch\n"; \ + printf "Makefile version: $(makefileversion)\n"; \ + printf "changelog version: $(changelogversion)\n"; \ + exit 1; \ + fi + +build-stamp: + dh_testdir + + $(MAKE) CC="$(CC)" CFLAGS="$(CFLAGS)" + + touch build-stamp + +.PHONY: build +build: check build-stamp + +.PHONY: install +install: + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + $(MAKE) install DESTDIR="$(pkgdir)" + +.PHONY: binary-indep +binary-indep: +#nothing to see here, move along + +.PHONY: binary-arch +binary-arch: build install + dh_testdir + dh_testroot + dh_installdocs + dh_installman + dh_installchangelogs + dh_install -s + dh_gconf + dh_strip -pmce --dbg-package=mce-dbg + dh_strip -pmcetools --dbg-package=mcetools-dbg + dh_link + dh_compress + dh_fixperms + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + aegis-deb-add -control debian/mce/DEBIAN/control .. debian/mce.aegis=_aegis + aegis-deb-add -control debian/mcetools/DEBIAN/control .. debian/mcetools.aegis=_aegis + +.PHONY: binary +binary: binary-indep binary-arch diff --git a/display.schemas b/display.schemas new file mode 100644 index 00000000..40d861f5 --- /dev/null +++ b/display.schemas @@ -0,0 +1,138 @@ + + + + + /schemas/system/osso/dsm/display/display_dim_timeout + /system/osso/dsm/display/display_dim_timeout + mce + int + 30 + + Timeout for display dimming + This key contains the timeout for display dimming in seconds. + + + + + /schemas/system/osso/dsm/display/display_blank_timeout + /system/osso/dsm/display/display_blank_timeout + mce + int + 3 + + Timeout for display blanking + This key contains the timeout for display blanking in seconds. + + + + + /schemas/system/osso/dsm/display/display_brightness + /system/osso/dsm/display/display_brightness + mce + int + 4 + + Display brightness level. + + + + + + /schemas/system/osso/dsm/display/display_brightness_level_step + /system/osso/dsm/display/display_brightness_level_step + mce + int + 1 + + Minimum step of brightness. + + + + + + /schemas/system/osso/dsm/display/max_display_brightness_levels + /system/osso/dsm/display/max_display_brightness_levels + mce + int + 5 + + Maximum brightness level. + + + + + + /schemas/system/osso/dsm/display/possible_display_dim_timeouts + /system/osso/dsm/display/possible_display_dim_timeouts + mce + list + int + [15, 30, 60, 120, 180] + + Possible timeouts for display dimming + This key contains the allowed values for display dimming timeout. + + + + + /schemas/system/osso/dsm/display/possible_display_blank_timeouts + /system/osso/dsm/display/possible_display_blank_timeouts + mce + list + int + [3, 10, 15] + + Possible timeouts for display blanking + This key contains the allowed values for display blanking timeout. + + + + + /schemas/system/osso/dsm/display/use_adaptive_display_dimming + /system/osso/dsm/display/use_adaptive_display_dimming + mce + bool + true + + Prolong display dim timeout if user taps to unblank. + If true, display dim timeouts will adapt to user unblank requests. + + + + + /schemas/system/osso/dsm/display/adaptive_display_dim_threshold + /system/osso/dsm/display/adaptive_display_dim_threshold + mce + int + 3000 + + Timeout before adaptive display dimming is disabled. + This key contains the timeout until adaptive display dimming is disabled for a particular case, in milliseconds. + + + + + /schemas/system/osso/dsm/locks/touchscreen_keypad_autolock_enabled + /system/osso/dsm/locks/touchscreen_keypad_autolock_enabled + mce + bool + true + + Should tklock be enabled when display is blanked. + If true, touch screen and keypad lock will be enabled, when display is blanked + + + + + /schemas/system/osso/dsm/display/inhibit_blank_mode + /system/osso/dsm/display/inhibit_blank_mode + mce + int + 0 + + Inhibit display blanking mode + 0 - Don't inhibit blanking; 1 - Inhibit dimming if charger is connected; 2 - Inhibit blanking if charger is connected; 3 - Inhibit dimming; 4 - Inhibit blanking + + + + diff --git a/energymanagement.schemas b/energymanagement.schemas new file mode 100644 index 00000000..4ec3fdbf --- /dev/null +++ b/energymanagement.schemas @@ -0,0 +1,54 @@ + + + + + /schemas/system/osso/dsm/energymanagement/enable_power_saving + /system/osso/dsm/energymanagement/enable_power_saving + mce + bool + false + + Enable automatic power saving mode + Enable automatic power saving, true; Disable automatic power saving, false + + + + + /schemas/system/osso/dsm/energymanagement/force_power_saving + /system/osso/dsm/energymanagement/force_power_saving + mce + bool + false + + Unconditionally enable power saving mode + Unconditionally enable power saving, true; user selected and system controlled power saving, false + + + + + /schemas/system/osso/dsm/energymanagement/psm_threshold + /system/osso/dsm/energymanagement/psm_threshold + mce + int + int + 20 + + Threshold for the power saving mode + This key contains the threshold for the power saving mode. + + + + + /schemas/system/osso/dsm/energymanagement/possible_psm_thresholds + /system/osso/dsm/energymanagement/possible_psm_thresholds + mce + list + int + [10, 20, 30, 40, 50] + + Possible thresholds for the power saving mode + This key contains the allowed threshold values for the power saving mode. + + + + diff --git a/event-input.c b/event-input.c new file mode 100644 index 00000000..4ba8b718 --- /dev/null +++ b/event-input.c @@ -0,0 +1,1099 @@ +/** + * @file event-input.c + * /dev/input event provider for the Mode Control Entity + *

+ * Copyright © 2004-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * @author Ismo Laitinen + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include +#include +#include /* g_access() */ +#include /* g_signal_connect(), + * G_OBJECT(), + * G_CALLBACK() + */ + +#include /* errno */ +#include /* open() */ +#include /* opendir(), readdir(), telldir() */ +#include /* strcmp() */ +#include /* close() */ +#include /* ioctl() */ +#include /* DIR */ +#include /* struct input_event, + * EVIOCGNAME, EVIOCGBIT, EVIOCGSW, + * EV_ABS, EV_KEY, EV_SW, + * ABS_PRESSURE, + * SW_CAMERA_LENS_COVER, + * SW_KEYPAD_SLIDE, + * SW_FRONT_PROXIMITY, + * KEY_SCREENLOCK, + * KEY_CAMERA_FOCUS, + * KEY_CAMERA + */ +#ifndef SW_CAMERA_LENS_COVER +/** Input layer code for the camera lens cover switch */ +#define SW_CAMERA_LENS_COVER 0x09 +#endif /* SW_CAMERA_LENS_COVER */ +#ifndef SW_KEYPAD_SLIDE +/** Input layer code for the keypad slide switch */ +#define SW_KEYPAD_SLIDE 0x0a +#endif /* SW_KEYPAD_SLIDE */ +#ifndef SW_FRONT_PROXIMITY +/** Input layer code for the front proximity sensor switch */ +#define SW_FRONT_PROXIMITY 0x0b +#endif /* SW_FRONT_PROXIMITY */ +#ifndef KEY_CAMERA_FOCUS +/** Input layer code for the camera focus button */ +#define KEY_CAMERA_FOCUS 0x0210 +#endif /* KEY_CAMERA_FOCUS */ + +#include "mce.h" +#include "event-input.h" + +#include "mce-io.h" /* mce_register_io_monitor_chunk(), + * mce_unregister_io_monitor() + */ +#include "mce-lib.h" /* bitsize_of(), + * set_bit(), clear_bit(), test_bit(), + * bitfield_to_string(), + * string_to_bitfield() + */ +#include "mce-log.h" /* mce_log(), LL_* */ +#include "mce-conf.h" /* mce_conf_get_int(), + * mce_conf_get_string() + */ +#include "datapipe.h" /* execute_datapipe() */ + +/** ID for touchscreen I/O monitor timeout source */ +static guint touchscreen_io_monitor_timeout_cb_id = 0; + +/** ID for keypress timeout source */ +static guint keypress_repeat_timeout_cb_id = 0; + +/** ID for misc timeout source */ +static guint misc_io_monitor_timeout_cb_id = 0; + +/** List of touchscreen input devices */ +static GSList *touchscreen_dev_list = NULL; +/** List of keyboard input devices */ +static GSList *keyboard_dev_list = NULL; +/** List of misc input devices */ +static GSList *misc_dev_list = NULL; + +/** GFile pointer for the directory we monitor */ +static GFile *dev_input_gfp = NULL; +/** GFileMonitor pointer for the directory we monitor */ +static GFileMonitor *dev_input_gfmp = NULL; +/** The handler ID for the signal handler */ +static gulong dev_input_handler_id = 0; + +/** Time in milliseconds before the key press is considered long */ +static gint longdelay = DEFAULT_HOME_LONG_DELAY; + +/** Can GPIO key interrupts be disabled? */ +static gboolean gpio_key_disable_exists = FALSE; + +static void update_inputdevices(const gchar *device, gboolean add); + +/** + * Enable the specified GPIO key + * non-existing or already enabled keys are silently ignored + * + * @param key The key to enable + */ +static void enable_gpio_key(guint16 key) +{ + gchar *disabled_keys = NULL; + gulong *keylist = NULL; + gsize keylistlen; + gchar *tmp = NULL; + + if (mce_read_string_from_file(GPIO_KEY_DISABLE_PATH, + &disabled_keys) == FALSE) + goto EXIT; + + keylistlen = (KEY_CNT / bitsize_of(*keylist)) + + ((KEY_CNT % bitsize_of(*keylist)) ? 1 : 0); + keylist = g_malloc0(keylistlen * sizeof (*keylist)); + + if (string_to_bitfield(disabled_keys, &keylist, keylistlen) == FALSE) + goto EXIT; + + clear_bit(key, &keylist); + + if ((tmp = bitfield_to_string(keylist, keylistlen)) == NULL) + goto EXIT; + + (void)mce_write_string_to_file(GPIO_KEY_DISABLE_PATH, tmp); + +EXIT: + g_free(disabled_keys); + g_free(keylist); + g_free(tmp); + + return; +} + +/** + * Disable the specified GPIO key/switch + * non-existing or already disabled keys/switches are silently ignored + * + * @param key The key/switch to disable + */ +static void disable_gpio_key(guint16 key) +{ + gchar *disabled_keys = NULL; + gulong *keylist = NULL; + gsize keylistlen; + gchar *tmp = NULL; + + if (mce_read_string_from_file(GPIO_KEY_DISABLE_PATH, + &disabled_keys) == FALSE) + goto EXIT; + + keylistlen = (KEY_CNT / bitsize_of(*keylist)) + + ((KEY_CNT % bitsize_of(*keylist)) ? 1 : 0); + keylist = g_malloc0(keylistlen * sizeof (*keylist)); + + if (string_to_bitfield(disabled_keys, &keylist, keylistlen) == FALSE) + goto EXIT; + + set_bit(key, &keylist); + + if ((tmp = bitfield_to_string(keylist, keylistlen)) == NULL) + goto EXIT; + + (void)mce_write_string_to_file(GPIO_KEY_DISABLE_PATH, tmp); + +EXIT: + g_free(disabled_keys); + g_free(keylist); + g_free(tmp); + + return; +} + +/** + * Wrapper function to call mce_suspend_io_monitor() from g_slist_foreach() + * + * @param io_monitor The I/O monitor to suspend + * @param user_data Unused + */ +static void suspend_io_monitor(gpointer io_monitor, gpointer user_data) +{ + (void)user_data; + + mce_suspend_io_monitor(io_monitor); +} + +/** + * Wrapper function to call mce_resume_io_monitor() from g_slist_foreach() + * + * @param io_monitor The I/O monitor to resume + * @param user_data Unused + */ +static void resume_io_monitor(gpointer io_monitor, gpointer user_data) +{ + (void)user_data; + + mce_resume_io_monitor(io_monitor); +} + +/** + * Wrapper function to call mce_unregister_io_monitor() from g_slist_foreach() + * + * @param io_monitor The I/O monitor to unregister + * @param user_data Unused + */ +static void unregister_io_monitor(gpointer io_monitor, gpointer user_data) +{ + /* If we opened an fd to monitor, retrieve it to ensure + * that we can close it after unregistering the I/O monitor + */ + int fd = mce_get_io_monitor_fd(io_monitor); + + (void)user_data; + + mce_unregister_io_monitor(io_monitor); + + /* Close the fd if there is one */ + if (fd != -1) + close(fd); +} + +/** + * Timeout function for touchscreen I/O monitor reprogramming + * + * @param data Unused + * @return Always returns FALSE, to disable the timeout + */ +static gboolean touchscreen_io_monitor_timeout_cb(gpointer data) +{ + (void)data; + + touchscreen_io_monitor_timeout_cb_id = 0; + + /* Resume I/O monitors */ + if (touchscreen_dev_list != NULL) { + g_slist_foreach(touchscreen_dev_list, + (GFunc)resume_io_monitor, NULL); + } + + return FALSE; +} + +/** + * Cancel timeout for touchscreen I/O monitor reprogramming + */ +static void cancel_touchscreen_io_monitor_timeout(void) +{ + if (touchscreen_io_monitor_timeout_cb_id != 0) { + g_source_remove(touchscreen_io_monitor_timeout_cb_id); + touchscreen_io_monitor_timeout_cb_id = 0; + } +} + +/** + * Setup timeout for touchscreen I/O monitor reprogramming + */ +static void setup_touchscreen_io_monitor_timeout(void) +{ + cancel_touchscreen_io_monitor_timeout(); + + /* Setup new timeout */ + touchscreen_io_monitor_timeout_cb_id = + g_timeout_add_seconds(MONITORING_DELAY, + touchscreen_io_monitor_timeout_cb, NULL); +} + +/** + * I/O monitor callback for the touchscreen + * + * @param data The new data + * @param bytes_read The number of bytes read + */ +static void touchscreen_cb(gpointer data, gsize bytes_read) +{ + submode_t submode = mce_get_submode_int32(); + struct input_event *ev; + + ev = data; + + /* Don't process invalid reads */ + if (bytes_read != sizeof (*ev)) { + goto EXIT; + } + + /* Ignore unwanted events */ + if ((ev->type != EV_ABS) && (ev->type != EV_KEY)) { + goto EXIT; + } + + /* Generate activity */ + (void)execute_datapipe(&device_inactive_pipe, GINT_TO_POINTER(FALSE), + USE_INDATA, CACHE_INDATA); + + /* If visual tklock is active or autorelock isn't active, + * suspend I/O monitors + */ + if (((submode & MCE_VISUAL_TKLOCK_SUBMODE) != 0) || + ((submode & MCE_AUTORELOCK_SUBMODE) == 0)) { + if (touchscreen_dev_list != NULL) { + g_slist_foreach(touchscreen_dev_list, + (GFunc)suspend_io_monitor, NULL); + } + + /* Setup a timeout I/O monitor reprogramming */ + setup_touchscreen_io_monitor_timeout(); + } + + /* Ignore non-pressure events */ + if (((ev->type != EV_ABS) || (ev->code != ABS_PRESSURE)) && + ((ev->type != EV_KEY) || (ev->code != BTN_TOUCH))) { + goto EXIT; + } + + /* For now there's no reason to cache the value, + * or indeed to send any kind of real value at all + * + * If the event eater is active, don't send anything + */ + if ((submode & MCE_EVEATER_SUBMODE) == 0) { + (void)execute_datapipe(&touchscreen_pipe, NULL, + USE_INDATA, DONT_CACHE_INDATA); + } + +EXIT: + return; +} + +/** + * Timeout function for keypress repeats + * @note Empty function; we check the callback id + * for 0 to know if we've had a timeout or not + * + * @param data Unused + * @return Always returns FALSE, to disable the timeout + */ +static gboolean keypress_repeat_timeout_cb(gpointer data) +{ + (void)data; + + keypress_repeat_timeout_cb_id = 0; + + return FALSE; +} + +/** + * Cancel timeout for keypress repeats + */ +static void cancel_keypress_repeat_timeout(void) +{ + if (keypress_repeat_timeout_cb_id != 0) { + g_source_remove(keypress_repeat_timeout_cb_id); + keypress_repeat_timeout_cb_id = 0; + } +} + +/** + * Setup timeout for touchscreen I/O monitoring + */ +static void setup_keypress_repeat_timeout(void) +{ + cancel_keypress_repeat_timeout(); + + /* Setup new timeout */ + keypress_repeat_timeout_cb_id = + g_timeout_add_seconds(MONITORING_DELAY, + keypress_repeat_timeout_cb, NULL); +} + +/** + * I/O monitor callback for keypresses + * + * @param data The new data + * @param bytes_read The number of bytes read + */ +static void keypress_cb(gpointer data, gsize bytes_read) +{ + submode_t submode = mce_get_submode_int32(); + struct input_event *ev; + + ev = data; + + /* Don't process invalid reads */ + if (bytes_read != sizeof (*ev)) { + goto EXIT; + } + + /* Ignore non-keypress events */ + if ((ev->type != EV_KEY) && (ev->type != EV_SW)) { + goto EXIT; + } + + if (ev->type == EV_KEY) { + if ((ev->code == KEY_SCREENLOCK) && (ev->value != 2)) { + (void)execute_datapipe(&lockkey_pipe, + GINT_TO_POINTER(ev->value), + USE_INDATA, CACHE_INDATA); + } + + /* For now there's no reason to cache the keypress + * + * If the event eater is active, and this is the press, + * don't send anything; never eat releases, otherwise + * the release event for a [power] press might get lost + * and the device shut down... Not good(tm) + * + * Also, don't send repeat events, and don't send + * keypress events for the focus and screenlock keys + */ + if ((ev->code != KEY_CAMERA_FOCUS) && + (ev->code != KEY_SCREENLOCK) && + ((((submode & MCE_EVEATER_SUBMODE) == 0) && + (ev->value == 1)) || (ev->value == 0))) { + (void)execute_datapipe(&keypress_pipe, &ev, + USE_INDATA, DONT_CACHE_INDATA); + } + } + + if (ev->type == EV_SW) { + switch (ev->code) { + case SW_CAMERA_LENS_COVER: + if (ev->value != 2) { + (void)execute_datapipe(&lens_cover_pipe, GINT_TO_POINTER(ev->value ? COVER_CLOSED : COVER_OPEN), USE_INDATA, CACHE_INDATA); + } + + /* Don't generate activity on COVER_CLOSED */ + if (ev->value == 1) + goto EXIT; + + break; + + case SW_KEYPAD_SLIDE: + if (ev->value != 2) { + (void)execute_datapipe(&keyboard_slide_pipe, GINT_TO_POINTER(ev->value ? COVER_CLOSED : COVER_OPEN), USE_INDATA, CACHE_INDATA); + } + + /* Don't generate activity on COVER_CLOSED */ + if (ev->value == 1) + goto EXIT; + + break; + + case SW_FRONT_PROXIMITY: + if (ev->value != 2) { + (void)execute_datapipe(&proximity_sensor_pipe, GINT_TO_POINTER(ev->value ? COVER_CLOSED : COVER_OPEN), USE_INDATA, CACHE_INDATA); + } + + break; + + case SW_HEADPHONE_INSERT: + case SW_MICROPHONE_INSERT: + case SW_LINEOUT_INSERT: + case SW_VIDEOOUT_INSERT: + if (ev->value != 2) { + (void)execute_datapipe(&jack_sense_pipe, GINT_TO_POINTER(ev->value ? COVER_CLOSED : COVER_OPEN), USE_INDATA, CACHE_INDATA); + } + + break; + + /* Other switches do not have custom actions */ + default: + break; + } + } + + /* Generate activity: + * 0 - release (always) + * 1 - press (always) + * 2 - repeat (once a second) + */ + if ((ev->value == 0) || (ev->value == 1) || + ((ev->value == 2) && (keypress_repeat_timeout_cb_id == 0))) { + (void)execute_datapipe(&device_inactive_pipe, + GINT_TO_POINTER(FALSE), + USE_INDATA, CACHE_INDATA); + + if (ev->value == 2) { + setup_keypress_repeat_timeout(); + } + } + +EXIT: + return; +} + +/** + * Timeout callback for misc event monitoring reprogramming + * + * @param data Unused + * @return Always returns FALSE, to disable the timeout + */ +static gboolean misc_io_monitor_timeout_cb(gpointer data) +{ + (void)data; + + misc_io_monitor_timeout_cb_id = 0; + + /* Resume I/O monitors */ + if (misc_dev_list != NULL) { + g_slist_foreach(misc_dev_list, + (GFunc)resume_io_monitor, NULL); + } + + return FALSE; +} + +/** + * Cancel timeout for misc event I/O monitoring + */ +static void cancel_misc_io_monitor_timeout(void) +{ + if (misc_io_monitor_timeout_cb_id != 0) { + g_source_remove(misc_io_monitor_timeout_cb_id); + misc_io_monitor_timeout_cb_id = 0; + } +} + +/** + * Setup timeout for misc event I/O monitoring + */ +static void setup_misc_io_monitor_timeout(void) +{ + cancel_misc_io_monitor_timeout(); + + /* Setup new timeout */ + misc_io_monitor_timeout_cb_id = + g_timeout_add_seconds(MONITORING_DELAY, + misc_io_monitor_timeout_cb, NULL); +} + +/** + * I/O monitor callback for misc /dev/input devices + * + * @param data Unused + * @param bytes_read Unused + */ +static void misc_cb(gpointer data, gsize bytes_read) +{ + struct input_event *ev; + + ev = data; + + /* Don't process invalid reads */ + if (bytes_read != sizeof (*ev)) { + goto EXIT; + } + + /* Ignore synchronisation, force feedback, LED, + * and force feedback status + */ + switch (ev->type) { + case EV_SYN: + case EV_LED: + case EV_SND: + case EV_FF: + case EV_FF_STATUS: + goto EXIT; + + default: + break; + } + + /* ev->type for the jack sense is EV_SW */ + mce_log(LL_DEBUG, "ev->type: %d", ev->type); + + /* Generate activity */ + (void)execute_datapipe(&device_inactive_pipe, GINT_TO_POINTER(FALSE), + USE_INDATA, CACHE_INDATA); + + /* Suspend I/O monitors */ + if (misc_dev_list != NULL) { + g_slist_foreach(misc_dev_list, + (GFunc)suspend_io_monitor, NULL); + } + + /* Setup a timeout I/O monitor reprogramming */ + setup_misc_io_monitor_timeout(); + +EXIT: + return; +} + +/** + * Check whether the fd in question supports the switches + * we want information about -- if so, update their state + */ +static void get_switch_state(gpointer io_monitor, gpointer user_data) +{ + /* Get the fd of the I/O monitor */ + const gchar *filename = mce_get_io_monitor_name(io_monitor); + int fd = mce_get_io_monitor_fd(io_monitor); + gulong *featurelist = NULL; + gulong *statelist = NULL; + gsize featurelistlen; + gint state; + + (void)user_data; + + featurelistlen = (KEY_CNT / bitsize_of(*featurelist)) + + ((KEY_CNT % bitsize_of(*featurelist)) ? 1 : 0); + featurelist = g_malloc0(featurelistlen * sizeof (*featurelist)); + statelist = g_malloc0(featurelistlen * sizeof (*statelist)); + + if (ioctl(fd, EVIOCGBIT(EV_SW, SW_MAX), featurelist) == -1) { + mce_log(LL_ERR, + "ioctl(EVIOCGBIT(EV_SW, SW_MAX)) failed on `%s'; %s", + filename, g_strerror(errno)); + errno = 0; + goto EXIT; + } + + if (ioctl(fd, EVIOCGSW(SW_MAX), statelist) == -1) { + mce_log(LL_ERR, + "ioctl(EVIOCGSW(SW_MAX)) failed on `%s'; %s", + filename, g_strerror(errno)); + errno = 0; + goto EXIT; + } + + if (test_bit(SW_CAMERA_LENS_COVER, featurelist) == TRUE) { + state = test_bit(SW_CAMERA_LENS_COVER, statelist); + + (void)execute_datapipe(&lens_cover_pipe, GINT_TO_POINTER(state ? COVER_CLOSED : COVER_OPEN), USE_INDATA, CACHE_INDATA); + } + + if (test_bit(SW_KEYPAD_SLIDE, featurelist) == TRUE) { + state = test_bit(SW_KEYPAD_SLIDE, statelist); + + (void)execute_datapipe(&keyboard_slide_pipe, GINT_TO_POINTER(state ? COVER_CLOSED : COVER_OPEN), USE_INDATA, CACHE_INDATA); + } + + if (test_bit(SW_FRONT_PROXIMITY, featurelist) == TRUE) { + state = test_bit(SW_FRONT_PROXIMITY, statelist); + + (void)execute_datapipe(&proximity_sensor_pipe, GINT_TO_POINTER(state ? COVER_CLOSED : COVER_OPEN), USE_INDATA, CACHE_INDATA); + } + +EXIT: + g_free(statelist); + g_free(featurelist); + + return; +} + +/** + * Update switch states + */ +static void update_switch_states(void) +{ + + if (keyboard_dev_list != NULL) { + g_slist_foreach(keyboard_dev_list, + (GFunc)get_switch_state, NULL); + } +} + +/** + * Try to match /dev/input event file to a specific driver + * + * @param filename A string containing the name of the event file + * @param drivers An array of driver names + * @return An open file descriptor on success, -1 on failure + */ +static int match_event_file(const gchar *const filename, + const gchar *const *const drivers) +{ + static char name[256]; + int fd = -1; + int i; + + /* If we cannot open the file, abort */ + if ((fd = open(filename, O_NONBLOCK | O_RDONLY)) == -1) { + mce_log(LL_DEBUG, "Failed to open `%s', skipping", + filename); + + /* Ignore error */ + errno = 0; + goto EXIT; + } + + for (i = 0; drivers[i] != NULL; i++) { + if (ioctl(fd, EVIOCGNAME(sizeof name), name) >= 0) { + if (strcmp(name, drivers[i]) == 0) { + /* We found our event file */ + mce_log(LL_DEBUG, + "`%s' is `%s'", + filename, drivers[i]); + break; + } + } else { + mce_log(LL_WARN, + "ioctl(EVIOCGNAME) failed on `%s'", + filename); + } + } + + /* If the scan terminated with drivers[i] == NULL, + * we didn't find any match + */ + if (drivers[i] == NULL) { + /* XXX: improve close policy? errno? */ + close(fd); + fd = -1; + goto EXIT; + } + +EXIT: + return fd; +} + +/** + * Custom compare function used to find I/O monitor entries + * + * @param iomon_id An I/O monitor cookie + * @param name The name to search for + * @return Less than, equal to, or greater than zero depending + * whether the name of the I/O monitor with the id iomon_id + * is less than, equal to, or greater than name + */ +static gint iomon_name_compare(gconstpointer iomon_id, + gconstpointer name) +{ + const gchar *iomon_name = mce_get_io_monitor_name(iomon_id); + + return strcmp(iomon_name, name); +} + +/** + * Match and register I/O monitor + */ +static void match_and_register_io_monitor(const gchar *filename) +{ + int fd; + + if ((fd = match_event_file(filename, + driver_blacklist)) != -1) { + /* If the driver for the event file is blacklisted, skip it */ + close(fd); + fd = -1; + } else if ((fd = match_event_file(filename, + touchscreen_event_drivers)) != -1) { + gconstpointer iomon = NULL; + + iomon = mce_register_io_monitor_chunk(fd, filename, MCE_IO_ERROR_POLICY_WARN, G_IO_IN | G_IO_ERR, FALSE, touchscreen_cb, sizeof (struct input_event)); + + /* If we fail to register an I/O monitor, + * don't leak the file descriptor, + * and don't add the device to the list + */ + if (iomon == NULL) { + close(fd); + } else { + touchscreen_dev_list = g_slist_prepend(touchscreen_dev_list, (gpointer)iomon); + } + } else if ((fd = match_event_file(filename, keyboard_event_drivers)) != -1) { + gconstpointer iomon = NULL; + + iomon = mce_register_io_monitor_chunk(fd, filename, MCE_IO_ERROR_POLICY_WARN, G_IO_IN | G_IO_ERR, FALSE, keypress_cb, sizeof (struct input_event)); + + /* If we fail to register an I/O monitor, + * don't leak the file descriptor, + * and don't add the device to the list + */ + if (iomon == NULL) { + close(fd); + } else { + keyboard_dev_list = g_slist_prepend(keyboard_dev_list, (gpointer)iomon); + } + } else { + gconstpointer iomon = NULL; + + /* XXX: don't register a misc device unless it has + * EV_KEY, EV_REL, EV_ABS, EV_MSC or EV_SW events + */ + iomon = mce_register_io_monitor_chunk(-1, filename, MCE_IO_ERROR_POLICY_WARN, G_IO_IN | G_IO_ERR, FALSE, misc_cb, sizeof (struct input_event)); + + /* If we fail to register an I/O monitor, + * don't add the device to the list + */ + if (iomon != NULL) { + misc_dev_list = g_slist_prepend(misc_dev_list, (gpointer)iomon); + } + } +} + +/** + * Update list of input devices + * Remove the I/O monitor for the specified device (if existing) + * and (re)open it if available + * + * @param device The device to add/remove + * @param add TRUE if the device was added, FALSE if it was removed + * @return TRUE on success, FALSE on failure + */ +static void update_inputdevices(const gchar *device, gboolean add) +{ + gconstpointer iomon_id = NULL; + GSList *list_entry = NULL; + + /* Try to find a matching touchscreen I/O monitor */ + list_entry = g_slist_find_custom(touchscreen_dev_list, device, + iomon_name_compare); + + /* If we find one, obtain the iomon ID, + * remove the entry and finally unregister the I/O monitor + */ + if (list_entry != NULL) { + iomon_id = list_entry->data; + touchscreen_dev_list = g_slist_remove(touchscreen_dev_list, + iomon_id); + mce_unregister_io_monitor(iomon_id); + } + + /* Try to find a matching keyboard I/O monitor */ + list_entry = g_slist_find_custom(keyboard_dev_list, device, + iomon_name_compare); + + /* If we find one, obtain the iomon ID, + * remove the entry and finally unregister the I/O monitor + */ + if (list_entry != NULL) { + iomon_id = list_entry->data; + keyboard_dev_list = g_slist_remove(keyboard_dev_list, + iomon_id); + mce_unregister_io_monitor(iomon_id); + } + + /* Try to find a matching touchscreen I/O monitor */ + list_entry = g_slist_find_custom(misc_dev_list, device, + iomon_name_compare); + + /* If we find one, obtain the iomon ID, + * remove the entry and finally unregister the I/O monitor + */ + if (list_entry != NULL) { + iomon_id = list_entry->data; + misc_dev_list = g_slist_remove(misc_dev_list, + iomon_id); + mce_unregister_io_monitor(iomon_id); + } + + if (add == TRUE) + match_and_register_io_monitor(device); +} + +/** + * Scan /dev/input for input event devices + * + * @todo Modify this function to use g_dir_open/g_dir_read_name/g_dir_close + * @return TRUE on success, FALSE on failure + */ +static gboolean scan_inputdevices(void) +{ + DIR *dir = NULL; + struct dirent *direntry = NULL; + gboolean status = FALSE; + + if ((dir = opendir(DEV_INPUT_PATH)) == NULL) { + mce_log(LL_ERR, "opendir() failed; %s", g_strerror(errno)); + errno = 0; + goto EXIT; + } + + for (direntry = readdir(dir); + (direntry != NULL && telldir(dir)); + direntry = readdir(dir)) { + gchar *filename = NULL; + + if (strncmp(direntry->d_name, EVENT_FILE_PREFIX, + strlen(EVENT_FILE_PREFIX)) != 0) { + mce_log(LL_DEBUG, + "`%s/%s' skipped", + DEV_INPUT_PATH, + direntry->d_name); + continue; + } + + filename = g_strconcat(DEV_INPUT_PATH, "/", + direntry->d_name, NULL); + match_and_register_io_monitor(filename); + g_free(filename); + } + + /* Report, but ignore, errors when closing directory */ + if (closedir(dir) == -1) { + mce_log(LL_ERR, + "closedir() failed; %s", g_strerror(errno)); + errno = 0; + } + + status = TRUE; + +EXIT: + return status; +} + +/** + * Unregister monitors for input devices allocated by scan_inputdevices + */ +static void unregister_inputdevices(void) +{ + if (touchscreen_dev_list != NULL) { + g_slist_foreach(touchscreen_dev_list, + (GFunc)unregister_io_monitor, NULL); + g_slist_free(touchscreen_dev_list); + touchscreen_dev_list = NULL; + } + + if (keyboard_dev_list != NULL) { + g_slist_foreach(keyboard_dev_list, + (GFunc)unregister_io_monitor, NULL); + g_slist_free(keyboard_dev_list); + keyboard_dev_list = NULL; + } + + if (misc_dev_list != NULL) { + g_slist_foreach(misc_dev_list, + (GFunc)unregister_io_monitor, NULL); + g_slist_free(misc_dev_list); + misc_dev_list = NULL; + } +} + +/** + * Callback for directory changes + * + * @param monitor Unused + * @param file The file that changed + * @param other_file Unused + * @param event_type The event that occured + * @param user_data Unused + */ +static void dir_changed_cb(GFileMonitor *monitor, + GFile *file, GFile *other_file, + GFileMonitorEvent event_type, gpointer user_data) +{ + char *filename = g_file_get_basename(file); + char *filepath = g_file_get_path(file); + + (void)monitor; + (void)other_file; + (void)user_data; + + if ((filename == NULL) || (filepath == NULL)) + goto EXIT; + + if (strncmp(filename, + EVENT_FILE_PREFIX, strlen(EVENT_FILE_PREFIX)) != 0) + goto EXIT; + + switch (event_type) { + case G_FILE_MONITOR_EVENT_CREATED: + update_inputdevices(filepath, TRUE); + break; + + case G_FILE_MONITOR_EVENT_DELETED: + update_inputdevices(filepath, FALSE); + break; + + default: + break; + } + +EXIT: + g_free(filepath); + g_free(filename); + + return; +} + +/** + * Handle submode change + * + * @param data The submode stored in a pointer + */ +static void submode_trigger(gconstpointer data) +{ + static submode_t old_submode = MCE_NORMAL_SUBMODE; + submode_t submode = GPOINTER_TO_INT(data); + + /* If the tklock is enabled, disable the camera focus interrupts, + * since we don't use them anyway + */ + if ((submode & MCE_TKLOCK_SUBMODE) != 0) { + if ((old_submode & MCE_TKLOCK_SUBMODE) == 0) { + if (gpio_key_disable_exists == TRUE) + disable_gpio_key(KEY_CAMERA_FOCUS); + } + } else { + if ((old_submode & MCE_TKLOCK_SUBMODE) != 0) { + if (gpio_key_disable_exists == TRUE) + enable_gpio_key(KEY_CAMERA_FOCUS); + } + } + + old_submode = submode; +} + +/** + * Init function for the /dev/input event component + * + * @return TRUE on success, FALSE on failure + */ +gboolean mce_input_init(void) +{ + GError *error = NULL; + gboolean status = FALSE; + + /* Append triggers/filters to datapipes */ + append_output_trigger_to_datapipe(&submode_pipe, + submode_trigger); + + /* Retrieve a GFile pointer to the directory to monitor */ + dev_input_gfp = g_file_new_for_path(DEV_INPUT_PATH); + + /* Monitor the directory */ + if ((dev_input_gfmp = g_file_monitor_directory(dev_input_gfp, + G_FILE_MONITOR_NONE, + NULL, &error)) == NULL) { + mce_log(LL_ERR, + "Failed to add monitor for directory `%s'; %s", + DEV_INPUT_PATH, error->message); + goto EXIT; + } + + /* XXX: There is a race condition here; if a file (dis)appears + * after this scan, but before we start monitoring, + * then we'll miss that device. The race is miniscule though, + * and any workarounds are likely to be cumbersome + */ + /* Find the initial set of input devices */ + if ((status = scan_inputdevices()) == FALSE) { + g_file_monitor_cancel(dev_input_gfmp); + dev_input_gfmp = NULL; + goto EXIT; + } + + /* Connect "changed" signal for the directory monitor */ + dev_input_handler_id = + g_signal_connect(G_OBJECT(dev_input_gfmp), "changed", + G_CALLBACK(dir_changed_cb), NULL); + + /* Get configuration options */ + longdelay = mce_conf_get_int(MCE_CONF_HOMEKEY_GROUP, + MCE_CONF_HOMEKEY_LONG_DELAY, + DEFAULT_HOME_LONG_DELAY, + NULL); + + update_switch_states(); + + gpio_key_disable_exists = (g_access(GPIO_KEY_DISABLE_PATH, W_OK) == 0); + +EXIT: + g_clear_error(&error); + + return status; +} + +/** + * Exit function for the /dev/input event component + */ +void mce_input_exit(void) +{ + /* Remove triggers/filters from datapipes */ + remove_output_trigger_from_datapipe(&submode_pipe, + submode_trigger); + + if (dev_input_gfmp != NULL) { + g_signal_handler_disconnect(G_OBJECT(dev_input_gfmp), + dev_input_handler_id); + dev_input_handler_id = 0; + g_file_monitor_cancel(dev_input_gfmp); + } + + unregister_inputdevices(); + + /* Remove all timer sources */ + cancel_touchscreen_io_monitor_timeout(); + cancel_keypress_repeat_timeout(); + cancel_misc_io_monitor_timeout(); + + return; +} diff --git a/event-input.h b/event-input.h new file mode 100644 index 00000000..bdac0f4a --- /dev/null +++ b/event-input.h @@ -0,0 +1,199 @@ +/** + * @file event-input.h + * Headers for the /dev/input event provider for the Mode Control Entity + *

+ * Copyright © 2007-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _EVENT_INPUT_H_ +#define _EVENT_INPUT_H_ + +#include + +#include /* KEY_POWER */ + +/** Path to the input device directory */ +#define DEV_INPUT_PATH "/dev/input" +/** Prefix for event files */ +#define EVENT_FILE_PREFIX "event" +/** Path to the GPIO key disable interface */ +#define GPIO_KEY_DISABLE_PATH "/sys/devices/platform/gpio-keys/disabled_keys" + +/* XXX: + * We should probably use + * /dev/input/keypad + * /dev/input/gpio-keys + * /dev/input/pwrbutton + * /dev/input/ts + * and add whitelist entries for misc devices instead + */ + +/** + * List of drivers that provide touchscreen events + * XXX: If this is made case insensitive, + * we could search for "* touchscreen" instead + */ +static const gchar *const touchscreen_event_drivers[] = { + /** Input layer name for the Atmel mXT touchscreen */ + "Atmel mXT Touchscreen", + + /** Input layer name for the Atmel QT602240 touchscreen */ + "Atmel QT602240 Touchscreen", + + /** TSC2005 touchscreen */ + "TSC2005 touchscreen", + + /** TSC2301 touchscreen */ + "TSC2301 touchscreen", + + /** ADS784x touchscreen */ + "ADS784x touchscreen", + + /** No more entries */ + NULL +}; + +/** + * List of drivers that provide keyboard events + */ +static const gchar *const keyboard_event_drivers[] = { + /** Input layer name for the TWL4030 keyboard/keypad */ + "TWL4030 Keypad", + + /** Legacy input layer name for the TWL4030 keyboard/keypad */ + "omap_twl4030keypad", + + /** Generic input layer name for keyboard/keypad */ + "Internal keyboard", + + /** Input layer name for the LM8323 keypad */ + "LM8323 keypad", + + /** Generic input layer name for keypad */ + "Internal keypad", + + /** Input layer name for the TSC2301 keypad */ + "TSC2301 keypad", + + /** Legacy generic input layer name for keypad */ + "omap-keypad", + + /** Input layer name for standard PC keyboards */ + "AT Translated Set 2 keyboard", + + /** Input layer name for the TWL4030 power button */ + "twl4030_pwrbutton", + + /** Input layer name for the Triton 2 power button */ + "triton2-pwrbutton", + + /** Input layer name for the Retu powerbutton */ + "retu-pwrbutton", + + /** Input layer name for the PC Power button */ + "Power Button", + + /** Input layer name for the PC Sleep button */ + "Sleep Button", + + /** Input layer name for the Thinkpad extra buttons */ + "Thinkpad Extra Buttons", + + /** Input layer name for ACPI virtual keyboard */ + "ACPI Virtual Keyboard Device", + + /** Input layer name for GPIO-keys */ + "gpio-keys", + + /** Input layer name for DFL-61/TWL4030 jack sense */ + "dfl61-twl4030 Jack", + + /** Legacy input layer name for TWL4030 jack sense */ + "rx71-twl4030 Jack", + + /** Input layer name for PC Lid switch */ + "Lid Switch", + + /** No more entries */ + NULL +}; + +/** + * List of drivers that we should not monitor + */ +static const gchar *const driver_blacklist[] = { + /** Input layer name for the AMI305 magnetometer */ + "ami305 magnetometer", + + /** Input layer name for the ST LIS3LV02DL accelerometer */ + "ST LIS3LV02DL Accelerometer", + + /** Input layer name for the ST LIS302DL accelerometer */ + "ST LIS302DL Accelerometer", + + /** Input layer name for the TWL4030 vibrator */ + "twl4030:vibrator", + + /** Input layer name for AV accessory */ + "AV Accessory", + + /** Input layer name for the video bus */ + "Video Bus", + + /** Input layer name for the PC speaker */ + "PC Speaker", + + /** Input layer name for the Intel HDA headphone */ + "HDA Intel Headphone", + + /** Input layer name for the Intel HDA microphone */ + "HDA Intel Mic", + + /** Input layer name for the UVC 17ef:4807 webcam in thinkpad X301 */ + "UVC Camera (17ef:4807)", + + /** Input layer name for the UVC 17ef:480c webcam in thinkpad X301si */ + "UVC Camera (17ef:480c)", + + /** No more entries */ + NULL +}; + +/** + * Delay between I/O monitoring setups and keypress repeats; 1 second + */ +#define MONITORING_DELAY 1 + +/** Name of Homekey configuration group */ +#define MCE_CONF_HOMEKEY_GROUP "HomeKey" + +/** Name of configuration key for long [home] press delay */ +#define MCE_CONF_HOMEKEY_LONG_DELAY "HomeKeyLongDelay" + +/** Name of configuration key for short [home] press action */ +#define MCE_CONF_HOMEKEY_SHORT_ACTION "HomeKeyShortAction" + +/** Name of configuration key for long [home] press action */ +#define MCE_CONF_HOMEKEY_LONG_ACTION "HomeKeyLongAction" + +/** Long delay for the [home] button in milliseconds */ +#define DEFAULT_HOME_LONG_DELAY 800 /* 0.8 seconds */ + +/* When MCE is made modular, this will be handled differently */ +gboolean mce_input_init(void); +void mce_input_exit(void); + +#endif /* _EVENT_INPUT_H_ */ diff --git a/event-switches.c b/event-switches.c new file mode 100644 index 00000000..962ae91d --- /dev/null +++ b/event-switches.c @@ -0,0 +1,531 @@ +/** + * @file event-switches.c + * Switch event provider for the Mode Control Entity + *

+ * Copyright © 2007-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include +#include /* g_access() */ + +#include /* strncmp(), strlen() */ +#include /* W_OK */ + +#include "mce.h" +#include "event-switches.h" + +#include "mce-io.h" /* mce_register_io_monitor_string(), + * mce_unregister_io_monitor() + */ +#include "datapipe.h" /* execute_datapipe(), + * append_input_trigger_to_datapipe(), + * remove_input_trigger_from_datapipe() + */ + +/** ID for the lockkey I/O monitor */ +static gconstpointer lockkey_iomon_id = NULL; + +/** ID for the keyboard slide I/O monitor */ +static gconstpointer kbd_slide_iomon_id = NULL; + +/** ID for the cam focus I/O monitor */ +static gconstpointer cam_focus_iomon_id = NULL; + +/** Can the camera focus interrupt be disabled? */ +static gboolean cam_focus_disable_exists = FALSE; + +/** ID for the cam launch I/O monitor */ +static gconstpointer cam_launch_iomon_id = NULL; + +/** ID for the lid cover I/O monitor */ +static gconstpointer lid_cover_iomon_id = NULL; + +/** ID for the proximity sensor I/O monitor */ +static gconstpointer proximity_sensor_iomon_id = NULL; + +/** Can the proximity sensor interrupt be disabled? */ +static gboolean proximity_sensor_disable_exists = FALSE; + +/** ID for the MUSB OMAP3 usb cable I/O monitor */ +static gconstpointer musb_omap3_usb_cable_iomon_id = NULL; + +/** ID for the mmc0 cover I/O monitor */ +static gconstpointer mmc0_cover_iomon_id = NULL; + +/** ID for the mmc cover I/O monitor */ +static gconstpointer mmc_cover_iomon_id = NULL; + +/** ID for the lens cover I/O monitor */ +static gconstpointer lens_cover_iomon_id = NULL; + +/** ID for the battery cover I/O monitor */ +static gconstpointer bat_cover_iomon_id = NULL; + +/** Cached call state */ +static call_state_t call_state = CALL_STATE_INVALID; + +/** Cached alarm UI state */ +static alarm_ui_state_t alarm_ui_state = MCE_ALARM_UI_INVALID_INT32; + +/** Does the device have a flicker key? */ +gboolean has_flicker_key = FALSE; + +/** + * Generic I/O monitor callback that only generates activity + * + * @param data Unused + * @param bytes_read Unused + */ +static void generic_activity_cb(gpointer data, gsize bytes_read) +{ + (void)data; + (void)bytes_read; + + /* Generate activity */ + (void)execute_datapipe(&device_inactive_pipe, GINT_TO_POINTER(FALSE), + USE_INDATA, CACHE_INDATA); +} + +/** + * I/O monitor callback for the camera launch button + * + * @param data Unused + * @param bytes_read Unused + */ +static void camera_launch_button_cb(gpointer data, gsize bytes_read) +{ + camera_button_state_t camera_button_state; + + (void)bytes_read; + + if (!strncmp(data, MCE_CAM_LAUNCH_ACTIVE, + strlen(MCE_CAM_LAUNCH_ACTIVE))) { + camera_button_state = CAMERA_BUTTON_LAUNCH; + } else { + camera_button_state = CAMERA_BUTTON_UNPRESSED; + } + + /* Generate activity */ + (void)execute_datapipe(&device_inactive_pipe, GINT_TO_POINTER(FALSE), + USE_INDATA, CACHE_INDATA); + + /* Update camera button state */ + (void)execute_datapipe(&camera_button_pipe, + GINT_TO_POINTER(camera_button_state), + USE_INDATA, CACHE_INDATA); +} + +/** + * I/O monitor callback for the lock flicker key + * + * @param data The new data + * @param bytes_read Unused + */ +static void lockkey_cb(gpointer data, gsize bytes_read) +{ + gint lockkey_state; + + (void)bytes_read; + + if (!strncmp(data, MCE_FLICKER_KEY_ACTIVE, + strlen(MCE_FLICKER_KEY_ACTIVE))) { + lockkey_state = 1; + } else { + lockkey_state = 0; + } + + (void)execute_datapipe(&lockkey_pipe, + GINT_TO_POINTER(lockkey_state), + USE_INDATA, CACHE_INDATA); +} + +/** + * I/O monitor callback for the keyboard slide + * + * @param data The new data + * @param bytes_read Unused + */ +static void kbd_slide_cb(gpointer data, gsize bytes_read) +{ + cover_state_t slide_state; + + (void)bytes_read; + + if (!strncmp(data, MCE_KBD_SLIDE_OPEN, strlen(MCE_KBD_SLIDE_OPEN))) { + slide_state = COVER_OPEN; + + /* Generate activity */ + (void)execute_datapipe(&device_inactive_pipe, + GINT_TO_POINTER(FALSE), + USE_INDATA, CACHE_INDATA); + } else { + slide_state = COVER_CLOSED; + } + + (void)execute_datapipe(&keyboard_slide_pipe, + GINT_TO_POINTER(slide_state), + USE_INDATA, CACHE_INDATA); +} + +/** + * I/O monitor callback for the lid cover + * + * @param data The new data + * @param bytes_read Unused + */ +static void lid_cover_cb(gpointer data, gsize bytes_read) +{ + cover_state_t lid_cover_state; + + (void)bytes_read; + + if (!strncmp(data, MCE_LID_COVER_OPEN, strlen(MCE_LID_COVER_OPEN))) { + lid_cover_state = COVER_OPEN; + + /* Generate activity */ + (void)execute_datapipe(&device_inactive_pipe, + GINT_TO_POINTER(FALSE), + USE_INDATA, CACHE_INDATA); + } else { + lid_cover_state = COVER_CLOSED; + } + + (void)execute_datapipe(&lid_cover_pipe, + GINT_TO_POINTER(lid_cover_state), + USE_INDATA, CACHE_INDATA); +} + +/** + * I/O monitor callback for the proximity sensor + * + * @param data The new data + * @param bytes_read Unused + */ +static void proximity_sensor_cb(gpointer data, gsize bytes_read) +{ + cover_state_t proximity_sensor_state; + + (void)bytes_read; + + if (!strncmp(data, MCE_PROXIMITY_SENSOR_OPEN, + strlen(MCE_PROXIMITY_SENSOR_OPEN))) { + proximity_sensor_state = COVER_OPEN; + } else { + proximity_sensor_state = COVER_CLOSED; + } + + (void)execute_datapipe(&proximity_sensor_pipe, + GINT_TO_POINTER(proximity_sensor_state), + USE_INDATA, CACHE_INDATA); +} + +/** + * I/O monitor callback for the USB cable + * + * @param data The new data + * @param bytes_read Unused + */ +static void usb_cable_cb(gpointer data, gsize bytes_read) +{ + usb_cable_state_t cable_state; + + (void)bytes_read; + + if (!strncmp(data, MCE_MUSB_OMAP3_USB_CABLE_CONNECTED, + strlen(MCE_MUSB_OMAP3_USB_CABLE_CONNECTED))) { + cable_state = USB_CABLE_CONNECTED; + } else { + cable_state = USB_CABLE_DISCONNECTED; + } + + /* Generate activity */ + (void)execute_datapipe(&device_inactive_pipe, GINT_TO_POINTER(FALSE), + USE_INDATA, CACHE_INDATA); + + (void)execute_datapipe(&usb_cable_pipe, + GINT_TO_POINTER(cable_state), + USE_INDATA, CACHE_INDATA); +} + +/** + * I/O monitor callback for the lens cover + * + * @param data The new data + * @param bytes_read Unused + */ +static void lens_cover_cb(gpointer data, gsize bytes_read) +{ + cover_state_t lens_cover_state; + + (void)bytes_read; + + if (!strncmp(data, MCE_LENS_COVER_OPEN, strlen(MCE_LENS_COVER_OPEN))) { + lens_cover_state = COVER_OPEN; + + /* Generate activity */ + (void)execute_datapipe(&device_inactive_pipe, + GINT_TO_POINTER(FALSE), + USE_INDATA, CACHE_INDATA); + } else { + lens_cover_state = COVER_CLOSED; + } + + (void)execute_datapipe(&lens_cover_pipe, + GINT_TO_POINTER(lens_cover_state), + USE_INDATA, CACHE_INDATA); +} + +/** + * Update the proximity state + * + * @note Only gives reasonable readings when the proximity sensor is enabled + * @return TRUE on success, FALSE on failure + */ +static gboolean update_proximity_sensor_state(void) +{ + cover_state_t proximity_sensor_state; + gboolean status = FALSE; + gchar *tmp = NULL; + + if (mce_read_string_from_file(MCE_PROXIMITY_SENSOR_STATE_PATH, + &tmp) == FALSE) { + goto EXIT; + } + + if (!strncmp(tmp, MCE_PROXIMITY_SENSOR_OPEN, + strlen(MCE_PROXIMITY_SENSOR_OPEN))) { + proximity_sensor_state = COVER_OPEN; + } else { + proximity_sensor_state = COVER_CLOSED; + } + + (void)execute_datapipe(&proximity_sensor_pipe, + GINT_TO_POINTER(proximity_sensor_state), + USE_INDATA, CACHE_INDATA); + + g_free(tmp); + +EXIT: + return status; +} + +/** + * Update the proximity monitoring + */ +static void update_proximity_monitor(void) +{ + if ((call_state == CALL_STATE_RINGING) || + (call_state == CALL_STATE_ACTIVE) || + (alarm_ui_state == MCE_ALARM_UI_VISIBLE_INT32) || + (alarm_ui_state == MCE_ALARM_UI_RINGING_INT32)) { + if (proximity_sensor_disable_exists == TRUE) { + mce_write_string_to_file(MCE_PROXIMITY_SENSOR_DISABLE_PATH, "0"); + (void)update_proximity_sensor_state(); + } + } else { + if (proximity_sensor_disable_exists == TRUE) + mce_write_string_to_file(MCE_PROXIMITY_SENSOR_DISABLE_PATH, "1"); + } +} + +/** + * Handle call state change + * + * @param data The call state stored in a pointer + */ +static void call_state_trigger(gconstpointer const data) +{ + call_state = GPOINTER_TO_INT(data); + + update_proximity_monitor(); +} + +/** + * Handle alarm UI state change + * + * @param data The alarm state stored in a pointer + */ +static void alarm_ui_state_trigger(gconstpointer const data) +{ + alarm_ui_state = GPOINTER_TO_INT(data); + + update_proximity_monitor(); +} + +/** + * Handle submode change + * + * @param data The submode stored in a pointer + */ +static void submode_trigger(gconstpointer data) +{ + static submode_t old_submode = MCE_NORMAL_SUBMODE; + submode_t submode = GPOINTER_TO_INT(data); + + /* If the tklock is enabled, disable the camera focus interrupts, + * since we don't use them anyway + */ + if ((submode & MCE_TKLOCK_SUBMODE) != 0) { + if ((old_submode & MCE_TKLOCK_SUBMODE) == 0) { + if ((cam_focus_disable_exists == TRUE) && + (cam_focus_iomon_id != NULL)) + mce_write_string_to_file(MCE_CAM_FOCUS_DISABLE_PATH, "1"); + } + } else { + if ((old_submode & MCE_TKLOCK_SUBMODE) != 0) { + if (cam_focus_disable_exists == TRUE) + mce_write_string_to_file(MCE_CAM_FOCUS_DISABLE_PATH, "0"); + } + } + + old_submode = submode; +} + +/** + * Init function for the switches component + * + * @return TRUE on success, FALSE on failure + */ +gboolean mce_switches_init(void) +{ + gboolean status = FALSE; + + /* Append triggers/filters to datapipes */ + append_input_trigger_to_datapipe(&call_state_pipe, + call_state_trigger); + append_input_trigger_to_datapipe(&alarm_ui_state_pipe, + alarm_ui_state_trigger); + append_output_trigger_to_datapipe(&submode_pipe, + submode_trigger); + + /* Set default values, in case these are not available */ + (void)execute_datapipe(&lid_cover_pipe, + GINT_TO_POINTER(COVER_OPEN), + USE_INDATA, CACHE_INDATA); + + /* Register I/O monitors */ + // FIXME: error handling? + lockkey_iomon_id = + mce_register_io_monitor_string(-1, + MCE_FLICKER_KEY_STATE_PATH, + MCE_IO_ERROR_POLICY_IGNORE, + G_IO_PRI | G_IO_ERR, + TRUE, lockkey_cb); + kbd_slide_iomon_id = + mce_register_io_monitor_string(-1, + MCE_KBD_SLIDE_STATE_PATH, + MCE_IO_ERROR_POLICY_IGNORE, + G_IO_PRI | G_IO_ERR, + TRUE, kbd_slide_cb); + cam_focus_iomon_id = + mce_register_io_monitor_string(-1, + MCE_CAM_FOCUS_STATE_PATH, + MCE_IO_ERROR_POLICY_IGNORE, + G_IO_PRI | G_IO_ERR, + TRUE, generic_activity_cb); + cam_launch_iomon_id = + mce_register_io_monitor_string(-1, + MCE_CAM_LAUNCH_STATE_PATH, + MCE_IO_ERROR_POLICY_IGNORE, + G_IO_PRI | G_IO_ERR, + TRUE, camera_launch_button_cb); + lid_cover_iomon_id = + mce_register_io_monitor_string(-1, + MCE_LID_COVER_STATE_PATH, + MCE_IO_ERROR_POLICY_IGNORE, + G_IO_PRI | G_IO_ERR, + TRUE, lid_cover_cb); + proximity_sensor_iomon_id = + mce_register_io_monitor_string(-1, + MCE_PROXIMITY_SENSOR_STATE_PATH, + MCE_IO_ERROR_POLICY_IGNORE, + G_IO_PRI | G_IO_ERR, + TRUE, proximity_sensor_cb); + musb_omap3_usb_cable_iomon_id = + mce_register_io_monitor_string(-1, + MCE_MUSB_OMAP3_USB_CABLE_STATE_PATH, + MCE_IO_ERROR_POLICY_IGNORE, + G_IO_PRI | G_IO_ERR, + TRUE, usb_cable_cb); + lens_cover_iomon_id = + mce_register_io_monitor_string(-1, + MCE_LENS_COVER_STATE_PATH, + MCE_IO_ERROR_POLICY_IGNORE, + G_IO_PRI | G_IO_ERR, + TRUE, lens_cover_cb); + mmc0_cover_iomon_id = + mce_register_io_monitor_string(-1, + MCE_MMC0_COVER_STATE_PATH, + MCE_IO_ERROR_POLICY_IGNORE, + G_IO_PRI | G_IO_ERR, + TRUE, generic_activity_cb); + mmc_cover_iomon_id = + mce_register_io_monitor_string(-1, + MCE_MMC_COVER_STATE_PATH, + MCE_IO_ERROR_POLICY_IGNORE, + G_IO_PRI | G_IO_ERR, + TRUE, generic_activity_cb); + bat_cover_iomon_id = + mce_register_io_monitor_string(-1, + MCE_BATTERY_COVER_STATE_PATH, + MCE_IO_ERROR_POLICY_IGNORE, + G_IO_PRI | G_IO_ERR, + TRUE, generic_activity_cb); + + update_proximity_monitor(); + + if (lockkey_iomon_id != NULL) + has_flicker_key = TRUE; + + proximity_sensor_disable_exists = + (g_access(MCE_PROXIMITY_SENSOR_DISABLE_PATH, W_OK) == 0); + + cam_focus_disable_exists = + (g_access(MCE_CAM_FOCUS_DISABLE_PATH, W_OK) == 0); + + status = TRUE; + + return status; +} + +/** + * Exit function for the switches component + */ +void mce_switches_exit(void) +{ + /* Remove triggers/filters from datapipes */ + remove_output_trigger_from_datapipe(&submode_pipe, + submode_trigger); + remove_input_trigger_from_datapipe(&alarm_ui_state_pipe, + alarm_ui_state_trigger); + remove_input_trigger_from_datapipe(&call_state_pipe, + call_state_trigger); + + /* Unregister I/O monitors */ + mce_unregister_io_monitor(bat_cover_iomon_id); + mce_unregister_io_monitor(mmc_cover_iomon_id); + mce_unregister_io_monitor(mmc0_cover_iomon_id); + mce_unregister_io_monitor(lens_cover_iomon_id); + mce_unregister_io_monitor(musb_omap3_usb_cable_iomon_id); + mce_unregister_io_monitor(proximity_sensor_iomon_id); + mce_unregister_io_monitor(lid_cover_iomon_id); + mce_unregister_io_monitor(cam_launch_iomon_id); + mce_unregister_io_monitor(cam_focus_iomon_id); + mce_unregister_io_monitor(kbd_slide_iomon_id); + mce_unregister_io_monitor(lockkey_iomon_id); + + return; +} diff --git a/event-switches.h b/event-switches.h new file mode 100644 index 00000000..312d3e45 --- /dev/null +++ b/event-switches.h @@ -0,0 +1,115 @@ +/** + * @file event-switches.h + * Headers for the switch event provider for the Mode Control Entity + *

+ * Copyright © 2007-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _EVENT_SWITCHES_H_ +#define _EVENT_SWITCHES_H_ + +#include + +/** Path to the SysFS interface for the lock flicker-key status */ +#define MCE_FLICKER_KEY_STATE_PATH "/sys/devices/platform/gpio-switch/kb_lock/state" +/** Value for the lock flicker-key active state */ +#define MCE_FLICKER_KEY_ACTIVE "closed" +/** Value for the lock flicker-key inactive state */ +#define MCE_FLICKER_KEY_INACTIVE "open" + +/** Path to the SysFS interface for the keyboard slide status */ +#define MCE_KBD_SLIDE_STATE_PATH "/sys/devices/platform/gpio-switch/slide/state" +/** Value for the keyboard slide open state */ +#define MCE_KBD_SLIDE_OPEN "open" +/** Value for the keyboard slide closed state */ +#define MCE_KBD_SLIDE_CLOSED "closed" + +/** Path to the SysFS interface for the camera focus state */ +#define MCE_CAM_FOCUS_STATE_PATH "/sys/devices/platform/gpio-switch/cam_focus/state" +/** Value for the camera focus active state */ +#define MCE_CAM_FOCUS_ACTIVE "active" +/** Value for the camera focus inactive state */ +#define MCE_CAM_FOCUS_INACTIVE "inactive" +/** SysFS interface to enable/disable camera focus IRQs */ +#define MCE_CAM_FOCUS_DISABLE_PATH "/sys/devices/platform/gpio-switch/cam_focus/disable" + +/** Path to the SysFS interface for the camera launch state */ +#define MCE_CAM_LAUNCH_STATE_PATH "/sys/devices/platform/gpio-switch/cam_launch/state" +/** Value for the camera launch active state */ +#define MCE_CAM_LAUNCH_ACTIVE "active" +/** Value for the camera launch inactive state */ +#define MCE_CAM_LAUNCH_INACTIVE "inactive" + +/** Path to the SysFS interface for the lid cover status */ +#define MCE_LID_COVER_STATE_PATH "/sys/devices/platform/gpio-switch/prot_shell/cover_switch" +/** Value for the lid cover open state */ +#define MCE_LID_COVER_OPEN "open" +/** Value for the lid cover closed state */ +#define MCE_LID_COVER_CLOSED "closed" + +/** Path to the SysFS interface for the proximity sensor status */ +#define MCE_PROXIMITY_SENSOR_STATE_PATH "/sys/devices/platform/gpio-switch/proximity/state" +/** Value for the proximity sensor open state */ +#define MCE_PROXIMITY_SENSOR_OPEN "open" +/** Value for the proximity sensor closed state */ +#define MCE_PROXIMITY_SENSOR_CLOSED "closed" +/** SysFS interface to enable/disable proximity sensor IRQs */ +#define MCE_PROXIMITY_SENSOR_DISABLE_PATH "/sys/devices/platform/gpio-switch/proximity/disable" + +/** + * Path to the SysFS interface for the MUSB HDRC USB cable status; + * RX-51 + */ +#define MCE_MUSB_OMAP3_USB_CABLE_STATE_PATH "/sys/class/i2c-adapter/i2c-1/1-0048/twl4030_usb/vbus" +/** Value for the MUSB HDRC USB cable connected state */ +#define MCE_MUSB_OMAP3_USB_CABLE_CONNECTED "1" +/** Value for the MUSB HDRC USB cable disconnected state */ +#define MCE_MUSB_OMAP3_USB_CABLE_DISCONNECTED "0" + +/** Path to the SysFS interface for the RX-51 MMC0 cover status */ +#define MCE_MMC0_COVER_STATE_PATH "/sys/class/mmc_host/mmc0/cover_switch" +/** Value for the RX-51 MMC0 cover open state */ +#define MCE_MMC_COVER_OPEN "open" +/** Value for the RX-51 MMC0 cover closed state */ +#define MCE_MMC_COVER_CLOSED "closed" + +/** Path to the SysFS interface for the MMC cover status */ +#define MCE_MMC_COVER_STATE_PATH "/sys/devices/platform/gpio-switch/mmci-omap.2/cover_switch" +/** Value for the MMC cover open state */ +#define MCE_MMC_COVER_OPEN "open" +/** Value for the MMC cover closed state */ +#define MCE_MMC_COVER_CLOSED "closed" + +/** Path to the SysFS interface for the lens cover status */ +#define MCE_LENS_COVER_STATE_PATH "/sys/devices/platform/gpio-switch/cam_shutter/state" +/** Value for the lens cover open state */ +#define MCE_LENS_COVER_OPEN "open" +/** Value for the lens cover closed state */ +#define MCE_LENS_COVER_CLOSED "closed" + +/** Path to the SysFS interface for the battery cover status */ +#define MCE_BATTERY_COVER_STATE_PATH "/sys/devices/platform/gpio-switch/bat_cover/cover_switch" +/** Value for the battery cover open state */ +#define MCE_BATTERY_COVER_OPEN "open" +/** Value for the battery cover closed state */ +#define MCE_BATTERY_COVER_CLOSED "closed" + + +/* When MCE is made modular, this will be handled differently */ +gboolean mce_switches_init(void); +void mce_switches_exit(void); + +#endif /* _EVENT_SWITCHES_H_ */ diff --git a/man/mce.8 b/man/mce.8 new file mode 100644 index 00000000..96b47142 --- /dev/null +++ b/man/mce.8 @@ -0,0 +1,165 @@ +.TH MCE 8 "Jun 16, 2010" "Nokia" + +.SH NAME +mce \- daemon that provides mode control functionality + +.SH SYNOPSIS +.B mce +[\fIOPTION\fP]... + +.SH DESCRIPTION +.B mce +is a daemon that provides mode control functionality. + +On startup, +.B mce +will read the initial states of the various radios from the +radio states file and broadcast these states. + +Notifiers are registered for various GConf entries, +as well as +\%D\(hyBus message handlers for the method calls that +.B mce +provides, and \%D\(hyBus signal handlers for the signals that +.B mce +listens to. + +.B mce +will also listen to key events from the power button. +If the user preses the power button, the action configured in +.I /etc/mce/mce.ini +will be executed. + +Among the other functionality handled by +.B mce +is the logic for the touch screen and keypad lock, +LED patterns and display backlight control. + +.SH OPTIONS +.TP +.BR \-d , \ \-\-daemonflag +Run +.B mce +as a daemon +.TP +.B \-\-force\-syslog +Log to syslog even when mce isn't run as a daemon +.TP +.B \-\-force\-stderr +Log to stderr even when mce is run as a daemon +.TP +.BR \-S , \ \-\-session +Use the session bus instead of the system bus for \%D\(hyBus +communication +.TP +.B \-\-show\-module\-info +Show information about loaded modules +.TP +.B \-\-debug\-mode +Start mce even if communication with dsme fails +.TP +.B \-\-quiet +Decrease debug message verbosity +.TP +.B \-\-verbose +Increase debug message verbosity +.TP +.B \-\-help +Display help for the command +.TP +.B \-\-version +Display version and author information + +.SH GCONF KEYS +.TP +.I /system/osso/dsm/dsm/locks/touchscreen_keypad_autolock_enabled +.B bool +(read only) +.br +If this key is set to +.BR true , +the touchscreen/keypad lock +will automatically be enabled when the screen blanks. +If this key is set to +.BR false , +the lock will only be activated if the user triggers it +manually. + +.TP +.I /system/osso/dsm/display/display_brightness +.B int +(read only) +.br +The brightness profile to use for the screen. +Valid values range from +.B 1 +to +.BR 5 . + +.TP +.I /system/osso/dsm/display/display_dim_timeout +.B int +(read only) +.br +Number of seconds to wait before the screen is dimmed. + +.TP +.I /system/osso/dsm/display/display_blank_timeout +.B int +(read only) +.br +Number of seconds to wait before the screen is blanked. + +.SH FILES +.TP +.I /var/run/mce.pid +PID\(hyfile used by +.BR mce . +.TP +.I /var/run/mce/boot +File used by +.B mce +to detect whether the program was started on bootup or +restarted due to a crash/manual restart. +.TP +.I /var/lib/mce/radio_states +Stores the system radio states. +This file should never be edited manually, +and shall not be modified by any other process than +.BR mce , +since the file is only read when +.B mce +is started. + +.SH SEE ALSO +.BR mcetool (8) + +.SH HISTORY +Jun 16 2010: Updated for new and modified functionality. +.br +Jun 13 2010: Various improvements. +.br +May 13 2010: Updated for modified functionality. +.br +Oct 19 2007: Use escape\(hycodes for hyphens. +.br +Apr 12 2007: Minor fixes. +.br +Oct 23 2006: Minor fixes. +.br +Oct 3 2006: Fix typo. +.br +Sep 23 2005: Fixed minor typos. +.br +Sep 21 2005: Initial version of this manual page. + +.SH AUTHOR +mce and its manual page is written by +David Weinehall <\fIdavid.weinehall@nokia.com\fP> + +.SH REPORTING BUGS +Report bugs to +<\fIdavid.weinehall@nokia.com\fP>. + +.SH COPYRIGHT +Copyright \(co 2005\(hy2010 Nokia Corporation. All rights reserved. diff --git a/man/mce.sv.8 b/man/mce.sv.8 new file mode 100644 index 00000000..a80e4c37 --- /dev/null +++ b/man/mce.sv.8 @@ -0,0 +1,166 @@ +.TH MCE 8 "Jun 16, 2010" "Nokia" + +.SH NAMN +mce \- ett program som tillhandah\(oaller l\(:ageskontrollfunktionalitet + +.SH SYNOPSIS +.B mce +[\fIFLAGGA\fP]... + +.SH BESKRIVNING +.B mce +\(:ar ett program som tillhandah\(oaller +l\(:ageskontrollfunktionalitet. + +Vid start l\(:aser +.B mce +in vilka tillst\(oand de de olika radiorna skall ha initialt fr\(oan +radiotillst\(oandsfilen och uts\(:ander sedan dessa tillst\(oand. + +\(:Andringshanterare registreras f\(:or diverse GConfnycklar, +samt meddelandehanterare f\(:or de \%D\(hyBus\(hy\:metodanrop som +.B mce +tillhandah\(oaller och signalhanterare f\(:or de \%D\(hyBus\(hy\:signaler +som +.B mce +lyssnar efter. + +.B mce +lyssnar \(:aven efter tangenttryckningar fr\(oan +av/p\(oa\(hyknappen. Om anv\(:andaren trycker p\(oa +av/p\(oa\(hyknappen utf\(:ors den handling som angivits i +.I /etc/mce/mce.ini. + +Bland den \(:ovriga funktionalitet som hanteras av +.B mce +finns logiken f\(:or trycksk\(:arms\(hy och tangentl\(oaset, +lysdiodsm\(:onster och ljusstyrkekontroll f\(:or +bildsk\(:armen. + +.SH FLAGGOR +.TP +.BR \-d , \ \-\-daemonflag +K\(:or +.B mce +som en bakgrundsprocess ("daemon") +.TP +.B \-\-force\-syslog +Logga till syslog \(:aven om mce inte k\(:ors +som en bakgrundsprocess ("daemon") +.TP +.B \-\-force\-stderr +Logga till stderr \(:aven om mce k\(:ors +som en bakgrundsprocess ("daemon") +.TP +.BR \-S , \ \-\-session +Anv\(:and sessionsbussen ist\(:allet f\(:or systembussen +vid \%D\(hyBus\:\(hykommunikation +.TP +.B \-\-show\-module\-info +Visa information om inl\(:asta moduler +.TP +.B \-\-debug\-mode +Starta mce \(:aven om kommunikation med dsme misslyckas +.TP +.B \-\-quiet +Minska m\(:angden debuginformation +.TP +.B \-\-verbose +\(:Oka m\(:angden debuginformation +.TP +.B \-\-help +Visa en hj\(:alptext f\(:or kommandot +.TP +.B \-\-version +Visa versionsinformation och information om upphovsr\(:att + +.SH GCONF\(hyNYCKLAR +.TP +.I /system/osso/dsm/dsm/locks/touchscreen_keypad_autolock_enabled +.B bool +(enbart l\(:asning) +.br +Om denna nyckel har v\(:ardet +.BR true , +kommer trycksk\(:arms\(hy och tangentl\(oaset att aktiveras n\(:ar sk\(:armen +sl\(:acks. +Om nyckeln har v\(:ardet +.BR false , +aktiveras l\(oaset endast n\(:ar anv\(:andaren manuellt triggar +det. + +.TP +.I /system/osso/dsm/display/display_brightness +.B int +(l\(:asning/skrivning) +.br +Den ljusstyrkeprofil som skall anv\(:andas f\(:or sk\(:armen. +Giltiga v\(:arden \(:ar +.B 1 +till +.BR 5 . + +.TP +.I /system/osso/dsm/display/display_dim_timeout +.B int +(enbart l\(:asning) +.br +V\(:antetid i antal sekunder innan sk\(:armens ljusstyrka d\(:ampas. + +.TP +.I /system/osso/dsm/display/display_blank_timeout +.B int +(enbart l\(:asning) +.br +V\(:antetid i sekunder innan sk\(:armen sl\(:acks. + +.SH FILER +.TP +.I /var/run/mce.pid +Processidentitetsfil ("PID\(hyfile") som anv\(:ands av +.BR mce . +.TP +.I /var/run/mce/boot +Fil som anv\(:ands av +.B mce +f\(:or att uppt\(:acka huruvida programmet startats vid +uppstart, eller om det startats om p\(oa grund av en +krasch/manuell omstart. +.TP +.I /var/lib/mce/radio_states +Inneh\(oaller radiotillst\(oand. +Den h\(:ar filen b\(:or aldrig \(:andras manuellt, +och f\(oar inte modifieras av andra processer \(:an +.BR mce , +eftersom filen endast l\(:ases av +.B mce +n\(:ar programmet startar. + +.SH SE \(:AVEN +.BR mcetool (8) + +.SH HISTORIK +Jun 16 2010: Uppdaterad f\(:or ny och \(:andrad funktionalitet. +.br +Jun 13 2010: Diverse f\(:orb\(:attringar. +.br +Maj 13 2010: Uppdaterad f\(:or \(:andrad funktionalitet. +.br +Okt 19 2007: Anv\(:and escape\(hysekvenser ist\(:allet f\(:or bindestreck. +.br +Apr 12 2007: Anv\(:and escape\(hysekvenser ist\(:allet f\(:or latin\(hy1. +.br +Okt 23 2006: Sm\(:arre r\(:attelser. +.br +Sep 23 2005: F\(:orsta svenska versionen av manualsidan. + +.SH F\(:ORFATTARE +mce och dess manualsida \(:ar skriven av +David Weinehall <\fIdavid.weinehall@nokia.com\fP> + +.SH RAPPORTERA FEL +Rapportera fel till +<\fIdavid.weinehall@nokia.com\fP>. + +.SH UPPHOVSR\(:ATT +Copyright \(co 2005\(hy2010 Nokia Corporation. Alla r\(:attigheter f\(:orbeh\(oallna. diff --git a/man/mcetool.8 b/man/mcetool.8 new file mode 100644 index 00000000..f370094f --- /dev/null +++ b/man/mcetool.8 @@ -0,0 +1,178 @@ +.TH MCETOOL 8 "Oct 27, 2010" "Nokia" + +.SH NAME +mcetool \- tool to test mode control functionality + +.SH SYNOPSIS +.B mcetool +[\fIOPTION\fP]... + +.SH DESCRIPTION +.B mcetool +provides several means of testing mode control functionality, +and to set or display mode related settings. + +.SH OPTIONS +.TP +.B \-\-blank\-prevent +Send blank prevent request to MCE +.TP +.B \-\-cancel\-blank\-prevent +Send cancel blank prevent request to MCE +.TP +.B \-\-unblank\-screen +Send unblank screen request to MCE +.TP +.B \-\-dim\-screen +Send screen dim request to MCE +.TP +.B \-\-blank\-screen +Send screen blank request to MCE +.TP +.B \-\-set\-display\-brightness +Set the display brightness level; valid levels are: +1\-5 +.TP +.B \-\-set\-inhibit\-mode +Set the screen blanking inhibit mode; valid modes are: +"disabled", "stay\-on\-with\-charger", "stay\-dim\-with\-charger", +"stay\-on" and "stay\-dim" +.TP +.B \-\-set\-cabc\-mode +Set the CABC mode; valid modes are: +"off", "ui", "still\-image" and "moving\-image" +.TP +.B \-\-set\-call\-state +Set the call state STATE:TYPE tuple; valid states are: +"none", "ringing", "active" and "service". +Valid types are: +"normal" and "emergency" +.TP +.B \-\-enable\-radio +Enable the specified radio; valid radios are: +"master", "wireless", "cellular" and "bluetooth". +"master" acts as master switch; individual radios are only +considered if the master switch is enabled +.TP +.B \-\-disable\-radio +Disable the specified radio; valid radios are: +"master", "wireless", "cellular" and "bluetooth". +"master" acts as master switch; individual radios are only +considered if the master switch is enabled +.TP +.B \-\-set\-power\-saving\-mode +Set the automatic power saving mode; valid modes are: +"enabled" and "disabled" +.TP +.B \-\-set\-forced\-psm +Force enable the power saving mode; valid modes are: +"enabled" and "disabled" +.TP +.B \-\-set\-psm\-threshold +Set the battery level threshold for the automatic power saving +mode; valid values are: +10, 20, 30, 40, 50 +.TP +.B \-\-set\-tklock\-mode +Set the touchscreen/keypad lock mode; valid values are: +"locked", "locked\-dim", "silent\-locked", "silent\-locked\-dim", +"unlocked" and "silent\-unlocked" +.TP +.B \-\-enable\-led +Enable the LED framework +.TP +.B \-\-disable\-led +Disable the LED framework +.TP +.B \-\-activate\-led\-pattern +Activate a LED pattern +.TP +.B \-\-deactivate\-led\-pattern +Deactivate a LED pattern +.TP +.B \-\-powerkey\-event +Trigger a powerkey event; valid values are: +"short", "double" and "long" +.TP +.B \-\-status +Output the MCE status even when executing a command +.TP +.B \-\-block +Block after executing commands; useful for commands that use +D\-Bus caller name monitoring +.TP +.B \-S, \-\-session +Use the session bus instead of the system bus for \%D\(hyBus communication +.TP +.B \-\-help +Display help for the command +.TP +.B \-\-version +Display version and author information + +.SH SEE ALSO +.BR mce (8) + +.SH HISTORY +oct 27 2010: Updated for new and removed functionality. +.br +Jun 16 2010: Updated for new functionality. +.br +Jun 15 2010: Updated for new functionality. +.br +Jun 2 2010: Updated for new functionality. +.br +May 11 2010: Updated for new functionality. +.br +May 5 2010: Updated for new functionality. +.br +May 4 2010: Updated for new functionality. +.br +May 3 2010: Updated for modified behaviour. +.br +Apr 28 2010: Updated for new functionality. +.br +Dec 2 2009: Updated for removed functionality. +.br +Mar 16 2009: Updated for new functionality. +.br +Jan 27 2009: Updated for new and removed functionality. +.br +Jan 07 2009: Updated for new and removed functionality. +.br +May 20 2008: Updated for new functionality. +.br +Oct 19 2007: Use escape\(hycodes for hyphens. +.br +Sep 4 2007: Updated for new functionality. +.br +Apr 12 2007: Minor fixes. +.br +Oct 23 2006: Minor fixes. +.br +Oct 11 2005: Updated for new functionality. +.br +Oct 10 2005: Updated for new functionality. +.br +Oct 6 2005: Updated for new functionality. +.br +Oct 5 2005: Updated for new functionality. +.br +Oct 4 2005: Updated for new functionality. +.br +Sep 23 2005: Updated for new functionality. +.br +Sep 21 2005: Fixed typos. +.br +Sep 19 2005: Initial version of this manual page. + +.SH AUTHOR +mcetool and its manual page is written by +David Weinehall <\fIdavid.weinehall@nokia.com\fP> + +.SH REPORTING BUGS +Report bugs to +<\fIdavid.weinehall@nokia.com\fP>. + +.SH COPYRIGHT +Copyright \(co 2005\(hy2010 Nokia Corporation. All rights reserved. diff --git a/man/mcetool.sv.8 b/man/mcetool.sv.8 new file mode 100644 index 00000000..bfc630c3 --- /dev/null +++ b/man/mcetool.sv.8 @@ -0,0 +1,201 @@ +.TH MCETOOL 8 "Okt 27, 2010" "Nokia" + +.SH NAMN +mcetool \- verktyg f\(:or att testa l\(:ageskontrollfunktionalitet + +.SH SYNOPSIS +.B mcetool +[\fIFLAGGA\fP]... + +.SH BESKRIVNING +.B mcetool +erbjuder ett flertal s\(:att att testa +l\(:ageskontrollfunktionalitet samt f\(:or att s\(:atta eller visa +l\(:agesrelaterade inst\(:allningar. + +.SH FLAGGOR +.TP +.B \-\-blank\-prevent +Skicka en beg\(:aran till MCE att sk\(:armsl\(:ackning ska f\(:orhindras +.TP +.B \-\-cancel\-blank\-prevent +Skicka en beg\(:aran till MCE att sk\(:armsl\(:ackning ej +l\(:angre ska f\(:orhindras +.TP +.B \-\-unblank\-screen +Skicka en beg\(:aran till MCE att sk\(:armen ska t\(:andas +.TP +.B \-\-dim\-screen +Skicka en beg\(:aran till MCE att sk\(:armen ska tonas ned +.TP +.B \-\-blank\-screen +Skicka en beg\(:aran till MCE att sk\(:armen ska sl\(:ackas +.TP +.B \-\-set\-display\-brightness +\(:Andra sk\(:armens ljusstyrka; giltiga v\(:arden \(:ar: +1\-5 +.TP +.B \-\-set\-inhibit\-mode +\(:Andra sk\(:armsl\(:ackningsinhiberingsl\(:aget; giltiga +l\(:agen \(:ar: +"disabled", "stay\-on\-with\-charger", "stay\-dim\-with\-charger", +"stay\-on" och "stay\-dim" +.TP +.B \-\-set\-cabc\-mode +\(:Andra CABC\(hyl\(:aget; giltiga l\(:agen \(:ar: +"off", "ui", "still\-image" och "moving\-image" +.TP +.B \-\-set\-call\-state +\(:Andra samtalstillst\(oanddubletten TILLST\(oAND:TYP; giltiga +tillst\(oand \(:ar: +"none", "ringing", "active" och "service". +Giltiga typer \(:ar: +"normal" och "emergency" +.TP +.B \-\-enable\-radio +Sl\(oa p\(oa den angivna radion; till\(oatna radior \(:ar: +"master", "wireless", "cellular" och "bluetooth". +"master" tj\(:anar som en huvudbrytare; individuella radior +beaktas endast om huvudbrytaren \(:ar p\(oa +.TP +.B \-\-disable\-radio +Sl\(oa av den angivna radion; till\(oatna radior \(:ar: +"master", "wireless", "cellular" och "bluetooth". +"master" tj\(:anar som en huvudbrytare; individuella radior +beaktas endast om huvudbrytaren \(:ar av +.TP +.B \-\-set\-power\-saving\-mode +S\(:att automatiskt str\:omsparl\(:age; giltiga l\(:agen \(:ar: +"enabled" och "disabled" +.TP +.B \-\-set\-forced\-psm +Framtvinga automatiskt str\:omsparl\(:age; giltiga l\(:agen \(:ar: +"enabled" och "disabled" +.TP +.B \-\-set\-psm\-threshold +.TP +10, 20, 30, 40, 50 +.B \-\-set\-tklock\-mode +\(:Andra tangentbordsl\(oasl\(:aget till det angivna l\(:aget; +giltiga v\(:arden \(:ar: +"locked", "locked\-dim", "silent\-locked", "silent\-locked\-dim", +"unlocked" och "silent\-unlocked" +.TP +.B \-\-powerup +Skicka en beg\(:aran till MCE att enheten skall starta upp +.TP +.B \-\-reboot +Skicka en beg\(:aran till MCE att enheten skall startas om +.TP +.B \-\-shutdown +Skicka en beg\(:aran till MCE att enheten skall st\(:angas av +.TP +.B \-\-set\-tklock\-mode +\(:Andra knapp\(hy/sk\(:arml\(oasets l\(:age; giltiga v\(:arden \(:ar: +"locked", "locked\-dim", "silent\-locked", "silent\-locked\-dim", +"unlocked" samt "silent\-unlocked" +.TP +.B \-\-enable\-led +Sl\(oa p\(oa LED\(hyramverket +.TP +.B \-\-disable\-led +St\(:ang av LED\(hyramverket +.TP +.B \-\-activate\-led\-pattern +Aktivera ett LED\(hym\(:onster +.TP +.B \-\-deactivate\-led\-pattern +Avaktivera ett LED\(hym\(:onster +.TP +.B \-\-powerkey\-event +Trigga en av/p\(oaknappsh\(:andelse; giltiga v\(:arden \(:ar: +"short", "double" samt "long" +.TP +.B \-\-status +Visa status\(hyinformation fr\(oan MCE \(:aven d\(oa ett +kommando har utf\(:orts +.TP +.B \-\-block +Blockera efter att kommandon utf\(:orts; anv\(:andbart f\(:or kommandon +som nyttjar anroparnamn\(:overvakning f\(:or D\-Bus +.TP +.B \-S, \-\-session +Anv\(:and sessionsbussen ist\(:allet f\(:or systembussen +vid \%D\(hyBus\:\(hykommunikation +.TP +.B \-\-help +Visa en hj\(:alptext f\(:or kommandot +.TP +.B \-\-version +Visa versionsinformation och information om upphovsr\(:att + +.SH SE \(:AVEN +.BR mce (8) + +.SH HISTORIK +Okt 27 2010: Uppdaterad f\(:or ny och borttagen funktionalitet. +.br +Jan 16 2010: Uppdaterad f\(:or ny funktionalitet. +.br +Jan 15 2010: Uppdaterad f\(:or ny och borttagen funktionalitet. +.br +Jun 2 2010: Uppdaterad f\(:or ny funktionalitet. +.br +Maj 12 2010: Uppdaterad f\(:or borttagen funktionalitet. +.br +Maj 11 2010: Uppdaterad f\(:or ny funktionalitet. +.br +Maj 5 2010: Uppdaterad f\(:or ny funktionalitet. +.br +Maj 4 2010: Uppdaterad f\(:or ny funktionalitet. +.br +Maj 3 2010: Uppdaterad f\(:or \(:andrat beteende. +.br +Apr 28 2010: Uppdaterad f\(:or ny funktionalitet. +.br +Mar 18 2010: Anv\(:and escape\(hysekvenser ist\(:allet f\(:or latin\(hy1. +.br +Mar 16 2009: Uppdaterad f\(:or borttagen funktionalitet. +.br +Mar 16 2009: Uppdaterad f\(:or ny funktionalitet. +.br +Jan 27 2009: Uppdaterad f\(:or ny och borttagen funktionalitet. +.br +Jan 7 2009: Uppdaterad f\(:or ny och borttagen funktionalitet. +.br +Maj 20 2008: Uppdaterad f\(:or ny funktionalitet. +.br +Okt 19 2007: Uppdaterad f\(:or ny funktionalitet. +.br +Okt 19 2007: Anv\(:and escape\(hysekvenser ist\(:allet f\(:or bindestreck. +.br +Apr 12 2007: Anv\(:and escape\(hysekvenser ist\(:allet f\(:or latin\(hy1. +.br +Okt 23 2006: Sm\(:arre r\(:attelser. +.br +Okt 11 2005: Uppdaterad f\(:or ny funktionalitet. +.br +Okt 10 2005: Uppdaterad f\(:or ny funktionalitet. +.br +Okt 6 2005: Uppdaterad f\(:or ny funktionalitet. +.br +Okt 5 2005: Uppdaterad f\(:or ny funktionalitet. +.br +Okt 4 2005: Uppdaterad f\(:or ny funktionalitet. +.br +Sep 23 2005: Uppdaterad f\(:or ny funktionalitet. +.br +Sep 21 2005: Sm\(:arre r\(:attelser. +.br +Sep 19 2005: F\(:orsta svenska versionen av manualsidan. + +.SH F\(:ORFATTARE +mcetool och dess manualsida \(:ar skriven av +David Weinehall <\fIdavid.weinehall@nokia.com\fP> + +.SH RAPPORTERA FEL +Rapportera fel till +<\fIdavid.weinehall@nokia.com\fP>. + +.SH UPPHOVSR\(:ATT +Copyright \(co 2005\(hy2010 Nokia Corporation. Alla r\(:attigheter f\(:orbeh\(oallna. diff --git a/man/mcetorture.8 b/man/mcetorture.8 new file mode 100644 index 00000000..ce7fefa4 --- /dev/null +++ b/man/mcetorture.8 @@ -0,0 +1,106 @@ +.TH MCETORTURE 8 "Nov 22, 2010" "Nokia" + +.SH NAME +mcetorture \- torture test for MCE + +.SH SYNOPSIS +.B mcetorture +[\fIOPTION\fP]... [\fITEST\fP]... + +.SH DESCRIPTION +.B mcetorture +is a used to stresstest the mode control daemon, MCE. + +.SH OPTIONS +.TP +.B \-\-no\-leakcheck +Disable leak\(hychecking +.TP +.B \-\-no\-logging +Disable error\-logging +.TP +.B \-\-no\-abort +Do not abort on error +.TP +.B \-S, \-\-session +Use the session bus instead of the system bus for \%D\(hyBus communication +.TP +.B \-\-one\-shot +Run the tests once, then exit +.TP +.B \-\-verbose +Display extra information when running tests +.TP +.B \-\-help +Display help for the command +.TP +.B \-\-version +Display version and author information + +.SH TESTS + +Valid tests are: +.BR blank , +.BR mceinfo , +.BR unblank , +.BR dim , +.BR radiostates , +.BR cabcmode , +.BR callstate , +.BR tklock , +.BR alarm , +.BR battery , +.BR charger , +.BR led , +.BR homeshort , +.BR homelong , +.BR powershort , +.BR powerdouble , +.BR powerlong , +.BR gpio-keyslide , +.BR touchscreen , +.BR powershort-dbus , +.BR powerdouble-dbus , +.BR powerlong-dbus , +.BR gconf\-brightness , +.BR gconf\-timeouts , +.BR gconf\-led , +.BR dbus\-errors + +.SH SEE ALSO +.BR mce (8) , +.BR mcetool (8) + +.SH HISTORY +Nov 22 2010: Updated for new functionality. +.br +Oct 27 2010: Updated for removed functionality. +.br +Jul 2 2010: Updated for removed functionality. +.br +Jun 13 2010: Updated for new functionality. +.br +Jun 3 2010: Updated for removed functionality. +.br +May 13 2010: Updated for new and removed functionality. +.br +Jan 7 2009: Updated for new and removed functionality. +.br +Oct 19 2007: Updated for new functionality. +.br +Oct 19 2007: Use escape\(hycodes for hyphens. +.br +Sep 5 2007: Updated for new functionality. +.br +Apr 12 2007: Initial version of this manual page. + +.SH AUTHOR +mcetorture and its manual page is written by +David Weinehall <\fIdavid.weinehall@nokia.com\fP> + +.SH REPORTING BUGS +Report bugs to +<\fIdavid.weinehall@nokia.com\fP>. + +.SH COPYRIGHT +Copyright \(co 2007\(hy2010 Nokia Corporation. All rights reserved. diff --git a/man/mcetorture.sv.8 b/man/mcetorture.sv.8 new file mode 100644 index 00000000..11c7d35f --- /dev/null +++ b/man/mcetorture.sv.8 @@ -0,0 +1,107 @@ +.TH MCETORTURE 8 "Nov 22, 2010" "Nokia" + +.SH NAME +mcetorture \- stresstest f\(:or MCE + +.SH SYNOPSIS +.B mcetorture +[\fIFLAGGA\fP]... [\fITEST\fP]... + +.SH BESKRIVNING +.B mcetorture +anv\(:ands f\(:or att stresstesta l\(:ageskontrolldemonen MCE. + +.SH FLAGGOR +.TP +.B \-\-no\-leakcheck +Avaktivera kontroll av minnesl\(:ackor +.TP +.B \-\-no\-logging +Disable fel\-loggning +.TP +.B \-\-no\-abort +Avbryt ej vid fel +.TP +.B \-S, \-\-session +Anv\(:and sessionsbussen ist\(:allet f\(:or systembussen +vid \%D\(hyBus\:\(hykommunikation +.TP +.B \-\-one\-shot +Utf\(:or testerna en g\(oang, avsluta sedan +.TP +.B \-\-verbose +Visa extra information n\(:ar testerna utf\(:ors +.TP +.B \-\-help +Visa en hj\(:alptext f\(:or kommandot +.TP +.B \-\-version +Visa versionsinformation och information om upphovsr\(:att + +.SH TESTER + +Giltiga tester \(:ar: +.BR blank , +.BR mceinfo , +.BR unblank , +.BR dim , +.BR radiostates , +.BR cabcmode , +.BR callstate , +.BR tklock , +.BR alarm , +.BR battery , +.BR charger , +.BR led , +.BR homeshort , +.BR homelong , +.BR powershort , +.BR powerdouble , +.BR powerlong , +.BR gpio-keyslide , +.BR touchscreen , +.BR powershort-dbus , +.BR powerdouble-dbus , +.BR powerlong-dbus , +.BR gconf\-brightness , +.BR gconf\-timeouts , +.BR gconf\-led , +.BR dbus\-errors + +.SH SE \(:AVEN +.BR mce (8) , +.BR mcetool (8) + +.SH HISTORIK +Nov 22 2010: Uppdaterad f\(:or ny funktionalitet. +.br +Okt 27 2010: Uppdaterad f\(:or borttagen funktionalitet. +.br +Jul 2 2010: Uppdaterad f\(:or borttagen funktionalitet. +.br +Jun 13 2010: Uppdaterad f\(:or ny funktionalitet. +.br +Jun 3 2010: Uppdaterad f\(:or borttagen funktionalitet. +.br +Maj 13 2010: Uppdaterad f\(:or ny och borttagen funktionalitet. +.br +Jan 7 2009: Uppdaterad f\(:or ny och borttagen funktionalitet. +.br +Okt 19 2007: Uppdaterad f\(:or ny funktionalitet. +.br +Okt 19 2007: Anv\(:and escape\(hysekvenser ist\(:allet f\(:or bindestreck. +.br +Sep 5 2007: Uppdaterad f\(:or ny funktionalitet. +.br +Apr 12 2007: F\(:orsta svenska versionen av manualsidan. + +.SH F\(:ORFATTARE +mcetorture och dess manualsida \(:ar skriven av +David Weinehall <\fIdavid.weinehall@nokia.com\fP> + +.SH RAPPORTERA FEL +Rapportera fel till +<\fIdavid.weinehall@nokia.com\fP>. + +.SH UPPHOVSR\(:ATT +Copyright \(co 2007\(hy2010 Nokia Corporation. Alla r\(:attigheter f\(:orbeh\(oallna. diff --git a/mce-backup b/mce-backup new file mode 100644 index 00000000..d971ad6b --- /dev/null +++ b/mce-backup @@ -0,0 +1,12 @@ +#! /bin/sh + +rm -rf $HOME/.mce + +(mkdir $HOME/.mce) && (cp /var/lib/mce/* $HOME/.mce) +status=$? + +if [ $status -ne 0 ]; then + status=2 +fi + +exit $status diff --git a/mce-conf.c b/mce-conf.c new file mode 100644 index 00000000..b1c91ea8 --- /dev/null +++ b/mce-conf.c @@ -0,0 +1,329 @@ +/** + * @file mce-conf.c + * Configuration option handling for MCE + *

+ * Copyright © 2006-2009 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include + +#include "mce.h" +#include "mce-conf.h" + +#include "mce-log.h" /* mce_log(), LL_* */ + +/** Pointer to the keyfile structure where config values are read from */ +static gpointer keyfile = NULL; + +/** + * Get a boolean configuration value + * + * @param group The configuration group to get the value from + * @param key The configuration key to get the value of + * @param defaultval The default value to use if the key isn't set + * @param keyfileptr A keyfile pointer, or NULL to use the default keyfile + * @return The configuration value on success, the default value on failure + */ +gboolean mce_conf_get_bool(const gchar *group, const gchar *key, + const gboolean defaultval, gpointer keyfileptr) +{ + gboolean tmp = FALSE; + GError *error = NULL; + + if (keyfileptr == NULL) { + if (keyfile == NULL) { + mce_log(LL_ERR, + "Could not get config key %s/%s; " + "no configuration file initialised; " + "defaulting to `%d'", + group, key, defaultval); + tmp = defaultval; + goto EXIT; + } else { + keyfileptr = keyfile; + } + } + + tmp = g_key_file_get_boolean(keyfileptr, group, key, &error); + + if (error != NULL) { + mce_log(LL_WARN, + "Could not get config key %s/%s; %s; " + "defaulting to `%d'", + group, key, error->message, defaultval); + tmp = defaultval; + } + + g_clear_error(&error); + +EXIT: + return tmp; +} + +/** + * Get an integer configuration value + * + * @param group The configuration group to get the value from + * @param key The configuration key to get the value of + * @param defaultval The default value to use if the key isn't set + * @param keyfileptr A keyfile pointer, or NULL to use the default keyfile + * @return The configuration value on success, the default value on failure + */ +gint mce_conf_get_int(const gchar *group, const gchar *key, + const gint defaultval, gpointer keyfileptr) +{ + gint tmp = -1; + GError *error = NULL; + + if (keyfileptr == NULL) { + if (keyfile == NULL) { + mce_log(LL_ERR, + "Could not get config key %s/%s; " + "no configuration file initialised; " + "defaulting to `%d'", + group, key, defaultval); + tmp = defaultval; + goto EXIT; + } else { + keyfileptr = keyfile; + } + } + + tmp = g_key_file_get_integer(keyfileptr, group, key, &error); + + if (error != NULL) { + mce_log(LL_WARN, + "Could not get config key %s/%s; %s; " + "defaulting to `%d'", + group, key, error->message, defaultval); + tmp = defaultval; + } + + g_clear_error(&error); + +EXIT: + return tmp; +} + +/** + * Get an integer list configuration value + * + * @param group The configuration group to get the value from + * @param key The configuration key to get the value of + * @param length The length of the list + * @param keyfileptr A keyfile pointer, or NULL to use the default keyfile + * @return The configuration value on success, NULL on failure + */ +gint *mce_conf_get_int_list(const gchar *group, const gchar *key, + gsize *length, gpointer keyfileptr) +{ + gint *tmp = NULL; + GError *error = NULL; + + if (keyfileptr == NULL) { + if (keyfile == NULL) { + mce_log(LL_ERR, + "Could not get config key %s/%s; " + "no configuration file initialised", + group, key); + *length = 0; + goto EXIT; + } else { + keyfileptr = keyfile; + } + } + + tmp = g_key_file_get_integer_list(keyfileptr, group, key, + length, &error); + + if (error != NULL) { + mce_log(LL_WARN, + "Could not get config key %s/%s; %s", + group, key, error->message); + *length = 0; + } + + g_clear_error(&error); + +EXIT: + return tmp; +} + +/** + * Get a string configuration value + * + * @param group The configuration group to get the value from + * @param key The configuration key to get the value of + * @param defaultval The default value to use if the key isn't set + * @param keyfileptr A keyfile pointer, or NULL to use the default keyfile + * @return The configuration value on success, the default value on failure + */ +gchar *mce_conf_get_string(const gchar *group, const gchar *key, + const gchar *defaultval, gpointer keyfileptr) +{ + gchar *tmp = NULL; + GError *error = NULL; + + if (keyfileptr == NULL) { + if (keyfile == NULL) { + mce_log(LL_ERR, + "Could not get config key %s/%s; " + "no configuration file initialised; " + "defaulting to `%s'", + group, key, defaultval); + + if (defaultval != NULL) + tmp = g_strdup(defaultval); + + goto EXIT; + } else { + keyfileptr = keyfile; + } + } + + tmp = g_key_file_get_string(keyfileptr, group, key, &error); + + if (error != NULL) { + mce_log(LL_WARN, + "Could not get config key %s/%s; %s; %s%s%s", + group, key, error->message, + defaultval ? "defaulting to `" : "no default set", + defaultval ? defaultval : "", + defaultval ? "'" : ""); + + if (defaultval != NULL) + tmp = g_strdup(defaultval); + } + + g_clear_error(&error); + +EXIT: + return tmp; +} + +/** + * Get a string list configuration value + * + * @param group The configuration group to get the value from + * @param key The configuration key to get the value of + * @param length The length of the list + * @param keyfileptr A keyfile pointer, or NULL to use the default keyfile + * @return The configuration value on success, NULL on failure + */ +gchar **mce_conf_get_string_list(const gchar *group, const gchar *key, + gsize *length, gpointer keyfileptr) +{ + gchar **tmp = NULL; + GError *error = NULL; + + if (keyfileptr == NULL) { + if (keyfile == NULL) { + mce_log(LL_ERR, + "Could not get config key %s/%s; " + "no configuration file initialised", + group, key); + *length = 0; + goto EXIT; + } else { + keyfileptr = keyfile; + } + } + + tmp = g_key_file_get_string_list(keyfileptr, group, key, + length, &error); + + if (error != NULL) { + mce_log(LL_WARN, + "Could not get config key %s/%s; %s", + group, key, error->message); + *length = 0; + } + + g_clear_error(&error); + +EXIT: + return tmp; +} + +/** + * Free configuration file + * + * @param keyfileptr A pointer to the keyfile to free + */ +void mce_conf_free_conf_file(gpointer keyfileptr) +{ + if (keyfileptr != NULL) { + g_key_file_free(keyfileptr); + } +} + +/** + * Read configuration file + * + * @param conffile The full path to the configuration file to read + * @return A keyfile pointer on success, NULL on failure + */ +gpointer mce_conf_read_conf_file(const gchar *const conffile) +{ + GError *error = NULL; + GKeyFile *keyfileptr; + + if ((keyfileptr = g_key_file_new()) == NULL) + goto EXIT; + + if (g_key_file_load_from_file(keyfileptr, conffile, + G_KEY_FILE_NONE, &error) == FALSE) { + mce_conf_free_conf_file(keyfileptr); + keyfileptr = NULL; + mce_log(LL_WARN, "Could not load %s; %s", + conffile, error->message); + goto EXIT; + } + +EXIT: + g_clear_error(&error); + + return keyfileptr; +} + +/** + * Init function for the mce-conf component + * + * @return TRUE on success, FALSE on failure + */ +gboolean mce_conf_init(void) +{ + gboolean status = FALSE; + + if ((keyfile = mce_conf_read_conf_file(G_STRINGIFY(MCE_CONF_FILE))) == NULL) { + goto EXIT; + } + + status = TRUE; + +EXIT: + return status; +} + +/** + * Exit function for the mce-conf component + */ +void mce_conf_exit(void) +{ + mce_conf_free_conf_file(keyfile); + + return; +} diff --git a/mce-conf.h b/mce-conf.h new file mode 100644 index 00000000..a3d49480 --- /dev/null +++ b/mce-conf.h @@ -0,0 +1,43 @@ +/** + * @file mce-conf.h + * Headers for the configuration option handling for MCE + *

+ * Copyright © 2006-2007 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _MCE_CONF_H_ +#define _MCE_CONF_H_ + +#include + +gboolean mce_conf_get_bool(const gchar *group, const gchar *key, + const gboolean defaultval, gpointer keyfileptr); +gint mce_conf_get_int(const gchar *group, const gchar *key, + const gint defaultval, gpointer keyfileptr); +gint *mce_conf_get_int_list(const gchar *group, const gchar *key, + gsize *length, gpointer keyfileptr); +gchar *mce_conf_get_string(const gchar *group, const gchar *key, + const gchar *defaultval, gpointer keyfileptr); +gchar **mce_conf_get_string_list(const gchar *group, const gchar *key, + gsize *length, gpointer keyfileptr); + +gpointer mce_conf_read_conf_file(const gchar *const conffile); +void mce_conf_free_conf_file(gpointer keyfileptr); + +gboolean mce_conf_init(void); +void mce_conf_exit(void); + +#endif /* _MCE_CONF_H_ */ diff --git a/mce-dbus.c b/mce-dbus.c new file mode 100644 index 00000000..74a016a5 --- /dev/null +++ b/mce-dbus.c @@ -0,0 +1,975 @@ +/** + * @file mce-dbus.c + * D-Bus handling code for the Mode Control Entity + *

+ * Copyright © 2004-2009 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * @author Ismo Laitinen + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include + +#include /* va_start(), va_end() */ +#include /* exit(), EXIT_FAILURE */ +#include /* strcmp() */ +#include +#include +#include /* dbus_connection_setup_with_g_main */ + +#include "mce.h" +#include "mce-dbus.h" + +#include "mce-log.h" /* mce_log(), LL_* */ + +/** List of all D-Bus handlers */ +static GSList *dbus_handlers = NULL; + +/** D-Bus handler structure */ +typedef struct { + gboolean (*callback)(DBusMessage *const msg); /**< Handler callback */ + gchar *interface; /**< The interface to listen on */ + gchar *rules; /**< Additional matching rules */ + gchar *name; /**< Method call or signal name */ + guint type; /**< DBUS_MESSAGE_TYPE */ +} handler_struct; + +/** Pointer to the DBusConnection */ +static DBusConnection *dbus_connection = NULL; + +/** + * Create a new D-Bus signal, with proper error checking + * will exit the mainloop if an error occurs + * + * @param path The signal path + * @param interface The signal interface + * @param name The name of the signal to send + * @return A new DBusMessage + */ +DBusMessage *dbus_new_signal(const gchar *const path, + const gchar *const interface, + const gchar *const name) +{ + DBusMessage *msg; + + if ((msg = dbus_message_new_signal(path, interface, name)) == NULL) { + mce_log(LL_CRIT, "No memory for new signal!"); + g_main_loop_quit(mainloop); + exit(EXIT_FAILURE); + } + + return msg; +} + +#if 0 +/** + * Create a new D-Bus error message, with proper error checking + * will exit the mainloop if an error occurs + * + * @param message The DBusMessage that caused the error message to be sent + * @param error The message to send + * @return A new DBusMessage + */ +static DBusMessage *dbus_new_error(DBusMessage *const message, + const gchar *const error) +{ + DBusMessage *error_msg; + + if ((error_msg = dbus_message_new_error(message, error, + NULL)) == NULL) { + mce_log(LL_CRIT, "No memory for new D-Bus error message!"); + g_main_loop_quit(mainloop); + exit(EXIT_FAILURE); + } + + return error_msg; +} +#endif + +/** + * Create a new D-Bus method call, with proper error checking + * will exit the mainloop if an error occurs + * + * @param service The method call service + * @param path The method call path + * @param interface The method call interface + * @param name The name of the method to call + * @return A new DBusMessage + */ +DBusMessage *dbus_new_method_call(const gchar *const service, + const gchar *const path, + const gchar *const interface, + const gchar *const name) +{ + DBusMessage *msg; + + if ((msg = dbus_message_new_method_call(service, path, + interface, name)) == NULL) { + mce_log(LL_CRIT, + "Cannot allocate memory for D-Bus method call!"); + g_main_loop_quit(mainloop); + exit(EXIT_FAILURE); + } + + return msg; +} + +/** + * Create a new D-Bus method call reply, with proper error checking + * will exit the mainloop if an error occurs + * + * @param message The DBusMessage to reply to + * @return A new DBusMessage + */ +DBusMessage *dbus_new_method_reply(DBusMessage *const message) +{ + DBusMessage *msg; + + if ((msg = dbus_message_new_method_return(message)) == NULL) { + mce_log(LL_CRIT, "No memory for new reply!"); + g_main_loop_quit(mainloop); + exit(EXIT_FAILURE); + } + + return msg; +} + +/** + * Send a D-Bus message + * Side-effects: frees msg + * + * @param msg The D-Bus message to send + * @return TRUE on success, FALSE on out of memory + */ +gboolean dbus_send_message(DBusMessage *const msg) +{ + gboolean status = FALSE; + + if (dbus_connection_send(dbus_connection, msg, NULL) == FALSE) { + mce_log(LL_CRIT, + "Out of memory when sending D-Bus message"); + goto EXIT; + } + + dbus_connection_flush(dbus_connection); + status = TRUE; + +EXIT: + dbus_message_unref(msg); + + return status; +} + +/** + * Send a D-Bus message and setup a reply callback + * Side-effects: frees msg + * + * @param msg The D-Bus message to send + * @param callback The reply callback + * @return TRUE on success, FALSE on failure + */ +gboolean dbus_send_message_with_reply_handler(DBusMessage *const msg, + DBusPendingCallNotifyFunction callback) +{ + DBusPendingCall *pending_call; + gboolean status = FALSE; + + if (dbus_connection_send_with_reply(dbus_connection, msg, + &pending_call, -1) == FALSE) { + mce_log(LL_CRIT, + "Out of memory when sending D-Bus message"); + goto EXIT; + } else if (pending_call == NULL) { + mce_log(LL_ERR, + "D-Bus connection disconnected"); + goto EXIT; + } + + dbus_connection_flush(dbus_connection); + + if (dbus_pending_call_set_notify(pending_call, callback, NULL, NULL) == FALSE) { + mce_log(LL_CRIT, + "Out of memory when sending D-Bus message"); + goto EXIT; + } + + status = TRUE; + +EXIT: + dbus_message_unref(msg); + + return status; +} + +/** + * Generic function to send D-Bus messages and signals + * to send a signal, call dbus_send with service == NULL + * + * @todo Make it possible to send D-Bus replies as well + * + * @param service D-Bus service; for signals, set to NULL + * @param path D-Bus path + * @param interface D-Bus interface + * @param name The D-Bus method or signal name to send to + * @param callback A reply callback, or NULL to set no reply; + * for signals, this is unused, but please use NULL + * for consistency + * @param first_arg_type The DBUS_TYPE of the first argument in the list + * @param ... The arguments to append to the D-Bus message; + * terminate with DBUS_TYPE_INVALID + * Note: the arguments MUST be passed by reference + * @return TRUE on success, FALSE on failure + */ +gboolean dbus_send(const gchar *const service, const gchar *const path, + const gchar *const interface, const gchar *const name, + DBusPendingCallNotifyFunction callback, + int first_arg_type, ...) +{ + DBusMessage *msg; + gboolean status = FALSE; + va_list var_args; + + if (service != NULL) { + msg = dbus_new_method_call(service, path, interface, name); + + if (callback == NULL) + dbus_message_set_no_reply(msg, TRUE); + } else { + if (callback != NULL) { + mce_log(LL_ERR, + "Programmer snafu! " + "dbus_send() called with a DBusPending " + "callback for a signal. Whoopsie!"); + callback = NULL; + } + + msg = dbus_new_signal(path, interface, name); + } + + /* Append the arguments, if any */ + va_start(var_args, first_arg_type); + + if (first_arg_type != DBUS_TYPE_INVALID) { + if (dbus_message_append_args_valist(msg, + first_arg_type, + var_args) == FALSE) { + mce_log(LL_CRIT, + "Failed to append arguments to D-Bus message " + "for %s.%s", + interface, name); + dbus_message_unref(msg); + goto EXIT; + } + } + + /* Send the signal / call the method */ + if (callback == NULL) { + status = dbus_send_message(msg); + } else { + status = dbus_send_message_with_reply_handler(msg, callback); + } + +EXIT: + va_end(var_args); + + return status; +} + +/** + * Generic function to send D-Bus messages, blocking version + * + * @param service D-Bus service + * @param path D-Bus path + * @param interface D-Bus interface + * @param name The D-Bus method to send to + * @param timeout The reply timeout in milliseconds to use + * @param first_arg_type The DBUS_TYPE of the first argument in the list + * @param ... The arguments to append to the D-Bus message; + * terminate with DBUS_TYPE_INVALID + * Note: the arguments MUST be passed by reference + * @return A new DBusMessage with the reply on success, NULL on failure + */ +DBusMessage *dbus_send_with_block(const gchar *const service, + const gchar *const path, + const gchar *const interface, + const gchar *const name, + gint timeout, int first_arg_type, ...) +{ + DBusMessage *reply = NULL; + DBusMessage *msg = NULL; + va_list var_args; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + msg = dbus_new_method_call(service, path, interface, name); + + /* Append the arguments, if any */ + va_start(var_args, first_arg_type); + + if (first_arg_type != DBUS_TYPE_INVALID) { + if (dbus_message_append_args_valist(msg, + first_arg_type, + var_args) == FALSE) { + mce_log(LL_CRIT, + "Failed to append arguments to D-Bus message " + "for %s.%s", + interface, name); + dbus_message_unref(msg); + goto EXIT; + } + } + + /* Call the method */ + reply = dbus_connection_send_with_reply_and_block(dbus_connection, msg, timeout, &error); + + dbus_message_unref(msg); + + if (dbus_error_is_set(&error) == TRUE) { + mce_log(LL_ERR, + "Error sending with reply to %s.%s: %s", + interface, name, error.message); + dbus_error_free(&error); + reply = NULL; + } + +EXIT: + va_end(var_args); + + return reply; +} + +/** + * Translate a D-Bus bus name into a pid + * + * @param bus_name A string with the bus name + * @return The pid of the process, or -1 if no process could be identified + */ +pid_t dbus_get_pid_from_bus_name(const gchar *const bus_name) +{ + dbus_uint32_t pid = -1; + DBusMessage *reply; + + if ((reply = dbus_send_with_block("org.freedesktop.DBus", + "/org/freedesktop/DBus/Bus", + "org.freedesktop.DBus", + "GetConnectionUnixProcessID", -1, + DBUS_TYPE_STRING, &bus_name, + DBUS_TYPE_INVALID)) != NULL) { + dbus_message_get_args(reply, NULL, + DBUS_TYPE_UINT32, &pid, + DBUS_TYPE_INVALID); + dbus_message_unref(reply); + } + + return (pid_t)pid; +} + + +/** + * D-Bus callback for the version get method call + * + * @param msg The D-Bus message to reply to + * @return TRUE on success, FALSE on failure + */ +static gboolean version_get_dbus_cb(DBusMessage *const msg) +{ + static const gchar *const versionstring = G_STRINGIFY(PRG_VERSION); + DBusMessage *reply = NULL; + gboolean status = FALSE; + + mce_log(LL_DEBUG, "Received version information request"); + + /* Create a reply */ + reply = dbus_new_method_reply(msg); + + /* Append the version information */ + if (dbus_message_append_args(reply, + DBUS_TYPE_STRING, &versionstring, + DBUS_TYPE_INVALID) == FALSE) { + mce_log(LL_CRIT, + "Failed to append reply argument to D-Bus message " + "for %s.%s", + MCE_REQUEST_IF, MCE_VERSION_GET); + dbus_message_unref(reply); + goto EXIT; + } + + /* Send the message */ + status = dbus_send_message(reply); + +EXIT: + return status; +} + +/** + * D-Bus message handler + * + * @param connection Unused + * @param msg The D-Bus message received + * @param user_data Unused + * @return DBUS_HANDLER_RESULT_HANDLED for handled messages + * DBUS_HANDLER_RESULT_NOT_HANDLED for unhandled messages + */ +static DBusHandlerResult msg_handler(DBusConnection *const connection, + DBusMessage *const msg, + gpointer const user_data) +{ + guint status = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + GSList *list; + + (void)connection; + (void)user_data; + + for (list = dbus_handlers; list != NULL; list = g_slist_next(list)) { + handler_struct *handler = list->data; + + switch (handler->type) { + case DBUS_MESSAGE_TYPE_METHOD_CALL: + if (dbus_message_is_method_call(msg, + handler->interface, + handler->name) == TRUE) { + handler->callback(msg); + status = DBUS_HANDLER_RESULT_HANDLED; + goto EXIT; + } + + break; + + case DBUS_MESSAGE_TYPE_ERROR: + if (dbus_message_is_error(msg, + handler->name) == TRUE) { + handler->callback(msg); + status = DBUS_HANDLER_RESULT_HANDLED; + goto EXIT; + } + + break; + + case DBUS_MESSAGE_TYPE_SIGNAL: + if (dbus_message_is_signal(msg, + handler->interface, + handler->name) == TRUE) { + handler->callback(msg); + status = DBUS_HANDLER_RESULT_HANDLED; + } + + break; + + default: + mce_log(LL_ERR, + "There's a bug somewhere in MCE; something " + "has registered an invalid D-Bus handler"); + break; + } + } + +EXIT: + return status; +} + +/** + * Register a D-Bus signal or method handler + * + * @param interface The interface to listen on + * @param name The signal/method call to listen for + * @param rules Additional matching rules + * @param type DBUS_MESSAGE_TYPE + * @param callback The callback function + * @return A D-Bus handler cookie on success, NULL on failure + */ +gconstpointer mce_dbus_handler_add(const gchar *const interface, + const gchar *const name, + const gchar *const rules, + const guint type, + gboolean (*callback)(DBusMessage *const msg)) +{ + handler_struct *h = NULL; + gchar *match = NULL; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + if (type == DBUS_MESSAGE_TYPE_SIGNAL) { + if ((match = g_strdup_printf("type='signal'" + "%s%s%s" + ", member='%s'" + "%s%s", + interface ? ", interface='" : "", + interface ? interface : "", + interface ? "'" : "", + name, + rules ? ", " : "", + rules ? rules : "")) == NULL) { + mce_log(LL_CRIT, + "Failed to allocate memory for match"); + goto EXIT; + } + } else if (type != DBUS_MESSAGE_TYPE_METHOD_CALL) { + mce_log(LL_CRIT, + "There's definitely a programming error somewhere; " + "MCE is trying to register an invalid message type"); + goto EXIT; + } + + if ((h = g_try_malloc(sizeof (*h))) == NULL) { + mce_log(LL_CRIT, "Failed to allocate memory for h"); + goto EXIT; + } + + h->interface = NULL; + + if (interface && (h->interface = g_strdup(interface)) == NULL) { + mce_log(LL_CRIT, "Failed to allocate memory for h->interface"); + g_free(h); + h = NULL; + goto EXIT; + } + + h->rules = NULL; + + if (rules && (h->rules = g_strdup(rules)) == NULL) { + mce_log(LL_CRIT, "Failed to allocate memory for h->rules"); + g_free(h->interface); + g_free(h); + h = NULL; + goto EXIT; + } + + if ((h->name = g_strdup(name)) == NULL) { + mce_log(LL_CRIT, "Failed to allocate memory for h->name"); + g_free(h->interface); + g_free(h->rules); + g_free(h); + h = NULL; + goto EXIT; + } + + h->type = type; + h->callback = callback; + + /* Only register D-Bus matches for signals */ + if (match != NULL) { + dbus_bus_add_match(dbus_connection, match, &error); + + if (dbus_error_is_set(&error) == TRUE) { + mce_log(LL_CRIT, + "Failed to add D-Bus match '%s' for '%s'; %s", + match, h->interface, error.message); + dbus_error_free(&error); + g_free(h->interface); + g_free(h->rules); + g_free(h); + h = NULL; + goto EXIT; + } + } + + dbus_handlers = g_slist_prepend(dbus_handlers, h); + +EXIT: + g_free(match); + + return h; +} + +/** + * Unregister a D-Bus signal or method handler + * + * @param cookie A D-Bus handler cookie for + * the handler that should be removed + */ +void mce_dbus_handler_remove(gconstpointer cookie) +{ + handler_struct *h = (handler_struct *)cookie; + gchar *match = NULL; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + if (h->type == DBUS_MESSAGE_TYPE_SIGNAL) { + match = g_strdup_printf("type='signal'" + "%s%s%s" + ", member='%s'" + "%s%s", + h->interface ? ", interface='" : "", + h->interface ? h->interface : "", + h->interface ? "'" : "", + h->name, + h->rules ? ", " : "", + h->rules ? h->rules : ""); + + if (match != NULL) { + dbus_bus_remove_match(dbus_connection, match, &error); + + if (dbus_error_is_set(&error) == TRUE) { + mce_log(LL_CRIT, + "Failed to remove D-Bus match " + "'%s' for '%s': %s", + match, h->interface, error.message); + dbus_error_free(&error); + } + } else { + mce_log(LL_CRIT, + "Failed to allocate memory for match"); + } + } else if (h->type != DBUS_MESSAGE_TYPE_METHOD_CALL) { + mce_log(LL_ERR, + "There's definitely a programming error somewhere; " + "MCE is trying to unregister an invalid message type"); + /* Don't abort here, since we want to unregister it anyway */ + } + + dbus_handlers = g_slist_remove(dbus_handlers, h); + + g_free(h->interface); + g_free(h->rules); + g_free(h->name); + g_free(h); + g_free(match); +} + +/** + * Unregister a D-Bus signal or method handler; + * to be used with g_slist_foreach() + * + * @param handler A pointer to the handler struct that should be removed + * @param user_data Unused + */ +static void mce_dbus_handler_remove_foreach(gpointer handler, + gpointer user_data) +{ + (void)user_data; + + mce_dbus_handler_remove(handler); +} + +/** + * Custom compare function used to find owner monitor entries + * + * @param owner_id An owner monitor cookie + * @param name The name to search for + * @return Less than, equal to, or greater than zero depending + * whether the name of the rules with the id owner_id + * is less than, equal to, or greater than name + */ +static gint monitor_compare(gconstpointer owner_id, gconstpointer name) +{ + handler_struct *hs = (handler_struct *)owner_id; + + return strcmp(hs->rules, name); +} + +/** + * Locate the specified D-Bus service in the monitor list + * + * @param service The service to check for + * @param monitor_list The monitor list check + * @return A pointer to the entry if the entry is in the list, + * NULL if the entry is not in the list + */ +static GSList *find_monitored_service(const gchar *service, + GSList *monitor_list) +{ + gchar *rule = NULL; + GSList *tmp = NULL; + + if (service == NULL) + goto EXIT; + + if ((rule = g_strdup_printf("arg1='%s'", service)) == NULL) + goto EXIT; + + tmp = g_slist_find_custom(monitor_list, rule, monitor_compare); + + g_free(rule); + +EXIT: + return tmp; +} + +/** + * Check whether the D-Bus service in question is in the monitor list or not + * + * @param service The service to check for + * @param monitor_list The monitor list check + * @return TRUE if the entry is in the list, + * FALSE if the entry is not in the list + */ +gboolean mce_dbus_is_owner_monitored(const gchar *service, + GSList *monitor_list) +{ + return (find_monitored_service(service, monitor_list) != NULL); +} + +/** + * Add a service to a D-Bus owner monitor list + * + * @param service The service to monitor + * @param callback A D-Bus monitor callback + * @param monitor_list The list of monitored services + * @param max_num The maximum number of monitored services; + * keep this number low, for performance + * and memory usage reasons + * @return -1 if the amount of monitored services would be exceeded; + * if either of service or monitor_list is NULL, + * or if adding a D-Bus monitor fails + * 0 if the service is already monitored + * >0 represents the number of monitored services after adding + * this service + */ +gssize mce_dbus_owner_monitor_add(const gchar *service, + gboolean (*callback)(DBusMessage *const msg), + GSList **monitor_list, + gssize max_num) +{ + gconstpointer cookie; + gchar *rule = NULL; + gssize retval = -1; + gssize num; + + /* If service or monitor_list is NULL, fail */ + if (service == NULL) { + mce_log(LL_CRIT, + "A programming error occured; " + "mce_dbus_owner_monitor_add() called with " + "service == NULL"); + goto EXIT; + } else if (monitor_list == NULL) { + mce_log(LL_CRIT, + "A programming error occured; " + "mce_dbus_owner_monitor_add() called with " + "monitor_list == NULL"); + goto EXIT; + } + + /* If the service is already in the list, we're done */ + if (find_monitored_service(service, *monitor_list) != NULL) { + retval = 0; + goto EXIT; + } + + /* If the service isn't in the list, and the list already + * contains max_num elements, bail out + */ + if ((num = g_slist_length(*monitor_list)) == max_num) + goto EXIT; + + if ((rule = g_strdup_printf("arg1='%s'", service)) == NULL) + goto EXIT; + + /* Add ownership monitoring for the service */ + cookie = mce_dbus_handler_add("org.freedesktop.DBus", + "NameOwnerChanged", + rule, + DBUS_MESSAGE_TYPE_SIGNAL, + callback); + + if (cookie == NULL) + goto EXIT; + + *monitor_list = g_slist_prepend(*monitor_list, (gpointer)cookie); + retval = num + 1; + +EXIT: + g_free(rule); + + return retval; +} + +/** + * Remove a service from a D-Bus owner monitor list + * + * @param service The service to remove from the monitor list + * @param monitor_list The monitor list to remove the service from + * @return The new number of monitored connections; + * -1 if the service was not monitored, + * if removing monitoring failed, + * or if either of service or monitor_list is NULL + */ +gssize mce_dbus_owner_monitor_remove(const gchar *service, + GSList **monitor_list) +{ + gssize retval = -1; + GSList *tmp; + + /* If service or monitor_list is NULL, fail */ + if ((service == NULL) || (monitor_list == NULL)) + goto EXIT; + + /* If the service is not in the list, fail */ + if ((tmp = find_monitored_service(service, *monitor_list)) == NULL) + goto EXIT; + + /* Remove ownership monitoring for the service */ + mce_dbus_handler_remove(tmp->data); + *monitor_list = g_slist_remove(*monitor_list, tmp->data); + retval = g_slist_length(*monitor_list); + +EXIT: + return retval; +} + +/** + * Remove all monitored service from a D-Bus owner monitor list + * + * @param monitor_list The monitor list to remove the service from + */ +void mce_dbus_owner_monitor_remove_all(GSList **monitor_list) +{ + if ((monitor_list != NULL) && (*monitor_list != NULL)) { + g_slist_foreach(*monitor_list, + (GFunc)mce_dbus_handler_remove_foreach, NULL); + g_slist_free(*monitor_list); + *monitor_list = NULL; + } +} + +/** + * Acquire D-Bus services + * + * @return TRUE on success, FALSE on failure + */ +static gboolean dbus_acquire_services(void) +{ + gboolean status = FALSE; + int ret; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + ret = dbus_bus_request_name(dbus_connection, MCE_SERVICE, 0, &error); + + if (ret == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + mce_log(LL_DEBUG, "Service %s acquired", MCE_SERVICE); + } else { + mce_log(LL_CRIT, "Cannot acquire service: %s", error.message); + dbus_error_free(&error); + goto EXIT; + } + + status = TRUE; + +EXIT: + return status; +} + +/** + * Initialise the message handler used by MCE + * + * @return TRUE on success, FALSE on failure + */ +static gboolean dbus_init_message_handler(void) +{ + gboolean status = FALSE; + + if (dbus_connection_add_filter(dbus_connection, msg_handler, + NULL, NULL) == FALSE) { + mce_log(LL_CRIT, "Failed to add D-Bus filter"); + goto EXIT; + } + + status = TRUE; + +EXIT: + return status; +} + +/** + * Init function for the mce-dbus component + * Pre-requisites: glib mainloop registered + * + * @param systembus TRUE to use system bus, FALSE to use session bus + * @return TRUE on success, FALSE on failure + */ +gboolean mce_dbus_init(const gboolean systembus) +{ + DBusBusType bus_type = DBUS_BUS_SYSTEM; + gboolean status = FALSE; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + if (systembus == FALSE) + bus_type = DBUS_BUS_SESSION; + + mce_log(LL_DEBUG, "Establishing D-Bus connection"); + + /* Establish D-Bus connection */ + if ((dbus_connection = dbus_bus_get(bus_type, + &error)) == NULL) { + mce_log(LL_CRIT, "Failed to open connection to message bus; %s", + error.message); + dbus_error_free(&error); + goto EXIT; + } + + mce_log(LL_DEBUG, "Connecting D-Bus to the mainloop"); + + /* Connect D-Bus to the mainloop */ + dbus_connection_setup_with_g_main(dbus_connection, NULL); + + mce_log(LL_DEBUG, "Acquiring D-Bus service"); + + /* Acquire D-Bus service */ + if (dbus_acquire_services() == FALSE) + goto EXIT; + + /* Initialise message handlers */ + if (dbus_init_message_handler() == FALSE) + goto EXIT; + + /* Register callbacks that are handled inside mce-dbus.c */ + + /* get_version */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_VERSION_GET, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + version_get_dbus_cb) == NULL) + goto EXIT; + + status = TRUE; + +EXIT: + return status; +} + +/** + * Exit function for the mce-dbus component + */ +void mce_dbus_exit(void) +{ + /* Unregister D-Bus handlers */ + if (dbus_handlers != NULL) { + g_slist_foreach(dbus_handlers, + (GFunc)mce_dbus_handler_remove_foreach, NULL); + g_slist_free(dbus_handlers); + dbus_handlers = NULL; + } + + /* If there is an established D-Bus connection, unreference it */ + if (dbus_connection != NULL) { + mce_log(LL_DEBUG, "Unreferencing D-Bus connection"); + dbus_connection_unref(dbus_connection); + dbus_connection = NULL; + } + + return; +} diff --git a/mce-dbus.h b/mce-dbus.h new file mode 100644 index 00000000..c58f926f --- /dev/null +++ b/mce-dbus.h @@ -0,0 +1,72 @@ +/** + * @file mce-dbus.h + * Headers for the D-Bus handling code for the Mode Control Entity + *

+ * Copyright © 2004-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _MCE_DBUS_H_ +#define _MCE_DBUS_H_ + +#include +#include + +#include + +DBusMessage *dbus_new_signal(const gchar *const path, + const gchar *const interface, + const gchar *const name); +DBusMessage *dbus_new_method_call(const gchar *const service, + const gchar *const path, + const gchar *const interface, + const gchar *const name); +DBusMessage *dbus_new_method_reply(DBusMessage *const message); + +gboolean dbus_send_message(DBusMessage *const msg); +gboolean dbus_send_message_with_reply_handler(DBusMessage *const msg, + DBusPendingCallNotifyFunction callback); + +gboolean dbus_send(const gchar *const service, const gchar *const path, + const gchar *const interface, const gchar *const name, + DBusPendingCallNotifyFunction callback, + int first_arg_type, ...); +DBusMessage *dbus_send_with_block(const gchar *const service, + const gchar *const path, + const gchar *const interface, + const gchar *const name, + gint timeout, int first_arg_type, ...); +pid_t dbus_get_pid_from_bus_name(const gchar *const bus_name); + +gconstpointer mce_dbus_handler_add(const gchar *const interface, + const gchar *const name, + const gchar *const rules, + const guint type, + gboolean (*callback)(DBusMessage *const msg)); +void mce_dbus_handler_remove(gconstpointer cookie); +gboolean mce_dbus_is_owner_monitored(const gchar *service, + GSList *monitor_list); +gssize mce_dbus_owner_monitor_add(const gchar *service, + gboolean (*callback)(DBusMessage *const msg), + GSList **monitor_list, + gssize max_num); +gssize mce_dbus_owner_monitor_remove(const gchar *service, + GSList **monitor_list); +void mce_dbus_owner_monitor_remove_all(GSList **monitor_list); + +gboolean mce_dbus_init(const gboolean systembus); +void mce_dbus_exit(void); + +#endif /* _MCE_DBUS_H_ */ diff --git a/mce-dsme.c b/mce-dsme.c new file mode 100644 index 00000000..fe449567 --- /dev/null +++ b/mce-dsme.c @@ -0,0 +1,810 @@ +/** + * @file mce-dsme.c + * Interface code and logic between + * DSME (the Device State Management Entity) + * and MCE (the Mode Control Entity) + *

+ * Copyright © 2004-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * @author Ismo Laitinen + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include + +#include /* time(), time_t */ +#include /* errno */ +#include /* exit(), free(), EXIT_FAILURE */ +#include /* getpid() */ + +#include /* dsme_state_t */ +#include /* DSM_MSGTYPE_* */ +#include /* dsmesock_send(), + * dsmesock_receive(), + * dsmesock_connect(), + * dsmesock_close(), + * dsmesock_connection_t + */ +#include /* DSM_MSGTYPE_PROCESSWD_PING, + * DSM_MSGTYPE_PROCESSWD_PONG, + * DSM_MSGTYPE_PROCESSWD_CREATE, + * DSM_MSGTYPE_PROCESSWD_DELETE + */ +#include +#include "mce.h" /* mce_add_submode_int32(), + * mce_rem_submode_int32(), + * mce_get_submode_int32(), + * MCE_INVALID_TRANSLATION, + * MCE_LED_PATTERN_DEVICE_ON, + * MCE_LED_PATTERN_DEVICE_SOFT_OFF, + * submode_t, + * system_state_t, + * MCE_SOFTOFF_SUBMODE, + * MCE_TRANSITION_SUBMODE, + * mainloop, + * charger_state_pipe, + * display_state_pipe, + * system_state_pipe, + * led_pattern_activate_pipe, + * led_pattern_deactivate_pipe + */ +#include "mce-dsme.h" + +#include "mce-lib.h" /* mce_translate_string_to_int_with_default(), + * mce_translation_t + */ +#include "mce-log.h" /* mce_log(), LL_* */ +#include "mce-dbus.h" /* mce_dbus_handler_add(), + * DBUS_MESSAGE_TYPE_SIGNAL + */ +#include "mce-conf.h" /* mce_conf_get_string() */ +#include "datapipe.h" /* execute_datapipe(), + * execute_datapippe_output_triggers(), + * datapipe_get_gint(), + * append_output_trigger_to_datapipe(), + * remove_output_trigger_from_datapipe() + */ +#include "connectivity.h" /* get_connectivity_status() */ + +/** Charger state */ +static gboolean charger_connected = FALSE; + +/** Pointer to the dsmesock connection */ +static dsmesock_connection_t *dsme_conn = NULL; +/** TRUE if dsme is disabled (for debugging), FALSE if dsme is enabled */ +static gboolean dsme_disabled = FALSE; + +/** ID for state transition timer source */ +static guint transition_timeout_cb_id = 0; + +/** Soft poweroff connectivity policy when connected to charger */ +static gint softoff_connectivity_policy_charger = + DEFAULT_SOFTOFF_CONNECTIVITY_CHARGER; + +/** Soft poweroff connectivity policy when running on battery */ +static gint softoff_connectivity_policy_battery = + DEFAULT_SOFTOFF_CONNECTIVITY_BATTERY; + +/** Soft poweroff connectivity policy on poweron */ +static gint softoff_connectivity_policy_poweron = + DEFAULT_SOFTOFF_CONNECTIVITY_POWERON; + +/** Soft poweroff charger connect policy */ +static gint softoff_charger_connect_policy = + DEFAULT_SOFTOFF_CHARGER_CONNECT; + +/** Previous master radio state */ +static gint previous_radio_state = -1; + +/** Mapping of soft poweroff connectivity integer <-> policy string */ +static const mce_translation_t soft_poweroff_connectivity_translation[] = { + { + .number = SOFTOFF_CONNECTIVITY_RETAIN, + .string = SOFTOFF_CONNECTIVITY_RETAIN_STR + }, { + .number = SOFTOFF_CONNECTIVITY_SOFT_OFFLINE, + .string = SOFTOFF_CONNECTIVITY_SOFT_OFFLINE_STR + }, { + .number = SOFTOFF_CONNECTIVITY_FORCE_OFFLINE, + .string = SOFTOFF_CONNECTIVITY_FORCE_OFFLINE_STR + }, { /* MCE_INVALID_TRANSLATION marks the end of this array */ + .number = MCE_INVALID_TRANSLATION, + .string = NULL + } +}; + +/** Mapping of soft poweron connectivity integer <-> policy string */ +static const mce_translation_t soft_poweron_connectivity_translation[] = { + { + .number = SOFTOFF_CONNECTIVITY_RETAIN, + .string = SOFTOFF_CONNECTIVITY_RETAIN_STR + }, { + .number = SOFTOFF_CONNECTIVITY_SOFT_OFFLINE, + .string = SOFTOFF_CONNECTIVITY_SOFT_OFFLINE_STR + }, { + .number = SOFTOFF_CONNECTIVITY_FORCE_OFFLINE, + .string = SOFTOFF_CONNECTIVITY_FORCE_OFFLINE_STR + }, { /* MCE_INVALID_TRANSLATION marks the end of this array */ + .number = MCE_INVALID_TRANSLATION, + .string = NULL + } +}; + +/** Mapping of soft poweroff charger connect integer <-> policy string */ +static const mce_translation_t soft_poweroff_charger_connect_translation[] = { + { + .number = SOFTOFF_CHARGER_CONNECT_WAKEUP, + .string = SOFTOFF_CHARGER_CONNECT_WAKEUP_STR + }, { + .number = SOFTOFF_CHARGER_CONNECT_IGNORE, + .string = SOFTOFF_CHARGER_CONNECT_IGNORE_STR + }, { /* MCE_INVALID_TRANSLATION marks the end of this array */ + .number = MCE_INVALID_TRANSLATION, + .string = NULL + } +}; + +/** dsme I/O channel */ +static GIOChannel *dsme_iochan = NULL; +/** dsme data channel GSource ID */ +static guint dsme_data_source_id; +/** dsme error channel GSource ID */ +static guint dsme_error_source_id; + +static gboolean init_dsmesock(void); + +/** + * Generic send function for dsmesock messages + * XXX: How should we handle sending failures? + * + * @param msg A pointer to the message to send + */ +static void mce_dsme_send(gpointer msg) +{ + if (dsme_disabled == TRUE) + goto EXIT; + + if (dsme_conn == NULL) { + mce_log(LL_CRIT, + "Attempt to use dsme_conn uninitialised; aborting!"); + g_main_loop_quit(mainloop); + exit(EXIT_FAILURE); + } + + if ((dsmesock_send(dsme_conn, msg)) == -1) { + mce_log(LL_CRIT, + "dsmesock_send error: %s", + g_strerror(errno)); +#ifdef MCE_DSME_ERROR_POLICY + g_main_loop_quit(mainloop); + exit(EXIT_FAILURE); +#endif /* MCE_DSME_ERROR_POLICY */ + } + +EXIT: + return; +} + +/** + * Send pong message to the DSME process watchdog + */ +static void dsme_send_pong(void) +{ + /* Set up the message */ + DSM_MSGTYPE_PROCESSWD_PONG msg = + DSME_MSG_INIT(DSM_MSGTYPE_PROCESSWD_PONG); + msg.pid = getpid(); + + /* Send the message */ + mce_dsme_send(&msg); + mce_log(LL_DEBUG, + "DSM_MSGTYPE_PROCESSWD_PONG sent to DSME"); +} + +/** + * Register to DSME process watchdog + */ +static void dsme_init_processwd(void) +{ + /* Set up the message */ + DSM_MSGTYPE_PROCESSWD_CREATE msg = + DSME_MSG_INIT(DSM_MSGTYPE_PROCESSWD_CREATE); + msg.pid = getpid(); + + /* Send the message */ + mce_dsme_send(&msg); + mce_log(LL_DEBUG, + "DSM_MSGTYPE_PROCESSWD_CREATE sent to DSME"); +} + +/** + * Unregister from DSME process watchdog + */ +static void dsme_exit_processwd(void) +{ + mce_log(LL_DEBUG, + "Disabling DSME process watchdog"); + + /* Set up the message */ + DSM_MSGTYPE_PROCESSWD_DELETE msg = + DSME_MSG_INIT(DSM_MSGTYPE_PROCESSWD_DELETE); + msg.pid = getpid(); + + /* Send the message */ + mce_dsme_send(&msg); + mce_log(LL_DEBUG, + "DSM_MSGTYPE_PROCESSWD_DELETE sent to DSME"); +} + +/** + * Send system state inquiry + */ +static void query_system_state(void) +{ + /* Set up the message */ + DSM_MSGTYPE_STATE_QUERY msg = DSME_MSG_INIT(DSM_MSGTYPE_STATE_QUERY); + + /* Send the message */ + mce_dsme_send(&msg); + mce_log(LL_DEBUG, + "DSM_MSGTYPE_STATE_QUERY sent to DSME"); +} + +/** + * Request powerup + */ +void request_powerup(void) +{ + /* Set up the message */ + DSM_MSGTYPE_POWERUP_REQ msg = DSME_MSG_INIT(DSM_MSGTYPE_POWERUP_REQ); + + /* Send the message */ + mce_dsme_send(&msg); + mce_log(LL_DEBUG, + "DSM_MSGTYPE_POWERUP_REQ sent to DSME"); +} + +/** + * Request reboot + */ +void request_reboot(void) +{ + /* Set up the message */ + DSM_MSGTYPE_REBOOT_REQ msg = DSME_MSG_INIT(DSM_MSGTYPE_REBOOT_REQ); + + /* Send the message */ + mce_dsme_send(&msg); + mce_log(LL_DEBUG, + "DSM_MSGTYPE_REBOOT_REQ sent to DSME"); +} + +/** + * Request soft poweron + */ +void request_soft_poweron(void) +{ + /* Disable the soft poweroff LED pattern */ + execute_datapipe_output_triggers(&led_pattern_deactivate_pipe, + MCE_LED_PATTERN_DEVICE_SOFT_OFF, + USE_INDATA); + + mce_rem_submode_int32(MCE_SOFTOFF_SUBMODE); + execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_ON), + USE_INDATA, CACHE_INDATA); + + /* Connectivity policy */ + switch (softoff_connectivity_policy_poweron) { + case SOFTOFF_CONNECTIVITY_FORCE_OFFLINE: + /* Restore previous radio state */ + execute_datapipe(&master_radio_pipe, + GINT_TO_POINTER(previous_radio_state), + USE_INDATA, CACHE_INDATA); + break; + + case SOFTOFF_CONNECTIVITY_OFFLINE: + default: + /* Do nothing */ + break; + } +} + +/** + * Request soft poweroff + */ +void request_soft_poweroff(void) +{ + gboolean connected; + gint policy; + + if (charger_connected == TRUE) + policy = softoff_connectivity_policy_charger; + else + policy = softoff_connectivity_policy_battery; + + connected = get_connectivity_status(); + + /* Connectivity policy */ + switch (policy) { + case SOFTOFF_CONNECTIVITY_SOFT_OFFLINE: + /* If there are open connections, abort */ + if (connected == TRUE) + break; + + /* Fall-through */ + case SOFTOFF_CONNECTIVITY_FORCE_OFFLINE: + /* Store radio state for restore on soft poweron */ + previous_radio_state = datapipe_get_gint(master_radio_pipe); + + /* Go offline */ + execute_datapipe(&master_radio_pipe, + GINT_TO_POINTER(0), + USE_INDATA, CACHE_INDATA); + break; + + case SOFTOFF_CONNECTIVITY_RETAIN: + default: + break; + } + + mce_add_submode_int32(MCE_SOFTOFF_SUBMODE); + execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_OFF), + USE_INDATA, CACHE_INDATA); + + /* Enable the soft poweroff LED pattern */ + execute_datapipe_output_triggers(&led_pattern_activate_pipe, + MCE_LED_PATTERN_DEVICE_SOFT_OFF, + USE_INDATA); +} + +/** + * Timeout callback for transition + * + * @param data Unused + * @return Always returns FALSE, to disable the timeout + */ +static gboolean transition_timeout_cb(gpointer data) +{ + (void)data; + + transition_timeout_cb_id = 0; + + mce_rem_submode_int32(MCE_TRANSITION_SUBMODE); + + return FALSE; +} + +/** + * Cancel state transition timeout + */ +static void cancel_state_transition_timeout(void) +{ + /* Remove the timeout source for state transitions */ + if (transition_timeout_cb_id != 0) { + g_source_remove(transition_timeout_cb_id); + transition_timeout_cb_id = 0; + } +} + +/** + * Setup state transition timeout + */ +static void setup_transition_timeout(void) +{ + cancel_state_transition_timeout(); + + /* Setup new timeout */ + transition_timeout_cb_id = + g_timeout_add(TRANSITION_DELAY, transition_timeout_cb, NULL); +} + +/** + * Request normal shutdown + */ +void request_normal_shutdown(void) +{ + /* Set up the message */ + DSM_MSGTYPE_SHUTDOWN_REQ msg = DSME_MSG_INIT(DSM_MSGTYPE_SHUTDOWN_REQ); + + /* Send the message */ + mce_dsme_send(&msg); + mce_log(LL_DEBUG, + "DSM_MSGTYPE_SHUTDOWN_REQ (DSME_NORMAL_SHUTDOWN) " + "sent to DSME"); +} + +/** + * Convert DSME dsme state + * to a state enum that we can export to datapipes + * + * @param dsmestate The DSME dsme_state_t with the value to convert + * @return the converted value + */ +static system_state_t normalise_dsme_state(dsme_state_t dsmestate) +{ + system_state_t state = MCE_STATE_UNDEF; + + switch (dsmestate) { + case DSME_STATE_SHUTDOWN: + state = MCE_STATE_SHUTDOWN; + break; + + case DSME_STATE_USER: + state = MCE_STATE_USER; + break; + + case DSME_STATE_ACTDEAD: + state = MCE_STATE_ACTDEAD; + break; + + case DSME_STATE_REBOOT: + state = MCE_STATE_REBOOT; + break; + + case DSME_STATE_BOOT: + state = MCE_STATE_BOOT; + break; + + case DSME_STATE_NOT_SET: + break; + + case DSME_STATE_TEST: + mce_log(LL_WARN, + "Received DSME_STATE_TEST; treating as undefined"); + break; + + case DSME_STATE_MALF: + mce_log(LL_WARN, + "Received DSME_STATE_MALF; treating as undefined"); + break; + + case DSME_STATE_LOCAL: + mce_log(LL_WARN, + "Received DSME_STATE_LOCAL; treating as undefined"); + break; + + default: + mce_log(LL_ERR, + "Received an unknown state from DSME; " + "treating as undefined"); + break; + } + + return state; +} + +/** + * Callback for pending I/O from dsmesock + * + * XXX: is the error policy reasonable? + * + * @param source Unused + * @param condition Unused + * @param data Unused + * @return TRUE on success, FALSE on failure + */ +static gboolean io_data_ready_cb(GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + dsmemsg_generic_t *msg; + DSM_MSGTYPE_STATE_CHANGE_IND *msg2; + system_state_t oldstate = datapipe_get_gint(system_state_pipe); + system_state_t newstate = MCE_STATE_UNDEF; + + (void)source; + (void)condition; + (void)data; + + if (dsme_disabled == TRUE) + goto EXIT; + + if ((msg = (dsmemsg_generic_t *)dsmesock_receive(dsme_conn)) == NULL) + goto EXIT; + + if (DSMEMSG_CAST(DSM_MSGTYPE_CLOSE, msg)) { + /* DSME socket closed: try once to reopen; + * if that fails, exit + */ + mce_log(LL_ERR, + "DSME socket closed; trying to reopen"); + + if ((init_dsmesock()) == FALSE) { + g_main_loop_quit(mainloop); + exit(EXIT_FAILURE); + } + } else if (DSMEMSG_CAST(DSM_MSGTYPE_PROCESSWD_PING, msg)) { + dsme_send_pong(); + } else if ((msg2 = DSMEMSG_CAST(DSM_MSGTYPE_STATE_CHANGE_IND, msg))) { + newstate = normalise_dsme_state(msg2->state); + mce_log(LL_DEBUG, + "DSME device state change: %d", + newstate); + + /* If we're changing to a different state, + * add the transition flag, UNLESS the old state + * was MCE_STATE_UNDEF + */ + if ((oldstate != newstate) && (oldstate != MCE_STATE_UNDEF)) + mce_add_submode_int32(MCE_TRANSITION_SUBMODE); + + switch (newstate) { + case MCE_STATE_USER: + execute_datapipe_output_triggers(&led_pattern_activate_pipe, MCE_LED_PATTERN_DEVICE_ON, USE_INDATA); + break; + + case MCE_STATE_ACTDEAD: + case MCE_STATE_BOOT: + case MCE_STATE_UNDEF: + break; + + case MCE_STATE_SHUTDOWN: + case MCE_STATE_REBOOT: + execute_datapipe_output_triggers(&led_pattern_deactivate_pipe, MCE_LED_PATTERN_DEVICE_ON, USE_INDATA); + break; + + default: + break; + } + + execute_datapipe(&system_state_pipe, + GINT_TO_POINTER(newstate), + USE_INDATA, CACHE_INDATA); + } else { + mce_log(LL_DEBUG, + "Unknown message type (%x) received from DSME!", + msg->type_); /* <- unholy access of a private member */ + } + + free(msg); + +EXIT: + return TRUE; +} + +/** + * Callback for I/O errors from dsmesock + * + * @param source Unused + * @param condition Unused + * @param data Unused + * @return Will never return; if there is an I/O-error we exit the mainloop + */ +static gboolean io_error_cb(GIOChannel *source, + GIOCondition condition, + gpointer data) G_GNUC_NORETURN; + +static gboolean io_error_cb(GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + /* Silence warnings */ + (void)source; + (void)condition; + (void)data; + + /* DSME socket closed/error */ + mce_log(LL_CRIT, + "DSME socket closed/error, exiting..."); + g_main_loop_quit(mainloop); + exit(EXIT_FAILURE); +} + +/** + * D-Bus callback for the init done notification signal + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean init_done_dbus_cb(DBusMessage *const msg) +{ + gboolean status = FALSE; + + (void)msg; + + mce_log(LL_DEBUG, + "Received init done notification"); + + if ((mce_get_submode_int32() & MCE_TRANSITION_SUBMODE)) { + setup_transition_timeout(); + } + + status = TRUE; + +//EXIT: + return status; +} + +/** + * Datapipe trigger for the charger state + * + * @param data TRUE if the charger was connected, + * FALSE if the charger was disconnected + */ +static void charger_state_trigger(gconstpointer const data) +{ + submode_t submode = mce_get_submode_int32(); + + charger_connected = GPOINTER_TO_INT(data); + + if ((submode & MCE_SOFTOFF_SUBMODE) != 0) { + if (softoff_charger_connect_policy == SOFTOFF_CHARGER_CONNECT_WAKEUP) { + request_soft_poweron(); + } + } +} + +/** + * Initialise dsmesock connection + * + * @return TRUE on success, FALSE on failure + */ +static gboolean init_dsmesock(void) +{ + gboolean status = FALSE; + + if (dsme_conn == NULL) { + if ((dsme_conn = dsmesock_connect()) == NULL) { + mce_log(LL_CRIT, + "Failed to open DSME socket"); + goto EXIT; + } + } + + if ((dsme_iochan = g_io_channel_unix_new(dsme_conn->fd)) == NULL) { + mce_log(LL_CRIT, + "Failed to set up I/O channel for DSME socket"); + goto EXIT; + } + + dsme_data_source_id = g_io_add_watch(dsme_iochan, + G_IO_IN | G_IO_PRI, + io_data_ready_cb, NULL); + dsme_error_source_id = g_io_add_watch(dsme_iochan, + G_IO_ERR | G_IO_HUP, + io_error_cb, NULL); + + /* Query the current system state; if the mainloop isn't running, + * this will trigger an update when the mainloop starts + */ + query_system_state(); + + status = TRUE; + +EXIT: + return status; +} + +/** + * Close dsmesock connection + */ +static void close_dsmesock(void) +{ + mce_log(LL_DEBUG, + "Shutting down dsmesock I/O channel"); + + if (dsme_iochan != NULL) { + GError *error = NULL; + g_source_remove(dsme_data_source_id); + g_source_remove(dsme_error_source_id); + g_io_channel_shutdown(dsme_iochan, FALSE, &error); + g_io_channel_unref(dsme_iochan); + g_clear_error(&error); + } + + mce_log(LL_DEBUG, + "Closing DSME sock"); + + dsmesock_close(dsme_conn); +} + +/** + * Init function for the mce-dsme component + * + * @param debug_mode TRUE - do not exit if dsme fails + * @return TRUE on success, FALSE on failure + */ +gboolean mce_dsme_init(gboolean debug_mode) +{ + gboolean status = FALSE; + gchar *tmp = NULL; + + /* Append triggers/filters to datapipes */ + append_output_trigger_to_datapipe(&charger_state_pipe, + charger_state_trigger); + + mce_log(LL_DEBUG, + "Connecting to DSME sock"); + + if (init_dsmesock() == FALSE) { + if (debug_mode == TRUE) { + dsme_disabled = TRUE; + } else { + goto EXIT; + } + } + + /* Register with DSME's process watchdog */ + dsme_init_processwd(); + + /* init_done */ + if (mce_dbus_handler_add("com.nokia.startup.signal", + "init_done", + NULL, + DBUS_MESSAGE_TYPE_SIGNAL, + init_done_dbus_cb) == NULL) + goto EXIT; + + /* Get configuration options */ + tmp = mce_conf_get_string(MCE_CONF_SOFTPOWEROFF_GROUP, + MCE_CONF_SOFTPOWEROFF_CONNECTIVITY_POLICY_CHARGER, + "", + NULL); + + softoff_connectivity_policy_charger = mce_translate_string_to_int_with_default(soft_poweroff_connectivity_translation, tmp, DEFAULT_SOFTOFF_CONNECTIVITY_CHARGER); + g_free(tmp); + + tmp = mce_conf_get_string(MCE_CONF_SOFTPOWEROFF_GROUP, + MCE_CONF_SOFTPOWEROFF_CONNECTIVITY_POLICY_BATTERY, + "", + NULL); + + softoff_connectivity_policy_battery = mce_translate_string_to_int_with_default(soft_poweroff_connectivity_translation, tmp, DEFAULT_SOFTOFF_CONNECTIVITY_BATTERY); + g_free(tmp); + + tmp = mce_conf_get_string(MCE_CONF_SOFTPOWEROFF_GROUP, + MCE_CONF_SOFTPOWEROFF_CONNECTIVITY_POLICY_POWERON, + "", + NULL); + + softoff_connectivity_policy_poweron = mce_translate_string_to_int_with_default(soft_poweron_connectivity_translation, tmp, DEFAULT_SOFTOFF_CONNECTIVITY_POWERON); + g_free(tmp); + + tmp = mce_conf_get_string(MCE_CONF_SOFTPOWEROFF_GROUP, + MCE_CONF_SOFTPOWEROFF_CHARGER_POLICY_CONNECT, + "", + NULL); + + softoff_charger_connect_policy = mce_translate_string_to_int_with_default(soft_poweroff_charger_connect_translation, tmp, DEFAULT_SOFTOFF_CHARGER_CONNECT); + g_free(tmp); + + status = TRUE; + +EXIT: + return status; +} + +/** + * Exit function for the mce-dsme component + * + * @todo D-Bus unregistration + * @todo trigger unregistration + */ +void mce_dsme_exit(void) +{ + if (dsme_conn != NULL) { + dsme_exit_processwd(); + close_dsmesock(); + } + + /* Remove triggers/filters from datapipes */ + remove_output_trigger_from_datapipe(&charger_state_pipe, + charger_state_trigger); + + /* Remove all timer sources */ + cancel_state_transition_timeout(); + + return; +} diff --git a/mce-dsme.h b/mce-dsme.h new file mode 100644 index 00000000..4fdaf6fb --- /dev/null +++ b/mce-dsme.h @@ -0,0 +1,126 @@ +/** + * @file mce-dsme.h + * Headers for the DSME<->MCE interface and logic + *

+ * Copyright © 2004-2009 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _MCE_DSME_H_ +#define _MCE_DSME_H_ + +#include + +/** Default delay before the user can power up the device from acting dead */ +#define TRANSITION_DELAY 1000 /**< 1 second */ + +/** Name of Powerkey configuration group */ +#define MCE_CONF_SOFTPOWEROFF_GROUP "SoftPowerOff" + +/** Name of configuration key for connectivity policy with charger connected */ +#define MCE_CONF_SOFTPOWEROFF_CONNECTIVITY_POLICY_CHARGER "ConnectivityPolicyCharger" + +/** Name of configuration key for connectivity policy when running on battery */ +#define MCE_CONF_SOFTPOWEROFF_CONNECTIVITY_POLICY_BATTERY "ConnectivityPolicyBattery" + +/** Name of configuration key for connectivity policy when powering on */ +#define MCE_CONF_SOFTPOWEROFF_CONNECTIVITY_POLICY_POWERON "ConnectivityPolicyPowerOn" + +/** Name of configuration key for charger connect policy in soft poweroff */ +#define MCE_CONF_SOFTPOWEROFF_CHARGER_POLICY_CONNECT "ChargerPolicyConnect" + +/** + * Name of configuration value for the "forced offline" policy + * when entering soft poweroff + */ +#define SOFTOFF_CONNECTIVITY_FORCE_OFFLINE_STR "forceoffline" +/** + * Name of configuration value for the "soft offline" policy + * when entering soft poweroff + */ +#define SOFTOFF_CONNECTIVITY_SOFT_OFFLINE_STR "softoffline" +/** + * Name of configuration value for the "retain connectivity" policy + * when entering soft poweroff + */ +#define SOFTOFF_CONNECTIVITY_RETAIN_STR "retain" +/** + * Name of configuration value for the "stay offline" policy + * when powering on from soft poweroff + */ +#define SOFTOFF_CONNECTIVITY_OFFLINE_STR "offline" +/** + * Name of configuration value for the "restore connectivity" policy + * when powering on from soft poweroff + */ +#define SOFTOFF_CONNECTIVITY_RESTORE_STR "restore" +/** + * Name of configuration value for the "wake on charger" policy + * when in soft poweroff + */ +#define SOFTOFF_CHARGER_CONNECT_WAKEUP_STR "wakeup" +/** + * Name of configuration value for the "ignore charger" policy + * when in soft poweroff + */ +#define SOFTOFF_CHARGER_CONNECT_IGNORE_STR "ignore" + +/** Soft poweroff connectivity policies */ +enum { + /** Policy not set */ + SOFTOFF_CONNECTIVITY_INVALID = MCE_INVALID_TRANSLATION, + /** Retain connectivity */ + SOFTOFF_CONNECTIVITY_RETAIN = 0, + /** Default setting when charger connected */ + DEFAULT_SOFTOFF_CONNECTIVITY_CHARGER = SOFTOFF_CONNECTIVITY_RETAIN, + /** Go to offline mode if no connections are open */ + SOFTOFF_CONNECTIVITY_SOFT_OFFLINE = 1, + /** Go to offline mode */ + SOFTOFF_CONNECTIVITY_FORCE_OFFLINE = 2, + /** Default setting when running on battery */ + DEFAULT_SOFTOFF_CONNECTIVITY_BATTERY = SOFTOFF_CONNECTIVITY_FORCE_OFFLINE, +}; + +/** Soft poweron connectivity policies */ +enum { + /** Stay in offline mode */ + SOFTOFF_CONNECTIVITY_OFFLINE = 0, + /** Default setting */ + DEFAULT_SOFTOFF_CONNECTIVITY_POWERON = SOFTOFF_CONNECTIVITY_OFFLINE, + /** Restore previous mode */ + SOFTOFF_CONNECTIVITY_RESTORE = 1, +}; + +/** Soft poweroff charger connect policy */ +enum { + /** Stay in offline mode */ + SOFTOFF_CHARGER_CONNECT_WAKEUP = 0, + /** Restore previous mode */ + SOFTOFF_CHARGER_CONNECT_IGNORE = 1, + /** Default setting */ + DEFAULT_SOFTOFF_CHARGER_CONNECT = SOFTOFF_CHARGER_CONNECT_IGNORE, +}; + +void request_powerup(void); +void request_reboot(void); +void request_soft_poweron(void); +void request_soft_poweroff(void); +void request_normal_shutdown(void); + +/* When MCE is made modular, this will be handled differently */ +gboolean mce_dsme_init(gboolean debug_mode); +void mce_dsme_exit(void); + +#endif /* _MCE_DSME_H_ */ diff --git a/mce-gconf.c b/mce-gconf.c new file mode 100644 index 00000000..6a95caf3 --- /dev/null +++ b/mce-gconf.c @@ -0,0 +1,298 @@ +/** + * @file mce-gconf.c + * Gconf handling code for the Mode Control Entity + *

+ * Copyright © 2004-2009 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include +#include /* g_object_unref() */ + +#include + +#include "mce.h" +#include "mce-gconf.h" + +#include "mce-log.h" /* mce_log(), LL_* */ + +/** Pointer to the GConf client */ +static GConfClient *gconf_client = NULL; +/** List of GConf notifiers */ +static GSList *gconf_notifiers = NULL; + +/** + * Set an integer GConf key to the specified value + * + * @param key The GConf key to set the value of + * @param value The value to set the key to + * @return TRUE on success, FALSE on failure + */ +gboolean mce_gconf_set_int(const gchar *const key, const gint value) +{ + gboolean status = FALSE; + + if (gconf_client_set_int(gconf_client, key, value, NULL) == FALSE) { + mce_log(LL_WARN, "Failed to write %s to GConf", key); + goto EXIT; + } + + /* synchronise if possible, ignore errors */ + gconf_client_suggest_sync(gconf_client, NULL); + + status = TRUE; + +EXIT: + return status; +} + +/** + * Return a boolean from the specified GConf key + * + * @param key The GConf key to get the value from + * @param[out] value Will contain the value on return, if successful + * @return TRUE on success, FALSE on failure + */ +gboolean mce_gconf_get_bool(const gchar *const key, gboolean *value) +{ + gboolean status = FALSE; + GError *error = NULL; + GConfValue *gcv; + + gcv = gconf_client_get(gconf_client, key, &error); + + if (gcv == NULL) { + mce_log((error != NULL) ? LL_WARN : LL_INFO, + "Could not retrieve %s from GConf; %s", + key, (error != NULL) ? error->message : "Key not set"); + goto EXIT; + } + + if (gcv->type != GCONF_VALUE_BOOL) { + mce_log(LL_ERR, + "GConf key %s should have type: %d, but has type: %d", + key, GCONF_VALUE_BOOL, gcv->type); + goto EXIT; + } + + *value = gconf_value_get_bool(gcv); + gconf_value_free(gcv); + + status = TRUE; + +EXIT: + g_clear_error(&error); + + return status; +} + +/** + * Return an integer from the specified GConf key + * + * @param key The GConf key to get the value from + * @param[out] value Will contain the value on return + * @return TRUE on success, FALSE on failure + */ +gboolean mce_gconf_get_int(const gchar *const key, gint *value) +{ + gboolean status = FALSE; + GError *error = NULL; + GConfValue *gcv; + + gcv = gconf_client_get(gconf_client, key, &error); + + if (gcv == NULL) { + mce_log((error != NULL) ? LL_WARN : LL_INFO, + "Could not retrieve %s from GConf; %s", + key, (error != NULL) ? error->message : "Key not set"); + goto EXIT; + } + + if (gcv->type != GCONF_VALUE_INT) { + mce_log(LL_ERR, + "GConf key %s should have type: %d, but has type: %d", + key, GCONF_VALUE_INT, gcv->type); + goto EXIT; + } + + *value = gconf_value_get_int(gcv); + gconf_value_free(gcv); + + status = TRUE; + +EXIT: + g_clear_error(&error); + + return status; +} + +/** + * Return an integer list from the specified GConf key + * + * @param key The GConf key to get the values from + * @param[out] values Will contain an GSList with the values on return + * @return TRUE on success, FALSE on failure + */ +gboolean mce_gconf_get_int_list(const gchar *const key, GSList **values) +{ + gboolean status = FALSE; + GError *error = NULL; + GConfValue *gcv, *gcv2; + GSList *list; + gint i; + + gcv = gconf_client_get(gconf_client, key, &error); + + if (gcv == NULL) { + mce_log((error != NULL) ? LL_WARN : LL_INFO, + "Could not retrieve %s from GConf; %s", + key, (error != NULL) ? error->message : "Key not set"); + goto EXIT; + } + + if ((gcv->type != GCONF_VALUE_LIST) || + (gconf_value_get_list_type(gcv) != GCONF_VALUE_INT)) { + mce_log(LL_ERR, + "GConf key %s should have type: %d<%d>, but has type: %d<%d>", + key, GCONF_VALUE_LIST, GCONF_VALUE_INT, + gcv->type, gconf_value_get_list_type(gcv)); + goto EXIT; + } + + list = gconf_value_get_list(gcv); + + for (i = 0; (gcv2 = g_slist_nth_data(list, i)) != NULL; i++) { + gint data; + + data = gconf_value_get_int(gcv2); + + /* Prepend is more efficient than append */ + *values = g_slist_prepend(*values, GINT_TO_POINTER(data)); + } + + /* Reverse the list, since we want the entries in the right order */ + *values = g_slist_reverse(*values); + gconf_value_free(gcv); + + status = TRUE; + +EXIT: + g_clear_error(&error); + + return status; +} + +/** + * Add a GConf notifier + * + * @param path The GConf directory to watch + * @param key The GConf key to add the notifier for + * @param callback The callback function + * @param[out] cb_id Will contain the callback ID on return + * @return TRUE on success, FALSE on failure + */ +gboolean mce_gconf_notifier_add(const gchar *path, const gchar *key, + const GConfClientNotifyFunc callback, + guint *cb_id) +{ + GError *error = NULL; + gboolean status = FALSE; + + gconf_client_add_dir(gconf_client, path, + GCONF_CLIENT_PRELOAD_NONE, &error); + + if (error != NULL) { + mce_log(LL_CRIT, + "Could not add %s to directories watched by " + "GConf client setting from GConf; %s", + path, error->message); + //goto EXIT; + } + + g_clear_error(&error); + + *cb_id = gconf_client_notify_add(gconf_client, key, callback, + NULL, NULL, &error); + if (error != NULL) { + mce_log(LL_CRIT, + "Could not register notifier for %s; %s", + key, error->message); + //goto EXIT; + } + + gconf_notifiers = g_slist_prepend(gconf_notifiers, + GINT_TO_POINTER(*cb_id)); + status = TRUE; + +//EXIT: + g_clear_error(&error); + + return status; +} + +/** + * Remove a GConf notifier + * + * @param cb_id The ID of the notifier to remove + * @param user_data Unused + */ +void mce_gconf_notifier_remove(gpointer cb_id, gpointer user_data) +{ + (void)user_data; + + gconf_client_notify_remove(gconf_client, GPOINTER_TO_INT(cb_id)); + gconf_notifiers = g_slist_remove(gconf_notifiers, cb_id); +} + +/** + * Init function for the mce-gconf component + * + * @return TRUE on success, FALSE on failure + */ +gboolean mce_gconf_init(void) +{ + gboolean status = FALSE; + + /* Get the default GConf client */ + if ((gconf_client = gconf_client_get_default()) == FALSE) { + mce_log(LL_CRIT, "Could not get default GConf client"); + goto EXIT; + } + + status = TRUE; + +EXIT: + return status; +} + +/** + * Exit function for the mce-gconf component + */ +void mce_gconf_exit(void) +{ + if (gconf_client != NULL) { + /* Free the list of GConf notifiers */ + if (gconf_notifiers != NULL) { + g_slist_foreach(gconf_notifiers, + (GFunc)mce_gconf_notifier_remove, NULL); + gconf_notifiers = NULL; + } + + /* Unreference GConf client */ + g_object_unref(gconf_client); + } + + return; +} diff --git a/mce-gconf.h b/mce-gconf.h new file mode 100644 index 00000000..230a1fca --- /dev/null +++ b/mce-gconf.h @@ -0,0 +1,42 @@ +/** + * @file mce-gconf.h + * Headers for the GConf handling code for the Mode Control Entity + *

+ * Copyright © 2004-2007 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _MCE_GCONF_H_ +#define _MCE_GCONF_H_ + +#include /* gboolean, gpointer, + * gchar, guint, gint + */ + +#include /* GConfClientNotifyFunc */ + +gboolean mce_gconf_set_int(const gchar *const key, const gint value); +gboolean mce_gconf_get_bool(const gchar *const key, gboolean *value); +gboolean mce_gconf_get_int(const gchar *const key, gint *value); +gboolean mce_gconf_get_int_list(const gchar *const key, GSList **values); +gboolean mce_gconf_notifier_add(const gchar *path, const gchar *key, + const GConfClientNotifyFunc callback, + guint *cb_id); +void mce_gconf_notifier_remove(gpointer cb_id, gpointer user_data); + +gboolean mce_gconf_init(void); +void mce_gconf_exit(void); + +#endif /* _MCE_GCONF_H_ */ diff --git a/mce-hal.c b/mce-hal.c new file mode 100644 index 00000000..82998e3b --- /dev/null +++ b/mce-hal.c @@ -0,0 +1,144 @@ +/** + * @file mce-hal.c + * Hardware Abstraction Layer for MCE + *

+ * Copyright © 2009-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include + +#include /* strstr() */ +#include /* free() */ +#include /* sysinfo_init(), + * sysinfo_get_value(), + * sysinfo_finish(), + * struct system_config + */ + +#include "mce-hal.h" + +#include "mce-log.h" /* mce_log(), LL_* */ + +#if 0 +/** Lock key type */ +typdef enum { + /** No lockkey */ + LOCKKEY_NONE, + /** Flicker key */ + LOCKKEY_FLICKER, + /** Slider key */ + LOCKKEY_SLIDER +} lockkey_t; + +/** Hardware information */ +typedef struct { + /** Does the device have a lock key? If so, what type? */ + lockkey_t lockkey; + /** Does the device have a hardware keyboard? */ + gboolean keyboard; +} product_info_t; +#endif + +/** + * The product ID of the device + */ +static product_id_t product_id = PRODUCT_UNSET; + +/** + * Compare a string with memory, with length checks + * + * @param mem The memory to compare with + * @param str The string to compare the memory to + * @param len The length of the memory area + * @return TRUE if the string matches the memory area, + * FALSE if the memory area does not match, or if the lengths differ + */ +static gboolean strmemcmp(guint8 *mem, const gchar *str, gulong len) +{ + gboolean result = FALSE; + + if (strlen(str) != len) + goto EXIT; + + if (memcmp(mem, str, len) != 0) + goto EXIT; + + result = TRUE; + +EXIT: + return result; +} + +/** + * Get product ID + * + * @return The product ID + */ +product_id_t get_product_id(void) +{ + static struct system_config *sc = 0; + guint8 *tmp = NULL; + gulong len = 0; + + if (product_id != PRODUCT_UNSET) + goto EXIT; + + if (sysinfo_init(&sc) != 0) { + mce_log(LL_ERR, + "sysinfo_init() failed"); + product_id = PRODUCT_UNKNOWN; + goto EXIT; + } + + if (sysinfo_get_value(sc, PRODUCT_SYSINFO_KEY, &tmp, &len) != 0) { + mce_log(LL_ERR, + "sysinfo_get_value() failed"); + product_id = PRODUCT_UNKNOWN; + goto EXIT2; + } + + if (strmemcmp(tmp, PRODUCT_SU18_STR, len) == TRUE) { + product_id = PRODUCT_SU18; + } else if (strmemcmp(tmp, PRODUCT_RX34_STR, len) == TRUE) { + product_id = PRODUCT_RX34; + } else if (strmemcmp(tmp, PRODUCT_RX44_STR, len) == TRUE) { + product_id = PRODUCT_RX44; + } else if (strmemcmp(tmp, PRODUCT_RX48_STR, len) == TRUE) { + product_id = PRODUCT_RX48; + } else if (strmemcmp(tmp, PRODUCT_RX51_STR, len) == TRUE) { + product_id = PRODUCT_RX51; + } else if (strmemcmp(tmp, PRODUCT_RX71_STR, len) == TRUE) { + product_id = PRODUCT_RX71; + } else if (strmemcmp(tmp, PRODUCT_RM680_STR, len) == TRUE) { + product_id = PRODUCT_RM680; + } else if (strmemcmp(tmp, PRODUCT_RM690_STR, len) == TRUE) { + product_id = PRODUCT_RM690; + } else if (strmemcmp(tmp, PRODUCT_RM696_STR, len) == TRUE) { + product_id = PRODUCT_RM696; + } else if (strmemcmp(tmp, PRODUCT_RM716_STR, len) == TRUE) { + product_id = PRODUCT_RM716; + } else { + product_id = PRODUCT_UNKNOWN; + } + + free(tmp); + +EXIT2: + sysinfo_finish(sc); + +EXIT: + return product_id; +} diff --git a/mce-hal.h b/mce-hal.h new file mode 100644 index 00000000..3f30c067 --- /dev/null +++ b/mce-hal.h @@ -0,0 +1,58 @@ +/** + * @file mce-hal.h + * Headers for the Hardware Abstraction Layer for MCE + *

+ * Copyright © 2008-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _MCE_HAL_H_ +#define _MCE_HAL_H_ + +#include + +/** The sysinfo key to request */ +#define PRODUCT_SYSINFO_KEY "/component/product" + +#define PRODUCT_SU18_STR "SU-18" /**< 770 */ +#define PRODUCT_RX34_STR "RX-34" /**< N800 */ +#define PRODUCT_RX44_STR "RX-44" /**< N810 */ +#define PRODUCT_RX48_STR "RX-48" /**< N810 WiMAX Edition */ +#define PRODUCT_RX51_STR "RX-51" /**< N900 */ +#define PRODUCT_RX71_STR "RX-71" /**< N/A */ +#define PRODUCT_RM680_STR "RM-680" /**< ??? */ +#define PRODUCT_RM690_STR "RM-690" /**< ??? */ +#define PRODUCT_RM696_STR "RM-696" /**< ??? */ +#define PRODUCT_RM716_STR "RM-716" /**< ??? */ + +/** Product ID type */ +typedef enum { + PRODUCT_UNSET = -1, /**< Product not set */ + PRODUCT_UNKNOWN = 0, /**< Product unknown */ + PRODUCT_SU18 = 1, /**< SU-18 */ + PRODUCT_RX34 = 2, /**< RX-34 */ + PRODUCT_RX44 = 3, /**< RX-44 */ + PRODUCT_RX48 = 4, /**< RX-48 */ + PRODUCT_RX51 = 5, /**< RX-51 */ + PRODUCT_RX71 = 6, /**< RX-71 */ + PRODUCT_RM680 = 9, /**< RM-680 */ + PRODUCT_RM690 = 10, /**< RM-690 */ + PRODUCT_RM696 = 11, /**< RM-696 */ + PRODUCT_RM716 = 12 /**< RM-716 */ +} product_id_t; + +product_id_t get_product_id(void); + +#endif /* _MCE_HAL_H_ */ diff --git a/mce-io.c b/mce-io.c new file mode 100644 index 00000000..7d356912 --- /dev/null +++ b/mce-io.c @@ -0,0 +1,1270 @@ +/** + * @file mce-io.c + * Generic I/O functionality for the Mode Control Entity + *

+ * Copyright © 2006-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include +#include /* g_access(), g_unlink() */ + +#include /* errno, EINVAL, ERANGE */ +#include /* open(), O_RDONLY */ +#include /* fopen(), fscanf(), fseek(), + * fclose(), fprintf(), fileno(), + * fflush() + */ +#include /* exit(), strtoul(), EXIT_FAILURE */ +#include /* strlen() */ +#include /* close(), read(), ftruncate() */ + +#include "mce.h" +#include "mce-io.h" + +#include "mce-log.h" /* mce_log(), LL_* */ + +/** List of all file monitors */ +static GSList *file_monitors = NULL; + +/** I/O monitor type */ +typedef enum { + IOMON_UNSET = -1, /**< I/O monitor type unset */ + IOMON_STRING = 0, /**< String I/O monitor */ + IOMON_CHUNK = 1 /**< Chunk I/O monitor */ +} iomon_type; + +/** I/O monitor structure */ +typedef struct { + gchar *file; /**< Monitored file */ + GIOChannel *iochan; /**< I/O channel */ + iomon_cb callback; /**< Callback */ + gulong chunk_size; /**< Read-chunk size */ + guint data_source_id; /**< GSource ID for data */ + guint error_source_id; /**< GSource ID for errors */ + gint fd; /**< File Descriptor */ + iomon_type type; /**< Monitor type */ + error_policy_t error_policy; /**< Error policy */ + GIOCondition monitored_io_conditions; /**< Conditions to monitor */ + GIOCondition latest_io_condition; /**< Latest I/O condition */ + gboolean rewind; /**< Rewind policy */ + gboolean suspended; /**< Is the I/O monitor + * suspended? */ +} iomon_struct; + +/** Suffix used for temporary files */ +#define TMP_SUFFIX ".tmp" + +/** + * Helper function for closing files that checks for NULL, + * prints proper error messages and NULLs the file pointer after close + * + * @param file The name of the file to close; only used by error messages + * @param fp A pointer to the file pointer to close + * @return TRUE on success, FALSE on failure + */ +gboolean mce_close_file(const gchar *const file, FILE **fp) +{ + gboolean status = FALSE; + + if (fp == NULL) { + mce_log(LL_CRIT, + "fp == NULL!"); + goto EXIT; + } + + if (*fp == NULL) { + status = TRUE; + goto EXIT; + } + + if (fclose(*fp) == EOF) { + mce_log(LL_ERR, + "Failed to close `%s'; %s", + file ? file : "", + g_strerror(errno)); + status = FALSE; + + /* Ignore error */ + errno = 0; + goto EXIT; + } + + *fp = NULL; + + status = TRUE; + +EXIT: + return status; +} + +/** + * Read a chunk from a file + * + * @param file Path to the file, or NULL to use an already open fd instead + * @param[out] data A newly allocated buffer with the first chunk from the file + * @param[in,out] len [in] The length of the buffer to read + * [out] The number of bytes read + * @param flags Additional flags to pass to open(); + * by default O_RDONLY is always passed -- this is mainly + * to allow passing O_NONBLOCK + * @param fd A file descriptor to use, or -1 to use the file path instead + * @return TRUE on success, FALSE on failure + */ +gboolean mce_read_chunk_from_file(const gchar *const file, void **data, + gssize *len, int flags, int fd) +{ + gboolean status = FALSE; + gint again_count = 0; + gssize result = -1; + + if ((file == NULL) && (fd == -1)) { + mce_log(LL_CRIT, "(file == NULL) && (fd == -1)!"); + goto EXIT; + } else if ((file != NULL) && (fd != -1)) { + mce_log(LL_CRIT, "(file != NULL) && (fd != -1)!"); + goto EXIT; + } + + if (len == NULL) { + mce_log(LL_CRIT, "len == NULL!"); + goto EXIT; + } + + if (*len <= 0) { + mce_log(LL_CRIT, "*len <= 0!"); + goto EXIT; + } + + /* If we cannot open the file, abort */ + if ((fd == -1) && (fd = open(file, O_RDONLY | flags)) == -1) { + mce_log(LL_ERR, + "Cannot open `%s' for reading; %s", + file, g_strerror(errno)); + + /* Ignore error */ + errno = 0; + goto EXIT; + } + + if ((*data = g_try_malloc(*len)) == NULL) { + mce_log(LL_CRIT, + "Failed to allocate memory (%zd bytes)!", + *len); + goto EXIT2; + } + + while (again_count++ < 10) { + /* Clear errors from earlier iterations */ + errno = 0; + + result = read(fd, *data, *len); + + if ((result == -1) && + ((errno == EAGAIN) || (errno == EWOULDBLOCK))) { + continue; + } else { + break; + } + } + + if (result == -1) { + mce_log(LL_ERR, + "Failed to read from `%s'; %s", + file, g_strerror(errno)); + + /* Ignore error */ + errno = 0; + goto EXIT2; + } + + status = TRUE; + +EXIT2: + /* XXX: improve close policy? */ + if (file != NULL) { + close(fd); + fd = -1; + } + + /* Ignore error */ + errno = 0; + + *len = result; + +EXIT: + return status; +} + +/** + * Read a string from a file + * + * @param file Path to the file + * @param[out] string A newly allocated string with the first line of the file + * @return TRUE on success, FALSE on failure + */ +gboolean mce_read_string_from_file(const gchar *const file, gchar **string) +{ + GError *error = NULL; + gboolean status = FALSE; + + if (file == NULL) { + mce_log(LL_CRIT, "file == NULL!"); + goto EXIT; + } + + if (g_file_get_contents(file, string, NULL, &error) == FALSE) { + mce_log(LL_ERR, + "Cannot open `%s' for reading; %s", + file, error->message); + goto EXIT; + } + + status = TRUE; + +EXIT: + g_clear_error(&error); + + return status; +} + +/** + * Read a number representation of a string from a file + * + * @param file Path to the file, or NULL to user an already open FILE * instead + * @param[out] number A number representation of the first line of the file + * @param fp A pointer to a FILE *; set the FILE * to NULL to use the file + * path instead + * @param rewind_file TRUE to seek to the beginning of the file, + * FALSE to read from the current position; + * only affects already open files + * @param close_on_exit TRUE to close the file on exit, + * FALSE to leave the file open + * @return TRUE on success, FALSE on failure + */ +gboolean mce_read_number_string_from_file(const gchar *const file, + gulong *number, FILE **fp, + gboolean rewind_file, + gboolean close_on_exit) +{ + gboolean status = FALSE; + FILE *new_fp = NULL; + gint retval; + + if ((file == NULL) && ((fp == NULL) || (*fp == NULL))) { + mce_log(LL_CRIT, + "(file == NULL) && ((fp == NULL) || (*fp == NULL))!"); + goto EXIT; + } + + if ((fp == NULL) && (close_on_exit == FALSE)) { + mce_log(LL_CRIT, + "(fp == NULL) && (close_on_exit == FALSE)!"); + goto EXIT; + } + + /* If we cannot open the file, abort */ + if ((fp == NULL) || (*fp == NULL)) { + if ((new_fp = fopen(file, "r")) == NULL) { + mce_log(LL_ERR, + "Cannot open `%s' for reading; %s", + file, g_strerror(errno)); + + /* Ignore error */ + errno = 0; + goto EXIT; + } + } else { + new_fp = *fp; + } + + /* Rewind file if we already have one */ + if ((fp != NULL) && (*fp != NULL) && (rewind_file == TRUE)) { + if (fseek(*fp, 0L, SEEK_SET) == -1) { + mce_log(LL_ERR, + "Failed to rewind `%s'; %s", + file, g_strerror(errno)); + + /* Ignore error */ + errno = 0; + goto EXIT2; + } + } + + if ((fp != NULL) && (*fp == NULL)) + *fp = new_fp; + + retval = fscanf(new_fp, "%lu", number); + + /* Was the read successful? */ + if (retval == EOF) { + mce_log(LL_ERR, + "Failed to read from `%s'; %s", + file, g_strerror(errno)); + clearerr(new_fp); + + /* Ignore error */ + errno = 0; + goto EXIT2; + } + + if (retval != 1) { + mce_log(LL_ERR, + "Could not match any values when reading from `%s'", + file); + goto EXIT2; + } + + status = TRUE; + +EXIT2: + /* XXX: improve close policy? */ + if ((status == FALSE) || (close_on_exit == TRUE)) { + (void)mce_close_file(file, &new_fp); + + if (fp != NULL) + *fp = NULL; + } + + /* Ignore error */ + errno = 0; + +EXIT: + return status; +} + +/** + * Write a string to a file + * + * @param file Path to the file + * @param string The string to write + * @return TRUE on success, FALSE on failure + */ +gboolean mce_write_string_to_file(const gchar *const file, + const gchar *const string) +{ + GIOChannel *iochan = NULL; + GIOStatus iostatus; + GError *error = NULL; + gboolean status = TRUE; + + if (file == NULL) { + mce_log(LL_CRIT, "file == NULL!"); + status = FALSE; + goto EXIT; + } + + if (string == NULL) { + mce_log(LL_CRIT, "string == NULL!"); + status = FALSE; + goto EXIT; + } + + if ((iochan = g_io_channel_new_file(file, "w", &error)) == NULL) { + mce_log(LL_ERR, + "Cannot open `%s' for writing; %s", + file, error->message); + status = FALSE; + goto EXIT; + } + + iostatus = g_io_channel_write_chars(iochan, string, + -1, NULL, &error); + + if (iostatus != G_IO_STATUS_NORMAL) { + mce_log(LL_ERR, + "Cannot modify `%s'; %s", + file, error->message); + status = FALSE; + g_clear_error(&error); + } + + iostatus = g_io_channel_shutdown(iochan, TRUE, &error); + + if (iostatus != G_IO_STATUS_NORMAL) { + mce_log(LL_ERR, + "Cannot close `%s'; %s", + file, error->message); + status = FALSE; + } + + g_io_channel_unref(iochan); + +EXIT: + g_clear_error(&error); + + return status; +} + +/** + * Write a string representation of a number to a file + * + * Note: this variant uses in-place rewrites when truncating. + * It should thus not be used in cases where atomicity is expected. + * For atomic replace, use mce_write_number_string_to_file_atomic() + * + * @param file Path to the file, or NULL to user an already open FILE * instead + * @param number The number to write + * @param fp A pointer to a FILE *; set the FILE * to NULL to use the file + * path instead + * @param truncate_file TRUE to truncate the file before writing, + * FALSE to append to the end of the file + * @param close_on_exit TRUE to close the file on exit, + * FALSE to leave the file open + * @return TRUE on success, FALSE on failure + */ +gboolean mce_write_number_string_to_file(const gchar *const file, + const gulong number, FILE **fp, + gboolean truncate_file, + gboolean close_on_exit) +{ + gboolean status = FALSE; + FILE *new_fp = NULL; + gint retval; + + if ((file == NULL) && ((fp == NULL) || (*fp == NULL))) { + mce_log(LL_CRIT, + "(file == NULL) && ((fp == NULL) || (*fp == NULL))!"); + goto EXIT; + } + + if ((fp == NULL) && (close_on_exit == FALSE)) { + mce_log(LL_CRIT, + "(fp == NULL) && (close_on_exit == FALSE)!"); + goto EXIT; + } + + /* If we cannot open the file, abort */ + if ((fp == NULL) || (*fp == NULL)) { + if ((new_fp = fopen(file, truncate_file ? "w" : "a")) == NULL) { + mce_log(LL_ERR, + "Cannot open `%s' for %s; %s", + file, + truncate_file ? "writing" : "appending", + g_strerror(errno)); + + /* Ignore error */ + errno = 0; + goto EXIT; + } + } else { + new_fp = *fp; + } + + /* Truncate file if we already have one */ + if ((fp != NULL) && (*fp != NULL) && (truncate_file == TRUE)) { + int fd = fileno(*fp); + + if (fd == -1) { + mce_log(LL_ERR, + "Failed to convert *fp to fd; %s", + g_strerror(errno)); + + /* Ignore error */ + errno = 0; + goto EXIT2; + } + + if (ftruncate(fd, 0L) == -1) { + mce_log(LL_ERR, + "Failed to truncate `%s'; %s", + file, g_strerror(errno)); + + /* Ignore error */ + errno = 0; + goto EXIT2; + } + } + + if ((fp != NULL) && (*fp == NULL)) + *fp = new_fp; + + retval = fprintf(new_fp, "%lu", number); + + /* Was the write successful? */ + if (retval == EOF) { + mce_log(LL_ERR, + "Failed to write to `%s'; %s", + file, g_strerror(errno)); + clearerr(new_fp); + + /* Ignore error */ + errno = 0; + goto EXIT2; + } + + status = TRUE; + +EXIT2: + /* XXX: improve close policy? */ + if ((status == FALSE) || (close_on_exit == TRUE)) { + (void)mce_close_file(file, &new_fp); + + if (fp != NULL) + *fp = NULL; + } else { + fflush(*fp); + } + + /* Ignore error */ + errno = 0; + +EXIT: + return status; +} + +/** + * Write a string representation of a number to a file + * in an atomic manner + * + * @param file Path to the file to write to + * @param number The number to write + * @return TRUE on success, FALSE on failure + */ +gboolean mce_write_number_string_to_file_atomic(const gchar *const file, + const gulong number) +{ + gboolean status = FALSE; + gchar *tmpname = NULL; + FILE *fp = NULL; + gint retval; + int fd; + + if (file == NULL) { + mce_log(LL_CRIT, + "file == NULL"); + goto EXIT; + } + + if ((tmpname = g_strconcat(file, TMP_SUFFIX, NULL)) == NULL) { + mce_log(LL_ERR, + "Failed to allocate memory for `%s%s'", + file, TMP_SUFFIX); + goto EXIT; + } + + /* If we cannot open the file, abort */ + if ((fp = fopen(tmpname, "w")) == NULL) { + mce_log(LL_ERR, + "Cannot open `%s' for writing; %s", + tmpname, g_strerror(errno)); + + /* Ignore error */ + errno = 0; + goto EXIT; + } + + retval = fprintf(fp, "%lu", number); + + /* Was the write successful? */ + if (retval == EOF) { + mce_log(LL_ERR, + "Failed to write to `%s'; %s", + tmpname, g_strerror(errno)); + clearerr(fp); + + /* Ignore error */ + errno = 0; + goto EXIT2; + } + + if ((fd = fileno(fp)) == -1) { + mce_log(LL_ERR, + "Failed to convert *fp to fd; %s", + g_strerror(errno)); + + /* Ignore error */ + errno = 0; + goto EXIT2; + } + + /** Ensure that the data makes it to disk */ + if (fsync(fd) == -1) { + mce_log(LL_ERR, + "Failed to fsync `%s'; %s", + tmpname, g_strerror(errno)); + + /* Ignore error */ + errno = 0; + goto EXIT2; + } + + status = TRUE; + +EXIT2: + /* Close the temporary file */ + if (mce_close_file(tmpname, &fp) == FALSE) { + status = FALSE; + goto EXIT; + } + + /* And if everything has been successful so far, + * rename the temporary file over the old file + */ + if (status == TRUE) { + if (rename(tmpname, file) == -1) { + mce_log(LL_ERR, + "Failed to rename `%s' to `%s'; %s", + tmpname, file, g_strerror(errno)); + + status = FALSE; + + /* Ignore error */ + errno = 0; + goto EXIT; + } + } + +EXIT: + g_free(tmpname); + + return status; +} + +/** + * Callback for successful string I/O + * + * @param source The source of the activity + * @param condition The I/O condition + * @param data The iomon structure + * @return Depending on error policy this function either exits + * or returns TRUE + */ +static gboolean io_string_cb(GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + iomon_struct *iomon = data; + gchar *str = NULL; + gsize bytes_read; + GError *error = NULL; + gboolean status = TRUE; + + /* Silence warnings */ + (void)condition; + + if (iomon == NULL) { + mce_log(LL_CRIT, "iomon == NULL!"); + status = FALSE; + goto EXIT; + } + + iomon->latest_io_condition = 0; + + /* Seek to the beginning of the file before reading if needed */ + if (iomon->rewind == TRUE) { + g_io_channel_seek_position(source, 0, G_SEEK_SET, &error); + g_clear_error(&error); + } + + g_io_channel_read_line(source, &str, &bytes_read, NULL, &error); + + /* Errors and empty reads are nasty */ + if (error != NULL) { + mce_log(LL_ERR, + "Error when reading from %s: %s", + iomon->file, error->message); + status = FALSE; + } else if ((bytes_read == 0) || (str == NULL) || (strlen(str) == 0)) { + mce_log(LL_ERR, + "Empty read from %s", + iomon->file); + } else { + iomon->callback(str, bytes_read); + } + + g_free(str); + g_clear_error(&error); + +EXIT: + if ((status == FALSE) && + (iomon != NULL) && + (iomon->error_policy == MCE_IO_ERROR_POLICY_EXIT)) { + g_main_loop_quit(mainloop); + exit(EXIT_FAILURE); + } + + return TRUE; +} + +/** + * Callback for successful chunk I/O + * + * @param source The source of the activity + * @param condition The I/O condition + * @param data The iomon structure + * @return Depending on error policy this function either exits + * or returns TRUE + */ +static gboolean io_chunk_cb(GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + iomon_struct *iomon = data; + gchar *chunk = NULL; + gsize bytes_read; + GIOStatus io_status; + GError *error = NULL; + gboolean status = TRUE; + + /* Silence warnings */ + (void)condition; + + if (iomon == NULL) { + mce_log(LL_CRIT, "iomon == NULL!"); + status = FALSE; + goto EXIT; + } + + iomon->latest_io_condition = 0; + + /* Seek to the beginning of the file before reading if needed */ + if (iomon->rewind == TRUE) { + g_io_channel_seek_position(source, 0, G_SEEK_SET, &error); + g_clear_error(&error); + } + + chunk = g_malloc(iomon->chunk_size); + + do { + io_status = g_io_channel_read_chars(source, chunk, + iomon->chunk_size, + &bytes_read, &error); + + if ((io_status != G_IO_STATUS_AGAIN) || (error == NULL)) + break; + + g_clear_error(&error); + } while (TRUE); + + /* Errors and empty reads are nasty */ + if (error != NULL) { + mce_log(LL_ERR, + "Error when reading from %s: %s", + iomon->file, error->message); + + if ((error->code == G_IO_CHANNEL_ERROR_FAILED) && + (errno == ENODEV) && + ((g_io_channel_get_flags(iomon->iochan) & + G_IO_FLAG_IS_SEEKABLE) == G_IO_FLAG_IS_SEEKABLE)) { + g_clear_error(&error); + g_io_channel_seek_position(iomon->iochan, 0, + G_SEEK_END, &error); + } else { + status = FALSE; + } + + /* Reset errno, + * to avoid false positives down the line + */ + errno = 0; + } else if (bytes_read == 0) { + mce_log(LL_ERR, + "Empty read from %s", + iomon->file); + } else { + iomon->callback(chunk, bytes_read); + } + + g_free(chunk); + g_clear_error(&error); + +EXIT: + if ((status == FALSE) && + (iomon != NULL) && + (iomon->error_policy == MCE_IO_ERROR_POLICY_EXIT)) { + g_main_loop_quit(mainloop); + exit(EXIT_FAILURE); + } + + return TRUE; +} + +/** + * Callback for I/O errors + * + * @param source Unused + * @param condition The GIOCondition for the error + * @param data The iomon structure + * @return Depending on error policy this function either exits + * or returns TRUE + */ +static gboolean io_error_cb(GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + iomon_struct *iomon = data; + gboolean exit_on_error = FALSE; + loglevel_t loglevel; + + /* Silence warnings */ + (void)source; + + if (iomon == NULL) { + mce_log(LL_CRIT, "iomon == NULL!"); + goto EXIT; + } + + switch (iomon->error_policy) { + case MCE_IO_ERROR_POLICY_EXIT: + exit_on_error = TRUE; + loglevel = LL_CRIT; + break; + + case MCE_IO_ERROR_POLICY_WARN: + loglevel = LL_WARN; + break; + + case MCE_IO_ERROR_POLICY_IGNORE: + default: + /* No log message when ignoring errors */ + loglevel = LL_NONE; + break; + } + + /* We just got an I/O condition we've already reported + * since the last successful read; don't log + */ + if ((exit_on_error == FALSE) && + ((iomon->latest_io_condition & condition) == condition)) { + loglevel = LL_NONE; + } else { + iomon->latest_io_condition |= condition; + } + + if (loglevel != LL_NONE) { + mce_log(loglevel, + "Error accessing %s (condition: %d). %s", + iomon->file, condition, + (exit_on_error == TRUE) ? "Exiting" : "Ignoring"); + } + +EXIT: + if ((iomon != NULL) && (exit_on_error == TRUE)) { + g_main_loop_quit(mainloop); + exit(EXIT_FAILURE); + } + + return TRUE; +} + +/** + * Suspend an I/O monitor + * + * @param io_monitor A pointer to the I/O monitor to suspend + */ +void mce_suspend_io_monitor(gconstpointer io_monitor) +{ + iomon_struct *iomon = (iomon_struct *)io_monitor; + + if (iomon == NULL) { + mce_log(LL_CRIT, "iomon == NULL!"); + goto EXIT; + } + + if (iomon->suspended == TRUE) + goto EXIT; + + /* Remove I/O watches */ + g_source_remove(iomon->data_source_id); + g_source_remove(iomon->error_source_id); + + iomon->suspended = TRUE; + +EXIT: + return; +} + +/** + * Resume an I/O monitor + * + * @param io_monitor A pointer to the I/O monitor to resume + */ +void mce_resume_io_monitor(gconstpointer io_monitor) +{ + iomon_struct *iomon = (iomon_struct *)io_monitor; + GIOFunc callback = NULL; + + if (iomon == NULL) { + mce_log(LL_CRIT, "iomon == NULL!"); + goto EXIT; + } + + if (iomon->suspended == FALSE) + goto EXIT; + + switch (iomon->type) { + case IOMON_STRING: + callback = io_string_cb; + break; + + case IOMON_CHUNK: + callback = io_chunk_cb; + break; + + case IOMON_UNSET: + default: + break; + } + + if (callback != NULL) { + GError *error = NULL; + + /* Seek to the end of the file if the file is seekable, + * unless we use the rewind policy + */ + if ((iomon->rewind == FALSE) && + ((g_io_channel_get_flags(iomon->iochan) & + G_IO_FLAG_IS_SEEKABLE) == G_IO_FLAG_IS_SEEKABLE)) { + g_io_channel_seek_position(iomon->iochan, 0, + G_SEEK_END, &error); + g_clear_error(&error); + } + + iomon->error_source_id = g_io_add_watch(iomon->iochan, + G_IO_HUP | G_IO_NVAL, + io_error_cb, iomon); + iomon->data_source_id = g_io_add_watch(iomon->iochan, + iomon->monitored_io_conditions, + callback, iomon); + iomon->suspended = FALSE; + } else { + mce_log(LL_ERR, + "Failed to resume `%s'; invalid callback", + iomon->file); + } + +EXIT: + return; +} + +/** + * Register an I/O monitor; reads and returns data + * + * @param fd File Descriptor; this takes priority over file; -1 if not used + * @param file Path to the file + * @param error_policy MCE_IO_ERROR_POLICY_EXIT to exit on error, + * MCE_IO_ERROR_POLICY_WARN to warn about errors + * but ignore them, + * MCE_IO_ERROR_POLICY_IGNORE to silently ignore errors + * @param monitored_conditions The GIOConditions to monitor + * @param callback Function to call with result + * @return An I/O monitor pointer on success, NULL on failure + */ +static iomon_struct *mce_register_io_monitor(const gint fd, + const gchar *const file, + error_policy_t error_policy, + GIOCondition monitored_conditions, + iomon_cb callback) +{ + iomon_struct *iomon = NULL; + GIOChannel *iochan = NULL; + GError *error = NULL; + + if (file == NULL) { + mce_log(LL_CRIT, "file == NULL!"); + goto EXIT; + } + + if (callback == NULL) { + mce_log(LL_CRIT, "callback == NULL!"); + goto EXIT; + } + + if ((iomon = g_slice_new(iomon_struct)) == NULL) { + mce_log(LL_CRIT, + "Failed to allocate memory for " + "iomon_struct (%zd bytes)", + sizeof (*iomon)); + goto EXIT; + } + + if (fd != -1) { + if ((iochan = g_io_channel_unix_new(fd)) == NULL) { + /* XXX: this is probably not good either; + * we should only ignore non-existing files + */ + if (error_policy != MCE_IO_ERROR_POLICY_IGNORE) + mce_log(LL_ERR, "Failed to open `%s'", file); + + g_slice_free(iomon_struct, iomon); + iomon = NULL; + goto EXIT; + } + } else { + if ((iochan = g_io_channel_new_file(file, "r", + &error)) == NULL) { + /* XXX: this is probably not good either; + * we should only ignore non-existing files + */ + if (error_policy != MCE_IO_ERROR_POLICY_IGNORE) + mce_log(LL_ERR, + "Failed to open `%s'; %s", + file, error->message); + + g_slice_free(iomon_struct, iomon); + iomon = NULL; + goto EXIT; + } + } + + iomon->fd = fd; + iomon->file = g_strdup(file); + iomon->iochan = iochan; + iomon->callback = callback; + iomon->error_policy = error_policy; + iomon->monitored_io_conditions = monitored_conditions; + iomon->latest_io_condition = 0; + iomon->rewind = FALSE; + iomon->chunk_size = 0; + + file_monitors = g_slist_prepend(file_monitors, iomon); + + iomon->suspended = TRUE; + +EXIT: + g_clear_error(&error); + + return iomon; +} + +/** + * Register an I/O monitor; reads and returns a string + * + * @param fd File Descriptor; this takes priority over file; -1 if not used + * @param file Path to the file + * @param error_policy MCE_IO_ERROR_POLICY_EXIT to exit on error, + * MCE_IO_ERROR_POLICY_WARN to warn about errors + * but ignore them, + * MCE_IO_ERROR_POLICY_IGNORE to silently ignore errors + * @param monitored_conditions The GIOConditions to monitor + * @param rewind_policy TRUE to seek to the beginning, + * FALSE to stay at current position + * @param callback Function to call with result + * @return An I/O monitor cookie on success, NULL on failure + */ +gconstpointer mce_register_io_monitor_string(const gint fd, + const gchar *const file, + error_policy_t error_policy, + GIOCondition monitored_conditions, + gboolean rewind_policy, + iomon_cb callback) +{ + iomon_struct *iomon = NULL; + + iomon = mce_register_io_monitor(fd, file, error_policy, monitored_conditions, callback); + + if (iomon == NULL) + goto EXIT; + + /* Verify that the rewind policy is sane */ + if ((g_io_channel_get_flags(iomon->iochan) & + G_IO_FLAG_IS_SEEKABLE) == G_IO_FLAG_IS_SEEKABLE) { + /* Set the rewind policy */ + iomon->rewind = rewind_policy; + } else if (rewind_policy == TRUE) { + mce_log(LL_ERR, + "Attempting to set rewind policy to TRUE " + "on non-seekable I/O channel `%s'", + file); + iomon->rewind = FALSE; + } + + /* Set the I/O monitor type and call resume to add an I/O watch */ + iomon->type = IOMON_STRING; + mce_resume_io_monitor(iomon); + +EXIT: + return iomon; +} + +/** + * Register an I/O monitor; reads and returns a chunk of specified size + * + * @param fd File Descriptor; this takes priority over file; -1 if not used + * @param file Path to the file + * @param error_policy MCE_IO_ERROR_POLICY_EXIT to exit on error, + * MCE_IO_ERROR_POLICY_WARN to warn about errors + * but ignore them, + * MCE_IO_ERROR_POLICY_IGNORE to silently ignore errors + * @param monitored_conditions The GIOConditions to monitor + * @param rewind_policy TRUE to seek to the beginning, + * FALSE to stay at current position + * @param callback Function to call with result + * @param chunk_size The number of bytes to read in each chunk + * @return An I/O monitor cookie on success, NULL on failure + */ +gconstpointer mce_register_io_monitor_chunk(const gint fd, + const gchar *const file, + error_policy_t error_policy, + GIOCondition monitored_conditions, + gboolean rewind_policy, + iomon_cb callback, + gulong chunk_size) +{ + iomon_struct *iomon = NULL; + GError *error = NULL; + + iomon = mce_register_io_monitor(fd, file, error_policy, monitored_conditions, callback); + + if (iomon == NULL) + goto EXIT; + + /* Set the read chunk size */ + iomon->chunk_size = chunk_size; + + /* Verify that the rewind policy is sane */ + if ((g_io_channel_get_flags(iomon->iochan) & + G_IO_FLAG_IS_SEEKABLE) == G_IO_FLAG_IS_SEEKABLE) { + /* Set the rewind policy */ + iomon->rewind = rewind_policy; + } else if (rewind_policy == TRUE) { + mce_log(LL_ERR, + "Attempting to set rewind policy to TRUE " + "on non-seekable I/O channel `%s'", + file); + iomon->rewind = FALSE; + } + + /* We only read this file in binary form */ + (void)g_io_channel_set_encoding(iomon->iochan, NULL, &error); + + g_clear_error(&error); + + /* Don't block */ + (void)g_io_channel_set_flags(iomon->iochan, G_IO_FLAG_NONBLOCK, &error); + + g_clear_error(&error); + + /* Set the I/O monitor type and call resume to add an I/O watch */ + iomon->type = IOMON_CHUNK; + mce_resume_io_monitor(iomon); + +EXIT: + return iomon; +} + +/** + * Unregister an I/O monitor + * Note: This does NOT shutdown I/O channels created from file descriptors + * + * @param io_monitor A pointer to the I/O monitor to unregister + */ +void mce_unregister_io_monitor(gconstpointer io_monitor) +{ + iomon_struct *iomon = (iomon_struct *)io_monitor; + guint oldlen; + + if (iomon == NULL) { + mce_log(LL_DEBUG, "iomon == NULL!"); + goto EXIT; + } + + oldlen = g_slist_length(file_monitors); + + if (file_monitors != NULL) + file_monitors = g_slist_remove(file_monitors, iomon); + + /* Did we remove any entry? */ + if (oldlen == g_slist_length(file_monitors)) { + mce_log(LL_WARN, + "Trying to unregister non-existing file monitor"); + } + + /* Remove I/O watches */ + mce_suspend_io_monitor(iomon); + + /* We can close this I/O channel, since it's not an external fd */ + if (iomon->fd == -1) { + GIOStatus iostatus; + GError *error = NULL; + + iostatus = g_io_channel_shutdown(iomon->iochan, TRUE, &error); + + if (iostatus != G_IO_STATUS_NORMAL) { + loglevel_t loglevel = LL_ERR; + + /* If we get ENODEV, only log a debug message, + * since this happens for hotpluggable + * /dev/input files + */ + if ((error->code == G_IO_CHANNEL_ERROR_FAILED) && + (errno == ENODEV)) + loglevel = LL_DEBUG; + + mce_log(loglevel, + "Cannot close `%s'; %s", + iomon->file, error->message); + } + + g_clear_error(&error); + } + + g_io_channel_unref(iomon->iochan); + g_free(iomon->file); + g_slice_free(iomon_struct, iomon); + +EXIT: + return; +} + +/** + * Return the name of the monitored file + * + * @param io_monitor An opaque pointer to the I/O monitor structure + * @return The name of the monitored file + */ +const gchar *mce_get_io_monitor_name(gconstpointer io_monitor) +{ + iomon_struct *iomon = (iomon_struct *)io_monitor; + + return iomon->file; +} + +/** + * Return the file descriptor of the monitored file; + * if the file being monitored was opened from a path + * rather than a file descriptor, -1 is returned + * + * @param io_monitor An opaque pointer to the I/O monitor structure + * @return The file descriptor of the monitored file + */ +int mce_get_io_monitor_fd(gconstpointer io_monitor) +{ + iomon_struct *iomon = (iomon_struct *)io_monitor; + + return iomon->fd; +} + +/** + * Test whether there's a pending backup/restore operation + * + * @return TRUE if the backup lock file is in place, + * FALSE if the backup lock file is not in place + */ +gboolean mce_is_backup_pending(void) +{ + return (g_access(MCE_BACKUP_LOCK_FILE_PATH, F_OK) == 0); +} + +/** + * Remove the backup/restore lock file + * + * @return TRUE on success, FALSE on failure + */ +gboolean mce_unlock_backup(void) +{ + return (g_unlink(MCE_BACKUP_LOCK_FILE_PATH) == 0); +} diff --git a/mce-io.h b/mce-io.h new file mode 100644 index 00000000..967db1b3 --- /dev/null +++ b/mce-io.h @@ -0,0 +1,79 @@ +/** + * @file mce-io.h + * Headers for the generic I/O functionality for the Mode Control Entity + *

+ * Copyright © 2007, 2009-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _MCE_IO_H_ +#define _MCE_IO_H_ + +#include + +#include /* FILE * */ + +/** Error policies for mce-io */ +typedef enum { + /** Exit on error */ + MCE_IO_ERROR_POLICY_EXIT, + /** Warn about errors */ + MCE_IO_ERROR_POLICY_WARN, + /** Silently ignore errors */ + MCE_IO_ERROR_POLICY_IGNORE +} error_policy_t; + +/** Function pointer for I/O monitor callback */ +typedef void (*iomon_cb)(gpointer data, gsize bytes_read); + +gboolean mce_close_file(const gchar *const file, FILE **fp); +gboolean mce_read_chunk_from_file(const gchar *const file, void **data, + gssize *len, int flags, int fd); +gboolean mce_read_string_from_file(const gchar *const file, gchar **string); +gboolean mce_read_number_string_from_file(const gchar *const file, + gulong *number, FILE **fp, + gboolean rewind, + gboolean close_on_exit); +gboolean mce_write_string_to_file(const gchar *const file, + const gchar *const string); +gboolean mce_write_number_string_to_file(const gchar *const file, + const gulong number, FILE **fp, + gboolean truncate_file, + gboolean close_on_exit); +gboolean mce_write_number_string_to_file_atomic(const gchar *const file, + const gulong number); +void mce_suspend_io_monitor(gconstpointer io_monitor); +void mce_resume_io_monitor(gconstpointer io_monitor); +gconstpointer mce_register_io_monitor_string(const gint fd, + const gchar *const file, + error_policy_t error_policy, + GIOCondition monitored_conditions, + gboolean rewind_policy, + iomon_cb callback); +gconstpointer mce_register_io_monitor_chunk(const gint fd, + const gchar *const file, + error_policy_t error_policy, + GIOCondition monitored_conditions, + gboolean rewind_policy, + iomon_cb callback, + gulong chunk_size); +void mce_unregister_io_monitor(gconstpointer io_monitor); +const gchar *mce_get_io_monitor_name(gconstpointer io_monitor); +int mce_get_io_monitor_fd(gconstpointer io_monitor); + +gboolean mce_is_backup_pending(void); +gboolean mce_unlock_backup(void); + +#endif /* _MCE_IO_H_ */ diff --git a/mce-lib.c b/mce-lib.c new file mode 100644 index 00000000..4a19b067 --- /dev/null +++ b/mce-lib.c @@ -0,0 +1,336 @@ +/** + * @file mce-lib.c + * This file provides various helper functions + * for the Mode Control Entity + *

+ * Copyright © 2004-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include + +#include /* sscanf() */ +#include /* strcmp() */ + +#include "mce.h" /* MCE_INVALID_TRANSLATION */ +#include "mce-lib.h" /* mce_translation_t */ + +#include "mce-log.h" /* mce_log(), LL_* */ + +/** + * Set a bit + * + * @param bit The bit to set + * @param bitfield A pointer to an array with the bitfield + */ +void set_bit(guint bit, gulong **bitfield) +{ + if ((bitfield == NULL) || (*bitfield == NULL)) + goto EXIT; + + (*bitfield)[bit / bitsize_of(**bitfield)] |= 1UL << (bit % bitsize_of(**bitfield)); + +EXIT: + return; +} + +/** + * Clear a bit + * + * @param bit The bit to clear + * @param bitfield A pointer to an array with the bitfield + */ +void clear_bit(guint bit, gulong **bitfield) +{ + if ((bitfield == NULL) || (*bitfield == NULL)) + goto EXIT; + + (*bitfield)[bit / bitsize_of(**bitfield)] &= ~(1UL << (bit % bitsize_of(**bitfield))); + +EXIT: + return; +} + +/** + * Test whether a bit is set + * + * @param bit The bit to test for + * @param bitfield An array with the bitfield + * @return TRUE if the bit is set, + * FALSE if the bit is unset + */ +gboolean test_bit(guint bit, const gulong *bitfield) +{ + return ((1UL << (bit % bitsize_of(*bitfield))) & + (((gulong *)bitfield)[bit / bitsize_of(*bitfield)])) != 0; +} + +/** + * Convert a string to a bitfield + * + * @param string The string with comma-separated numbers + * to turn into a bitfield + * @param[in,out] bitfield A bitfield to return the string in + * @param bitfieldsize The size of the bitfield + * @return TRUE on success, + * FALSE if the string could not be parsed numerically + * or if a number was out of range for the bitfield + */ +gboolean string_to_bitfield(const gchar *string, + gulong **bitfield, gsize bitfieldsize) +{ + gchar *tmp = (gchar *)string; + gboolean status = FALSE; + int offset = 0; + guint num; + + if ((string == NULL) || (bitfield == NULL) || (*bitfield == NULL)) + goto EXIT; + + while ((sscanf(tmp, "%u%n", &num, &offset) != 0) && (offset != 0)) { + /* Make sure we can represent this number */ + if (num > (bitfieldsize * bitsize_of(**bitfield))) + goto EXIT; + + set_bit(num, bitfield); + tmp += (offset + 1); + offset = 0; + } + + status = TRUE; + +EXIT: + return status; +} + +/** + * Convert a bitfield to a string + * + * @param bitfield The bitfield to convert to a comma-separated string + * with the numbers of the set bits + * @param bitfieldsize The size of the bitfield + * @return A string with the newly allocated string on success, + * NULL on failure + */ +char *bitfield_to_string(const gulong *bitfield, gsize bitfieldsize) +{ + gchar *tmp = NULL; + guint i, j; + + /* Always pass 0; this way a NULL string represents failure, + * and a string with no bits set will represent an empty mask; + * we also simplify the g_strdup_printf() case quite a bit + */ + if ((tmp = strdup("0")) == NULL) { + mce_log(LL_CRIT, + "Failed to allocate memory " + "for tmp"); + goto EXIT; + } + + for (i = 0; i < bitfieldsize; i++) { + for (j = 0; bitfield[i] && j < bitsize_of(*bitfield); j++) { + if (bitfield[i] & (1UL << j)) { + gchar *tmp2; + + tmp2 = g_strdup_printf("%s,%u", + tmp, (i * bitsize_of(*bitfield)) + j); + + g_free(tmp); + + if (tmp2 == NULL) { + mce_log(LL_CRIT, + "Failed to allocate memory " + "for tmp2"); + goto EXIT; + } + + tmp = tmp2; + } + } + } + +EXIT: + return tmp; +} + +/** + * Convert a value to a binary string (9-bit, since it's for Lysti) + * FIXME: convert to handle arbitrary length instead and make reentrant + * @note This function is non-reentrant; it returns a fixed sized, + * statically allocated, string that should not be freed + * + * @param bin The value to convert to a binary string + * @return A static string with a representation the value + */ +const gchar *bin_to_string(guint bin) +{ + static gchar bin_string[] = "000000000"; + gint i; + + for (i = 0; i < 9; i++) { + bin_string[8 - i] = (bin & (1 << i)) ? '1' : '0'; + } + + return bin_string; +} + +/** + * Translate an integer to its string representation; + * if no valid mapping exists, return the provided default string + * (if one has been provided) + * + * @param translation A mce_translation_t mapping + * @param number The number to map to a string + * @param default_string The default string to return if no match is found + * @return A string translation of the integer + */ +const gchar *mce_translate_int_to_string_with_default(const mce_translation_t translation[], gint number, const gchar *default_string) +{ + const gchar *string; + gint i = 0; + + /* This might seem awkward, but it's made to allow sparse + * number spaces + */ + do { + string = translation[i].string; + } while (translation[i].number != MCE_INVALID_TRANSLATION && + translation[i++].number != number); + + /* XXX: will this really behave correctly if there's only + * one (MCE_INVALID_TRANSLATION) element in the structure? + */ + if ((translation[i].number == MCE_INVALID_TRANSLATION) && + (translation[i - 1].number != number) && + (default_string != NULL)) + string = default_string; + + return string; +} + +/** + * Translate an integer to its string representation + * + * @param translation A mce_translation_t mapping + * @param number The number to map to a string + * @return A string translation of the integer + */ +const gchar *mce_translate_int_to_string(const mce_translation_t translation[], + gint number) +{ + return mce_translate_int_to_string_with_default(translation, number, NULL); +} + +/** + * Translate a string to its integer representation + * if no valid mapping exists, return the provided default integer + * (if one has been provided) + * + * @param translation A mce_translation_t mapping + * @param string The string to map to an number + * @param default_integer The number to return if no match is found + * @return An integer translation value of the string + */ +gint mce_translate_string_to_int_with_default(const mce_translation_t translation[], const gchar *const string, gint default_integer) +{ + gint number = MCE_INVALID_TRANSLATION; + gint i = 0; + + while (translation[i].number != MCE_INVALID_TRANSLATION) { + /* If the string matches, set number and stop searching */ + if (strcmp(translation[i].string, string) == 0) { + number = translation[i].number; + break; + } + + i++; + } + + if (translation[i].number == MCE_INVALID_TRANSLATION) + number = default_integer; + + return number; +} +/** + * Translate a string to its integer representation + * + * @param translation A mce_translation_t mapping + * @param string The string to map to an number + * @return An integer translation value of the string + */ +gint mce_translate_string_to_int(const mce_translation_t translation[], + const gchar *const string) +{ + return mce_translate_string_to_int_with_default(translation, string, MCE_INVALID_TRANSLATION); +} + +/** + * Locate a delimited substring + * + * @param haystack The string to search in + * @param needle The string to search for + * @param delimiter The delimiter + * @return A pointer to the position of the substring on match, + * NULL if no match found + */ +gchar *strstr_delim(const gchar *const haystack, const char *needle, + const char *const delimiter) +{ + char *match = NULL; + const char *tmp2; + size_t dlen; + + if ((haystack == NULL) || (needle == NULL)) + return NULL; + + /* If there's no delimiter, we'll behave as strstr */ + dlen = (delimiter == NULL) ? 0 : strlen(delimiter); + + tmp2 = haystack; + + while (tmp2 != NULL) { + const char *tmp; + ptrdiff_t len; + + /* Find the first occurence of the delimiter */ + if (dlen != 0) + tmp = strstr(tmp2, delimiter); + else + tmp = NULL; + + /* If there's a delimiter, match up to it, + * if not, match the entire remaining string + */ + if (tmp != NULL) + len = tmp - tmp2; + else + len = strlen(tmp2); + + /* If we find a match, we're done */ + if ((match = g_strstr_len(tmp2, len, needle)) != NULL) + goto EXIT; + + /* If there's no more delimiters, we're done */ + if (len == 0) + goto EXIT; + + /* Skip past the current token + the delimiter */ + tmp2 += (len + dlen); + }; + +EXIT: + return match; +} diff --git a/mce-lib.h b/mce-lib.h new file mode 100644 index 00000000..c2ff4d6d --- /dev/null +++ b/mce-lib.h @@ -0,0 +1,58 @@ +/** + * @file mce-lib.h + * Headers for various helper functions + * for the Mode Control Entity + *

+ * Copyright © 2004-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _MCE_LIB_H_ +#define _MCE_LIB_H_ + +#include + +/** Find the number of bits of a type */ +#define bitsize_of(__x) (guint)(sizeof (__x) * 8) + +/** translation structure */ +typedef struct { + const gint number; /**< Number representation */ + const gchar *const string; /**< String representation */ +} mce_translation_t; + +void set_bit(guint bit, gulong **bitfield); +void clear_bit(guint bit, gulong **bitfield); +gboolean test_bit(guint bit, const gulong *bitfield); + +gboolean string_to_bitfield(const gchar *string, + gulong **bitfield, gsize bitfieldsize); +char *bitfield_to_string(const gulong *bitfield, gsize bitfieldsize); + +const gchar *bin_to_string(guint bin); + +const gchar *mce_translate_int_to_string_with_default(const mce_translation_t translation[], gint number, const gchar *default_string); +const gchar *mce_translate_int_to_string(const mce_translation_t translation[], + gint number); + +gint mce_translate_string_to_int_with_default(const mce_translation_t translation[], const gchar *const string, gint default_number); +gint mce_translate_string_to_int(const mce_translation_t translation[], + const gchar *const string); + +gchar *strstr_delim(const gchar *const haystack, const char *needle, + const char *const delimiter); + + +#endif /* _MCE_LIB_H_ */ diff --git a/mce-log.c b/mce-log.c new file mode 100644 index 00000000..6d8e0b50 --- /dev/null +++ b/mce-log.c @@ -0,0 +1,142 @@ +/** + * @file mce-log.c + * Logging functions for Mode Control Entity + *

+ * Copyright © 2006-2007, 2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include +#include + +#ifdef OSSOLOG_COMPILE +#include /* fprintf() */ +#include /* va_start(), va_end(), vfprintf() */ +#include /* strdup() */ +#include /* openlog(), closelog(), vsyslog() */ + +#include "mce-log.h" + +static unsigned int logverbosity = LL_WARN; /**< Log verbosity */ +static int logtype = MCE_LOG_STDERR; /**< Output for log messages */ +static char *logname = NULL; + +/** + * Log debug message with optional filename and function name attached + * + * @param loglevel The level of severity for this message + * @param fmt The format string for this message + * @param ... Input to the format string + */ +void mce_log_file(const loglevel_t loglevel, const char *const file, + const char *const function, const char *const fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + if (logverbosity >= loglevel) { + gchar *tmp; + gchar *msg; + + g_vasprintf(&tmp, fmt, args); + + if ((file != NULL) && (function != NULL)) { + msg = g_strconcat(file, ":", + function, "(): ", + tmp, NULL); + } else { + msg = g_strdup(tmp); + } + + g_free(tmp); + + if (logtype == MCE_LOG_STDERR) { + fprintf(stderr, "%s: %s\n", logname, msg); + } else { + int priority; + + switch (loglevel) { + case LL_DEBUG: + priority = LOG_DEBUG; + break; + + case LL_ERR: + priority = LOG_ERR; + break; + + case LL_CRIT: + priority = LOG_CRIT; + break; + + case LL_INFO: + priority = LOG_INFO; + break; + + case LL_WARN: + default: + priority = LOG_WARNING; + break; + } + + syslog(priority, "%s", msg); + } + + g_free(msg); + } + + va_end(args); +} + +/** + * Set log verbosity + * messages with loglevel higher than or equal to verbosity will be logged + * + * @param verbosity minimum level for log level + */ +void mce_log_set_verbosity(const int verbosity) +{ + logverbosity = verbosity; +} + +/** + * Open log + * + * @param name identifier to use for log messages + * @param facility the log facility; normally LOG_USER or LOG_DAEMON + * @param type log type to use; MCE_LOG_STDERR or MCE_LOG_SYSLOG + */ +void mce_log_open(const char *const name, const int facility, const int type) +{ + logtype = type; + + if (logtype == MCE_LOG_SYSLOG) + openlog(name, LOG_PID | LOG_NDELAY, facility); + else + logname = g_strdup(name); +} + +/** + * Close log + */ +void mce_log_close(void) +{ + if (logname) + g_free(logname); + + if (logtype == MCE_LOG_SYSLOG) + closelog(); +} +#endif /* OSSOLOG_COMPILE */ diff --git a/mce-log.h b/mce-log.h new file mode 100644 index 00000000..f2d06760 --- /dev/null +++ b/mce-log.h @@ -0,0 +1,60 @@ +/** + * @file mce-log.h + * Headers for the logging functions for Mode Control Entity + *

+ * Copyright © 2006-2007, 2009-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _MCE_LOG_H_ +#define _MCE_LOG_H_ + +#include /* LOG_DAEMON, LOG_USER */ + +#define MCE_LOG_SYSLOG 1 /**< Log to syslog */ +#define MCE_LOG_STDERR 0 /**< Log to stderr */ + +/** Severity of loglevels */ +typedef enum { + LL_NONE = 0, /**< No logging at all */ + LL_CRIT = 1, /**< Critical error */ + LL_ERR = 2, /**< Error */ + LL_WARN = 3, /**< Warning */ + LL_DEFAULT = LL_WARN, /**< Default log level */ + LL_INFO = 4, /**< Informational message */ + LL_DEBUG = 5 /**< Useful when debugging */ +} loglevel_t; + +#ifdef OSSOLOG_COMPILE +void mce_log_file(const loglevel_t loglevel, const char *const file, + const char *const function, const char *const fmt, ...) + __attribute__((format(printf, 4, 5))); +#define mce_log_raw(__loglevel, __fmt, __args...) mce_log_file(__loglevel, NULL, NULL, __fmt , ## __args) +#define mce_log(__loglevel, __fmt, __args...) mce_log_file(__loglevel, __FILE__, __FUNCTION__, __fmt , ## __args) +void mce_log_set_verbosity(const int verbosity); +void mce_log_open(const char *const name, const int facility, const int type); +void mce_log_close(void); +#else +/** Dummy version used when logging is disabled at compile time */ +#define mce_log(_loglevel, _fmt, ...) do {} while (0) +/** Dummy version used when logging is disabled at compile time */ +#define mce_log_set_verbosity(_verbosity) do {} while (0) +/** Dummy version used when logging is disabled at compile time */ +#define mce_log_open(_name, _facility, _type) do {} while (0) +/** Dummy version used when logging is disabled at compile time */ +#define mce_log_close() do {} while (0) +#endif /* OSSOLOG_COMPILE */ + +#endif /* _MCE_LOG_H_ */ diff --git a/mce-modules.c b/mce-modules.c new file mode 100644 index 00000000..fe04c71d --- /dev/null +++ b/mce-modules.c @@ -0,0 +1,217 @@ +/** + * @file mce-modules.c + * Module handling for MCE + *

+ * Copyright © 2007-2009 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include +#include + +#include /* fprintf(), stdout */ +#include /* strcmp() */ + +#include "mce.h" /* module_info_struct */ +#include "mce-modules.h" + +#include "mce-log.h" /* mce_log(), LL_* */ +#include "mce-conf.h" /* mce_conf_get_string(), + * mce_conf_get_string_list() + */ + +/** List of all loaded modules */ +static GSList *modules = NULL; + +/** + * Dump information about mce modules to stdout + */ +void mce_modules_dump_info(void) +{ + GModule *module; + gint i; + + for (i = 0; (module = g_slist_nth_data(modules, i)) != NULL; i++) { + const gchar *modulename = g_module_name(module); + module_info_struct *modinfo; + gchar *tmp = NULL; + gpointer mip; + + fprintf(stdout, + _("\n" + "Module: %s\n"), + modulename); + + if (g_module_symbol(module, + "module_info", + &mip) == FALSE) { + fprintf(stdout, + " %-32s\n", + "module lacks information"); + continue; + } + + modinfo = (module_info_struct *)mip; + + fprintf(stdout, + " %-32s %s\n", + _("name:"), + modinfo->name ? modinfo->name : _("")); + + if (modinfo->depends != NULL) + tmp = g_strjoinv(",", (gchar **)(modinfo->depends)); + + fprintf(stdout, + " %-32s %s\n", + _("depends:"), + tmp ? tmp : ""); + + g_free(tmp); + tmp = NULL; + + if (modinfo->recommends != NULL) + tmp = g_strjoinv(",", (gchar **)(modinfo->recommends)); + + fprintf(stdout, + " %-32s %s\n", + _("recommends:"), + tmp ? tmp : ""); + + g_free(tmp); + tmp = NULL; + + if (modinfo->provides != NULL) + tmp = g_strjoinv(",", (gchar **)(modinfo->provides)); + + fprintf(stdout, + " %-32s %s\n", + _("provides:"), + tmp ? tmp : ""); + + g_free(tmp); + tmp = NULL; + + if (modinfo->enhances != NULL) + tmp = g_strjoinv(",", (gchar **)(modinfo->enhances)); + + fprintf(stdout, + " %-32s %s\n", + _("enhances:"), + tmp ? tmp : ""); + + g_free(tmp); + tmp = NULL; + + if (modinfo->conflicts != NULL) + tmp = g_strjoinv(",", (gchar **)(modinfo->conflicts)); + + fprintf(stdout, + " %-32s %s\n", + _("conflicts:"), + tmp ? tmp : ""); + + g_free(tmp); + tmp = NULL; + + if (modinfo->replaces != NULL) + tmp = g_strjoinv(",", (gchar **)(modinfo->replaces)); + + fprintf(stdout, + " %-32s %s\n", + _("replaces:"), + tmp ? tmp : ""); + + g_free(tmp); + + fprintf(stdout, + " %-32s %d\n", + _("priority:"), + modinfo->priority); + } +} + +/** + * Init function for the mce-modules component + * + * @return TRUE on success, FALSE on failure + */ +gboolean mce_modules_init(void) +{ + gchar **modlist = NULL; + gsize length; + gchar *path = NULL; + + /* Get the module path */ + path = mce_conf_get_string(MCE_CONF_MODULES_GROUP, + MCE_CONF_MODULES_PATH, + DEFAULT_MCE_MODULE_PATH, + NULL); + + /* Get the list modules to load */ + modlist = mce_conf_get_string_list(MCE_CONF_MODULES_GROUP, + MCE_CONF_MODULES_MODULES, + &length, + NULL); + + if (modlist != NULL) { + gint i; + + for (i = 0; modlist[i]; i++) { + GModule *module; + gchar *tmp = g_module_build_path(path, modlist[i]); + + mce_log(LL_DEBUG, + "Loading module: %s from %s", + modlist[i], path); + + if ((module = g_module_open(tmp, 0)) != NULL) { + /* XXX: check dependencies, conflicts, et al */ + modules = g_slist_prepend(modules, module); + } else { + mce_log(LL_DEBUG, + "Failed to load module: %s; skipping", + modlist[i]); + } + + g_free(tmp); + } + + g_strfreev(modlist); + } + + g_free(path); + + return TRUE; +} + +/** + * Exit function for the mce-modules component + */ +void mce_modules_exit(void) +{ + GModule *module; + gint i; + + if (modules != NULL) { + for (i = 0; (module = g_slist_nth_data(modules, i)) != NULL; i++) { + g_module_close(module); + } + + g_slist_free(modules); + modules = NULL; + } + + return; +} diff --git a/mce-modules.h b/mce-modules.h new file mode 100644 index 00000000..4fa2ea89 --- /dev/null +++ b/mce-modules.h @@ -0,0 +1,42 @@ +/** + * @file mce-modules.h + * Headers for the module handling for MCE + *

+ * Copyright © 2007 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _MCE_MODULES_H_ +#define _MCE_MODULES_H_ + +#include + +/** Name of Modules configuration group */ +#define MCE_CONF_MODULES_GROUP "Modules" + +/** Name of configuration key for module path */ +#define MCE_CONF_MODULES_PATH "ModulePath" + +/** Name of configuration key for modules to load */ +#define MCE_CONF_MODULES_MODULES "Modules" + +/** Default value for module path */ +#define DEFAULT_MCE_MODULE_PATH "/usr/lib/mce/modules" + +void mce_modules_dump_info(void); +gboolean mce_modules_init(void); +void mce_modules_exit(void); + +#endif /* _MCE_MODULES_H_ */ diff --git a/mce-restore b/mce-restore new file mode 100644 index 00000000..bad467a1 --- /dev/null +++ b/mce-restore @@ -0,0 +1,12 @@ +#! /bin/sh + +(touch /var/lib/mce/restored) && (cp $HOME/.mce/* /var/lib/mce/) +status=$? + +if [ $status -ne 0 ]; then + status=2 +else + rm -rf $HOME/.mce +fi + +exit $status diff --git a/mce.c b/mce.c new file mode 100644 index 00000000..f2e9963f --- /dev/null +++ b/mce.c @@ -0,0 +1,765 @@ +/** + * @file mce.c + * Mode Control Entity - main file + *

+ * Copyright © 2004-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include +#include /* g_type_init() */ + +#include /* errno, ENOMEM */ +#include /* open(), O_RDWR, O_CREAT */ +#include /* fprintf(), sprintf(), + * stdout, stderr + */ +#include /* getopt_long(), + * struct options + */ +#include /* signal(), + * SIGTSTP, SIGTTOU, SIGTTIN, + * SIGCHLD, SIGUSR1, SIGHUP, + * SIGTERM, SIG_IGN + */ +#include /* exit(), EXIT_FAILURE, EXIT_SUCCESS */ +#include /* strlen() */ +#include /* close(), lockf(), fork(), chdir(), + * getpid(), getppid(), setsid(), + * write(), getdtablesize(), dup(), + * F_TLOCK + */ +#include /* umask() */ + +#include "mce.h" /* _(), + * setlocale() -- indirect, + * bindtextdomain(), + * textdomain(), + * mainloop, + * system_state_pipe, + * master_radio_pipe, + * call_state_pipe, + * call_type_pipe, + * submode_pipe, + * display_state_pipe, + * display_brightness_pipe, + * led_brightness_pipe, + * led_pattern_activate_pipe, + * led_pattern_deactivate_pipe, + * key_backlight_pipe, + * keypress_pipe, + * touchscreen_pipe, + * device_inactive_pipe, + * lockkey_pipe, + * keyboard_slide_pipe, + * lid_cover_pipe, + * lens_cover_pipe, + * proximity_sensor_pipe, + * tk_lock_pipe, + * charger_state_pipe, + * battery_status_pipe, + * battery_level_pipe, + * inactivity_timeout_pipe, + * audio_route_pipe, + * usb_cable_pipe, + * jack_sense_pipe, + * power_saving_mode_pipe, + * thermal_state_pipe, + * MCE_STATE_UNDEF, + * MCE_INVALID_MODE_INT32, + * CALL_STATE_NONE, + * NORMAL_CALL, + * MCE_ALARM_UI_INVALID_INT32, + * MCE_NORMAL_SUBMODE, + * MCE_DISPLAY_UNDEF, + * LOCK_UNDEF, + * BATTERY_STATUS_UNDEF, + * THERMAL_STATE_UNDEF, + * DEFAULT_INACTIVITY_TIMEOUT + */ + +#include "mce-log.h" /* mce_log_open(), mce_log_close(), + * mce_log_set_verbosity(), mce_log(), + * LL_* + */ +#include "mce-conf.h" /* mce_conf_init(), + * mce_conf_exit() + */ +#include "mce-dbus.h" /* mce_dbus_init(), + * mce_dbus_exit() + */ +#include "mce-dsme.h" /* mce_dsme_init(), + * mce_dsme_exit() + */ +#include "mce-gconf.h" /* mce_gconf_init(), + * mce_gconf_exit() + */ +#include "mce-modules.h" /* mce_modules_dump_info(), + * mce_modules_init(), + * mce_modules_exit() + */ +#include "event-input.h" /* mce_input_init(), + * mce_input_exit() + */ +#include "event-switches.h" /* mce_switches_init(), + * mce_switches_exit() + */ +#include "connectivity.h" /* mce_connectivity_init(), + * mce_connectivity_exit() + */ +#include "datapipe.h" /* setup_datapipe(), + * free_datapipe() + */ +#include "modetransition.h" /* mce_mode_init(), + * mce_mode_exit() + */ + +/* "TBD" Modules; eventually this should be handled differently */ +#include "tklock.h" /* mce_tklock_init(), + * mce_tklock_exit() + */ +#include "powerkey.h" /* mce_powerkey_init(), + * mce_powerkey_exit() + */ + +/** Path to the lockfile */ +#define MCE_LOCKFILE "/var/run/mce.pid" +/** Name shown by --help etc. */ +#define PRG_NAME "mce" + +extern int optind; /**< Used by getopt */ +extern char *optarg; /**< Used by getopt */ + +static const gchar *progname; /**< Used to store the name of the program */ + +/** + * Display usage information + */ +static void usage(void) +{ + fprintf(stdout, + _("Usage: %s [OPTION]...\n" + "Mode Control Entity\n" + "\n" + " -d, --daemonflag run MCE as a daemon\n" + " --force-syslog log to syslog even when not " + "daemonized\n" + " --force-stderr log to stderr even when " + "daemonized\n" + " -S, --session use the session bus instead\n" + " of the " + "system bus for D-Bus\n" + " --show-module-info show information about " + "loaded modules\n" + " --debug-mode run even if dsme fails\n" + " --quiet decrease debug message " + "verbosity\n" + " --verbose increase debug message " + "verbosity\n" + " --help display this help and exit\n" + " --version output version information " + "and exit\n" + "\n" + "Report bugs to \n"), + progname); +} + +/** + * Display version information + */ +static void version(void) +{ + fprintf(stdout, _("%s v%s\n%s"), + progname, + G_STRINGIFY(PRG_VERSION), + _("Written by David Weinehall.\n" + "\n" + "Copyright (C) 2004-2010 Nokia Corporation. " + "All rights reserved.\n")); +} + +/** + * Initialise locale support + * + * @param name The program name to output in usage/version information + * @return 0 on success, non-zero on failure + */ +static gint init_locales(const gchar *const name) +{ + gint status = 0; + +#ifdef ENABLE_NLS + setlocale(LC_ALL, ""); + + if ((bindtextdomain(name, LOCALEDIR) == 0) && (errno == ENOMEM)) { + status = errno; + goto EXIT; + } + + if ((textdomain(name) == 0) && (errno == ENOMEM)) { + status = errno; + goto EXIT; + } + +EXIT: + /* In this error-message we don't use _(), since we don't + * know where the locales failed, and we probably won't + * get a reasonable result if we try to use them. + */ + if (status != 0) { + fprintf(stderr, + "%s: `%s' failed; %s. Aborting.\n", + name, "init_locales", g_strerror(status)); + } + + if (errno != ENOMEM) + errno = 0; +#endif /* ENABLE_NLS */ + progname = name; + + return status; +} + +/** + * Signal handler + * + * @param signr Signal type + */ +static void signal_handler(const gint signr) +{ + switch (signr) { + case SIGUSR1: + /* We'll probably want some way to communicate with MCE */ + break; + + case SIGHUP: + /* Possibly for re-reading configuration? */ + break; + + case SIGTERM: + /* This should be done through a pipe or signalfd instead */ + g_main_loop_quit(mainloop); + break; + + default: + /* Should never happen */ + break; + } +} + +/** + * Daemonize the program + * + * @return TRUE if MCE is started during boot, FALSE otherwise + */ +static gboolean daemonize(void) +{ + gint retries = 0; + gint i = 0; + gchar str[10]; + + if (getppid() == 1) + goto EXIT; /* Already daemonized */ + + /* Detach from process group */ + switch (fork()) { + case -1: + /* Failure */ + mce_log(LL_CRIT, "daemonize: fork failed: %s", + g_strerror(errno)); + mce_log_close(); + exit(EXIT_FAILURE); + + case 0: + /* Child */ + break; + + default: + /* Parent -- exit */ + exit(EXIT_SUCCESS); + } + + /* Detach TTY */ + setsid(); + + /* Close all file descriptors and redirect stdio to /dev/null */ + if ((i = getdtablesize()) == -1) + i = 256; + + while (--i >= 0) { + if (close(i) == -1) { + if (retries > 10) { + mce_log(LL_CRIT, + "close() was interrupted more than " + "10 times. Exiting."); + mce_log_close(); + exit(EXIT_FAILURE); + } + + if (errno == EINTR) { + mce_log(LL_INFO, + "close() was interrupted; retrying."); + errno = 0; + i++; + retries++; + } else if (errno == EBADF) { + mce_log(LL_ERR, + "Failed to close() fd %d; %s. " + "Ignoring.", + i + 1, g_strerror(errno)); + errno = 0; + } else { + mce_log(LL_CRIT, + "Failed to close() fd %d; %s. " + "Exiting.", + i + 1, g_strerror(errno)); + mce_log_close(); + exit(EXIT_FAILURE); + } + } else { + retries = 0; + } + } + + if ((i = open("/dev/null", O_RDWR)) == -1) { + mce_log(LL_CRIT, + "Cannot open `/dev/null'; %s. Exiting.", + g_strerror(errno)); + mce_log_close(); + exit(EXIT_FAILURE); + } + + if ((dup(i) == -1)) { + mce_log(LL_CRIT, + "Failed to dup() `/dev/null'; %s. Exiting.", + g_strerror(errno)); + mce_log_close(); + exit(EXIT_FAILURE); + } + + if ((dup(i) == -1)) { + mce_log(LL_CRIT, + "Failed to dup() `/dev/null'; %s. Exiting.", + g_strerror(errno)); + mce_log_close(); + exit(EXIT_FAILURE); + } + + /* Set umask */ + umask(022); + + /* Set working directory */ + if ((chdir("/tmp") == -1)) { + mce_log(LL_CRIT, + "Failed to chdir() to `/tmp'; %s. Exiting.", + g_strerror(errno)); + mce_log_close(); + exit(EXIT_FAILURE); + } + + /* Single instance */ + if ((i = open(MCE_LOCKFILE, O_RDWR | O_CREAT, 0640)) == -1) { + mce_log(LL_CRIT, + "Cannot open lockfile; %s. Exiting.", + g_strerror(errno)); + mce_log_close(); + exit(EXIT_FAILURE); + } + + if (lockf(i, F_TLOCK, 0) == -1) { + mce_log(LL_CRIT, "Already running. Exiting."); + mce_log_close(); + exit(EXIT_FAILURE); + } + + sprintf(str, "%d\n", getpid()); + write(i, str, strlen(str)); + close(i); + + /* Ignore TTY signals */ + signal(SIGTSTP, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + signal(SIGTTIN, SIG_IGN); + + /* Ignore child terminate signal */ + signal(SIGCHLD, SIG_IGN); + +EXIT: + return 0; +} + +/** + * Main + * + * @param argc Number of command line arguments + * @param argv Array with command line arguments + * @return 0 on success, non-zero on failure + */ +int main(int argc, char **argv) +{ + int optc; + int opt_index; + + int verbosity = LL_DEFAULT; + int logtype = -1; + + gint status = 0; + gboolean show_module_info = FALSE; + gboolean daemonflag = FALSE; + gboolean systembus = TRUE; + gboolean debugmode = FALSE; + + const char optline[] = "dS"; + + struct option const options[] = { + { "daemonflag", no_argument, 0, 'd' }, + { "force-syslog", no_argument, 0, 's' }, + { "force-stderr", no_argument, 0, 'T' }, + { "session", no_argument, 0, 'S' }, + { "show-module-info", no_argument, 0, 'M' }, + { "debug-mode", no_argument, 0, 'D' }, + { "quiet", no_argument, 0, 'q' }, + { "verbose", no_argument, 0, 'v' }, + { "help", no_argument, 0, 'h' }, + { "version", no_argument, 0, 'V' }, + { 0, 0, 0, 0 } + }; + + /* NULL the mainloop */ + mainloop = NULL; + + /* Initialise support for locales, and set the program-name */ + if (init_locales(PRG_NAME) != 0) + goto EXIT; + + /* Parse the command-line options */ + while ((optc = getopt_long(argc, argv, optline, + options, &opt_index)) != -1) { + switch (optc) { + case 'd': + daemonflag = TRUE; + break; + + case 's': + if (logtype != -1) { + usage(); + status = EINVAL; + goto EXIT; + } + + logtype = MCE_LOG_SYSLOG; + break; + + case 'T': + if (logtype != -1) { + usage(); + status = EINVAL; + goto EXIT; + } + + logtype = MCE_LOG_STDERR; + break; + + case 'S': + systembus = FALSE; + break; + + case 'M': + show_module_info = TRUE; + break; + + case 'D': + debugmode = TRUE; + break; + + case 'q': + if (verbosity > LL_NONE) + verbosity--; + break; + + case 'v': + if (verbosity < LL_DEBUG) + verbosity++; + break; + + case 'h': + usage(); + goto EXIT; + + case 'V': + version(); + goto EXIT; + + default: + usage(); + status = EINVAL; + goto EXIT; + } + } + + /* We don't take any non-flag arguments */ + if ((argc - optind) > 0) { + fprintf(stderr, + _("%s: Too many arguments\n" + "Try: `%s --help' for more information.\n"), + progname, progname); + status = EINVAL; + goto EXIT; + } + + if (logtype == -1) + logtype = (daemonflag == TRUE) ? MCE_LOG_SYSLOG : + MCE_LOG_STDERR; + + mce_log_open(PRG_NAME, LOG_DAEMON, logtype); + mce_log_set_verbosity(verbosity); + + /* Daemonize if requested */ + if (daemonflag == TRUE) + daemonize(); + + signal(SIGUSR1, signal_handler); + signal(SIGHUP, signal_handler); + signal(SIGTERM, signal_handler); + + /* Initialise GType system */ + g_type_init(); + + /* Register a mainloop */ + mainloop = g_main_loop_new(NULL, FALSE); + + /* Initialise subsystems */ + + /* Get configuration options */ + /* ignore errors; this way the defaults will be used if + * the configuration file is invalid or unavailable + */ + (void)mce_conf_init(); + + /* Initialise D-Bus */ + if (mce_dbus_init(systembus) == FALSE) { + mce_log(LL_CRIT, + "Failed to initialise D-Bus"); + mce_log_close(); + exit(EXIT_FAILURE); + } + + /* Initialise GConf + * pre-requisite: g_type_init() + */ + if (mce_gconf_init() == FALSE) { + mce_log(LL_CRIT, + "Cannot connect to default GConf engine"); + mce_log_close(); + exit(EXIT_FAILURE); + } + + /* Setup all datapipes */ + setup_datapipe(&system_state_pipe, READ_WRITE, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(MCE_STATE_UNDEF)); + setup_datapipe(&master_radio_pipe, READ_WRITE, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(0)); + setup_datapipe(&call_state_pipe, READ_WRITE, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(CALL_STATE_NONE)); + setup_datapipe(&call_type_pipe, READ_WRITE, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(NORMAL_CALL)); + setup_datapipe(&alarm_ui_state_pipe, READ_ONLY, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(MCE_ALARM_UI_INVALID_INT32)); + setup_datapipe(&submode_pipe, READ_ONLY, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(MCE_NORMAL_SUBMODE)); + setup_datapipe(&display_state_pipe, READ_ONLY, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(MCE_DISPLAY_UNDEF)); + setup_datapipe(&display_brightness_pipe, READ_WRITE, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(0)); + setup_datapipe(&led_brightness_pipe, READ_WRITE, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(0)); + setup_datapipe(&led_pattern_activate_pipe, READ_ONLY, FREE_CACHE, + 0, NULL); + setup_datapipe(&led_pattern_deactivate_pipe, READ_ONLY, FREE_CACHE, + 0, NULL); + setup_datapipe(&key_backlight_pipe, READ_WRITE, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(0)); + setup_datapipe(&keypress_pipe, READ_WRITE, FREE_CACHE, + sizeof (struct input_event), NULL); + setup_datapipe(&touchscreen_pipe, READ_ONLY, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(0)); + setup_datapipe(&device_inactive_pipe, READ_WRITE, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(FALSE)); + setup_datapipe(&lockkey_pipe, READ_ONLY, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(0)); + setup_datapipe(&keyboard_slide_pipe, READ_ONLY, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(0)); + setup_datapipe(&lid_cover_pipe, READ_ONLY, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(0)); + setup_datapipe(&lens_cover_pipe, READ_ONLY, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(0)); + setup_datapipe(&proximity_sensor_pipe, READ_ONLY, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(0)); + setup_datapipe(&tk_lock_pipe, READ_ONLY, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(LOCK_UNDEF)); + setup_datapipe(&charger_state_pipe, READ_ONLY, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(0)); + setup_datapipe(&battery_status_pipe, READ_ONLY, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(BATTERY_STATUS_UNDEF)); + setup_datapipe(&battery_level_pipe, READ_ONLY, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(100)); + setup_datapipe(&camera_button_pipe, READ_ONLY, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(CAMERA_BUTTON_UNDEF)); + setup_datapipe(&inactivity_timeout_pipe, READ_ONLY, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(DEFAULT_INACTIVITY_TIMEOUT)); + setup_datapipe(&audio_route_pipe, READ_ONLY, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(AUDIO_ROUTE_UNDEF)); + setup_datapipe(&usb_cable_pipe, READ_ONLY, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(0)); + setup_datapipe(&jack_sense_pipe, READ_ONLY, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(0)); + setup_datapipe(&power_saving_mode_pipe, READ_ONLY, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(0)); + setup_datapipe(&thermal_state_pipe, READ_ONLY, DONT_FREE_CACHE, + 0, GINT_TO_POINTER(THERMAL_STATE_UNDEF)); + + /* Initialise connectivity monitoring + * pre-requisite: g_type_init() + */ + if (mce_connectivity_init() == FALSE) { + status = EXIT_FAILURE; + goto EXIT; + } + + /* Initialise mode management + * pre-requisite: mce_gconf_init() + * pre-requisite: mce_dbus_init() + */ + if (mce_mode_init() == FALSE) { + status = EXIT_FAILURE; + goto EXIT; + } + + /* Initialise DSME + * pre-requisite: mce_gconf_init() + * pre-requisite: mce_dbus_init() + * pre-requisite: mce_mce_init() + */ + if (mce_dsme_init(debugmode) == FALSE) { + if (debugmode == FALSE) { + mce_log(LL_CRIT, "Cannot connect to DSME"); + status = EXIT_FAILURE; + goto EXIT; + } + } + + /* Initialise powerkey driver */ + if (mce_powerkey_init() == FALSE) { + status = EXIT_FAILURE; + goto EXIT; + } + + /* Initialise /dev/input driver + * pre-requisite: g_type_init() + */ + if (mce_input_init() == FALSE) { + status = EXIT_FAILURE; + goto EXIT; + } + + /* Initialise switch driver */ + if (mce_switches_init() == FALSE) { + status = EXIT_FAILURE; + goto EXIT; + } + + /* Initialise tklock driver */ + if (mce_tklock_init() == FALSE) { + status = EXIT_FAILURE; + goto EXIT; + } + + /* Load all modules */ + if (mce_modules_init() == FALSE) { + status = EXIT_FAILURE; + goto EXIT; + } + + if (show_module_info == TRUE) { + mce_modules_dump_info(); + goto EXIT; + } + + /* Run the main loop */ + g_main_loop_run(mainloop); + + /* If we get here, the main loop has terminated; + * either because we requested or because of an error + */ +EXIT: + /* Unload all modules */ + mce_modules_exit(); + + /* Call the exit function for all components */ + mce_tklock_exit(); + mce_switches_exit(); + mce_input_exit(); + mce_powerkey_exit(); + mce_dsme_exit(); + mce_mode_exit(); + mce_connectivity_exit(); + + /* Free all datapipes */ + free_datapipe(&thermal_state_pipe); + free_datapipe(&power_saving_mode_pipe); + free_datapipe(&jack_sense_pipe); + free_datapipe(&usb_cable_pipe); + free_datapipe(&audio_route_pipe); + free_datapipe(&inactivity_timeout_pipe); + free_datapipe(&battery_level_pipe); + free_datapipe(&battery_status_pipe); + free_datapipe(&charger_state_pipe); + free_datapipe(&tk_lock_pipe); + free_datapipe(&proximity_sensor_pipe); + free_datapipe(&lens_cover_pipe); + free_datapipe(&lid_cover_pipe); + free_datapipe(&keyboard_slide_pipe); + free_datapipe(&lockkey_pipe); + free_datapipe(&device_inactive_pipe); + free_datapipe(&touchscreen_pipe); + free_datapipe(&keypress_pipe); + free_datapipe(&key_backlight_pipe); + free_datapipe(&led_pattern_deactivate_pipe); + free_datapipe(&led_pattern_activate_pipe); + free_datapipe(&led_brightness_pipe); + free_datapipe(&display_brightness_pipe); + free_datapipe(&display_state_pipe); + free_datapipe(&submode_pipe); + free_datapipe(&alarm_ui_state_pipe); + free_datapipe(&call_type_pipe); + free_datapipe(&call_state_pipe); + free_datapipe(&master_radio_pipe); + free_datapipe(&system_state_pipe); + + /* Call the exit function for all subsystems */ + mce_gconf_exit(); + mce_dbus_exit(); + mce_conf_exit(); + + /* If the mainloop is initialised, unreference it */ + if (mainloop != NULL) + g_main_loop_unref(mainloop); + + /* Log a farewell message and close the log */ + mce_log(LL_INFO, "Exiting..."); + mce_log_close(); + + return status; +} diff --git a/mce.conf b/mce.conf new file mode 100644 index 00000000..4427cbde --- /dev/null +++ b/mce.conf @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mce.h b/mce.h new file mode 100644 index 00000000..561a2241 --- /dev/null +++ b/mce.h @@ -0,0 +1,344 @@ +/** + * @file mce.h + * Generic headers for Mode Control Entity + *

+ * Copyright © 2004-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _MCE_H_ +#define _MCE_H_ + +#include +#include + +#include "datapipe.h" + +#ifdef ENABLE_NLS +#include +/** _() to use when NLS is enabled */ +#define _(__str) gettext(__str) +#else +#undef bindtextdomain +/** Dummy bindtextdomain to use when NLS is disabled */ +#define bindtextdomain(__domain, __directory) +#undef textdomain +/** Dummy textdomain to use when NLS is disabled */ +#define textdomain(__domain) +/** Dummy _() to use when NLS is disabled */ +#define _(__str) __str +#endif /* ENABLE_NLS */ + +/** Indicate enabled (sub)mode */ +#define DISABLED_STRING "yes" +/** Indicate disabled (sub)mode */ +#define ENABLED_STRING "no" + +/* Names of LED patterns */ + +/** LED pattern used when powering on the device */ +#define MCE_LED_PATTERN_POWER_ON "PatternPowerOn" +/** LED pattern used when powering off the device */ +#define MCE_LED_PATTERN_POWER_OFF "PatternPowerOff" +/** LED pattern used when camera is active */ +#define MCE_LED_PATTERN_CAMERA "PatternWebcamActive" +/** LED pattern used to indicate that the device is on when idle */ +#define MCE_LED_PATTERN_DEVICE_ON "PatternDeviceOn" +/** LED pattern used when the device is in soft poweroff mode */ +#define MCE_LED_PATTERN_DEVICE_SOFT_OFF "PatternDeviceSoftOff" +/** LED pattern used when charging the battery */ +#define MCE_LED_PATTERN_BATTERY_CHARGING "PatternBatteryCharging" +/** LED pattern used when the battery is full */ +#define MCE_LED_PATTERN_BATTERY_FULL "PatternBatteryFull" +/** LED pattern used when the battery is low */ +#define MCE_LED_PATTERN_BATTERY_LOW "PatternBatteryLow" +/** LED pattern used for communication events */ +#define MCE_LED_PATTERN_COMMUNICATION_EVENT "PatternCommunication" +/** LED pattern used for communication events when battery is full */ +#define MCE_LED_PATTERN_COMMUNICATION_EVENT_BATTERY_FULL "PatternCommunicationAndBatteryFull" + +/** Persistent lock file for backups */ +#define MCE_BACKUP_LOCK_FILE_PATH G_STRINGIFY(MCE_VAR_DIR) "/restored" + +/** Module information */ +typedef struct { +/** Name of the module */ + const gchar *const name; +/** Module dependencies */ + const gchar *const *const depends; +/** Module recommends */ + const gchar *const *const recommends; +/** Module provides */ + const gchar *const *const provides; +/** Module provides */ + const gchar *const *const enhances; +/** Module conflicts */ + const gchar *const *const conflicts; +/** Module replaces */ + const gchar *const *const replaces; +/** Module priority: + * lower value == higher priority + * This value is only used when modules conflict + */ + const gint priority; +} module_info_struct; + +/** The GMainLoop used by MCE */ +GMainLoop *mainloop; + +/** Used for invalid translations and values */ +#define MCE_INVALID_TRANSLATION -1 + +/** Alarm UI states; integer representations */ +typedef enum { + /** Alarm UI state not valid */ + MCE_ALARM_UI_INVALID_INT32 = MCE_INVALID_TRANSLATION, + /** Alarm UI not visible */ + MCE_ALARM_UI_OFF_INT32 = 0, + /** Alarm UI visible and ringing */ + MCE_ALARM_UI_RINGING_INT32 = 1, + /** Alarm UI visible but not ringing */ + MCE_ALARM_UI_VISIBLE_INT32 = 2, +} alarm_ui_state_t; + +/** System sub-modes; several of these can be active at once */ +typedef gint submode_t; + +/** Submode invalid */ +#define MCE_INVALID_SUBMODE (1 << 31) +/** No submodes enabled */ +#define MCE_NORMAL_SUBMODE 0 +/** Touchscreen/Keypad lock enabled */ +#define MCE_TKLOCK_SUBMODE (1 << 0) +/** Event eater enabled */ +#define MCE_EVEATER_SUBMODE (1 << 1) +/** Device emulates soft poweroff */ +#define MCE_SOFTOFF_SUBMODE (1 << 2) +/** Bootup in progress */ +#define MCE_BOOTUP_SUBMODE (1 << 3) +/** State transition in progress */ +#define MCE_TRANSITION_SUBMODE (1 << 4) +/** Touchscreen/Keypad autorelock active */ +#define MCE_AUTORELOCK_SUBMODE (1 << 5) +/** Visual Touchscreen/Keypad active */ +#define MCE_VISUAL_TKLOCK_SUBMODE (1 << 6) + +/** System state */ +typedef enum { + MCE_STATE_UNDEF = -1, /**< System state not set */ + MCE_STATE_SHUTDOWN = 0, /**< System is in shutdown state */ + MCE_STATE_USER = 2, /**< System is in user state */ + MCE_STATE_ACTDEAD = 5, /**< System is in acting dead state */ + MCE_STATE_REBOOT = 6, /**< System is in reboot state */ + MCE_STATE_BOOT = 9 /**< System is in bootup state */ +} system_state_t; + +/** Call state */ +typedef enum { + /** Invalid call state */ + CALL_STATE_INVALID = MCE_INVALID_TRANSLATION, + /** No call on-going */ + CALL_STATE_NONE = 0, + /** There's an incoming call ringing */ + CALL_STATE_RINGING = 1, + /** There's an active call */ + CALL_STATE_ACTIVE = 2, + /** The device is in service state */ + CALL_STATE_SERVICE = 3 +} call_state_t; + +/** Call type */ +typedef enum { + /** Invalid call type */ + INVALID_CALL = MCE_INVALID_TRANSLATION, + /** The call is a normal call */ + NORMAL_CALL = 0, + /** The call is an emergency call */ + EMERGENCY_CALL = 1 +} call_type_t; + +/** Display state */ +typedef enum { + MCE_DISPLAY_UNDEF = -1, /**< Display state not set */ + MCE_DISPLAY_OFF = 0, /**< Display is off */ + MCE_DISPLAY_LOW_POWER = 1, /**< Display is in low power mode */ + MCE_DISPLAY_DIM = 2, /**< Display is dimmed */ + MCE_DISPLAY_ON = 3 /**< Display is on */ +} display_state_t; + +/** Cover state */ +typedef enum { + COVER_UNDEF = -1, /**< Cover state not set */ + COVER_CLOSED = 0, /**< Cover is closed */ + COVER_OPEN = 1 /**< Cover is open */ +} cover_state_t; + +/** Lock state */ +typedef enum { + /** Lock state not set */ + LOCK_UNDEF = -1, + /** Lock is disabled */ + LOCK_OFF = 0, + /** + * Lock is disabled, but unlock doesn't trigger artificial activity; + * used when opening the tklock UI or event eater fails + */ + LOCK_OFF_NO_ACTIVITY = 1, + /** Delayed unlock; write only */ + LOCK_OFF_DELAYED = 2, + /** Silent unlock; write only */ + LOCK_OFF_SILENT = 3, + /** Lock is disabled, but autorelock isn't disabled; write only */ + LOCK_OFF_PROXIMITY = 4, + /** Lock is enabled */ + LOCK_ON = 5, + /** Dimmed lock; write only */ + LOCK_ON_DIMMED = 6, + /** Silent lock; write only */ + LOCK_ON_SILENT = 7, + /** Silent dimmed lock; write only */ + LOCK_ON_SILENT_DIMMED = 8, + /** Enable proximity lock (no UI); write only */ + LOCK_ON_PROXIMITY = 9, + /** Toggle lock state; write only */ + LOCK_TOGGLE = 10 +} lock_state_t; + +/** Battery status */ +typedef enum { + BATTERY_STATUS_UNDEF = -1, /**< Battery status not known */ + BATTERY_STATUS_FULL = 0, /**< Battery full */ + BATTERY_STATUS_OK = 1, /**< Battery ok */ + BATTERY_STATUS_LOW = 2, /**< Battery low */ + BATTERY_STATUS_EMPTY = 3, /**< Battery empty */ +} battery_status_t; + +/** Camera button state */ +typedef enum { + CAMERA_BUTTON_UNDEF = -1, /**< Camera button state not set */ + CAMERA_BUTTON_UNPRESSED = 0, /**< Camera button not pressed */ + CAMERA_BUTTON_LAUNCH = 1, /**< Camera button fully pressed */ +} camera_button_state_t; + +/** Audio route */ +typedef enum { + /** Audio route not defined */ + AUDIO_ROUTE_UNDEF = -1, + /** Audio routed to handset */ + AUDIO_ROUTE_HANDSET = 0, + /** Audio routed to speaker */ + AUDIO_ROUTE_SPEAKER = 1, + /** Audio routed to headset */ + AUDIO_ROUTE_HEADSET = 2, +} audio_route_t; + +/** USB cable state */ +typedef enum { + USB_CABLE_UNDEF = -1, /**< Usb cable state not set */ + USB_CABLE_DISCONNECTED = 0, /**< Cable is not connected */ + USB_CABLE_CONNECTED = 1 /**< Cable is connected */ +} usb_cable_state_t; + +/** Thermal status */ +typedef enum { + /** Thermal state not set */ + THERMAL_STATE_UNDEF = -1, + /** Thermal state ok */ + THERMAL_STATE_OK = 0, + /** Thermal sensors indicate overheating */ + THERMAL_STATE_OVERHEATED = 1, +} thermal_state_t; + +/** LED brightness */ +datapipe_struct led_brightness_pipe; +/** State of device; read only */ +datapipe_struct device_inactive_pipe; +/** LED pattern to activate; read only */ +datapipe_struct led_pattern_activate_pipe; +/** LED pattern to deactivate; read only */ +datapipe_struct led_pattern_deactivate_pipe; +/** State of display; read only */ +datapipe_struct display_state_pipe; +/** Display brightness */ +datapipe_struct display_brightness_pipe; +/** Key backlight */ +datapipe_struct key_backlight_pipe; +/** A key has been pressed */ +datapipe_struct keypress_pipe; +/** Touchscreen activity took place */ +datapipe_struct touchscreen_pipe; +/** The lock-key has been pressed; read only */ +datapipe_struct lockkey_pipe; +/** Keyboard open/closed; read only */ +datapipe_struct keyboard_slide_pipe; +/** Lid cover open/closed; read only */ +datapipe_struct lid_cover_pipe; +/** Lens cover open/closed; read only */ +datapipe_struct lens_cover_pipe; +/** Proximity sensor; read only */ +datapipe_struct proximity_sensor_pipe; +/** The alarm UI state */ +datapipe_struct alarm_ui_state_pipe; +/** The device state */ +datapipe_struct system_state_pipe; +/** Enable/disable radios */ +datapipe_struct master_radio_pipe; +/** The device submode */ +datapipe_struct submode_pipe; +/** The call state */ +datapipe_struct call_state_pipe; +/** The call type */ +datapipe_struct call_type_pipe; +/** The touchscreen/keypad lock state */ +datapipe_struct tk_lock_pipe; +/** Charger state; read only */ +datapipe_struct charger_state_pipe; +/** Battery status; read only */ +datapipe_struct battery_status_pipe; +/** Battery charge level; read only */ +datapipe_struct battery_level_pipe; +/** Camera button; read only */ +datapipe_struct camera_button_pipe; +/** The inactivity timeout; read only */ +datapipe_struct inactivity_timeout_pipe; +/** Audio routing state; read only */ +datapipe_struct audio_route_pipe; +/** USB cable has been connected/disconnected; read only */ +datapipe_struct usb_cable_pipe; +/** A jack connector has been connected/disconnected; read only */ +datapipe_struct jack_sense_pipe; +/** Power save mode is active; read only */ +datapipe_struct power_saving_mode_pipe; +/** Thermal state; read only */ +datapipe_struct thermal_state_pipe; + +/* XXX: use HAL */ +/** Does the device have a flicker key? */ +extern gboolean has_flicker_key; + +/** + * Default inactivity timeout, in seconds; + * dim timeout: 30 seconds + * blank timeout: 3 seconds + * + * Used in case the display module doesn't load for some reason + */ +#define DEFAULT_INACTIVITY_TIMEOUT 33 + +submode_t mce_get_submode_int32(void); +gboolean mce_add_submode_int32(const submode_t submode); +gboolean mce_rem_submode_int32(const submode_t submode); + +#endif /* _MCE_H_ */ diff --git a/mce.ini b/mce.ini new file mode 100644 index 00000000..89c272f1 --- /dev/null +++ b/mce.ini @@ -0,0 +1,632 @@ +# Configuration file for MCE + +[Modules] + +# Path to modules +# +# Do not modify unless you're sure that you know what you're doing! +ModulePath=/usr/lib/mce/modules + +# Modules +# +# List of modules to load +# Note: the name should not include the "lib"-prefix +Modules=radiostates;display;keypad;led;battery;filter-brightness-als;inactivity;alarm;callstate;audiorouting;proximity;powersavemode + + +[HomeKey] + +# Try to make this possible somehow +# [HomeKey] keycode +# HomeKeyCode=Key_F5 + +# Timeout before keypress is regarded as a long press +# +# Timeout in milliseconds, default 800 +HomeKeyLongDelay=800 + + +[PowerKey] + +# Timeout before keypress is regarded as a medium press +# This delay is used when powering up from charging +# +# Timeout in milliseconds, default 1000 +PowerKeyMediumDelay=1000 + +# Timeout before keypress is regarded as a long press +# +# Timeout in milliseconds, default 1500 +PowerKeyLongDelay=1500 + +# Timeout for double keypresses +# +# Timeout in milliseconds, default 500 +PowerKeyDoubleDelay=500 + +# Short [power] behaviour +# +# WARNING: +# Setting short, long, and double press to disabled will make it +# near impossible to turn off your device without removing the battery! +# +# Valid options: +# disabled - do nothing on short press +# poweroff - shutdown device +# softpoweroff - enter soft poweroff mode +# tklock-lock - lock touchscreen/keypad lock in not locked +# tklock-unlock - unlock the touchscreen/keypad lock if locked +# tklock-both - lock the touchscreen/keypad if not locked, +# unlock the touchscreen/keypad lock if locked +# dbus-signal- - send a D-Bus signal with the name +PowerKeyShortAction=tklock-lock + +# Long [power] behaviour +# +# Valid options: +# disabled - do nothing on long press +# poweroff - shutdown device +# softpoweroff - enter soft poweroff mode +# tklock-lock - lock touchscreen/keypad lock in not locked +# tklock-unlock - unlock the touchscreen/keypad lock if locked +# tklock-both - lock the touchscreen/keypad if not locked, +# unlock the touchscreen/keypad lock if locked +# dbus-signal- - send a D-Bus signal with the name +PowerKeyLongAction=poweroff + +# Double press [power] behaviour +# Note: the double press action is triggered on press, rather than release, +# to avoid the second press to be processed elsewhere before the +# double press action has taken place +# +# Valid options: +# disabled - do nothing on double press +# poweroff - shutdown device +# softpoweroff - enter soft poweroff mode +# tklock-lock - lock touchscreen/keypad lock in not locked +# tklock-unlock - unlock the touchscreen/keypad lock if locked +# tklock-both - lock the touchscreen/keypad if not locked, +# unlock the touchscreen/keypad lock if locked +# dbus-signal- - send a D-Bus signal with the name +# PowerKeyDoubleAction=dbus-signal-powerkey_double_ind +PowerKeyDoubleAction=disabled + + +[SoftPowerOff] + +# Connectivity policy with charger connected +# +# Valid options: +# forceoffline - always go to offline +# softoffline - offline only if there are no open connections +# retain - do not change the network status +ConnectivityPolicyCharger=retain + +# Connectivity policy when running on battery +# +# Valid options: +# forceoffline - always go to offline +# softoffline - offline only if there are no open connections +# retain - do not change the network status +ConnectivityPolicyBattery=forceoffline + +# Connectivity policy when powering on from soft poweroff +# Note that this policy only affects the connectivity state +# +# Valid options: +# offline - stay offline +# restore - restore state from before soft poweroff +ConnectivityPolicyPowerOn=offline + +# Charger connect policy +# +# Valid options: +# wakeup - wake up from soft poweroff when a charger is connected +# ignore - remain in soft poweroff when a charger is connected +ChargerPolicyConnect=ignore + + +[TKLock] + +# Blank immediately instead of dim before blank when tklock is enabled +# +# 1 to blank immediately, 0 to dim before blanking +BlankImmediately=1 + +# Dim immediately instead of having a timeout before dimming +# when tklock is enabled +# +# 1 to dim immediately, 0 to wait for timeout +DimImmediately=1 + +# Timeout before dimming +# +# Timeout in seconds, default 3 +DimDelay=3 + +# Policy for touchscreen interrupts +# +# 1 - disable immediately +# 0 - wait until display is blanked +DisableTSImmediately=1 + +# Policy for keypad interrupts +# +# 2 - leave keypad interrupts on even after blanking +# (used to support pass-through of +/-) +# 1 - disable interrupts immediately +# 0 to wait until display is blanked +DisableKPImmediately=2 + +# Inhibit autolock when keyboard slide is open +# +# 1 - allow autolock when the keyboard slide is open +# 0 - inhibit autolock when the keyboard slide is open +AutolockWhenSlideOpen=0 + +# Inhibit proximity sensor based locking during calls +# when keyboard slide is open +# +# 1 - allow proximity lock when the keyboard slide is open +# 0 - inhibit proximity lock when the keyboard slide is open +ProximityLockWhenSlideOpen=0 + +# Always enable tklock when closing keyboard slide +# +# 1 - lock when keyboard slide is closed +# 0 - only enable lock when keyboard slide is closed if it was enabled +# before opening the slide and there has been no input +AlwaysLockOnSlideClose=0 + +# Unlock the tklock if the camera is popped out +# +# 1 to enable, 0 to disable +CameraPopoutUnlock=1 + +# Unlock the tklock if the lens cover is opened +# +# 1 to enable, 0 to disable +LensCoverUnlock=1 + +# Trigger unlock screen with volume keys +# Note: you need to set DisableKPImmediately=2 for this to work +# +# 1 to enable, 0 to disable +TriggerUnlockScreenWithVolumeKeys=0 + + +[KeyPad] + +# Timeout before disabling keyboard backlight when unused +# +# Timeout in seconds, default 30 +BacklightTimeout=30 + +# Fade in time for keyboard backlight +# +# Timeout in milliseconds, default 125; +# valid values: 0, 125, 250, 375, 500 +# 625, 750, 875, 1000 +BacklightFadeInTime=250 + +# Fade out time for keyboard backlight +# +# Timeout in milliseconds, default 250; +# valid values: 0, 125, 250, 375, 500 +# 625, 750, 875, 1000 +BacklightFadeOutTime=1000 + + +[Display] + +# Policy for display brightness increase +# +# direct - Instantly switch to the new brightness level +# steptime - Fade to the new value with fixed step-time +# (larger difference in brightness == longer fadetime) +# constanttime - Fade to the new value with constant time +BrightnessIncreasePolicy=constanttime + +# Step-time for brightness increase +# +# Steptime in milliseconds, default 5; +# valid values: 5, 10, 20, 30, 40, 50 +StepTimeIncrease=5 + +# Constant time for brightness increase +# +# Constant time in milliseconds, default: 2000 +# valid values: 2000-5000 +ConstantTimeIncrease=2000 + +# Policy for display brightness decrease +# +# direct - Instantly switch to the new brightness level +# steptime - Fade to the new value with fixed step-time +# (larger difference in brightness == longer fadetime) +# constanttime - Fade to the new value with constant time +BrightnessDecreasePolicy=constanttime + +# Step-time for brightness decrease +# +# Steptime in milliseconds, default 10; +# valid values: 5, 10, 20, 30, 40, 50 +StepTimeDecrease=10 + +# Constant time for brightness decrease +# +# Constant time in milliseconds, default: 3000 +# valid values: 2000-5000 +ConstantTimeDecrease=3000 + + +[ALS] + +# Policy for brightness level step-downs +# +# direct - Switch to next brightness level immediately +# unblank - Only step down the brightness after a blank->unblank cycle +StepDownPolicy=direct + + +[LED] + +# List of patterns for the LED functionality +# +# A list of all pattern names that should be configured +LEDPatterns=PatternPowerOn;PatternPowerOff;PatternCommunication;PatternCommunicationAndBatteryFull;PatternBatteryCharging;PatternBatteryChargingFlat;PatternBatteryFull;PatternDeviceSoftOff +CombinationRules=CombinationCommunicationAndBatteryFull + + +[LEDPatternMonoRX34] + +# Patterns used for the RX-34 hardware; +# this hardware has a single-colour LED without a dedicated engine +# Please prefix pattern names with Pattern to avoid name space clashes +# +# Note: For power management purposes, remember to keep try to keep the +# onPeriod relatively short (not shorter than 50ms though), +# the offPeriod long; if this is not possible, make sure to have +# a timeout for the pattern so that it goes off after 15-30 seconds +# +# Priority (0 - highest, 255 - lowest) +# ScreenOn - 0 only show pattern when the display is off +# 1 show pattern even when the display is on +# 2 only show pattern when the display is off, including acting dead +# 3 show pattern even when the display is on, including acting dead +# 4 only show pattern if the display is off, or if in acting dead +# 5 always show pattern, even if LED disabled +# Timeout in seconds before pattern is disabled, 0 for infinite +# OnPeriod time in milliseconds +# OffPeriod time in milliseconds +# (0 for continuous light; ONLY when the charger is connected!) +# Intensity in steps from 0 (off) to 15 (full intensity) +PatternDeviceOn=254;0;0;75;5000;10 +PatternDeviceSoftOff=253;0;0;100;10000;5 +PatternPowerOn=9;3;0;2000;1000;15 +PatternPowerOff=10;3;0;2000;1000;15 +PatternCommunication=30;1;0;250;2000;15 +PatternCommunicationCall=30;1;0;250;2000;15 +PatternCommunicationIM=30;1;0;250;2000;15 +PatternCommunicationSMS=30;1;0;250;2000;15 +PatternCommunicationEmail=30;1;0;250;2000;15 +PatternCommonNotification=30;1;0;250;2000;15 +PatternWebcamActive=255;0;0;0;0;0 +PatternBatteryCharging=50;4;0;500;7500;10 +PatternBatteryFull=40;4;0;1500;0;10 + +# This example pattern has a priority of 42 (all patterns with a *lower* +# priority value will have precedence), an on-period of 0.5 seconds, +# and an off-period of 1.5 seconds. The pattern will be active for +# 30 seconds, then stop. The brightness level will be 10 (with 15 being +# the maximum, this would amount to 66%. +# This pattern will be visible even when the display is on. +PatternExample=42;1;30;500;1500;10 + + +[LEDPatternNJoyRX44] + +# Patterns used for the RX-44 hardware; +# this hardware has an RGB LED connected to a NJoy controller +# Please prefix pattern names with Pattern to avoid name space clashes +# +# Priority (0 - highest, 255 - lowest) +# ScreenOn - 0 only show pattern when the display is off +# 1 show pattern even when the display is on +# 2 only show pattern when the display is off, including acting dead +# 3 show pattern even when the display is on, including acting dead +# 4 only show pattern if the display is off, or if in acting dead +# 5 always show pattern, even if LED disabled +# Timeout in seconds before pattern is disabled, 0 for infinite +# R-channel pattern in NJoy format (16 commands at most) +# G-channel pattern in NJoy format (16 commands at most) +# B-channel pattern in NJoy format (16 commands at most) +# +# 0000 -- Jump to the start of the pattern for the channel +# 40xx -- Set channel brightness +# xxyy -- Increment/decrement +# xx determines the speed; +# 01-3f -- short step time (granularity 0.49ms) +# 41-7f -- long step time (granularity 15.6ms) +# yy determines the increment/decrement steps +# 00-7f -- increment steps 00 = 0 steps, 7f = 127 steps +# 80-ff -- decrement steps 80 = 0 steps, ff = 127 steps +# +# Use 0 steps to create pauses +# Two consecutive increment/decrement sequences are needed +# to cover the entire range from 0-255 +# c000 -- End pattern execution +# e002 -- Send red trigger +# e004 -- Send green trigger +# e008 -- Send blue trigger +# e080 -- Wait for red trigger +# e100 -- Wait for green trigger +# e200 -- Wait for blue trigger +PatternDeviceOn=254;0;0;4000205f20df7f007f007f007f000000;4000205f20df7f007f007f007f000000;4000205f20df7f007f007f007f000000 +PatternDeviceSoftOff=253;0;0;4000203f20bf7f007f007f007f007f000000;4000203f20bf7f007f007f007f007f000000;0000 +PatternPowerOn=9;3;0;4000207f207f01ff01ffc000;4000207f207f01ff01ffc000;4000207f207f01ff01ffc000 +PatternPowerOff=10;3;0;4000017f017f36ff36ff7f00c000;4000017f017f36ff36ff7f00c000;4000017f017f36ff36ff7f00c000 +PatternCommunication=30;1;0;0000;0000;40007f00017f017f050001ff01ff0000 +PatternCommunicationCall=30;1;0;0000;0000;40007f00017f017f050001ff01ff0000 +PatternCommunicationIM=30;1;0;0000;0000;40007f00017f017f050001ff01ff0000 +PatternCommunicationSMS=30;1;0;0000;0000;40007f00017f017f050001ff01ff0000 +PatternCommunicationEmail=30;1;0;0000;0000;40007f00017f017f050001ff01ff0000 +PatternCommonNotification=30;1;0;0000;0000;40007f00017f017f050001ff01ff0000 +PatternWebcamActive=20;1;0;4000027f027fc000;0000;0000 +PatternBatteryCharging=50;4;0;0000;4000257f06ff7f0041000000;0000 +PatternBatteryFull=40;4;0;0000;407f0000;0000 + +# This example pattern has a priority of 42 (all patterns with a *lower* +# priority value will have precedence), and will flash in yellow +# This pattern will be visible even when the display is on. +PatternExample=42;1;30;4000167f167f17ff17ff0000;4000167f167f17ff17ff0000;0000 + + +[LEDPatternNJoyRX48] + +# Patterns used for the RX-48 hardware; +# this hardware has an RGB LED connected to a NJoy controller +# Please prefix pattern names with Pattern to avoid name space clashes +# +# Priority (0 - highest, 255 - lowest) +# ScreenOn - 0 only show pattern when the display is off +# 1 show pattern even when the display is on +# 2 only show pattern when the display is off, including acting dead +# 3 show pattern even when the display is on, including acting dead +# 4 only show pattern if the display is off, or if in acting dead +# 5 always show pattern, even if LED disabled +# Timeout in seconds before pattern is disabled, 0 for infinite +# R-channel pattern in NJoy format (16 commands at most) +# G-channel pattern in NJoy format (16 commands at most) +# B-channel pattern in NJoy format (16 commands at most) +# +# 0000 -- Jump to the start of the pattern for the channel +# 40xx -- Set channel brightness +# xxyy -- Increment/decrement +# xx determines the speed; +# 01-3f -- short step time (granularity 0.49ms) +# 41-7f -- long step time (granularity 15.6ms) +# yy determines the increment/decrement steps +# 00-7f -- increment steps 00 = 0 steps, 7f = 127 steps +# 80-ff -- decrement steps 80 = 0 steps, ff = 127 steps +# +# Use 0 steps to create pauses +# Two consecutive increment/decrement sequences are needed +# to cover the entire range from 0-255 +# c000 -- End pattern execution +# e002 -- Send red trigger +# e004 -- Send green trigger +# e008 -- Send blue trigger +# e080 -- Wait for red trigger +# e100 -- Wait for green trigger +# e200 -- Wait for blue trigger +PatternDeviceOn=254;0;0;4000e100207e207ee00420fe20fee0047f007f007f007f000000;4000e0024a15e0804a95e0807f007f007f007f000000;0000 +PatternDeviceSoftOff=253;0;0;4000204f20cf7f007f007f007f007f000000;4000204f20cf7f007f007f007f007f000000;0000 +PatternPowerOn=9;3;0;4000207f207f01ff01ffc000;4000207f207f01ff01ffc000;4000207f207f01ff01ffc000 +PatternPowerOff=10;3;0;4000017f017f36ff36ff7f00c000;4000017f017f36ff36ff7f00c000;4000017f017f36ff36ff7f00c000 +PatternCommunication=30;1;0;0000;0000;40007f00017f017f050001ff01ff0000 +PatternCommunicationCall=30;1;0;0000;0000;40007f00017f017f050001ff01ff0000 +PatternCommunicationIM=30;1;0;0000;0000;40007f00017f017f050001ff01ff0000 +PatternCommunicationSMS=30;1;0;0000;0000;40007f00017f017f050001ff01ff0000 +PatternCommunicationEmail=30;1;0;0000;0000;40007f00017f017f050001ff01ff0000 +PatternCommonNotification=30;1;0;0000;0000;40007f00017f017f050001ff01ff0000 +PatternWebcamActive=20;1;0;4000027f027fc000;0000;0000 +PatternBatteryCharging=50;4;0;0000;4000257f06ff7f0041000000;0000 +PatternBatteryFull=40;4;0;0000;407f0000;0000 + +# This example pattern has a priority of 42 (all patterns with a *lower* +# priority value will have precedence), and will flash in yellow +# This pattern will be visible even when the display is on. +PatternExample=42;1;30;4000167f167f17ff17ff0000;4000167f167f17ff17ff0000;0000 + + +[LEDPatternLystiRX51] + +# Patterns used for the RX-51 hardware; +# this hardware has an RGB LED connected to a Lysti controller +# Please prefix pattern names with Pattern to avoid name space clashes +# +# Priority (0 - highest, 255 - lowest) +# ScreenOn - 0 only show pattern when the display is off +# 1 show pattern even when the display is on +# 2 only show pattern when the display is off, including acting dead +# 3 show pattern even when the display is on, including acting dead +# 4 only show pattern if the display is off, or if in acting dead +# 5 always show pattern, even if LED disabled +# Timeout in seconds before pattern is disabled, 0 for infinite +# LED(s) to map to Engine 1/Engine 2 +# "r", "g", "b" maps the LED to engine 1 +# "R", "G", "B" maps the LED to engine 2 +# Example: +# "rG" maps the red LED to engine 1, +# the green LED to engine 2, +# and leaves the blue LED unmapped +# Avoid mapping the same LEDs to both engines... +# Engine 1 pattern in Lysti format (16 commands at most) +# Engine 2 pattern in Lysti format (16 commands at most) +# +# 0000 -- Jump to the start of the pattern for the channel +# 40xx -- Set channel brightness +# 9d80 -- Refresh Mux (use as first command in every pattern!) +# xxyy -- Increment/decrement +# xx determines the speed; +# 02-3f -- short step time (granularity 0.49ms) +# 42-7f -- long step time (granularity 15.6ms) +# +# If xx is even, increment +# If xx is odd, decrement +# yy determines the increment/decrement steps +# 00-ff -- in/decrement steps +# +# Use 0 steps to create pauses +# c000 -- End pattern execution +# e002 -- Send engine 1 trigger +# e004 -- Send engine 2 trigger +# e008 -- Send engine 3 trigger +# e080 -- Wait for engine 1 trigger +# e100 -- Wait for engine 2 trigger +# e200 -- Wait for engine 3 trigger +PatternDeviceOn=254;0;0;rgb;9d804000422043207f100000;9d800000 +PatternDeviceSoftOff=253;0;0;rg;9d804000423f433f7f100000;9d800000 +PatternPowerOn=9;3;0;rgb;9d80400042ff02ffc000;9d800000 +PatternPowerOff=10;3;0;rgb;9d80400001ff43ff7f007f00c000;9d800000 +PatternCommunication=30;1;0;b;9d80400002ff03ff02ff03ff71080000;9d800000 +PatternCommunicationCall=30;1;0;b;9d80400002ff03ff02ff03ff71080000;9d800000 +PatternCommunicationIM=30;1;0;b;9d80400002ff03ff02ff03ff71080000;9d800000 +PatternCommunicationSMS=30;1;0;b;9d80400002ff03ff02ff03ff71080000;9d800000 +PatternCommunicationEmail=30;1;0;b;9d80400002ff03ff02ff03ff71080000;9d800000 +PatternCommonNotification=30;1;0;b;9d80400002ff03ff02ff03ff71080000;9d800000 +PatternWebcamActive=20;1;0;r;9d80400004ffc0000000;9d800000 +PatternBatteryCharging=50;4;0;rg;9d804000427f0d7f7f007f0042000000;9d800000 +PatternBatteryFull=40;4;0;g;9d80407f0000;9d800000 + +# This example pattern has a priority of 42 (all patterns with a *lower* +# priority value will have precedence), and will flash in yellow +# This pattern will be visible even when the display is on. +PatternExample=42;1;30;rg;9d80400044ff45ff0000;9d800000 + + +[LEDPatternLystiRM680] + +# Patterns used for the RM-680/RM-690 hardware; +# this hardware has a single-colour LED connected to a Lysti controller +# Please prefix pattern names with Pattern to avoid name space clashes +# +# Priority (0 - highest, 255 - lowest) +# ScreenOn - 0 only show pattern when the display is off +# 1 show pattern even when the display is on +# 2 only show pattern when the display is off, including acting dead +# 3 show pattern even when the display is on, including acting dead +# 4 only show pattern if the display is off, or if in acting dead +# 5 always show pattern, even if LED disabled +# Timeout in seconds before pattern is disabled, 0 for infinite +# Pattern in Lysti format (16 commands at most) +# +# 0000 -- Jump to the start of the pattern for the channel +# 40xx -- Set channel brightness +# 9d80 -- Refresh Mux (use as first command in every pattern!) +# xxyy -- Increment/decrement +# xx determines the speed; +# 02-3f -- short step time (granularity 0.49ms) +# 42-7f -- long step time (granularity 15.6ms) +# +# If xx is even, increment +# If xx is odd, decrement +# yy determines the increment/decrement steps +# 00-ff -- in/decrement steps +# +# Use 0 steps to create pauses +# c000 -- End pattern execution + +# FIXME: sloooooow breathing +PatternDeviceSoftOff=253;0;0;9d804000423f433f7f100000 + +PatternPowerOn=9;3;0;9d80400012ff03ffc000 +PatternPowerOff=10;3;0;9d80400002ff1bffc000 +PatternCommunication=30;1;0;9d80400004ff05ff04ff05ff437f0000 +PatternBatteryCharging=50;4;0;9d804000427f437f433f0000 +PatternBatteryChargingFlat=50;4;0;9d80407f7f007f007f007f004000437f0000 +PatternBatteryFull=40;4;0;9d80407f0000 + +# Patterns only for use by combination rules +PatternCommunicationAndBatteryFull=29;1;0;9d8040ff05ff04ff05ff04ff42bf0000 + +# A combination-rule describes pattern transformations +# Please prefix combination-rule names with Combination +# to avoid name space clashes +# +# The first entry is the name of the pattern that holds the combined +# pattern; the following entries are the pre-requisites +# If all pre-requisites are true, then the combination pattern +# is activated +# +# Note: Ensure that the combination pattern always has a higher priority +# than the highest priority component +CombinationCommunicationAndBatteryFull=PatternCommunicationAndBatteryFull;PatternCommunication;PatternBatteryFull + +# This example pattern has a priority of 42 (all patterns with a *lower* +# priority value will have precedence), and will flash +# This pattern will be visible even when the display is on. +PatternExample=42;1;30;9d80400044ff45ff0000 + + +[LEDPatternNJoyRM696] + +# Patterns used for the RM-696 hardware; +# this hardware has a single-colour LED connected to a NJoy controller +# Please prefix pattern names with Pattern to avoid name space clashes +# +# Priority (0 - highest, 255 - lowest) +# ScreenOn - 0 only show pattern when the display is off +# 1 show pattern even when the display is on +# 2 only show pattern when the display is off, including acting dead +# 3 show pattern even when the display is on, including acting dead +# 4 only show pattern if the display is off, or if in acting dead +# 5 always show pattern, even if LED disabled +# Timeout in seconds before pattern is disabled, 0 for infinite +# Pattern in NJoy format (16 commands at most) +# +# 0000 -- Jump to the start of the pattern for the channel +# 40xx -- Set channel brightness +# xxyy -- Increment/decrement +# xx determines the speed; +# 01-3f -- short step time (granularity 0.49ms) +# 41-7f -- long step time (granularity 15.6ms) +# yy determines the increment/decrement steps +# 00-7f -- increment steps 00 = 0 steps, 7f = 127 steps +# 80-ff -- decrement steps 80 = 0 steps, ff = 127 steps +# +# Use 0 steps to create pauses +# Two consecutive increment/decrement sequences are needed +# to cover the entire range from 0-255 + +# FIXME: sloooooow breathing +PatternDeviceSoftOff=253;0;0;4000413f41bf5f900000 + +PatternPowerOn=9;3;0;4000087f087f01ff01ffc000 +PatternPowerOff=10;3;0;4000017f017f0dff0dffc000 +PatternCommunication=30;1;0;4000027f027f02ff02ff027f027f02ff02ff41ff0000 +PatternBatteryCharging=50;4;0;4000417f41ff41bf0000 +PatternBatteryChargingFlat=50;4;0;407f5f005f005f005f00400041ff0000 +PatternBatteryFull=40;4;0;407f0000 + +# Patterns only for use by combination rules +PatternCommunicationAndBatteryFull=29;1;0;40ff02ff02ff027f027f02ff02ff027f027f417f413f0000 + +# A combination-rule describes pattern transformations +# Please prefix combination-rule names with Combination +# to avoid name space clashes +# +# The first entry is the name of the pattern that holds the combined +# pattern; the following entries are the pre-requisites +# If all pre-requisites are true, then the combination pattern +# is activated +# +# Note: Ensure that the combination pattern always has a higher priority +# than the highest priority component +CombinationCommunicationAndBatteryFull=PatternCommunicationAndBatteryFull;PatternCommunication;PatternBatteryFull + +# This example pattern has a priority of 42 (all patterns with a *lower* +# priority value will have precedence), and will flash +# This pattern will be visible even when the display is on. +PatternExample=42;1;30;4000427f427f42ff42ff0000 diff --git a/mcebackup.conf b/mcebackup.conf new file mode 100644 index 00000000..ba95703d --- /dev/null +++ b/mcebackup.conf @@ -0,0 +1,12 @@ + + nokia + mce + backup-scripts + + /usr/share/mce/mce-backup + /usr/share/mce/mce-restore + + + $HOME/.mce + + diff --git a/mcetool.conf b/mcetool.conf new file mode 100644 index 00000000..f1037e88 --- /dev/null +++ b/mcetool.conf @@ -0,0 +1,9 @@ + + + + + + + diff --git a/median_filter.c b/median_filter.c new file mode 100644 index 00000000..2c5f7cf2 --- /dev/null +++ b/median_filter.c @@ -0,0 +1,180 @@ +/** + * @file median_filter.c + * median filter -- this implements a median filter + *

+ * Copyright © 2007-2008 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * @author Semi Malinen + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include + +#include "median_filter.h" + +/** + * Initialise median filter + + * @param filter The median filter to initialise + * @param window_size The window size to use + * + * @return FALSE if window_size is too big or filter is NULL, + * TRUE on success + */ +gboolean median_filter_init(median_filter_struct *filter, gsize window_size) +{ + gboolean status = FALSE; + guint i; + + if ((filter == NULL) || (window_size > MEDIAN_FILTER_MAX_WINDOW_SIZE)) + goto EXIT; + + filter->window_size = window_size; + + for (i = 0; i < filter->window_size; i++) { + filter->window[i] = 0; + filter->ordered_window[i] = 0; + } + + filter->samples = 0; + filter->oldest = 0; + + status = TRUE; + +EXIT: + return status; +} + +/** + * Insert a new sample into the median filter + * + * @param filter The median filter to insert the value into + * @param value The value to insert + * @param oldest The oldest value + * @return The filtered value + */ +static gint insert_ordered(median_filter_struct *filter, + gint value, gint oldest) +{ + guint i; + + /* If the filter window hasn't been filled yet, insert the new value */ + if (filter->samples < filter->window_size) { + /* Find insertion point */ + for (i = 0; i < filter->samples; i++) { + if (filter->ordered_window[i] >= value) { + /* Found the insertion point */ + for ( ; i < filter->samples; ++i) { + gint tmp; + + /* Swap the value at insertion point + * with the new value + */ + tmp = filter->ordered_window[i]; + filter->ordered_window[i] = value; + value = tmp; + } + + break; + } + } + + /* Do the insertion */ + filter->ordered_window[i] = value; + filter->samples++; + + goto EXIT; + } else { + /* The filter window is full; + * remove the oldest value and insert new + */ + if (value == oldest) { + /* Do nothing */ + goto EXIT; + } + + /* Find either the insertion point + * and/or the deletion point + */ + for (i = 0; i < filter->window_size; i++) { + if (filter->ordered_window[i] >= value) { + /* Found the insertion point + * (it might be the deletion point + * as well!) + */ + for ( ; i < filter->window_size; i++) { + /* Swap value at insertion + * point and the new value + */ + int tmp = filter->ordered_window[i]; + + filter->ordered_window[i] = value; + value = tmp; + + if (value == oldest) { + /* Found the deletion point */ + goto EXIT; + } + } + + goto EXIT; + } else if (filter->ordered_window[i] == oldest) { + /* Found the deletion point */ + for ( ; i < filter->window_size - 1; i++) { + if (filter->ordered_window[i + 1] >= value) { + /* Found the insertion point */ + break; + } + /* Shift the window, + * overwriting the value to delete + */ + filter->ordered_window[i] = filter->ordered_window[i + 1]; + } + /* Insert */ + filter->ordered_window[i] = value; + goto EXIT; + } + } + } + +EXIT: + /* For odd number of samples return the middle one + * For even number of samples return the average + * of the two middle ones + */ + return (filter->ordered_window[(filter->samples - 1) / 2] + + filter->ordered_window[filter->samples / 2]) / 2; +} + +/** + * Do a complete insertion of a sample into the median filter + * + * @param filter The median filter to insert the value into + * @param value The value to insert + * @return The filtered value + */ +gint median_filter_map(median_filter_struct *filter, gint value) +{ + gint filtered_value; + + /* Insert into the ordered array (deleting the oldest value) */ + filtered_value = insert_ordered(filter, value, + filter->window[filter->oldest]); + + /* Insert into the ring buffer (overwriting the oldest value) */ + filter->window[filter->oldest] = value; + filter->oldest = (filter->oldest + 1) % filter->window_size; + + return filtered_value; +} diff --git a/median_filter.h b/median_filter.h new file mode 100644 index 00000000..1ff672e4 --- /dev/null +++ b/median_filter.h @@ -0,0 +1,44 @@ +/** + * @file median_filter.h + * Headers for the median filter + *

+ * Copyright © 2007-2009 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * @author Semi Malinen + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _MEDIAN_FILTER_H_ +#define _MEDIAN_FILTER_H_ + +#include + +/** Maximum window size of the median filter */ +#define MEDIAN_FILTER_MAX_WINDOW_SIZE 11 + +/** Median filter */ +typedef struct { + gsize window_size; /**< Window size */ + /** Current number of samples in the window */ + gsize samples; + /** Index of the oldest sample in the window */ + gsize oldest; + gint window[MEDIAN_FILTER_MAX_WINDOW_SIZE]; /**< Ring buffer */ + gint ordered_window[MEDIAN_FILTER_MAX_WINDOW_SIZE]; /**< Ordered buffer */ +} median_filter_struct; + +gboolean median_filter_init(median_filter_struct *filter, gsize window_size); +gint median_filter_map(median_filter_struct *filter, gint value); + +#endif /* _MEDIAN_FILTER_H_ */ diff --git a/modetransition.c b/modetransition.c new file mode 100644 index 00000000..3d8e8bc4 --- /dev/null +++ b/modetransition.c @@ -0,0 +1,233 @@ +/** + * @file modetransition.c + * This file implements the mode transition component + * (normal/flight mode/off) + * of the Mode Control Entity + *

+ * Copyright © 2004-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include + +#include /* errno, ENOENT */ +#include /* exit(), EXIT_FAILURE */ +#include /* access(), F_OK */ + +#include + +#include "mce.h" /* MCE_LED_PATTERN_POWER_ON, + * MCE_LED_PATTERN_POWER_OFF, + * MCE_LED_PATTERN_DEVICE_ON, + * MCE_STATE_UNDEF, + * submode_t, + * system_state_t, + * MCE_TRANSITION_SUBMODE, + * mainloop, + * display_state_pipe, + * led_pattern_activate_pipe, + * led_pattern_deactivate_pipe, + * submode_pipe, + * system_state_pipe + */ +#include "modetransition.h" + +#include "mce-io.h" /* mce_read_string_from_file(), + * mce_write_string_to_file() + */ +#include "mce-log.h" /* mce_log(), LL_* */ +#include "datapipe.h" /* execute_datapipe(), + * execute_datapipe_output_triggers(), + * datapipe_get_gint(), + * append_output_trigger_to_datapipe(), + * remove_output_trigger_from_datapipe() + */ +#include "connectivity.h" /* get_connectivity_status() */ + +/** + * Set the MCE submode flags + * + * @param submode All submodes to set OR:ed together + * @return TRUE on success, FALSE on failure + */ +static gboolean mce_set_submode_int32(const submode_t submode) +{ + submode_t old_submode = datapipe_get_gint(submode_pipe); + + if (old_submode == submode) + goto EXIT; + + execute_datapipe(&submode_pipe, GINT_TO_POINTER(submode), + USE_INDATA, CACHE_INDATA); + mce_log(LL_DEBUG, "Submode changed to %d", submode); + +EXIT: + return TRUE; +} + +/** + * Add flags to the MCE submode + * + * @param submode submode(s) to add OR:ed together + * @return TRUE on success, FALSE on failure + */ +gboolean mce_add_submode_int32(const submode_t submode) +{ + submode_t old_submode = datapipe_get_gint(submode_pipe); + + return mce_set_submode_int32(old_submode | submode); +} + +/** + * Remove flags from the MCE submode + * + * @param submode submode(s) to remove OR:ed together + * @return TRUE on success, FALSE on failure + */ +gboolean mce_rem_submode_int32(const submode_t submode) +{ + submode_t old_submode = datapipe_get_gint(submode_pipe); + + return mce_set_submode_int32(old_submode & ~submode); +} + +/** + * Return all set MCE submode flags + * + * @return All set submode flags OR:ed together + */ +submode_t mce_get_submode_int32(void) G_GNUC_PURE; +submode_t mce_get_submode_int32(void) +{ + submode_t submode = datapipe_get_gint(submode_pipe); + + return submode; +} + +/** + * Handle system state change + * + * @param data The system state stored in a pointer + */ +static void system_state_trigger(gconstpointer data) +{ + static system_state_t old_system_state = MCE_STATE_UNDEF; + system_state_t system_state = GPOINTER_TO_INT(data); + + switch (system_state) { + case MCE_STATE_USER: + if (old_system_state == MCE_STATE_ACTDEAD) { + execute_datapipe_output_triggers(&led_pattern_deactivate_pipe, MCE_LED_PATTERN_POWER_ON, USE_INDATA); + } + + break; + + case MCE_STATE_SHUTDOWN: + case MCE_STATE_REBOOT: + /* Actions to perform when shutting down/rebooting + * from anything else than acting dead + */ + if ((old_system_state == MCE_STATE_USER) || + (old_system_state == MCE_STATE_BOOT) || + (old_system_state == MCE_STATE_UNDEF)) { + execute_datapipe_output_triggers(&led_pattern_deactivate_pipe, MCE_LED_PATTERN_DEVICE_ON, USE_INDATA); + execute_datapipe_output_triggers(&led_pattern_activate_pipe, MCE_LED_PATTERN_POWER_OFF, USE_INDATA); + } + + /* If we're shutting down/rebooting from acting dead, + * blank the screen + */ + if (old_system_state == MCE_STATE_ACTDEAD) { + execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_OFF), + USE_INDATA, CACHE_INDATA); + } + + break; + + case MCE_STATE_ACTDEAD: + break; + + case MCE_STATE_UNDEF: + goto EXIT; + + default: + break; + } + + mce_log(LL_DEBUG, + "dsmestate set to: %d (old: %d)", + system_state, old_system_state); + + old_system_state = system_state; + +EXIT: + return; +} + +/** + * Init function for the modetransition component + * + * @return TRUE on success, FALSE on failure + */ +gboolean mce_mode_init(void) +{ + gboolean status = FALSE; + + /* Append triggers/filters to datapipes */ + append_output_trigger_to_datapipe(&system_state_pipe, + system_state_trigger); + + /* If the bootup file exists, mce has crashed / restarted; + * since it exists in /var/run it will be removed when we reboot. + * + * If the file doesn't exist, create it to ensure that + * restarting mce doesn't get mce stuck in the transition submode + */ + if (access(MCE_BOOTUP_FILENAME, F_OK) == -1) { + if (errno == ENOENT) { + mce_log(LL_DEBUG, "Bootup mode enabled"); + mce_add_submode_int32(MCE_TRANSITION_SUBMODE); + errno = 0; + + (void)mce_write_string_to_file(MCE_BOOTUP_FILENAME, + ENABLED_STRING); + } else { + mce_log(LL_CRIT, + "access() failed: %s. Exiting.", + g_strerror(errno)); + goto EXIT; + } + } + + status = TRUE; + +EXIT: + return status; +} + +/** + * Exit function for the modetransition component + * + * @todo D-Bus unregistration + */ +void mce_mode_exit(void) +{ + /* Remove triggers/filters from datapipes */ + remove_output_trigger_from_datapipe(&system_state_pipe, + system_state_trigger); + + return; +} diff --git a/modetransition.h b/modetransition.h new file mode 100644 index 00000000..34038704 --- /dev/null +++ b/modetransition.h @@ -0,0 +1,37 @@ +/** + * @file modetransition.h + * Headers for the mode transition component of the Mode Control Entity + *

+ * Copyright © 2004-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _MODETRANSITION_H_ +#define _MODETRANSITION_H_ + +#include + +/** Path to the boot detection file */ +#define MCE_BOOTUP_FILENAME G_STRINGIFY(MCE_RUN_DIR) "/boot" + +#define SPLASH_DELAY 500 /**< 0.5 seconds */ +#define ACTDEAD_DELAY 1500 /**< 1.5 seconds */ +#define POWERUP_DELAY 3500 /**< 3.5 seconds */ + +/* When MCE is made modular, this will be handled differently */ +gboolean mce_mode_init(void); +void mce_mode_exit(void); + +#endif /* _MODETRANSITION_H_ */ diff --git a/modules/alarm.c b/modules/alarm.c new file mode 100644 index 00000000..0652d159 --- /dev/null +++ b/modules/alarm.c @@ -0,0 +1,177 @@ +/** + * @file alarm.c + * Alarm interface module for the Mode Control Entity + *

+ * Copyright © 2005-2009 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include +#include + +#include /* strcmp() */ + +#include "mce.h" + +#include "mce-log.h" /* mce_log(), LL_* */ +#include "mce-dbus.h" /* Direct: + * --- + * mce_dbus_handler_add(), + * dbus_send_message(), + * dbus_new_method_reply(), + * dbus_message_get_args(), + * dbus_message_get_no_reply(), + * dbus_error_init(), + * dbus_error_free(), + * DBUS_MESSAGE_TYPE_METHOD_CALL, + * DBUS_TYPE_INT32, + * DBUS_TYPE_INVALID, + * DBusMessage, DBusError, + * dbus_int32_t + */ +#include "datapipe.h" /* execute_datapipe() */ + +#ifndef VISUAL_REMINDERS_SERVICE +typedef enum { + VISUAL_REMINDER_ON_SCREEN, + VISUAL_REMINDER_NOT_ON_SCREEN, + VISUAL_REMINDER_ON_SCREEN_NO_SOUND +} visual_reminders_status; + +#define VISUAL_REMINDERS_SERVICE "com.nokia.voland" +#define VISUAL_REMINDERS_SIGNAL_IF "com.nokia.voland.signal" +#define VISUAL_REMINDERS_SIGNAL_PATH "/com/nokia/voland/signal" +#define VISUAL_REMINDER_STATUS_SIG "visual_reminders_status" +#endif /* VISUAL_REMINDERS_SERVICE */ + +/** Module name */ +#define MODULE_NAME "alarm" + +/** Functionality provided by this module */ +static const gchar *const provides[] = { MODULE_NAME, NULL }; + +/** Module information */ +G_MODULE_EXPORT module_info_struct module_info = { + /** Name of the module */ + .name = MODULE_NAME, + /** Module provides */ + .provides = provides, + /** Module priority */ + .priority = 250 +}; + +/** + * D-Bus callback for the alarm dialog status signal + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean alarm_dialog_status_dbus_cb(DBusMessage *const msg) +{ + alarm_ui_state_t alarm_ui_state = MCE_ALARM_UI_INVALID_INT32; + gboolean status = FALSE; + DBusError error; + dbus_int32_t dialog_status; + + /* Register error channel */ + dbus_error_init(&error); + + mce_log(LL_DEBUG, + "Received alarm dialog status signal"); + + if (dbus_message_get_args(msg, &error, + DBUS_TYPE_INT32, &dialog_status, + DBUS_TYPE_INVALID) == FALSE) { + // XXX: should we return an error instead? + mce_log(LL_CRIT, + "Failed to get argument from %s.%s: %s", + VISUAL_REMINDERS_SIGNAL_IF, + VISUAL_REMINDER_STATUS_SIG, + error.message); + dbus_error_free(&error); + goto EXIT; + } + + /* Convert alarm dialog status to to MCE alarm ui enum */ + switch (dialog_status) { + case VISUAL_REMINDER_ON_SCREEN: + alarm_ui_state = MCE_ALARM_UI_RINGING_INT32; + break; + + case VISUAL_REMINDER_ON_SCREEN_NO_SOUND: + alarm_ui_state = MCE_ALARM_UI_VISIBLE_INT32; + break; + + case VISUAL_REMINDER_NOT_ON_SCREEN: + alarm_ui_state = MCE_ALARM_UI_OFF_INT32; + break; + + default: + mce_log(LL_ERR, + "Received invalid alarm dialog status; " + "defaulting to OFF"); + alarm_ui_state = MCE_ALARM_UI_OFF_INT32; + break; + } + + (void)execute_datapipe(&alarm_ui_state_pipe, + GINT_TO_POINTER(alarm_ui_state), + USE_INDATA, CACHE_INDATA); + + status = TRUE; + +EXIT: + return status; +} + +/** + * Init function for the alarm interface module + * + * @todo XXX status needs to be set on error! + * + * @param module Unused + * @return NULL on success, a string with an error message on failure + */ +G_MODULE_EXPORT const gchar *g_module_check_init(GModule *module); +const gchar *g_module_check_init(GModule *module) +{ + (void)module; + + /* visual_reminders_status */ + if (mce_dbus_handler_add(VISUAL_REMINDERS_SIGNAL_IF, + VISUAL_REMINDER_STATUS_SIG, + NULL, + DBUS_MESSAGE_TYPE_SIGNAL, + alarm_dialog_status_dbus_cb) == NULL) + goto EXIT; + +EXIT: + return NULL; +} + +/** + * Exit function for the alarm interface module + * + * @todo D-Bus unregistration + * + * @param module Unused + */ +G_MODULE_EXPORT void g_module_unload(GModule *module); +void g_module_unload(GModule *module) +{ + (void)module; + + return; +} diff --git a/modules/audiorouting.c b/modules/audiorouting.c new file mode 100644 index 00000000..2b9a34e5 --- /dev/null +++ b/modules/audiorouting.c @@ -0,0 +1,435 @@ +/** + * @file audiorouting.c + * Audio routing module -- this listens to the audio routing + *

+ * Copyright © 2009-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include +#include + +#include /* strcmp() */ + +#include "mce.h" + +#include "mce-log.h" /* mce_log(), LL_* */ +#include "mce-dbus.h" /* Direct: + * --- + * DBusMessageIter + * + * Indirect: + * --- + */ + +/** Module name */ +#define MODULE_NAME "audiorouting" + +/** Functionality provided by this module */ +static const gchar *const provides[] = { MODULE_NAME, NULL }; + +/** Module information */ +G_MODULE_EXPORT module_info_struct module_info = { + /** Name of the module */ + .name = MODULE_NAME, + /** Module provides */ + .provides = provides, + /** Module priority */ + .priority = 100 +}; + +/** D-Bus interface for the policy framework */ +#define POLICY_DBUS_INTERFACE "com.nokia.policy" +/** D-Bus signal for actions from the policy framework */ +#define POLICY_AUDIO_ACTIONS "audio_actions" + +/** Macro to access offsets in the action data struct */ +#define STRUCT_OFFSET(s, m) ((char *)&(((s *)0)->m) - (char *)0) + +/** audio route arguments */ +struct argrt { + gchar *type; /**< Audio route type */ + gchar *device; /**< Device */ + gchar *mode; /**< Mode */ + gchar *hwid; /**< Hardware ID */ +}; + +/** argument descriptor for actions */ +struct argdsc { + const gchar *name; /**< Name */ + gint offs; /**< Offset */ + gint type; /**< Type */ +}; + +/** Full audio route */ +typedef enum { + FULL_AUDIO_ROUTE_UNDEF = -1, /**< Audio routing not set */ + /** Audio disabled */ + FULL_AUDIO_ROUTE_NULL = 0, + /** Audio routed through internal stereo speakers */ + FULL_AUDIO_ROUTE_IHF = 1, + /** Audio routed through FM transmitter */ + FULL_AUDIO_ROUTE_FMTX = 2, + /** Audio routed through internal stereo speakers and FM transmitter */ + FULL_AUDIO_ROUTE_IHF_AND_FMTX = 3, + /** Audio routed through earpiece */ + FULL_AUDIO_ROUTE_EARPIECE = 4, + /** Audio routed through earpiece and TV-out */ + FULL_AUDIO_ROUTE_EARPIECE_AND_TVOUT = 5, + /** Audio routed through TV-out */ + FULL_AUDIO_ROUTE_TVOUT = 6, + /** Audio routed through internal stereo speakers and TV-out */ + FULL_AUDIO_ROUTE_IHF_AND_TVOUT = 7, + /** Audio routed through headphone */ + FULL_AUDIO_ROUTE_HEADPHONE = 8, + /** Audio routed through headset */ + FULL_AUDIO_ROUTE_HEADSET = 9, + /** Audio routed through BT HSP */ + FULL_AUDIO_ROUTE_BTHSP = 10, + /** Audio routed through BT A2DP */ + FULL_AUDIO_ROUTE_BTA2DP = 11, + /** Audio routed through internal stereo speakers and headset */ + FULL_AUDIO_ROUTE_IHF_AND_HEADSET = 12, + /** Audio routed through internal stereo speakers and BT HSP */ + FULL_AUDIO_ROUTE_IHF_AND_BTHSP = 13, + /** Audio routed through TV-out and BT HSP */ + FULL_AUDIO_ROUTE_TVOUT_AND_BTHSP = 14, + /** Audio routed through TV-out and BT A2DP */ + FULL_AUDIO_ROUTE_TVOUT_AND_BTA2DP = 15, + /** Audio routed through some other device */ + FULL_AUDIO_ROUTE_OTHER = 255 +} full_audio_route_t; + +/** + * Parser used to parse the action information + * + * @param actit Iterator for the action data + * @param descs A struct with the argument descriptors + * @param args The arguments to parse + * @param len The size of args + * @return TRUE on success, FALSE on failure + */ +static gboolean action_parser(DBusMessageIter *actit, struct argdsc *descs, + void *args, gint len) +{ + gboolean status = FALSE; + DBusMessageIter cmdit; + DBusMessageIter argit; + DBusMessageIter valit; + struct argdsc *desc; + gchar *argname; + void *argval; + + dbus_message_iter_recurse(actit, &cmdit); + + memset(args, 0, len); + + do { + if (dbus_message_iter_get_arg_type(&cmdit) != DBUS_TYPE_STRUCT) + goto EXIT; + + dbus_message_iter_recurse(&cmdit, &argit); + + if (dbus_message_iter_get_arg_type(&argit) != DBUS_TYPE_STRING) + goto EXIT; + + dbus_message_iter_get_basic(&argit, (void *)&argname); + + if (!dbus_message_iter_next(&argit)) + goto EXIT; + + if (dbus_message_iter_get_arg_type(&argit) != DBUS_TYPE_VARIANT) + goto EXIT; + + dbus_message_iter_recurse(&argit, &valit); + + for (desc = descs; desc->name != NULL; desc++) { + if (!strcmp(argname, desc->name)) { + if ((desc->offs + + (gint)sizeof (gchar *)) > len) { + mce_log(LL_ERR, + "%s desc offset %d is " + "out of range %d", + "action_parser()", + desc->offs, len); + goto EXIT; + } else { + if (dbus_message_iter_get_arg_type(&valit) != desc->type) + goto EXIT; + + argval = (char *)args + desc->offs; + + dbus_message_iter_get_basic(&valit, argval); + } + + break; + } + } + + } while (dbus_message_iter_next(&cmdit)); + + status = TRUE; + +EXIT: + return status; +} + +/** + * Parser for the audio route + * + * @param data Iterator for the data + * @return TRUE on success, FALSE on failure + */ +static gboolean audio_route_parser(DBusMessageIter *data) +{ + static struct argdsc descs[] = { + { + "type", + STRUCT_OFFSET(struct argrt, type), + DBUS_TYPE_STRING + }, { + "device", + STRUCT_OFFSET(struct argrt, device), + DBUS_TYPE_STRING + }, { + "mode", + STRUCT_OFFSET(struct argrt, mode), + DBUS_TYPE_STRING + }, { + "hwid", + STRUCT_OFFSET(struct argrt, hwid), + DBUS_TYPE_STRING + }, { NULL, 0, DBUS_TYPE_INVALID } + }; + + full_audio_route_t full_audio_route = FULL_AUDIO_ROUTE_UNDEF; + static audio_route_t old_audio_route = AUDIO_ROUTE_UNDEF; + audio_route_t audio_route = AUDIO_ROUTE_UNDEF; + gboolean status = FALSE; + struct argrt args; + + do { + /* If we fail to parse, abort */ + if (!action_parser(data, descs, &args, sizeof (args))) + goto EXIT; + + /* If we don't get the type or device, abort */ + if ((args.type == NULL) || (args.device == NULL)) + goto EXIT; + + /* If this isn't the sink, we're not interested */ + if (strcmp(args.type, "sink")) + continue; + + if (!strcmp(args.device, "null")) { + full_audio_route = FULL_AUDIO_ROUTE_NULL; + } else if (!strcmp(args.device, "ihf")) { + full_audio_route = FULL_AUDIO_ROUTE_IHF; + } else if (!strcmp(args.device, "fmtx")) { + full_audio_route = FULL_AUDIO_ROUTE_FMTX; + } else if (!strcmp(args.device, "ihfandfmtx")) { + full_audio_route = FULL_AUDIO_ROUTE_IHF_AND_FMTX; + } else if (!strcmp(args.device, "earpiece")) { + full_audio_route = FULL_AUDIO_ROUTE_EARPIECE; + } else if (!strcmp(args.device, "earpieceandtvout")) { + full_audio_route = FULL_AUDIO_ROUTE_EARPIECE_AND_TVOUT; + } else if (!strcmp(args.device, "tvout")) { + full_audio_route = FULL_AUDIO_ROUTE_TVOUT; + } else if (!strcmp(args.device, "ihfandtvout")) { + full_audio_route = FULL_AUDIO_ROUTE_IHF_AND_TVOUT; + } else if (!strcmp(args.device, "headphone")) { + full_audio_route = FULL_AUDIO_ROUTE_HEADPHONE; + } else if (!strcmp(args.device, "headset")) { + full_audio_route = FULL_AUDIO_ROUTE_HEADSET; + } else if (!strcmp(args.device, "headsetforcall")) { + full_audio_route = FULL_AUDIO_ROUTE_HEADSET; + } else if (!strcmp(args.device, "bthsp")) { + full_audio_route = FULL_AUDIO_ROUTE_BTHSP; + } else if (!strcmp(args.device, "bta2dp")) { + full_audio_route = FULL_AUDIO_ROUTE_BTA2DP; + } else if (!strcmp(args.device, "ihfandheadset")) { + full_audio_route = FULL_AUDIO_ROUTE_IHF_AND_HEADSET; + } else if (!strcmp(args.device, "ihfandbthsp")) { + full_audio_route = FULL_AUDIO_ROUTE_IHF_AND_BTHSP; + } else if (!strcmp(args.device, "tvoutandbthsp")) { + full_audio_route = FULL_AUDIO_ROUTE_TVOUT_AND_BTHSP; + } else if (!strcmp(args.device, "tvoutandbta2dp")) { + full_audio_route = FULL_AUDIO_ROUTE_TVOUT_AND_BTA2DP; + } else { + full_audio_route = FULL_AUDIO_ROUTE_OTHER; + } + } while (dbus_message_iter_next(data)); + +EXIT: + /* Convert the full audio route to a simplified version + * suitable for mass consumption + */ + switch (full_audio_route) { + /* Handset */ + case FULL_AUDIO_ROUTE_EARPIECE: + case FULL_AUDIO_ROUTE_EARPIECE_AND_TVOUT: + audio_route = AUDIO_ROUTE_HANDSET; + break; + + /* Internal stereo speakers */ + case FULL_AUDIO_ROUTE_IHF: + case FULL_AUDIO_ROUTE_IHF_AND_FMTX: + case FULL_AUDIO_ROUTE_IHF_AND_TVOUT: + audio_route = AUDIO_ROUTE_SPEAKER; + break; + + /* Headset */ + case FULL_AUDIO_ROUTE_HEADSET: + case FULL_AUDIO_ROUTE_IHF_AND_HEADSET: + case FULL_AUDIO_ROUTE_BTHSP: + case FULL_AUDIO_ROUTE_BTA2DP: + case FULL_AUDIO_ROUTE_TVOUT_AND_BTHSP: + case FULL_AUDIO_ROUTE_TVOUT_AND_BTA2DP: + audio_route = AUDIO_ROUTE_HEADSET; + break; + + /* If the full audio route didn't change, don't change audio route; + * also ignore NULL routes + */ + case FULL_AUDIO_ROUTE_NULL: + case FULL_AUDIO_ROUTE_UNDEF: + audio_route = old_audio_route; + break; + + /* For cases that aren't relevant to us anyway, map to undef */ + default: + audio_route = AUDIO_ROUTE_UNDEF; + break; + } + + if (audio_route != old_audio_route) { + execute_datapipe(&audio_route_pipe, + GINT_TO_POINTER(audio_route), + USE_INDATA, CACHE_INDATA); + old_audio_route = audio_route; + } + + if (full_audio_route != FULL_AUDIO_ROUTE_UNDEF) { + status = TRUE; + } + + return status; +} + +/** + * D-Bus callback for the actions signal + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean actions_dbus_cb(DBusMessage *msg) +{ + gboolean status = FALSE; + DBusMessageIter msgit; + DBusMessageIter arrit; + DBusMessageIter entit; + DBusMessageIter actit; + dbus_uint32_t txid; + gchar *actname; + + mce_log(LL_DEBUG, "Received policy actions"); + + dbus_message_iter_init(msg, &msgit); + + if (dbus_message_iter_get_arg_type(&msgit) != DBUS_TYPE_UINT32) + goto EXIT; + + dbus_message_iter_get_basic(&msgit, (void *)&txid); + + mce_log(LL_DEBUG, "got actions; txid: %d", txid); + + if (!dbus_message_iter_next(&msgit) || + dbus_message_iter_get_arg_type(&msgit) != DBUS_TYPE_ARRAY) + goto EXIT; + + dbus_message_iter_recurse(&msgit, &arrit); + + do { + if (dbus_message_iter_get_arg_type(&arrit) != DBUS_TYPE_DICT_ENTRY) + continue; + + dbus_message_iter_recurse(&arrit, &entit); + + do { + if (dbus_message_iter_get_arg_type(&entit) != DBUS_TYPE_STRING) + continue; + + dbus_message_iter_get_basic(&entit, (void *)&actname); + + if (!dbus_message_iter_next(&entit) || + dbus_message_iter_get_arg_type(&entit) != DBUS_TYPE_ARRAY) + continue; + + dbus_message_iter_recurse(&entit, &actit); + + if (dbus_message_iter_get_arg_type(&actit) != DBUS_TYPE_ARRAY) + continue; + + if (!strcmp("com.nokia.policy.audio_route", actname)) + if (audio_route_parser(&actit) == TRUE) + break; + } while (dbus_message_iter_next(&entit)); + } while (dbus_message_iter_next(&arrit)); + + status = TRUE; + +EXIT: + return status; +} + +/** + * Init function for the audio routing module + * + * @todo XXX status needs to be set on error! + * + * @param module Unused + * @return NULL on success, a string with an error message on failure + */ +G_MODULE_EXPORT const gchar *g_module_check_init(GModule *module); +const gchar *g_module_check_init(GModule *module) +{ + (void)module; + + /* actions */ + if (mce_dbus_handler_add(POLICY_DBUS_INTERFACE, + POLICY_AUDIO_ACTIONS, + NULL, + DBUS_MESSAGE_TYPE_SIGNAL, + actions_dbus_cb) == NULL) + goto EXIT; + +EXIT: + return NULL; +} + +/** + * Exit function for the audio routing module + * + * @todo D-Bus unregistration + * + * @param module Unused + */ +G_MODULE_EXPORT void g_module_unload(GModule *module); +void g_module_unload(GModule *module) +{ + (void)module; + + return; +} diff --git a/modules/battery.c b/modules/battery.c new file mode 100644 index 00000000..cec3f0d0 --- /dev/null +++ b/modules/battery.c @@ -0,0 +1,536 @@ +/** + * @file battery.c + * Battery module -- this implements battery and charger logic for MCE + *

+ * Copyright © 2008-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include +#include + +#include /* BME_SERVICE, BME_REQUEST_PATH, + * BME_REQUEST_IF, BME_SIGNAL_IF, + * BME_STATUS_INFO_REQ, + * BME_BATTERY_FULL, + * BME_BATTERY_OK, + * BME_BATTERY_LOW, + * BME_BATTERY_EMPTY, + * BME_CHARGER_CHARGING_ON, + * BME_CHARGER_CHARGING_OFF, + * BME_CHARGER_CONNECTED, + * BME_CHARGER_DISCONNECTED + */ + +#include "mce.h" /* + * MCE_LED_PATTERN_BATTERY_LOW, + * MCE_LED_PATTERN_BATTERY_FULL, + * MCE_LED_PATTERN_BATTERY_CHARGING, + * charger_state_pipe, + * device_inactive_pipe, + * led_pattern_activate_pipe, + * led_pattern_deactivate_pipe, + */ + +#include "mce-log.h" /* mce_log(), LL_* */ +#include "mce-dbus.h" /* Direct: + * --- + * mce_dbus_handler_add(), + * dbus_send(), + * + * Indirect: + * --- + * dbus_message_get_args(), + * dbus_error_init(), + * dbus_error_free(), + * DBusError, + * DBusMessage, + * DBUS_MESSAGE_TYPE_SIGNAL, + * DBUS_TYPE_UINT32, + * DBUS_TYPE_INVALID, + * dbus_bool_t, + * dbus_uint32_t + */ +#include "datapipe.h" /* execute_datapipe(), + * execute_datapipe_output_triggers() + */ + +/** Module name */ +#define MODULE_NAME "battery" + +/** Functionality provided by this module */ +static const gchar *const provides[] = { MODULE_NAME, NULL }; + +/** Module information */ +G_MODULE_EXPORT module_info_struct module_info = { + /** Name of the module */ + .name = MODULE_NAME, + /** Module provides */ + .provides = provides, + /** Module priority */ + .priority = 100 +}; + +/** Cached value of the charger connected state */ +static gint cached_charger_connected = -1; + +/** + * D-Bus callback for the battery full signal + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean battery_full_dbus_cb(DBusMessage *const msg) +{ + gboolean status = FALSE; + + (void)msg; + + mce_log(LL_DEBUG, + "Received battery full signal"); + + execute_datapipe_output_triggers(&led_pattern_deactivate_pipe, MCE_LED_PATTERN_BATTERY_CHARGING, USE_INDATA); + execute_datapipe_output_triggers(&led_pattern_activate_pipe, MCE_LED_PATTERN_BATTERY_FULL, USE_INDATA); + + execute_datapipe(&battery_status_pipe, + GINT_TO_POINTER(BATTERY_STATUS_FULL), + USE_INDATA, CACHE_INDATA); + + status = TRUE; + +//EXIT: + return status; +} + +/** + * D-Bus callback for the battery ok signal + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean battery_ok_dbus_cb(DBusMessage *const msg) +{ + gboolean status = FALSE; + + (void)msg; + + mce_log(LL_DEBUG, + "Received battery ok signal"); + +// execute_datapipe_output_triggers(&led_pattern_deactivate_pipe, MCE_LED_PATTERN_BATTERY_LOW, USE_INDATA); + + execute_datapipe(&battery_status_pipe, + GINT_TO_POINTER(BATTERY_STATUS_OK), + USE_INDATA, CACHE_INDATA); + + status = TRUE; + +//EXIT: + return status; +} + +/** + * D-Bus callback for the battery low signal + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean battery_low_dbus_cb(DBusMessage *const msg) +{ + gboolean status = FALSE; + + (void)msg; + + mce_log(LL_DEBUG, + "Received battery low signal"); + +// execute_datapipe_output_triggers(&led_pattern_activate_pipe, MCE_LED_PATTERN_BATTERY_LOW, USE_INDATA); + + execute_datapipe(&battery_status_pipe, + GINT_TO_POINTER(BATTERY_STATUS_LOW), + USE_INDATA, CACHE_INDATA); + + status = TRUE; + +//EXIT: + return status; +} + +/** + * D-Bus callback for the battery empty signal + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean battery_empty_dbus_cb(DBusMessage *const msg) +{ + gboolean status = FALSE; + + (void)msg; + + mce_log(LL_DEBUG, + "Received battery empty signal"); + + execute_datapipe(&battery_status_pipe, + GINT_TO_POINTER(BATTERY_STATUS_EMPTY), + USE_INDATA, CACHE_INDATA); + + status = TRUE; + +//EXIT: + return status; +} + +/** + * D-Bus callback for the battery state changed signal + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean battery_state_changed_dbus_cb(DBusMessage *const msg) +{ + dbus_uint32_t now; + dbus_uint32_t max; + gboolean status = FALSE; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + mce_log(LL_DEBUG, + "Received battery state changed signal"); + + if (dbus_message_get_args(msg, &error, + DBUS_TYPE_UINT32, &now, + DBUS_TYPE_UINT32, &max, + DBUS_TYPE_INVALID) == FALSE) { + // XXX: should we return an error instead? + mce_log(LL_CRIT, + "Failed to get argument from %s.%s: %s", + BME_SIGNAL_IF, BME_BATTERY_STATE_UPDATE, + error.message); + dbus_error_free(&error); + goto EXIT; + } + + mce_log(LL_DEBUG, + "Battery bars: %d/%d (%d %%)", + now, max, (now * 10 / max) * 10); + + execute_datapipe(&battery_level_pipe, + GINT_TO_POINTER((now * 10 / max) * 10), + USE_INDATA, CACHE_INDATA); + + status = TRUE; + +EXIT: + return status; +} + +/** + * D-Bus callback for the charger_charging_on signal + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean charger_charging_on_dbus_cb(DBusMessage *const msg) +{ + gboolean old_charger_state = datapipe_get_gbool(charger_state_pipe); + gboolean status = FALSE; + + (void)msg; + + mce_log(LL_DEBUG, + "Received charger_charging_on signal"); + + /* Only update the charger state if needed */ + if (old_charger_state == FALSE) { + execute_datapipe(&charger_state_pipe, GINT_TO_POINTER(TRUE), + USE_INDATA, CACHE_INDATA); + } + + /* In case these are active; there's no harm to call them anyway */ + execute_datapipe_output_triggers(&led_pattern_deactivate_pipe, MCE_LED_PATTERN_BATTERY_FULL, USE_INDATA); +// execute_datapipe_output_triggers(&led_pattern_deactivate_pipe, MCE_LED_PATTERN_BATTERY_LOW, USE_INDATA); + + execute_datapipe_output_triggers(&led_pattern_activate_pipe, MCE_LED_PATTERN_BATTERY_CHARGING, USE_INDATA); + + status = TRUE; + +//EXIT: + return status; +} + +/** + * D-Bus callback for the charger_charging_off signal + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean charger_charging_off_dbus_cb(DBusMessage *const msg) +{ + gboolean old_charger_state = datapipe_get_gbool(charger_state_pipe); + gboolean status = FALSE; + + (void)msg; + + mce_log(LL_DEBUG, + "Received charger_charging_off signal"); + + /* Only update the charger state if needed */ + if (old_charger_state == TRUE) { + execute_datapipe(&charger_state_pipe, GINT_TO_POINTER(FALSE), + USE_INDATA, CACHE_INDATA); + } + + /* In case these are active; there's no harm to call them anyway */ + execute_datapipe_output_triggers(&led_pattern_deactivate_pipe, MCE_LED_PATTERN_BATTERY_CHARGING, USE_INDATA); + + status = TRUE; + +//EXIT: + return status; +} + +/** + * D-Bus callback for the charger_charging_failed signal + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean charger_charging_failed_dbus_cb(DBusMessage *const msg) +{ + gboolean old_charger_state = datapipe_get_gbool(charger_state_pipe); + gboolean status = FALSE; + + (void)msg; + + mce_log(LL_DEBUG, + "Received charger_charging_failed signal"); + + /* Only update the charger state if needed */ + if (old_charger_state == TRUE) { + execute_datapipe(&charger_state_pipe, GINT_TO_POINTER(FALSE), + USE_INDATA, CACHE_INDATA); + } + + /* In case these are active; there's no harm to call them anyway */ + execute_datapipe_output_triggers(&led_pattern_deactivate_pipe, MCE_LED_PATTERN_BATTERY_FULL, USE_INDATA); + execute_datapipe_output_triggers(&led_pattern_deactivate_pipe, MCE_LED_PATTERN_BATTERY_CHARGING, USE_INDATA); + + /* Generate activity */ + (void)execute_datapipe(&device_inactive_pipe, GINT_TO_POINTER(FALSE), + USE_INDATA, CACHE_INDATA); + + status = TRUE; + +//EXIT: + return status; +} + +/** + * D-Bus callback for the charger_connected signal + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean charger_connected_dbus_cb(DBusMessage *const msg) +{ + gboolean status = FALSE; + + (void)msg; + + mce_log(LL_DEBUG, + "Received charger_connected signal"); + + if (cached_charger_connected != 1) { + /* Generate activity */ + (void)execute_datapipe(&device_inactive_pipe, + GINT_TO_POINTER(FALSE), + USE_INDATA, CACHE_INDATA); + cached_charger_connected = 1; + } + + status = TRUE; + +//EXIT: + return status; +} + +/** + * D-Bus callback for the charger_disconnected signal + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean charger_disconnected_dbus_cb(DBusMessage *const msg) +{ + gboolean old_charger_state = datapipe_get_gbool(charger_state_pipe); + gboolean status = FALSE; + + (void)msg; + + mce_log(LL_DEBUG, + "Received charger_disconnected signal"); + + /* Only update the charger state if needed */ + if (old_charger_state == TRUE) { + execute_datapipe(&charger_state_pipe, GINT_TO_POINTER(FALSE), + USE_INDATA, CACHE_INDATA); + } + + /* In case these are active; there's no harm to call them anyway */ + execute_datapipe_output_triggers(&led_pattern_deactivate_pipe, MCE_LED_PATTERN_BATTERY_FULL, USE_INDATA); + execute_datapipe_output_triggers(&led_pattern_deactivate_pipe, MCE_LED_PATTERN_BATTERY_CHARGING, USE_INDATA); + + if (cached_charger_connected != 0) { + /* Generate activity */ + (void)execute_datapipe(&device_inactive_pipe, + GINT_TO_POINTER(FALSE), + USE_INDATA, CACHE_INDATA); + cached_charger_connected = 0; + } + + status = TRUE; + +//EXIT: + return status; +} + +/** + * Request update of charger status + * + * @return TRUE on success, FALSE on failure + */ +static gboolean request_charger_status(void) +{ + return dbus_send(BME_SERVICE, BME_REQUEST_PATH, BME_REQUEST_IF, + BME_STATUS_INFO_REQ, NULL, DBUS_TYPE_INVALID); +} + +/** + * Init function for the battery and charger module + * + * @todo XXX status needs to be set on error! + * + * @param module Unused + * @return NULL on success, a string with an error message on failure + */ +G_MODULE_EXPORT const gchar *g_module_check_init(GModule *module); +const gchar *g_module_check_init(GModule *module) +{ + (void)module; + + /* battery_full */ + if (mce_dbus_handler_add(BME_SIGNAL_IF, + BME_BATTERY_FULL, + NULL, + DBUS_MESSAGE_TYPE_SIGNAL, + battery_full_dbus_cb) == NULL) + goto EXIT; + + /* battery_ok */ + if (mce_dbus_handler_add(BME_SIGNAL_IF, + BME_BATTERY_OK, + NULL, + DBUS_MESSAGE_TYPE_SIGNAL, + battery_ok_dbus_cb) == NULL) + goto EXIT; + + /* battery_low */ + if (mce_dbus_handler_add(BME_SIGNAL_IF, + BME_BATTERY_LOW, + NULL, + DBUS_MESSAGE_TYPE_SIGNAL, + battery_low_dbus_cb) == NULL) + goto EXIT; + + /* battery_empty */ + if (mce_dbus_handler_add(BME_SIGNAL_IF, + BME_BATTERY_EMPTY, + NULL, + DBUS_MESSAGE_TYPE_SIGNAL, + battery_empty_dbus_cb) == NULL) + goto EXIT; + + /* battery_state_changed */ + if (mce_dbus_handler_add(BME_SIGNAL_IF, + BME_BATTERY_STATE_UPDATE, + NULL, + DBUS_MESSAGE_TYPE_SIGNAL, + battery_state_changed_dbus_cb) == NULL) + goto EXIT; + + /* charger_charging_on */ + if (mce_dbus_handler_add(BME_SIGNAL_IF, + BME_CHARGER_CHARGING_ON, + NULL, + DBUS_MESSAGE_TYPE_SIGNAL, + charger_charging_on_dbus_cb) == NULL) + goto EXIT; + + /* charger_charging_off */ + if (mce_dbus_handler_add(BME_SIGNAL_IF, + BME_CHARGER_CHARGING_OFF, + NULL, + DBUS_MESSAGE_TYPE_SIGNAL, + charger_charging_off_dbus_cb) == NULL) + goto EXIT; + + /* charger_charging_failed */ + if (mce_dbus_handler_add(BME_SIGNAL_IF, + BME_CHARGER_CHARGING_FAILED, + NULL, + DBUS_MESSAGE_TYPE_SIGNAL, + charger_charging_failed_dbus_cb) == NULL) + goto EXIT; + + /* charger_connected */ + if (mce_dbus_handler_add(BME_SIGNAL_IF, + BME_CHARGER_CONNECTED, + NULL, + DBUS_MESSAGE_TYPE_SIGNAL, + charger_connected_dbus_cb) == NULL) + goto EXIT; + + /* charger_disconnected */ + if (mce_dbus_handler_add(BME_SIGNAL_IF, + BME_CHARGER_DISCONNECTED, + NULL, + DBUS_MESSAGE_TYPE_SIGNAL, + charger_disconnected_dbus_cb) == NULL) + goto EXIT; + + /* Update charger status */ + request_charger_status(); + +EXIT: + return NULL; +} + +/** + * Exit function for the battery and charger module + * + * @todo D-Bus unregistration + * + * @param module Unused + */ +G_MODULE_EXPORT void g_module_unload(GModule *module); +void g_module_unload(GModule *module) +{ + (void)module; + + return; +} diff --git a/modules/callstate.c b/modules/callstate.c new file mode 100644 index 00000000..525f6311 --- /dev/null +++ b/modules/callstate.c @@ -0,0 +1,511 @@ +/** + * @file callstate.c + * Call state module -- this handles the call state for MCE + *

+ * Copyright © 2008-2009 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include +#include + +#include /* strncmp(), strlen() */ + +#include "mce.h" +#include "callstate.h" + +#include /* MCE_CALL_STATE_NONE, + * MCE_CALL_STATE_ACTIVE, + * MCE_CALL_STATE_SERVICE, + * MCE_NORMAL_CALL, + * MCE_EMERGENCY_CALL + */ + +#include "mce.h" /* FIXME + * CALL_STATE_INVALID, + * CALL_STATE_NONE, + * CALL_STATE_RINGING, + * CALL_STATE_ACTIVE, + * CALL_STATE_SERVICE, + * INVALID_CALL, + * NORMAL_CALL, + * EMERGENCY_CALL, + * call_state_t, + * call_type_t + */ + +#include "mce-lib.h" /* mce_translate_int_to_string(), + * mce_translate_string_to_int(), + * mce_translation_t + */ +#include "mce-log.h" /* mce_log(), LL_* */ +#include "mce-dbus.h" /* Direct: + * --- + * mce_dbus_handler_add(), + * dbus_send_message(), + * dbus_new_signal(), + * dbus_new_method_reply(), + * dbus_message_get_args(), + * dbus_message_get_no_reply(), + * dbus_message_append_args(), + * dbus_message_unref(), + * dbus_error_init(), + * dbus_error_free(), + * DBUS_MESSAGE_TYPE_METHOD_CALL, + * DBUS_TYPE_STRING, + * DBUS_TYPE_INVALID, + * DBusMessage, DBusError, + * dbus_bool_t + * + * Indirect: + * --- + * MCE_SIGNAL_IF, + * MCE_SIGNAL_PATH, + * MCE_REQUEST_IF, + * MCE_CALL_STATE_GET, + * MCE_CALL_STATE_CHANGE_REQ, + * MCE_CALL_STATE_SIG + */ +#include "datapipe.h" /* FIXME execute_datapipe() */ + +/** Module name */ +#define MODULE_NAME "callstate" + +/** Functionality provided by this module */ +static const gchar *const provides[] = { MODULE_NAME, NULL }; + +/** Module information */ +G_MODULE_EXPORT module_info_struct module_info = { + /** Name of the module */ + .name = MODULE_NAME, + /** Module provides */ + .provides = provides, + /** Module priority */ + .priority = 250 +}; + +/** Mapping of call state integer <-> call state string */ +static const mce_translation_t call_state_translation[] = { + { + .number = CALL_STATE_NONE, + .string = MCE_CALL_STATE_NONE + }, { + .number = CALL_STATE_RINGING, + .string = MCE_CALL_STATE_RINGING, + }, { + .number = CALL_STATE_ACTIVE, + .string = MCE_CALL_STATE_ACTIVE, + }, { + .number = CALL_STATE_SERVICE, + .string = MCE_CALL_STATE_SERVICE + }, { /* MCE_INVALID_TRANSLATION marks the end of this array */ + .number = MCE_INVALID_TRANSLATION, + .string = MCE_CALL_STATE_NONE + } +}; + +/** Mapping of call type integer <-> call type string */ +static const mce_translation_t call_type_translation[] = { + { + .number = NORMAL_CALL, + .string = MCE_NORMAL_CALL + }, { + .number = EMERGENCY_CALL, + .string = MCE_EMERGENCY_CALL + }, { /* MCE_INVALID_TRANSLATION marks the end of this array */ + .number = MCE_INVALID_TRANSLATION, + .string = MCE_NORMAL_CALL + } +}; + +/** List of monitored call state requesters; holds zero or one entries */ +static GSList *call_state_monitor_list = NULL; + +/** Keep track of whether call state is monitored */ +static gboolean call_state_is_monitored = FALSE; + +/** + * Send the call state and type + * + * @param method_call A DBusMessage to reply to; + * pass NULL to send a signal instead + * @param call_state A string representation of an alternate state + * to send instead of the real call state + * @param call_type A string representation of an alternate type + * to send instead of the real call type + * @return TRUE on success, FALSE on failure + */ +static gboolean send_call_state(DBusMessage *const method_call, + const gchar *const call_state, + const gchar *const call_type) +{ + DBusMessage *msg = NULL; + gboolean status = FALSE; + const gchar *sstate; + const gchar *stype; + + /* Allow spoofing */ + if (call_state != NULL) + sstate = call_state; + else + sstate = mce_translate_int_to_string(call_state_translation, + datapipe_get_gint(call_state_pipe)); + + if (call_type != NULL) + stype = call_type; + else + stype = mce_translate_int_to_string(call_type_translation, + datapipe_get_gint(call_type_pipe)); + + /* If method_call is set, send a reply, + * otherwise, send a signal + */ + if (method_call != NULL) { + msg = dbus_new_method_reply(method_call); + } else { + /* sig_call_state_ind */ + msg = dbus_new_signal(MCE_SIGNAL_PATH, MCE_SIGNAL_IF, + MCE_CALL_STATE_SIG); + } + + /* Append the call state and call type */ + if (dbus_message_append_args(msg, + DBUS_TYPE_STRING, &sstate, + DBUS_TYPE_STRING, &stype, + DBUS_TYPE_INVALID) == FALSE) { + mce_log(LL_CRIT, + "Failed to append %sarguments to D-Bus message " + "for %s.%s", + method_call ? "reply " : "", + method_call ? MCE_REQUEST_IF : + MCE_SIGNAL_IF, + method_call ? MCE_CALL_STATE_GET : + MCE_CALL_STATE_SIG); + dbus_message_unref(msg); + goto EXIT; + } + + /* Send the message */ + status = dbus_send_message(msg); + +EXIT: + return status; +} + +/** + * D-Bus callback used for monitoring the process that requested + * the call state; if that process exits, immediately + * restore the call state to "none" and call type to "normal" + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean call_state_owner_monitor_dbus_cb(DBusMessage *const msg) +{ + gboolean status = FALSE; + const gchar *old_name; + const gchar *new_name; + const gchar *service; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + /* Extract result */ + if (dbus_message_get_args(msg, &error, + DBUS_TYPE_STRING, &service, + DBUS_TYPE_STRING, &old_name, + DBUS_TYPE_STRING, &new_name, + DBUS_TYPE_INVALID) == FALSE) { + mce_log(LL_ERR, + "Failed to get argument from %s.%s; %s", + "org.freedesktop.DBus", "NameOwnerChanged", + error.message); + dbus_error_free(&error); + goto EXIT; + } + + /* Remove the name monitor for the call state requester */ + if (mce_dbus_owner_monitor_remove(old_name, + &call_state_monitor_list) == 0) { + /* Signal the new call state/type */ + send_call_state(NULL, MCE_CALL_STATE_NONE, MCE_NORMAL_CALL); + + (void)execute_datapipe(&call_state_pipe, + GINT_TO_POINTER(CALL_STATE_NONE), + USE_INDATA, CACHE_INDATA); + + (void)execute_datapipe(&call_type_pipe, + GINT_TO_POINTER(NORMAL_CALL), + USE_INDATA, CACHE_INDATA); + + call_state_is_monitored = FALSE; + } + + status = TRUE; + +EXIT: + return status; +} + +/** + * D-Bus callback for the call state change request method call + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean change_call_state_dbus_cb(DBusMessage *const msg) +{ + call_state_t old_call_state = datapipe_get_gint(call_state_pipe); + call_state_t old_call_type = datapipe_get_gint(call_type_pipe); + const gchar *sender = dbus_message_get_sender(msg); + call_state_t call_state = CALL_STATE_NONE; + dbus_bool_t monitored_owner_ok = FALSE; + call_type_t call_type = NORMAL_CALL; + dbus_bool_t state_changed = FALSE; + DBusMessage *reply = NULL; + const gchar *state = NULL; + const gchar *type = NULL; + gboolean status = FALSE; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + mce_log(LL_DEBUG, + "Received set call state request"); + + if (dbus_message_get_args(msg, &error, + DBUS_TYPE_STRING, &state, + DBUS_TYPE_STRING, &type, + DBUS_TYPE_INVALID) == FALSE) { + // XXX: return an error! + mce_log(LL_CRIT, + "Failed to get argument from %s.%s: %s", + MCE_REQUEST_IF, MCE_CALL_STATE_CHANGE_REQ, + error.message); + dbus_error_free(&error); + goto EXIT; + } + + /* Convert call state to enum */ + call_state = mce_translate_string_to_int(call_state_translation, + state); + + if (call_state == MCE_INVALID_TRANSLATION) { + mce_log(LL_DEBUG, + "Invalid call state received; request ignored"); + goto EXIT; + } + + /* Convert call type to enum */ + call_type = mce_translate_string_to_int(call_type_translation, + type); + + if (call_type == MCE_INVALID_TRANSLATION) { + mce_log(LL_DEBUG, + "Invalid call type received; request ignored"); + goto EXIT; + } + + /* If call state isn't monitored or if the request comes from + * the owner of the current state, then some additional changes + * are ok + */ + if ((call_state_is_monitored == FALSE) || + (call_state_monitor_list == NULL) || + ((mce_dbus_is_owner_monitored(sender, + call_state_monitor_list) == TRUE))) { + monitored_owner_ok = TRUE; + } + + /* Only transitions to/from "none" are allowed, + * and from "ringing" to "active", + * to avoid race conditions; except when new tuple + * is active:emergency + */ + if ((call_state != CALL_STATE_NONE) && + (old_call_state != CALL_STATE_NONE) && + ((call_state != CALL_STATE_ACTIVE) || + (old_call_state != CALL_STATE_RINGING)) && + ((call_state != CALL_STATE_RINGING) || + (old_call_state != CALL_STATE_ACTIVE) || + (monitored_owner_ok == FALSE)) && + ((call_state != CALL_STATE_ACTIVE) || + (call_type != EMERGENCY_CALL))) { + mce_log(LL_INFO, + "Call state change vetoed. Requested: %i:%i " + "(current: %i:%i)", + call_state, call_type, + old_call_state, old_call_type); + goto EXIT; + } + +#ifdef STRICT_CALL_STATE_OWNER_POLICY + /* We always allow changes to the call state + * if the new type is emergency, or if the old call state is none, + * but otherwise we only allow them if the requester of + * the change is the owner of the current call state + */ + if ((old_call_state != CALL_STATE_NONE) && + (call_type != EMERGENCY_CALL) && + (monitored_owner_ok == FALSE)) { + mce_log(LL_ERR, + "Call state change vetoed. " + "`%s' request the new call state (%i:%i), " + "but does not own current call state (%i:%i)", + sender, + call_state, call_type, + old_call_state, old_call_type); + goto EXIT; + } +#endif /* STRICT_CALL_STATE_OWNER_POLICY */ + + if (call_state != CALL_STATE_NONE) { + if (mce_dbus_owner_monitor_add(sender, + call_state_owner_monitor_dbus_cb, + &call_state_monitor_list, 1) == -1) { + /* This is dangerous, but calls are our priority */ + mce_log(LL_ERR, + "Failed to add a D-Bus service owner monitor " + "for the call state; " + "call state will not be monitored!"); + call_state_is_monitored = FALSE; + } else { + call_state_is_monitored = TRUE; + } + } else { + (void)mce_dbus_owner_monitor_remove(sender, + &call_state_monitor_list); + call_state_is_monitored = FALSE; + } + + state_changed = TRUE; + +EXIT: + /* Setup the reply */ + reply = dbus_new_method_reply(msg); + + /* Append the result */ + if (dbus_message_append_args(reply, + DBUS_TYPE_BOOLEAN, &state_changed, + DBUS_TYPE_INVALID) == FALSE) { + mce_log(LL_CRIT, + "Failed to append reply arguments to D-Bus " + "message for %s.%s", + MCE_REQUEST_IF, MCE_CALL_STATE_CHANGE_REQ); + dbus_message_unref(reply); + + /* If we cannot send the reply, + * we have to abort the state change + */ + state_changed = FALSE; + } else { + /* Send the message */ + status = dbus_send_message(reply); + } + + /* If the state changed, signal the new state; + * first externally, then internally + * + * The reason we do it externally first is to + * make sure that the camera application doesn't + * grab audio, otherwise the ring tone might go missing + */ + if (state_changed == TRUE) { + /* Signal the new call state/type */ + send_call_state(NULL, state, type); + + (void)execute_datapipe(&call_state_pipe, + GINT_TO_POINTER(call_state), + USE_INDATA, CACHE_INDATA); + + (void)execute_datapipe(&call_type_pipe, + GINT_TO_POINTER(call_type), + USE_INDATA, CACHE_INDATA); + } + + return status; +} + +/** + * D-Bus callback for the get call state method call + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean get_call_state_dbus_cb(DBusMessage *const msg) +{ + gboolean status = FALSE; + + mce_log(LL_DEBUG, "Received call state get request"); + + /* Try to send a reply that contains the current call state and type */ + if (send_call_state(msg, NULL, NULL) == FALSE) + goto EXIT; + + status = TRUE; + +EXIT: + return status; +} + +/** + * Init function for the call state module + * + * @todo XXX status needs to be set on error! + * + * @param module Unused + * @return NULL on success, a string with an error message on failure + */ +G_MODULE_EXPORT const gchar *g_module_check_init(GModule *module); +const gchar *g_module_check_init(GModule *module) +{ + (void)module; + + /* req_call_state_change */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_CALL_STATE_CHANGE_REQ, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + change_call_state_dbus_cb) == NULL) + goto EXIT; + + /* get_call_state */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_CALL_STATE_GET, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + get_call_state_dbus_cb) == NULL) + goto EXIT; + +EXIT: + return NULL; +} + +/** + * Exit function for the call state module + * + * @todo D-Bus unregistration + * + * @param module Unused + */ +G_MODULE_EXPORT void g_module_unload(GModule *module); +void g_module_unload(GModule *module) +{ + (void)module; + + return; +} diff --git a/modules/callstate.h b/modules/callstate.h new file mode 100644 index 00000000..e6959ad9 --- /dev/null +++ b/modules/callstate.h @@ -0,0 +1,30 @@ +/** + * @file callstate.h + * Headers for the callstate module + *

+ * Copyright © 2009 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _CALLSTATE_H_ +#define _CALLSTATE_H_ + +/* If this is set, the call state can only be modified by MCE and the owner + * of the current call state, unless the old call state is "none" or the + * new call type is emergency + */ +#define STRICT_CALL_STATE_OWNER_POLICY + +#endif /* _CALLSTATE_H_ */ diff --git a/modules/camera.c b/modules/camera.c new file mode 100644 index 00000000..d6575e6e --- /dev/null +++ b/modules/camera.c @@ -0,0 +1,175 @@ +/** + * @file camera.c + * Camera module -- this handles the camera LED-indicator for MCE + *

+ * Copyright © 2007-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include +#include + +#include /* strncmp(), strlen() */ + +#include "mce.h" +#include "camera.h" + +#include "mce-io.h" /* mce_register_io_monitor_string(), + * mce_unregister_io_monitor() + */ +#include "mce-conf.h" /* mce_conf_get_bool() */ +#include "datapipe.h" /* execute_datapipe(), + * execute_datapipe_output_triggers() + */ + +/** Unlock the tklock if the camera is popped out? */ +static gboolean popout_unlock = DEFAULT_CAMERA_POPOUT_UNLOCK; + +/** Module name */ +#define MODULE_NAME "camera" + +/** Functionality provided by this module */ +static const gchar *const provides[] = { MODULE_NAME, NULL }; + +/** Functionality that this module depends on */ +static const gchar *const depends[] = { "tklock", NULL }; + +/** Functionality that this module recommends */ +static const gchar *const recommends[] = { "led", NULL }; + +/** Module information */ +G_MODULE_EXPORT module_info_struct module_info = { + /** Name of the module */ + .name = MODULE_NAME, + /** Module dependencies */ + .depends = depends, + /** Module recommends */ + .recommends = recommends, + /** Module provides */ + .provides = provides, + /** Module priority */ + .priority = 250 +}; + +/** ID for the camera active state I/O monitor */ +static gconstpointer camera_active_state_iomon_id = NULL; + +/** ID for the camera pop-out state I/O monitor */ +static gconstpointer camera_popout_state_iomon_id = NULL; + +/** + * I/O monitor callback for the camera active state + * + * @param data The new data + * @param bytes_read Unused + */ +static void camera_active_state_cb(gpointer data, gsize bytes_read) +{ + (void)bytes_read; + + if (!strncmp(data, MCE_CAMERA_ACTIVE, strlen(MCE_CAMERA_ACTIVE))) { + execute_datapipe_output_triggers(&led_pattern_activate_pipe, + MCE_LED_PATTERN_CAMERA, + USE_INDATA); + } else { + execute_datapipe_output_triggers(&led_pattern_deactivate_pipe, + MCE_LED_PATTERN_CAMERA, + USE_INDATA); + } +} + +/** + * I/O monitor callback for the camera pop-out state + * + * @param data The new data + * @param bytes_read Unused + */ +static void camera_popout_state_cb(gpointer data, gsize bytes_read) +{ + (void)bytes_read; + + /* Generate activity */ + (void)execute_datapipe(&device_inactive_pipe, GINT_TO_POINTER(FALSE), + USE_INDATA, CACHE_INDATA); + + if (popout_unlock == FALSE) + goto EXIT; + + /* Unlock tklock if camera is popped out */ + if (!strncmp(data, MCE_CAMERA_POPPED_OUT, + strlen(MCE_CAMERA_POPPED_OUT))) { + /* Request delayed unlock of touchscreen/keypad lock */ + (void)execute_datapipe(&tk_lock_pipe, + GINT_TO_POINTER(LOCK_OFF_DELAYED), + USE_INDATA, CACHE_INDATA); + } + +EXIT: + return; +} + +/** + * Init function for the camera module + * + * @todo XXX status needs to be set on error! + * + * @param module Unused + * @return NULL on success, a string with an error message on failure + */ +G_MODULE_EXPORT const gchar *g_module_check_init(GModule *module); +const gchar *g_module_check_init(GModule *module) +{ + (void)module; + + /* Get configuration options */ + popout_unlock = mce_conf_get_bool(MCE_CONF_TKLOCK_GROUP, + MCE_CONF_CAMERA_POPOUT_UNLOCK, + DEFAULT_CAMERA_POPOUT_UNLOCK, + NULL); + + /* Register I/O monitors */ + // FIXME: error handling? + camera_active_state_iomon_id = + mce_register_io_monitor_string(-1, CAMERA_ACTIVE_STATE_PATH, + MCE_IO_ERROR_POLICY_IGNORE, + G_IO_PRI | G_IO_ERR, + TRUE, camera_active_state_cb); + + camera_popout_state_iomon_id = + mce_register_io_monitor_string(-1, CAMERA_POPOUT_STATE_PATH, + MCE_IO_ERROR_POLICY_IGNORE, + G_IO_PRI | G_IO_ERR, + TRUE, camera_popout_state_cb); + +//EXIT: + return NULL; +} + +/** + * Exit function for the camera module + * + * @param module Unused + */ +G_MODULE_EXPORT void g_module_unload(GModule *module); +void g_module_unload(GModule *module) +{ + (void)module; + + /* Unregister I/O monitors */ + mce_unregister_io_monitor(camera_popout_state_iomon_id); + mce_unregister_io_monitor(camera_active_state_iomon_id); + + return; +} diff --git a/modules/camera.h b/modules/camera.h new file mode 100644 index 00000000..907878b3 --- /dev/null +++ b/modules/camera.h @@ -0,0 +1,49 @@ +/** + * @file camera.h + * Headers for the camera LED-indicator module + *

+ * Copyright © 2007 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _CAMERA_H_ +#define _CAMERA_H_ + +/** Path to the SysFS interface for the camera active state */ +#define CAMERA_ACTIVE_STATE_PATH "/sys/devices/platform/omap24xxcam/streaming" +/** Value for the camera active state */ +#define MCE_CAMERA_ACTIVE "active" +/** Value for the camera inactive state */ +#define MCE_CAMERA_INACTIVE "inactive" + +/** Path to the SysFS interface for the camera pop-out state */ +#define CAMERA_POPOUT_STATE_PATH "/sys/devices/platform/gpio-switch/cam_act/state" +/** Value for the camera in popped out state */ +#define MCE_CAMERA_POPPED_OUT "active" +/** Value for the camera in popped in state */ +#define MCE_CAMERA_POPPED_IN "inactive" + +#ifndef MCE_CONF_TKLOCK_GROUP +/** Name of Touchscreen/Keypad lock configuration group */ +#define MCE_CONF_TKLOCK_GROUP "TKLock" +#endif /* MCE_CONF_TKLOCK_GROUP */ + +/** Name of configuration key for camera popout unlock */ +#define MCE_CONF_CAMERA_POPOUT_UNLOCK "CameraPopoutUnlock" + +/** Default fallback setting for the touchscreen/keypad autolock */ +#define DEFAULT_CAMERA_POPOUT_UNLOCK TRUE /* FALSE / TRUE */ + +#endif /* _CAMERA_H_ */ diff --git a/modules/display.c b/modules/display.c new file mode 100644 index 00000000..15ef4db3 --- /dev/null +++ b/modules/display.c @@ -0,0 +1,2456 @@ +/** + * @file display.c + * Display module -- this implements display handling for MCE + *

+ * Copyright © 2007-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include +#include +#include /* g_access() */ + +#include /* errno */ +#include /* open() */ +#include /* O_RDWR */ +#include /* strcmp() */ +#include /* close() */ +#include /* FBIOBLANK, + * FB_BLANK_POWERDOWN, + * FB_BLANK_UNBLANK + */ +#include /* ioctl() */ + +#include /* MCE_CABC_MODE_OFF, + * MCE_CABC_MODE_UI, + * MCE_CABC_MODE_STILL_IMAGE, + * MCE_CABC_MODE_MOVING_IMAGE, + * MCE_DISPLAY_ON_STRING, + * MCE_DISPLAY_DIM_STRING, + * MCE_DISPLAY_OFF_STRING + */ + +#include "mce.h" /* display_state_t, + * charger_state_pipe, + * display_state_pipe, + * display_brightness_pipe, + * inactivity_timeout_pipe, + * led_pattern_deactivate_pipe, + * submode_pipe, + * system_state_pipe, + * device_inactive_pipe + */ +#include "display.h" + +#include "mce-io.h" /* mce_read_string_from_file(), + * mce_read_number_string_from_file(), + * mce_write_number_string_to_file() + */ +#include "mce-lib.h" /* strstr_delim(), + * mce_translate_string_to_int_with_default(), + * mce_translation_t + */ +#include "mce-log.h" /* mce_log(), LL_* */ +#include "mce-conf.h" /* mce_conf_get_int(), + * mce_conf_get_string() + */ +#include "mce-dbus.h" /* Direct: + * --- + * mce_dbus_handler_add(), + * dbus_send_message(), + * dbus_new_method_reply(), + * dbus_new_signal(), + * dbus_message_append_args(), + * dbus_message_get_no_reply(), + * dbus_message_unref(), + * DBusMessage, + * DBUS_MESSAGE_TYPE_METHOD_CALL, + * DBUS_MESSAGE_TYPE_SIGNAL, + * DBUS_TYPE_STRING, + * DBUS_TYPE_INVALID, + * dbus_bool_t + * + * Indirect: + * --- + * MCE_SIGNAL_IF, + * MCE_SIGNAL_PATH, + * MCE_REQUEST_IF, + * MCE_DISPLAY_STATUS_GET, + * MCE_DISPLAY_ON_REQ, + * MCE_DISPLAY_DIM_REQ, + * MCE_DISPLAY_OFF_REQ, + * MCE_PREVENT_BLANK_REQ, + * MCE_CABC_MODE_GET, + * MCE_CABC_MODE_REQ + */ +#include "mce-gconf.h" /* mce_gconf_get_int(), + * mce_gconf_get_bool(), + * mce_gconf_notifier_add(), + * gconf_entry_get_key(), + * gconf_value_get_int(), + * gconf_value_get_bool(), + * GConfClient, GConfEntry, GConfValue + */ +#include "datapipe.h" /* datapipe_get_gint(), + * execute_datapipe(), + * append_output_trigger_to_datapipe(), + * remove_output_trigger_from_datapipe() + */ + +/* These defines are taken from devicelock.h, but slightly modified */ +#ifndef DEVICELOCK_H +/** Devicelock D-Bus service */ +#define DEVLOCK_SERVICE "com.nokia.devicelock" + +/** Devicelock D-Bus service */ +#define DEVLOCK_PATH "/request" + +/** Set devicelock state */ +#define DEVLOCK_SET "setState" + +/** Enumeration of the valid locks on the device */ +enum LockType { + /** TouchAndKeyboard -- The touch screen and keypad lock */ + TouchAndKeyboard = 0, + /** Device -- The device lock, password protected lock screen */ + Device +}; + +/** Enumeration of the valid states that a lock can be in */ +enum LockState { + /** Unlocked - The lock is unlocked */ + Unlocked = 0, + /** Locked - The lock is being used */ + Locked, + /** Configuration - Open the locks configuration settings */ + Configuration, +/** WipeMMC - Secure wipe of the device */ + WipeMMC, + /** Inhibit - Stop the lock ui(s) from being displayed */ + Inhibit, + /** Undefined - Lock state is unknown or the lock does not exist */ + Undefined +}; +#endif /* DEVICELOCK_H */ + +// XXX: remove +#ifndef MCE_DISPLAY_LOW_POWER_MODE_REQ +#define MCE_DISPLAY_LOW_POWER_MODE_REQ "req_display_state_low_power" +#endif + +/** Module name */ +#define MODULE_NAME "display" + +/** Functionality provided by this module */ +static const gchar *const provides[] = { MODULE_NAME, NULL }; + +/** Module information */ +G_MODULE_EXPORT module_info_struct module_info = { + /** Name of the module */ + .name = MODULE_NAME, + /** Module provides */ + .provides = provides, + /** Module priority */ + .priority = 250 +}; + +/** GConf callback ID for display brightness setting */ +static guint disp_brightness_gconf_cb_id = 0; + +/** Display dimming timeout setting */ +static gint disp_dim_timeout = DEFAULT_DIM_TIMEOUT; +/** GConf callback ID for display dimming timeout setting */ +static guint disp_dim_timeout_gconf_cb_id = 0; + +/** Display blanking timeout setting */ +static gint disp_blank_timeout = DEFAULT_BLANK_TIMEOUT; +/** GConf callback ID for display blanking timeout setting */ +static guint disp_blank_timeout_gconf_cb_id = 0; + +/** ID for display blank prevention timer source */ +static guint blank_prevent_timeout_cb_id = 0; + +/** GConf callback ID for display blanking timeout setting */ +static guint adaptive_dimming_enabled_gconf_cb_id = 0; + +/** ID for adaptive display dimming timer source */ +static guint adaptive_dimming_timeout_cb_id = 0; + +/** Use adaptive timeouts for dimming */ +static gboolean adaptive_dimming_enabled = DEFAULT_ADAPTIVE_DIMMING_ENABLED; + +/** GConf callback ID for the threshold for adaptive display dimming */ +static guint adaptive_dimming_threshold_gconf_cb_id = 0; + +/** Threshold to use for adaptive timeouts for dimming in milliseconds */ +static gint adaptive_dimming_threshold = DEFAULT_ADAPTIVE_DIMMING_THRESHOLD; + +/** Display blank prevention timer */ +static gint blank_prevent_timeout = BLANK_PREVENT_TIMEOUT; + +/** Bootup dim additional timeout */ +static gint bootup_dim_additional_timeout = 0; + +/** Cached brightness */ +static gint cached_brightness = -1; + +/** Target brightness */ +static gint target_brightness = -1; + +/** Brightness */ +static gint set_brightness = -1; + +/** Dim brightness */ +static gint dim_brightness = (DEFAULT_MAXIMUM_DISPLAY_BRIGHTNESS * + DEFAULT_DIM_BRIGHTNESS) / 100; + +/** CABC mode -- uses the SysFS mode names */ +static const gchar *cabc_mode = DEFAULT_CABC_MODE; +/** + * CABC mode (power save mode active) -- uses the SysFS mode names; + * NULL to disable + */ +static const gchar *psm_cabc_mode = NULL; + +/** Fadeout step length */ +static gint brightness_fade_steplength = 2; + +/** Brightness fade timeout callback ID */ +static guint brightness_fade_timeout_cb_id = 0; +/** Display dimming timeout callback ID */ +static guint dim_timeout_cb_id = 0; +/** Display blanking timeout callback ID */ +static guint blank_timeout_cb_id = 0; + +/** Charger state */ +static gboolean charger_connected = FALSE; + +/** Maximum display brightness */ +static gint maximum_display_brightness = DEFAULT_MAXIMUM_DISPLAY_BRIGHTNESS; + +/** File used to set display brightness */ +static gchar *brightness_file = NULL; +/** File pointer used to set display brightness */ +static FILE *brightness_fp = NULL; +/** File used to get maximum display brightness */ +static gchar *max_brightness_file = NULL; +/** File used to set the CABC mode */ +static gchar *cabc_mode_file = NULL; +/** File used to get the available CABC modes */ +static gchar *cabc_available_modes_file = NULL; +/** Is hardware driven display fading supported */ +static gboolean hw_display_fading_supported = FALSE; +/** Is display low power mode supported */ +static gboolean display_low_power_mode_supported = FALSE; + +/** Brightness change policies */ +typedef enum { + /** Policy not set */ + BRIGHTNESS_CHANGE_POLICY_INVALID = MCE_INVALID_TRANSLATION, + /** Brightness changes instantly */ + BRIGHTNESS_CHANGE_DIRECT = 0, + /** Fade with fixed step time */ + BRIGHTNESS_CHANGE_STEP_TIME = 1, + /** Fade time independent of number of steps faded */ + BRIGHTNESS_CHANGE_CONSTANT_TIME = 2, + /** Default setting when brightness increases */ + DEFAULT_BRIGHTNESS_INCREASE_POLICY = BRIGHTNESS_CHANGE_CONSTANT_TIME, + /** Default setting when brightness decreases */ + DEFAULT_BRIGHTNESS_DECREASE_POLICY = BRIGHTNESS_CHANGE_CONSTANT_TIME +} brightness_change_policy_t; + +/** Mapping of brightness change integer <-> policy string */ +static const mce_translation_t brightness_change_policy_translation[] = { + { + .number = BRIGHTNESS_CHANGE_DIRECT, + .string = "direct", + }, { + .number = BRIGHTNESS_CHANGE_STEP_TIME, + .string = "steptime", + }, { + .number = BRIGHTNESS_CHANGE_CONSTANT_TIME, + .string = "constanttime", + }, { /* MCE_INVALID_TRANSLATION marks the end of this array */ + .number = MCE_INVALID_TRANSLATION, + .string = NULL + } +}; + +/** Real display brightness setting */ +static gint real_disp_brightness = DEFAULT_DISP_BRIGHTNESS; + +/** Brightness increase policy */ +static brightness_change_policy_t brightness_increase_policy = + DEFAULT_BRIGHTNESS_INCREASE_POLICY; + +/** Brightness decrease policy */ +static brightness_change_policy_t brightness_decrease_policy = + DEFAULT_BRIGHTNESS_DECREASE_POLICY; + +/** Brightness increase step-time */ +static gint brightness_increase_step_time = + DEFAULT_BRIGHTNESS_INCREASE_STEP_TIME; + +/** Brightness decrease step-time */ +static gint brightness_decrease_step_time = + DEFAULT_BRIGHTNESS_DECREASE_STEP_TIME; + +/** Brightness increase constant time */ +static gint brightness_increase_constant_time = + DEFAULT_BRIGHTNESS_INCREASE_CONSTANT_TIME; + +/** Brightness decrease constant time */ +static gint brightness_decrease_constant_time = + DEFAULT_BRIGHTNESS_DECREASE_CONSTANT_TIME; + +/** + * Display brightness setting (power save mode active); + * -1 to disable + */ +static gint psm_disp_brightness = -1; + +/** Inhibit type */ +typedef enum { + /** Inhibit value invalid */ + INHIBIT_INVALID = -1, + /** No inhibit */ + INHIBIT_OFF = 0, + /** Default value */ + DEFAULT_BLANKING_INHIBIT_MODE = INHIBIT_OFF, + /** Inhibit blanking; always keep on if charger connected */ + INHIBIT_STAY_ON_WITH_CHARGER = 1, + /** Inhibit blanking; always keep on or dimmed if charger connected */ + INHIBIT_STAY_DIM_WITH_CHARGER = 2, + /** Inhibit blanking; always keep on */ + INHIBIT_STAY_ON = 3, + /** Inhibit blanking; always keep on or dimmed */ + INHIBIT_STAY_DIM = 4, +} inhibit_t; + +/** Display blanking inhibit mode */ +static inhibit_t blanking_inhibit_mode = DEFAULT_BLANKING_INHIBIT_MODE; +/** GConf callback ID for display blanking inhibit mode setting */ +static guint blanking_inhibit_mode_gconf_cb_id = 0; + +/** Blanking inhibited */ +static gboolean blanking_inhibited = FALSE; +/** Dimming inhibited */ +static gboolean dimming_inhibited = FALSE; + +/** List of monitored blanking pause requesters */ +static GSList *blanking_pause_monitor_list = NULL; + +/** List of monitored CABC mode requesters */ +static GSList *cabc_mode_monitor_list = NULL; + +static void update_blanking_inhibit(gboolean timed_inhibit); +static void cancel_dim_timeout(void); + +/** Display type */ +typedef enum { + /** Display type unset */ + DISPLAY_TYPE_UNSET = -1, + /** No display available; XXX should never happen */ + DISPLAY_TYPE_NONE = 0, + /** Generic display interface without CABC */ + DISPLAY_TYPE_GENERIC = 1, + /** EID l4f00311 with CABC */ + DISPLAY_TYPE_L4F00311 = 2, + /** Sony acx565akm with CABC */ + DISPLAY_TYPE_ACX565AKM = 3, + /** Taal display */ + DISPLAY_TYPE_TAAL = 4, + /** Himalaya display */ + DISPLAY_TYPE_HIMALAYA = 5, + /** Generic display name */ + DISPLAY_TYPE_DISPLAY0 = 6, + /** Generic name for ACPI-controlled displays */ + DISPLAY_TYPE_ACPI_VIDEO0 = 7 +} display_type_t; + +/** + * Array of possible display dim timeouts + */ +static GSList *possible_dim_timeouts = NULL; + +/** + * Index for the array of possible display dim timeouts + */ +static guint dim_timeout_index = 0; + +/** + * Index for the array of adaptive dimming timeout multipliers + */ +static guint adaptive_dimming_index = 0; + +/** + * CABC mapping; D-Bus API modes vs SysFS mode + */ +typedef struct { + /** CABC mode D-Bus name */ + const gchar *const dbus; + /** CABC mode SysFS name */ + const gchar *const sysfs; + /** CABC mode available */ + gboolean available; +} cabc_mode_mapping_t; + +/** + * CABC mappings; D-Bus API modes vs SysFS mode + */ +cabc_mode_mapping_t cabc_mode_mapping[] = { + { + .dbus = MCE_CABC_MODE_OFF, + .sysfs = CABC_MODE_OFF, + .available = FALSE + }, { + .dbus = MCE_CABC_MODE_UI, + .sysfs = CABC_MODE_UI, + .available = FALSE + }, { + .dbus = MCE_CABC_MODE_STILL_IMAGE, + .sysfs = CABC_MODE_STILL_IMAGE, + .available = FALSE + }, { + .dbus = MCE_CABC_MODE_MOVING_IMAGE, + .sysfs = CABC_MODE_MOVING_IMAGE, + .available = FALSE + }, { + .dbus = NULL, + .sysfs = NULL, + .available = FALSE + } +}; + +/** + * Get the display type + * + * @return The display type + */ +static display_type_t get_display_type(void) +{ + static display_type_t display_type = DISPLAY_TYPE_UNSET; + + /* If we have the display type already, return it */ + if (display_type != DISPLAY_TYPE_UNSET) + goto EXIT; + + if (g_access(DISPLAY_CABC_PATH DISPLAY_ACX565AKM, W_OK) == 0) { + display_type = DISPLAY_TYPE_ACX565AKM; + + brightness_file = g_strconcat(DISPLAY_CABC_PATH, DISPLAY_ACX565AKM, DISPLAY_CABC_BRIGHTNESS_FILE, NULL); + max_brightness_file = g_strconcat(DISPLAY_CABC_PATH, DISPLAY_ACX565AKM, DISPLAY_CABC_MAX_BRIGHTNESS_FILE, NULL); + cabc_mode_file = g_strconcat(DISPLAY_CABC_PATH, DISPLAY_ACX565AKM, DISPLAY_CABC_MODE_FILE, NULL); + cabc_available_modes_file = g_strconcat(DISPLAY_CABC_PATH, DISPLAY_ACX565AKM, DISPLAY_CABC_AVAILABLE_MODES_FILE, NULL); + } else if (g_access(DISPLAY_CABC_PATH DISPLAY_L4F00311, W_OK) == 0) { + display_type = DISPLAY_TYPE_L4F00311; + + brightness_file = g_strconcat(DISPLAY_CABC_PATH, DISPLAY_L4F00311, DISPLAY_CABC_BRIGHTNESS_FILE, NULL); + max_brightness_file = g_strconcat(DISPLAY_CABC_PATH, DISPLAY_L4F00311, DISPLAY_CABC_MAX_BRIGHTNESS_FILE, NULL); + cabc_mode_file = g_strconcat(DISPLAY_CABC_PATH, DISPLAY_L4F00311, DISPLAY_CABC_MODE_FILE, NULL); + cabc_available_modes_file = g_strconcat(DISPLAY_CABC_PATH, DISPLAY_L4F00311, DISPLAY_CABC_AVAILABLE_MODES_FILE, NULL); + } else if (g_access(DISPLAY_CABC_PATH DISPLAY_TAAL, W_OK) == 0) { + display_type = DISPLAY_TYPE_TAAL; + + brightness_file = g_strconcat(DISPLAY_CABC_PATH, DISPLAY_TAAL, DISPLAY_CABC_BRIGHTNESS_FILE, NULL); + max_brightness_file = g_strconcat(DISPLAY_CABC_PATH, DISPLAY_TAAL, DISPLAY_CABC_MAX_BRIGHTNESS_FILE, NULL); + + cabc_mode_file = g_strconcat(DISPLAY_CABC_PATH, DISPLAY_TAAL, "/device", DISPLAY_CABC_MODE_FILE, NULL); + cabc_available_modes_file = g_strconcat(DISPLAY_CABC_PATH, DISPLAY_TAAL, "/device", DISPLAY_CABC_AVAILABLE_MODES_FILE, NULL); + } else if (g_access(DISPLAY_CABC_PATH DISPLAY_HIMALAYA, W_OK) == 0) { + display_type = DISPLAY_TYPE_HIMALAYA; + + brightness_file = g_strconcat(DISPLAY_CABC_PATH, DISPLAY_HIMALAYA, DISPLAY_CABC_BRIGHTNESS_FILE, NULL); + max_brightness_file = g_strconcat(DISPLAY_CABC_PATH, DISPLAY_HIMALAYA, DISPLAY_CABC_MAX_BRIGHTNESS_FILE, NULL); + + cabc_mode_file = g_strconcat(DISPLAY_CABC_PATH, DISPLAY_HIMALAYA, "/device", DISPLAY_CABC_MODE_FILE, NULL); + cabc_available_modes_file = g_strconcat(DISPLAY_CABC_PATH, DISPLAY_HIMALAYA, "/device", DISPLAY_CABC_AVAILABLE_MODES_FILE, NULL); + } else if (g_access(DISPLAY_CABC_PATH DISPLAY_DISPLAY0, W_OK) == 0) { + display_type = DISPLAY_TYPE_DISPLAY0; + + brightness_file = g_strconcat(DISPLAY_CABC_PATH, DISPLAY_DISPLAY0, DISPLAY_CABC_BRIGHTNESS_FILE, NULL); + max_brightness_file = g_strconcat(DISPLAY_CABC_PATH, DISPLAY_DISPLAY0, DISPLAY_CABC_MAX_BRIGHTNESS_FILE, NULL); + + cabc_mode_file = g_strconcat(DISPLAY_CABC_PATH, DISPLAY_DISPLAY0, "/device", DISPLAY_CABC_MODE_FILE, NULL); + cabc_available_modes_file = g_strconcat(DISPLAY_CABC_PATH, DISPLAY_DISPLAY0, "/device", DISPLAY_CABC_AVAILABLE_MODES_FILE, NULL); + hw_display_fading_supported = (g_access(DISPLAY_HARDWARE_DIMMING, W_OK) == 0); + +#if 0 + /* Enable hardware fading if supported */ + if (hw_display_fading_supported == TRUE) + (void)mce_write_number_string_to_file(DISPLAY_HARDWARE_DIMMING, 1, NULL, TRUE, TRUE); +#endif + } else if (g_access(DISPLAY_CABC_PATH DISPLAY_ACPI_VIDEO0, W_OK) == 0) { + display_type = DISPLAY_TYPE_ACPI_VIDEO0; + + brightness_file = g_strconcat(DISPLAY_CABC_PATH, DISPLAY_ACPI_VIDEO0, DISPLAY_CABC_BRIGHTNESS_FILE, NULL); + max_brightness_file = g_strconcat(DISPLAY_CABC_PATH, DISPLAY_ACPI_VIDEO0, DISPLAY_CABC_MAX_BRIGHTNESS_FILE, NULL); + } else if (g_access(DISPLAY_GENERIC_PATH, W_OK) == 0) { + display_type = DISPLAY_TYPE_GENERIC; + + brightness_file = g_strconcat(DISPLAY_GENERIC_PATH, DISPLAY_GENERIC_BRIGHTNESS_FILE, NULL); + max_brightness_file = g_strconcat(DISPLAY_GENERIC_PATH, DISPLAY_GENERIC_MAX_BRIGHTNESS_FILE, NULL); + } else { + display_type = DISPLAY_TYPE_NONE; + } + + mce_log(LL_DEBUG, "Display type: %d", display_type); + +EXIT: + return display_type; +} + +/** + * Set CABC mode + * + * @param mode The CABC mode to set + */ +static void set_cabc_mode(const gchar *const mode) +{ + static gboolean available_modes_scanned = FALSE; + const gchar *tmp = NULL; + gint i; + + if (cabc_available_modes_file == NULL) + goto EXIT; + + /* Update the list of available modes against the list we support */ + if (available_modes_scanned == FALSE) { + gchar *available_modes = NULL; + + available_modes_scanned = TRUE; + + if (mce_read_string_from_file(cabc_available_modes_file, + &available_modes) == FALSE) + goto EXIT; + + for (i = 0; (tmp = cabc_mode_mapping[i].sysfs) != NULL; i++) { + if (strstr_delim(available_modes, tmp, " ") != NULL) + cabc_mode_mapping[i].available = TRUE; + } + + g_free(available_modes); + } + + /* If the requested mode is supported, use it */ + for (i = 0; (tmp = cabc_mode_mapping[i].sysfs) != NULL; i++) { + if (cabc_mode_mapping[i].available == FALSE) + continue; + + if (!strcmp(tmp, mode)) { + mce_write_string_to_file(cabc_mode_file, tmp); + + /* Don't overwrite the regular CABC mode with the + * power save mode CABC mode + */ + if (psm_cabc_mode == NULL) + cabc_mode = tmp; + + break; + } + } + +EXIT: + return; +} + +/** + * Call the FBIOBLANK ioctl + * + * @param value The ioctl value to pass to the backlight + * @return TRUE on success, FALSE on failure + */ +static gboolean backlight_ioctl(int value) +{ + static int old_value = FB_BLANK_UNBLANK; + static int fd = -1; + gboolean status = FALSE; + + if (fd == -1) { + if ((fd = open(FB_DEVICE, O_RDWR)) == -1) { + mce_log(LL_CRIT, + "Failed to open `%s'; %s", + FB_DEVICE, g_strerror(errno)); + + /* Reset errno, + * to avoid false positives down the line + */ + errno = 0; + goto EXIT; + } + + old_value = !value; /* force ioctl() */ + } + + if (value != old_value) { + if (ioctl(fd, FBIOBLANK, value) == -1) { + mce_log(LL_CRIT, + "ioctl() FBIOBLANK (%d) failed on `%s'; %s", + value, FB_DEVICE, g_strerror(errno)); + + /* Reset errno, + * to avoid false positives down the line + */ + errno = 0; + + if (close(fd) == -1) { + mce_log(LL_ERR, + "Failed to close `%s': %s", + FB_DEVICE, g_strerror(errno)); + + /* Reset errno, + * to avoid false positives down the line + */ + errno = 0; + } + + fd = -1; + goto EXIT; + } + + old_value = value; + } + + status = TRUE; + +EXIT: + return status; +} + +/** + * Timeout callback for the brightness fade + * + * @param data Unused + * @return Returns TRUE to repeat, until the cached brightness has reached + * the destination value; when this happens, FALSE is returned + */ +static gboolean brightness_fade_timeout_cb(gpointer data) +{ + gboolean retval = TRUE; + + (void)data; + + if ((cached_brightness <= 0) && (target_brightness != 0)) { + backlight_ioctl(FB_BLANK_UNBLANK); + } + + if ((cached_brightness == -1) || + (ABS(cached_brightness - + target_brightness) < brightness_fade_steplength)) { + cached_brightness = target_brightness; + retval = FALSE; + } else if (target_brightness > cached_brightness) { + cached_brightness += brightness_fade_steplength; + } else { + cached_brightness -= brightness_fade_steplength; + } + + mce_write_number_string_to_file(brightness_file, + cached_brightness, + &brightness_fp, TRUE, FALSE); + + if (cached_brightness == 0) { + backlight_ioctl(FB_BLANK_POWERDOWN); + } + + if (retval == FALSE) + brightness_fade_timeout_cb_id = 0; + + return retval; +} + +/** + * Cancel the brightness fade timeout + */ +static void cancel_brightness_fade_timeout(void) +{ + /* Remove the timeout source for the display brightness fade */ + if (brightness_fade_timeout_cb_id != 0) { + g_source_remove(brightness_fade_timeout_cb_id); + brightness_fade_timeout_cb_id = 0; + } +} + +/** + * Setup the brightness fade timeout + * + * @param step_time The time between each brightness step + */ +static void setup_brightness_fade_timeout(gint step_time) +{ + cancel_brightness_fade_timeout(); + + /* Setup new timeout */ + brightness_fade_timeout_cb_id = + g_timeout_add(step_time, brightness_fade_timeout_cb, NULL); +} + +/** + * Update brightness fade + * + * Will fade from current value to new value + * + * @param new_brightness The new brightness to fade to + */ +static void update_brightness_fade(gint new_brightness) +{ + gboolean increase = (new_brightness >= cached_brightness); + gint step_time = 10; + + /* This should never happen, but just in case */ + if (cached_brightness == new_brightness) + goto EXIT; + + /* If we have support for HW-fading, or if we're using the direct + * brightness change policy, don't bother with any of this + */ + if ((hw_display_fading_supported == TRUE) || + ((brightness_increase_policy == BRIGHTNESS_CHANGE_DIRECT) && + (increase == TRUE)) || + ((brightness_decrease_policy == BRIGHTNESS_CHANGE_DIRECT) && + (increase == FALSE))) { + cancel_brightness_fade_timeout(); + cached_brightness = new_brightness; + target_brightness = new_brightness; + backlight_ioctl(FB_BLANK_UNBLANK); + mce_write_number_string_to_file(brightness_file, + new_brightness, + &brightness_fp, TRUE, FALSE); + goto EXIT; + } + + /* If we're already fading towards the right brightness, + * don't change anything + */ + if (target_brightness == new_brightness) + goto EXIT; + + target_brightness = new_brightness; + + if (increase == TRUE) { + if (brightness_increase_policy == BRIGHTNESS_CHANGE_STEP_TIME) + step_time = brightness_increase_step_time; + else { + step_time = brightness_increase_constant_time / + (new_brightness - cached_brightness); + } + } else { + if (brightness_decrease_policy == BRIGHTNESS_CHANGE_STEP_TIME) + step_time = brightness_decrease_step_time; + else { + step_time = brightness_decrease_constant_time / + (cached_brightness - new_brightness); + } + } + + /* Special case */ + if (step_time == 5) { + step_time = 2; + brightness_fade_steplength = 2; + } else { + brightness_fade_steplength = 1; + } + + setup_brightness_fade_timeout(step_time); + +EXIT: + return; +} + +/** + * Blank display + */ +static void display_blank(void) +{ + cancel_brightness_fade_timeout(); + cached_brightness = 0; + target_brightness = 0; + mce_write_number_string_to_file(brightness_file, 0, + &brightness_fp, TRUE, FALSE); + backlight_ioctl(FB_BLANK_POWERDOWN); +} + +/** + * Dim display + */ +static void display_dim(void) +{ + /* If we unblank, switch on display immediately */ + if (cached_brightness == 0) { + cached_brightness = dim_brightness; + target_brightness = dim_brightness; + backlight_ioctl(FB_BLANK_UNBLANK); + mce_write_number_string_to_file(brightness_file, + dim_brightness, + &brightness_fp, TRUE, FALSE); + } else { + update_brightness_fade(dim_brightness); + } +} + +/** + * Unblank display + */ +static void display_unblank(void) +{ + /* If we unblank, switch on display immediately */ + if (cached_brightness == 0) { + cached_brightness = set_brightness; + target_brightness = set_brightness; + backlight_ioctl(FB_BLANK_UNBLANK); + mce_write_number_string_to_file(brightness_file, + set_brightness, + &brightness_fp, TRUE, FALSE); + } else { + update_brightness_fade(set_brightness); + } +} + +/** + * Display brightness trigger + * + * @note A brightness request is only sent if the value changed + * @param data The display brightness stored in a pointer + */ +static void display_brightness_trigger(gconstpointer data) +{ + display_state_t display_state = datapipe_get_gint(display_state_pipe); + gint new_brightness = GPOINTER_TO_INT(data); + + /* If the pipe is choked, ignore the value */ + if (new_brightness == 0) + goto EXIT; + + /* Adjust the value, since it's a percentage value */ + new_brightness = (maximum_display_brightness * new_brightness) / 100; + + /* If we're just rehashing the same brightness value, don't bother */ + if ((new_brightness == cached_brightness) && (cached_brightness != -1)) + goto EXIT; + + /* The value we have here is for non-dimmed screen only */ + set_brightness = new_brightness; + + if ((display_state == MCE_DISPLAY_OFF) || + (display_state == MCE_DISPLAY_DIM)) + goto EXIT; + + update_brightness_fade(new_brightness); + +EXIT: + return; +} + +/** + * Timeout callback for display blanking + * + * @param data Unused + * @return Always returns FALSE, to disable the timeout + */ +static gboolean blank_timeout_cb(gpointer data) +{ + (void)data; + + blank_timeout_cb_id = 0; + + (void)execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_OFF), + USE_INDATA, CACHE_INDATA); + + return FALSE; +} + +/** + * Cancel the display blanking timeout + */ +static void cancel_blank_timeout(void) +{ + /* Remove the timeout source for display blanking */ + if (blank_timeout_cb_id != 0) { + g_source_remove(blank_timeout_cb_id); + blank_timeout_cb_id = 0; + } +} + +/** + * Setup blank timeout + */ +static void setup_blank_timeout(void) +{ + cancel_blank_timeout(); + cancel_dim_timeout(); + + if (blanking_inhibited == TRUE) + return; + + /* Setup new timeout */ + blank_timeout_cb_id = + g_timeout_add_seconds(disp_blank_timeout, + blank_timeout_cb, NULL); +} + +/** + * Timeout callback for adaptive dimming timeout + * + * @param data Unused + * @return Always returns FALSE, to disable the timeout + */ +static gboolean adaptive_dimming_timeout_cb(gpointer data) +{ + (void)data; + + adaptive_dimming_timeout_cb_id = 0; + adaptive_dimming_index = 0; + + return FALSE; +} + +/** + * Cancel the adaptive dimming timeout + */ +static void cancel_adaptive_dimming_timeout(void) +{ + /* Remove the timeout source for adaptive dimming */ + if (adaptive_dimming_timeout_cb_id != 0) { + g_source_remove(adaptive_dimming_timeout_cb_id); + adaptive_dimming_timeout_cb_id = 0; + } +} + +/** + * Setup adaptive dimming timeout + */ +static void setup_adaptive_dimming_timeout(void) +{ + cancel_adaptive_dimming_timeout(); + + if (adaptive_dimming_enabled == FALSE) + goto EXIT; + + /* Setup new timeout */ + adaptive_dimming_timeout_cb_id = + g_timeout_add_seconds(adaptive_dimming_threshold, + adaptive_dimming_timeout_cb, NULL); + +EXIT: + return; +} + +/** + * Timeout callback for display dimming + * + * @param data Unused + * @return Always returns FALSE, to disable the timeout + */ +static gboolean dim_timeout_cb(gpointer data) +{ + (void)data; + + dim_timeout_cb_id = 0; + + (void)execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_DIM), + USE_INDATA, CACHE_INDATA); + + return FALSE; +} + +/** + * Cancel display dimming timeout + */ +static void cancel_dim_timeout(void) +{ + /* Remove the timeout source for display dimming */ + if (dim_timeout_cb_id != 0) { + g_source_remove(dim_timeout_cb_id); + dim_timeout_cb_id = 0; + } +} + +/** + * Setup dim timeout + */ +static void setup_dim_timeout(void) +{ + gint dim_timeout = disp_dim_timeout + bootup_dim_additional_timeout; + + cancel_blank_timeout(); + cancel_adaptive_dimming_timeout(); + cancel_dim_timeout(); + + if (dimming_inhibited == TRUE) + return; + + if (adaptive_dimming_enabled == TRUE) { + gpointer *tmp = g_slist_nth_data(possible_dim_timeouts, + dim_timeout_index + + adaptive_dimming_index); + + if (tmp != NULL) + dim_timeout = GPOINTER_TO_INT(tmp) + + bootup_dim_additional_timeout; + } + + /* Setup new timeout */ + dim_timeout_cb_id = + g_timeout_add_seconds(dim_timeout, + dim_timeout_cb, NULL); +} + +/** + * Timeout callback for display blanking pause + * + * @param data Unused + * @return Always returns FALSE, to disable the timeout + */ +static gboolean blank_prevent_timeout_cb(gpointer data) +{ + (void)data; + + blank_prevent_timeout_cb_id = 0; + + update_blanking_inhibit(FALSE); + + return FALSE; +} + +/** + * Cancel blank prevention timeout + */ +static void cancel_blank_prevent(void) +{ + if (blank_prevent_timeout_cb_id != 0) { + g_source_remove(blank_prevent_timeout_cb_id); + blank_prevent_timeout_cb_id = 0; + } +} + +/** + * Prevent screen blanking for display_timeout seconds + */ +static void request_display_blanking_pause(void) +{ + /* Also cancels any old timeouts */ + update_blanking_inhibit(TRUE); + + /* Setup new timeout */ + blank_prevent_timeout_cb_id = + g_timeout_add_seconds(blank_prevent_timeout, + blank_prevent_timeout_cb, NULL); +} + +/** + * Enable/Disable blanking inhibit, + * based on charger status and inhibit mode + * + * @param timed_inhibit TRUE for timed inhibiting, + * FALSE for triggered inhibiting + */ +static void update_blanking_inhibit(gboolean timed_inhibit) +{ + display_state_t display_state = datapipe_get_gint(display_state_pipe); + system_state_t system_state = datapipe_get_gint(system_state_pipe); + alarm_ui_state_t alarm_ui_state = + datapipe_get_gint(alarm_ui_state_pipe); + call_state_t call_state = datapipe_get_gint(call_state_pipe); + + if ((system_state == MCE_STATE_ACTDEAD) && + (charger_connected == TRUE) && + ((alarm_ui_state == MCE_ALARM_UI_OFF_INT32) || + (alarm_ui_state == MCE_ALARM_UI_INVALID_INT32))) { + /* If there's no alarm UI visible and we're in acting dead, + * never inhibit blanking + */ + blanking_inhibited = FALSE; + dimming_inhibited = FALSE; + cancel_blank_prevent(); + } else if ((call_state == CALL_STATE_RINGING) || + (blanking_inhibit_mode == INHIBIT_STAY_ON) || + (blanking_inhibit_mode == INHIBIT_STAY_DIM) || + (timed_inhibit == TRUE) || + ((charger_connected == TRUE) && + ((blanking_inhibit_mode == INHIBIT_STAY_ON_WITH_CHARGER) || + (blanking_inhibit_mode == + INHIBIT_STAY_DIM_WITH_CHARGER)))) { + /* Always inhibit blanking */ + blanking_inhibited = TRUE; + + /* If the policy calls for it, also inhibit dimming; + * INHIBIT_STAY_ON{,WITH_CHARGER} doesn't affect the + * policy in acting dead though + */ + if ((((blanking_inhibit_mode == INHIBIT_STAY_ON_WITH_CHARGER) || + (blanking_inhibit_mode == INHIBIT_STAY_ON)) && + (system_state != MCE_STATE_ACTDEAD)) || + (call_state == CALL_STATE_RINGING) || + (timed_inhibit == TRUE)) { + dimming_inhibited = TRUE; + } else { + dimming_inhibited = FALSE; + } + + cancel_blank_prevent(); + } else if (blank_prevent_timeout_cb_id == 0) { + blanking_inhibited = FALSE; + dimming_inhibited = FALSE; + } + + /* Reprogram timeouts, if necessary */ + if (display_state == MCE_DISPLAY_DIM) + setup_blank_timeout(); + else if (display_state != MCE_DISPLAY_OFF) + setup_dim_timeout(); +} + +/** + * D-Bus reply handler for device lock inhibit + * + * @param pending_call The DBusPendingCall + * @param data Unused + */ +static void devlock_inhibit_reply_dbus_cb(DBusPendingCall *pending_call, + void *data) +{ + DBusMessage *reply; + dbus_int32_t retval; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + (void)data; + + mce_log(LL_DEBUG, "Received device lock inhibit reply"); + + if ((reply = dbus_pending_call_steal_reply(pending_call)) == NULL) { + mce_log(LL_ERR, + "Device lock inhibit reply callback invoked, " + "but no pending call available"); + goto EXIT; + } + + /* Make sure we didn't get an error message */ + if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) { + char *error_msg; + + /* If we got an error, it's a string */ + if (dbus_message_get_args(reply, &error, + DBUS_TYPE_STRING, &error_msg, + DBUS_TYPE_INVALID) == FALSE) { + mce_log(LL_CRIT, + "Failed to get error reply argument " + "from %s.%s: %s", + DEVLOCK_SERVICE, DEVLOCK_SET, + error.message); + dbus_error_free(&error); + } else { + mce_log(LL_ERR, + "D-Bus call to %s.%s failed: %s", + DEVLOCK_SERVICE, DEVLOCK_SET, + error_msg); + } + + goto EXIT2; + } + + /* Extract reply */ + if (dbus_message_get_args(reply, &error, + DBUS_TYPE_BOOLEAN, &retval, + DBUS_TYPE_INVALID) == FALSE) { + mce_log(LL_CRIT, + "Failed to get reply argument from %s.%s: %s", + DEVLOCK_SERVICE, DEVLOCK_SET, + error.message); + dbus_error_free(&error); + goto EXIT2; + } + + mce_log(LL_DEBUG, + "Return value: %d", + retval); + +EXIT2: + dbus_message_unref(reply); + +EXIT: + dbus_pending_call_unref(pending_call); + + return; +} + +/** + * Inhibit device lock + */ +static void inhibit_devicelock(void) +{ + dbus_int32_t lock_type = Device; + dbus_int32_t lock_state = Inhibit; + + mce_log(LL_DEBUG, + "Requesting device lock inhibit"); + + dbus_send(DEVLOCK_SERVICE, DEVLOCK_PATH, + DEVLOCK_SERVICE, DEVLOCK_SET, + devlock_inhibit_reply_dbus_cb, + DBUS_TYPE_INT32, &lock_type, + DBUS_TYPE_INT32, &lock_state, + DBUS_TYPE_INVALID); +} + +/** + * Find the dim timeout index from a dim timeout + * + * @param dim_timeout The dim timeout to find the index for + * @return The closest dim timeout index + */ +static guint find_dim_timeout_index(gint dim_timeout) +{ + gpointer tmp; + guint i; + + for (i = 0; + ((tmp = g_slist_nth_data(possible_dim_timeouts, i)) != NULL) && + GPOINTER_TO_INT(tmp) < dim_timeout; i++) + /* Do nothing */; + + return i; +} + +/** + * GConf callback for display related settings + * + * @param gcc Unused + * @param id Connection ID from gconf_client_notify_add() + * @param entry The modified GConf entry + * @param data Unused + */ +static void display_gconf_cb(GConfClient *const gcc, const guint id, + GConfEntry *const entry, gpointer const data) +{ + const GConfValue *gcv = gconf_entry_get_value(entry); + + (void)gcc; + (void)data; + + /* Key is unset */ + if (gcv == NULL) { + mce_log(LL_DEBUG, + "GConf Key `%s' has been unset", + gconf_entry_get_key(entry)); + goto EXIT; + } + + if (id == disp_brightness_gconf_cb_id) { + real_disp_brightness = gconf_value_get_int(gcv); + + if (psm_disp_brightness == -1) { + (void)execute_datapipe(&display_brightness_pipe, GINT_TO_POINTER(real_disp_brightness), USE_INDATA, CACHE_INDATA); + } + } else if (id == disp_blank_timeout_gconf_cb_id) { + disp_blank_timeout = gconf_value_get_int(gcv); + + /* Update blank prevent */ + update_blanking_inhibit(FALSE); + + /* Update inactivity timeout */ + (void)execute_datapipe(&inactivity_timeout_pipe, + GINT_TO_POINTER(disp_dim_timeout + + disp_blank_timeout), + USE_INDATA, CACHE_INDATA); + } else if (id == adaptive_dimming_enabled_gconf_cb_id) { + adaptive_dimming_enabled = gconf_value_get_bool(gcv); + cancel_adaptive_dimming_timeout(); + } else if (id == adaptive_dimming_threshold_gconf_cb_id) { + adaptive_dimming_threshold = gconf_value_get_int(gcv); + cancel_adaptive_dimming_timeout(); + } else if (id == disp_dim_timeout_gconf_cb_id) { + disp_dim_timeout = gconf_value_get_int(gcv); + + /* Find the closest match in the list of valid dim timeouts */ + dim_timeout_index = find_dim_timeout_index(disp_dim_timeout); + adaptive_dimming_index = 0; + + /* Update blank prevent */ + update_blanking_inhibit(FALSE); + + /* Update inactivity timeout */ + (void)execute_datapipe(&inactivity_timeout_pipe, + GINT_TO_POINTER(disp_dim_timeout + + disp_blank_timeout), + USE_INDATA, CACHE_INDATA); + } else if (id == blanking_inhibit_mode_gconf_cb_id) { + blanking_inhibit_mode = gconf_value_get_int(gcv); + + /* Update blank prevent */ + update_blanking_inhibit(FALSE); + } else { + mce_log(LL_WARN, + "Spurious GConf value received; confused!"); + } + +EXIT: + return; +} + +/** + * Send a display status reply or signal + * + * @param method_call A DBusMessage to reply to; + * pass NULL to send a display status signal instead + * @return TRUE on success, FALSE on failure + */ +static gboolean send_display_status(DBusMessage *const method_call) +{ + display_state_t display_state = datapipe_get_gint(display_state_pipe); + DBusMessage *msg = NULL; + const gchar *state = NULL; + gboolean status = FALSE; + + switch (display_state) { + case MCE_DISPLAY_OFF: + state = MCE_DISPLAY_OFF_STRING; + break; + + case MCE_DISPLAY_DIM: + state = MCE_DISPLAY_DIM_STRING; + break; + + case MCE_DISPLAY_ON: + default: + state = MCE_DISPLAY_ON_STRING; + break; + } + + mce_log(LL_DEBUG, + "Sending display status: %s", + state); + + /* If method_call is set, send a reply, + * otherwise, send a signal + */ + if (method_call != NULL) { + msg = dbus_new_method_reply(method_call); + } else { + /* display_status_ind */ + msg = dbus_new_signal(MCE_SIGNAL_PATH, MCE_SIGNAL_IF, + MCE_DISPLAY_SIG); + } + + /* Append the display status */ + if (dbus_message_append_args(msg, + DBUS_TYPE_STRING, &state, + DBUS_TYPE_INVALID) == FALSE) { + mce_log(LL_CRIT, + "Failed to append %sargument to D-Bus message " + "for %s.%s", + method_call ? "reply " : "", + method_call ? MCE_REQUEST_IF : + MCE_SIGNAL_IF, + method_call ? MCE_DISPLAY_STATUS_GET : + MCE_DISPLAY_SIG); + dbus_message_unref(msg); + goto EXIT; + } + + /* Send the message */ + status = dbus_send_message(msg); + +EXIT: + return status; +} + +/** + * D-Bus callback for the get display status method call + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean display_status_get_dbus_cb(DBusMessage *const msg) +{ + gboolean status = FALSE; + + mce_log(LL_DEBUG, + "Received display status get request"); + + /* Try to send a reply that contains the current display status */ + if (send_display_status(msg) == FALSE) + goto EXIT; + + status = TRUE; + +EXIT: + return status; +} + +/** + * Send a CABC status reply + * + * @param method_call A DBusMessage to reply to + * @return TRUE on success, FALSE on failure + */ +static gboolean send_cabc_mode(DBusMessage *const method_call) +{ + const gchar *dbus_cabc_mode = NULL; + DBusMessage *msg = NULL; + gboolean status = FALSE; + gint i; + + for (i = 0; cabc_mode_mapping[i].sysfs != NULL; i++) { + if (!strcmp(cabc_mode_mapping[i].sysfs, cabc_mode)) { + dbus_cabc_mode = cabc_mode_mapping[i].dbus; + break; + } + } + + if (dbus_cabc_mode == NULL) + dbus_cabc_mode = MCE_CABC_MODE_OFF; + + mce_log(LL_DEBUG, + "Sending CABC mode: %s", + dbus_cabc_mode); + + msg = dbus_new_method_reply(method_call); + + /* Append the CABC mode */ + if (dbus_message_append_args(msg, + DBUS_TYPE_STRING, &dbus_cabc_mode, + DBUS_TYPE_INVALID) == FALSE) { + mce_log(LL_CRIT, + "Failed to append reply argument to D-Bus message " + "for %s.%s", + MCE_REQUEST_IF, MCE_CABC_MODE_GET); + dbus_message_unref(msg); + goto EXIT; + } + + /* Send the message */ + status = dbus_send_message(msg); + +EXIT: + return status; +} + +/** + * D-Bus callback for the get CABC mode method call + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean cabc_mode_get_dbus_cb(DBusMessage *const msg) +{ + gboolean status = FALSE; + + mce_log(LL_DEBUG, + "Received CABC mode get request"); + + /* Try to send a reply that contains the current CABC mode */ + if (send_cabc_mode(msg) == FALSE) + goto EXIT; + + status = TRUE; + +EXIT: + return status; +} + +/** + * D-Bus callback for the display on method call + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean display_on_req_dbus_cb(DBusMessage *const msg) +{ + call_state_t call_state = datapipe_get_gint(call_state_pipe); + dbus_bool_t no_reply = dbus_message_get_no_reply(msg); + gboolean status = FALSE; + + mce_log(LL_DEBUG, + "Received display on request"); + + if (call_state != CALL_STATE_RINGING) { + (void)execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_ON), + USE_INDATA, CACHE_INDATA); + } + + if (no_reply == FALSE) { + DBusMessage *reply = dbus_new_method_reply(msg); + + status = dbus_send_message(reply); + } else { + status = TRUE; + } + + return status; +} + +/** + * D-Bus callback for the display dim method call + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean display_dim_req_dbus_cb(DBusMessage *const msg) +{ + dbus_bool_t no_reply = dbus_message_get_no_reply(msg); + gboolean status = FALSE; + + mce_log(LL_DEBUG, + "Received display dim request"); + + (void)execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_DIM), + USE_INDATA, CACHE_INDATA); + + if (no_reply == FALSE) { + DBusMessage *reply = dbus_new_method_reply(msg); + + status = dbus_send_message(reply); + } else { + status = TRUE; + } + + return status; +} + +/** + * D-Bus callback for the display low power mode method call + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean display_low_power_mode_req_dbus_cb(DBusMessage *const msg) +{ + dbus_bool_t no_reply = dbus_message_get_no_reply(msg); + gboolean status = FALSE; + + mce_log(LL_DEBUG, + "Received display low power mode request"); + + if (display_low_power_mode_supported == TRUE) { + (void)execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_LOW_POWER), + USE_INDATA, CACHE_INDATA); + } else { + (void)execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_OFF), + USE_INDATA, CACHE_INDATA); + mce_log(LL_DEBUG, + "Display low power mode not supported; " + "using display off instead"); + } + + if (no_reply == FALSE) { + DBusMessage *reply = dbus_new_method_reply(msg); + + status = dbus_send_message(reply); + } else { + status = TRUE; + } + + return status; +} + +/** + * D-Bus callback for the display off method call + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean display_off_req_dbus_cb(DBusMessage *const msg) +{ + dbus_bool_t no_reply = dbus_message_get_no_reply(msg); + gboolean status = FALSE; + + mce_log(LL_DEBUG, + "Received display off request"); + + (void)execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_OFF), + USE_INDATA, CACHE_INDATA); + + if (no_reply == FALSE) { + DBusMessage *reply = dbus_new_method_reply(msg); + + status = dbus_send_message(reply); + } else { + status = TRUE; + } + + return status; +} + +/** + * Remove a blanking pause with its D-Bus monitor + * + * @param name The name of the D-Bus owner to remove + * @return TRUE on success, FALSE if name is NULL + */ +static gboolean remove_blanking_pause(const gchar *name) +{ + gboolean status = FALSE; + gssize count; + + if (name == NULL) + goto EXIT; + + /* Remove the name monitor for the blanking pause requester; + * if we don't have any requesters left, remove the timeout + */ + count = mce_dbus_owner_monitor_remove(name, + &blanking_pause_monitor_list); + + if (count == 0) { + cancel_blank_prevent(); + update_blanking_inhibit(FALSE); + } + + status = TRUE; + +EXIT: + return status; +} + +/** + * D-Bus callback used for monitoring the process that requested + * blanking prevention; if that process exits, immediately + * cancel the blanking timeout and resume normal operation + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean blanking_pause_owner_monitor_dbus_cb(DBusMessage *const msg) +{ + gboolean status = FALSE; + const gchar *old_name; + const gchar *new_name; + const gchar *service; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + /* Extract result */ + if (dbus_message_get_args(msg, &error, + DBUS_TYPE_STRING, &service, + DBUS_TYPE_STRING, &old_name, + DBUS_TYPE_STRING, &new_name, + DBUS_TYPE_INVALID) == FALSE) { + mce_log(LL_ERR, + "Failed to get argument from %s.%s; %s", + "org.freedesktop.DBus", "NameOwnerChanged", + error.message); + dbus_error_free(&error); + goto EXIT; + } + + remove_blanking_pause(old_name); + status = TRUE; + +EXIT: + return status; +} + +/** + * D-Bus callback for display cancel blanking prevent request method call + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean display_cancel_blanking_pause_req_dbus_cb(DBusMessage *const msg) +{ + dbus_bool_t no_reply = dbus_message_get_no_reply(msg); + const gchar *sender = dbus_message_get_sender(msg); + gboolean status = FALSE; + + mce_log(LL_DEBUG, + "Received cancel blanking pause request from %s", + (sender == NULL) ? "(unknown)" : sender); + + remove_blanking_pause(sender); + + if (no_reply == FALSE) { + DBusMessage *reply = dbus_new_method_reply(msg); + + status = dbus_send_message(reply); + } else { + status = TRUE; + } + + return status; +} + +/** + * D-Bus callback for display blanking prevent request method call + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean display_blanking_pause_req_dbus_cb(DBusMessage *const msg) +{ + dbus_bool_t no_reply = dbus_message_get_no_reply(msg); + const gchar *sender = dbus_message_get_sender(msg); + gboolean status = FALSE; + + mce_log(LL_DEBUG, + "Received blanking pause request from %s", + (sender == NULL) ? "(unknown)" : sender); + + request_display_blanking_pause(); + inhibit_devicelock(); + + if (mce_dbus_owner_monitor_add(sender, + blanking_pause_owner_monitor_dbus_cb, + &blanking_pause_monitor_list, + MAX_MONITORED_SERVICES) == -1) { + mce_log(LL_INFO, + "Failed to add name owner monitoring for `%s'", + sender); + } + + if (no_reply == FALSE) { + DBusMessage *reply = dbus_new_method_reply(msg); + + status = dbus_send_message(reply); + } else { + status = TRUE; + } + + return status; +} + +/** + * D-Bus callback used for monitoring the process that requested + * CABC mode change; if that process exits, immediately + * restore the CABC mode to the default + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean cabc_mode_owner_monitor_dbus_cb(DBusMessage *const msg) +{ + gboolean status = FALSE; + const gchar *old_name; + const gchar *new_name; + const gchar *service; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + /* Extract result */ + if (dbus_message_get_args(msg, &error, + DBUS_TYPE_STRING, &service, + DBUS_TYPE_STRING, &old_name, + DBUS_TYPE_STRING, &new_name, + DBUS_TYPE_INVALID) == FALSE) { + mce_log(LL_ERR, + "Failed to get argument from %s.%s; %s", + "org.freedesktop.DBus", "NameOwnerChanged", + error.message); + dbus_error_free(&error); + goto EXIT; + } + + /* Remove the name monitor for the CABC mode */ + mce_dbus_owner_monitor_remove_all(&cabc_mode_monitor_list); + set_cabc_mode(DEFAULT_CABC_MODE); + + status = TRUE; + +EXIT: + return status; +} + +/** + * D-Bus callback for the set CABC mode method call + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean cabc_mode_req_dbus_cb(DBusMessage *const msg) +{ + dbus_bool_t no_reply = dbus_message_get_no_reply(msg); + const gchar *sender = dbus_message_get_sender(msg); + const gchar *sysfs_cabc_mode = NULL; + const gchar *dbus_cabc_mode = NULL; + gboolean status = FALSE; + gint i; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + mce_log(LL_DEBUG, + "Received set CABC mode request from %s", + (sender == NULL) ? "(unknown)" : sender); + + /* Extract result */ + if (dbus_message_get_args(msg, &error, + DBUS_TYPE_STRING, &dbus_cabc_mode, + DBUS_TYPE_INVALID) == FALSE) { + mce_log(LL_ERR, + "Failed to get argument from %s.%s; %s", + MCE_REQUEST_IF, MCE_CABC_MODE_REQ, + error.message); + dbus_error_free(&error); + goto EXIT; + } + + for (i = 0; cabc_mode_mapping[i].dbus != NULL; i++) { + if (!strcmp(cabc_mode_mapping[i].dbus, dbus_cabc_mode)) { + sysfs_cabc_mode = cabc_mode_mapping[i].sysfs; + } + } + + /* Use the default if the requested mode was invalid */ + if (sysfs_cabc_mode == NULL) { + mce_log(LL_WARN, + "Invalid CABC mode requested; using %s", + DEFAULT_CABC_MODE); + sysfs_cabc_mode = DEFAULT_CABC_MODE; + } + + set_cabc_mode(sysfs_cabc_mode); + + /* We only ever monitor one owner; latest wins */ + mce_dbus_owner_monitor_remove_all(&cabc_mode_monitor_list); + + if (mce_dbus_owner_monitor_add(sender, + cabc_mode_owner_monitor_dbus_cb, + &cabc_mode_monitor_list, + 1) == -1) { + mce_log(LL_INFO, + "Failed to add name owner monitoring for `%s'", + sender); + } + + /* If reply is wanted, send the current CABC mode */ + if (no_reply == FALSE) { + DBusMessage *reply = dbus_new_method_reply(msg); + + for (i = 0; cabc_mode_mapping[i].sysfs != NULL; i++) { + if (!strcmp(sysfs_cabc_mode, cabc_mode_mapping[i].sysfs)) { + dbus_message_append_args(reply, DBUS_TYPE_STRING, &cabc_mode_mapping[i].dbus, DBUS_TYPE_INVALID); + break; + } + } + status = dbus_send_message(reply); + } else { + status = TRUE; + } + +EXIT: + return status; +} + +/** + * D-Bus callback for the desktop startup notification signal + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean desktop_startup_dbus_cb(DBusMessage *const msg) +{ + gboolean status = FALSE; + + (void)msg; + + mce_log(LL_DEBUG, + "Received desktop startup notification"); + + execute_datapipe_output_triggers(&led_pattern_deactivate_pipe, + MCE_LED_PATTERN_POWER_ON, USE_INDATA); + + mce_rem_submode_int32(MCE_BOOTUP_SUBMODE); + + /* Restore normal inactivity timeout */ + (void)execute_datapipe(&inactivity_timeout_pipe, + GINT_TO_POINTER(disp_dim_timeout + + disp_blank_timeout), + USE_INDATA, CACHE_INDATA); + + /* Remove the additional timeout */ + bootup_dim_additional_timeout = 0; + + /* Update blank prevent */ + update_blanking_inhibit(FALSE); + + status = TRUE; + + return status; +} + +/** + * Handle display state change + * + * @param data The display state stored in a pointer + */ +static void display_state_trigger(gconstpointer data) +{ + /** Cached display state */ + static display_state_t cached_display_state = MCE_DISPLAY_UNDEF; + display_state_t display_state = GPOINTER_TO_INT(data); + submode_t submode = mce_get_submode_int32(); + + switch (display_state) { + case MCE_DISPLAY_OFF: + cancel_dim_timeout(); + cancel_adaptive_dimming_timeout(); + adaptive_dimming_index = 0; + cancel_blank_timeout(); + break; + + case MCE_DISPLAY_DIM: + cancel_dim_timeout(); + setup_adaptive_dimming_timeout(); + setup_blank_timeout(); + break; + + case MCE_DISPLAY_ON: + default: + /* The tklock has its own timeout */ + if ((submode & MCE_TKLOCK_SUBMODE) == 0) { + setup_dim_timeout(); + } + + break; + } + + /* If we already have the right state, + * we're done here + */ + if (cached_display_state == display_state) + goto EXIT; + + switch (display_state) { + case MCE_DISPLAY_OFF: + display_blank(); + break; + + case MCE_DISPLAY_DIM: + display_dim(); + break; + + case MCE_DISPLAY_ON: + default: + display_unblank(); + break; + } + + /* This will send the correct state + * since the pipe contains the new value + */ + send_display_status(NULL); + + /* Update the cached value */ + cached_display_state = display_state; + +EXIT: + return; +} + +/** + * Handle submode change + * + * @param data The submode stored in a pointer + */ +static void submode_trigger(gconstpointer data) +{ + static submode_t old_submode = MCE_NORMAL_SUBMODE; + submode_t submode = GPOINTER_TO_INT(data); + + /* Avoid unnecessary updates: + * Note: this *must* be binary or/and, + * not logical, else it won't work, + * for (hopefully) obvious reasons + */ + if ((old_submode | submode) & MCE_TRANSITION_SUBMODE) + update_blanking_inhibit(FALSE); + + submode = old_submode; +} + +/** + * Datapipe trigger for the charger state + * + * @param data TRUE if the charger was connected, + * FALSE if the charger was disconnected + */ +static void charger_state_trigger(gconstpointer data) +{ + charger_connected = GPOINTER_TO_INT(data); + + update_blanking_inhibit(FALSE); +} + +/** + * Datapipe trigger for device inactivity + * + * @param data The inactivity stored in a pointer; + * TRUE if the device is inactive, + * FALSE if the device is active + */ +static void device_inactive_trigger(gconstpointer data) +{ + system_state_t system_state = datapipe_get_gint(system_state_pipe); + alarm_ui_state_t alarm_ui_state = + datapipe_get_gint(alarm_ui_state_pipe); + gboolean device_inactive = GPOINTER_TO_INT(data); + submode_t submode = mce_get_submode_int32(); + + /* Unblank screen on device activity, + * unless the device is in acting dead and no alarm is visible + * or if the tklock is active + */ + if (((system_state == MCE_STATE_USER) || + ((system_state == MCE_STATE_ACTDEAD) && + ((alarm_ui_state == MCE_ALARM_UI_VISIBLE_INT32) || + (alarm_ui_state == MCE_ALARM_UI_RINGING_INT32)))) && + (device_inactive == FALSE) && + ((submode & MCE_TKLOCK_SUBMODE) == 0)) { + /* Adjust the adaptive dimming timeouts, + * even if we don't use them + */ + if (adaptive_dimming_timeout_cb_id != 0) { + if (g_slist_nth(possible_dim_timeouts, + dim_timeout_index + + adaptive_dimming_index + 1) != NULL) + adaptive_dimming_index++; + } + + (void)execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_ON), + USE_INDATA, CACHE_INDATA); + } +} + +/** + * Datapipe trigger for call state + * + * @param data Unused + */ +static void call_state_trigger(gconstpointer data) +{ + (void)data; + + update_blanking_inhibit(FALSE); +} + +/** + * Datapipe trigger for the power saving mode + * + * @param data Unused + */ +static void power_saving_mode_trigger(gconstpointer data) +{ + gboolean power_saving_mode = GPOINTER_TO_INT(data); + + if (power_saving_mode == TRUE) { + /* Override the CABC mode and brightness setting */ + psm_cabc_mode = DEFAULT_PSM_CABC_MODE; + psm_disp_brightness = DEFAULT_PSM_DISP_BRIGHTNESS; + + (void)execute_datapipe(&display_brightness_pipe, GINT_TO_POINTER(psm_disp_brightness), USE_INDATA, CACHE_INDATA); + set_cabc_mode(psm_cabc_mode); + } else { + /* Restore the CABC mode and brightness setting */ + psm_cabc_mode = NULL; + psm_disp_brightness = -1; + + (void)execute_datapipe(&display_brightness_pipe, GINT_TO_POINTER(real_disp_brightness), USE_INDATA, CACHE_INDATA); + set_cabc_mode(cabc_mode); + } +} + +/** + * Handle system state change + * + * @param data The system state stored in a pointer + */ +static void system_state_trigger(gconstpointer data) +{ + alarm_ui_state_t alarm_ui_state = + datapipe_get_gint(alarm_ui_state_pipe); + system_state_t system_state = GPOINTER_TO_INT(data); + + switch (system_state) { + case MCE_STATE_USER: + (void)execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_ON), + USE_INDATA, CACHE_INDATA); + break; + + case MCE_STATE_ACTDEAD: + if ((alarm_ui_state == MCE_ALARM_UI_RINGING_INT32) || + (alarm_ui_state == MCE_ALARM_UI_VISIBLE_INT32)) { + (void)execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_ON), + USE_INDATA, CACHE_INDATA); + } + + break; + + case MCE_STATE_SHUTDOWN: + case MCE_STATE_REBOOT: + case MCE_STATE_UNDEF: + default: + break; + } + + return; +} + +/** + * Init function for the display handling module + * + * @todo XXX status needs to be set on error! + * + * @param module Unused + * @return NULL on success, a string with an error message on failure + */ +G_MODULE_EXPORT const gchar *g_module_check_init(GModule *module); +const gchar *g_module_check_init(GModule *module) +{ + submode_t submode = mce_get_submode_int32(); + gchar *str = NULL; + gulong tmp; + + (void)module; + + /* Initialise the display type and the relevant paths */ + (void)get_display_type(); + + if ((submode & MCE_TRANSITION_SUBMODE) != 0) { + mce_add_submode_int32(MCE_BOOTUP_SUBMODE); + bootup_dim_additional_timeout = BOOTUP_DIM_ADDITIONAL_TIMEOUT; + } else { + bootup_dim_additional_timeout = 0; + } + + /* Append triggers/filters to datapipes */ + append_output_trigger_to_datapipe(&system_state_pipe, + system_state_trigger); + append_output_trigger_to_datapipe(&charger_state_pipe, + charger_state_trigger); + append_output_trigger_to_datapipe(&display_brightness_pipe, + display_brightness_trigger); + append_output_trigger_to_datapipe(&display_state_pipe, + display_state_trigger); + append_output_trigger_to_datapipe(&submode_pipe, + submode_trigger); + append_output_trigger_to_datapipe(&device_inactive_pipe, + device_inactive_trigger); + append_output_trigger_to_datapipe(&call_state_pipe, + call_state_trigger); + append_output_trigger_to_datapipe(&power_saving_mode_pipe, + power_saving_mode_trigger); + + /* Get maximum brightness */ + if (mce_read_number_string_from_file(max_brightness_file, + &tmp, NULL, FALSE, + TRUE) == FALSE) { + mce_log(LL_ERR, + "Could not read the maximum brightness from %s; " + "defaulting to %d", + max_brightness_file, + DEFAULT_MAXIMUM_DISPLAY_BRIGHTNESS); + tmp = DEFAULT_MAXIMUM_DISPLAY_BRIGHTNESS; + } + + maximum_display_brightness = tmp; + dim_brightness = (maximum_display_brightness * + DEFAULT_DIM_BRIGHTNESS) / 100; + + set_cabc_mode(DEFAULT_CABC_MODE); + + /* Display brightness */ + /* Since we've set a default, error handling is unnecessary */ + (void)mce_gconf_get_int(MCE_GCONF_DISPLAY_BRIGHTNESS_PATH, + &real_disp_brightness); + + /* Use the current brightness as cached brightness on startup, + * and fade from that value + */ + if (mce_read_number_string_from_file(brightness_file, + &tmp, NULL, FALSE, + TRUE) == FALSE) { + mce_log(LL_ERR, + "Could not read the current brightness from %s", + brightness_file); + cached_brightness = -1; + } else { + cached_brightness = tmp; + } + + (void)execute_datapipe(&display_brightness_pipe, + GINT_TO_POINTER(real_disp_brightness), + USE_INDATA, CACHE_INDATA); + + if (mce_gconf_notifier_add(MCE_GCONF_DISPLAY_PATH, + MCE_GCONF_DISPLAY_BRIGHTNESS_PATH, + display_gconf_cb, + &disp_brightness_gconf_cb_id) == FALSE) + goto EXIT; + + /* Display blank */ + /* Since we've set a default, error handling is unnecessary */ + (void)mce_gconf_get_int(MCE_GCONF_DISPLAY_BLANK_TIMEOUT_PATH, + &disp_blank_timeout); + + if (mce_gconf_notifier_add(MCE_GCONF_DISPLAY_PATH, + MCE_GCONF_DISPLAY_BLANK_TIMEOUT_PATH, + display_gconf_cb, + &disp_blank_timeout_gconf_cb_id) == FALSE) + goto EXIT; + + /* Use adaptive display dim timeout */ + /* Since we've set a default, error handling is unnecessary */ + (void)mce_gconf_get_bool(MCE_GCONF_DISPLAY_ADAPTIVE_DIMMING_PATH, + &adaptive_dimming_enabled); + + if (mce_gconf_notifier_add(MCE_GCONF_DISPLAY_PATH, + MCE_GCONF_DISPLAY_ADAPTIVE_DIMMING_PATH, + display_gconf_cb, + &adaptive_dimming_enabled_gconf_cb_id) == FALSE) + goto EXIT; + + /* Possible dim timeouts */ + if (mce_gconf_get_int_list(MCE_GCONF_DISPLAY_DIM_TIMEOUT_LIST_PATH, + &possible_dim_timeouts) == FALSE) + goto EXIT; + + /* Adaptive display dimming threshold */ + /* Since we've set a default, error handling is unnecessary */ + (void)mce_gconf_get_int(MCE_GCONF_DISPLAY_ADAPTIVE_DIM_THRESHOLD_PATH, + &adaptive_dimming_threshold); + + if (mce_gconf_notifier_add(MCE_GCONF_DISPLAY_PATH, + MCE_GCONF_DISPLAY_ADAPTIVE_DIM_THRESHOLD_PATH, + display_gconf_cb, + &adaptive_dimming_threshold_gconf_cb_id) == FALSE) + goto EXIT; + + /* Display dim */ + /* Since we've set a default, error handling is unnecessary */ + (void)mce_gconf_get_int(MCE_GCONF_DISPLAY_DIM_TIMEOUT_PATH, + &disp_dim_timeout); + + dim_timeout_index = find_dim_timeout_index(disp_dim_timeout); + adaptive_dimming_index = 0; + + if (mce_gconf_notifier_add(MCE_GCONF_DISPLAY_PATH, + MCE_GCONF_DISPLAY_DIM_TIMEOUT_PATH, + display_gconf_cb, + &disp_dim_timeout_gconf_cb_id) == FALSE) + goto EXIT; + + /* Update inactivity timeout */ + (void)execute_datapipe(&inactivity_timeout_pipe, + GINT_TO_POINTER(disp_dim_timeout + + disp_blank_timeout + + bootup_dim_additional_timeout), + USE_INDATA, CACHE_INDATA); + + /* Don't blank on charger */ + /* Since we've set a default, error handling is unnecessary */ + (void)mce_gconf_get_int(MCE_GCONF_BLANKING_INHIBIT_MODE_PATH, + &blanking_inhibit_mode); + + if (mce_gconf_notifier_add(MCE_GCONF_DISPLAY_PATH, + MCE_GCONF_BLANKING_INHIBIT_MODE_PATH, + display_gconf_cb, + &blanking_inhibit_mode_gconf_cb_id) == FALSE) + goto EXIT; + + /* get_display_status */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_DISPLAY_STATUS_GET, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + display_status_get_dbus_cb) == NULL) + goto EXIT; + + /* get_cabc_mode */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_CABC_MODE_GET, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + cabc_mode_get_dbus_cb) == NULL) + goto EXIT; + + /* req_display_state_on */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_DISPLAY_ON_REQ, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + display_on_req_dbus_cb) == NULL) + goto EXIT; + + /* req_display_state_dim */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_DISPLAY_DIM_REQ, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + display_dim_req_dbus_cb) == NULL) + goto EXIT; + + /* req_display_state_low_power */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_DISPLAY_LOW_POWER_MODE_REQ, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + display_low_power_mode_req_dbus_cb) == NULL) + goto EXIT; + + /* req_display_state_off */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_DISPLAY_OFF_REQ, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + display_off_req_dbus_cb) == NULL) + goto EXIT; + + /* req_display_blanking_pause */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_PREVENT_BLANK_REQ, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + display_blanking_pause_req_dbus_cb) == NULL) + goto EXIT; + + /* req_display_cancel_blanking_pause */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_CANCEL_PREVENT_BLANK_REQ, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + display_cancel_blanking_pause_req_dbus_cb) == NULL) + goto EXIT; + + /* req_cabc_mode */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_CABC_MODE_REQ, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + cabc_mode_req_dbus_cb) == NULL) + goto EXIT; + + /* Desktop readiness signal */ + if (mce_dbus_handler_add("com.nokia.startup.signal", + "desktop_visible", + NULL, + DBUS_MESSAGE_TYPE_SIGNAL, + desktop_startup_dbus_cb) == NULL) + goto EXIT; + + /* Get configuration options */ + str = mce_conf_get_string(MCE_CONF_DISPLAY_GROUP, + MCE_CONF_BRIGHTNESS_INCREASE_POLICY, + "", + NULL); + + brightness_increase_policy = mce_translate_string_to_int_with_default(brightness_change_policy_translation, str, DEFAULT_BRIGHTNESS_INCREASE_POLICY); + g_free(str); + + str = mce_conf_get_string(MCE_CONF_DISPLAY_GROUP, + MCE_CONF_BRIGHTNESS_DECREASE_POLICY, + "", + NULL); + + brightness_decrease_policy = mce_translate_string_to_int_with_default(brightness_change_policy_translation, str, DEFAULT_BRIGHTNESS_DECREASE_POLICY); + g_free(str); + + brightness_increase_step_time = + mce_conf_get_int(MCE_CONF_DISPLAY_GROUP, + MCE_CONF_STEP_TIME_INCREASE, + DEFAULT_BRIGHTNESS_INCREASE_STEP_TIME, + NULL); + + brightness_decrease_step_time = + mce_conf_get_int(MCE_CONF_DISPLAY_GROUP, + MCE_CONF_STEP_TIME_DECREASE, + DEFAULT_BRIGHTNESS_DECREASE_STEP_TIME, + NULL); + + brightness_increase_constant_time = + mce_conf_get_int(MCE_CONF_DISPLAY_GROUP, + MCE_CONF_CONSTANT_TIME_INCREASE, + DEFAULT_BRIGHTNESS_INCREASE_CONSTANT_TIME, + NULL); + + brightness_decrease_constant_time = + mce_conf_get_int(MCE_CONF_DISPLAY_GROUP, + MCE_CONF_CONSTANT_TIME_DECREASE, + DEFAULT_BRIGHTNESS_DECREASE_CONSTANT_TIME, + NULL); + + /* Request display on to get the state machine in sync */ + (void)execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_ON), + USE_INDATA, CACHE_INDATA); + +EXIT: + return NULL; +} + +/** + * Exit function for the display handling module + * + * @todo D-Bus unregistration + * + * @param module Unused + */ +G_MODULE_EXPORT void g_module_unload(GModule *module); +void g_module_unload(GModule *module) +{ + (void)module; + + /* Remove triggers/filters from datapipes */ + remove_output_trigger_from_datapipe(&power_saving_mode_pipe, + power_saving_mode_trigger); + remove_output_trigger_from_datapipe(&call_state_pipe, + call_state_trigger); + remove_output_trigger_from_datapipe(&device_inactive_pipe, + device_inactive_trigger); + remove_output_trigger_from_datapipe(&submode_pipe, + submode_trigger); + remove_output_trigger_from_datapipe(&display_state_pipe, + display_state_trigger); + remove_output_trigger_from_datapipe(&display_brightness_pipe, + display_brightness_trigger); + remove_output_trigger_from_datapipe(&charger_state_pipe, + charger_state_trigger); + remove_output_trigger_from_datapipe(&system_state_pipe, + system_state_trigger); + + /* Free lists */ + g_slist_free(possible_dim_timeouts); + + /* Free strings */ + g_free(brightness_file); + g_free(max_brightness_file); + g_free(cabc_mode_file); + g_free(cabc_available_modes_file); + + /* Remove all timer sources */ + cancel_blank_prevent(); + cancel_brightness_fade_timeout(); + cancel_dim_timeout(); + cancel_adaptive_dimming_timeout(); + cancel_blank_timeout(); + + return; +} diff --git a/modules/display.h b/modules/display.h new file mode 100644 index 00000000..16f6e2cb --- /dev/null +++ b/modules/display.h @@ -0,0 +1,160 @@ +/** + * @file display.h + * Headers for the display module + *

+ * Copyright © 2007-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _DISPLAY_H_ +#define _DISPLAY_H_ + +/** Name of Display configuration group */ +#define MCE_CONF_DISPLAY_GROUP "Display" + +/** Name of the configuration key for the brightness increase policy */ +#define MCE_CONF_BRIGHTNESS_INCREASE_POLICY "BrightnessIncreasePolicy" + +/** Name of the configuration key for the step-time for brightness increase */ +#define MCE_CONF_STEP_TIME_INCREASE "StepTimeIncrease" + +/** Name of the configuration key for the constant time brightness increase */ +#define MCE_CONF_CONSTANT_TIME_INCREASE "ConstantTimeIncrease" + +/** Name of the configuration key for the brightness decrease policy */ +#define MCE_CONF_BRIGHTNESS_DECREASE_POLICY "BrightnessDecreasePolicy" + +/** Name of the configuration key for the step-time for brightness decrease */ +#define MCE_CONF_STEP_TIME_DECREASE "StepTimeDecrease" + +/** Name of the configuration key for the constant time brightness decrease */ +#define MCE_CONF_CONSTANT_TIME_DECREASE "ConstantTimeDecrease" + +/** Default brightness increase step-time */ +#define DEFAULT_BRIGHTNESS_INCREASE_STEP_TIME 5 + +/** Default brightness increase constant time */ +#define DEFAULT_BRIGHTNESS_INCREASE_CONSTANT_TIME 2000 + +/** Default brightness decrease step-time */ +#define DEFAULT_BRIGHTNESS_DECREASE_STEP_TIME 10 + +/** Default brightness decrease constant time */ +#define DEFAULT_BRIGHTNESS_DECREASE_CONSTANT_TIME 3000 + +/** Path to the SysFS entry for the CABC controls */ +#define DISPLAY_CABC_PATH "/sys/class/backlight" +/** CABC brightness file */ +#define DISPLAY_CABC_BRIGHTNESS_FILE "/brightness" +/** CABC maximum brightness file */ +#define DISPLAY_CABC_MAX_BRIGHTNESS_FILE "/max_brightness" +/** CABC mode file */ +#define DISPLAY_CABC_MODE_FILE "/cabc_mode" +/** CABC available modes file */ +#define DISPLAY_CABC_AVAILABLE_MODES_FILE "/cabc_available_modes" + +/** Generic name for the display in newer hardware */ +#define DISPLAY_DISPLAY0 "/display0" +/** The name of the directory for the Sony acx565akm display */ +#define DISPLAY_ACX565AKM "/acx565akm" +/** The name of the directory for the EID l4f00311 display */ +#define DISPLAY_L4F00311 "/l4f00311" +/** The name of the directory for the Taal display */ +#define DISPLAY_TAAL "/taal" +/** The name of the directory for the Himalaya display */ +#define DISPLAY_HIMALAYA "/himalaya" +/** The name of the directory for ACPI controlled displays */ +#define DISPLAY_ACPI_VIDEO0 "/acpi_video0" + +/** Path to hardware dimming support */ +#define DISPLAY_HARDWARE_DIMMING "/sys/devices/omapdss/display0/dimming" + +/** CABC name for CABC disabled */ +#define CABC_MODE_OFF "off" +/** CABC name for UI mode */ +#define CABC_MODE_UI "ui" +/** CABC name for still image mode */ +#define CABC_MODE_STILL_IMAGE "still-image" +/** CABC name for moving image mode */ +#define CABC_MODE_MOVING_IMAGE "moving-image" + +/** Default CABC mode */ +#define DEFAULT_CABC_MODE CABC_MODE_UI +/** Default CABC mode (power save mode active) */ +#define DEFAULT_PSM_CABC_MODE CABC_MODE_MOVING_IMAGE + +/** Path to the SysFS entry for the generic display interface */ +#define DISPLAY_GENERIC_PATH "/sys/class/graphics/fb0/device/panel" +/** Generic brightness file */ +#define DISPLAY_GENERIC_BRIGHTNESS_FILE "/backlight_level" +/** Generic maximum brightness file */ +#define DISPLAY_GENERIC_MAX_BRIGHTNESS_FILE "/backlight_max" + +/** Path to the framebuffer device */ +#define FB_DEVICE "/dev/fb0" + +/** Path to the GConf settings for the display */ +#ifndef MCE_GCONF_DISPLAY_PATH +#define MCE_GCONF_DISPLAY_PATH "/system/osso/dsm/display" +#endif /* MCE_GCONF_DISPLAY_PATH */ +/** Path to the display brightness GConf setting */ +#define MCE_GCONF_DISPLAY_BRIGHTNESS_PATH MCE_GCONF_DISPLAY_PATH "/display_brightness" +/** Path to the list of possible dim timeouts GConf setting */ +#define MCE_GCONF_DISPLAY_DIM_TIMEOUT_LIST_PATH MCE_GCONF_DISPLAY_PATH "/possible_display_dim_timeouts" +/** Path to the dim timeout GConf setting */ +#define MCE_GCONF_DISPLAY_DIM_TIMEOUT_PATH MCE_GCONF_DISPLAY_PATH "/display_dim_timeout" +/** Path to the blank timeout GConf setting */ +#define MCE_GCONF_DISPLAY_BLANK_TIMEOUT_PATH MCE_GCONF_DISPLAY_PATH "/display_blank_timeout" +/** Path to the adaptive display dimming GConf setting */ +#define MCE_GCONF_DISPLAY_ADAPTIVE_DIMMING_PATH MCE_GCONF_DISPLAY_PATH "/use_adaptive_display_dimming" +/** Path to the adaptive display threshold timeout GConf setting */ +#define MCE_GCONF_DISPLAY_ADAPTIVE_DIM_THRESHOLD_PATH MCE_GCONF_DISPLAY_PATH "/adaptive_display_dim_threshold" +/** Path to the blanking inhibit GConf setting */ +#define MCE_GCONF_BLANKING_INHIBIT_MODE_PATH MCE_GCONF_DISPLAY_PATH "/inhibit_blank_mode" + +/** Default display brightness on a scale from 1-5 */ +#define DEFAULT_DISP_BRIGHTNESS 3 /* 60% */ +/** Default display brightness (power save mode active) on a scale from 1-5 */ +#define DEFAULT_PSM_DISP_BRIGHTNESS 1 /* 20% */ +/** Default blank timeout, in seconds */ +#define DEFAULT_BLANK_TIMEOUT 3 /* 3 seconds */ +/** Default adaptive dimming threshold, in milliseconds */ +#define DEFAULT_ADAPTIVE_DIMMING_ENABLED TRUE /* TRUE */ +/** Default adaptive dimming threshold, in milliseconds */ +#define DEFAULT_ADAPTIVE_DIMMING_THRESHOLD 3000 /* 3 seconds */ +/** Default dim timeout, in seconds */ +#define DEFAULT_DIM_TIMEOUT 30 /* 30 seconds */ +/** Additional dim timeout during bootup, in seconds */ +#define BOOTUP_DIM_ADDITIONAL_TIMEOUT 120 /* 120 seconds */ + +/** + * Blank prevent timeout, in seconds; + * Don't alter this, since this is part of the defined behaviour + * for blanking inhibit that applications rely on + */ +#define BLANK_PREVENT_TIMEOUT 60 /* 60 seconds */ + +/** + * Default maximum brightness; + * used if the maximum brightness cannot be read from SysFS + */ +#define DEFAULT_MAXIMUM_DISPLAY_BRIGHTNESS 127 +/** Default dim brightness, in percent */ +#define DEFAULT_DIM_BRIGHTNESS 3 + +/** Maximum number of monitored services that calls blanking pause */ +#define MAX_MONITORED_SERVICES 5 + +#endif /* _DISPLAY_H_ */ diff --git a/modules/filter-brightness-als.c b/modules/filter-brightness-als.c new file mode 100644 index 00000000..178c114e --- /dev/null +++ b/modules/filter-brightness-als.c @@ -0,0 +1,1107 @@ +/** + * @file filter-brightness-als.c + * Ambient Light Sensor level adjusting filter module + * for display backlight, key backlight, and LED brightness + * This file implements a filter module for MCE + *

+ * Copyright © 2007-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * @author Tuomo Tanskanen + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include +#include +#include /* g_access */ + +#include /* O_NONBLOCK */ +#include /* R_OK */ +#include /* free() */ + +#include /* cal_init(), cal_read_block(), + * cal_finish(), + * struct cal + */ + +#include "mce.h" +#include "filter-brightness-als.h" + +#include "mce-io.h" /* mce_read_chunk_from_file(), + * mce_read_number_string_from_file() + */ +#include "mce-lib.h" /* mce_translate_string_to_int_with_default(), + * mce_translation_t + */ +#include "mce-log.h" /* mce_log(), LL_* */ +#include "mce-conf.h" /* mce_conf_get_int(), + * mce_conf_get_string() + */ +#include "mce-gconf.h" /* mce_gconf_get_bool(), + * mce_gconf_notifier_add(), + * gconf_entry_get_key(), + * gconf_value_get_bool(), + * GConfClient, GConfEntry, GConfValue + */ +#include "datapipe.h" /* execute_datapipe(), + * append_output_trigger_to_datapipe(), + * append_filter_to_datapipe(), + * remove_filter_from_datapipe(), + * remove_output_trigger_from_datapipe() + */ +#include "median_filter.h" /* median_filter_init(), + * median_filter_map() + */ + +/** Module name */ +#define MODULE_NAME "filter-brightness-als" + +/** Functionality provided by this module */ +static const gchar *const provides[] = { + "display-brightness-filter", + "led-brightness-filter", + "key-backlight-brightness-filter", + NULL +}; + +/** Functionality that this module enhances */ +static const gchar *const enhances[] = { + "display-brightness", + "led-brightness", + "key-backlight-brightness", + NULL +}; + +/** Module information */ +G_MODULE_EXPORT module_info_struct module_info = { + /** Name of the module */ + .name = MODULE_NAME, + /** Module enhances */ + .enhances = enhances, + /** Module provides */ + .provides = provides, + /** Module priority */ + .priority = 100 +}; + +/** GConf callback ID for ALS enabled */ +static guint als_enabled_gconf_cb_id = 0; + +/** ID for the ALS I/O monitor */ +static gconstpointer als_iomon_id = NULL; + +/** Path to the ALS device file entry */ +static const gchar *als_device_path = NULL; +/** Path to the ALS lux sysfs entry */ +static const gchar *als_lux_path = NULL; +/** Path to the first ALS calibration point sysfs entry */ +static const gchar *als_calib0_path = NULL; +/** Path to the second ALS calibration point sysfs entry */ +static const gchar *als_calib1_path = NULL; +/** Path to the ALS threshold range sysfs entry */ +static const gchar *als_threshold_range_path = NULL; +/** Is there an ALS available? */ +static gboolean als_available = TRUE; +/** Filter things through ALS? */ +static gboolean als_enabled = TRUE; +/** Pass input through a median filter? */ +static gboolean use_median_filter = FALSE; +/** Lux reading from the ALS */ +static gint als_lux = -1; +/** ALS profiles for the display */ +static als_profile_struct *display_als_profiles = NULL; +/** ALS profiles for the LED */ +static als_profile_struct *led_als_profiles = NULL; +/** ALS profiles for the keyboard backlight */ +static als_profile_struct *kbd_als_profiles = NULL; +/** ALS lower threshold for display brightness */ +static gint display_brightness_lower = -1; +/** ALS upper threshold for display brightness */ +static gint display_brightness_upper = -1; +/** ALS lower threshold for led brightness */ +static gint led_brightness_lower = -1; +/** ALS upper threshold for led brightness */ +static gint led_brightness_upper = -1; +/** ALS lower threshold for keyboard backlight */ +static gint kbd_brightness_lower = -1; +/** ALS upper threshold for keyboard backlight */ +static gint kbd_brightness_upper = -1; + +/** Display state */ +static display_state_t display_state = MCE_DISPLAY_UNDEF; + +/** Median filter */ +static median_filter_struct median_filter; + +/** ALS poll interval */ +static gint als_poll_interval = ALS_DISPLAY_ON_POLL_FREQ; + +/** ID for ALS poll timer source */ +static guint als_poll_timer_cb_id = 0; + +/** FILE * for the ambient_light_sensor */ +static FILE *als_fp = NULL; + +/** Ambient Light Sensor type */ +typedef enum { + /** ALS type unset */ + ALS_TYPE_UNSET = -1, + /** No ALS available */ + ALS_TYPE_NONE = 0, + /** TSL2562 type ALS */ + ALS_TYPE_TSL2562 = 1, + /** TSL2563 type ALS */ + ALS_TYPE_TSL2563 = 2, + /** BH1780GLI type ALS */ + ALS_TYPE_BH1780GLI = 3, + /** Dipro (BH1770GLC/SFH7770) type ALS */ + ALS_TYPE_DIPRO = 4, + /** Avago (APDS990x (QPDS-T900)) type ALS */ + ALS_TYPE_AVAGO = 5, +} als_type_t; + +static void cancel_als_poll_timer(void); + +/** Brightness level step policies */ +typedef enum { + /** Policy not set */ + BRIGHTNESS_STEP_POLICY_INVALID = MCE_INVALID_TRANSLATION, + /** Brightness level step instantly */ + BRIGHTNESS_STEP_DIRECT = 0, + /** Only step after a blank->unblank cycle (only for step-down) */ + BRIGHTNESS_STEP_UNBLANK = 1, + /** Default setting when performing brightness level step-down */ + DEFAULT_BRIGHTNESS_STEP_DOWN_POLICY = BRIGHTNESS_STEP_DIRECT +} brightness_step_policy_t; + +/** Mapping of brightness level step integer <-> policy string */ +static const mce_translation_t brightness_step_policy_translation[] = { + { + .number = BRIGHTNESS_STEP_DIRECT, + .string = "direct", + }, { + .number = BRIGHTNESS_STEP_UNBLANK, + .string = "unblank", + }, { /* MCE_INVALID_TRANSLATION marks the end of this array */ + .number = MCE_INVALID_TRANSLATION, + .string = NULL + } +}; + +/** Brightness step-down policy */ +static brightness_step_policy_t brightness_step_down_policy = + DEFAULT_BRIGHTNESS_STEP_DOWN_POLICY; + +/** + * GConf callback for ALS settings + * + * @param gcc Unused + * @param id Connection ID from gconf_client_notify_add() + * @param entry The modified GConf entry + * @param data Unused + */ +static void als_gconf_cb(GConfClient *const gcc, const guint id, + GConfEntry *const entry, gpointer const data) +{ + const GConfValue *gcv = gconf_entry_get_value(entry); + + (void)gcc; + (void)data; + + /* Key is unset */ + if (gcv == NULL) { + mce_log(LL_DEBUG, + "GConf Key `%s' has been unset", + gconf_entry_get_key(entry)); + goto EXIT; + } + + if (id == als_enabled_gconf_cb_id) { + gint tmp = gconf_value_get_bool(gcv); + + /* Only care about the setting if there's an ALS available */ + if (als_available == TRUE) + als_enabled = tmp; + } else { + mce_log(LL_WARN, + "Spurious GConf value received; confused!"); + } + +EXIT: + return; +} + +/** + * Get the ALS type + * + * @return The ALS-type + */ +static als_type_t get_als_type(void) +{ + static als_type_t als_type = ALS_TYPE_UNSET; + + /* If we have the ALS-type already, return it */ + if (als_type != ALS_TYPE_UNSET) + goto EXIT; + + if (g_access(ALS_DEVICE_PATH_AVAGO, R_OK) == 0) { + als_type = ALS_TYPE_AVAGO; + als_device_path = ALS_DEVICE_PATH_AVAGO; + als_calib0_path = ALS_CALIB_PATH_AVAGO; + als_threshold_range_path = ALS_THRESHOLD_RANGE_PATH_AVAGO; + display_als_profiles = display_als_profiles_rm696; + led_als_profiles = led_als_profiles_rm696; + use_median_filter = FALSE; + } else if (g_access(ALS_DEVICE_PATH_DIPRO, R_OK) == 0) { + als_type = ALS_TYPE_DIPRO; + als_device_path = ALS_DEVICE_PATH_DIPRO; + als_calib0_path = ALS_CALIB_PATH_DIPRO; + als_threshold_range_path = ALS_THRESHOLD_RANGE_PATH_DIPRO; + display_als_profiles = display_als_profiles_rm680; + led_als_profiles = led_als_profiles_rm680; + kbd_als_profiles = kbd_als_profiles_rm680; + use_median_filter = FALSE; + } else if (g_access(ALS_LUX_PATH_TSL2563, R_OK) == 0) { + als_type = ALS_TYPE_TSL2563; + als_lux_path = ALS_LUX_PATH_TSL2563; + als_calib0_path = ALS_CALIB0_PATH_TSL2563; + als_calib1_path = ALS_CALIB1_PATH_TSL2563; + display_als_profiles = display_als_profiles_rx51; + led_als_profiles = led_als_profiles_rx51; + kbd_als_profiles = kbd_als_profiles_rx51; + use_median_filter = TRUE; + } else if (g_access(ALS_LUX_PATH_TSL2562, R_OK) == 0) { + als_type = ALS_TYPE_TSL2562; + als_lux_path = ALS_LUX_PATH_TSL2562; + als_calib0_path = ALS_CALIB0_PATH_TSL2562; + als_calib1_path = ALS_CALIB1_PATH_TSL2562; + display_als_profiles = display_als_profiles_rx44; + led_als_profiles = led_als_profiles_rx44; + kbd_als_profiles = kbd_als_profiles_rx44; + use_median_filter = TRUE; + } else { + als_type = ALS_TYPE_NONE; + } + + /* If the range path doesn't exist, disable it */ + if (als_threshold_range_path != NULL) { + if (g_access(als_threshold_range_path, W_OK) == -1) + als_threshold_range_path = NULL; + } + + mce_log(LL_DEBUG, "ALS-type: %d", als_type); + +EXIT: + return als_type; +} + +/** + * Calibrate the ALS using calibration values from CAL + */ +static void calibrate_als(void) +{ + struct cal *cal_data = NULL; + + /* If we don't have any calibration points, don't bother */ + if ((als_calib0_path == NULL) && (als_calib1_path == NULL)) + goto EXIT; + + /* Retrieve the calibration data stored in CAL */ + if (cal_init(&cal_data) >= 0) { + void *ptr = NULL; + unsigned long len; + int retval; + + if ((retval = cal_read_block(cal_data, ALS_CALIB_IDENTIFIER, + &ptr, &len, 0)) == 0) { + guint32 *als_calib = ptr; + + /* Correctness checks */ + if ((len == sizeof (guint32)) || + (len == (2 * sizeof (guint32)))) { + /* Write calibration values */ + if (als_calib0_path != NULL) + mce_write_number_string_to_file(als_calib0_path, als_calib[0], NULL, TRUE, TRUE); + + if (als_calib1_path != NULL) + mce_write_number_string_to_file(als_calib1_path, als_calib[1], NULL, TRUE, TRUE); + } else { + mce_log(LL_ERR, + "Received incorrect number of ALS " + "calibration values from CAL"); + } + + free(ptr); + } else { + mce_log(LL_ERR, + "cal_read_block() (als_calib) failed; " + "retval: %d", + retval); + } + + cal_finish(cal_data); + } else { + mce_log(LL_ERR, + "cal_init() failed"); + } + +EXIT: + return; +} + +/** + * Use the ALS profiles to calculate proper ALS modified values; + * also reprogram the sensor thresholds if the sensor supports such + * + * @param profiles The profile struct to use for calculations + * @param profile The profile to use + * @param lux The lux value + * @param[in,out] level The old level; will be replaced by the new level + * @param[out] lower The new lower ALS interrupt threshold + * @param[out] upper The new upper ALS interrupt threshold + * @return The brightness in % of maximum + */ +static gint filter_data(als_profile_struct *profiles, als_profile_t profile, + gint lux, gint *level, gint *lower, gint *upper) +{ + gint tmp = *level; + gint i; + + if (tmp == -1) + tmp = 0; + else if (tmp > ALS_RANGES) + tmp = ALS_RANGES; + + for (i = 0; i < ALS_RANGES; i++) { + *level = i; + + if (profiles[profile].range[i][0] == -1) + break; + + if (lux < profiles[profile].range[i][(((i + 1) - tmp) > 0) ? 1 : 0]) + break; + } + + *lower = (i == 0) ? 0 : profiles[profile].range[i - 1][0]; + + if (i >= ALS_RANGES) { + /* This is a programming error! */ + mce_log(LL_CRIT, + "The ALS profile %d lacks terminating { -1, -1 }", + profile); + *upper = 65535; + } else { + *upper = (profiles[profile].range[i][1] == -1) ? 65535 : profiles[profile].range[i][1]; + } + + return profiles[profile].value[*level]; +} + +/** + * Ambient Light Sensor filter for display brightness + * + * @param data The un-processed brightness setting (1-5) stored in a pointer + * @return The processed brightness value (percentage) + */ +static gpointer display_brightness_filter(gpointer data) +{ + /** Display ALS level */ + static gint display_als_level = -1; + gint raw = GPOINTER_TO_INT(data) - 1; + gpointer retval; + + /* If the display is off, don't update its brightness */ + if (display_state == MCE_DISPLAY_OFF) { + raw = 0; + goto EXIT; + } + + /* Safety net */ + if (raw < ALS_PROFILE_MINIMUM) + raw = ALS_PROFILE_MINIMUM; + else if (raw > ALS_PROFILE_MAXIMUM) + raw = ALS_PROFILE_MAXIMUM; + + if ((als_enabled == TRUE) && (display_als_profiles != NULL)) { + gint percentage = filter_data(display_als_profiles, raw, + als_lux, &display_als_level, + &display_brightness_lower, + &display_brightness_upper); + + raw = percentage; + } else { + raw = (raw + 1) * 20; + } + +EXIT: + retval = GINT_TO_POINTER(raw); + + return retval; +} + +/** + * Ambient Light Sensor filter for LED brightness + * + * @param data The un-processed brightness setting (1-5) stored in a pointer + * @return The processed brightness value + */ +static gpointer led_brightness_filter(gpointer data) +{ + /** LED ALS level */ + static gint led_als_level = -1; + gint brightness; + + if ((als_enabled == TRUE) && (led_als_profiles != NULL)) { + /* XXX: this always uses the NORMAL profile */ + gint percentage = filter_data(led_als_profiles, + ALS_PROFILE_NORMAL, + als_lux, &led_als_level, + &led_brightness_lower, + &led_brightness_upper); + brightness = (GPOINTER_TO_INT(data) * percentage) / 100; + } else { + brightness = GPOINTER_TO_INT(data); + } + + return GINT_TO_POINTER(brightness); +} + +/** + * Ambient Light Sensor filter for keyboard backlight brightness + * + * @param data The un-processed brightness setting (1-5) stored in a pointer + * @return The processed brightness value + */ +static gpointer key_backlight_filter(gpointer data) +{ + /** Keyboard ALS level */ + static gint kbd_als_level = -1; + gint brightness = 0; + + if ((als_enabled == TRUE) && (kbd_als_profiles != NULL)) { + /* XXX: this always uses the NORMAL profile */ + gint percentage = filter_data(kbd_als_profiles, + ALS_PROFILE_NORMAL, + als_lux, &kbd_als_level, + &kbd_brightness_lower, + &kbd_brightness_upper); + brightness = (GPOINTER_TO_INT(data) * percentage) / 100; + } else { + brightness = GPOINTER_TO_INT(data); + } + + return GINT_TO_POINTER(brightness); +} + +/** + * Wrapper function for median_filter_init() + * + * @return TRUE on success, FALSE on failure + */ +static gboolean als_median_filter_init(void) +{ + gboolean status = TRUE; + + if (use_median_filter == FALSE) + goto EXIT; + + /* Re-initialise the median filter */ + if (median_filter_init(&median_filter, + MEDIAN_FILTER_WINDOW_SIZE) == FALSE) { + mce_log(LL_CRIT, "median_filter_init() failed"); + als_enabled = FALSE; + status = FALSE; + } + +EXIT: + return status; +} + +/** + * Wrapper function for median_filter_map() + * + * @param value The value to insert + * @return The filtered value + */ +static gint als_median_filter_map(gint value) +{ + return (use_median_filter == TRUE) ? + median_filter_map(&median_filter, value) : value; +} + +/** + * Read a value from the ALS and update the median filter + * + * @return the filtered result of the read, + * -1 on failure, + * -2 if the ALS is disabled + */ +static gint als_read_value_filtered(void) +{ + gint filtered_read = -2; + gulong lux; + + if (als_enabled == FALSE) + goto EXIT; + + if (get_als_type() == ALS_TYPE_AVAGO) { + struct avago_als *als; + void *tmp = NULL; + gssize len = sizeof (struct avago_als); + + if (mce_read_chunk_from_file(als_device_path, &tmp, &len, + 0, -1) == FALSE) { + filtered_read = -1; + goto EXIT; + } + + if (len != sizeof (struct avago_als)) { + mce_log(LL_ERR, + "Short read from `%s'", + als_device_path); + g_free(tmp); + filtered_read = -1; + goto EXIT; + } + + als = (struct avago_als *)tmp; + lux = als->lux; + + g_free(tmp); + } else if (get_als_type() == ALS_TYPE_DIPRO) { + struct dipro_als *als; + void *tmp = NULL; + gssize len = sizeof (struct dipro_als); + + if (mce_read_chunk_from_file(als_device_path, &tmp, &len, + 0, -1) == FALSE) { + filtered_read = -1; + goto EXIT; + } + + if (len != sizeof (struct dipro_als)) { + mce_log(LL_ERR, + "Short read from `%s'", + als_device_path); + g_free(tmp); + filtered_read = -1; + goto EXIT; + } + + als = (struct dipro_als *)tmp; + lux = als->lux; + + g_free(tmp); + } else { + /* Read lux value from ALS */ + if (mce_read_number_string_from_file(als_lux_path, + &lux, &als_fp, + TRUE, FALSE) == FALSE) { + filtered_read = -1; + goto EXIT; + } + } + + filtered_read = als_median_filter_map(lux); + +EXIT: + return filtered_read; +} + +/** + * Adjust ALS thresholds if supported + * + * @param lower Lower threshold; + * any reading below this will generate interrupts + * @param upper Upper threshold; + * any reading above this will generate interrupts + */ +static void adjust_als_thresholds(gint lower, gint upper) +{ + /* Only adjust thresholds if there's support for doing so */ + if (als_threshold_range_path == NULL) + goto EXIT; + + /* If the lower threshold is higher than the upper threshold, + * set both to 0 to guarantee that we get a new interupt + */ + if (lower >= upper) { + lower = 0; + upper = 0; + } + + /* Only write to the threshold registers + * if we are monitoring the ALS + */ + if ((als_poll_timer_cb_id != 0) || (als_iomon_id != NULL)) { + gchar *str = g_strdup_printf("%d %d", lower, upper); + mce_write_string_to_file(als_threshold_range_path, str); + g_free(str); + } + +EXIT: + return; +} + +/** + * Timer callback for polling of the Ambient Light Sensor + * + * @param data Unused + * @return Always returns TRUE, for continuous polling, + unless the ALS is disabled + */ +static gboolean als_poll_timer_cb(gpointer data) +{ + gboolean status = FALSE; + gint new_lux; + gint lower; + gint upper; + + (void)data; + + /* Read lux value from ALS */ + if ((new_lux = als_read_value_filtered()) == -2) + goto EXIT; + + /* There's no point in readjusting the brightness + * if the read failed; also no readjustment is needed + * if the read is identical to the old value, unless + * we've never set the threshold values before + */ + if ((new_lux == -1) || + ((als_lux == new_lux) && (display_brightness_lower != -1))) + goto EXIT2; + + als_lux = new_lux; + + /* Re-filter the brightness */ + (void)execute_datapipe(&display_brightness_pipe, NULL, + USE_CACHE, DONT_CACHE_INDATA); + (void)execute_datapipe(&led_brightness_pipe, NULL, + USE_CACHE, DONT_CACHE_INDATA); + (void)execute_datapipe(&key_backlight_pipe, NULL, + USE_CACHE, DONT_CACHE_INDATA); + + /* The lower threshold is the largest of the lower thresholds */ + lower = display_brightness_lower; + + if (led_als_profiles != NULL) + lower = MAX(lower, led_brightness_lower); + + if (kbd_als_profiles != NULL) + lower = MAX(lower, kbd_brightness_lower); + + /* The upper threshold is the smallest of the upper thresholds */ + upper = display_brightness_upper; + + if (led_als_profiles != NULL) + upper = MIN(upper, led_brightness_upper); + + if (kbd_als_profiles != NULL) + upper = MIN(upper, kbd_brightness_upper); + + adjust_als_thresholds(lower, upper); + +EXIT2: + status = TRUE; + +EXIT: + if (status == FALSE) + als_poll_timer_cb_id = 0; + + return status; +} + +/** + * I/O monitor callback for the Ambient Light Sensor + * + * @param lux The lux value + */ +static void als_iomon_common(gint lux) +{ + gboolean status = FALSE; + gint new_lux; + gint lower; + gint upper; + + new_lux = als_median_filter_map(lux); + + /* No readjustment is needed if the read is identical + * to the old value, unless we've never set the threshold + * values before + */ + if ((als_lux == new_lux) && (display_brightness_lower != -1)) + goto EXIT; + + als_lux = new_lux; + + /* Re-filter the brightness */ + (void)execute_datapipe(&display_brightness_pipe, NULL, + USE_CACHE, DONT_CACHE_INDATA); + (void)execute_datapipe(&led_brightness_pipe, NULL, + USE_CACHE, DONT_CACHE_INDATA); + (void)execute_datapipe(&key_backlight_pipe, NULL, + USE_CACHE, DONT_CACHE_INDATA); + + /* The lower threshold is the largest of the lower thresholds */ + lower = display_brightness_lower; + + if (led_als_profiles != NULL) + lower = MAX(lower, led_brightness_lower); + + if (kbd_als_profiles != NULL) + lower = MAX(lower, kbd_brightness_lower); + + /* The upper threshold is the smallest of the upper thresholds */ + upper = display_brightness_upper; + + if (led_als_profiles != NULL) + upper = MIN(upper, led_brightness_upper); + + if (kbd_als_profiles != NULL) + upper = MIN(upper, kbd_brightness_upper); + + adjust_als_thresholds(lower, upper); + +EXIT: + status = TRUE; + + return; +} + + +/** + * I/O monitor callback for the Dipro Ambient Light Sensor + * + * @param data The new data + * @param bytes_read Unused + */ +static void als_iomon_dipro_cb(gpointer data, gsize bytes_read) +{ + struct dipro_als *als; + als = data; + + /* Don't process invalid reads */ + if (bytes_read != sizeof (struct dipro_als)) { + als_poll_timer_cb_id = 0; + cancel_als_poll_timer(); + goto EXIT; + } + + als_iomon_common(als->lux); + +EXIT: + return; +} + +/** + * I/O monitor callback for the Avago Ambient Light Sensor + * + * @param data The new data + * @param bytes_read Unused + */ +static void als_iomon_avago_cb(gpointer data, gsize bytes_read) +{ + struct avago_als *als; + als = data; + + /* Don't process invalid reads */ + if (bytes_read != sizeof (struct avago_als)) { + als_poll_timer_cb_id = 0; + cancel_als_poll_timer(); + goto EXIT; + } + + /* The ALS hasn't got anything to offer */ + if ((als->status & APDS990X_ALS_UPDATED) == 0) + goto EXIT; + + if ((als->status & APDS990X_ALS_SATURATED) != 0) { + als_iomon_common(G_MAXUINT); + } else { + als_iomon_common(als->lux); + } + +EXIT: + return; +} + + +/** + * Cancel Ambient Light Sensor poll timer + */ +static void cancel_als_poll_timer(void) +{ + /* Unregister ALS I/O monitor */ + if (als_iomon_id != NULL) { + mce_unregister_io_monitor(als_iomon_id); + als_iomon_id = NULL; + } + + /* Disable old ALS timer */ + if (als_poll_timer_cb_id != 0) { + g_source_remove(als_poll_timer_cb_id); + als_poll_timer_cb_id = 0; + } +} + +/** + * Setup Ambient Light Sensor poll timer + */ +static void setup_als_poll_timer(void) +{ + /* If we don't want polling to take place, disable it */ + if (als_poll_interval == 0) { + cancel_als_poll_timer(); + + /* Close the file pointer when we disable the als polling + * to ensure that the ALS can sleep + */ + (void)mce_close_file(als_lux_path, &als_fp); + goto EXIT; + } + + switch (get_als_type()) { + case ALS_TYPE_AVAGO: + /* If we already have have an I/O monitor registered, + * we can skip this + */ + if (als_iomon_id != NULL) + goto EXIT; + + /* Register ALS I/O monitor */ + als_iomon_id = mce_register_io_monitor_chunk(-1, als_device_path, MCE_IO_ERROR_POLICY_WARN, G_IO_IN | G_IO_PRI | G_IO_ERR, FALSE, als_iomon_avago_cb, sizeof (struct avago_als)); + break; + + case ALS_TYPE_DIPRO: + /* If we already have have an I/O monitor registered, + * we can skip this + */ + if (als_iomon_id != NULL) + goto EXIT; + + /* Register ALS I/O monitor */ + als_iomon_id = mce_register_io_monitor_chunk(-1, als_device_path, MCE_IO_ERROR_POLICY_WARN, G_IO_IN | G_IO_PRI | G_IO_ERR, FALSE, als_iomon_dipro_cb, sizeof (struct dipro_als)); + break; + + default: + /* Setup new timer; + * for light sensors that we don't use polling for + */ + cancel_als_poll_timer(); + als_poll_timer_cb_id = g_timeout_add(als_poll_interval, + als_poll_timer_cb, NULL); + break; + } + +EXIT: + return; +} + +/** + * Handle display state change + * + * @param data The display stated stored in a pointer + */ +static void display_state_trigger(gconstpointer data) +{ + static display_state_t old_display_state = MCE_DISPLAY_UNDEF; + gint old_als_poll_interval = als_poll_interval; + display_state = GPOINTER_TO_INT(data); + + if (als_enabled == FALSE) + goto EXIT; + + old_als_poll_interval = als_poll_interval; + + /* Update poll timeout */ + switch (display_state) { + case MCE_DISPLAY_OFF: + als_poll_interval = ALS_DISPLAY_OFF_POLL_FREQ; + break; + + case MCE_DISPLAY_DIM: + als_poll_interval = ALS_DISPLAY_DIM_POLL_FREQ; + break; + + case MCE_DISPLAY_UNDEF: + case MCE_DISPLAY_ON: + default: + als_poll_interval = ALS_DISPLAY_ON_POLL_FREQ; + break; + } + + /* Re-fill the median filter */ + if ((old_display_state == MCE_DISPLAY_OFF) && + ((display_state == MCE_DISPLAY_ON) || + (display_state == MCE_DISPLAY_DIM))) { + gint new_lux; + + cancel_als_poll_timer(); + +#ifdef ALS_DISPLAY_OFF_FLUSH_FILTER + /* Re-initialise the median filter */ + if (als_median_filter_init() == FALSE) + goto EXIT; +#endif /* ALS_DISPLAY_OFF_FLUSH_FILTER */ + + /* Read lux value from ALS */ + new_lux = als_read_value_filtered(); + + /* There's no point in readjusting the brightness + * if the ambient light did not change, + * unless we use the unblank policy for step-downs + */ + if ((new_lux >= 0) && + ((als_lux != new_lux) || + (brightness_step_down_policy == + BRIGHTNESS_STEP_UNBLANK))) { + /* Re-filter the brightness */ + (void)execute_datapipe(&display_brightness_pipe, NULL, USE_CACHE, DONT_CACHE_INDATA); + (void)execute_datapipe(&led_brightness_pipe, NULL, USE_CACHE, DONT_CACHE_INDATA); + (void)execute_datapipe(&key_backlight_pipe, NULL, USE_CACHE, DONT_CACHE_INDATA); + } + } + + /* Reprogram timer, if needed */ + if ((als_poll_interval != old_als_poll_interval) || + ((als_poll_timer_cb_id == 0) && (als_iomon_id == NULL))) + setup_als_poll_timer(); + +EXIT: + old_display_state = display_state; + + return; +} + +/** + * Init function for the ALS filter + * + * @todo XXX status needs to be set on error! + * + * @param module Unused + * @return NULL on success, a string with an error message on failure + */ +G_MODULE_EXPORT const gchar *g_module_check_init(GModule *module); +const gchar *g_module_check_init(GModule *module) +{ + gchar *str = NULL; + + (void)module; + + /* Append triggers/filters to datapipes */ + append_filter_to_datapipe(&display_brightness_pipe, + display_brightness_filter); + append_filter_to_datapipe(&led_brightness_pipe, + led_brightness_filter); + append_filter_to_datapipe(&key_backlight_pipe, + key_backlight_filter); + append_output_trigger_to_datapipe(&display_state_pipe, + display_state_trigger); + + /* ALS enabled */ + /* Since we've set a default, error handling is unnecessary */ + (void)mce_gconf_get_bool(MCE_GCONF_DISPLAY_ALS_ENABLED_PATH, + &als_enabled); + + if (mce_gconf_notifier_add(MCE_GCONF_DISPLAY_PATH, + MCE_GCONF_DISPLAY_ALS_ENABLED_PATH, + als_gconf_cb, + &als_enabled_gconf_cb_id) == FALSE) + goto EXIT; + + /* Do we have an ALS at all? + * If so, make an initial read + */ + if (get_als_type() != ALS_TYPE_NONE) { + /* Initialise the median filter */ + if (als_median_filter_init() == FALSE) { + goto EXIT; + } + + /* Calibrate the ALS */ + calibrate_als(); + + /* Initial read of lux value from ALS */ + if ((als_lux = als_read_value_filtered()) >= 0) { + /* Set initial polling interval */ + als_poll_interval = ALS_DISPLAY_ON_POLL_FREQ; + + /* Setup ALS polling */ + setup_als_poll_timer(); + } else { + /* Reading from the ALS failed */ + als_lux = -1; + als_available = FALSE; + als_enabled = FALSE; + } + } else { + /* We don't have an ALS */ + als_lux = -1; + als_available = FALSE; + als_enabled = FALSE; + } + + /* Re-filter the brightness if we got an ALS-reading */ + if (als_lux != -1) { + (void)execute_datapipe(&display_brightness_pipe, NULL, + USE_CACHE, DONT_CACHE_INDATA); + (void)execute_datapipe(&led_brightness_pipe, NULL, + USE_CACHE, DONT_CACHE_INDATA); + (void)execute_datapipe(&key_backlight_pipe, NULL, + USE_CACHE, DONT_CACHE_INDATA); + } + + /* Get configuration options */ + str = mce_conf_get_string(MCE_CONF_ALS_GROUP, + MCE_CONF_STEP_DOWN_POLICY, + "", + NULL); + + brightness_step_down_policy = mce_translate_string_to_int_with_default(brightness_step_policy_translation, str, DEFAULT_BRIGHTNESS_STEP_DOWN_POLICY); + g_free(str); + +EXIT: + return NULL; +} + +/** + * Exit function for the ALS filter + * + * @param module Unused + */ +G_MODULE_EXPORT void g_module_unload(GModule *module); +void g_module_unload(GModule *module) +{ + (void)module; + + als_enabled = FALSE; + + /* Close the ALS file pointer */ + (void)mce_close_file(als_lux_path, &als_fp); + + /* Remove triggers/filters from datapipes */ + remove_output_trigger_from_datapipe(&display_state_pipe, + display_state_trigger); + remove_filter_from_datapipe(&key_backlight_pipe, + key_backlight_filter); + remove_filter_from_datapipe(&led_brightness_pipe, + led_brightness_filter); + remove_filter_from_datapipe(&display_brightness_pipe, + display_brightness_filter); + + /* Remove all timer sources */ + cancel_als_poll_timer(); + + return; +} diff --git a/modules/filter-brightness-als.h b/modules/filter-brightness-als.h new file mode 100644 index 00000000..57e773d9 --- /dev/null +++ b/modules/filter-brightness-als.h @@ -0,0 +1,593 @@ +/** + * @file filter-brightness-als.h + * Headers for the Ambient Light Sensor level adjusting filter module + * for display backlight, key backlight, and LED brightness + *

+ * Copyright © 2007-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * @author Tuomo Tanskanen + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _FILTER_BRIGHTNESS_ALS_H_ +#define _FILTER_BRIGHTNESS_ALS_H_ + +/** Name of ALS configuration group */ +#define MCE_CONF_ALS_GROUP "ALS" + +/** Name of the configuration key for the brightness level step-down policy */ +#define MCE_CONF_STEP_DOWN_POLICY "StepDownPolicy" + +/* Paths for Avago APDS990x (QPDS-T900) ALS */ + +/** Device path for Avago ALS */ +#define ALS_DEVICE_PATH_AVAGO "/dev/apds990x0" + +#ifndef APDS990X_ALS_SATURATED +/** Read is saturated */ +#define APDS990X_ALS_SATURATED 0x1 +#endif /* APDS990X_ALS_SATURATED */ + +#ifndef APDS990X_ALS_UPDATED +/** Sensor has up to date data */ +#define APDS990X_ALS_UPDATED 0x4 +#endif /* APDS990X_ALS_UPDATED */ + +/** Struct for the Avago data */ +struct avago_als { + /** The filtered ambient light in lux */ + guint32 lux; /* 10x scale */ + /** The raw ambient light in lux */ + guint32 lux_raw; /* 10x scale */ + /** The filtered proximity */ + guint16 ps; + /** The raw proximity */ + guint16 ps_raw; + /** The sensor status */ + guint16 status; +} __attribute__((packed)); + +/** Base path to the Avago ALS */ +#define ALS_PATH_AVAGO "/sys/class/misc/apds990x0/device" +/** Path to the first calibration point for the Avago ALS */ +#define ALS_CALIB_PATH_AVAGO ALS_PATH_AVAGO "/als_calib" +/** ALS threshold range for the Avago ALS */ +#define ALS_THRESHOLD_RANGE_PATH_AVAGO ALS_PATH_AVAGO "/als_threshold_range" + + +/* Paths for the Dipro (BH1770GLC/SFH7770) ALS */ + +/** Device path for the Dipro ALS */ +#define ALS_DEVICE_PATH_DIPRO "/dev/bh1770glc_als" + +/** Struct for the Dipro data */ +struct dipro_als { + /** The ambient light in lux */ + guint16 lux; +} __attribute__((packed)); + +/** Base path to the Dipro ALS */ +#define ALS_PATH_DIPRO "/sys/class/misc/bh1770glc_als/device" +/** Path to the first calibration point for the Dipro ALS */ +#define ALS_CALIB_PATH_DIPRO ALS_PATH_DIPRO "/als_calib" + +/** ALS threshold range for the Dipro ALS */ +#define ALS_THRESHOLD_RANGE_PATH_DIPRO ALS_PATH_DIPRO "/als_thres_range" + +/* Paths for the BH1780GLI ALS */ + +/** Base path to the BH1780GLI ALS */ +#define ALS_PATH_BH1780GLI "/sys/devices/platform/i2c_omap.3/i2c-3/3-0029" +/** Path to the ALS lux value */ +#define ALS_LUX_PATH_BH1780GLI ALS_PATH_BH1780GLI "/lux" +/** Path to the first calibration point for the BH1780GLI ALS */ +#define ALS_CALIB_PATH_BH1780GLI ALS_PATH_BH1780GLI "/calib" + +/* Paths for the TSL2563 ALS */ + +/** Base path to the TSL2563 ALS */ +#define ALS_PATH_TSL2563 "/sys/class/i2c-adapter/i2c-2/2-0029" +/** Path to the TSL2563 ALS lux value */ +#define ALS_LUX_PATH_TSL2563 ALS_PATH_TSL2563 "/lux" +/** Path to the first calibration point for the TSL2563 ALS */ +#define ALS_CALIB0_PATH_TSL2563 ALS_PATH_TSL2563 "/calib0" +/** Path to the second calibration point for the TSL2563 ALS */ +#define ALS_CALIB1_PATH_TSL2563 ALS_PATH_TSL2563 "/calib1" + +/* Paths for the TSL2562 ALS */ + +/** Base path to the TSL2562 ALS */ +#define ALS_PATH_TSL2562 "/sys/devices/platform/i2c_omap.2/i2c-0/0-0029" +/** Path to the TSL2562 ALS lux value */ +#define ALS_LUX_PATH_TSL2562 ALS_PATH_TSL2562 "/lux" +/** Path to the first calibration point for the TSL2562 ALS */ +#define ALS_CALIB0_PATH_TSL2562 ALS_PATH_TSL2562 "/calib0" +/** Path to the second calibration point for the TSL2562 ALS */ +#define ALS_CALIB1_PATH_TSL2562 ALS_PATH_TSL2562 "/calib1" + +/** Path to the GConf settings for the display */ +#ifndef MCE_GCONF_DISPLAY_PATH +#define MCE_GCONF_DISPLAY_PATH "/system/osso/dsm/display" +#endif /* MCE_GCONF_DISPLAY_PATH */ +/** Path to the ALS enabled GConf setting */ +#define MCE_GCONF_DISPLAY_ALS_ENABLED_PATH MCE_GCONF_DISPLAY_PATH "/als_enabled" + +/** Default ALS polling frequency when the display is on */ +#define ALS_DISPLAY_ON_POLL_FREQ 1500 /* Milliseconds */ +/** Default ALS polling frequency when the display is dimmed */ +#define ALS_DISPLAY_DIM_POLL_FREQ 5000 /* Milliseconds */ +/** + * Default ALS polling frequency when the display is off + * + * 0 disables polling completely; + * with hardware that supports power saving + * in a better way, 60000 should be used + */ +#define ALS_DISPLAY_OFF_POLL_FREQ 0 /* Milliseconds */ +/** + * Define this to re-initialise the median filter on display blank; + * this will trigger a re-read on wakeup + */ +#define ALS_DISPLAY_OFF_FLUSH_FILTER + +/** Window size for the median filter */ +#define MEDIAN_FILTER_WINDOW_SIZE 5 + +/** CAL identifier for the ALS calibration values */ +#define ALS_CALIB_IDENTIFIER "als_calib" + +/** Number of ranges in ALS profile */ +#define ALS_RANGES 10 + +/** ALS profile */ +typedef struct { + /** Lower and upper bound for each brightness range */ + gint range[ALS_RANGES][2]; + /** brightness in % */ + gint value[ALS_RANGES + 1]; +} als_profile_struct; + +/** + * ALS profile for the display in: + * RM-716 + * RM-696 + * + * [255-212] 100% + * [211-154] 83% + * [153-110] 60% + * [109-77] 42% + * [76-52] 30% + * [51-26] 20% + * [25-13] 10% + * [12-1] 5% + * [0] 0% + */ +als_profile_struct display_als_profiles_rm696[] = { + { /* Minimum */ + { + { 30, 50 }, + { 70, 100 }, + { 150, 200 }, + { 800, 1100 }, + { 5000, 10000 }, + { 10000, 35000 }, + { 20000, 50000 }, + { -1, -1 }, + }, { 5, 10, 20, 30, 42, 60, 83, 100 } + }, { /* Economy */ + { + { 10, 15 }, + { 30, 50 }, + { 50, 100 }, + { 150, 200 }, + { 800, 1100 }, + { 5000, 10000 }, + { 10000, 35000 }, + { -1, -1 }, + }, { 5, 10, 20, 30, 42, 60, 83, 100 } + }, { /* Normal */ + { + { 3, 5 }, + { 10, 15 }, + { 20, 30 }, + { 50, 100 }, + { 150, 200 }, + { 800, 1100 }, + { 5000, 10000 }, + { -1, -1 }, + }, { 5, 10, 20, 30, 42, 60, 83, 100 } + }, { /* Bright */ + { + { 3, 5 }, + { 10, 15 }, + { 20, 30 }, + { 50, 100 }, + { 150, 200 }, + { 800, 1100 }, + { -1, -1 }, + }, { 10, 20, 30, 42, 60, 83, 100 } + }, { /* Maximum */ + { + { 20, 30 }, + { 50, 100 }, + { -1, -1 }, +/* XXX: Insane request from higher management */ + }, { 30, 60, 100 } + } +}; + +/** + * ALS profile for the display in: + * RX-71 (HWID: 3xxx) + * RM-680 + * RM-690 + */ +als_profile_struct display_als_profiles_rm680[] = { + { /* Minimum */ + { + { 3, 5 }, + { 20, 30 }, + { 50, 100 }, + { 150, 200 }, + { 800, 1100 }, + { 8000, 20000 }, + { 10000, 35000 }, + { 20000, 50000 }, + { -1, -1 }, + }, { 1, 3, 6, 13, 22, 35, 50, 70, 100 } + }, { /* Economy */ + { + { 3, 5 }, + { 15, 15 }, + { 20, 30 }, + { 50, 100 }, + { 150, 200 }, + { 800, 1100 }, + { 8000, 20000 }, + { 10000, 35000 }, + { -1, -1 }, + }, { 3, 4, 6, 10, 22, 35, 60, 70, 100 } + }, { /* Normal */ + { + { 3, 5 }, + { 10, 15 }, + { 20, 30 }, + { 50, 100 }, + { 150, 200 }, + { 800, 1100 }, + { 5000, 10000 }, + { 8000, 20000 }, + { -1, -1 }, + }, { 4, 6, 10, 16, 30, 50, 70, 83, 100 } + }, { /* Bright */ + { + { 3, 5 }, + { 10, 15 }, + { 20, 30 }, + { 50, 100 }, + { 150, 200 }, + { 800, 1100 }, + { 5000, 10000 }, + { -1, -1 }, + }, { 5, 10, 16, 22, 35, 60, 83, 100 } + }, { /* Maximum */ + { + { 20, 30 }, + { 50, 100 }, + { 150, 200 }, + { 800, 1100 }, + { -1, -1 }, +/* XXX: Insane request from higher management */ + }, { 30, 50, 100, 100, 100 } +// }, { 16, 22, 30, 83, 100 } + } +}; + +/** + * ALS profile for the display in: + * RX-51 + */ +als_profile_struct display_als_profiles_rx51[] = { + { /* Minimum */ + { + { 24, 32 }, + { 160, 320 }, + { 720, 1200 }, + { 14400, 17600 }, + { -1, -1 }, + }, { 3, 10, 30, 50, 1 } + }, { /* Economy */ + { + { 24, 40 }, + { 100, 200 }, + { 300, 500 }, + { 720, 1200 }, + { -1, -1 }, + }, { 10, 20, 40, 60, 80 } + }, { /* Normal */ + { + { 24, 40 }, + { 100, 200 }, + { 300, 500 }, + { 720, 1200 }, + { -1, -1 }, + }, { 17, 30, 60, 90, 100 } + }, { /* Bright */ + { + { 24, 40 }, + { 50, 70 }, + { 60, 80 }, + { 100, 160 }, + { 200, 300 }, + { -1, -1 }, + }, { 25, 40, 60, 75, 90, 100 } + }, { /* Maximum */ + { + { 32, 64 }, + { 160, 320 }, + { -1, -1 }, +/* XXX: Insane request from higher management */ + }, { 100, 100, 100 } +// }, { 35, 80, 100 } + } +}; + +/** + * ALS profile for the display in: + * RX-48 + * RX-44 + */ +als_profile_struct display_als_profiles_rx44[] = { + { /* Minimum */ + { + { 10000, 13000 }, + { -1, -1 }, + }, { 5, 20 } + }, { /* Economy */ + { + { 2, 4 }, + { 24, 45 }, + { 260, 400 }, + { 10000, 13000 }, + { -1, -1 }, + }, { 5, 20, 40, 50, 70 } + }, { /* Normal */ + { + { 2, 4 }, + { 24, 45 }, + { 260, 400 }, + { 10000, 13000 }, + { -1, -1 }, + }, { 10, 20, 50, 80, 100 } + }, { /* Bright */ + { + { 2, 4 }, + { 24, 45 }, + { 260, 400 }, + { 10000, 13000 }, + { -1, -1 }, + }, { 30, 60, 80, 90, 100 } + }, { /* Maximum */ + { + { 2, 4 }, + { 8, 12 }, + { -1, -1 }, + }, { 50, 80, 100 } + } +}; + +/** + * ALS profile for the monochrome LED in: + * RM-716 - FIXME/TODO: No idea if usable for RM-696, just a copy of RM-680 + * RM-696 - FIXME/TODO: No idea if usable for RM-696, just a copy of RM-680 + */ +als_profile_struct led_als_profiles_rm696[] = { + { /* Minimum; unused */ + { { -1, -1 } }, + { } + }, { /* Economy; unused */ + { { -1, -1 } }, + { } + }, { /* Normal */ + { + { 3, 5 }, + { -1, -1 }, + }, { 80, 100 } + }, { /* Bright; unused */ + { { -1, -1 } }, + { } + }, { /* Maximum; unused */ + { { -1, -1 } }, + { } + } +}; + +/** + * ALS profile for the monochrome LED in: + * RX-71 (HWID: 3xxx) + * RM-680 + * RM-690 + */ +als_profile_struct led_als_profiles_rm680[] = { + { /* Minimum; unused */ + { { -1, -1 } }, + { } + }, { /* Economy; unused */ + { { -1, -1 } }, + { } + }, { /* Normal */ + { + { 3, 5 }, + { -1, -1 }, + }, { 80, 100 } + }, { /* Bright; unused */ + { { -1, -1 } }, + { } + }, { /* Maximum; unused */ + { { -1, -1 } }, + { } + } +}; + +/** + * ALS profile for the RGB LED in: + * RX-51 + */ +als_profile_struct led_als_profiles_rx51[] = { + { /* Minimum; unused */ + { { -1, -1 } }, + { } + }, { /* Economy; unused */ + { { -1, -1 } }, + { } + }, { /* Normal */ + { + { 32, 64 }, + { 100, 1000 }, + { -1, -1 }, + }, { 5, 5, 0 } + }, { /* Bright; unused */ + { { -1, -1 } }, + { } + }, { /* Maximum; unused */ + { { -1, -1 } }, + { } + } +}; + +/** + * ALS profile for the RGB LED in: + * RX-48 + * RX-44 + */ +als_profile_struct led_als_profiles_rx44[] = { + { /* Minimum; unused */ + { { -1, -1 } }, + { } + }, { /* Economy; unused */ + { { -1, -1 } }, + { } + }, { /* Normal */ + { + { 3, 5 }, + { 15, 27 }, + { -1, -1 }, + }, { 10, 30, 50 } + }, { /* Bright */ + { + { 3, 5 }, + { 15, 27 }, + { -1, -1 }, + }, { 30, 50, 100 } + }, { /* Maximum */ + { + { 3, 5 }, + { -1, -1 }, + }, { 50, 100 } + } +}; + +/** + * ALS profile for the keyboard backlight in: + * RX-71 (HWID: 3xxx) + * RM-680 + * RM-690 + */ +als_profile_struct kbd_als_profiles_rm680[] = { + { /* Minimum; unused */ + { { -1, -1 } }, + { } + }, { /* Economy; unused */ + { { -1, -1 } }, + { } + }, { /* Normal */ + { + { 24, 40 }, + { 100, 1000 }, + { -1, -1 }, + }, { 25, 50, 0 } + }, { /* Bright */ + { { -1, -1 } }, + { } + }, { /* Maximum; unused */ + { { -1, -1 } }, + { } + } +}; + +/** + * ALS profile for the keyboard backlight in: + * RX-51 + */ +als_profile_struct kbd_als_profiles_rx51[] = { + { /* Minimum; unused */ + { { -1, -1 } }, + { } + }, { /* Economy; unused */ + { { -1, -1 } }, + { } + }, { /* Normal */ + { + { 24, 40 }, + { -1, -1 }, + }, { 50, 0 } + }, { /* Bright */ + { { -1, -1 } }, + { } + }, { /* Maximum; unused */ + { { -1, -1 } }, + { } + } +}; + +/** + * ALS profile for the keyboard backlight in: + * RX-48 + * RX-44 + */ +als_profile_struct kbd_als_profiles_rx44[] = { + { /* Minimum; unused */ + { { -1, -1 } }, + { } + }, { /* Economy; unused */ + { { -1, -1 } }, + { } + }, { /* Normal */ + { + { 3, 5 }, + { 15, 27 }, + { -1, -1 }, + }, { 50, 100, 0 } + }, { /* Bright */ + { + { 3, 5 }, + { 15, 27 }, + { -1, -1 }, + }, { 80, 100, 0 } + }, { /* Maximum; unused */ + { { -1, -1 } }, + { } + } +}; + +/** ALS profiles */ +typedef enum { + ALS_PROFILE_MINIMUM = 0, /**< Minimum profile */ + ALS_PROFILE_ECONOMY, /**< Economy profile */ + ALS_PROFILE_NORMAL, /**< Normal profile */ + ALS_PROFILE_BRIGHT, /**< Bright profile */ + ALS_PROFILE_MAXIMUM /**< Maximum profile */ +} als_profile_t; + +#endif /* _FILTER_BRIGHTNESS_ALS_H_ */ diff --git a/modules/filter-brightness-simple.c b/modules/filter-brightness-simple.c new file mode 100644 index 00000000..bd8be58e --- /dev/null +++ b/modules/filter-brightness-simple.c @@ -0,0 +1,135 @@ +/** + * @file filter-brightness-simple.c + * Simple level adjusting brightness filter module + * for display backlight brightness + * This file implements a filter module for MCE + *

+ * Copyright © 2007-2009 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include +#include + +#include "mce.h" +#include "filter-brightness-simple.h" + +#include "datapipe.h" /* append_filter_to_datapipe(), + * remove_filter_from_datapipe(), + * execute_datapipe() + */ + +/** Module name */ +#define MODULE_NAME "filter-brightness-simple" + +/** Functionality provided by this module */ +static const gchar *const provides[] = { + "display-brightness-filter", + NULL +}; + +/** Functionality that this module enhances */ +static const gchar *const enhances[] = { + "display-brightness", + NULL +}; +/** Module information */ +G_MODULE_EXPORT module_info_struct module_info = { + /** Name of the module */ + .name = MODULE_NAME, + /** Module enhances */ + .enhances = enhances, + /** Module provides */ + .provides = provides, + /** Module priority */ + .priority = 250 +}; + +/** Display state */ +static display_state_t display_state = MCE_DISPLAY_UNDEF; + +/** + * Simple level adjustment filter for display brightness + * + * @param data The un-processed brightness setting (1-5) stored in a pointer + * @return The processed brightness value (percentage) + */ +static gpointer display_brightness_filter(gpointer data) G_GNUC_PURE; +static gpointer display_brightness_filter(gpointer data) +{ + gint raw = GPOINTER_TO_INT(data); + gpointer retval; + + /* If the display is off, don't update its brightness */ + if (display_state == MCE_DISPLAY_OFF) { + raw = 0; + goto EXIT; + } + + /* Safety net */ + if (raw < DISPLAY_BRIGHTNESS_MINIMUM) + raw = DISPLAY_BRIGHTNESS_MINIMUM; + else if (raw > DISPLAY_BRIGHTNESS_MAXIMUM) + raw = DISPLAY_BRIGHTNESS_MAXIMUM; + + /* Convert to percentage */ + raw *= 20; + +EXIT: + retval = GINT_TO_POINTER(raw); + + return retval; +} + +/** + * Init function for the simple level-adjusting brightness filter + * + * @todo XXX status needs to be set on error! + * + * @param module Unused + * @return NULL on success, a string with an error message on failure + */ +G_MODULE_EXPORT const gchar *g_module_check_init(GModule *module); +const gchar *g_module_check_init(GModule *module) +{ + (void)module; + + /* Append triggers/filters to datapipes */ + append_filter_to_datapipe(&display_brightness_pipe, + display_brightness_filter); + + /* Re-filter the brightness */ + (void)execute_datapipe(&display_brightness_pipe, NULL, + USE_CACHE, DONT_CACHE_INDATA); + + return NULL; +} + +/** + * Exit function for the simple level-adjusting brightness filter + * + * @param module Unused + */ +G_MODULE_EXPORT void g_module_unload(GModule *module); +void g_module_unload(GModule *module) +{ + (void)module; + + /* Remove triggers/filters from datapipes */ + remove_filter_from_datapipe(&display_brightness_pipe, + display_brightness_filter); + + return; +} diff --git a/modules/filter-brightness-simple.h b/modules/filter-brightness-simple.h new file mode 100644 index 00000000..5f40f18f --- /dev/null +++ b/modules/filter-brightness-simple.h @@ -0,0 +1,30 @@ +/** + * @file filter-brightness-simple.h + * Headers for the simple brightness filter module + * for display backlight brightness + *

+ * Copyright © 2007, 2009 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _FILTER_BRIGHTNESS_SIMPLE_H_ +#define _FILTER_BRIGHTNESS_SIMPLE_H_ + +/** Minimum display brightness */ +#define DISPLAY_BRIGHTNESS_MINIMUM 1 +/** Maximum display brightness */ +#define DISPLAY_BRIGHTNESS_MAXIMUM 5 + +#endif /* _FILTER_BRIGHTNESS_SIMPLE_H_ */ diff --git a/modules/inactivity.c b/modules/inactivity.c new file mode 100644 index 00000000..1cdbd5a0 --- /dev/null +++ b/modules/inactivity.c @@ -0,0 +1,313 @@ +/** + * @file inactivity.c + * Inactivity module -- this implements inactivity logic for MCE + *

+ * Copyright © 2007-2009 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include +#include + +#include "mce.h" + +#include "mce-log.h" /* mce_log(), LL_* */ +#include "mce-dbus.h" /* Direct: + * --- + * mce_dbus_handler_add(), + * dbus_send_message(), + * dbus_new_method_reply(), + * dbus_new_signal(), + * dbus_message_append_args(), + * dbus_message_unref(), + * DBusMessage, + * DBUS_MESSAGE_TYPE_METHOD_CALL, + * DBUS_TYPE_BOOLEAN, + * DBUS_TYPE_INVALID, + * dbus_bool_t + * + * Indirect: + * --- + * MCE_SIGNAL_IF, + * MCE_SIGNAL_PATH, + * MCE_REQUEST_IF + * MCE_INACTIVITY_STATUS_GET, + * MCE_INACTIVITY_SIG + */ +#include "datapipe.h" /* datapipe_get_gbool(), + * append_filter_to_datapipe(), + * remove_filter_from_datapipe() + */ + +/** Module name */ +#define MODULE_NAME "inactivity" + +/** Functionality provided by this module */ +static const gchar *const provides[] = { MODULE_NAME, NULL }; + +/** Module information */ +G_MODULE_EXPORT module_info_struct module_info = { + /** Name of the module */ + .name = MODULE_NAME, + /** Module provides */ + .provides = provides, + /** Module priority */ + .priority = 250 +}; + +/** ID for inactivity timeout source */ +static guint inactivity_timeout_cb_id = 0; + +/** Device inactivity state */ +static gboolean device_inactive = FALSE; + +/** + * Send an inactivity status reply or signal + * + * @param method_call A DBusMessage to reply to; + * pass NULL to send an inactivity status signal instead + * @return TRUE on success, FALSE on failure + */ +static gboolean send_inactivity_status(DBusMessage *const method_call) +{ + DBusMessage *msg = NULL; + gboolean status = FALSE; + + mce_log(LL_DEBUG, + "Sending inactivity status: %s", + device_inactive ? "inactive" : "active"); + + /* If method_call is set, send a reply, + * otherwise, send a signal + */ + if (method_call != NULL) { + msg = dbus_new_method_reply(method_call); + } else { + /* system_inactivity_ind */ + msg = dbus_new_signal(MCE_SIGNAL_PATH, MCE_SIGNAL_IF, + MCE_INACTIVITY_SIG); + } + + /* Append the inactivity status */ + if (dbus_message_append_args(msg, + DBUS_TYPE_BOOLEAN, &device_inactive, + DBUS_TYPE_INVALID) == FALSE) { + mce_log(LL_CRIT, + "Failed to append %sargument to D-Bus message " + "for %s.%s", + method_call ? "reply " : "", + method_call ? MCE_REQUEST_IF : + MCE_SIGNAL_IF, + method_call ? MCE_INACTIVITY_STATUS_GET : + MCE_INACTIVITY_SIG); + dbus_message_unref(msg); + goto EXIT; + } + + /* Send the message */ + status = dbus_send_message(msg); + +EXIT: + return status; +} + +/** + * D-Bus callback for the get inactivity status method call + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean inactivity_status_get_dbus_cb(DBusMessage *const msg) +{ + gboolean status = FALSE; + + mce_log(LL_DEBUG, "Received inactivity status get request"); + + /* Try to send a reply that contains the current inactivity status */ + if (send_inactivity_status(msg) == FALSE) + goto EXIT; + + status = TRUE; + +EXIT: + return status; +} + +/** + * Timeout callback for inactivity + * + * @param data Unused + * @return Always returns FALSE, to disable the timeout + */ +static gboolean inactivity_timeout_cb(gpointer data) +{ + (void)data; + + inactivity_timeout_cb_id = 0; + + (void)execute_datapipe(&device_inactive_pipe, GINT_TO_POINTER(TRUE), + USE_INDATA, CACHE_INDATA); + + return FALSE; +} + +/** + * Cancel inactivity timeout + */ +static void cancel_inactivity_timeout(void) +{ + /* Remove inactivity timeout source */ + if (inactivity_timeout_cb_id != 0) { + g_source_remove(inactivity_timeout_cb_id); + inactivity_timeout_cb_id = 0; + } +} + +/** + * Setup inactivity timeout + */ +static void setup_inactivity_timeout(void) +{ + gint timeout = datapipe_get_gint(inactivity_timeout_pipe); + + cancel_inactivity_timeout(); + + /* Sanitise timeout */ + if (timeout <= 0) + timeout = 30; + + /* Setup new timeout */ + inactivity_timeout_cb_id = + g_timeout_add_seconds(timeout, inactivity_timeout_cb, NULL); +} + +/** + * Datapipe filter for inactivity + * + * @param data The unfiltered inactivity state; + * TRUE if the device is inactive, + * FALSE if the device is active + * @return The filtered inactivity state; + * TRUE if the device is inactive, + * FALSE if the device is active + */ +static gpointer device_inactive_filter(gpointer data) +{ + static gboolean old_device_inactive = FALSE; + alarm_ui_state_t alarm_ui_state = + datapipe_get_gint(alarm_ui_state_pipe); + submode_t submode = mce_get_submode_int32(); + gpointer retval; + + device_inactive = GPOINTER_TO_INT(data); + + /* If the tklock is enabled, + * filter activity, unless there's an active alarm + */ + if ((device_inactive == FALSE) && + ((submode & MCE_TKLOCK_SUBMODE) != 0) && + (((alarm_ui_state != MCE_ALARM_UI_VISIBLE_INT32) && + (alarm_ui_state != MCE_ALARM_UI_RINGING_INT32)) || + (((submode & MCE_AUTORELOCK_SUBMODE) != 0)))) { + device_inactive = TRUE; + goto EXIT; + } + + /* We got activity; restart timeouts */ + if (device_inactive == FALSE) + setup_inactivity_timeout(); + + /* Only send the inactivity status if it changed */ + if ((old_device_inactive != device_inactive) && + (((submode & MCE_TKLOCK_SUBMODE) == 0) || + (device_inactive == TRUE))) + send_inactivity_status(NULL); + + old_device_inactive = device_inactive; + +EXIT: + retval = GINT_TO_POINTER(device_inactive); + + return retval; +} + +/** + * Inactivity timeout trigger + * + * @param data Unused + */ +static void inactivity_timeout_trigger(gconstpointer data) +{ + (void)data; + + setup_inactivity_timeout(); +} + +/** + * Init function for the inactivity module + * + * @todo XXX status needs to be set on error! + * + * @param module Unused + * @return NULL on success, a string with an error message on failure + */ +G_MODULE_EXPORT const gchar *g_module_check_init(GModule *module); +const gchar *g_module_check_init(GModule *module) +{ + (void)module; + + /* Append triggers/filters to datapipes */ + append_filter_to_datapipe(&device_inactive_pipe, + device_inactive_filter); + append_output_trigger_to_datapipe(&inactivity_timeout_pipe, + inactivity_timeout_trigger); + + /* get_inactivity_status */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_INACTIVITY_STATUS_GET, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + inactivity_status_get_dbus_cb) == NULL) + goto EXIT; + + setup_inactivity_timeout(); + +EXIT: + return NULL; +} + +/** + * Exit function for the inactivity module + * + * @todo D-Bus unregistration + * + * @param module Unused + */ +G_MODULE_EXPORT void g_module_unload(GModule *module); +void g_module_unload(GModule *module) +{ + (void)module; + + /* Remove triggers/filters from datapipes */ + remove_output_trigger_from_datapipe(&inactivity_timeout_pipe, + inactivity_timeout_trigger); + remove_filter_from_datapipe(&device_inactive_pipe, + device_inactive_filter); + + /* Remove all timer sources */ + cancel_inactivity_timeout(); + + return; +} diff --git a/modules/keypad.c b/modules/keypad.c new file mode 100644 index 00000000..93a6006e --- /dev/null +++ b/modules/keypad.c @@ -0,0 +1,800 @@ +/** + * @file keypad.c + * Keypad module -- this handles the keypress logic for MCE + *

+ * Copyright © 2004-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include +#include + +#include /* exit(), EXIT_FAILURE */ + +#include "mce.h" +#include "keypad.h" + +#include "mce-io.h" /* mce_write_number_string_to_file() */ +#include "mce-hal.h" /* get_product_id() */ +#include "mce-lib.h" /* bin_to_string() */ +#include "mce-log.h" /* mce_log(), LL_* */ +#include "mce-dbus.h" /* Direct: + * --- + * mce_dbus_handler_add(), + * dbus_send_message(), + * dbus_new_method_reply(), + * dbus_message_append_args(), + * dbus_message_unref(), + * DBusMessage, + * DBUS_MESSAGE_TYPE_METHOD_CALL, + * DBUS_TYPE_BOOLEAN, + * DBUS_TYPE_INVALID, + * dbus_bool_t + * + * Indirect: + * --- + * MCE_REQUEST_IF, + * MCE_KEY_BACKLIGHT_STATE_GET + */ +#include "datapipe.h" /* execute_datapipe(), + * datapipe_get_gbool(), + * datapipe_get_guint(), + * datapipe_get_old_gint(), + * append_output_trigger_to_datapipe(), + * remove_output_trigger_from_datapipe() + */ +#include "mce-conf.h" /* mce_conf_get_bool() */ + +#include "mce-dbus.h" +#include +#include + +/** Module name */ +#define MODULE_NAME "keypad" + +/** Functionality provided by this module */ +static const gchar *const provides[] = { MODULE_NAME, NULL }; + +/** Module information */ +G_MODULE_EXPORT module_info_struct module_info = { + /** Name of the module */ + .name = MODULE_NAME, + /** Module provides */ + .provides = provides, + /** Module priority */ + .priority = 100 +}; + +/** + * The ID of the timeout used for the key backlight + */ +static guint key_backlight_timeout_cb_id = 0; + +/** Default backlight brightness */ +static gint key_backlight_timeout = DEFAULT_KEY_BACKLIGHT_TIMEOUT; + +/** Default backlight fade in time */ +static gint key_backlight_fade_in_time = DEFAULT_KEY_BACKLIGHT_FADE_IN_TIME; + +/** Default backlight fade out time */ +static gint key_backlight_fade_out_time = DEFAULT_KEY_BACKLIGHT_FADE_OUT_TIME; + +/** Key backlight enabled/disabled */ +static gboolean key_backlight_is_enabled = FALSE; + +/** Key backlight channel 0 LED current path */ +static gchar *led_current_kb0_path = NULL; +/** Key backlight channel 1 LED current path */ +static gchar *led_current_kb1_path = NULL; +/** Key backlight channel 2 LED current path */ +static gchar *led_current_kb2_path = NULL; +/** Key backlight channel 3 LED current path */ +static gchar *led_current_kb3_path = NULL; +/** Key backlight channel 4 LED current path */ +static gchar *led_current_kb4_path = NULL; +/** Key backlight channel 5 LED current path */ +static gchar *led_current_kb5_path = NULL; + +/** Key backlight channel 0 backlight path */ +static gchar *led_brightness_kb0_path = NULL; +/** Key backlight channel 1 backlight path */ +static gchar *led_brightness_kb1_path = NULL; +/** Key backlight channel 2 backlight path */ +static gchar *led_brightness_kb2_path = NULL; +/** Key backlight channel 3 backlight path */ +static gchar *led_brightness_kb3_path = NULL; +/** Key backlight channel 4 backlight path */ +static gchar *led_brightness_kb4_path = NULL; +/** Key backlight channel 5 backlight path */ +static gchar *led_brightness_kb5_path = NULL; + +/** Path to engine 3 mode */ +static gchar *engine3_mode_path = NULL; + +/** Path to engine 3 load */ +static gchar *engine3_load_path = NULL; + +/** Path to engine 3 leds */ +static gchar *engine3_leds_path = NULL; + +/** File pointer for the keyboard backlight 1 LED brightness */ +static FILE *led_brightness_kb0_fp = NULL; +/** File pointer for the keyboard backlight 2 LED brightness */ +static FILE *led_brightness_kb1_fp = NULL; +/** File pointer for the keyboard backlight 3 LED brightness */ +static FILE *led_brightness_kb2_fp = NULL; +/** File pointer for the keyboard backlight 4 LED brightness */ +static FILE *led_brightness_kb3_fp = NULL; +/** File pointer for the keyboard backlight 5 LED brightness */ +static FILE *led_brightness_kb4_fp = NULL; +/** File pointer for the keyboard backlight 6 LED brightness */ +static FILE *led_brightness_kb5_fp = NULL; + +/** File pointer for the keyboard backlight 1 LED current */ +static FILE *led_current_kb0_fp = NULL; +/** File pointer for the keyboard backlight 2 LED current */ +static FILE *led_current_kb1_fp = NULL; +/** File pointer for the keyboard backlight 3 LED current */ +static FILE *led_current_kb2_fp = NULL; +/** File pointer for the keyboard backlight 4 LED current */ +static FILE *led_current_kb3_fp = NULL; +/** File pointer for the keyboard backlight 5 LED current */ +static FILE *led_current_kb4_fp = NULL; +/** File pointer for the keyboard backlight 6 LED current */ +static FILE *led_current_kb5_fp = NULL; + +/** File pointer for the N810 keypad fadetime */ +static FILE *n810_keypad_fadetime_fp = NULL; +/** File pointer for the N810 keyboard fadetime */ +static FILE *n810_keyboard_fadetime_fp = NULL; + + +/** Key backlight mask */ +static guint key_backlight_mask = 0; + +static void cancel_key_backlight_timeout(void); + +/** + * Setup model specific key backlight values/paths + */ +static void setup_key_backlight(void) +{ + switch (get_product_id()) { + case PRODUCT_RM690: + case PRODUCT_RM680: + key_backlight_mask = MCE_LYSTI_KB_BACKLIGHT_MASK_RM680; + + led_current_kb0_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_CURRENT_SUFFIX, NULL); + led_current_kb1_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL1, MCE_LED_CURRENT_SUFFIX, NULL); + led_current_kb2_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL2, MCE_LED_CURRENT_SUFFIX, NULL); + led_current_kb3_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL3, MCE_LED_CURRENT_SUFFIX, NULL); + led_current_kb4_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL4, MCE_LED_CURRENT_SUFFIX, NULL); + led_current_kb5_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL5, MCE_LED_CURRENT_SUFFIX, NULL); + + led_brightness_kb0_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_BRIGHTNESS_SUFFIX, NULL); + led_brightness_kb1_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL1, MCE_LED_BRIGHTNESS_SUFFIX, NULL); + led_brightness_kb2_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL2, MCE_LED_BRIGHTNESS_SUFFIX, NULL); + led_brightness_kb3_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL3, MCE_LED_BRIGHTNESS_SUFFIX, NULL); + led_brightness_kb4_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL4, MCE_LED_BRIGHTNESS_SUFFIX, NULL); + led_brightness_kb5_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL5, MCE_LED_BRIGHTNESS_SUFFIX, NULL); + + engine3_mode_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE3, MCE_LED_MODE_SUFFIX, NULL); + engine3_load_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE3, MCE_LED_LOAD_SUFFIX, NULL); + engine3_leds_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE3, MCE_LED_LEDS_SUFFIX, NULL); + break; + + case PRODUCT_RX51: + key_backlight_mask = MCE_LYSTI_KB_BACKLIGHT_MASK_RX51; + + led_current_kb0_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_CURRENT_SUFFIX, NULL); + led_current_kb1_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL1, MCE_LED_CURRENT_SUFFIX, NULL); + led_current_kb2_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL2, MCE_LED_CURRENT_SUFFIX, NULL); + led_current_kb3_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL3, MCE_LED_CURRENT_SUFFIX, NULL); + led_current_kb4_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL7, MCE_LED_CURRENT_SUFFIX, NULL); + led_current_kb5_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL8, MCE_LED_CURRENT_SUFFIX, NULL); + + led_brightness_kb0_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_BRIGHTNESS_SUFFIX, NULL); + led_brightness_kb1_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL1, MCE_LED_BRIGHTNESS_SUFFIX, NULL); + led_brightness_kb2_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL2, MCE_LED_BRIGHTNESS_SUFFIX, NULL); + led_brightness_kb3_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL3, MCE_LED_BRIGHTNESS_SUFFIX, NULL); + led_brightness_kb4_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL7, MCE_LED_BRIGHTNESS_SUFFIX, NULL); + led_brightness_kb5_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL8, MCE_LED_BRIGHTNESS_SUFFIX, NULL); + + engine3_mode_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE3, MCE_LED_MODE_SUFFIX, NULL); + engine3_load_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE3, MCE_LED_LOAD_SUFFIX, NULL); + engine3_leds_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE3, MCE_LED_LEDS_SUFFIX, NULL); + break; + + case PRODUCT_RX48: + case PRODUCT_RX44: + /* Has backlight, but no special setup needed */ + led_brightness_kb0_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_COVER_PREFIX, MCE_LED_BRIGHTNESS_SUFFIX, NULL); + led_brightness_kb1_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_KEYBOARD_PREFIX, MCE_LED_BRIGHTNESS_SUFFIX, NULL); + break; + + default: + /* No keyboard available */ + break; + } +} + +/** + * Key backlight brightness for Lysti + * + * @param fadetime The fade time + * @param brightness Backlight brightness + */ +static void set_lysti_backlight_brightness(guint fadetime, guint brightness) +{ + /* remux|bright| fade | stop + * xxxx xx xxxx */ + static gchar pattern[] = "9d80" "4000" "0000" "c000"; + static gchar convert[] = "0123456789abcdef"; + static gint old_brightness = 0; + + /* If we're fading towards 0 and receive a new brightness, + * without the backlight timeout being set, the ALS has + * adjusted the brightness; just ignore the request + */ + if ((old_brightness == 0) && (key_backlight_timeout_cb_id == 0)) + goto EXIT; + + /* Calculate fade time; if fade time is 0, set immediately */ + if (fadetime == 0) { + /* No fade */ + pattern[6] = convert[(brightness & 0xf0) >> 4]; + pattern[7] = convert[brightness & 0xf]; + pattern[8] = '0'; + pattern[9] = '0'; + pattern[10] = '0'; + pattern[11] = '0'; + } else { + gint steps = (gint)brightness - old_brightness; + gint stepspeed; + + /* Figure out how big steps we need to take when + * fading (brightness - old_brightness) steps + * + * During calculations the fade time is multiplied by 1000 + * to avoid losing precision + * + * Every step is 0.49ms big + */ + stepspeed = (((fadetime * 1000) / ABS(steps)) / 0.49) / 1000; + + /* Sanity check the stepspeed */ + if (stepspeed < 1) + stepspeed = 1; + else if (stepspeed > 31) + stepspeed = 31; + + /* Even for increment, odd for decrement */ + stepspeed *= 2; + stepspeed += steps > 0 ? 0 : 1; + + /* Start from current brightness */ + pattern[6] = convert[(old_brightness & 0xf0) >> 4]; + pattern[7] = convert[old_brightness & 0xf]; + + /* Program the step speed */ + pattern[8] = convert[(stepspeed & 0xf0) >> 4]; + pattern[9] = convert[stepspeed & 0xf]; + + /* Program the number of steps */ + pattern[10] = convert[(ABS(steps) & 0xf0) >> 4]; + pattern[11] = convert[ABS(steps) & 0x0f]; + } + + /* Store the new brightness as the current one */ + old_brightness = brightness; + + /* Disable engine 3 */ + (void)mce_write_string_to_file(engine3_mode_path, + MCE_LED_DISABLED_MODE); + + /* Turn off all keyboard backlight LEDs */ + (void)mce_write_number_string_to_file(led_brightness_kb0_path, 0, &led_brightness_kb0_fp, TRUE, FALSE); + (void)mce_write_number_string_to_file(led_brightness_kb1_path, 0, &led_brightness_kb1_fp, TRUE, FALSE); + (void)mce_write_number_string_to_file(led_brightness_kb2_path, 0, &led_brightness_kb2_fp, TRUE, FALSE); + (void)mce_write_number_string_to_file(led_brightness_kb3_path, 0, &led_brightness_kb3_fp, TRUE, FALSE); + (void)mce_write_number_string_to_file(led_brightness_kb4_path, 0, &led_brightness_kb4_fp, TRUE, FALSE); + (void)mce_write_number_string_to_file(led_brightness_kb5_path, 0, &led_brightness_kb5_fp, TRUE, FALSE); + + /* Set backlight LED current */ + (void)mce_write_number_string_to_file(led_current_kb0_path, MAXIMUM_LYSTI_BACKLIGHT_LED_CURRENT, &led_current_kb0_fp, TRUE, FALSE); + (void)mce_write_number_string_to_file(led_current_kb1_path, MAXIMUM_LYSTI_BACKLIGHT_LED_CURRENT, &led_current_kb1_fp, TRUE, FALSE); + (void)mce_write_number_string_to_file(led_current_kb2_path, MAXIMUM_LYSTI_BACKLIGHT_LED_CURRENT, &led_current_kb2_fp, TRUE, FALSE); + (void)mce_write_number_string_to_file(led_current_kb3_path, MAXIMUM_LYSTI_BACKLIGHT_LED_CURRENT, &led_current_kb3_fp, TRUE, FALSE); + (void)mce_write_number_string_to_file(led_current_kb4_path, MAXIMUM_LYSTI_BACKLIGHT_LED_CURRENT, &led_current_kb4_fp, TRUE, FALSE); + (void)mce_write_number_string_to_file(led_current_kb5_path, MAXIMUM_LYSTI_BACKLIGHT_LED_CURRENT, &led_current_kb5_fp, TRUE, FALSE); + + /* Engine 3 */ + (void)mce_write_string_to_file(engine3_mode_path, + MCE_LED_LOAD_MODE); + + (void)mce_write_string_to_file(engine3_leds_path, + bin_to_string(key_backlight_mask)); + (void)mce_write_string_to_file(engine3_load_path, + pattern); + (void)mce_write_string_to_file(engine3_mode_path, + MCE_LED_RUN_MODE); + +EXIT: + return; +} + +/** + * Key backlight brightness for N810/N810 WiMAX Edition + * + * @param fadetime The fade time + * @param brightness Backlight brightness + */ +static void set_n810_backlight_brightness(guint fadetime, guint brightness) +{ + /* Set fade time */ + if (brightness == 0) { + (void)mce_write_number_string_to_file(MCE_KEYPAD_BACKLIGHT_FADETIME_SYS_PATH, fadetime, &n810_keypad_fadetime_fp, TRUE, FALSE); + (void)mce_write_number_string_to_file(MCE_KEYBOARD_BACKLIGHT_FADETIME_SYS_PATH, fadetime, &n810_keyboard_fadetime_fp, TRUE, FALSE); + } else { + (void)mce_write_number_string_to_file(MCE_KEYPAD_BACKLIGHT_FADETIME_SYS_PATH, 0, &n810_keypad_fadetime_fp, TRUE, FALSE); + (void)mce_write_number_string_to_file(MCE_KEYBOARD_BACKLIGHT_FADETIME_SYS_PATH, 0, &n810_keyboard_fadetime_fp, TRUE, FALSE); + } + + (void)mce_write_number_string_to_file(led_brightness_kb0_path, brightness, &led_brightness_kb0_fp, TRUE, FALSE); + (void)mce_write_number_string_to_file(led_brightness_kb1_path, brightness, &led_brightness_kb1_fp, TRUE, FALSE); +} + +/** + * Set key backlight brightness + * + * @param data Backlight brightness passed as a gconstpointer + */ +static void set_backlight_brightness(gconstpointer data) +{ + static gint cached_brightness = -1; + gint new_brightness = GPOINTER_TO_INT(data); + gint fadetime; + + if (new_brightness == 0) { + fadetime = key_backlight_fade_out_time; + } else { + fadetime = key_backlight_fade_in_time; + } + + /* If we're just rehashing the same brightness value, don't bother */ + if ((new_brightness == cached_brightness) || (new_brightness == -1)) + goto EXIT; + + cached_brightness = new_brightness; + + key_backlight_is_enabled = (new_brightness != 0); + + /* Product specific key backlight handling */ + switch (get_product_id()) { + case PRODUCT_RM690: + case PRODUCT_RM680: + case PRODUCT_RX51: + set_lysti_backlight_brightness(fadetime, new_brightness); + break; + + case PRODUCT_RX48: + case PRODUCT_RX44: + set_n810_backlight_brightness(fadetime, new_brightness); + break; + + default: + break; + } + +EXIT: + return; +} + +/** + * Disable key backlight + */ +static void disable_key_backlight(void) +{ + cancel_key_backlight_timeout(); + + execute_datapipe(&key_backlight_pipe, GINT_TO_POINTER(0), + USE_INDATA, CACHE_INDATA); +} + +/** + * Timeout callback for key backlight + * + * @param data Unused + * @return Always returns FALSE, to disable the timeout + */ +static gboolean key_backlight_timeout_cb(gpointer data) +{ + (void)data; + + key_backlight_timeout_cb_id = 0; + + disable_key_backlight(); + + return FALSE; +} + +/** + * Cancel key backlight timeout + */ +static void cancel_key_backlight_timeout(void) +{ + if (key_backlight_timeout_cb_id != 0) { + g_source_remove(key_backlight_timeout_cb_id); + key_backlight_timeout_cb_id = 0; + } +} + +/** + * Setup key backlight timeout + */ +static void setup_key_backlight_timeout(void) +{ + cancel_key_backlight_timeout(); + + /* Setup a new timeout */ + key_backlight_timeout_cb_id = + g_timeout_add_seconds(key_backlight_timeout, + key_backlight_timeout_cb, NULL); +} + +/** + * Enable key backlight + */ +static void enable_key_backlight(void) +{ + cancel_key_backlight_timeout(); + + /* Only enable the key backlight if the slide is open */ + if (datapipe_get_gint(keyboard_slide_pipe) != COVER_OPEN) + goto EXIT; + + setup_key_backlight_timeout(); + + /* If the backlight is off, turn it on */ + if (datapipe_get_guint(key_backlight_pipe) == 0) { + execute_datapipe(&key_backlight_pipe, + GINT_TO_POINTER(DEFAULT_KEY_BACKLIGHT_LEVEL), + USE_INDATA, CACHE_INDATA); + } + +EXIT: + return; +} + +/** + * Policy based enabling of key backlight + */ +static void enable_key_backlight_policy(void) +{ + cover_state_t kbd_slide_state = datapipe_get_gint(keyboard_slide_pipe); + system_state_t system_state = datapipe_get_gint(system_state_pipe); + alarm_ui_state_t alarm_ui_state = + datapipe_get_gint(alarm_ui_state_pipe); + + /* If the keyboard slide isn't open, there's no point in enabling + * the backlight + * + * XXX: this policy will have to change if/when we get devices + * with external keypads that needs to be backlit, but for now + * that's not an issue + */ + if (kbd_slide_state != COVER_OPEN) + goto EXIT; + + /* Only enable the key backlight in USER state + * and when the alarm dialog is visible + */ + if ((system_state == MCE_STATE_USER) || + ((alarm_ui_state == MCE_ALARM_UI_VISIBLE_INT32) || + (alarm_ui_state == MCE_ALARM_UI_RINGING_INT32))) { + /* If there's a key backlight timeout active, restart it, + * else enable the backlight + */ + if (key_backlight_timeout_cb_id != 0) + setup_key_backlight_timeout(); + else + enable_key_backlight(); + } + +EXIT: + return; +} + +/** + * Send a key backlight state reply + * + * @param method_call A DBusMessage to reply to + * @return TRUE on success, FALSE on failure + */ +static gboolean send_key_backlight_state(DBusMessage *const method_call) +{ + DBusMessage *msg = NULL; + dbus_bool_t state = key_backlight_is_enabled; + gboolean status = FALSE; + + mce_log(LL_DEBUG, + "Sending key backlight state: %d", + state); + + msg = dbus_new_method_reply(method_call); + + /* Append the display status */ + if (dbus_message_append_args(msg, + DBUS_TYPE_BOOLEAN, &state, + DBUS_TYPE_INVALID) == FALSE) { + mce_log(LL_CRIT, + "Failed to append reply argument to D-Bus message " + "for %s.%s", + MCE_REQUEST_IF, MCE_KEY_BACKLIGHT_STATE_GET); + dbus_message_unref(msg); + goto EXIT; + } + + /* Send the message */ + status = dbus_send_message(msg); + +EXIT: + return status; +} + +/** + * D-Bus callback for the get key backlight state method call + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean key_backlight_state_get_dbus_cb(DBusMessage *const msg) +{ + gboolean status = FALSE; + + mce_log(LL_DEBUG, + "Received key backlight state get request"); + + /* Try to send a reply that contains the current key backlight state */ + if (send_key_backlight_state(msg) == FALSE) + goto EXIT; + + status = TRUE; + +EXIT: + return status; +} + +/** + * Datapipe trigger for device inactivity + * + * @param data The inactivity stored in a pointer; + * TRUE if the device is inactive, + * FALSE if the device is active + */ +static void device_inactive_trigger(gconstpointer const data) +{ + gboolean device_inactive = GPOINTER_TO_INT(data); + + if (device_inactive == FALSE) + enable_key_backlight_policy(); +} + +/** + * Datapipe trigger for the keyboard slide + * + * @param data The keyboard slide state stored in a pointer; + * COVER_OPEN if the keyboard is open, + * COVER_CLOSED if the keyboard is closed + */ +static void keyboard_slide_trigger(gconstpointer const data) +{ + if ((GPOINTER_TO_INT(data) == COVER_OPEN) && + ((mce_get_submode_int32() & MCE_TKLOCK_SUBMODE) == 0)) { + enable_key_backlight_policy(); + } else { + disable_key_backlight(); + } +} + +/** + * Datapipe trigger for display state + * + * @param data The display stated stored in a pointer + */ +static void display_state_trigger(gconstpointer data) +{ + static display_state_t old_display_state = MCE_DISPLAY_UNDEF; + display_state_t display_state = GPOINTER_TO_INT(data); + + if (old_display_state == display_state) + goto EXIT; + + /* Disable the key backlight if the display dims */ + switch (display_state) { + case MCE_DISPLAY_OFF: + case MCE_DISPLAY_DIM: + disable_key_backlight(); + break; + + case MCE_DISPLAY_ON: + if (old_display_state == MCE_DISPLAY_OFF) + enable_key_backlight_policy(); + + break; + + case MCE_DISPLAY_UNDEF: + default: + break; + } + + old_display_state = display_state; + +EXIT: + return; +} + +/** + * Handle system state change + * + * @param data The system state stored in a pointer + */ +static void system_state_trigger(gconstpointer data) +{ + system_state_t system_state = GPOINTER_TO_INT(data); + + /* If we're changing to another state than USER, + * disable the key backlight + */ + if (system_state != MCE_STATE_USER) + disable_key_backlight(); +} + +/** + * Init function for the keypad module + * + * @todo XXX status needs to be set on error! + * + * @param module Unused + * @return NULL on success, a string with an error message on failure + */ +G_MODULE_EXPORT const gchar *g_module_check_init(GModule *module); +const gchar *g_module_check_init(GModule *module) +{ + gchar *status = NULL; + + (void)module; + + /* Append triggers/filters to datapipes */ + append_output_trigger_to_datapipe(&system_state_pipe, + system_state_trigger); + append_output_trigger_to_datapipe(&key_backlight_pipe, + set_backlight_brightness); + append_output_trigger_to_datapipe(&device_inactive_pipe, + device_inactive_trigger); + append_output_trigger_to_datapipe(&keyboard_slide_pipe, + keyboard_slide_trigger); + append_output_trigger_to_datapipe(&display_state_pipe, + display_state_trigger); + + /* Get configuration options */ + key_backlight_timeout = + mce_conf_get_int(MCE_CONF_KEYPAD_GROUP, + MCE_CONF_KEY_BACKLIGHT_TIMEOUT, + DEFAULT_KEY_BACKLIGHT_TIMEOUT, + NULL); + + key_backlight_fade_in_time = + mce_conf_get_int(MCE_CONF_KEYPAD_GROUP, + MCE_CONF_KEY_BACKLIGHT_FADE_IN_TIME, + DEFAULT_KEY_BACKLIGHT_FADE_IN_TIME, + NULL); + + if (((key_backlight_fade_in_time % 125) != 0) && + (key_backlight_fade_in_time > 1000)) + key_backlight_fade_in_time = + DEFAULT_KEY_BACKLIGHT_FADE_IN_TIME; + + key_backlight_fade_out_time = + mce_conf_get_int(MCE_CONF_KEYPAD_GROUP, + MCE_CONF_KEY_BACKLIGHT_FADE_OUT_TIME, + DEFAULT_KEY_BACKLIGHT_FADE_OUT_TIME, + NULL); + + if (((key_backlight_fade_out_time % 125) != 0) && + (key_backlight_fade_out_time > 1000)) + key_backlight_fade_out_time = + DEFAULT_KEY_BACKLIGHT_FADE_OUT_TIME; + + /* get_key_backlight_state */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_KEY_BACKLIGHT_STATE_GET, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + key_backlight_state_get_dbus_cb) == NULL) + goto EXIT; + + setup_key_backlight(); + +EXIT: + return status; +} + +/** + * Exit function for the keypad module + * + * @param module Unused + */ +G_MODULE_EXPORT void g_module_unload(GModule *module); +void g_module_unload(GModule *module) +{ + (void)module; + + /* Close files */ + mce_close_file(led_current_kb0_path, &led_current_kb0_fp); + mce_close_file(led_current_kb1_path, &led_current_kb1_fp); + mce_close_file(led_current_kb2_path, &led_current_kb2_fp); + mce_close_file(led_current_kb3_path, &led_current_kb3_fp); + mce_close_file(led_current_kb4_path, &led_current_kb4_fp); + mce_close_file(led_current_kb5_path, &led_current_kb5_fp); + + mce_close_file(led_brightness_kb0_path, &led_brightness_kb0_fp); + mce_close_file(led_brightness_kb1_path, &led_brightness_kb1_fp); + mce_close_file(led_brightness_kb2_path, &led_brightness_kb2_fp); + mce_close_file(led_brightness_kb3_path, &led_brightness_kb3_fp); + mce_close_file(led_brightness_kb4_path, &led_brightness_kb4_fp); + mce_close_file(led_brightness_kb5_path, &led_brightness_kb5_fp); + + mce_close_file(MCE_KEYPAD_BACKLIGHT_FADETIME_SYS_PATH, + &n810_keypad_fadetime_fp); + mce_close_file(MCE_KEYBOARD_BACKLIGHT_FADETIME_SYS_PATH, + &n810_keyboard_fadetime_fp); + + /* Free path strings */ + g_free(led_current_kb0_path); + g_free(led_current_kb1_path); + g_free(led_current_kb2_path); + g_free(led_current_kb3_path); + g_free(led_current_kb4_path); + g_free(led_current_kb5_path); + + g_free(led_brightness_kb0_path); + g_free(led_brightness_kb1_path); + g_free(led_brightness_kb2_path); + g_free(led_brightness_kb3_path); + g_free(led_brightness_kb4_path); + g_free(led_brightness_kb5_path); + + g_free(engine3_mode_path); + g_free(engine3_load_path); + g_free(engine3_leds_path); + + /* Remove triggers/filters from datapipes */ + remove_output_trigger_from_datapipe(&display_state_pipe, + display_state_trigger); + remove_output_trigger_from_datapipe(&keyboard_slide_pipe, + keyboard_slide_trigger); + remove_output_trigger_from_datapipe(&device_inactive_pipe, + device_inactive_trigger); + remove_output_trigger_from_datapipe(&key_backlight_pipe, + set_backlight_brightness); + remove_output_trigger_from_datapipe(&system_state_pipe, + system_state_trigger); + + /* Remove all timer sources */ + cancel_key_backlight_timeout(); + + return; +} diff --git a/modules/keypad.h b/modules/keypad.h new file mode 100644 index 00000000..d290ed93 --- /dev/null +++ b/modules/keypad.h @@ -0,0 +1,66 @@ +/** + * @file keypad.h + * Headers for the keypad module + *

+ * Copyright © 2004-2009 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _KEYPAD_H_ +#define _KEYPAD_H_ + +#include + +/* XXX: Ewwww, this is soooo ugly */ +#include "led.h" + +/** Path to keypad backlight fade-time /sys entry */ +#define MCE_KEYPAD_BACKLIGHT_FADETIME_SYS_PATH MCE_LED_DIRECT_SYS_PATH MCE_LED_COVER_PREFIX "/time" + +/** Path to keyboard backlight /sys directory */ +#define MCE_KEYBOARD_BACKLIGHT_SYS_PATH "/sys/class/leds/keyboard" +/** Path to the SysFS interface for the keyboard backlight fade-time */ +#define MCE_KEYBOARD_BACKLIGHT_FADETIME_SYS_PATH MCE_LED_DIRECT_SYS_PATH MCE_LED_KEYBOARD_PREFIX "/time" + +/** Maximum Lysti backlight LED current */ +#define MAXIMUM_LYSTI_BACKLIGHT_LED_CURRENT 50 /* 5 mA */ + +/** Default key backlight brightness */ +#define DEFAULT_KEY_BACKLIGHT_LEVEL 255 + +/** Default key backlight timeout in seconds */ +#define DEFAULT_KEY_BACKLIGHT_TIMEOUT 30 /* 30 s */ + +/** Default key backlight fade in time in milliseconds */ +#define DEFAULT_KEY_BACKLIGHT_FADE_IN_TIME 250 /* 250 ms */ + +/** Default key backlight fade out time in milliseconds */ +#define DEFAULT_KEY_BACKLIGHT_FADE_OUT_TIME 1000 /* 1000 ms */ + +#ifndef MCE_CONF_KEYPAD_GROUP +/** Name of Keypad configuration group */ +#define MCE_CONF_KEYPAD_GROUP "KeyPad" +#endif /* MCE_CONF_KEYPAD_GROUP */ + +/** Name of configuration key for keyboard backlight timeout */ +#define MCE_CONF_KEY_BACKLIGHT_TIMEOUT "BacklightTimeout" + +/** Name of configuration key for keyboard backlight fade in time */ +#define MCE_CONF_KEY_BACKLIGHT_FADE_IN_TIME "BacklightFadeInTime" + +/** Name of configuration key for keyboard backlight fade out time */ +#define MCE_CONF_KEY_BACKLIGHT_FADE_OUT_TIME "BacklightFadeOutTime" + +#endif /* _KEYPAD_H_ */ diff --git a/modules/led.c b/modules/led.c new file mode 100644 index 00000000..29804e38 --- /dev/null +++ b/modules/led.c @@ -0,0 +1,2233 @@ +/** + * @file led.c + * LED module -- this handles the LED logic for MCE + *

+ * Copyright © 2006-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include +#include +#include /* g_access */ + +#include /* errno, EINVAL, ERANGE */ +#include /* open(), O_RDWR, O_CREAT */ +#include /* strtoul() */ +#include /* strcmp(), strcpy(), strdup() */ +#include /* close(), W_OK */ +#include /* ioctl() */ +#include /* i2c_smbus_write_byte_data(), + * I2C_SLAVE_FORCE + */ + +#include "mce.h" +#include "led.h" + +#include "mce-io.h" /* mce_close_file(), + * mce_write_string_to_file(), + * mce_write_number_string_to_file() + */ +#include "mce-hal.h" /* get_product_id(), + * product_id_t + */ +#include "mce-lib.h" /* bin_to_string() */ +#include "mce-log.h" /* mce_log(), LL_* */ +#include "mce-conf.h" /* mce_conf_get_string_list() */ +#include "mce-dbus.h" /* Direct: + * --- + * mce_dbus_handler_add(), + * dbus_send_message(), + * dbus_new_method_reply(), + * dbus_message_get_no_reply(), + * dbus_message_get_args(), + * dbus_error_init(), + * dbus_error_free(), + * DBUS_MESSAGE_TYPE_METHOD_CALL, + * DBUS_TYPE_STRING, + * DBUS_TYPE_INVALID, + * DBusMessage, DBusError, + * dbus_bool_t + * + * Indirect: + * --- + * MCE_REQUEST_IF, + * MCE_ACTIVATE_LED_PATTERN, + * MCE_DEACTIVATE_LED_PATTERN, + * MCE_ENABLE_LED, + * MCE_DISABLE_LED + */ +#include "mce-gconf.h" /* mce_gconf_notifier_add(), + * mce_gconf_notifier_remove(), + * mce_gconf_get_bool(), + * gconf_entry_get_key(), + * gconf_entry_get_value(), + * gconf_value_get_bool(), + * gconf_concat_dir_and_key(), + * GConfClient, GConfEntry, GConfValue + */ +#include "datapipe.h" /* execute_datapipe(), + * datapipe_get_gint(), + * append_output_trigger_to_datapipe(), + * remove_output_trigger_from_datapipe() + */ + +/** Module name */ +#define MODULE_NAME "led" + +/** Functionality provided by this module */ +static const gchar *const provides[] = { MODULE_NAME, NULL }; + +/** Module information */ +G_MODULE_EXPORT module_info_struct module_info = { + /** Name of the module */ + .name = MODULE_NAME, + /** Module provides */ + .provides = provides, + /** Module priority */ + .priority = 100 +}; + +/** The pattern queue */ +static GQueue *pattern_stack = NULL; +/** The pattern combination rule queue */ +static GQueue *combination_rule_list = NULL; +/** The pattern combination rule queue */ +static GQueue *combination_rule_xref_list = NULL; +/** The D-Bus controlled LED switch */ +static gboolean led_enabled = FALSE; + +/** Fields in the patterns */ +typedef enum { + /** Pattern priority field */ + PATTERN_PRIO_FIELD = 0, + /** Pattern screen display policy field */ + PATTERN_SCREEN_ON_FIELD = 1, + /** Pattern timeout field */ + PATTERN_TIMEOUT_FIELD = 2, + /** On-period field for direct-controlled monochrome patterns */ + PATTERN_ON_PERIOD_FIELD = 3, + /** R-channel pattern field for NJoy-controlled RGB patterns */ + PATTERN_R_CHANNEL_FIELD = 3, + /** LED-muxing field for Lysti-controlled RGB patterns */ + PATTERN_MUXING_FIELD = 3, + /** + * Engine channel field for Lysti-controlled monochrome patterns + * and NJoy-controlled monochrome patterns + */ + PATTERN_E_CHANNEL_FIELD = 3, + /** Number of fields used by Lysti-controlled monochrome patterns */ + NUMBER_OF_PATTERN_FIELDS_LYSTI_MONO = 4, + /** Number of fields used by NJoy-controlled monochrome patterns */ + NUMBER_OF_PATTERN_FIELDS_NJOY_MONO = 4, + /** Off-period field for direct-controlled monochrome patterns */ + PATTERN_OFF_PERIOD_FIELD = 4, + /** G-channel pattern field for NJoy-controlled RGB patterns */ + PATTERN_G_CHANNEL_FIELD = 4, + /** Engine channel 1 field for Lysti-controlled RGB patterns */ + PATTERN_E1_CHANNEL_FIELD = 4, + /** Pattern brightness field for direct-controlled monochrome patterns */ + PATTERN_BRIGHTNESS_FIELD = 5, + /** B-channel pattern field for NJoy-controlled RGB patterns */ + PATTERN_B_CHANNEL_FIELD = 5, + /** Engine channel 2 field for Lysti-controlled RGB patterns */ + PATTERN_E2_CHANNEL_FIELD = 5, + /** + * Number of fields used by Lysti-controlled RGB patterns, + * NJoy-controlled RGB patterns, + * and monochrome direct-controlled patterns + */ + NUMBER_OF_PATTERN_FIELDS = 6 +} pattern_field; + +/** + * Size of each LED channel + * + * Multiply the channel size by 2 since we store hexadecimal ASCII + */ +#define CHANNEL_SIZE 32 * 2 + +/** Structure holding LED patterns */ +typedef struct { + gchar *name; /**< Pattern name */ + gint priority; /**< Pattern priority */ + gint policy; /**< Show pattern when screen is on? */ + gint timeout; /**< Timeout in seconds */ + gint on_period; /**< Pattern on-period in ms */ + gint off_period; /**< Pattern off-period in ms */ + gint brightness; /**< Pattern brightness */ + gboolean active; /**< Is the pattern active? */ + gboolean enabled; /**< Is the pattern enabled? */ + guint engine1_mux; /**< Muxing for engine 1 */ + guint engine2_mux; /**< Muxing for engine 2 */ + /** Pattern for the R-channel/engine 1 */ + gchar channel1[CHANNEL_SIZE + 1]; + /** Pattern for the G-channel/engine 2 */ + gchar channel2[CHANNEL_SIZE + 1]; + /** Pattern for the B-channel */ + gchar channel3[CHANNEL_SIZE + 1]; + guint gconf_cb_id; /**< Callback ID for GConf entry */ +} pattern_struct; + +/** Pattern combination rule struct; this is also used for cross-referencing */ +typedef struct { + /** Name of the combined pattern */ + gchar *rulename; + /** List of pre-requisite patterns */ + GQueue *pre_requisites; +} combination_rule_struct; + +/** Pointer to the top pattern */ +static pattern_struct *active_pattern = NULL; +/** The active brightness */ +static gint active_brightness = -1; + +/** Currently driven leds */ +static guint current_lysti_led_pattern = 0; + +/** Brightness levels for the mono-LED */ +static const gchar *const brightness_map[] = { + BRIGHTNESS_LEVEL_0, + BRIGHTNESS_LEVEL_1, + BRIGHTNESS_LEVEL_2, + BRIGHTNESS_LEVEL_3, + BRIGHTNESS_LEVEL_4, + BRIGHTNESS_LEVEL_5, + BRIGHTNESS_LEVEL_6, + BRIGHTNESS_LEVEL_7, + BRIGHTNESS_LEVEL_8, + BRIGHTNESS_LEVEL_9, + BRIGHTNESS_LEVEL_10, + BRIGHTNESS_LEVEL_11, + BRIGHTNESS_LEVEL_12, + BRIGHTNESS_LEVEL_13, + BRIGHTNESS_LEVEL_14, + BRIGHTNESS_LEVEL_15 +}; + +/** LED type */ +typedef enum { + /** LED type unset */ + LED_TYPE_UNSET = -1, + /** No LED available */ + LED_TYPE_NONE = 0, + /** Monochrome LED, direct LED control */ + LED_TYPE_DIRECT_MONO = 1, + /** RGB LED, NJoy (LP5521) LED controller */ + LED_TYPE_NJOY_RGB = 2, + /** Monochrome LED, NJoy (LP5521) LED controller */ + LED_TYPE_NJOY_MONO = 3, + /** RGB LED, Lysti (LP5523) LED controller */ + LED_TYPE_LYSTI_RGB = 4, + /** Monochrome LED, Lysti (LP5523) LED controller */ + LED_TYPE_LYSTI_MONO = 5, +} led_type_t; + +/** + * The ID of the LED timer + */ +static guint led_pattern_timeout_cb_id = 0; + +/** + * The configuration group containing the LED pattern + */ +static const gchar *led_pattern_group = NULL; + +/** Path to monochrome/red channel LED current path */ +static gchar *led_current_rm_path = NULL; +/** Path to green channel LED current path */ +static gchar *led_current_g_path = NULL; +/** Path to blue channel LED current path */ +static gchar *led_current_b_path = NULL; + +/** Path to monochrome/red channel LED brightness path */ +static gchar *led_brightness_rm_path = NULL; +/** Path to red channel LED brightness path */ +static gchar *led_brightness_g_path = NULL; +/** Path to blue channel LED brightness path */ +static gchar *led_brightness_b_path = NULL; + +/** Path to engine 1 mode */ +static gchar *engine1_mode_path = NULL; +/** Path to engine 2 mode */ +static gchar *engine2_mode_path = NULL; +/** Path to engine 3 mode */ +static gchar *engine3_mode_path = NULL; + +/** Path to engine 1 load */ +static gchar *engine1_load_path = NULL; +/** Path to engine 2 load */ +static gchar *engine2_load_path = NULL; +/** Path to engine 3 load */ +static gchar *engine3_load_path = NULL; + +/** Path to engine 1 leds */ +static gchar *engine1_leds_path = NULL; +/** Path to engine 2 leds */ +static gchar *engine2_leds_path = NULL; +/** Path to engine 3 leds */ +static gchar *engine3_leds_path = NULL; + +/** File pointer for the monochrome/red channel LED current */ +static FILE *led_current_rm_fp = NULL; +/** File pointer for the green channel LED current */ +static FILE *led_current_g_fp = NULL; +/** File pointer for the blue channel LED current */ +static FILE *led_current_b_fp = NULL; + +/** File pointer for the monochrome/red channel LED brightness */ +static FILE *led_brightness_rm_fp = NULL; +/** File pointer for the green channel LED brightness */ +static FILE *led_brightness_g_fp = NULL; +/** File pointer for the blue channel LED brightness */ +static FILE *led_brightness_b_fp = NULL; + +/** Maximum LED brightness */ +static guint maximum_led_brightness = MAXIMUM_LYSTI_MONOCHROME_LED_CURRENT; + +static void cancel_pattern_timeout(void); +static void led_update_active_pattern(void); + +/** + * Disable the Reno LED controller + */ +static void disable_reno(void) +{ + int fd; + + mce_log(LL_DEBUG, "Disabling Reno"); + + if ((fd = open("/dev/i2c-1", O_RDWR)) == -1) { + mce_log(LL_CRIT, "Failed to open /dev/i2c-1; %s", + g_strerror(errno)); + + /* Reset errno, + * to avoid false positives down the line + */ + errno = 0; + goto EXIT; + } + + if (ioctl(fd, I2C_SLAVE_FORCE, TWL5031_BCC) == -1) { + mce_log(LL_CRIT, + "ioctl() I2C_SLAVE_FORCE (%d) failed on `%s'; %s", + TWL5031_BCC, "/dev/i2c-1", g_strerror(errno)); + + /* Reset errno, + * to avoid false positives down the line + */ + errno = 0; + goto EXIT; + } + + if (i2c_smbus_write_byte_data(fd, LED_DRIVER_CTRL, LEDC_DISABLE) < 0) { + mce_log(LL_ERR, + "i2c_smbus_write_byte_data(TWL5031_BCC, ...) failed"); + } + +EXIT: + if (fd != -1) { + if (close(fd) == -1) { + mce_log(LL_ERR, + "Failed to close `%s': %s", + "/dev/i2c-1", g_strerror(errno)); + + /* Reset errno, + * to avoid false positives down the line + */ + errno = 0; + } + } + + return; +} + +/** + * Get the LED type + * + * @return The LED type + */ +static led_type_t get_led_type(void) +{ + product_id_t product_id = get_product_id(); + static led_type_t led_type = LED_TYPE_UNSET; + + /* If we have the LED type already, return it */ + if (led_type != LED_TYPE_UNSET) + goto EXIT; + + /* First build the paths needed to check */ + switch (product_id) { + case PRODUCT_RM716: + case PRODUCT_RM696: + led_type = LED_TYPE_NJOY_MONO; + led_pattern_group = MCE_CONF_LED_PATTERN_RM696_GROUP; + maximum_led_brightness = MAXIMUM_NJOY_MONOCHROME_LED_CURRENT; + + /* Build paths */ + led_current_rm_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5521_PREFIX, MCE_LED_CHANNEL0, MCE_LED_CURRENT_SUFFIX, NULL); + led_brightness_rm_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5521_PREFIX, MCE_LED_CHANNEL0, MCE_LED_BRIGHTNESS_SUFFIX, NULL); + + engine1_mode_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5521_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE1, MCE_LED_MODE_SUFFIX, NULL); + engine2_mode_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5521_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE2, MCE_LED_MODE_SUFFIX, NULL); + engine3_mode_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5521_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE3, MCE_LED_MODE_SUFFIX, NULL); + + /* We have 3 engines, but only 1 LED, + * so while we need to be able to set the mode of all + * engines (to disable the unused ones), we don't need + * to program them + */ + engine1_load_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5521_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE1, MCE_LED_LOAD_SUFFIX, NULL); + + disable_reno(); + break; + + case PRODUCT_RM690: + case PRODUCT_RM680: + led_type = LED_TYPE_LYSTI_MONO; + led_pattern_group = MCE_CONF_LED_PATTERN_RM680_GROUP; + maximum_led_brightness = MAXIMUM_LYSTI_MONOCHROME_LED_CURRENT; + + /* Build paths */ + led_current_rm_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL8, MCE_LED_CURRENT_SUFFIX, NULL); + led_brightness_rm_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL8, MCE_LED_BRIGHTNESS_SUFFIX, NULL); + + /* Engine 3 is used by keyboard backlight */ + engine1_mode_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE1, MCE_LED_MODE_SUFFIX, NULL); + engine2_mode_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE2, MCE_LED_MODE_SUFFIX, NULL); + + engine1_load_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE1, MCE_LED_LOAD_SUFFIX, NULL); + engine2_load_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE2, MCE_LED_LOAD_SUFFIX, NULL); + + engine1_leds_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE1, MCE_LED_LEDS_SUFFIX, NULL); + engine2_leds_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE2, MCE_LED_LEDS_SUFFIX, NULL); + + disable_reno(); + break; + + case PRODUCT_RX51: + led_type = LED_TYPE_LYSTI_RGB; + led_pattern_group = MCE_CONF_LED_PATTERN_RX51_GROUP; + maximum_led_brightness = MAXIMUM_LYSTI_RGB_LED_CURRENT; + + /* Build paths */ + led_current_rm_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_CURRENT_SUFFIX, NULL); + led_current_g_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL1, MCE_LED_CURRENT_SUFFIX, NULL); + led_current_b_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL2, MCE_LED_CURRENT_SUFFIX, NULL); + led_brightness_rm_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_BRIGHTNESS_SUFFIX, NULL); + led_brightness_g_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL1, MCE_LED_BRIGHTNESS_SUFFIX, NULL); + led_brightness_b_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL2, MCE_LED_BRIGHTNESS_SUFFIX, NULL); + + engine1_mode_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE1, MCE_LED_MODE_SUFFIX, NULL); + engine2_mode_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE2, MCE_LED_MODE_SUFFIX, NULL); + engine3_mode_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE3, MCE_LED_MODE_SUFFIX, NULL); + + engine1_load_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE1, MCE_LED_LOAD_SUFFIX, NULL); + engine2_load_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE2, MCE_LED_LOAD_SUFFIX, NULL); + engine3_load_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE3, MCE_LED_LOAD_SUFFIX, NULL); + + engine1_leds_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE1, MCE_LED_LEDS_SUFFIX, NULL); + engine2_leds_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE2, MCE_LED_LEDS_SUFFIX, NULL); + engine3_leds_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5523_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE3, MCE_LED_LEDS_SUFFIX, NULL); + break; + + case PRODUCT_RX44: + case PRODUCT_RX48: + led_type = LED_TYPE_NJOY_RGB; + maximum_led_brightness = MAXIMUM_NJOY_RGB_LED_CURRENT; + + if (product_id == PRODUCT_RX48) + led_pattern_group = MCE_CONF_LED_PATTERN_RX48_GROUP; + else + led_pattern_group = MCE_CONF_LED_PATTERN_RX44_GROUP; + + /* Build paths */ + led_current_rm_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5521_PREFIX, MCE_LED_CHANNEL0, MCE_LED_CURRENT_SUFFIX, NULL); + led_brightness_rm_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5521_PREFIX, MCE_LED_CHANNEL0, MCE_LED_BRIGHTNESS_SUFFIX, NULL); + + engine1_mode_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5521_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE1, MCE_LED_MODE_SUFFIX, NULL); + engine2_mode_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5521_PREFIX, MCE_LED_CHANNEL1, MCE_LED_DEVICE, MCE_LED_ENGINE2, MCE_LED_MODE_SUFFIX, NULL); + engine3_mode_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5521_PREFIX, MCE_LED_CHANNEL2, MCE_LED_DEVICE, MCE_LED_ENGINE3, MCE_LED_MODE_SUFFIX, NULL); + + engine1_load_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5521_PREFIX, MCE_LED_CHANNEL0, MCE_LED_DEVICE, MCE_LED_ENGINE1, MCE_LED_LOAD_SUFFIX, NULL); + engine2_load_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5521_PREFIX, MCE_LED_CHANNEL1, MCE_LED_DEVICE, MCE_LED_ENGINE2, MCE_LED_LOAD_SUFFIX, NULL); + engine3_load_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_LP5521_PREFIX, MCE_LED_CHANNEL2, MCE_LED_DEVICE, MCE_LED_ENGINE3, MCE_LED_LOAD_SUFFIX, NULL); + break; + + case PRODUCT_RX34: + led_type = LED_TYPE_DIRECT_MONO; + led_pattern_group = MCE_CONF_LED_PATTERN_RX34_GROUP; + + /* Build paths */ + led_brightness_rm_path = g_strconcat(MCE_LED_DIRECT_SYS_PATH, MCE_LED_KEYPAD_PREFIX, MCE_LED_BRIGHTNESS_SUFFIX, NULL); + break; + + default: + led_type = LED_TYPE_NONE; + break; + } + + mce_log(LL_DEBUG, "LED-type: %d", led_type); + +EXIT: + return led_type; +} + +/** + * Custom find function to get a particular entry in the pattern stack + * + * @param data The pattern_struct entry + * @param userdata The pattern name + * @return Less than, equal to, or greater than zero depending + * whether the name of the pattern struct pointed to by data + * is less than, equal to, or greater than entry2 + */ +static gint queue_find(gconstpointer data, gconstpointer userdata) G_GNUC_PURE; +static gint queue_find(gconstpointer data, gconstpointer userdata) +{ + pattern_struct *psp; + + if (data == NULL || userdata == NULL) + return -1; + + psp = (pattern_struct *)data; + + if (psp->name == NULL) + return -1; + + return strcmp(psp->name, (gchar *)userdata); +} + +/** + * Custom compare function used for priority insertions + * + * @param entry1 Queue entry 1 + * @param entry2 Queue entry 2 + * @param userdata The pattern name + * @return Less than, equal to, or greater than zero depending + * whether the priority of entry1 is less than, equal to, + * or greater than the priority of entry2 + */ +static gint queue_prio_compare(gconstpointer entry1, + gconstpointer entry2, + gpointer userdata) G_GNUC_PURE; +static gint queue_prio_compare(gconstpointer entry1, + gconstpointer entry2, + gpointer userdata) +{ + pattern_struct *psp1 = (pattern_struct *)entry1; + pattern_struct *psp2 = (pattern_struct *)entry2; + + (void)userdata; + + return psp1->priority - psp2->priority; +} + +/** + * Set Lysti-LED brightness + * + * @param brightness The brightness of the LED + * (0 - maximum_led_brightness), + * or -1 to adjust colour hues without changing brightness + */ +static void lysti_set_brightness(gint brightness) +{ + guint r_brightness = 0; + guint g_brightness = 0; + guint b_brightness = 0; + + if (brightness < -1 || brightness > (gint)maximum_led_brightness) { + mce_log(LL_WARN, "Invalid brightness value %d", brightness); + return; + } + + /* -1 is used to set proper colour hues + * without changing current brightness + */ + if (brightness != -1) { + if (active_brightness == brightness) + return; + + active_brightness = brightness; + } + + if ((current_lysti_led_pattern & MCE_LYSTI_RED_MASK_RX51) && + (get_led_type() == LED_TYPE_LYSTI_RGB)) { + /* Red is on, tweaking is needed */ + if ((current_lysti_led_pattern & MCE_LYSTI_GREEN_MASK_RX51) && + (current_lysti_led_pattern & MCE_LYSTI_BLUE_MASK_RX51)) { + /* White */ + r_brightness = (unsigned)active_brightness * 4; + r_brightness = (r_brightness < maximum_led_brightness) ? r_brightness : maximum_led_brightness; + g_brightness = r_brightness / 4; + b_brightness = r_brightness / 4; + } else if (current_lysti_led_pattern & MCE_LYSTI_GREEN_MASK_RX51) { + /* Orange */ + r_brightness = (unsigned)active_brightness * 10; + r_brightness = (r_brightness < maximum_led_brightness) ? r_brightness : maximum_led_brightness; + g_brightness = r_brightness / 10; + b_brightness = 0; + } else { + /* Purple */ + r_brightness = (unsigned)active_brightness * 4; + r_brightness = (r_brightness < maximum_led_brightness) ? r_brightness : maximum_led_brightness; + b_brightness = r_brightness / 4; + g_brightness = 0; + } + } else { + /* When red is not on, we use brightness as is */ + r_brightness = (unsigned)active_brightness; + g_brightness = (unsigned)active_brightness; + b_brightness = (unsigned)active_brightness; + } + + if (get_led_type() == LED_TYPE_LYSTI_MONO) { + /* If we have a monochrome LED only set one brightness */ + (void)mce_write_number_string_to_file(led_current_rm_path, r_brightness, &led_current_rm_fp, TRUE, FALSE); + + mce_log(LL_DEBUG, + "Brightness set to %d", + active_brightness); + } else if (get_led_type() == LED_TYPE_LYSTI_RGB) { + /* If we have an RGB LED set the brightness for all channels */ + (void)mce_write_number_string_to_file(led_current_rm_path, r_brightness, &led_current_rm_fp, TRUE, FALSE); + (void)mce_write_number_string_to_file(led_current_g_path, g_brightness, &led_current_g_fp, TRUE, FALSE); + (void)mce_write_number_string_to_file(led_current_b_path, b_brightness, &led_current_b_fp, TRUE, FALSE); + + mce_log(LL_DEBUG, + "Brightness set to %d (%d, %d, %d)", + active_brightness, r_brightness, + g_brightness, b_brightness); + } +} + +/** + * Set NJoy-LED brightness + * + * @param brightness The brightness of the LED (0-3) + */ +static void njoy_set_brightness(gint brightness) +{ + if (brightness < 0 || brightness > (gint)maximum_led_brightness) { + mce_log(LL_WARN, "Invalid brightness value %d", brightness); + return; + } + + if (active_brightness == brightness) + return; + + active_brightness = brightness; + (void)mce_write_number_string_to_file(led_brightness_rm_path, + (unsigned)brightness, &led_brightness_rm_fp, TRUE, FALSE); + + mce_log(LL_DEBUG, "Brightness set to %d", brightness); +} + +/** + * Set mono-LED brightness + * + * @param brightness The brightness of the LED (0-15) + */ +static void mono_set_brightness(gint brightness) +{ + if (brightness < 0 || brightness > 15) { + mce_log(LL_WARN, "Invalid brightness value %d", brightness); + return; + } + + if (active_brightness == brightness) + return; + + active_brightness = brightness; + (void)mce_write_string_to_file(led_brightness_rm_path, + brightness_map[brightness]); + + mce_log(LL_DEBUG, "Brightness set to %d", brightness); +} + +/** + * Disable the Lysti-LED + */ +static void lysti_disable_led(void) +{ + /* Disable engine 1 */ + (void)mce_write_string_to_file(engine1_mode_path, + MCE_LED_DISABLED_MODE); + + if (get_led_type() == LED_TYPE_LYSTI_MONO) { + /* Turn off the led */ + (void)mce_write_number_string_to_file(led_brightness_rm_path, 0, &led_brightness_rm_fp, TRUE, FALSE); + } else if (get_led_type() == LED_TYPE_LYSTI_RGB) { + /* Disable engine 2 */ + (void)mce_write_string_to_file(engine2_mode_path, + MCE_LED_DISABLED_MODE); + + /* Turn off all three leds */ + (void)mce_write_number_string_to_file(led_brightness_rm_path, 0, &led_brightness_rm_fp, TRUE, FALSE); + (void)mce_write_number_string_to_file(led_brightness_g_path, 0, &led_brightness_g_fp, TRUE, FALSE); + (void)mce_write_number_string_to_file(led_brightness_b_path, 0, &led_brightness_b_fp, TRUE, FALSE); + } +} + +/** + * Disable the NJoy-LED + */ +static void njoy_disable_led(void) +{ + /* Disable engine 1 */ + (void)mce_write_string_to_file(engine1_mode_path, + MCE_LED_DISABLED_MODE); + + if (get_led_type() == LED_TYPE_NJOY_MONO) { + /* Turn off the led */ + (void)mce_write_number_string_to_file(led_brightness_rm_path, 0, &led_brightness_rm_fp, TRUE, FALSE); + } else if (get_led_type() == LED_TYPE_NJOY_RGB) { + /* Disable engine 2 */ + (void)mce_write_string_to_file(engine2_mode_path, + MCE_LED_DISABLED_MODE); + + /* Disable engine 3 */ + (void)mce_write_string_to_file(engine3_mode_path, + MCE_LED_DISABLED_MODE); + + /* Turn off all three leds */ + (void)mce_write_number_string_to_file(led_brightness_rm_path, 0, &led_brightness_rm_fp, TRUE, FALSE); + (void)mce_write_number_string_to_file(led_brightness_g_path, 0, &led_brightness_g_fp, TRUE, FALSE); + (void)mce_write_number_string_to_file(led_brightness_b_path, 0, &led_brightness_b_fp, TRUE, FALSE); + } +} + +/** + * Disable the mono-LED + */ +static void mono_disable_led(void) +{ + (void)mce_write_string_to_file(MCE_LED_TRIGGER_PATH, + MCE_LED_TRIGGER_NONE); + mono_set_brightness(0); +} + +/** + * Disable the LED + */ +static void disable_led(void) +{ + cancel_pattern_timeout(); + + switch (get_led_type()) { + case LED_TYPE_LYSTI_RGB: + case LED_TYPE_LYSTI_MONO: + lysti_disable_led(); + break; + + case LED_TYPE_NJOY_RGB: + case LED_TYPE_NJOY_MONO: + njoy_disable_led(); + break; + + case LED_TYPE_DIRECT_MONO: + mono_disable_led(); + break; + + default: + break; + } +} + +/** + * Timeout callback for LED patterns + * + * @param data Unused + * @return Always returns FALSE to disable timeout + */ +static gboolean led_pattern_timeout_cb(gpointer data) +{ + (void)data; + + led_pattern_timeout_cb_id = 0; + + active_pattern->active = FALSE; + led_update_active_pattern(); + + return FALSE; +} + +/** + * Cancel pattern timeout + */ +static void cancel_pattern_timeout(void) +{ + /* Remove old timeout */ + if (led_pattern_timeout_cb_id != 0) { + g_source_remove(led_pattern_timeout_cb_id); + led_pattern_timeout_cb_id = 0; + } +} + +/** + * Setup pattern timeout + */ +static void setup_pattern_timeout(gint timeout) +{ + cancel_pattern_timeout(); + + /* Setup new timeout */ + led_pattern_timeout_cb_id = + g_timeout_add_seconds(timeout, led_pattern_timeout_cb, NULL); +} + +/** + * Setup and activate a new Lysti-LED pattern + * + * @param pattern A pointer to a pattern_struct with the new pattern + */ +static void lysti_program_led(const pattern_struct *const pattern) +{ + /* Disable old LED patterns */ + lysti_disable_led(); + + /* Load new patterns, one engine at a time */ + + /* Engine 1 */ + (void)mce_write_string_to_file(engine1_mode_path, + MCE_LED_LOAD_MODE); + (void)mce_write_string_to_file(engine1_leds_path, + bin_to_string(pattern->engine1_mux)); + (void)mce_write_string_to_file(engine1_load_path, + pattern->channel1); + + /* Engine 2; if needed */ + if (get_led_type() == LED_TYPE_LYSTI_RGB) { + (void)mce_write_string_to_file(engine2_mode_path, + MCE_LED_LOAD_MODE); + (void)mce_write_string_to_file(engine2_leds_path, + bin_to_string(pattern->engine2_mux)); + (void)mce_write_string_to_file(engine2_load_path, + pattern->channel2); + + /* Run the new pattern; enable engines in reverse order */ + (void)mce_write_string_to_file(engine2_mode_path, + MCE_LED_RUN_MODE); + } + + (void)mce_write_string_to_file(engine1_mode_path, + MCE_LED_RUN_MODE); + + /* Save what colors we are driving */ + current_lysti_led_pattern = pattern->engine1_mux | pattern->engine2_mux; + + /* Update color hue according what leds are driven */ + lysti_set_brightness(-1); +} + +/** + * Setup and activate a new NJoy-LED pattern + * + * @param pattern A pointer to a pattern_struct with the new pattern + */ +static void njoy_program_led(const pattern_struct *const pattern) +{ + /* Disable old LED patterns */ + njoy_disable_led(); + + /* Load new patterns */ + + /* Engine 1 */ + (void)mce_write_string_to_file(engine1_mode_path, + MCE_LED_LOAD_MODE); + (void)mce_write_string_to_file(engine1_load_path, + pattern->channel1); + + if (get_led_type() == LED_TYPE_NJOY_RGB) { + /* Engine 2 */ + (void)mce_write_string_to_file(engine2_mode_path, + MCE_LED_LOAD_MODE); + (void)mce_write_string_to_file(engine2_load_path, + pattern->channel2); + + /* Engine 3 */ + (void)mce_write_string_to_file(engine3_mode_path, + MCE_LED_LOAD_MODE); + (void)mce_write_string_to_file(engine3_load_path, + pattern->channel3); + + /* Run the new pattern; enable engines in reverse order */ + (void)mce_write_string_to_file(engine3_mode_path, + MCE_LED_RUN_MODE); + (void)mce_write_string_to_file(engine2_mode_path, + MCE_LED_RUN_MODE); + } + + (void)mce_write_string_to_file(engine1_mode_path, + MCE_LED_RUN_MODE); +} + +/** + * Setup and activate a new mono-LED pattern + * + * @param pattern A pointer to a pattern_struct with the new pattern + */ +static void mono_program_led(const pattern_struct *const pattern) +{ + /* This shouldn't happen; disable the LED instead */ + if (pattern->on_period == 0) { + mono_disable_led(); + goto EXIT; + } + + /* If we have a normal, on/off pattern, + * use a timer trigger, otherwise disable the trigger + */ + if (pattern->off_period != 0) { + (void)mce_write_string_to_file(MCE_LED_TRIGGER_PATH, + MCE_LED_TRIGGER_TIMER); + (void)mce_write_number_string_to_file(MCE_LED_OFF_PERIOD_PATH, + (unsigned)pattern->off_period, NULL, TRUE, TRUE); + (void)mce_write_number_string_to_file(MCE_LED_ON_PERIOD_PATH, + (unsigned)pattern->on_period, NULL, TRUE, TRUE); + } else { + (void)mce_write_string_to_file(MCE_LED_TRIGGER_PATH, + MCE_LED_TRIGGER_NONE); + } + + mono_set_brightness(pattern->brightness); + +EXIT: + return; +} + +/** + * Setup and activate a new LED pattern + * + * @param pattern A pointer to a pattern_struct with the new pattern + */ +static void program_led(const pattern_struct *const pattern) +{ + switch (get_led_type()) { + case LED_TYPE_LYSTI_RGB: + case LED_TYPE_LYSTI_MONO: + lysti_program_led(pattern); + break; + + case LED_TYPE_NJOY_RGB: + case LED_TYPE_NJOY_MONO: + njoy_program_led(pattern); + break; + + case LED_TYPE_DIRECT_MONO: + mono_program_led(pattern); + break; + + default: + break; + } +} + +/** + * Recalculate active pattern and update the pattern timer + */ +static void led_update_active_pattern(void) +{ + display_state_t display_state = datapipe_get_gint(display_state_pipe); + system_state_t system_state = datapipe_get_gint(system_state_pipe); + pattern_struct *new_active_pattern; + gint i = 0; + + if (g_queue_is_empty(pattern_stack) == TRUE) { + disable_led(); + goto EXIT; + } + + while ((new_active_pattern = g_queue_peek_nth(pattern_stack, + i++)) != NULL) { + mce_log(LL_DEBUG, + "pattern: %s, active: %d, enabled: %d", + new_active_pattern->name, + new_active_pattern->active, + new_active_pattern->enabled); + + /* If the pattern is deactivated, ignore */ + if (new_active_pattern->active == FALSE) + continue; + + /* If the pattern is disabled through GConf, ignore */ + if (new_active_pattern->enabled == FALSE) + continue; + + /* If the LED is disabled, + * only patterns with visibility 5 are shown + */ + if ((led_enabled == FALSE) && + (new_active_pattern->policy != 5)) + continue; + + /* Always show pattern with visibility 3 or 5 */ + if ((new_active_pattern->policy == 3) || + (new_active_pattern->policy == 5)) + break; + + /* Acting dead behaviour */ + if (system_state == MCE_STATE_ACTDEAD) { + /* If we're in acting dead, + * show patterns with visibility 4 + */ + if (new_active_pattern->policy == 4) + break; + + /* If we're in acting dead + * and the display is off, show pattern + */ + if ((display_state == MCE_DISPLAY_OFF) && + (new_active_pattern->policy == 2)) + break; + + /* If the display is on and visibility is 2, + * or if visibility is 1/0, ignore pattern + */ + continue; + } + + /* If the display is off, we can use any active pattern */ + if (display_state == MCE_DISPLAY_OFF) + break; + + /* If the pattern should be shown with screen on, use it */ + if (new_active_pattern->policy == 1) + break; + } + + if ((new_active_pattern == NULL) || + ((led_enabled == FALSE) && + (new_active_pattern->policy != 5))) { + active_pattern = NULL; + disable_led(); + cancel_pattern_timeout(); + goto EXIT; + } + + /* Only reprogram the pattern and timer if the pattern changed */ + if (new_active_pattern != active_pattern) { + disable_led(); + + if (new_active_pattern->timeout != -1) { + setup_pattern_timeout(new_active_pattern->timeout); + } + + program_led(new_active_pattern); + } + + active_pattern = new_active_pattern; + +EXIT: + return; +} + +/** + * Find the pattern struct for a pattern + * + * @param name The name of the pattern + * @return A pointer to the pattern struct, or NULL if no such pattern exists + */ +static pattern_struct *find_pattern_struct(const gchar *const name) +{ + pattern_struct *psp = NULL; + GList *glp; + + if (name == NULL) + goto EXIT; + + if ((glp = g_queue_find_custom(pattern_stack, + name, queue_find)) != NULL) { + psp = (pattern_struct *)glp->data; + } + +EXIT: + return psp; +} + +/** + * Update combination rule + * + * @param name The rule to process + * @param data Unused + */ +static void update_combination_rule(gpointer name, gpointer data) +{ + combination_rule_struct *cr; + gboolean enabled = TRUE; + pattern_struct *psp; + GList *glp; + gchar *tmp; + gint i; + + (void)data; + + if ((glp = g_queue_find_custom(combination_rule_list, + name, queue_find)) == NULL) + goto EXIT; + + cr = glp->data; + + /* If all patterns in the pre_requisite list are enabled, + * then enable this pattern, else disable it + */ + for (i = 0; (tmp = g_queue_peek_nth(cr->pre_requisites, i)) != NULL; i++) { + /* We've got a pattern name; check if that pattern is active */ + if (((psp = find_pattern_struct(tmp)) == NULL) || + (psp->active == FALSE)) { + enabled = FALSE; + break; + } + } + + if ((psp = find_pattern_struct(name)) == NULL) + goto EXIT; + + psp->active = enabled; + +EXIT: + return; +} + +/** + * Update activate patterns based on combination rules + * + * @param name THe name of the pattern that changed state + */ +static void update_combination_rules(const gchar *const name) +{ + GList *glp; + + if (name == NULL) { + mce_log(LL_CRIT, + "called with name == NULL"); + goto EXIT; + } + + if ((glp = g_queue_find_custom(combination_rule_xref_list, name, + queue_find)) != NULL) { + combination_rule_struct *xrf = glp->data; + + /* Update all combination rules that this pattern influences */ + g_queue_foreach(xrf->pre_requisites, + update_combination_rule, NULL); + } + +EXIT: + return; +} + +/** + * Activate a pattern in the pattern-stack + * + * @param name The name of the pattern to activate + */ +static void led_activate_pattern(const gchar *const name) +{ + pattern_struct *psp; + + if (name == NULL) { + mce_log(LL_CRIT, + "called with name == NULL"); + goto EXIT; + } + + if ((psp = find_pattern_struct(name)) != NULL) { + psp->active = TRUE; + update_combination_rules(name); + led_update_active_pattern(); + mce_log(LL_DEBUG, + "LED pattern %s activated", + name); + } else { + mce_log(LL_DEBUG, + "Received request to activate " + "a non-existing LED pattern"); + } + +EXIT: + return; +} + +/** + * Deactivate a pattern in the pattern-stack + * + * @param name The name of the pattern to deactivate + */ +static void led_deactivate_pattern(const gchar *const name) +{ + pattern_struct *psp; + + if ((psp = find_pattern_struct(name)) != NULL) { + psp->active = FALSE; + update_combination_rules(name); + led_update_active_pattern(); + mce_log(LL_DEBUG, + "LED pattern %s deactivated", + name); + } else { + mce_log(LL_DEBUG, + "Received request to deactivate " + "a non-existing LED pattern"); + } +} + +/** + * Enable the LED + */ +static void led_enable(void) +{ + led_enabled = TRUE; + led_update_active_pattern(); +} + +/** + * Disable the LED + */ +static void led_disable(void) +{ + led_enabled = FALSE; + disable_led(); +} + +/** + * Handle system state change + * + * @param data Unused + */ +static void system_state_trigger(gconstpointer data) +{ + (void)data; + + led_update_active_pattern(); +} + +/** + * Handle display state change + * + * @param data Unused + */ +static void display_state_trigger(gconstpointer data) +{ + static display_state_t old_display_state = MCE_DISPLAY_UNDEF; + display_state_t display_state = GPOINTER_TO_INT(data); + + if (old_display_state == display_state) + goto EXIT; + + led_update_active_pattern(); + old_display_state = display_state; + +EXIT: + return; +} + +/** + * Handle led brightness change + * + * @param data The LED brightness stored in a pointer + */ +static void led_brightness_trigger(gconstpointer data) +{ + gint led_brightness = GPOINTER_TO_INT(data); + + switch (get_led_type()) { + case LED_TYPE_LYSTI_RGB: + case LED_TYPE_LYSTI_MONO: + lysti_set_brightness(led_brightness); + break; + + case LED_TYPE_NJOY_RGB: + case LED_TYPE_NJOY_MONO: + njoy_set_brightness(led_brightness); + break; + + case LED_TYPE_DIRECT_MONO: + case LED_TYPE_UNSET: + case LED_TYPE_NONE: + default: + break; + } +} + +/** + * Handle LED pattern activate requests + * + * @param data The pattern name + */ +static void led_pattern_activate_trigger(gconstpointer data) +{ + led_activate_pattern((gchar *)data); +} + +/** + * Handle LED pattern deactivate requests + * + * @param data The pattern name + */ +static void led_pattern_deactivate_trigger(gconstpointer data) +{ + led_deactivate_pattern((gchar *)data); +} + +/** + * Custom find function to get a GConf callback ID in the pattern stack + * + * @param data The pattern_struct entry + * @param userdata The pattern name + * @return 0 if the GConf callback id of data matches that of userdata, + * -1 if they don't match + */ +static gint gconf_cb_find(gconstpointer data, gconstpointer userdata) +{ + pattern_struct *psp; + + if ((data == NULL) || (userdata == NULL)) + return -1; + + psp = (pattern_struct *)data; + + return psp->gconf_cb_id != *(guint *)userdata; +} + +/** + * GConf callback for LED related settings + * + * @param gcc Unused + * @param id Connection ID from gconf_client_notify_add() + * @param entry The modified GConf entry + * @param data Unused + */ +static void led_gconf_cb(GConfClient *const gcc, const guint id, + GConfEntry *const entry, gpointer const data) +{ + const GConfValue *gcv = gconf_entry_get_value(entry); + pattern_struct *psp = NULL; + GList *glp = NULL; + + (void)gcc; + (void)data; + + /* Key is unset */ + if (gcv == NULL) { + mce_log(LL_DEBUG, + "GConf Key `%s' has been unset", + gconf_entry_get_key(entry)); + goto EXIT; + } + + if ((glp = g_queue_find_custom(pattern_stack, + &id, gconf_cb_find)) != NULL) { + psp = (pattern_struct *)glp->data; + psp->enabled = gconf_value_get_bool(gcv); + led_update_active_pattern(); + } else { + mce_log(LL_WARN, "Spurious GConf value received; confused!"); + } + +EXIT: + return; +} + +/** + * Get the enabled/disabled value from GConf and set up a notifier + */ +static gboolean pattern_get_enabled(const gchar *const patternname, + guint *gconf_cb_id) +{ + gboolean retval = DEFAULT_PATTERN_ENABLED; + gchar *path = gconf_concat_dir_and_key(MCE_GCONF_LED_PATH, + patternname); + + /* Since we've set a default, error handling is unnecessary */ + (void)mce_gconf_get_bool(path, &retval); + + if (mce_gconf_notifier_add(MCE_GCONF_LED_PATH, path, + led_gconf_cb, gconf_cb_id) == FALSE) + goto EXIT; + +EXIT: + g_free(path); + + return retval; +} + +/** + * D-Bus callback for the activate LED pattern method call + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean led_activate_pattern_dbus_cb(DBusMessage *const msg) +{ + dbus_bool_t no_reply = dbus_message_get_no_reply(msg); + const gchar *pattern = NULL; + gboolean status = FALSE; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + mce_log(LL_DEBUG, "Received activate LED pattern request"); + + if (dbus_message_get_args(msg, &error, + DBUS_TYPE_STRING, &pattern, + DBUS_TYPE_INVALID) == FALSE) { + // XXX: should we return an error instead? + mce_log(LL_CRIT, + "Failed to get argument from %s.%s: %s", + MCE_REQUEST_IF, MCE_ACTIVATE_LED_PATTERN, + error.message); + dbus_error_free(&error); + goto EXIT; + } + + led_activate_pattern(pattern); + + if (no_reply == FALSE) { + DBusMessage *reply = dbus_new_method_reply(msg); + + status = dbus_send_message(reply); + } else { + status = TRUE; + } + +EXIT: + return status; +} + +/** + * D-Bus callback for the deactivate LED pattern method call + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean led_deactivate_pattern_dbus_cb(DBusMessage *const msg) +{ + dbus_bool_t no_reply = dbus_message_get_no_reply(msg); + const gchar *pattern = NULL; + gboolean status = FALSE; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + mce_log(LL_DEBUG, "Received deactivate LED pattern request"); + + if (dbus_message_get_args(msg, &error, + DBUS_TYPE_STRING, &pattern, + DBUS_TYPE_INVALID) == FALSE) { + // XXX: should we return an error instead? + mce_log(LL_CRIT, + "Failed to get argument from %s.%s: %s", + MCE_REQUEST_IF, MCE_DEACTIVATE_LED_PATTERN, + error.message); + dbus_error_free(&error); + goto EXIT; + } + + led_deactivate_pattern(pattern); + + if (no_reply == FALSE) { + DBusMessage *reply = dbus_new_method_reply(msg); + + status = dbus_send_message(reply); + } else { + status = TRUE; + } + +EXIT: + return status; +} + +/** + * D-Bus callback for the enable LED method call + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean led_enable_dbus_cb(DBusMessage *const msg) +{ + dbus_bool_t no_reply = dbus_message_get_no_reply(msg); + gboolean status = FALSE; + + mce_log(LL_DEBUG, "Received LED enable request"); + + led_enable(); + + if (no_reply == FALSE) { + DBusMessage *reply = dbus_new_method_reply(msg); + + status = dbus_send_message(reply); + } else { + status = TRUE; + } + +//EXIT: + return status; +} + +/** + * D-Bus callback for the disable LED method call + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean led_disable_dbus_cb(DBusMessage *const msg) +{ + dbus_bool_t no_reply = dbus_message_get_no_reply(msg); + gboolean status = FALSE; + + mce_log(LL_DEBUG, "Received LED disable request"); + + led_disable(); + active_pattern = NULL; + + if (no_reply == FALSE) { + DBusMessage *reply = dbus_new_method_reply(msg); + + status = dbus_send_message(reply); + } else { + status = TRUE; + } + +//EXIT: + return status; +} + +/** + * Init LED pattern combination rules + * + * @return TRUE on success, FALSE on failure + */ +static gboolean init_combination_rules(void) +{ + gboolean status = FALSE; + gchar **crlist = NULL; + gsize length; + gint i; + + /* Get the list of valid LED patttern combination rules */ + crlist = mce_conf_get_string_list(MCE_CONF_LED_GROUP, + MCE_CONF_LED_COMBINATION_RULES, + &length, + NULL); + + /* Treat failed conf-value reads as if they were due to invalid keys + * rather than failed allocations; let future allocation attempts fail + * instead; otherwise we'll miss the real invalid key failures + */ + if (crlist == NULL) { + mce_log(LL_WARN, + "Failed to configure LED pattern combination rules"); + status = TRUE; + goto EXIT; + } + + /* Used for all combination patterns */ + for (i = 0; crlist[i]; i++) { + gchar **tmp; + + mce_log(LL_DEBUG, + "Getting LED pattern combination rule for: %s", + crlist[i]); + + tmp = mce_conf_get_string_list(led_pattern_group, + crlist[i], + &length, + NULL); + + if (tmp != NULL) { + combination_rule_struct *cr = NULL; + guint j; + + if (length < 2) { + mce_log(LL_ERR, + "LED Pattern Combination rule `%s'", + crlist[i]); + g_strfreev(tmp); + goto EXIT2; + } + + cr = g_slice_new(combination_rule_struct); + + if (cr == NULL) { + g_strfreev(tmp); + goto EXIT2; + } + + cr->rulename = strdup(tmp[0]); + cr->pre_requisites = g_queue_new(); + + for (j = 1; j < length; j++) { + gchar *str = strdup(tmp[j]); + GList *glp; + combination_rule_struct *xrf = NULL; + + g_queue_push_head(cr->pre_requisites, str); + + glp = g_queue_find_custom(combination_rule_xref_list, str, queue_find); + + if ((glp == NULL) || (glp->data == NULL)) { + xrf = g_slice_new(combination_rule_struct); + xrf->rulename = str; + xrf->pre_requisites = g_queue_new(); + g_queue_push_head(combination_rule_xref_list, xrf); + } else { + xrf = (combination_rule_struct *)glp->data; + } + + /* If the cross reference isn't in the list + * already, add it + */ + if (g_queue_find_custom(xrf->pre_requisites, cr->rulename, queue_find) == NULL) { + g_queue_push_head(xrf->pre_requisites, + cr->rulename); + } + } + + g_queue_push_head(combination_rule_list, cr); + } + } + + status = TRUE; + +EXIT2: + g_strfreev(crlist); + +EXIT: + return status; +} + +/** + * Init patterns for Lysti controlled RGB or monochrome LED + * + * @return TRUE on success, FALSE on failure + */ +static gboolean init_lysti_patterns(void) +{ + led_type_t led_type = get_led_type(); + gchar **patternlist = NULL; + gboolean status = FALSE; + gsize length; + gint i; + + /* Get the list of valid LED patterns */ + patternlist = mce_conf_get_string_list(MCE_CONF_LED_GROUP, + MCE_CONF_LED_PATTERNS, + &length, + NULL); + + /* Treat failed conf-value reads as if they were due to invalid keys + * rather than failed allocations; let future allocation attempts fail + * instead; otherwise we'll miss the real invalid key failures + */ + if (patternlist == NULL) { + mce_log(LL_WARN, + "Failed to configure LED patterns"); + status = TRUE; + goto EXIT; + } + + /* Used for Lysti LED patterns */ + for (i = 0; patternlist[i]; i++) { + gchar **tmp; + + mce_log(LL_DEBUG, + "Getting LED pattern for: %s", + patternlist[i]); + + tmp = mce_conf_get_string_list(led_pattern_group, + patternlist[i], + &length, + NULL); + + if (tmp != NULL) { + pattern_struct *psp; + guint engine1_mux; + guint engine2_mux; + + if (((led_type == LED_TYPE_LYSTI_MONO) && + ((length != NUMBER_OF_PATTERN_FIELDS_LYSTI_MONO) || + (strlen(tmp[PATTERN_E_CHANNEL_FIELD]) > + CHANNEL_SIZE))) || + ((led_type == LED_TYPE_LYSTI_RGB) && + ((length != NUMBER_OF_PATTERN_FIELDS) || + (strlen(tmp[PATTERN_E1_CHANNEL_FIELD]) > + CHANNEL_SIZE) || + (strlen(tmp[PATTERN_E2_CHANNEL_FIELD]) > + CHANNEL_SIZE)))) { + mce_log(LL_ERR, + "Skipping invalid LED-pattern"); + g_strfreev(tmp); + continue; + } + + engine1_mux = 0; + engine2_mux = 0; + + if (led_type == LED_TYPE_LYSTI_MONO) { + engine1_mux |= MCE_LYSTI_MONOCHROME_MASK_RM680; + } else if (led_type == LED_TYPE_LYSTI_RGB) { + if (strchr(tmp[PATTERN_MUXING_FIELD], 'r')) + engine1_mux |= MCE_LYSTI_RED_MASK_RX51; + + if (strchr(tmp[PATTERN_MUXING_FIELD], 'R')) + engine2_mux |= MCE_LYSTI_RED_MASK_RX51; + + if (strchr(tmp[PATTERN_MUXING_FIELD], 'g')) + engine1_mux |= MCE_LYSTI_GREEN_MASK_RX51; + + if (strchr(tmp[PATTERN_MUXING_FIELD], 'G')) + engine2_mux |= MCE_LYSTI_GREEN_MASK_RX51; + + if (strchr(tmp[PATTERN_MUXING_FIELD], 'b')) + engine1_mux |= MCE_LYSTI_BLUE_MASK_RX51; + + if (strchr(tmp[PATTERN_MUXING_FIELD], 'B')) + engine2_mux |= MCE_LYSTI_BLUE_MASK_RX51; + } + + if ((engine1_mux & engine2_mux) != 0) { + mce_log(LL_ERR, + "Same LED muxed to multiple engines; " + "skipping invalid LED-pattern"); + g_strfreev(tmp); + continue; + } + + psp = g_slice_new(pattern_struct); + + if (!psp) { + g_strfreev(tmp); + goto EXIT2; + } + + psp->priority = strtoul(tmp[PATTERN_PRIO_FIELD], + NULL, 10); + psp->policy = strtoul(tmp[PATTERN_SCREEN_ON_FIELD], + NULL, 10); + + if ((psp->timeout = strtoul(tmp[PATTERN_TIMEOUT_FIELD], + NULL, 10)) == 0) + psp->timeout = -1; + + /* Catch all error checking for all three strtoul */ + if ((errno == EINVAL) || (errno == ERANGE)) { + /* Reset errno, + * to avoid false positives further down + */ + g_strfreev(tmp); + g_slice_free(pattern_struct, psp); + continue; + } + + psp->engine1_mux = engine1_mux; + psp->engine2_mux = engine2_mux; + + if (led_type == LED_TYPE_LYSTI_MONO) { + strncpy(psp->channel1, + tmp[PATTERN_E_CHANNEL_FIELD], + CHANNEL_SIZE); + } else if (led_type == LED_TYPE_LYSTI_RGB) { + strncpy(psp->channel1, + tmp[PATTERN_E1_CHANNEL_FIELD], + CHANNEL_SIZE); + strncpy(psp->channel2, + tmp[PATTERN_E2_CHANNEL_FIELD], + CHANNEL_SIZE); + } + + psp->active = FALSE; + + psp->enabled = pattern_get_enabled(patternlist[i], + &(psp->gconf_cb_id)); + + psp->name = strdup(patternlist[i]); + + g_strfreev(tmp); + + g_queue_insert_sorted(pattern_stack, psp, + queue_prio_compare, + NULL); + } + } + + init_combination_rules(); + + /* Set the LED brightness */ + execute_datapipe(&led_brightness_pipe, + GINT_TO_POINTER(maximum_led_brightness), + USE_INDATA, CACHE_INDATA); + + status = TRUE; + +EXIT2: + g_strfreev(patternlist); + +EXIT: + return status; +} + +/** + * Init patterns for NJoy controlled RGB LED + * + * @return TRUE on success, FALSE on failure + */ +static gboolean init_njoy_patterns(void) +{ + led_type_t led_type = get_led_type(); + gchar **patternlist = NULL; + gboolean status = FALSE; + gsize length; + gint i; + + /* Get the list of valid LED patterns */ + patternlist = mce_conf_get_string_list(MCE_CONF_LED_GROUP, + MCE_CONF_LED_PATTERNS, + &length, + NULL); + + /* Treat failed conf-value reads as if they were due to invalid keys + * rather than failed allocations; let future allocation attempts fail + * instead; otherwise we'll miss the real invalid key failures + */ + if (patternlist == NULL) { + mce_log(LL_WARN, + "Failed to configure LED patterns"); + status = TRUE; + goto EXIT; + } + + /* Used for RGB NJoy LED patterns */ + for (i = 0; patternlist[i]; i++) { + gchar **tmp; + + mce_log(LL_DEBUG, + "Getting LED pattern for: %s", + patternlist[i]); + + tmp = mce_conf_get_string_list(led_pattern_group, + patternlist[i], + &length, + NULL); + + if (tmp != NULL) { + pattern_struct *psp; + + if (((led_type == LED_TYPE_NJOY_MONO) && + ((length != NUMBER_OF_PATTERN_FIELDS_NJOY_MONO) || + (strlen(tmp[PATTERN_E_CHANNEL_FIELD]) > + CHANNEL_SIZE))) || + ((led_type == LED_TYPE_NJOY_RGB) && + ((length != NUMBER_OF_PATTERN_FIELDS) || + (strlen(tmp[PATTERN_R_CHANNEL_FIELD]) > + CHANNEL_SIZE) || + (strlen(tmp[PATTERN_G_CHANNEL_FIELD]) > + CHANNEL_SIZE) || + (strlen(tmp[PATTERN_B_CHANNEL_FIELD]) > + CHANNEL_SIZE)))) { + mce_log(LL_ERR, + "Skipping invalid LED-pattern"); + g_strfreev(tmp); + continue; + } + + psp = g_slice_new(pattern_struct); + + if (!psp) { + g_strfreev(tmp); + goto EXIT2; + } + + psp->priority = strtoul(tmp[PATTERN_PRIO_FIELD], + NULL, 10); + psp->policy = strtoul(tmp[PATTERN_SCREEN_ON_FIELD], + NULL, 10); + + if ((psp->timeout = strtoul(tmp[PATTERN_TIMEOUT_FIELD], + NULL, 10)) == 0) + psp->timeout = -1; + + /* Catch all error checking for all three strtoul */ + if ((errno == EINVAL) || (errno == ERANGE)) { + /* Reset errno, + * to avoid false positives further down + */ + g_strfreev(tmp); + g_slice_free(pattern_struct, psp); + continue; + } + + if (led_type == LED_TYPE_NJOY_MONO) { + strncpy(psp->channel1, + tmp[PATTERN_E_CHANNEL_FIELD], + CHANNEL_SIZE); + } else { + strncpy(psp->channel1, + tmp[PATTERN_R_CHANNEL_FIELD], + CHANNEL_SIZE); + strncpy(psp->channel2, + tmp[PATTERN_G_CHANNEL_FIELD], + CHANNEL_SIZE); + strncpy(psp->channel3, + tmp[PATTERN_B_CHANNEL_FIELD], + CHANNEL_SIZE); + } + + psp->active = FALSE; + + psp->enabled = pattern_get_enabled(patternlist[i], + &(psp->gconf_cb_id)); + + psp->name = strdup(patternlist[i]); + + g_strfreev(tmp); + + g_queue_insert_sorted(pattern_stack, psp, + queue_prio_compare, + NULL); + } + } + + /* Set the LED brightness */ + execute_datapipe(&led_brightness_pipe, + GINT_TO_POINTER(maximum_led_brightness), + USE_INDATA, CACHE_INDATA); + + status = TRUE; + +EXIT2: + g_strfreev(patternlist); + +EXIT: + return status; +} + +/** + * Init patterns for direct controlled monochrome LED + * + * @return TRUE on success, FALSE on failure + */ +static gboolean init_mono_patterns(void) +{ + gchar **patternlist = NULL; + gboolean status = FALSE; + gsize length; + gint i; + + /* Get the list of valid LED patterns */ + patternlist = mce_conf_get_string_list(MCE_CONF_LED_GROUP, + MCE_CONF_LED_PATTERNS, + &length, + NULL); + + /* Treat failed conf-value reads as if they were due to invalid keys + * rather than failed allocations; let future allocation attempts fail + * instead; otherwise we'll miss the real invalid key failures + */ + if (patternlist == NULL) { + mce_log(LL_WARN, + "Failed to configure LED patterns"); + status = TRUE; + goto EXIT; + } + + /* Used for single-colour LED patterns */ + for (i = 0; patternlist[i]; i++) { + gint *tmp; + + mce_log(LL_DEBUG, + "Getting LED pattern for: %s", + patternlist[i]); + + tmp = mce_conf_get_int_list(led_pattern_group, + patternlist[i], + &length, + NULL); + + if (tmp != NULL) { + pattern_struct *psp; + + if (length != NUMBER_OF_PATTERN_FIELDS) { + mce_log(LL_ERR, + "Skipping invalid LED-pattern"); + g_free(tmp); + continue; + } + + psp = g_slice_new(pattern_struct); + + if (!psp) { + g_free(tmp); + goto EXIT2; + } + + psp->name = strdup(patternlist[i]); + psp->priority = tmp[PATTERN_PRIO_FIELD]; + psp->policy = tmp[PATTERN_SCREEN_ON_FIELD]; + psp->timeout = tmp[PATTERN_TIMEOUT_FIELD] ? tmp[PATTERN_TIMEOUT_FIELD] : -1; + psp->on_period = tmp[PATTERN_ON_PERIOD_FIELD]; + psp->off_period = tmp[PATTERN_OFF_PERIOD_FIELD]; + psp->brightness = tmp[PATTERN_BRIGHTNESS_FIELD]; + psp->active = FALSE; + + psp->enabled = pattern_get_enabled(patternlist[i], + &(psp->gconf_cb_id)); + + g_free(tmp); + + g_queue_insert_sorted(pattern_stack, psp, + queue_prio_compare, + NULL); + } + } + + status = TRUE; + +EXIT2: + g_strfreev(patternlist); + +EXIT: + return status; +} + +/** + * Init patterns for the LED + * + * @return TRUE on success, FALSE on failure + */ +static gboolean init_patterns(void) +{ + gboolean status; + + switch (get_led_type()) { + case LED_TYPE_LYSTI_MONO: + case LED_TYPE_LYSTI_RGB: + status = init_lysti_patterns(); + break; + + case LED_TYPE_NJOY_MONO: + case LED_TYPE_NJOY_RGB: + status = init_njoy_patterns(); + break; + + case LED_TYPE_DIRECT_MONO: + status = init_mono_patterns(); + break; + + default: + status = TRUE; + break; + } + + return status; +} + +/** + * Init function for the LED logic module + * + * @todo XXX status needs to be set on error! + * + * @param module Unused + * @return NULL on success, a string with an error message on failure + */ +G_MODULE_EXPORT const gchar *g_module_check_init(GModule *module); +const gchar *g_module_check_init(GModule *module) +{ + gchar *status = NULL; + + (void)module; + + /* Append triggers/filters to datapipes */ + append_output_trigger_to_datapipe(&system_state_pipe, + system_state_trigger); + append_output_trigger_to_datapipe(&display_state_pipe, + display_state_trigger); + append_output_trigger_to_datapipe(&led_brightness_pipe, + led_brightness_trigger); + append_output_trigger_to_datapipe(&led_pattern_activate_pipe, + led_pattern_activate_trigger); + append_output_trigger_to_datapipe(&led_pattern_deactivate_pipe, + led_pattern_deactivate_trigger); + + /* Setup a pattern stack, + * a combination rule stack and a cross-refernce for said stack + * and initialise the patterns + */ + pattern_stack = g_queue_new(); + combination_rule_list = g_queue_new(); + combination_rule_xref_list = g_queue_new(); + + if (init_patterns() == FALSE) + goto EXIT; + + /* req_led_pattern_activate */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_ACTIVATE_LED_PATTERN, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + led_activate_pattern_dbus_cb) == NULL) + goto EXIT; + + /* req_led_pattern_deactivate */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_DEACTIVATE_LED_PATTERN, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + led_deactivate_pattern_dbus_cb) == NULL) + goto EXIT; + + /* req_led_enable */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_ENABLE_LED, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + led_enable_dbus_cb) == NULL) + goto EXIT; + + /* req_led_disable */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_DISABLE_LED, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + led_disable_dbus_cb) == NULL) + goto EXIT; + + led_enable(); + +EXIT: + return status; +} + +/** + * Exit function for the LED logic module + * + * @todo D-Bus unregistration + * + * @param module Unused + */ +G_MODULE_EXPORT void g_module_unload(GModule *module); +void g_module_unload(GModule *module) +{ + system_state_t system_state = datapipe_get_gint(system_state_pipe); + + (void)module; + + /* Close files */ + mce_close_file(led_current_rm_path, &led_current_rm_fp); + mce_close_file(led_current_g_path, &led_current_g_fp); + mce_close_file(led_current_b_path, &led_current_b_fp); + + mce_close_file(led_brightness_rm_path, &led_brightness_rm_fp); + mce_close_file(led_brightness_g_path, &led_brightness_g_fp); + mce_close_file(led_brightness_b_path, &led_brightness_b_fp); + + /* Remove triggers/filters from datapipes */ + remove_output_trigger_from_datapipe(&led_pattern_deactivate_pipe, + led_pattern_deactivate_trigger); + remove_output_trigger_from_datapipe(&led_pattern_activate_pipe, + led_pattern_activate_trigger); + remove_output_trigger_from_datapipe(&led_brightness_pipe, + led_brightness_trigger); + remove_output_trigger_from_datapipe(&display_state_pipe, + display_state_trigger); + remove_output_trigger_from_datapipe(&system_state_pipe, + system_state_trigger); + + /* Don't disable the LED on shutdown/reboot/acting dead */ + if ((system_state != MCE_STATE_ACTDEAD) && + (system_state != MCE_STATE_SHUTDOWN) && + (system_state != MCE_STATE_REBOOT)) { + led_disable(); + } + + /* Free path strings; this has to be done after led_disable(), + * since it uses these paths + */ + g_free(led_current_rm_path); + g_free(led_current_g_path); + g_free(led_current_b_path); + + g_free(led_brightness_rm_path); + g_free(led_brightness_g_path); + g_free(led_brightness_b_path); + + g_free(engine1_mode_path); + g_free(engine2_mode_path); + g_free(engine3_mode_path); + + g_free(engine1_load_path); + g_free(engine2_load_path); + g_free(engine3_load_path); + + g_free(engine1_leds_path); + g_free(engine2_leds_path); + g_free(engine3_leds_path); + + /* Free the pattern stack */ + if (pattern_stack != NULL) { + pattern_struct *psp; + + while ((psp = g_queue_pop_head(pattern_stack)) != NULL) { + mce_gconf_notifier_remove(GINT_TO_POINTER(psp->gconf_cb_id), NULL); + g_free(psp->name); + psp->name = NULL; + g_slice_free(pattern_struct, psp); + } + + g_queue_free(pattern_stack); + pattern_stack = NULL; + } + + /* Free the combination rule list */ + if (combination_rule_list != NULL) { + combination_rule_struct *cr; + + while ((cr = g_queue_pop_head(combination_rule_list)) != NULL) { + gchar *tmp; + + while ((tmp = g_queue_pop_head(cr->pre_requisites)) != NULL) { + g_free(tmp); + tmp = NULL; + } + + g_queue_free(cr->pre_requisites); + cr->pre_requisites = NULL; + g_slice_free(combination_rule_struct, cr); + } + + g_queue_free(combination_rule_list); + combination_rule_list = NULL; + } + + /* Free the combination rule cross reference list */ + if (combination_rule_xref_list != NULL) { + combination_rule_struct *xrf; + + while ((xrf = g_queue_pop_head(combination_rule_xref_list)) != NULL) { + g_queue_free(xrf->pre_requisites); + xrf->pre_requisites = NULL; + g_slice_free(combination_rule_struct, xrf); + } + + g_queue_free(combination_rule_xref_list); + combination_rule_xref_list = NULL; + } + + /* Remove all timer sources */ + cancel_pattern_timeout(); + + return; +} diff --git a/modules/led.h b/modules/led.h new file mode 100644 index 00000000..8254ab97 --- /dev/null +++ b/modules/led.h @@ -0,0 +1,291 @@ +/** + * @file led.h + * Headers for the LED module + *

+ * Copyright © 2006-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _LED_H_ +#define _LED_H_ + +#include + +/** Name of LED configuration group */ +#define MCE_CONF_LED_GROUP "LED" + +/** Name of configuration key for the list of LED patterns */ +#define MCE_CONF_LED_PATTERNS "LEDPatterns" + +/** Name of configuration key for the list of LED Pattern combination-rules */ +#define MCE_CONF_LED_COMBINATION_RULES "CombinationRules" + +/** + * Name of LED single-colour pattern configuration group for + * RX-34 + */ +#define MCE_CONF_LED_PATTERN_RX34_GROUP "LEDPatternMonoRX34" + +/** + * Name of LED NJoy-controlled RGB pattern configuration group for + * RX-44 + */ +#define MCE_CONF_LED_PATTERN_RX44_GROUP "LEDPatternNJoyRX44" + +/** + * Name of LED NJoy-controlled RGB pattern configuration group for + * RX-48 + */ +#define MCE_CONF_LED_PATTERN_RX48_GROUP "LEDPatternNJoyRX48" + +/** + * Name of LED Lysti-controlled RGB pattern configuration group for + * RX-51 + */ +#define MCE_CONF_LED_PATTERN_RX51_GROUP "LEDPatternLystiRX51" + +/** + * Name of LED Lysti-controlled RGB pattern configuration group for + * RM-680/RM-690 + */ +#define MCE_CONF_LED_PATTERN_RM680_GROUP "LEDPatternLystiRM680" + +/** + * Name of LED Lysti-controlled RGB pattern configuration group for + * RM-696/RM-716 + */ +#define MCE_CONF_LED_PATTERN_RM696_GROUP "LEDPatternNJoyRM696" + +/** Path to the GConf settings for the display */ +#define MCE_GCONF_LED_PATH "/system/osso/dsm/leds" + +/** Default value for LED settings */ +#define DEFAULT_PATTERN_ENABLED TRUE + +/** Reno control channel */ +#define TWL5031_BCC 0x4a +/** Reno LED controller */ +#define LED_DRIVER_CTRL 0xaf +/** Reno command to disable LED */ +#define LEDC_DISABLE 0x08 + + +/** Default NJoy (LP5521)-controlled RGB LED current */ +#define MAXIMUM_NJOY_RGB_LED_CURRENT 47 /* 4.7 mA */ + +/** Default NJoy (LP5521)-controlled monochrome LED current */ +#define MAXIMUM_NJOY_MONOCHROME_LED_CURRENT 50 /* 5.0 mA */ + +/** Maximum Lysti (LP5523)-controlled RGB LED current */ +#define MAXIMUM_LYSTI_RGB_LED_CURRENT 47 /* 4.7 mA */ + +/** Maximum Lysti (LP5523)-controlled monochrome LED current */ +#define MAXIMUM_LYSTI_MONOCHROME_LED_CURRENT 100 /* 10.0 mA */ + + +/** Path to the mono LED /sys directory */ +#define MCE_MONO_LED_SYS_PATH "/sys/class/leds/keypad" + +/** Monochrome LED on period file */ +#define MCE_LED_ON_PERIOD_PATH MCE_MONO_LED_SYS_PATH "/delay_on" +/** Monochrome LED off period file */ +#define MCE_LED_OFF_PERIOD_PATH MCE_MONO_LED_SYS_PATH "/delay_off" +/** Monochrome LED trigger file */ +#define MCE_LED_TRIGGER_PATH MCE_MONO_LED_SYS_PATH "/trigger" + +/* Trigger type */ +/** Timer based trigger */ +#define MCE_LED_TRIGGER_TIMER "timer" +/** No trigger */ +#define MCE_LED_TRIGGER_NONE "none" + + +/* LED modes */ +/** LED disabled */ +#define MCE_LED_DISABLED_MODE "disabled" +/** LED direct control mode */ +#define MCE_LED_DIRECT_MODE "direct" +/** LED load mode */ +#define MCE_LED_LOAD_MODE "load" +/** LED run mode */ +#define MCE_LED_RUN_MODE "run" + + +/** Suffix used for LED current */ +#define MCE_LED_CURRENT_SUFFIX "/led_current" +/** Suffix used for LED brightness */ +#define MCE_LED_BRIGHTNESS_SUFFIX "/brightness" + + +/** Path to direct LED control /sys directory */ +#define MCE_LED_DIRECT_SYS_PATH "/sys/class/leds" + +/** Directory prefix for keypad LED controller */ +#define MCE_LED_KEYPAD_PREFIX "/keypad" + +/** Directory prefix for cover LED controller */ +#define MCE_LED_COVER_PREFIX "/cover" + +/** Directory prefix for keyboard LED controller */ +#define MCE_LED_KEYBOARD_PREFIX "/keyboard" + +/** Directory prefix for LP5521 LED controller */ +#define MCE_LED_LP5521_PREFIX "/lp5521" + +/** Directory prefix for LP5523 LED controller */ +#define MCE_LED_LP5523_PREFIX "/lp5523" + +/** Name of LED channel 0 */ +#define MCE_LED_CHANNEL0 ":channel0" +/** Name of LED channel 1 */ +#define MCE_LED_CHANNEL1 ":channel1" +/** Name of LED channel 2 */ +#define MCE_LED_CHANNEL2 ":channel2" +/** Name of LED channel 3 */ +#define MCE_LED_CHANNEL3 ":channel3" +/** Name of LED channel 4 */ +#define MCE_LED_CHANNEL4 ":channel4" +/** Name of LED channel 5 */ +#define MCE_LED_CHANNEL5 ":channel5" +/** Name of LED channel 6 */ +#define MCE_LED_CHANNEL6 ":channel6" +/** Name of LED channel 7 */ +#define MCE_LED_CHANNEL7 ":channel7" +/** Name of LED channel 8 */ +#define MCE_LED_CHANNEL8 ":channel8" + +/** Name of Engine 1 */ +#define MCE_LED_ENGINE1 "/engine1_" +/** Name of Engine 2 */ +#define MCE_LED_ENGINE2 "/engine2_" +/** Name of Engine 3 */ +#define MCE_LED_ENGINE3 "/engine3_" + +/** LED device suffix */ +#define MCE_LED_DEVICE "/device" + +/** LED mode suffix */ +#define MCE_LED_MODE_SUFFIX "mode" +/** LED load suffix */ +#define MCE_LED_LOAD_SUFFIX "load" +/** LED leds suffix */ +#define MCE_LED_LEDS_SUFFIX "leds" + + +/** + * Lysti LED mask for LED 1 (channel 8); + * RM-680/RM-690 monochrome LED + * RX-51 keyboard backlight 6 + */ +#define MCE_LYSTI_LED1_MASK (1 << 0) +/** + * Lysti LED mask for LED 2 (channel 7); + * RM-680/RM-690 Monochrome LED behind model name + * RX-51 keyboard backlight 5 + */ +#define MCE_LYSTI_LED2_MASK (1 << 1) +/** + * Lysti LED mask for LED 3 (channel 6); + * RM-680/RM-690 Monochrome LED behind model name + * RX-51 red component of RGB LED + */ +#define MCE_LYSTI_LED3_MASK (1 << 2) +/** + * Lysti LED mask for LED 4 (channel 5); + * RM-680/RM-690 monochrome keyboard backlight 6 + * RX-51 green component of RGB LED + */ +#define MCE_LYSTI_LED4_MASK (1 << 3) +/** + * Lysti LED mask for LED 5 (channel 4); + * RM-680/RM-690 monochrome keyboard backlight 5 + * RX-51 blue component of RGB LED + */ +#define MCE_LYSTI_LED5_MASK (1 << 4) +/** + * Lysti LED mask for LED 6 (channel 3); + * RM-680/RM-690 monochrome keyboard backlight 4 + * RX-51 monochrome keyboard backlight 4 + */ +#define MCE_LYSTI_LED6_MASK (1 << 5) +/** + * Lysti LED mask for LED 7 (channel 2); + * RM-680/RM-690 monochrome keyboard backlight 3 + * RX-51 monochrome keyboard backlight 3 + */ +#define MCE_LYSTI_LED7_MASK (1 << 6) +/** + * Lysti LED mask for LED 8 (channel 1); + * RM-680/RM-690 monochrome keyboard backlight 2 + * RX-51 monochrome keyboard backlight 2 + */ +#define MCE_LYSTI_LED8_MASK (1 << 7) +/** + * Lysti LED mask for LED 9 (channel 0); + * RM-680/RM-690 monochrome keyboard backlight 1 + * RX-51 monochrome keyboard backlight 1 + */ +#define MCE_LYSTI_LED9_MASK (1 << 8) + +/** + * Lysti LED mask for the + * RM-680/RM-690 monochrome power button LED + */ +#define MCE_LYSTI_MONOCHROME_MASK_RM680 MCE_LYSTI_LED1_MASK + +/** Lysti LED mask for the RX-51 red RGB LED component */ +#define MCE_LYSTI_RED_MASK_RX51 MCE_LYSTI_LED3_MASK + +/** Lysti LED mask for the RX-51 green RGB LED component */ +#define MCE_LYSTI_GREEN_MASK_RX51 MCE_LYSTI_LED4_MASK + +/** Lysti LED mask for the RX-51 blue RGB LED component */ +#define MCE_LYSTI_BLUE_MASK_RX51 MCE_LYSTI_LED5_MASK + +/** Lysti LED mask for the RX-51 keyboard backlight */ +#define MCE_LYSTI_KB_BACKLIGHT_MASK_RX51 (MCE_LYSTI_LED9_MASK | \ + MCE_LYSTI_LED8_MASK | \ + MCE_LYSTI_LED7_MASK | \ + MCE_LYSTI_LED6_MASK | \ + MCE_LYSTI_LED2_MASK | \ + MCE_LYSTI_LED1_MASK) + +/** Lysti LED mask for the RM-680/RM-690 keyboard backlight */ +#define MCE_LYSTI_KB_BACKLIGHT_MASK_RM680 (MCE_LYSTI_LED9_MASK | \ + MCE_LYSTI_LED8_MASK | \ + MCE_LYSTI_LED7_MASK | \ + MCE_LYSTI_LED6_MASK | \ + MCE_LYSTI_LED5_MASK | \ + MCE_LYSTI_LED4_MASK) + +/* Brightness levels used by the keypad LED */ +#define BRIGHTNESS_LEVEL_0 "0" /**< off */ +#define BRIGHTNESS_LEVEL_1 "12" /**< faintest */ +#define BRIGHTNESS_LEVEL_2 "24" /**< level 2 */ +#define BRIGHTNESS_LEVEL_3 "36" /**< level 3 */ +#define BRIGHTNESS_LEVEL_4 "48" /**< level 4 */ +#define BRIGHTNESS_LEVEL_5 "60" /**< level 5 */ +#define BRIGHTNESS_LEVEL_6 "72" /**< level 6 */ +#define BRIGHTNESS_LEVEL_7 "84" /**< level 7 */ +#define BRIGHTNESS_LEVEL_8 "96" /**< level 8 */ +#define BRIGHTNESS_LEVEL_9 "108" /**< level 9 */ +#define BRIGHTNESS_LEVEL_10 "120" /**< level 10 */ +#define BRIGHTNESS_LEVEL_11 "132" /**< level 11 */ +#define BRIGHTNESS_LEVEL_12 "144" /**< level 12 */ +#define BRIGHTNESS_LEVEL_13 "156" /**< level 13 */ +#define BRIGHTNESS_LEVEL_14 "168" /**< level 14 */ +#define BRIGHTNESS_LEVEL_15 "180" /**< brightest */ + +#endif /* _LED_H_ */ diff --git a/modules/powersavemode.c b/modules/powersavemode.c new file mode 100644 index 00000000..37a35061 --- /dev/null +++ b/modules/powersavemode.c @@ -0,0 +1,394 @@ +/** + * @file powersavemode.c + * Power saving mode module -- this handles the power saving mode + * for MCE + *

+ * Copyright © 2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include +#include + +#include "mce.h" +#include "powersavemode.h" + +#include "mce-log.h" /* mce_log(), LL_* */ +#include "mce-dbus.h" /* Direct: + * --- + * mce_dbus_handler_add(), + * dbus_send_message(), + * dbus_new_method_reply(), + * dbus_new_signal(), + * dbus_message_append_args(), + * dbus_message_unref(), + * DBusMessage, + * DBUS_MESSAGE_TYPE_METHOD_CALL, + * DBUS_TYPE_BOOLEAN, + * DBUS_TYPE_INVALID, + * dbus_bool_t + * + * Indirect: + * --- + * MCE_SIGNAL_IF, + * MCE_SIGNAL_PATH, + * MCE_REQUEST_IF, + * MCE_PSM_STATE_GET, + * MCE_PSM_STATE_SIG, + * MCE_PSM_MODE_GET, + * MCE_PSM_MODE_SIG + */ +#include "mce-gconf.h" /* mce_gconf_get_bool(), + * mce_gconf_notifier_add(), + * gconf_entry_get_key(), + * gconf_value_get_bool(), + * GConfClient, GConfEntry, GConfValue + */ +#include "datapipe.h" /* execute_datapipe(), + * append_output_trigger_to_datapipe(), + * remove_output_trigger_from_datapipe() + */ + +/** Module name */ +#define MODULE_NAME "powersavemode" + +/** Functionality provided by this module */ +static const gchar *const provides[] = { MODULE_NAME, NULL }; + +/** Module information */ +G_MODULE_EXPORT module_info_struct module_info = { + /** Name of the module */ + .name = MODULE_NAME, + /** Module provides */ + .provides = provides, + /** Module priority */ + .priority = 250 +}; + +/** Battery charge level */ +static gint battery_level = 100; + +/** GConf callback ID for power saving mode setting */ +static guint psm_gconf_cb_id = 0; +/** Power saving mode from GConf */ +static gboolean power_saving_mode = DEFAULT_POWER_SAVING_MODE; + +/** GConf callback ID for forced power saving mode setting */ +static guint force_psm_gconf_cb_id = 0; +/** Forced power saving mode from GConf */ +static gboolean force_psm = FALSE; + +/** GConf callback ID for power saving mode threshold */ +static guint psm_threshold_gconf_cb_id = 0; +/** Power saving mode threshold from GConf */ +static gint psm_threshold = DEFAULT_PSM_THRESHOLD; + +/** Active power saving mode */ +static gboolean active_power_saving_mode = FALSE; + +/** Device thermal state */ +static thermal_state_t thermal_state = THERMAL_STATE_UNDEF; + +/** + * Send the PSM state + * + * @param method_call A DBusMessage to reply to; + * pass NULL to send a signal instead + * @return TRUE on success, FALSE on failure + */ +static gboolean send_psm_state(DBusMessage *const method_call) +{ + DBusMessage *msg = NULL; + gboolean status = FALSE; + + mce_log(LL_DEBUG, + "Sending PSM state: %s", + active_power_saving_mode ? "TRUE" : "FALSE"); + + /* If method_call is set, send a reply, + * otherwise, send a signal + */ + if (method_call != NULL) { + msg = dbus_new_method_reply(method_call); + } else { + /* psm_state_ind */ + msg = dbus_new_signal(MCE_SIGNAL_PATH, MCE_SIGNAL_IF, + MCE_PSM_STATE_SIG); + } + + /* Append the power saving mode */ + if (dbus_message_append_args(msg, + DBUS_TYPE_BOOLEAN, &active_power_saving_mode, + DBUS_TYPE_INVALID) == FALSE) { + mce_log(LL_CRIT, + "Failed to append %sargument to D-Bus message " + "for %s.%s", + method_call ? "reply " : "", + method_call ? MCE_REQUEST_IF : + MCE_SIGNAL_IF, + method_call ? MCE_PSM_STATE_GET : + MCE_PSM_STATE_SIG); + dbus_message_unref(msg); + goto EXIT; + } + + /* Send the message */ + status = dbus_send_message(msg); + +// XXX: remove +if (method_call == NULL) { + /* psm_mode_ind */ + msg = dbus_new_signal(MCE_SIGNAL_PATH, MCE_SIGNAL_IF, + MCE_PSM_MODE_SIG); + + /* Append the power saving mode */ + if (dbus_message_append_args(msg, + DBUS_TYPE_BOOLEAN, &active_power_saving_mode, + DBUS_TYPE_INVALID) == FALSE) { + mce_log(LL_CRIT, + "Failed to append %sargument to D-Bus message " + "for %s.%s", + method_call ? "reply " : "", + method_call ? MCE_REQUEST_IF : + MCE_SIGNAL_IF, + method_call ? MCE_PSM_MODE_GET : + MCE_PSM_MODE_SIG); + dbus_message_unref(msg); + goto EXIT; + } + + /* Send the message */ + dbus_send_message(msg); +} + +EXIT: + return status; +} + +/** + * Update the power saving mode + */ +static void update_power_saving_mode(void) +{ + gint new_power_saving_mode; + + /* XXX: later on we should make overheating another + * trigger for power saving mode too + */ + if (((battery_level <= psm_threshold) && + (power_saving_mode == TRUE)) || + (force_psm == TRUE) || + (thermal_state == THERMAL_STATE_OVERHEATED)) { + /* If the battery charge level is lower than the threshold, + * and the user has enable power saving mode, + * or if the device is overheated, + * activate low power mode + */ + new_power_saving_mode = TRUE; + } else { + new_power_saving_mode = FALSE; + } + + if (active_power_saving_mode != new_power_saving_mode) { + active_power_saving_mode = new_power_saving_mode; + (void)execute_datapipe(&power_saving_mode_pipe, + GINT_TO_POINTER(active_power_saving_mode), + USE_INDATA, CACHE_INDATA); + send_psm_state(NULL); + } +} + +/** + * Datapipe trigger for the battery charge level + * + * @param data A pointer representation of the battery charge level in percent + */ +static void battery_level_trigger(gconstpointer const data) +{ + battery_level = GPOINTER_TO_INT(data); + + update_power_saving_mode(); +} + +/** + * Datapipe trigger for the thermal state + * + * @param data THERMAL_STATE_OK if the device temperature is normal, + * THERMAL_STATE_OVERHEATED if the device is overheated + */ +static void thermal_state_trigger(gconstpointer const data) +{ + thermal_state = GPOINTER_TO_INT(data); + + update_power_saving_mode(); +} + +/** + * GConf callback for power saving related settings + * + * @param gcc Unused + * @param id Connection ID from gconf_client_notify_add() + * @param entry The modified GConf entry + * @param data Unused + */ +static void psm_gconf_cb(GConfClient *const gcc, const guint id, + GConfEntry *const entry, gpointer const data) +{ + const GConfValue *gcv = gconf_entry_get_value(entry); + + (void)gcc; + (void)data; + + /* Key is unset */ + if (gcv == NULL) { + mce_log(LL_DEBUG, + "GConf Key `%s' has been unset", + gconf_entry_get_key(entry)); + goto EXIT; + } + + if (id == psm_gconf_cb_id) { + power_saving_mode = gconf_value_get_bool(gcv); + update_power_saving_mode(); + } else if (id == force_psm_gconf_cb_id) { + force_psm = gconf_value_get_bool(gcv); + update_power_saving_mode(); + } else if (id == psm_threshold_gconf_cb_id) { + psm_threshold = gconf_value_get_int(gcv); + update_power_saving_mode(); + } else { + mce_log(LL_WARN, + "Spurious GConf value received; confused!"); + } + +EXIT: + return; +} + +/** + * D-Bus callback for the get PSM mode method call + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean psm_state_get_dbus_cb(DBusMessage *const msg) +{ + gboolean status = FALSE; + + mce_log(LL_DEBUG, + "Received PSM state get request"); + + /* Try to send a reply that contains the current PSM state */ + if (send_psm_state(msg) == FALSE) + goto EXIT; + + status = TRUE; + +EXIT: + return status; +} + +/** + * Init function for the power saving mode module + * + * @todo XXX status needs to be set on error! + * + * @param module Unused + * @return NULL on success, a string with an error message on failure + */ +G_MODULE_EXPORT const gchar *g_module_check_init(GModule *module); +const gchar *g_module_check_init(GModule *module) +{ + (void)module; + + /* Append triggers/filters to datapipes */ + append_output_trigger_to_datapipe(&battery_level_pipe, + battery_level_trigger); + append_output_trigger_to_datapipe(&thermal_state_pipe, + thermal_state_trigger); + + /* Power saving mode setting */ + /* Since we've set a default, error handling is unnecessary */ + (void)mce_gconf_get_bool(MCE_GCONF_PSM_PATH, + &power_saving_mode); + + if (mce_gconf_notifier_add(MCE_GCONF_EM_PATH, + MCE_GCONF_PSM_PATH, + psm_gconf_cb, + &psm_gconf_cb_id) == FALSE) + goto EXIT; + + /* Forced power saving mode setting */ + /* Since we've set a default, error handling is unnecessary */ + (void)mce_gconf_get_bool(MCE_GCONF_FORCED_PSM_PATH, + &force_psm); + + if (mce_gconf_notifier_add(MCE_GCONF_EM_PATH, + MCE_GCONF_FORCED_PSM_PATH, + psm_gconf_cb, + &force_psm_gconf_cb_id) == FALSE) + goto EXIT; + + /* Power saving mode threshold */ + /* Since we've set a default, error handling is unnecessary */ + (void)mce_gconf_get_int(MCE_GCONF_PSM_THRESHOLD_PATH, + &psm_threshold); + + if (mce_gconf_notifier_add(MCE_GCONF_EM_PATH, + MCE_GCONF_PSM_THRESHOLD_PATH, + psm_gconf_cb, + &psm_threshold_gconf_cb_id) == FALSE) + goto EXIT; + + /* get_psm_state */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_PSM_STATE_GET, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + psm_state_get_dbus_cb) == NULL) + goto EXIT; + + /* get_psm_mode */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_PSM_MODE_GET, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + psm_state_get_dbus_cb) == NULL) + goto EXIT; + +EXIT: + return NULL; +} + +/** + * Exit function for the power saving mode module + * + * @todo D-Bus unregistration + * + * @param module Unused + */ +G_MODULE_EXPORT void g_module_unload(GModule *module); +void g_module_unload(GModule *module) +{ + (void)module; + + /* Remove triggers/filters from datapipes */ + remove_output_trigger_from_datapipe(&thermal_state_pipe, + thermal_state_trigger); + remove_output_trigger_from_datapipe(&battery_level_pipe, + battery_level_trigger); + + return; +} diff --git a/modules/powersavemode.h b/modules/powersavemode.h new file mode 100644 index 00000000..d13b96ed --- /dev/null +++ b/modules/powersavemode.h @@ -0,0 +1,42 @@ +/** + * @file powersavemode.h + * Headers for the power saving mode module + *

+ * Copyright © 2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _POWERSAVEMODE_H_ +#define _POWERSAVEMODE_H_ + +/** Default Power Saving Mode; boolean */ +#define DEFAULT_POWER_SAVING_MODE FALSE + +/** Default PSM threshold as battery percentage; integer */ +#define DEFAULT_PSM_THRESHOLD 10 + +/** Path to the GConf settings for energy management */ +#ifndef MCE_GCONF_EM_PATH +#define MCE_GCONF_EM_PATH "/system/osso/dsm/energymanagement" +#endif /* MCE_GCONF_EM_PATH */ + +/** Path to the power saving mode GConf setting */ +#define MCE_GCONF_PSM_PATH MCE_GCONF_EM_PATH "/enable_power_saving" +/** Path to the forced power saving mode GConf setting */ +#define MCE_GCONF_FORCED_PSM_PATH MCE_GCONF_EM_PATH "/force_power_saving" +/** Path to the power save mode threshold GConf setting */ +#define MCE_GCONF_PSM_THRESHOLD_PATH MCE_GCONF_EM_PATH "/psm_threshold" + +#endif /* _POWERSAVEMODE_H_ */ diff --git a/modules/proximity.c b/modules/proximity.c new file mode 100644 index 00000000..3e1e7223 --- /dev/null +++ b/modules/proximity.c @@ -0,0 +1,711 @@ +/** + * @file proximity.c + * Proximity sensor module + *

+ * Copyright © 2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * @author Tuomo Tanskanen + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include +#include +#include /* g_access */ + +#include /* O_NONBLOCK */ +#include /* R_OK */ +#include /* free() */ + +#include /* cal_init(), cal_read_block(), + * cal_finish(), + * struct cal + */ + +#include "mce.h" +#include "proximity.h" + +#include "mce-io.h" /* mce_read_chunk_from_file() */ +#include "mce-hal.h" /* get_product_id(), + * product_id_t + */ +#include "mce-log.h" /* mce_log(), LL_* */ +#include "mce-dbus.h" /* Direct: + * --- + * mce_dbus_handler_add(), + * dbus_send_message(), + * dbus_new_method_reply(), + * dbus_new_signal(), + * dbus_message_append_args(), + * dbus_message_get_no_reply(), + * dbus_message_unref(), + * DBusMessage, + * DBUS_MESSAGE_TYPE_METHOD_CALL, + * DBUS_TYPE_INVALID, + * dbus_bool_t + * + * Indirect: + * --- + * MCE_REQUEST_IF + */ +#include "datapipe.h" /* execute_datapipe(), + * append_input_trigger_to_datapipe(), + * remove_input_trigger_from_datapipe() + */ + +/** Request enabling of proximity sensor; reference counted */ +#define MCE_REQ_PS_ENABLE "req_proximity_sensor_enable" + +/** Request disabling of proximity sensor; reference counted */ +#define MCE_REQ_PS_DISABLE "req_proximity_sensor_disable" + +/** Maximum number of monitored proximity sensor owners */ +#define PS_MAX_MONITORED 16 + +/** Module name */ +#define MODULE_NAME "proximity" + +/** Functionality provided by this module */ +static const gchar *const provides[] = { MODULE_NAME, NULL }; + +/** Module information */ +G_MODULE_EXPORT module_info_struct module_info = { + /** Name of the module */ + .name = MODULE_NAME, + /** Module provides */ + .provides = provides, + /** Module priority */ + .priority = 100 +}; + +/** Proximity sensor type */ +typedef enum { + /** Sensor type unset */ + PS_TYPE_UNSET = -1, + /** No sensor available */ + PS_TYPE_NONE = 0, + /** Dipro (BH1770GLC/SFH7770) type sensor */ + PS_TYPE_DIPRO = 1, + /** + * Avago (APDS990x (QPDS-T900)) type sensor */ + PS_TYPE_AVAGO = 2 +} ps_type_t; + +/** ID for the proximity sensor I/O monitor */ +static gconstpointer proximity_sensor_iomon_id = NULL; + +/** Path to the proximity sensor device file entry */ +static const gchar *ps_device_path = NULL; +/** Path to the proximity sensor enable/disable file entry */ +static const gchar *ps_enable_path = NULL; +/** Path to the first proximity sensor calibration point sysfs entry */ +static const gchar *ps_calib0_path = NULL; +/** Path to the second proximity sensor calibration point sysfs entry */ +static const gchar *ps_calib1_path = NULL; + +/** Proximity threshold */ +static hysteresis_t *ps_threshold = NULL; + +/** Last proximity sensor state */ +static cover_state_t old_proximity_sensor_state = COVER_UNDEF; + +/** Cached call state */ +static call_state_t call_state = CALL_STATE_INVALID; + +/** Cached alarm UI state */ +static alarm_ui_state_t alarm_ui_state = MCE_ALARM_UI_INVALID_INT32; + +/** External reference count for proximity sensor */ +static guint ps_external_refcount = 0; + +/** List of monitored proximity sensor owners */ +static GSList *proximity_sensor_owner_monitor_list = NULL; + +/** + * Get the proximity sensor type + * + * @return The sensor-type + */ +static ps_type_t get_ps_type(void) +{ + static ps_type_t ps_type = PS_TYPE_UNSET; + + /* If we have the sensor-type already, return it */ + if (ps_type != PS_TYPE_UNSET) + goto EXIT; + + if (g_access(PS_DEVICE_PATH_AVAGO, R_OK) == 0) { + ps_type = PS_TYPE_AVAGO; + ps_device_path = PS_DEVICE_PATH_AVAGO; + ps_enable_path = PS_PATH_AVAGO_ENABLE; + } else if (g_access(PS_DEVICE_PATH_DIPRO, R_OK) == 0) { + ps_type = PS_TYPE_DIPRO; + ps_device_path = PS_DEVICE_PATH_DIPRO; + ps_calib0_path = PS_CALIB_PATH_DIPRO; + ps_threshold = &dipro_ps_threshold_dipro; + } else { + /* Device either has no proximity sensor, + * or handles it through gpio-keys + */ + ps_type = PS_TYPE_NONE; + ps_device_path = NULL; + } + + mce_log(LL_DEBUG, "Proximity sensor-type: %d", ps_type); + +EXIT: + return ps_type; +} + +/** + * Enable proximity sensor + */ +static void enable_proximity_sensor(void) +{ + if (ps_enable_path != NULL) + mce_write_string_to_file(ps_enable_path, "1"); +} + +/** + * Disable proximity sensor + */ +static void disable_proximity_sensor(void) +{ + if (ps_enable_path != NULL) + mce_write_string_to_file(ps_enable_path, "0"); +} + +/** + * Calibrate the proximity sensor using calibration values from CAL + */ +static void calibrate_ps(void) +{ + struct cal *cal_data = NULL; + + /* If we don't have any calibration points, don't bother */ + if ((ps_calib0_path == NULL) && (ps_calib1_path == NULL)) + goto EXIT; + + /* Retrieve the calibration data stored in CAL */ + if (cal_init(&cal_data) >= 0) { + void *ptr = NULL; + unsigned long len; + int retval; + + if ((retval = cal_read_block(cal_data, PS_CALIB_IDENTIFIER, + &ptr, &len, 0)) == 0) { + guint32 *ps_calib = ptr; + + /* Correctness checks */ + if ((len == sizeof (guint32)) || + (len == (2 * sizeof (guint32)))) { + /* Write calibration values */ + if (ps_calib0_path != NULL) + mce_write_number_string_to_file(ps_calib0_path, ps_calib[0], NULL, TRUE, TRUE); + + if (ps_calib1_path != NULL) + mce_write_number_string_to_file(ps_calib1_path, ps_calib[1], NULL, TRUE, TRUE); + } else { + mce_log(LL_ERR, + "Received incorrect number of " + "proximity sensor " + "calibration values from CAL"); + } + + free(ptr); + } else { + mce_log(LL_ERR, + "cal_read_block() (ps_calib) failed; " + "retval: %d", + retval); + } + + cal_finish(cal_data); + } else { + mce_log(LL_ERR, + "cal_init() failed"); + } + +EXIT: + return; +} + +/** + * I/O monitor callback for the proximity sensor (Avago) + * + * @param data The new data + * @param bytes_read Unused + */ +static void proximity_sensor_avago_cb(gpointer data, gsize bytes_read) +{ + cover_state_t proximity_sensor_state = COVER_UNDEF; + struct avago_ps *ps; + + ps = data; + + /* Don't process invalid reads */ + if (bytes_read != sizeof (struct avago_ps)) { + goto EXIT; + } + + if ((ps->status & APDS990X_PS_UPDATED) == 0) + goto EXIT; + + if (ps->ps != 0) + proximity_sensor_state = COVER_CLOSED; + else + proximity_sensor_state = COVER_OPEN; + + if (old_proximity_sensor_state == proximity_sensor_state) + goto EXIT; + + old_proximity_sensor_state = proximity_sensor_state; + + (void)execute_datapipe(&proximity_sensor_pipe, + GINT_TO_POINTER(proximity_sensor_state), + USE_INDATA, CACHE_INDATA); + + old_proximity_sensor_state = proximity_sensor_state; + +EXIT: + return; +} + +/** + * I/O monitor callback for the proximity sensor (Dipro) + * + * @param data The new data + * @param bytes_read Unused + */ +static void proximity_sensor_dipro_cb(gpointer data, gsize bytes_read) +{ + cover_state_t proximity_sensor_state = COVER_UNDEF; + struct dipro_ps *ps; + + ps = data; + + /* Don't process invalid reads */ + if (bytes_read != sizeof (struct dipro_ps)) { + goto EXIT; + } + + if (old_proximity_sensor_state == COVER_UNDEF) { + if (ps->led1 < ps_threshold->threshold_rising) + proximity_sensor_state = COVER_OPEN; + else + proximity_sensor_state = COVER_CLOSED; + } else if (ps->led1 > ps_threshold->threshold_rising) { + proximity_sensor_state = COVER_CLOSED; + } else if (ps->led1 < ps_threshold->threshold_falling) { + proximity_sensor_state = COVER_OPEN; + } else { + proximity_sensor_state = old_proximity_sensor_state; + goto EXIT; + } + + if (old_proximity_sensor_state == proximity_sensor_state) + goto EXIT; + + old_proximity_sensor_state = proximity_sensor_state; + + (void)execute_datapipe(&proximity_sensor_pipe, + GINT_TO_POINTER(proximity_sensor_state), + USE_INDATA, CACHE_INDATA); + + old_proximity_sensor_state = proximity_sensor_state; + +EXIT: + return; +} + +/** + * Update the proximity state (Avago) + * + * @note Only gives reasonable readings when the proximity sensor is enabled + * @return TRUE on success, FALSE on failure + */ +static gboolean update_proximity_sensor_state_avago(void) +{ + cover_state_t proximity_sensor_state; + gboolean status = FALSE; + struct avago_ps *ps; + void *tmp = NULL; + gssize len = sizeof (struct avago_ps); + + if (mce_read_chunk_from_file(ps_device_path, &tmp, &len, + 0, -1) == FALSE) + goto EXIT; + + if (len != sizeof (struct avago_ps)) { + mce_log(LL_ERR, + "Short read from `%s'", + ps_device_path); + g_free(tmp); + goto EXIT; + } + + ps = (struct avago_ps *)tmp; + + if ((ps->status & APDS990X_PS_UPDATED) == 0) + goto EXIT2; + + if (ps->ps != 0) + proximity_sensor_state = COVER_CLOSED; + else + proximity_sensor_state = COVER_OPEN; + + old_proximity_sensor_state = proximity_sensor_state; + + (void)execute_datapipe(&proximity_sensor_pipe, + GINT_TO_POINTER(proximity_sensor_state), + USE_INDATA, CACHE_INDATA); + +EXIT2: + g_free(tmp); + + status = TRUE; + +EXIT: + return status; +} + +/** + * Update the proximity state (Dipro) + * + * @note Only gives reasonable readings when the proximity sensor is enabled + * @return TRUE on success, FALSE on failure + */ +static gboolean update_proximity_sensor_state_dipro(void) +{ + cover_state_t proximity_sensor_state; + gboolean status = FALSE; + struct dipro_ps *ps; + void *tmp = NULL; + gssize len = sizeof (struct dipro_ps); + + if (mce_read_chunk_from_file(ps_device_path, &tmp, &len, + 0, -1) == FALSE) + goto EXIT; + + if (len != sizeof (struct dipro_ps)) { + mce_log(LL_ERR, + "Short read from `%s'", + ps_device_path); + g_free(tmp); + goto EXIT; + } + + ps = (struct dipro_ps *)tmp; + + if (ps->led1 < ps_threshold->threshold_rising) + proximity_sensor_state = COVER_OPEN; + else + proximity_sensor_state = COVER_CLOSED; + + old_proximity_sensor_state = proximity_sensor_state; + + (void)execute_datapipe(&proximity_sensor_pipe, + GINT_TO_POINTER(proximity_sensor_state), + USE_INDATA, CACHE_INDATA); + + g_free(tmp); + + status = TRUE; + +EXIT: + return status; +} + +/** + * Update the proximity monitoring + */ +static void update_proximity_monitor(void) +{ + if (get_ps_type() == PS_TYPE_NONE) + goto EXIT; + + if ((ps_external_refcount > 0) || + (call_state == CALL_STATE_RINGING) || + (call_state == CALL_STATE_ACTIVE) || + (alarm_ui_state == MCE_ALARM_UI_VISIBLE_INT32) || + (alarm_ui_state == MCE_ALARM_UI_RINGING_INT32)) { + /* Register proximity sensor I/O monitor */ + if (proximity_sensor_iomon_id == NULL) { + (void)enable_proximity_sensor(); + + /* FIXME: is code forking the only way to do these? */ + switch (get_ps_type()) { + case PS_TYPE_AVAGO: + (void)update_proximity_sensor_state_avago(); + + if ((proximity_sensor_iomon_id = mce_register_io_monitor_chunk(-1, ps_device_path, MCE_IO_ERROR_POLICY_WARN, G_IO_IN | G_IO_PRI | G_IO_ERR, FALSE, proximity_sensor_avago_cb, sizeof (struct avago_ps))) == NULL) + goto EXIT; + break; + + case PS_TYPE_DIPRO: + (void)update_proximity_sensor_state_dipro(); + + if ((proximity_sensor_iomon_id = mce_register_io_monitor_chunk(-1, ps_device_path, MCE_IO_ERROR_POLICY_WARN, G_IO_IN | G_IO_PRI | G_IO_ERR, FALSE, proximity_sensor_dipro_cb, sizeof (struct dipro_ps))) == NULL) + goto EXIT; + break; + + default: + break; + } + } + } else { + /* Unregister proximity sensor I/O monitor */ + disable_proximity_sensor(); + mce_unregister_io_monitor(proximity_sensor_iomon_id); + proximity_sensor_iomon_id = NULL; + } + +EXIT: + return; +} + +/** + * Handle call state change + * + * @param data The call state stored in a pointer + */ +static void call_state_trigger(gconstpointer const data) +{ + call_state = GPOINTER_TO_INT(data); + + update_proximity_monitor(); +} + +/** + * Handle alarm UI state change + * + * @param data The alarm state stored in a pointer + */ +static void alarm_ui_state_trigger(gconstpointer const data) +{ + alarm_ui_state = GPOINTER_TO_INT(data); + + update_proximity_monitor(); +} + +/** + * D-Bus callback used for reference counting proximity sensor enabling; + * if the requesting process exits, immediately decrease the refcount + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean proximity_sensor_owner_monitor_dbus_cb(DBusMessage *const msg) +{ + gboolean status = FALSE; + const gchar *old_name; + const gchar *new_name; + const gchar *service; + gssize retval; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + /* Extract result */ + if (dbus_message_get_args(msg, &error, + DBUS_TYPE_STRING, &service, + DBUS_TYPE_STRING, &old_name, + DBUS_TYPE_STRING, &new_name, + DBUS_TYPE_INVALID) == FALSE) { + mce_log(LL_ERR, + "Failed to get argument from %s.%s; %s", + "org.freedesktop.DBus", "NameOwnerChanged", + error.message); + dbus_error_free(&error); + goto EXIT; + } + + /* Remove the name monitor for the CABC mode */ + retval = mce_dbus_owner_monitor_remove(old_name, &proximity_sensor_owner_monitor_list); + + if (retval == -1) { + mce_log(LL_INFO, + "Failed to remove name owner monitoring for `%s'", + old_name); + } else { + ps_external_refcount = retval; + + if (ps_external_refcount == 0) + update_proximity_monitor(); + } + + status = TRUE; + +EXIT: + return status; +} + +/** + * D-Bus callback for the proximity sensor enabling method call + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean proximity_sensor_enable_req_dbus_cb(DBusMessage *const msg) +{ + dbus_bool_t no_reply = dbus_message_get_no_reply(msg); + const gchar *sender = dbus_message_get_sender(msg); + gboolean status = FALSE; + gssize retval; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + mce_log(LL_DEBUG, + "Received proximity sensor enable request from %s", + (sender == NULL) ? "(unknown)" : sender); + + retval = mce_dbus_owner_monitor_add(sender, proximity_sensor_owner_monitor_dbus_cb, &proximity_sensor_owner_monitor_list, PS_MAX_MONITORED); + + if (retval == -1) { + mce_log(LL_INFO, + "Failed to add name owner monitoring for `%s'", + sender); + } else { + ps_external_refcount = retval; + + if (ps_external_refcount == 1) + update_proximity_monitor(); + } + + if (no_reply == FALSE) { + DBusMessage *reply = dbus_new_method_reply(msg); + + status = dbus_send_message(reply); + } else { + status = TRUE; + } + + return status; +} + +/** + * D-Bus callback for the proximity sensor disabling method call + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean proximity_sensor_disable_req_dbus_cb(DBusMessage *const msg) +{ + dbus_bool_t no_reply = dbus_message_get_no_reply(msg); + const gchar *sender = dbus_message_get_sender(msg); + gboolean status = FALSE; + gssize retval; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + mce_log(LL_DEBUG, + "Received proximity sensor disable request from %s", + (sender == NULL) ? "(unknown)" : sender); + + retval = mce_dbus_owner_monitor_remove(sender, + &proximity_sensor_owner_monitor_list); + + if (retval == -1) { + mce_log(LL_INFO, + "Failed to remove name owner monitoring for `%s'", + sender); + } else { + ps_external_refcount = retval; + + if (ps_external_refcount == 0) + update_proximity_monitor(); + } + + if (no_reply == FALSE) { + DBusMessage *reply = dbus_new_method_reply(msg); + + status = dbus_send_message(reply); + } else { + status = TRUE; + } + + return status; +} + +/** + * Init function for the proximity sensor module + * + * @todo XXX status needs to be set on error! + * + * @param module Unused + * @return NULL on success, a string with an error message on failure + */ +G_MODULE_EXPORT const gchar *g_module_check_init(GModule *module); +const gchar *g_module_check_init(GModule *module) +{ + (void)module; + + /* Append triggers/filters to datapipes */ + append_input_trigger_to_datapipe(&call_state_pipe, + call_state_trigger); + append_input_trigger_to_datapipe(&alarm_ui_state_pipe, + alarm_ui_state_trigger); + + /* req_proximity_sensor_enable */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_REQ_PS_ENABLE, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + proximity_sensor_enable_req_dbus_cb) == NULL) + goto EXIT; + + /* req_proximity_sensor_disable */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_REQ_PS_DISABLE, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + proximity_sensor_disable_req_dbus_cb) == NULL) + goto EXIT; + + if (get_ps_type() != PS_TYPE_NONE) { + /* Calibrate the proximity sensor */ + calibrate_ps(); + } + + ps_external_refcount = 0; + +EXIT: + return NULL; +} + +/** + * Exit function for the proximity sensor module + * + * @param module Unused + */ +G_MODULE_EXPORT void g_module_unload(GModule *module); +void g_module_unload(GModule *module) +{ + (void)module; + + /* Remove triggers/filters from datapipes */ + remove_input_trigger_from_datapipe(&alarm_ui_state_pipe, + alarm_ui_state_trigger); + remove_input_trigger_from_datapipe(&call_state_pipe, + call_state_trigger); + + /* Unregister I/O monitors */ + mce_unregister_io_monitor(proximity_sensor_iomon_id); + + return; +} diff --git a/modules/proximity.h b/modules/proximity.h new file mode 100644 index 00000000..ef236424 --- /dev/null +++ b/modules/proximity.h @@ -0,0 +1,102 @@ +/** + * @file proximity.h + * Headers for the proximity sensor module + *

+ * Copyright © 2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * @author Tuomo Tanskanen + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _PROXIMITY_H_ +#define _PROXIMITY_H_ + +/** + * Paths for the Avago (APDS990x (QPDS-T900)) proximity sensor + */ + +/** Device path for the Avago PS */ +#define PS_DEVICE_PATH_AVAGO "/dev/apds990x0" + +/** Struct for the Avago data */ +struct avago_ps { + /** The filtered ambient light in lux */ + guint32 lux; /* 10x scale */ + /** The raw ambient light in lux */ + guint32 lux_raw; /* 10x scale */ + /** The filtered proximity */ + guint16 ps; + /** The raw proximity */ + guint16 ps_raw; + /** The sensor status */ + guint16 status; +} __attribute__((packed)); + +/** Base path to the Avago proximity sensor */ +#define PS_PATH_AVAGO "/sys/class/misc/apds990x0/device" +/** Path to the first calibration point for the Avago ALS */ +/* FIXME: There is no calibration sysfs for Avago */ +/* #define PS_CALIB_PATH_AVAGO PS_PATH_AVAGO "/fixme" */ + +/** Path to enable/disable Avago proximity sensor */ +#define PS_PATH_AVAGO_ENABLE PS_PATH_AVAGO "/prox_enable" + +/** Proximity Sensor status */ +#ifndef APDS990X_PS_UPDATED +/** Sensor has up to date data */ +#define APDS990X_PS_UPDATED 0x8 +#endif /* APDS990X_PS_UPDATED */ + +/** + * Paths for the Dipro (BH1770GLC/SFH7770) proximity sensor + */ + +/** Device path for the Dipro PS */ +#define PS_DEVICE_PATH_DIPRO "/dev/bh1770glc_ps" + +/** Struct for the Dipro data */ +struct dipro_ps { + /** The amount of reflected light from LED 1 */ + guint8 led1; + /** The amount of reflected light from LED 2 */ + guint8 led2; + /** The amount of reflected light from LED 3 */ + guint8 led3; +} __attribute__((packed)); + +/** Base path to the Dipro proximity sensor */ +#define PS_PATH_DIPRO "/sys/class/misc/bh1770glc_ps/device" +/** Path to the first calibration point for the Dipro ALS */ +#define PS_CALIB_PATH_DIPRO PS_PATH_DIPRO "/ps_calib" + +/** Hysteresis levels */ +typedef struct { + /** Rising hysteresis threshold */ + guint threshold_rising; + /** Falling hysteresis threshold */ + guint threshold_falling; +} hysteresis_t; + +/** Proximity threshold for the Dipro proximity sensor */ +static hysteresis_t dipro_ps_threshold_dipro = { + /** Rising hysteresis threshold for Dipro */ + .threshold_rising = 80, + /** Falling hysteresis threshold for Dipro */ + .threshold_falling = 70, +}; + +/** CAL identifier for the proximity sensor calibration values */ +#define PS_CALIB_IDENTIFIER "ps_calib" + +#endif /* _PROXIMITY_H_ */ diff --git a/modules/radiostates.c b/modules/radiostates.c new file mode 100644 index 00000000..8966323c --- /dev/null +++ b/modules/radiostates.c @@ -0,0 +1,400 @@ +/** + * @file radiostates.c + * Radio state module for the Mode Control Entity + *

+ * Copyright © 2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include +#include + +#include /* errno */ +#include /* strlen(), strncmp() */ + +#include /* MCE_RADIO_STATE_MASTER */ + +#include "mce.h" +#include "radiostates.h" /* MCE_RADIO_STATES_PATH */ + +#include "mce-io.h" /* mce_read_number_string_from_file(), + * mce_write_number_string_to_file_atomic() + */ +#include "mce-log.h" /* mce_log(), LL_* */ +#include "mce-dbus.h" /* Direct: + * --- + * mce_dbus_handler_add(), + * dbus_send_message(), + * dbus_new_method_reply(), + * dbus_new_signal(), + * dbus_message_append_args(), + * dbus_message_unref(), + * DBusMessage, + * DBUS_MESSAGE_TYPE_METHOD_CALL, + * DBUS_TYPE_UINT64, + * DBUS_TYPE_INVALID, + * dbus_uint64_t + * + * Indirect: + * --- + * MCE_SIGNAL_IF, + * MCE_SIGNAL_PATH, + * MCE_REQUEST_IF, + * MCE_RADIO_STATES_GET, + * MCE_RADIO_STATES_CHANGE_REQ, + * MCE_RADIO_STATES_SIG + */ +#include "datapipe.h" /* execute_datapipe(), + * append_output_trigger_to_datapipe(), + * remove_output_trigger_from_datapipe() + */ + +/** Module name */ +#define MODULE_NAME "radiostates" + +/** Functionality provided by this module */ +static const gchar *const provides[] = { MODULE_NAME, NULL }; + +/** Module information */ +G_MODULE_EXPORT module_info_struct module_info = { + /** Name of the module */ + .name = MODULE_NAME, + /** Module provides */ + .provides = provides, + /** Module priority */ + .priority = 250 +}; + +/** Real radio states */ +static dbus_uint32_t radio_states = 0; + +/** Active radio states (master switch disables all radios) */ +static dbus_uint32_t active_radio_states = 0; + +/** + * Send the radio states + * + * @param method_call A DBusMessage to reply to; + * pass NULL to send a signal instead + * @return TRUE on success, FALSE on failure + */ +static gboolean send_radio_states(DBusMessage *const method_call) +{ + DBusMessage *msg = NULL; + gboolean status = FALSE; + + mce_log(LL_DEBUG, + "Sending radio states: %x", active_radio_states); + + /* If method_call is set, send a reply, + * otherwise, send a signal + */ + if (method_call != NULL) { + msg = dbus_new_method_reply(method_call); + } else { + /* radio_states_ind */ + msg = dbus_new_signal(MCE_SIGNAL_PATH, MCE_SIGNAL_IF, + MCE_RADIO_STATES_SIG); + } + + /* Append the radio states */ + if (dbus_message_append_args(msg, + DBUS_TYPE_UINT32, &active_radio_states, + DBUS_TYPE_INVALID) == FALSE) { + mce_log(LL_CRIT, + "Failed to append %sargument to D-Bus message " + "for %s.%s", + method_call ? "reply " : "", + method_call ? MCE_REQUEST_IF : + MCE_SIGNAL_IF, + method_call ? MCE_RADIO_STATES_GET : + MCE_RADIO_STATES_SIG); + dbus_message_unref(msg); + goto EXIT; + } + + /* Send the message */ + status = dbus_send_message(msg); + +EXIT: + return status; +} + +/** + * Set the radio states + * + * @param states The raw radio states + * @param mask The raw radio states mask + */ +static void set_radio_states(const dbus_uint32_t states, + const dbus_uint32_t mask) +{ + radio_states = (radio_states & ~mask) | (states & mask); + + if (((mask & MCE_RADIO_STATE_MASTER) != 0) && + ((states & MCE_RADIO_STATE_MASTER) == 0)) { + active_radio_states = states & mask; + } else if ((radio_states & MCE_RADIO_STATE_MASTER) == 0) { + active_radio_states = (active_radio_states & ~mask) | (states & mask); + } else { + active_radio_states = (radio_states & ~mask) | (states & mask); + } +} + +/** + * Save the radio states to persistant storage + * + * @param online_states The online radio states to store + * @param offline_states The offline radio states to store + * @return TRUE on success, FALSE on failure + */ +static gboolean save_radio_states(const gulong online_states, + const gulong offline_states) +{ + gboolean status = FALSE; + + if (mce_is_backup_pending() == TRUE) { + mce_log(LL_WARN, + "Cannot save radio states; backup/restore pending"); + goto EXIT; + } + + status = mce_write_number_string_to_file_atomic(MCE_ONLINE_RADIO_STATES_PATH, online_states); + + if (status == FALSE) + goto EXIT; + + status = mce_write_number_string_to_file_atomic(MCE_OFFLINE_RADIO_STATES_PATH, offline_states); + +EXIT: + return status; +} + +/** + * Restore the radio states from persistant storage + * + * @param[out] online_states A pointer to the restored online radio states + * @param[out] offline_states A pointer to the restored offline radio states + * @return TRUE on success, FALSE on failure + */ +static gboolean restore_radio_states(gulong *online_states, + gulong *offline_states) +{ + gboolean status = FALSE; + + if (mce_is_backup_pending() == TRUE) { + mce_log(LL_INFO, + "Removing stale backup lockfile"); + + if (mce_unlock_backup() == FALSE) { + mce_log(LL_ERR, + "Failed to remove backup lockfile; %s", + g_strerror(errno)); + errno = 0; + } + } + + status = mce_read_number_string_from_file(MCE_OFFLINE_RADIO_STATES_PATH, offline_states, NULL, TRUE, TRUE); + + if (status == FALSE) + goto EXIT; + + status = mce_read_number_string_from_file(MCE_ONLINE_RADIO_STATES_PATH, online_states, NULL, TRUE, TRUE); + +EXIT: + return status; +} + +/** + * D-Bus callback for the get radio states method call + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean get_radio_states_dbus_cb(DBusMessage *const msg) +{ + gboolean status = FALSE; + + mce_log(LL_DEBUG, + "Received get radio states request"); + + /* Try to send a reply that contains the current radio states */ + if (send_radio_states(msg) == FALSE) + goto EXIT; + + status = TRUE; + +EXIT: + return status; +} + +/** + * D-Bus callback for radio states change method call + * + * @todo Decide on error handling policy + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean req_radio_states_change_dbus_cb(DBusMessage *const msg) +{ + dbus_bool_t no_reply = dbus_message_get_no_reply(msg); + dbus_uint32_t old_radio_states = active_radio_states; + gboolean status = FALSE; + dbus_uint32_t states; + dbus_uint32_t mask; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + mce_log(LL_DEBUG, "Received radio states change request"); + + if (dbus_message_get_args(msg, &error, + DBUS_TYPE_UINT32, &states, + DBUS_TYPE_UINT32, &mask, + DBUS_TYPE_INVALID) == FALSE) { + // XXX: should we return an error instead? + mce_log(LL_CRIT, + "Failed to get argument from %s.%s: %s", + MCE_REQUEST_IF, MCE_RADIO_STATES_CHANGE_REQ, + error.message); + dbus_error_free(&error); + goto EXIT; + } + + set_radio_states(states, mask); + + /* If we fail to write the radio states, restore the old states */ + if (save_radio_states((gulong)active_radio_states, + (gulong)radio_states) == FALSE) + set_radio_states(old_radio_states, ~0); + + /* Once we're here radio_states will hold the new radio states, + * or the fallback value + */ + + if (no_reply == FALSE) { + DBusMessage *reply = dbus_new_method_reply(msg); + + status = dbus_send_message(reply); + } else { + status = TRUE; + } + + if (old_radio_states != active_radio_states) { + send_radio_states(NULL); + + /* This is just to make sure that the cache is up to date + * and that all callbacks are called; the trigger inside + * radiostates.c has already had all its actions performed + */ + execute_datapipe(&master_radio_pipe, GINT_TO_POINTER((gint)(radio_states & MCE_RADIO_STATE_MASTER)), USE_INDATA, CACHE_INDATA); + } + +EXIT: + return status; +} + +/** + * Handle master radio state + * + * @param data The master radio state stored in a pointer + */ +static void master_radio_trigger(gconstpointer data) +{ + dbus_uint32_t new_radio_states; + + if (GPOINTER_TO_UINT(data) != 0) + new_radio_states = (radio_states | MCE_RADIO_STATE_MASTER); + else + new_radio_states = (radio_states & ~MCE_RADIO_STATE_MASTER); + + /* If we fail to write the radio states, use the old states */ + if (save_radio_states((gulong)active_radio_states, + (gulong)radio_states) == FALSE) + new_radio_states = radio_states; + + if (radio_states != new_radio_states) { + set_radio_states(new_radio_states, MCE_RADIO_STATE_MASTER); + send_radio_states(NULL); + } +} + +/** + * Init function for the radio states module + * + * @todo XXX status needs to be set on error! + * + * @param module Unused + * @return NULL on success, a string with an error message on failure + */ +G_MODULE_EXPORT const gchar *g_module_check_init(GModule *module); +const gchar *g_module_check_init(GModule *module) +{ + (void)module; + gulong tmp, tmp2; + + /* If we fail to restore the radio states, default to offline */ + if (restore_radio_states(&tmp, &tmp2) == FALSE) { + tmp2 = 0; + tmp = 0; + } + + radio_states = tmp2; + active_radio_states = tmp; + + /* Append triggers/filters to datapipes */ + append_output_trigger_to_datapipe(&master_radio_pipe, + master_radio_trigger); + + /* get_radio_states */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_RADIO_STATES_GET, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + get_radio_states_dbus_cb) == NULL) + goto EXIT; + + /* req_radio_states_change */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_RADIO_STATES_CHANGE_REQ, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + req_radio_states_change_dbus_cb) == NULL) + goto EXIT; + +EXIT: + return NULL; +} + +/** + * Exit function for the radio states module + * + * @todo D-Bus unregistration + * + * @param module Unused + */ +G_MODULE_EXPORT void g_module_unload(GModule *module); +void g_module_unload(GModule *module) +{ + (void)module; + + /* Remove triggers/filters from datapipes */ + remove_output_trigger_from_datapipe(&master_radio_pipe, + master_radio_trigger); + + return; +} diff --git a/modules/radiostates.h b/modules/radiostates.h new file mode 100644 index 00000000..6adb2a51 --- /dev/null +++ b/modules/radiostates.h @@ -0,0 +1,31 @@ +/** + * @file radiostates.h + * Headers for the radio states module + *

+ * Copyright © 2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _RADIOSTATES_H_ +#define _RADIOSTATES_H_ + +#include + +/** Path to online radio states file */ +#define MCE_ONLINE_RADIO_STATES_PATH G_STRINGIFY(MCE_VAR_DIR) "/radio_states.online" +/** Path to offline radio states file */ +#define MCE_OFFLINE_RADIO_STATES_PATH G_STRINGIFY(MCE_VAR_DIR) "/radio_states.offline" + +#endif /* _RADIOSTATES_H_ */ diff --git a/powerkey.c b/powerkey.c new file mode 100644 index 00000000..eb723790 --- /dev/null +++ b/powerkey.c @@ -0,0 +1,721 @@ +/** + * @file powerkey.c + * Power key logic for the Mode Control Entity + *

+ * Copyright © 2004-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include + +#include /* exit(), EXIT_FAILURE */ +#include /* strcmp() */ +#include /* struct input_event */ + +#include "mce.h" /* mce_get_submode_int32(), + * mce_add_submode_int32(), + * mce_rem_submode_int32(), + * submode_pipe, + * system_state_pipe, + * tk_lock_pipe, + * keypress_pipe, + * system_state_t, + * submode_t + */ +#include "powerkey.h" + +#include "mce-log.h" /* mce_log(), LL_* */ +#include "mce-conf.h" /* mce_conf_get_int(), + * mce_conf_get_string() + */ +#include "mce-dbus.h" /* mce_dbus_handler_add(), + * dbus_send_message(), + * dbus_new_method_reply(), + * dbus_message_get_no_reply(), + * dbus_error_init(), + * dbus_error_free(), + * DBUS_MESSAGE_TYPE_METHOD_CALL, + * DBUS_TYPE_BOOLEAN, + * DBUS_TYPE_INVALID, + * DBusMessage, DBusError, + * dbus_bool_t, + */ +#include "mce-dsme.h" /* request_normal_shutdown(), + * request_soft_poweron(), + * request_soft_poweroff(), + * request_powerup(), + * request_reboot() + */ +#include "datapipe.h" /* execute_datapipe(), + * execute_datapipipe_output_triggers(), + * datapipe_get_gint(), + * append_input_trigger_to_datapipe(), + * remove_input_trigger_from_datapipe() + */ + +/** + * The ID of the timeout used when determining + * whether the key press was short or long + */ +static guint powerkey_timeout_cb_id = 0; + +/** + * The ID of the timeout used when determining + * whether the key press was a double press + */ +static guint doublepress_timeout_cb_id = 0; + +/** Time in milliseconds before the key press is considered medium */ +static gint mediumdelay = DEFAULT_POWER_MEDIUM_DELAY; +/** Time in milliseconds before the key press is considered long */ +static gint longdelay = DEFAULT_POWER_LONG_DELAY; +/** Timeout in milliseconds during which key press is considered double */ +static gint doublepressdelay = DEFAULT_POWER_DOUBLE_DELAY; +/** Action to perform on a short key press */ +static poweraction_t shortpressaction = DEFAULT_POWERKEY_SHORT_ACTION; +/** Action to perform on a long key press */ +static poweraction_t longpressaction = DEFAULT_POWERKEY_LONG_ACTION; +/** Action to perform on a double key press */ +static poweraction_t doublepressaction = DEFAULT_POWERKEY_DOUBLE_ACTION; + +/** D-Bus signal to send on short [power] press */ +static gchar *shortpresssignal = NULL; +/** D-Bus signal to send on long [power] press */ +static gchar *longpresssignal = NULL; +/** D-Bus signal to send on double [power] press */ +static gchar *doublepresssignal = NULL; + +static void cancel_powerkey_timeout(void); + +/** + * Generic logic for key presses + * + * @param action The action to take + * @param dbus_signal A D-Bus signal to send + */ +static void generic_powerkey_handler(poweraction_t action, + gchar *dbus_signal) +{ + alarm_ui_state_t alarm_ui_state = + datapipe_get_gint(alarm_ui_state_pipe); + submode_t submode = mce_get_submode_int32(); + + /* Ignore keypress if the alarm UI is visible */ + if ((alarm_ui_state == MCE_ALARM_UI_VISIBLE_INT32) || + (alarm_ui_state == MCE_ALARM_UI_RINGING_INT32)) + goto EXIT; + + switch (action) { + case POWER_DISABLED: + break; + + case POWER_POWEROFF: + default: + /* Do not shutdown if the tklock is active + * or if we're in alarm state + */ + if ((submode & MCE_TKLOCK_SUBMODE) == 0) { + mce_log(LL_WARN, + "Requesting shutdown from " + "powerkey.c: generic_powerkey_handler(); " + "action: %d", + action); + + request_normal_shutdown(); + } + + break; + + case POWER_SOFT_POWEROFF: + /* Only soft poweroff if the tklock isn't active */ + if ((submode & MCE_TKLOCK_SUBMODE) == 0) { + request_soft_poweroff(); + } + + break; + + case POWER_TKLOCK_LOCK: + /* Request enabling of touchscreen/keypad lock + * if the tklock isn't already active + */ + if ((submode & MCE_TKLOCK_SUBMODE) == 0) { + execute_datapipe(&tk_lock_pipe, + GINT_TO_POINTER(LOCK_ON_SILENT), + USE_INDATA, CACHE_INDATA); + } + + break; + + case POWER_TKLOCK_UNLOCK: + /* Request disabling of touchscreen/keypad lock + * if the tklock isn't already inactive + */ + if ((submode & MCE_TKLOCK_SUBMODE) != 0) { + execute_datapipe(&tk_lock_pipe, + GINT_TO_POINTER(LOCK_OFF_SILENT), + USE_INDATA, CACHE_INDATA); + } + + break; + + case POWER_TKLOCK_BOTH: + /* Request enabling of touchscreen/keypad lock + * if the tklock isn't active, + * and disabling if the tklock is active + */ + if ((submode & MCE_TKLOCK_SUBMODE) == 0) { + execute_datapipe(&tk_lock_pipe, + GINT_TO_POINTER(LOCK_ON_SILENT), + USE_INDATA, CACHE_INDATA); + } else { + execute_datapipe(&tk_lock_pipe, + GINT_TO_POINTER(LOCK_OFF_SILENT), + USE_INDATA, CACHE_INDATA); + } + + break; + + case POWER_DBUS_SIGNAL: + /* Send a D-Bus signal */ + dbus_send(NULL, MCE_REQUEST_PATH, + MCE_REQUEST_IF, dbus_signal, + NULL, + DBUS_TYPE_INVALID); + } + +EXIT: + return; +} + +/** + * Timeout callback for double key press + * + * @param data Unused + * @return Always returns FALSE, to disable the timeout + */ +static gboolean doublepress_timeout_cb(gpointer data) +{ + system_state_t system_state = datapipe_get_gint(system_state_pipe); + + (void)data; + + doublepress_timeout_cb_id = 0; + + /* doublepress timer expired without any secondary press; + * thus this was a short press + */ + if (system_state == MCE_STATE_USER) + generic_powerkey_handler(shortpressaction, + shortpresssignal); + + return FALSE; +} + +/** + * Cancel doublepress timeout + */ +static void cancel_doublepress_timeout(void) +{ + /* Remove the timeout source for the [power] double key press handler */ + if (doublepress_timeout_cb_id != 0) { + g_source_remove(doublepress_timeout_cb_id); + doublepress_timeout_cb_id = 0; + } +} + +/** + * Setup doublepress timeout + * + * @return TRUE if the doublepress action was setup, + * FALSE if no action was setup + */ +static gboolean setup_doublepress_timeout(void) +{ + submode_t submode = mce_get_submode_int32(); + gboolean status = FALSE; + + /* Only setup the doublepress timeout when needed */ + if (doublepressaction == POWER_DISABLED) + goto EXIT; + + cancel_doublepress_timeout(); + + /* If the tklock is enabled, but doublepress to unlock is disabled, + * or if the tklock isn't enabled and short press to lock is enabled, + * exit + */ + if (doublepressaction != POWER_DBUS_SIGNAL) { + if ((submode & MCE_TKLOCK_SUBMODE) != 0) { + if ((doublepressaction != POWER_TKLOCK_UNLOCK) && + (doublepressaction != POWER_TKLOCK_BOTH)) + goto EXIT; + } else { + if ((shortpressaction == POWER_TKLOCK_LOCK) || + (shortpressaction == POWER_TKLOCK_BOTH)) + goto EXIT; + } + } + + /* Setup new timeout */ + doublepress_timeout_cb_id = + g_timeout_add(doublepressdelay, doublepress_timeout_cb, NULL); + status = TRUE; + +EXIT: + return status; +} + +/** + * Logic for short key press + * + * @return TRUE on success, FALSE on failure + */ +static gboolean handle_shortpress(void) +{ + gboolean status = FALSE; + + cancel_powerkey_timeout(); + + if (doublepress_timeout_cb_id == 0) { + if (setup_doublepress_timeout() == FALSE) + generic_powerkey_handler(shortpressaction, + shortpresssignal); + } else { + cancel_doublepress_timeout(); + generic_powerkey_handler(doublepressaction, + doublepresssignal); + } + + status = TRUE; + +//EXIT: + return status; +} + +/** + * Logic for long key press + * + * @return TRUE on success, FALSE on failure + */ +static gboolean handle_longpress(void) +{ + system_state_t state = datapipe_get_gint(system_state_pipe); + alarm_ui_state_t alarm_ui_state = + datapipe_get_gint(alarm_ui_state_pipe); + submode_t submode = mce_get_submode_int32(); + gboolean status = TRUE; + + /* Ignore keypress if the alarm UI is visible */ + if ((alarm_ui_state == MCE_ALARM_UI_VISIBLE_INT32) || + (alarm_ui_state == MCE_ALARM_UI_RINGING_INT32)) + goto EXIT; + + /* Ignore if we're already shutting down/rebooting */ + switch (state) { + case MCE_STATE_SHUTDOWN: + case MCE_STATE_REBOOT: + status = FALSE; + break; + + case MCE_STATE_ACTDEAD: + request_powerup(); + break; + + case MCE_STATE_USER: + /* If softoff is enabled, wake up + * Otherwise, perform long press action + */ + if ((submode & MCE_SOFTOFF_SUBMODE)) { + request_soft_poweron(); + } else { + generic_powerkey_handler(longpressaction, + longpresssignal); + } + + break; + + default: + /* If no special cases are needed, + * just do a regular shutdown + */ + mce_log(LL_WARN, + "Requesting shutdown; state: %d", + state); + + request_normal_shutdown(); + break; + } + +EXIT: + return status; +} + +/** + * Timeout callback for long key press + * + * @param data Unused + * @return Always returns FALSE, to disable the timeout + */ +static gboolean powerkey_timeout_cb(gpointer data) +{ + (void)data; + + powerkey_timeout_cb_id = 0; + + handle_longpress(); + + return FALSE; +} + +/** + * Cancel powerkey timeout + */ +static void cancel_powerkey_timeout(void) +{ + /* Remove the timeout source for the [power] long key press handler */ + if (powerkey_timeout_cb_id != 0) { + g_source_remove(powerkey_timeout_cb_id); + powerkey_timeout_cb_id = 0; + } +} + +/** + * Setup powerkey timeout + */ +static void setup_powerkey_timeout(gint powerkeydelay) +{ + cancel_powerkey_timeout(); + + /* Setup new timeout */ + powerkey_timeout_cb_id = + g_timeout_add(powerkeydelay, powerkey_timeout_cb, NULL); +} + +/** + * D-Bus callback for powerkey event triggering + * + * @param msg D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean trigger_powerkey_event_req_dbus_cb(DBusMessage *const msg) +{ + dbus_bool_t no_reply = dbus_message_get_no_reply(msg); + DBusMessageIter iter; + dbus_uint32_t uintval; + dbus_bool_t boolval; + gint argcount = 0; + gint argtype; + gboolean status = FALSE; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + mce_log(LL_DEBUG, "Received [power] button trigger request"); + + if (dbus_message_iter_init(msg, &iter) == FALSE) { + // XXX: should we return an error instead? + mce_log(LL_ERR, + "Too few arguments passed to %s.%s; " + "got %d, expected %d", + MCE_REQUEST_IF, MCE_TRIGGER_POWERKEY_EVENT_REQ, + 0, 1); + goto EXIT; + } + + argtype = dbus_message_iter_get_arg_type(&iter); + argcount++; + + switch (argtype) { + case DBUS_TYPE_BOOLEAN: + dbus_message_iter_get_basic(&iter, &boolval); + uintval = (boolval == TRUE) ? 1 : 0; + break; + + case DBUS_TYPE_UINT32: + dbus_message_iter_get_basic(&iter, &uintval); + + if (uintval > 2) { + mce_log(LL_ERR, + "Incorrect powerkey event passed to %s.%s; " + "ignoring request", + MCE_REQUEST_IF, MCE_TRIGGER_POWERKEY_EVENT_REQ); + goto EXIT; + } + + break; + + default: + mce_log(LL_ERR, + "Argument %d passed to %s.%s has incorrect type", + argcount, + MCE_REQUEST_IF, MCE_TRIGGER_POWERKEY_EVENT_REQ); + goto EXIT; + } + + while (dbus_message_iter_next(&iter) == TRUE) + argcount++; + + if (argcount > 1) { + mce_log(LL_WARN, + "Too many arguments passed to %s.%s; " + "got %d, expected %d -- ignoring extra arguments", + MCE_REQUEST_IF, MCE_TRIGGER_POWERKEY_EVENT_REQ, + argcount, 1); + } + + mce_log(LL_DEBUG, "[power] button event trigger value: %d", uintval); + + cancel_powerkey_timeout(); + cancel_doublepress_timeout(); + + switch (uintval) { + default: + case 0: + /* short press */ + generic_powerkey_handler(shortpressaction, + shortpresssignal); + break; + + case 1: + /* long press */ + handle_longpress(); + break; + + case 2: + /* double press */ + generic_powerkey_handler(doublepressaction, + doublepresssignal); + break; + } + + if (no_reply == FALSE) { + DBusMessage *reply = dbus_new_method_reply(msg); + + status = dbus_send_message(reply); + } else { + status = TRUE; + } + +EXIT: + return status; +} + +/** + * Datapipe trigger for the [power] key + * + * @param data A pointer to the input_event struct + */ +static void powerkey_trigger(gconstpointer const data) +{ + system_state_t system_state = datapipe_get_gint(system_state_pipe); + submode_t submode = mce_get_submode_int32(); + struct input_event const *const *evp; + struct input_event const *ev; + + /* Don't dereference until we know it's safe */ + if (data == NULL) + goto EXIT; + + evp = data; + ev = *evp; + + if ((ev != NULL) && (ev->code == KEY_POWER)) { + /* If set, the [power] key was pressed */ + if (ev->value == 1) { + mce_log(LL_DEBUG, "[power] pressed"); + + /* Are we waiting for a doublepress? */ + if (doublepress_timeout_cb_id != 0) { + handle_shortpress(); + } else if ((system_state == MCE_STATE_ACTDEAD) || + ((submode & MCE_SOFTOFF_SUBMODE) != 0)) { + /* Setup new timeout */ + execute_datapipe_output_triggers(&led_pattern_activate_pipe, MCE_LED_PATTERN_POWER_ON, USE_INDATA); + + /* Shorter delay for startup + * than for shutdown + */ + setup_powerkey_timeout(mediumdelay); + } else { + setup_powerkey_timeout(longdelay); + } + } else if (ev->value == 0) { + mce_log(LL_DEBUG, "[power] released"); + + /* Short key press */ + if (powerkey_timeout_cb_id != 0) { + handle_shortpress(); + + if ((system_state == MCE_STATE_ACTDEAD) || + ((submode & MCE_SOFTOFF_SUBMODE) != 0)) { + execute_datapipe_output_triggers(&led_pattern_deactivate_pipe, MCE_LED_PATTERN_POWER_ON, USE_INDATA); + } + } + } + } + +EXIT: + return; +} + +/** + * Parse the [power] action string + * + * @todo Implement this using string to enum mappings instead, + * to allow for better debugging messages and a generic parser + * + * @param string The string to parse + * @param dbus_signal A D-Bus signal to send + * @param action A pointer to the variable to store the action in + * @return TRUE if the string contained a valid action, + * FALSE if the action was invalid + */ +static gboolean parse_action(char *string, + char **dbus_signal, + poweraction_t *action) +{ + gboolean status = FALSE; + + if (!strcmp(string, POWER_DISABLED_STR)) { + *action = POWER_DISABLED; + } else if (!strcmp(string, POWER_MENU_STR)) { + *action = POWER_MENU; + } else if (!strcmp(string, POWER_POWEROFF_STR)) { + *action = POWER_POWEROFF; + } else if (!strcmp(string, POWER_SOFT_POWEROFF_STR)) { + *action = POWER_SOFT_POWEROFF; + } else if (!strcmp(string, POWER_TKLOCK_LOCK_STR)) { + *action = POWER_TKLOCK_LOCK; + } else if (!strcmp(string, POWER_TKLOCK_UNLOCK_STR)) { + *action = POWER_TKLOCK_UNLOCK; + } else if (!strcmp(string, POWER_TKLOCK_BOTH_STR)) { + *action = POWER_TKLOCK_BOTH; + } else if (!strncmp(string, + POWER_DBUS_SIGNAL_STR, + strlen(POWER_DBUS_SIGNAL_STR))) { + gchar *tmp = string + strlen(POWER_DBUS_SIGNAL_STR); + + if (strlen(tmp) == 0) { + mce_log(LL_ERR, + "No signal name provided to action " + "`dbus-signal-'; ignoring"); + goto EXIT; + } + + *action = POWER_DBUS_SIGNAL; + *dbus_signal = g_strdup(tmp); + } else { + mce_log(LL_WARN, + "Unknown [power] action; " + "using default"); + goto EXIT; + } + + status = TRUE; + +EXIT: + return status; +} + +/** + * Init function for the powerkey component + * + * @return TRUE on success, FALSE on failure + */ +gboolean mce_powerkey_init(void) +{ + gboolean status = FALSE; + gchar *tmp = NULL; + + /* Append triggers/filters to datapipes */ + append_input_trigger_to_datapipe(&keypress_pipe, + powerkey_trigger); + + /* req_trigger_powerkey_event */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_TRIGGER_POWERKEY_EVENT_REQ, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + trigger_powerkey_event_req_dbus_cb) == NULL) + goto EXIT; + + /* Get configuration options */ + longdelay = mce_conf_get_int(MCE_CONF_POWERKEY_GROUP, + MCE_CONF_POWERKEY_LONG_DELAY, + DEFAULT_POWER_LONG_DELAY, + NULL); + mediumdelay = mce_conf_get_int(MCE_CONF_POWERKEY_GROUP, + MCE_CONF_POWERKEY_MEDIUM_DELAY, + DEFAULT_POWER_MEDIUM_DELAY, + NULL); + tmp = mce_conf_get_string(MCE_CONF_POWERKEY_GROUP, + MCE_CONF_POWERKEY_SHORT_ACTION, + "", + NULL); + + /* Since we've set a default, error handling is unnecessary */ + (void)parse_action(tmp, &shortpresssignal, &shortpressaction); + g_free(tmp); + + tmp = mce_conf_get_string(MCE_CONF_POWERKEY_GROUP, + MCE_CONF_POWERKEY_LONG_ACTION, + "", + NULL); + + /* Since we've set a default, error handling is unnecessary */ + (void)parse_action(tmp, &longpresssignal, &longpressaction); + g_free(tmp); + + doublepressdelay = mce_conf_get_int(MCE_CONF_POWERKEY_GROUP, + MCE_CONF_POWERKEY_DOUBLE_DELAY, + DEFAULT_POWER_DOUBLE_DELAY, + NULL); + tmp = mce_conf_get_string(MCE_CONF_POWERKEY_GROUP, + MCE_CONF_POWERKEY_DOUBLE_ACTION, + "", + NULL); + + /* Since we've set a default, error handling is unnecessary */ + (void)parse_action(tmp, &doublepresssignal, &doublepressaction); + g_free(tmp); + + status = TRUE; + +EXIT: + return status; +} + +/** + * Exit function for the powerkey component + * + * @todo D-Bus unregistration + */ +void mce_powerkey_exit(void) +{ + /* Remove triggers/filters from datapipes */ + remove_input_trigger_from_datapipe(&keypress_pipe, + powerkey_trigger); + + /* Remove all timer sources */ + cancel_powerkey_timeout(); + cancel_doublepress_timeout(); + + g_free(doublepresssignal); + g_free(longpresssignal); + g_free(shortpresssignal); + + return; +} diff --git a/powerkey.h b/powerkey.h new file mode 100644 index 00000000..862d51e9 --- /dev/null +++ b/powerkey.h @@ -0,0 +1,105 @@ +/** + * @file powerkey.h + * Headers for the power key logic for the Mode Control Entity + *

+ * Copyright © 2004-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _POWERKEY_H_ +#define _POWERKEY_H_ + +#include + +/** Configuration value used for the disabled policy */ +#define POWER_DISABLED_STR "disabled" +/** Configuration value used for the device menu policy */ +#define POWER_MENU_STR "menu" +/** Configuration value used for poweroff */ +#define POWER_POWEROFF_STR "poweroff" +/** Configuration value used for soft poweroff */ +#define POWER_SOFT_POWEROFF_STR "softpoweroff" +/** Configuration value used for tklock lock */ +#define POWER_TKLOCK_UNLOCK_STR "tklock-unlock" +/** Configuration value used for tklock unlock */ +#define POWER_TKLOCK_LOCK_STR "tklock-lock" +/** Configuration value used for tklock both */ +#define POWER_TKLOCK_BOTH_STR "tklock-both" +/** Configuration value used for D-Bus signal */ +#define POWER_DBUS_SIGNAL_STR "dbus-signal-" + +/** Action to perform on [power] keypress */ +typedef enum { +/** No action */ + POWER_DISABLED = 0, +/** Show device menu */ + POWER_MENU = 1, +/** Default for long press */ + DEFAULT_POWERKEY_LONG_ACTION = 2, +/** Shutdown */ + POWER_POWEROFF = 2, +/** Soft poweroff */ + POWER_SOFT_POWEROFF = 3, +/** Default for short press */ + DEFAULT_POWERKEY_SHORT_ACTION = 4, +/** Lock the TKLock if unlocked */ + POWER_TKLOCK_LOCK = 4, +/** Unlock the TKLock if locked */ + POWER_TKLOCK_UNLOCK = 5, +/** Lock the TKLock if unlocked, unlock the TKLock if locked */ + POWER_TKLOCK_BOTH = 6, +/** Default for double press */ + DEFAULT_POWERKEY_DOUBLE_ACTION = 7, +/** Send a D-Bus signal */ + POWER_DBUS_SIGNAL = 7 +} poweraction_t; + +/** Name of Powerkey configuration group */ +#define MCE_CONF_POWERKEY_GROUP "PowerKey" + +/** Name of configuration key for medium [power] press delay */ +#define MCE_CONF_POWERKEY_MEDIUM_DELAY "PowerKeyMediumDelay" + +/** Name of configuration key for long [power] press delay */ +#define MCE_CONF_POWERKEY_LONG_DELAY "PowerKeyLongDelay" + +/** Name of configuration key for double [power] press delay */ +#define MCE_CONF_POWERKEY_DOUBLE_DELAY "PowerKeyDoubleDelay" + +/** Name of configuration key for short [power] press action */ +#define MCE_CONF_POWERKEY_SHORT_ACTION "PowerKeyShortAction" + +/** Name of configuration key for long [power] press action */ +#define MCE_CONF_POWERKEY_LONG_ACTION "PowerKeyLongAction" + +/** Name of configuration key for double [power] press action */ +#define MCE_CONF_POWERKEY_DOUBLE_ACTION "PowerKeyDoubleAction" + +/** + * Long delay for the [power] button in milliseconds; 1.5 seconds + */ +#define DEFAULT_POWER_LONG_DELAY 1500 + +/** Medium delay for the [power] button in milliseconds; 1 second */ +#define DEFAULT_POWER_MEDIUM_DELAY 1000 + +/** Double press timeout for the [power] button in milliseconds; 0.5 seconds */ +#define DEFAULT_POWER_DOUBLE_DELAY 500 + +/* When MCE is made modular, this will be handled differently */ +gboolean mce_powerkey_init(void); +void mce_powerkey_exit(void); + +#endif /* _POWERKEY_H_ */ diff --git a/radio_states.offline b/radio_states.offline new file mode 100644 index 00000000..110e5006 --- /dev/null +++ b/radio_states.offline @@ -0,0 +1 @@ +0000000007 \ No newline at end of file diff --git a/radio_states.online b/radio_states.online new file mode 100644 index 00000000..110e5006 --- /dev/null +++ b/radio_states.online @@ -0,0 +1 @@ +0000000007 \ No newline at end of file diff --git a/systemui/dbus-names.h b/systemui/dbus-names.h new file mode 100644 index 00000000..9200872b --- /dev/null +++ b/systemui/dbus-names.h @@ -0,0 +1,50 @@ +/** + * @file dbus-names.h + * DBus Interface to the System UI + *

+ * This file is part of osso-systemui-dbus-dev + *

+ * Copyright (C) 2004-2006 Nokia Corporation. + *

+ * Contact person: David Weinehall + * + * These headers are free software; you can redistribute them + * and/or modify them under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * These headers are distributed in the hope that they 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 software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef _SYSTEMUI_DBUS_NAMES_H +#define _SYSTEMUI_DBUS_NAMES_H + +/** The System UI service */ +#define SYSTEMUI_SERVICE "com.nokia.system_ui" +/** The System UI request interface. */ +#define SYSTEMUI_REQUEST_IF "com.nokia.system_ui.request" +/** The System UI signal interface. */ +#define SYSTEMUI_SIGNAL_IF "com.nokia.system_ui.signal" +/** The System UI request path. */ +#define SYSTEMUI_REQUEST_PATH "/com/nokia/system_ui/request" +/** The System UI signal path. */ +#define SYSTEMUI_SIGNAL_PATH "/com/nokia/system_ui/signal" + +/** + * Requests system UI to shut down. + */ +#define SYSTEMUI_QUIT_REQ "quit" + +/** + * Notify everyone that the System UI has started. + */ +#define SYSTEMUI_STARTED_SIG "system_ui_started" + +#endif diff --git a/systemui/tklock-dbus-names.h b/systemui/tklock-dbus-names.h new file mode 100644 index 00000000..24421f13 --- /dev/null +++ b/systemui/tklock-dbus-names.h @@ -0,0 +1,41 @@ +/** + @file tklock-dbus-names.h +

+ Copyright (c) 2005-09 Nokia Corporation + + Contact: David Weinehall +*/ + +#ifndef _SYSTEMUI_TKLOCK_DBUS_NAMES_H +#define _SYSTEMUI_TKLOCK_DBUS_NAMES_H + +#define SYSTEMUI_TKLOCK_OPEN_REQ "tklock_open" +#define SYSTEMUI_TKLOCK_CLOSE_REQ "tklock_close" + +#define TKLOCK_SIGNAL_IF "com.nokia.tklock.signal" +#define TKLOCK_SIGNAL_PATH "/com/nokia/tklock/signal" +#define TKLOCK_MM_KEY_PRESS_SIG "mm_key_press" + +typedef enum +{ + TKLOCK_NONE, + TKLOCK_ENABLE, + TKLOCK_HELP, + TKLOCK_SELECT, + TKLOCK_ONEINPUT, + /* slider screen unlocking ui mode, + * alternative to unconditional unlocking with flicker + */ + TKLOCK_ENABLE_VISUAL +} tklock_mode; + +typedef enum +{ + TKLOCK_UNLOCK = 1, + TKLOCK_RETRY, + TKLOCK_TIMEOUT, + TKLOCK_CLOSED + +} tklock_status; + +#endif diff --git a/tests/fakealarm b/tests/fakealarm new file mode 100755 index 00000000..c8f874b2 --- /dev/null +++ b/tests/fakealarm @@ -0,0 +1,82 @@ +#! /bin/sh +program=fakealarm +version=1.0.2 + +DBUS_PATH=/com/nokia/voland/signal +DBUS_INTERFACE=com.nokia.voland.signal +DBUS_SEND=dbus-send +DBUS_SEND_FLAGS='--system --type=signal' + +usage() +{ + printf "Usage: %s [OPTION]... [STATE]\n" $program + printf "Test MCE by emulating alarm UI state signals\n\n" + + printf " --help display this help and exit\n" + printf " --version output version information and exit\n\n" + + printf "Valid states are:\n\n" + + printf " on-screen\n" + printf " not-ringing\n" + printf " not-on-screen\n" +} + +error() +{ + usage + exit 1 +} + +version() +{ + printf "%s %s\n" $program $version +} + +alarm_dialog_on_screen() +{ +# Signal that the alarm dialog is on screen and alarm is ringing + $DBUS_SEND $DBUS_SEND_FLAGS $DBUS_PATH $DBUS_INTERFACE.visual_reminders_status uint32:0 +} + +alarm_dialog_not_ringing() +{ +# Signal that the alarm dialog is on screen, but alarm is not ringing + $DBUS_SEND $DBUS_SEND_FLAGS $DBUS_PATH $DBUS_INTERFACE.visual_reminders_status uint32:2 +} + +alarm_dialog_not_on_screen() +{ +# Signal that the alarm dialog is not on screen + $DBUS_SEND $DBUS_SEND_FLAGS $DBUS_PATH $DBUS_INTERFACE.visual_reminders_status uint32:1 +} + +[ $# -eq 0 ] && error + +# setup command line options +while ! [ $# -eq 0 ]; do + case $1 in + on-screen) + alarm_dialog_on_screen + ;; + not-ringing) + alarm_dialog_not_ringing + ;; + not-on-screen) + alarm_dialog_not_on_screen + ;; + --help) + usage + exit 0 + ;; + --version) + version + exit 0 + ;; + *) + usage + exit 1 + ;; + esac + shift +done diff --git a/tests/fakecharger b/tests/fakecharger new file mode 100755 index 00000000..391070cf --- /dev/null +++ b/tests/fakecharger @@ -0,0 +1,145 @@ +#! /bin/sh +program=fakecharger +version=1.1.2 + +DBUS_PATH=/com/nokia/bme/signal +DBUS_INTERFACE=com.nokia.bme.signal +DBUS_SEND=dbus-send +DBUS_SEND_FLAGS='--system --type=signal' + +usage() +{ + printf "Usage: %s [OPTION]... [TEST]...\n" $program + printf "Test MCE and Battery status-bar applet by emulating\n" + printf "charger plugging events\n\n" + + printf " --help display this help and exit\n" + printf " --version output version information and exit\n\n" + + printf "Valid tests are:\n\n" + + printf " charger_charging_on\n" + printf " charger_charging_off\n" + printf " charger_connected\n" + printf " charger_disconnected\n" + printf " battery_full\n" + printf " battery_ok\n" + printf " battery_low\n" + printf " battery_empty\n" + printf " battery_state_changed CURRENT_BARS MAX_BARS\n" +} + +error() +{ + usage + exit 1 +} + +version() +{ + printf "%s %s\n" $program $version +} + +charger_charging_on() +{ +# Fake that charging started + $DBUS_SEND $DBUS_SEND_FLAGS $DBUS_PATH $DBUS_INTERFACE.charger_charging_on +} + +charger_charging_off() +{ +# Fake that charging stopped + $DBUS_SEND $DBUS_SEND_FLAGS $DBUS_PATH $DBUS_INTERFACE.charger_charging_off +} + +charger_connected() +{ +# Fake that charger was connected + $DBUS_SEND $DBUS_SEND_FLAGS $DBUS_PATH $DBUS_INTERFACE.charger_connected +} + +charger_disconnected() +{ +# Fake that charger was disconnected + $DBUS_SEND $DBUS_SEND_FLAGS $DBUS_PATH $DBUS_INTERFACE.charger_disconnected +} + +battery_full() +{ +# Fake that the battery is full + $DBUS_SEND $DBUS_SEND_FLAGS $DBUS_PATH $DBUS_INTERFACE.battery_full +} + +battery_ok() +{ +# Fake that the battery is ok + $DBUS_SEND $DBUS_SEND_FLAGS $DBUS_PATH $DBUS_INTERFACE.battery_ok +} + +battery_low() +{ +# Fake that the battery is low + $DBUS_SEND $DBUS_SEND_FLAGS $DBUS_PATH $DBUS_INTERFACE.battery_low +} + +battery_empty() +{ +# Fake that the battery is empty + $DBUS_SEND $DBUS_SEND_FLAGS $DBUS_PATH $DBUS_INTERFACE.battery_empty +} + +battery_state_changed() +{ +# Fake that the number of battery bars changed + $DBUS_SEND $DBUS_SEND_FLAGS $DBUS_PATH $DBUS_INTERFACE.battery_state_changed uint32:$1 uint32:$2 +} + +[ $# -eq 0 ] && error + +# setup command line options +while ! [ $# -eq 0 ]; do + case $1 in + charger_charging_on) + eval ${1} + ;; + charger_charging_off) + eval ${1} + ;; + charger_connected) + eval ${1} + ;; + charger_disconnected) + eval ${1} + ;; + battery_full) + eval ${1} + ;; + battery_ok) + eval ${1} + ;; + battery_low) + eval ${1} + ;; + battery_empty) + eval ${1} + ;; + battery_state_changed) + eval ${1} ${2} ${3} + shift + shift + ;; + --help) + usage + exit 0 + ;; + --version) + version + exit 0 + ;; + *) + usage + exit 1 + ;; + esac + shift +done diff --git a/tests/inhibit b/tests/inhibit new file mode 100644 index 00000000..36f9f57a --- /dev/null +++ b/tests/inhibit @@ -0,0 +1,155 @@ +#! /bin/sh +program=inhibit +version=1.0.0 + +GCONFTOOL=gconftool-2 + +GCONF_DISPLAY_BLANKING_INHIBIT_MODE_PATH=/system/osso/dsm/display/inhibit_blank_mode + +usage() +{ + printf "Usage: %s [OPTION]... [MODE]...\n" $program + printf "Get/set display dimming and blanking inhibit mode\n\n" + + printf " --help display this help and exit\n" + printf " --version output version information and exit\n\n" + + printf "Valid modes are:\n\n" + + printf " no-inhibit\n" + printf " stay-on-with-charger\n" + printf " stay-dim-with-charger\n" + printf " stay-on\n" + printf " stay-dim\n" +} + +version() +{ + printf "%s %s\n" $program $version +} + +gconf_unset_key() +{ + $GCONFTOOL --unset $1 +} + +gconf_set() +{ + keytype=$($GCONFTOOL --get-type $1 2> /dev/null) + + # If the key is not set, accept any keytype + if [ x"$keytype" = x"" ]; then + keytype=$2 + fi + + if [ x"$keytype" != x"$2" ]; then + abort "Got type $keytype for GConf key $1, expected $2" + fi + + $GCONFTOOL --set --type=$2 $1 $3 + sleep 1 +} + +gconf_set_int() +{ + gconf_set $1 int $2 +} + +gconf_get() +{ + keytype=$($GCONFTOOL --get-type $2 2> /dev/null) + + status=0 + + # if the key is not set, don't bother reading it + if [ x"$keytype" = x"" ]; then + status=42 + retval=0 + return + fi + + if [ x"$keytype" != x"$1" ]; then + abort "Got type $keytype for GConf key $2, expected $1" + fi + + retval=$($GCONFTOOL --get $2 2> /dev/null) +} + +gconf_get_int() +{ + gconf_get int $1 + # This looks stupid; it's just to allow altering + # of the return value + tmp=$retval + retval=$tmp +} + +status() +{ + # Get current value + gconf_get_int $GCONF_DISPLAY_BLANKING_INHIBIT_MODE_PATH + + case $retval in + 0) + mode="no inhibit" + ;; + 1) + mode="inhibit blank+dim if charger is connected" + ;; + 2) + mode="inhibit blank if charger is connected" + ;; + 3) + mode="inhibit blank unconditionally" + ;; + 4) + mode="inhibit dim unconditionally" + ;; + *) + mode="invalid" + ;; + esac + + printf "Inhibit mode: %s\n" "$mode" +} + +[ $# -eq 0 ] && ( status; exit 0 ) + +# setup command line options +while ! [ $# -eq 0 ]; do + case $1 in + no-inhibit) + # do not inhibit blanking or dimming + gconf_set_int $GCONF_DISPLAY_BLANKING_INHIBIT_MODE_PATH 0 + ;; + stay-on-with-charger) + # if charger is connected, inhibit blanking and dimming + gconf_set_int $GCONF_DISPLAY_BLANKING_INHIBIT_MODE_PATH 1 + ;; + stay-dim-with-charger) + # if charger is connected, inhibit blanking, but not dimming + gconf_set_int $GCONF_DISPLAY_BLANKING_INHIBIT_MODE_PATH 2 + ;; + stay-on) + # inhibit blanking and dimming + gconf_set_int $GCONF_DISPLAY_BLANKING_INHIBIT_MODE_PATH 3 + ;; + stay-dim) + # inhibit blanking, but not dimming + gconf_set_int $GCONF_DISPLAY_BLANKING_INHIBIT_MODE_PATH 4 + ;; + --help) + usage + exit 0 + ;; + --version) + version + exit 0 + ;; + *) + usage + exit 1 + ;; + esac + shift +done diff --git a/tests/ledtest b/tests/ledtest new file mode 100644 index 00000000..00919f80 --- /dev/null +++ b/tests/ledtest @@ -0,0 +1,50 @@ +#! /bin/sh +DBUS_PATH=/com/nokia/mce/request +DBUS_DEST=com.nokia.mce +DBUS_INTERFACE=$DBUS_DEST.request +DBUS_SEND=dbus-send +DBUS_SEND_FLAGS_REPLY='--system --type=method_call --print-reply' +DBUS_SEND_FLAGS='--system --type=method_call' + +# dbus-send --system --type=method_call --print-reply --dest=com.nokia.mce /com/nokia/mce/request com.nokia.mce.request. +echo Enabling led +$DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_led_enable + +echo Activating poweron led pattern # priority 1 +$DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_led_pattern_activate string:PatternPowerOn + +# sleep for 10 seconds +sleep 10 + +echo Activating error led pattern # priority 0 +$DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_led_pattern_activate string:PatternError + +# sleep for 10 seconds +sleep 10 + +echo Activating poweroff led pattern # priority 2 -- should not make a difference +$DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_led_pattern_activate string:PatternPowerOff + +# sleep for 10 seconds +sleep 10 + +echo Deactivating error led pattern # should cause poweron to be visible +$DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_led_pattern_deactivate string:PatternError + +# sleep for 10 seconds +sleep 10 + +echo Deactivating poweron led pattern # should cause poweroff to be visible +$DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_led_pattern_deactivate string:PatternPowerOn + +# sleep for 10 seconds +sleep 10 + +echo Deactivating poweroff led pattern +$DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_led_pattern_deactivate string:PatternPowerOff + +# sleep for 10 seconds +sleep 10 + +echo Disabling led +$DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_led_disable diff --git a/tests/mcetorture b/tests/mcetorture new file mode 100755 index 00000000..db574a1e --- /dev/null +++ b/tests/mcetorture @@ -0,0 +1,1066 @@ +#! /bin/sh +program=mcetorture +version=1.8.4 + +GCONFTOOL=gconftool-2 + +DBUS_SEND=dbus-send +DBUS_SEND_FLAGS_REPLY='--system --type=method_call --print-reply' +DBUS_SEND_FLAGS='--system --type=method_call' +DBUS_SEND_SIGNAL_FLAGS='--system --type=signal' + +DBUS_PATH=/com/nokia/mce/request +DBUS_DEST=com.nokia.mce +DBUS_INTERFACE=$DBUS_DEST.request + +logdir=/media/mmc1 +[ -d $logdir ] || logdir=/root + +logfile=$logdir/mcetorture.log +logball=$logdir/dsmelog.tar +memfile=$logdir/mcememlog +mcepid=$(pidof mce) + +syslog=1 +syslogdir=/var/log +[ -f $syslogdir/syslog ] || syslogdir=/var/ftd-log +[ -f $syslogdir/syslog ] || syslog=0 + +KEYBOARD_EVENT_FILE=/dev/input/keypad +POWERBUTTON_EVENT_FILE=/dev/input/pwrbutton +TOUCHSCREEN_EVENT_FILE=/dev/input/ts +GPIOKEYS_EVENT_FILE=/dev/input/gpio-keys + +x=0 +tests=0 +retval= +status=0 + +MCETOOL_FLAGS= + +oneshot=0 +noleakcheck=0 +noabort=0 +verbose=0 +nolog=0 + +blank= +mceinfo= +unblank= +dim= +radiostates= +cabcmode= +callstate= +tklock= +alarm= +battery= +charger= +led= +homeshort= +homelong= +powershort= +powerdouble= +powerlong= +gpiokeyslide= +touchscreen= +powershortdbus= +powerdoubledbus= +powerlongdbus= +gconfbrightness= +gconftimeout= +gconfled= +dbuserrors= + +EVENT_TIMESTAMP="\x48\x67\x98\x45\x5f\x16\x0b\x00" + +EVENT_KEY_TYPE="\x01\x00" # EV_KEY / 0x01 +EVENT_SWITCH_TYPE="\x05\x00" # EV_SW / 0x05 +EVENT_ABS_TYPE="\x03\x00" # EV_ABS / 0x03 + +EVENT_ABS_PRESSURE="\x18\x00" # ABS_PRESSURE / 0x18 +EVENT_BTN_TOUCH="\x4a\x01" # BTN_TOUCH / 0x14a +EVENT_POWER_KEY="\x74\x00" # KEY_POWER / 0x74 +EVENT_HOME_KEY="\x3f\x00" # KEY_F5 [home] / 0x3f +EVENT_KEYPAD_SLIDE="\x0a\x00" # SW_KEYPAD_SLIDE / 0x0a + +EVENT_PRESS_VALUE="\x01\x00\x00\x00" +EVENT_RELEASE_VALUE="\x00\x00\x00\x00" + +GCONF_DISPLAY_BRIGHTNESS_PATH=/system/osso/dsm/display/display_brightness +GCONF_DISPLAY_BLANK_TIMEOUT_PATH=/system/osso/dsm/display/display_blank_timeout +GCONF_DISPLAY_DIM_TIMEOUT_PATH=/system/osso/dsm/display/display_dim_timeout +GCONF_DISPLAY_INHIBIT_BLANK_PATH=/system/osso/dsm/display/inhibit_blank_mode +GCONF_DISPLAY_ADAPTIVE_DIMMING_ENABLED_PATH=/system/osso/dsm/display/adaptive_dimming_enabled +GCONF_DISPLAY_ADAPTIVE_DIMMING_THRESHOLD_PATH=/system/osso/dsm/display/adaptive_dimming_threshold +GCONF_LED_PATTERN_ERROR_PATH=/system/osso/dsm/leds/PatternError +GCONF_BRIGHTNESS_MIN=1 +GCONF_BRIGHTNESS_MAX=5 + +crashcheck() +{ + mcerunpid=$(pidof mce) + + if [ x$mcerunpid == x"" ]; then + printf "MCE (pid %d) crashed\n" $mcepid + return 1 + fi + + if [ $mcepid -ne $(pidof mce) ]; then + printf "MCE (pid %d) crashed and restarted as %d\n" $mcepid $(pidof mce) + return 1 + fi + + return 0 +} + +get_memstats() +{ + size=$(awk '/Size:/ { a += $2 } END { printf a }' /proc/$mcepid/smaps) + rss=$(awk '/Rss:/ { a += $2 }' /proc/$mcepid/smaps) + shared_clean=$(awk '/Shared_Clean:/ { a += $2 } END { printf a }' /proc/$mcepid/smaps) + shared_dirty=$(awk '/Shared_Dirty:/ { a += $2 } END { printf a }' /proc/$mcepid/smaps) + private_clean=$(awk '/Private_Clean:/ { a += $2 } END { printf a }' /proc/$mcepid/smaps) + private_dirty=$(awk '/Private_Dirty/ { a += $2 } END { printf a }' /proc/$mcepid/smaps) + + # Make sure we don't get any unset values + [ x"$size" = x"" ] && size=0 + [ x"$rss" = x"" ] && rss=0 + [ x"$shared_clean" = x"" ] && shared_clean=0 + [ x"$shared_dirty" = x"" ] && shared_dirty=0 + [ x"$private_clean" = x"" ] && private_clean=0 + [ x"$private_dirty" = x"" ] && private_dirty=0 +} + +leakcheck() +{ + if [ $noleakcheck -eq 1 ]; then + return 0 + fi + + old_size=$size + old_rss=$rss + old_shared_clean=$shared_clean + old_shared_dirty=$shared_dirty + old_private_clean=$private_clean + old_private_dirty=$private_dirty + + get_memstats + + if [ $size -gt $old_size ] || + [ $rss -gt $old_rss ] || + [ $shared_clean -gt $old_shared_clean ] || + [ $shared_dirty -gt $old_shared_dirty ] || + [ $private_clean -gt $old_private_clean ] || + [ $private_dirty -gt $old_private_dirty ]; then + printf "Aieeek, a leak!\n" + if [ $nolog -eq 0 ]; then + cp /proc/$mcepid/smaps $memfile.after + fi + return 1 + fi + + return 0 +} + +abort() +{ + if [ $noabort -eq 1 ]; then + printf "Abort disabled, error message: %s\n" "$1" + return 0 + fi + + if [ $nolog -eq 0 ]; then + printf "Aborting: $1\n" | tee $logfile + printf "iterations: $x\n" >> $logfile + printf "testcases: $blank $mceinfo $unblank $dim $radiostates $cabcmode $callstate $tklock $alarm $battery $charger $led $homeshort $homelong $powershort $powerdouble $powerlong $gpiokeyslide $touchscreen $powershortdbus $powerdoubledbus $powerlongdbus $gconfbrightness $gconftimeouts $gconfled $dbuserrors\n" >> $logfile + if [ $syslog -eq 1 ]; then + cat $syslogdir/syslog >> $logfile + fi + tar cf $logball /var/lib/dsme 2> /dev/null + fi + + exit 42 +} + +normal_exit() +{ + test_unblank + exit 0 +} + +usage() +{ + printf "Usage: %s [OPTION]... [TEST]...\n" "$program" + printf "Continuously run the specified tests;\n" + printf "if no tests are listed, all tests are executed\n" + printf "\n" + printf " --no-leakcheck disable leak-checking\n" + printf " --no-logging disable error-logging\n" + printf " --no-abort do not abort on error\n" + printf " --session use the session bus instead of the system bus for D-Bus\n" + printf " --one-shot run the tests once, then exit\n" + printf " --verbose print the name of each test case as it's " + printf "being run\n" + printf " --help display this help and exit\n" + printf " --version output version information and exit\n" + printf "\n" + printf "Valid tests are:\n" + printf "\n" + printf " blank\n" + printf " mceinfo\n" + printf " unblank\n" + printf " dim\n" + printf " radiostates\n" + printf " cabcmode\n" + printf " callstate\n" + printf " tklock\n" + printf " alarm\n" + printf " battery\n" + printf " charger\n" + printf " led\n" + printf " homeshort(1)\n" + printf " homelong(1)\n" + printf " powershort\n" + printf " powerdouble\n" + printf " powerlong(2)\n" + printf " gpio-keyslide\n" + printf " touchscreen\n" + printf " powershort-dbus\n" + printf " powerdouble-dbus\n" + printf " powerlong-dbus(2)\n" + printf " gconf-brightness\n" + printf " gconf-timeouts\n" + printf " gconf-led\n" + printf " dbus-errors\n" + printf "\n" + printf "(1) not included in the default set of test cases since the\n" + printf " [home] module isn't part of the default configuration\n" + printf "(2) not included in the default set of test cases\n" + printf " since it would cause the device to reboot\n" +} + +version() +{ + printf "%s %s\n" "$program" "$version" +} + +test_mceinfo() +{ + # just dump information + mcetool $MCETOOL_FLAGS > /dev/null +} + +test_blank() +{ + # cancel blank prevent and blank screen + mcetool $MCETOOL_FLAGS --cancel-blank-prevent > /dev/null + mcetool $MCETOOL_FLAGS --blank-screen > /dev/null +} + +test_dim() +{ + # dim screen + mcetool $MCETOOL_FLAGS --dim-screen > /dev/null +} + +test_unblank() +{ + # unblank screen and prevent blank + mcetool $MCETOOL_FLAGS --unblank-screen > /dev/null + mcetool $MCETOOL_FLAGS --blank-prevent > /dev/null +} + +test_radiostates() +{ + # enable master radio + mcetool $MCETOOL_FLAGS --enable-radio=master > /dev/null + + # enable cellular + mcetool $MCETOOL_FLAGS --enable-radio=cellular > /dev/null + + # enable wlan + mcetool $MCETOOL_FLAGS --enable-radio=wlan > /dev/null + + # enable bluetooth + mcetool $MCETOOL_FLAGS --enable-radio=bluetooth > /dev/null + + # disable master radio + mcetool $MCETOOL_FLAGS --disable-radio=master > /dev/null + + # disable cellular + mcetool $MCETOOL_FLAGS --disable-radio=cellular > /dev/null + + # disable wlan + mcetool $MCETOOL_FLAGS --disable-radio=wlan > /dev/null + + # disable bluetooth + mcetool $MCETOOL_FLAGS --disable-radio=bluetooth > /dev/null + + # enable master radio + mcetool $MCETOOL_FLAGS --enable-radio=master > /dev/null + + # enable cellular + mcetool $MCETOOL_FLAGS --enable-radio=cellular > /dev/null + + # enable wlan + mcetool $MCETOOL_FLAGS --enable-radio=wlan > /dev/null + + # enable bluetooth + mcetool $MCETOOL_FLAGS --enable-radio=bluetooth > /dev/null +} + +# When mcetool exits, it will be considered to have died and thus the cabc +# mode will revert to the default... +test_cabcmode() +{ + # switch to off + mcetool $MCETOOL_FLAGS --set-cabc-mode=off > /dev/null + + # switch to ui + mcetool $MCETOOL_FLAGS --set-cabc-mode=ui > /dev/null + + # switch to still-image + mcetool $MCETOOL_FLAGS --set-cabc-mode=still-image > /dev/null + + # switch to moving-image + mcetool $MCETOOL_FLAGS --set-cabc-mode=moving-image > /dev/null +} + +# XXX: this test case needs an modified mcetool that returns the result +# value from the call state request to be able to detect veto:ed calls +# +# Even then, we'll have big problems because when mcetool exits, +# it will be considered to have died and thus the call state will +# revert to none... +test_callstate() +{ + # switch to none:normal + mcetool $MCETOOL_FLAGS --set-call-state=none:normal > /dev/null + # switch to none:emergency [undefined combination] + mcetool $MCETOOL_FLAGS --set-call-state=none:emergency > /dev/null + # switch to none:normal + mcetool $MCETOOL_FLAGS --set-call-state=none:normal > /dev/null + + # switch to active:normal + mcetool $MCETOOL_FLAGS --set-call-state=active:normal > /dev/null + # switch to none:normal + mcetool $MCETOOL_FLAGS --set-call-state=none:normal > /dev/null + # switch to active:emergency + mcetool $MCETOOL_FLAGS --set-call-state=active:emergency > /dev/null + # switch to none:normal + mcetool $MCETOOL_FLAGS --set-call-state=none:normal > /dev/null + + # switch to active:normal + mcetool $MCETOOL_FLAGS --set-call-state=active:normal > /dev/null + # switch to active:emergency + mcetool $MCETOOL_FLAGS --set-call-state=active:emergency > /dev/null + # switch to none:normal + mcetool $MCETOOL_FLAGS --set-call-state=none:normal > /dev/null + + # switch to service:normal + mcetool $MCETOOL_FLAGS --set-call-state=service:normal > /dev/null + # switch to none:normal + mcetool $MCETOOL_FLAGS --set-call-state=none:normal > /dev/null + # switch to service:emergency [undefined combination] + mcetool $MCETOOL_FLAGS --set-call-state=service:emergency > /dev/null + # switch to none:normal + mcetool $MCETOOL_FLAGS --set-call-state=none:normal > /dev/null + + # switch to service:normal + mcetool $MCETOOL_FLAGS --set-call-state=service:normal > /dev/null + # switch to active:emergency + mcetool $MCETOOL_FLAGS --set-call-state=active:emergency > /dev/null + # switch to none:normal + mcetool $MCETOOL_FLAGS --set-call-state=none:normal > /dev/null + + # switch to active:normal + mcetool $MCETOOL_FLAGS --set-call-state=active:normal > /dev/null + # switch to active:normal [should be vetoed] + mcetool $MCETOOL_FLAGS --set-call-state=active:normal > /dev/null + # switch to none:normal + mcetool $MCETOOL_FLAGS --set-call-state=none:normal > /dev/null + + # switch to active:emergency + mcetool $MCETOOL_FLAGS --set-call-state=active:emergency > /dev/null + # switch to active:normal [should be vetoed] + mcetool $MCETOOL_FLAGS --set-call-state=active:normal > /dev/null + # switch to none:normal + mcetool $MCETOOL_FLAGS --set-call-state=none:normal > /dev/null + +} + +test_tklock() +{ + # lock tklock + mcetool $MCETOOL_FLAGS --set-tklock-mode=locked > /dev/null + # unlock tklock + mcetool $MCETOOL_FLAGS --set-tklock-mode=unlocked > /dev/null + sleep 1 + # lock tklock and dim the screen + mcetool $MCETOOL_FLAGS --set-tklock-mode=locked-dim > /dev/null + # unlock tklock + mcetool $MCETOOL_FLAGS --set-tklock-mode=unlocked > /dev/null + sleep 1 +} + +ALARM_DBUS_PATH=/com/nokia/voland/signal +ALARM_DBUS_INTERFACE=com.nokia.voland.signal +test_alarm() +{ + # Fake that alarm ui is visible, and alarm is ringing + $DBUS_SEND $DBUS_SEND_SIGNAL_FLAGS $ALARM_DBUS_PATH $ALARM_DBUS_INTERFACE.visual_reminders_status int32:0 + + # Fake that alarm ui is visible, but alarm is not ringing + $DBUS_SEND $DBUS_SEND_SIGNAL_FLAGS $ALARM_DBUS_PATH $ALARM_DBUS_INTERFACE.visual_reminders_status int32:2 + + # Fake that alarm ui isn't visible + $DBUS_SEND $DBUS_SEND_SIGNAL_FLAGS $ALARM_DBUS_PATH $ALARM_DBUS_INTERFACE.visual_reminders_status int32:1 +} + +BME_DBUS_PATH=/com/nokia/bme/signal +BME_DBUS_INTERFACE=com.nokia.bme.signal +test_battery() +{ + # Fake that battery level is empty + $DBUS_SEND $DBUS_SEND_SIGNAL_FLAGS $BME_DBUS_PATH $BME_DBUS_INTERFACE.battery_empty + + # Fake that battery level is low + $DBUS_SEND $DBUS_SEND_SIGNAL_FLAGS $BME_DBUS_PATH $BME_DBUS_INTERFACE.battery_low + + # Fake that battery level is ok + $DBUS_SEND $DBUS_SEND_SIGNAL_FLAGS $BME_DBUS_PATH $BME_DBUS_INTERFACE.battery_ok + + # Fake that battery level is full + $DBUS_SEND $DBUS_SEND_SIGNAL_FLAGS $BME_DBUS_PATH $BME_DBUS_INTERFACE.battery_full + + # Send a fake battery state + $DBUS_SEND $DBUS_SEND_SIGNAL_FLAGS $BME_DBUS_PATH $BME_DBUS_INTERFACE.battery_state_changed uint32:1 uint32:8 + + # Send a fake battery state + $DBUS_SEND $DBUS_SEND_SIGNAL_FLAGS $BME_DBUS_PATH $BME_DBUS_INTERFACE.battery_state_changed uint32:9 uint32:8 +} + +test_charger() +{ + # Fake that charger is connected + $DBUS_SEND $DBUS_SEND_SIGNAL_FLAGS $BME_DBUS_PATH $BME_DBUS_INTERFACE.charger_connected + + # Fake that charging started + $DBUS_SEND $DBUS_SEND_SIGNAL_FLAGS $BME_DBUS_PATH $BME_DBUS_INTERFACE.charger_charging_on + + # Fake that charging stopped + $DBUS_SEND $DBUS_SEND_SIGNAL_FLAGS $BME_DBUS_PATH $BME_DBUS_INTERFACE.charger_charging_off + + # Fake that charger is disconnected + $DBUS_SEND $DBUS_SEND_SIGNAL_FLAGS $BME_DBUS_PATH $BME_DBUS_INTERFACE.charger_connected +} + +test_led() +{ + # Enable LED + mcetool $MCETOOL_FLAGS --enable-led + # Activate poweron LED pattern + mcetool $MCETOOL_FLAGS --activate-led-pattern=PatternPowerOn + # Activate error LED pattern + mcetool $MCETOOL_FLAGS --activate-led-pattern=PatternError + # Disable LED + mcetool $MCETOOL_FLAGS --disable-led + # Enable LED + mcetool $MCETOOL_FLAGS --enable-led + # Deactivate poweron LED pattern + mcetool $MCETOOL_FLAGS --deactivate-led-pattern=PatternPowerOn + # Deactivate error LED pattern + mcetool $MCETOOL_FLAGS --deactivate-led-pattern=PatternError + # Disable LED + mcetool $MCETOOL_FLAGS --disable-led +} + +inject_tap() +{ + printf "$EVENT_TIMESTAMP$EVENT_ABS_TYPE$1$EVENT_PRESS_VALUE$EVENT_TIMESTAMP$EVENT_ABS_TYPE$1$EVENT_RELEASE_VALUE" > $2 + sleep 1 +} + +inject_keyshort() +{ + printf "$EVENT_TIMESTAMP$EVENT_KEY_TYPE$1$EVENT_PRESS_VALUE$EVENT_TIMESTAMP$EVENT_KEY_TYPE$1$EVENT_RELEASE_VALUE" > $2 + sleep 1 +} + +inject_keydouble() +{ + printf "$EVENT_TIMESTAMP$EVENT_KEY_TYPE$1$EVENT_PRESS_VALUE$EVENT_TIMESTAMP$EVENT_KEY_TYPE$1$EVENT_RELEASE_VALUE$EVENT_TIMESTAMP$EVENT_KEY_TYPE$1$EVENT_PRESS_VALUE$EVENT_TIMESTAMP$EVENT_KEY_TYPE$1$EVENT_RELEASE_VALUE" > $2 + sleep 1 +} + +inject_keylong() +{ + printf "$EVENT_TIMESTAMP$EVENT_KEY_TYPE$1$EVENT_PRESS_VALUE" > $2 + sleep 2 + printf "$EVENT_TIMESTAMP$EVENT_KEY_TYPE$1$EVENT_RELEASE_VALUE" > $2 + sleep 1 +} + +inject_switch() +{ + # switch closed + printf "$EVENT_TIMESTAMP$EVENT_SWITCH_TYPE$1$EVENT_RELEASE_VALUE" > $2 + sleep 2 + # switch open + printf "$EVENT_TIMESTAMP$EVENT_SWITCH_TYPE$1$EVENT_PRESS_VALUE" > $2 + sleep 1 +} + +test_homeshort() +{ + # Inject a short [home] event into the keyboard event queue + inject_keyshort $EVENT_HOME_KEY $KEYBOARD_EVENT_FILE +} + +test_homelong() +{ + # Inject a long [home] event into the keyboard event queue + inject_keylong $EVENT_HOME_KEY $KEYBOARD_EVENT_FILE +} + +test_powershort() +{ + # Inject a short [power] event into the keyboard event queue + inject_keyshort $EVENT_POWER_KEY $POWERBUTTON_EVENT_FILE +} + +test_powerdouble() +{ + # Inject a double [power] event into the keyboard event queue + inject_keydouble $EVENT_POWER_KEY $POWERBUTTON_EVENT_FILE +} + +test_powerlong() +{ + # Inject a long [power] event into the keyboard event queue + inject_keylong $EVENT_POWER_KEY $POWERBUTTON_EVENT_FILE +} + +test_gpio_keyslide() +{ + # Inject a keyboard slide event into the gpio-keys event queue + inject_switch $EVENT_KEYPAD_SLIDE $GPIOKEYS_EVENT_FILE +} + +test_touchscreen() +{ + # Inject a touchscren tap into the touchscreen event queue + inject_tap $EVENT_ABS_PRESSURE $TOUCHSCREEN_EVENT_FILE + + # Inject a touchscren key press into the touchscreen event queue + inject_keyshort $EVENT_BTN_TOUCH $TOUCHSCREEN_EVENT_FILE +} + +test_powershort_dbus() +{ + # Trigger a short [power] event using the D-Bus method + mcetool $MCETOOL_FLAGS --powerkey-event=short +} + +test_powerdouble_dbus() +{ + # Trigger a short [power] event using the D-Bus method + mcetool $MCETOOL_FLAGS --powerkey-event=double +} + +test_powerlong_dbus() +{ + # Trigger a long [power] event using the D-Bus method + mcetool $MCETOOL_FLAGS --powerkey-event=long +} + +gconf_unset_key() +{ + $GCONFTOOL --unset $1 +} + +gconf_set() +{ + keytype=$($GCONFTOOL --get-type $1 2> /dev/null) + + # If the key is not set, accept any keytype + if [ x"$keytype" = x"" ]; then + keytype=$2 + fi + + if [ x"$keytype" != x"$2" ]; then + abort "Got type $keytype for GConf key $1, expected $2" + fi + + $GCONFTOOL --set --type=$2 $1 $3 + sleep 1 +} + +gconf_set_bool() +{ + gconf_set $1 bool $2 +} + +gconf_set_int() +{ + gconf_set $1 int $2 +} + +gconf_get() +{ + keytype=$($GCONFTOOL --get-type $2 2> /dev/null) + + status=0 + + # if the key is not set, don't bother reading it + if [ x"$keytype" = x"" ]; then + status=42 + retval=0 + return + fi + + if [ x"$keytype" != x"$1" ]; then + abort "Got type $keytype for GConf key $2, expected $1" + fi + + retval=$($GCONFTOOL --get $2 2> /dev/null) +} + +gconf_get_bool() +{ + gconf_get bool $1 + # This looks stupid; it's just to allow altering + # of the return value + tmp=$retval + retval=$tmp +} + +gconf_get_int() +{ + gconf_get int $1 + # This looks stupid; it's just to allow altering + # of the return value + tmp=$retval + retval=$tmp +} + +test_gconf_brightness() +{ + # Save old brightness + gconf_get_int $GCONF_DISPLAY_BRIGHTNESS_PATH + old_brightness=$retval + + # Set maximum brightness + gconf_set_int $GCONF_DISPLAY_BRIGHTNESS_PATH $GCONF_BRIGHTNESS_MAX + + # Set minimum brightness + gconf_set_int $GCONF_DISPLAY_BRIGHTNESS_PATH $GCONF_BRIGHTNESS_MIN + + # Restore brightness + gconf_set_int $GCONF_DISPLAY_BRIGHTNESS_PATH $old_brightness +} + +test_gconf_timeouts() +{ + # Save old blank timeout + gconf_get_int $GCONF_DISPLAY_BLANK_TIMEOUT_PATH + old_blank_timeout=$retval + + # Save old dim timeout + gconf_get_int $GCONF_DISPLAY_DIM_TIMEOUT_PATH + old_dim_timeout=$retval + + # Save old display blanking inhibit + gconf_get_int $GCONF_DISPLAY_INHIBIT_BLANK_PATH + old_display_inhibit_blank=$retval + + # Save old adaptive dimming + gconf_get_int $GCONF_DISPLAY_ADAPTIVE_DIMMING_ENABLED_PATH + old_display_adaptive_dimming_enabled=$retval + + # Save old adaptive dimming threshold + gconf_get_int $GCONF_DISPLAY_ADAPTIVE_DIMMING_THRESHOLD_PATH + old_display_adaptive_dimming_threshold=$retval + + # Set 5 second dim timeout + gconf_set_int $GCONF_DISPLAY_DIM_TIMEOUT_PATH 5 + + # Set 15 second blank timeout + gconf_set_int $GCONF_DISPLAY_DIM_TIMEOUT_PATH 15 + + # Disable blanking inhibiting + gconf_set_int $GCONF_DISPLAY_INHIBIT_BLANK_PATH 0 + + # Inhibit dimming when charger is connected + gconf_set_int $GCONF_DISPLAY_INHIBIT_BLANK_PATH 1 + + # Inhibit blanking when charger is connected + gconf_set_int $GCONF_DISPLAY_INHIBIT_BLANK_PATH 2 + + # Inhibit dimming + gconf_set_int $GCONF_DISPLAY_INHIBIT_BLANK_PATH 3 + + # Inhibit blanking + gconf_set_int $GCONF_DISPLAY_INHIBIT_BLANK_PATH 4 + + # Set the adaptive dimming threhold to 7500 milliseconds + gconf_set_int $GCONF_DISPLAY_ADAPTIVE_DIMMING_THRESHOLD 15 + + # Disable adaptive dimming + gconf_set_bool $GCONF_DISPLAY_ADAPTIVE_DIMMING_ENABLED false + + # Restore blank timeout + gconf_set_int $GCONF_DISPLAY_BLANK_TIMEOUT_PATH $old_blank_timeout + + # Restore dim timeout + gconf_set_int $GCONF_DISPLAY_DIM_TIMEOUT_PATH $old_dim_timeout + + # Restore display blanking inhibit + gconf_set_int $GCONF_DISPLAY_INHIBIT_BLANK_PATH $old_display_inhibit_blank + + # Restore display adaptive dimming + gconf_set_bool $GCONF_DISPLAY_ADAPTIVE_DIMMING_ENABLED $old_display_adaptive_dimming_enabled + + # Restore display adaptive dimming threshold + gconf_set_int $GCONF_DISPLAY_ADAPTIVE_DIMMING_THRESHOLD $old_display_adaptive_dimming_threshold +} + +test_gconf_led() +{ + # Save old PatternError LED setting + gconf_get_bool $GCONF_LED_PATTERN_ERROR_PATH + old_led_pattern_error=$retval + + # No matter if the key exists or not, unset it + gconf_unset_key $GCONF_LED_PATTERN_ERROR_PATH + + # If there's no value set for PatternError, set it + if [ $status -eq 42 ]; then + gconf_set_bool $GCONF_LED_PATTERN_ERROR_PATH true + fi + + # Enable PatternError + gconf_set_bool $GCONF_LED_PATTERN_ERROR_PATH true + + # enable LED + mcetool $MCETOOL_FLAGS --enable-led + + # activate error LED pattern + mcetool $MCETOOL_FLAGS --activate-led-pattern=PatternError + + # Disable PatternError + gconf_set_bool $GCONF_LED_PATTERN_ERROR_PATH false + + # Deactivate error LED pattern + mcetool $MCETOOL_FLAGS --deactivate-led-pattern=PatternError + + # Disable LED + mcetool $MCETOOL_FLAGS --disable-led + + # Restore old PatternError LED setting + if [ x"$old_led_pattern_error" = x"" ]; then + gconf_unset_key $GCONF_LED_PATTERN_ERROR_PATH + else + gconf_set_bool $GCONF_LED_PATTERN_ERROR_PATH $old_led_pattern_error + fi +} + +test_dbus_errors() +{ + # Send invalid D-Bus method calls to MCE + + # LED tests + + # Invalid LED disabling (too many arguments) + # $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_led_disable int32:42 + + # Invalid LED enabling (too many arguments) + # $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_led_enable string:Foo + + # activate LED pattern (too few arguments) + $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_led_pattern_activate + + # activate LED pattern (too many arguments) + # $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_led_pattern_activate string:PatternError int32:42 + + # activate LED pattern (invalid type) + $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_led_pattern_activate int32:42 + + # activate LED pattern (non-existing pattern) + $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_led_pattern_activate string:PatternNonExisting + + # de-activate LED pattern (too few arguments) + $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_led_pattern_deactivate + + # de-activate LED pattern (too many arguments) + # $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_led_pattern_deactivate string:PatternError int32:42 + + # de-activate LED pattern (invalid type) + $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_led_pattern_deactivate int32:42 + + # de-activate LED pattern (non-existing pattern) + $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_led_pattern_deactivate string:PatternNonExisting + + + # Misc + + # get inactivity status (too many arguments) + # $DBUS_SEND $DBUS_SEND_FLAGS_REPLY --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.get_inactivity_status int32:42 + + # get MCE version (too many arguments) + # $DBUS_SEND $DBUS_SEND_FLAGS_REPLY --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.get_version int32:42 + + + # Display state + + # get display status (too many arguments) + # $DBUS_SEND $DBUS_SEND_FLAGS_REPLY --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.get_display_status int32:42 + + # blank screen (too many arguments) + # $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_display_state_off int32:42 + + # dim screen (too many arguments) + # $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_display_state_dim int32:42 + + # unblank screen (too many arguments) + # $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_display_state_on int32:42 + + # prevent blank (too many arguments) + $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_display_blanking_pause int32:42 + + # cancel prevent blank (too many arguments) + # $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_display_cancel_blanking_pause int32:42 + + + # Device mode + + # get device mode (too many arguments) + # $DBUS_SEND $DBUS_SEND_FLAGS_REPLY --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.get_device_mode int32:42 + + # request device mode change (too few arguments) + $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_device_mode_change + + # request device mode change (too many arguments) + # $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_device_mode_change string:normal int32:42 + + # request device mode change (invalid type) + $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_device_mode_change int32:42 + + # request device mode change (invalid value) + $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_device_mode_change string:foo + + + # Radio states + + # get radio states (too many arguments) + # $DBUS_SEND $DBUS_SEND_FLAGS_REPLY --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.get_radio_states int32:42 + + # request radio states change (too few arguments) + $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_radio_states_change + + # request radio states change (too many arguments) + # $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_radio_states_change uint32:0 uint32:0 uint32:0 + + # request radio states change (invalid type) + $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_radio_states_change string:foo uint32:0 + + + # Tklock mode + + # get touchscreen/keypad lock mode (too many arguments) + # $DBUS_SEND $DBUS_SEND_FLAGS_REPLY --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.get_tklock_mode int32:42 + + # request tklock mode change (too few arguments) + $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_device_mode_change + + # request tklock mode change (too many arguments) + # $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_device_mode_change string:unlocked int32:42 + + # request tklock mode change (invalid type) + $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_device_mode_change int32:42 + + # request tklock mode change (invalid value) + $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_device_mode_change string:foo + + + # Powerkey trigger + + # trigger powerkey event (too few arguments) + $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_trigger_powerkey_event + + # trigger powerkey event; boolean interface (too many arguments) + # $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_trigger_powerkey_event boolean:false int32:42 + + # trigger powerkey event; uint32 interface (too many arguments) + # $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_trigger_powerkey_event uint32:2 int32:42 + + # trigger powerkey event (invalid type) + $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_trigger_powerkey_event string:foo + + # trigger powerkey event (invalid value) + $DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_trigger_powerkey_event uint32:42 +} + +run_test() +{ + [ x"$2" == x"" ] && return 0 + + [ $verbose -eq 1 ] && printf "Running test-case $1\n" + + $2 + + crashcheck || abort "$1 test-case caused a crash" + leakcheck || abort "$1 test-case leaked" +} + +# setup command line options +while ! [ $# -eq 0 ]; do + case $1 in + mceinfo|blank|dim|unblank|radiostates|cabcmode|callstate|tklock|alarm|battery|charger|led|homeshort|homelong|powershort|powerdouble|powerlong|touchscreen) + eval ${1}=test_${1} + tests=$((tests + 1)) + ;; + gpio-keyslide) + gpiokeyslide=test_gpio_keyslide + tests=$((tests + 1)) + ;; + powershort-dbus) + powershortdbus=test_powershort_dbus + tests=$((tests + 1)) + ;; + powerdouble-dbus) + powerdoubledbus=test_powerdouble_dbus + tests=$((tests + 1)) + ;; + powerlong-dbus) + powerlongdbus=test_powerlong_dbus + tests=$((tests + 1)) + ;; + gconf-brightness) + gconfbrightness=test_gconf_brightness + tests=$((tests + 1)) + ;; + gconf-timeouts) + gconftimeouts=test_gconf_timeouts + tests=$((tests + 1)) + ;; + gconf-led) + gconfled=test_gconf_led + tests=$((tests + 1)) + ;; + dbus-errors) + dbuserrors=test_dbus_errors + tests=$((tests + 1)) + ;; + --no-leakcheck) + printf "Leak checking disabled\n" + noleakcheck=1 + ;; + --no-abort) + printf "Abort on error disabled\n" + noabort=1 + ;; + --session) + MCETOOL_FLAGS="$MCETOOL_FLAGS --session" + ;; + --one-shot) + oneshot=1 + ;; + --verbose) + verbose=1 + ;; + --help) + usage + exit 0 + ;; + --version) + version + exit 0 + ;; + *) + usage + exit 1 + ;; + esac + shift +done + +if ! [ -x "/sbin/mcetool" ]; then + printf "mcetool is not installed; exiting\n" + exit 1 +fi + +if [ $tests -eq 0 ]; then + printf "No test cases specified; enabling all default tests\n" + + mceinfo=test_mceinfo + unblank=test_unblank + dim=test_dim + blank=test_blank + radiostates=test_radiostates + cabcmode=test_cabcmode + callstate=test_callstate + tklock=test_tklock + alarm=test_alarm + battery=test_battery + charger=test_charger + led=test_led + powershort=test_powershort + powerdouble=test_powerdouble + touchscreen=test_touchscreen + gpiokeyslide=test_gpio_keyslide + powershortdbus=test_powershort_dbus + powerdoubledbus=test_powerdouble_dbus + gconfbrightness=test_gconf_brightness + gconftimeouts=test_gconf_timeouts + gconfled=test_gconf_led + dbuserrors=test_dbus_errors +fi + +if [ x"$mcepid" = x"" ]; then + printf "MCE not running; aborting!\n" + exit 1 +fi + +if [ $nolog -eq 0 ]; then + cp /proc/$mcepid/smaps $memfile.before +fi + +get_memstats + +while /bin/true; do + x=$((x+1)) + + run_test "blank" $blank + run_test "mceinfo" $mceinfo + run_test "unblank" $unblank + run_test "dim" $dim + run_test "radiostates" $radiostates + run_test "cabcmode" $cabcmode + run_test "tklock" $tklock + run_test "alarm" $alarm + run_test "battery" $battery + run_test "led" $led + run_test "homeshort" $homeshort + run_test "homelong" $homelong + run_test "powershort" $powershort + run_test "powerdouble" $powerdouble + run_test "powerlong" $powerlong + run_test "gpio-keyslide" $gpiokeyslide + run_test "touchscreen" $touchscreen + run_test "powershort-dbus" $powershortdbus + run_test "powerdouble-dbus" $powerdoubledbus + run_test "powerlong-dbus" $powerlongdbus + run_test "gconf-brightness" $gconfbrightness + run_test "gconf-timeouts" $gconftimeouts + run_test "gconf-led" $gconfled + run_test "dbus-errors" $dbuserrors + + printf "iterations: $x\n" + + if [ $syslog -eq 1 ]; then + tail -n100 /var/log/syslog | grep -q "DSME: process" && abort "A process monitored by DSME died" + fi + + [ $oneshot -eq 1 ] && normal_exit +done diff --git a/tests/sendtests b/tests/sendtests new file mode 100755 index 00000000..6741d85b --- /dev/null +++ b/tests/sendtests @@ -0,0 +1,25 @@ +#! /bin/sh +DBUS_PATH=/com/nokia/mce/request +DBUS_DEST=com.nokia.mce +DBUS_INTERFACE=com.nokia.mce.request +DBUS_SEND=dbus-send +DBUS_SEND_FLAGS_REPLY='--system --type=method_call --print-reply' +DBUS_SEND_FLAGS='--system --type=method_call' + +# set device mode to normal +$DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_device_mode_change string:normal + +# should return normal +$DBUS_SEND $DBUS_SEND_FLAGS_REPLY --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.get_device_mode + +# set device mode to flight +$DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_device_mode_change string:flight + +# should return flight +$DBUS_SEND $DBUS_SEND_FLAGS_REPLY --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.get_device_mode + +# set device mode to offline +$DBUS_SEND $DBUS_SEND_FLAGS --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.req_device_mode_change string:offline + +# should return flight (since offline and flight are equivalent for now) +$DBUS_SEND $DBUS_SEND_FLAGS_REPLY --dest=$DBUS_DEST $DBUS_PATH $DBUS_INTERFACE.get_device_mode diff --git a/tests/verifybacklight b/tests/verifybacklight new file mode 100755 index 00000000..fac29190 --- /dev/null +++ b/tests/verifybacklight @@ -0,0 +1,213 @@ +#! /bin/sh +# Verify that the kernel LED support works +program=verifybacklight +version=1.16 + +engine=lysti +enable=disable + +LED_PATH_LYSTI_DIRECT=/sys/class/leds/lp5523 +LED_PATH_LYSTI_DIRECT_CHANNEL0_BRIGHTNESS=$LED_PATH_LYSTI_DIRECT:channel0/brightness +LED_PATH_LYSTI_DIRECT_CHANNEL1_BRIGHTNESS=$LED_PATH_LYSTI_DIRECT:channel1/brightness +LED_PATH_LYSTI_DIRECT_CHANNEL2_BRIGHTNESS=$LED_PATH_LYSTI_DIRECT:channel2/brightness +LED_PATH_LYSTI_DIRECT_CHANNEL3_BRIGHTNESS=$LED_PATH_LYSTI_DIRECT:channel3/brightness +LED_PATH_LYSTI_DIRECT_CHANNEL4_BRIGHTNESS=$LED_PATH_LYSTI_DIRECT:channel4/brightness +LED_PATH_LYSTI_DIRECT_CHANNEL5_BRIGHTNESS=$LED_PATH_LYSTI_DIRECT:channel5/brightness +LED_PATH_LYSTI_DIRECT_CHANNEL0_CURRENT=$LED_PATH_LYSTI_DIRECT:channel0/led_current +LED_PATH_LYSTI_DIRECT_CHANNEL1_CURRENT=$LED_PATH_LYSTI_DIRECT:channel1/led_current +LED_PATH_LYSTI_DIRECT_CHANNEL2_CURRENT=$LED_PATH_LYSTI_DIRECT:channel2/led_current +LED_PATH_LYSTI_DIRECT_CHANNEL3_CURRENT=$LED_PATH_LYSTI_DIRECT:channel3/led_current +LED_PATH_LYSTI_DIRECT_CHANNEL4_CURRENT=$LED_PATH_LYSTI_DIRECT:channel4/led_current +LED_PATH_LYSTI_DIRECT_CHANNEL5_CURRENT=$LED_PATH_LYSTI_DIRECT:channel5/led_current + +LED_PATH_LYSTI_ENGINE=/sys/class/i2c-adapter/i2c-2/2-0032 +LED_PATH_LYSTI_ENGINE3_MODE=$LED_PATH_LYSTI_ENGINE/engine3_mode +LED_PATH_LYSTI_ENGINE3_LOAD=$LED_PATH_LYSTI_ENGINE/engine3_load +LED_PATH_LYSTI_ENGINE3_LEDS=$LED_PATH_LYSTI_ENGINE/engine3_leds + +RX51_LYSTI_MASK_BACKLIGHT=111100011 +RM680_LYSTI_MASK_BACKLIGHT=111111000 + +# Default to RM-680 +LYSTI_MASK_BACKLIGHT=$RM680_LYSTI_MASK_BACKLIGHT + +LYSTI_MODE_DISABLED=disabled +LYSTI_MODE_DIRECT=direct +LYSTI_MODE_LOAD=load +LYSTI_MODE_RUN=run + +LYSTI_TEST_PATTERN=9d80407f00000000 + +BACKLIGHT_LED_CURRENT=50 +BACKLIGHT_LED_BRIGHTNESS=127 + +usage() +{ + printf "Usage: %s [OPTION]... [MODEL] [ENGINE] [COMMAND]\n" "$program" + printf "Test functionality of the keyboard backlight;\n" + printf "if no command is passed, an attempt to disable the \n" + printf "backlight of the default LED controller ($engine)\n" + printf "\n" + printf " --enable-pm-debug enable use of backlight for " + printf "PM debuging\n" + printf " --disable-pm-debug disable use of backlight for " + printf "PM debuging\n" + printf " --help display this help and exit\n" + printf " --version output version information and exit\n" + printf "\n" + printf "Valid commands are:\n" + printf " enable enable the LED backlight engine\n" + printf " disable disable the LED backlight engine\n" + printf " enable-direct enable the LED backlight\n" + printf " disable-direct disable the LED backlight\n" + printf "\n" + printf "Valid models are:\n" + printf " rm680 (*)\n" + printf " rx51\n" + printf "\n" + printf "Valid engines are:\n" + printf " lysti (*)\n" + printf "\n" + printf "(*) Default value\n" +} + +version() +{ + printf "%s %s\n" "$program" "$version" +} + +lysti_disable() +{ + # disable engine 3 + printf $LYSTI_MODE_DISABLED > $LED_PATH_LYSTI_ENGINE3_MODE + + # set brightness to 0 + printf 0 > $LED_PATH_LYSTI_DIRECT_CHANNEL0_BRIGHTNESS + printf 0 > $LED_PATH_LYSTI_DIRECT_CHANNEL1_BRIGHTNESS + printf 0 > $LED_PATH_LYSTI_DIRECT_CHANNEL2_BRIGHTNESS + printf 0 > $LED_PATH_LYSTI_DIRECT_CHANNEL3_BRIGHTNESS + printf 0 > $LED_PATH_LYSTI_DIRECT_CHANNEL4_BRIGHTNESS + printf 0 > $LED_PATH_LYSTI_DIRECT_CHANNEL5_BRIGHTNESS + + # set led current to 0 + printf 0 > $LED_PATH_LYSTI_DIRECT_CHANNEL0_CURRENT + printf 0 > $LED_PATH_LYSTI_DIRECT_CHANNEL1_CURRENT + printf 0 > $LED_PATH_LYSTI_DIRECT_CHANNEL2_CURRENT + printf 0 > $LED_PATH_LYSTI_DIRECT_CHANNEL3_CURRENT + printf 0 > $LED_PATH_LYSTI_DIRECT_CHANNEL4_CURRENT + printf 0 > $LED_PATH_LYSTI_DIRECT_CHANNEL5_CURRENT +} + +lysti_enable_direct() +{ + lysti_disable + + # set led current + printf $BACKLIGHT_LED_CURRENT > $LED_PATH_LYSTI_DIRECT_CHANNEL0_CURRENT + printf $BACKLIGHT_LED_CURRENT > $LED_PATH_LYSTI_DIRECT_CHANNEL1_CURRENT + printf $BACKLIGHT_LED_CURRENT > $LED_PATH_LYSTI_DIRECT_CHANNEL2_CURRENT + printf $BACKLIGHT_LED_CURRENT > $LED_PATH_LYSTI_DIRECT_CHANNEL3_CURRENT + printf $BACKLIGHT_LED_CURRENT > $LED_PATH_LYSTI_DIRECT_CHANNEL4_CURRENT + printf $BACKLIGHT_LED_CURRENT > $LED_PATH_LYSTI_DIRECT_CHANNEL5_CURRENT + + # set brightness + printf $BACKLIGHT_LED_BRIGHTNESS > $LED_PATH_LYSTI_DIRECT_CHANNEL0_BRIGHTNESS + printf $BACKLIGHT_LED_BRIGHTNESS > $LED_PATH_LYSTI_DIRECT_CHANNEL1_BRIGHTNESS + printf $BACKLIGHT_LED_BRIGHTNESS > $LED_PATH_LYSTI_DIRECT_CHANNEL2_BRIGHTNESS + printf $BACKLIGHT_LED_BRIGHTNESS > $LED_PATH_LYSTI_DIRECT_CHANNEL3_BRIGHTNESS + printf $BACKLIGHT_LED_BRIGHTNESS > $LED_PATH_LYSTI_DIRECT_CHANNEL4_BRIGHTNESS + printf $BACKLIGHT_LED_BRIGHTNESS > $LED_PATH_LYSTI_DIRECT_CHANNEL5_BRIGHTNESS +} + +lysti_enable() +{ + lysti_disable + + # set led current + printf $BACKLIGHT_LED_CURRENT > $LED_PATH_LYSTI_DIRECT_CHANNEL0_CURRENT + printf $BACKLIGHT_LED_CURRENT > $LED_PATH_LYSTI_DIRECT_CHANNEL1_CURRENT + printf $BACKLIGHT_LED_CURRENT > $LED_PATH_LYSTI_DIRECT_CHANNEL2_CURRENT + printf $BACKLIGHT_LED_CURRENT > $LED_PATH_LYSTI_DIRECT_CHANNEL3_CURRENT + printf $BACKLIGHT_LED_CURRENT > $LED_PATH_LYSTI_DIRECT_CHANNEL4_CURRENT + printf $BACKLIGHT_LED_CURRENT > $LED_PATH_LYSTI_DIRECT_CHANNEL5_CURRENT + + # set engine 3 to load + printf $LYSTI_MODE_LOAD > $LED_PATH_LYSTI_ENGINE3_MODE + + # set engine 3 to control the backlight leds + printf $LYSTI_MASK_BACKLIGHT > $LED_PATH_LYSTI_ENGINE3_LEDS + + # load test pattern to engine 3 + printf $LYSTI_TEST_PATTERN > $LED_PATH_LYSTI_ENGINE3_LOAD + + # enable engine 3 + printf $LYSTI_MODE_RUN > $LED_PATH_LYSTI_ENGINE3_MODE +} + +# setup command line options +while ! [ $# -eq 0 ]; do + case $1 in + rx51) + LYSTI_MASK_BACKLIGHT=$RX51_LYSTI_MASK_BACKLIGHT + ;; + rm680) + LYSTI_MASK_BACKLIGHT=$RM680_LYSTI_MASK_BACKLIGHT + ;; + lysti) + engine=${1} + ;; + enable|disable) + enable=${1} + ;; + enable-direct) + enable=enable_direct + ;; + --disable-pm-debug) + # Old interface + [ -f /sys/devices/platform/gpio-switch/sleep_ind/state ] && printf inactive > /sys/devices/platform/gpio-switch/sleep_ind/state + + # New interface + if [ -f /sys/class/gpio/export ]; then + echo 92 > /sys/class/gpio/export + # This doesn't seem to be used anymore + # echo out > /sys/class/gpio/gpio92/direction + echo 0 > /sys/class/gpio/gpio92/value + # This seems to reset the value back to 1 + # echo 92 > /sys/class/gpio/unexport + fi + ;; + --enable-pm-debug) + # Old interface + [ -f /sys/devices/platform/gpio-switch/sleep_ind/state ] && printf active > /sys/devices/platform/gpio-switch/sleep_ind/state + + # New interface + if [ -f /sys/class/gpio/export ]; then + echo 92 > /sys/class/gpio/export + # This doesn't seem to be used anymore + # echo out > /sys/class/gpio/gpio92/direction + echo 1 > /sys/class/gpio/gpio92/value + echo 92 > /sys/class/gpio/unexport + fi + ;; + --help) + usage + exit 0 + ;; + --version) + version + exit 0 + ;; + *) + usage + exit 1 + ;; + esac + shift +done + +if [ x"$(pidof mce)" != x"" ]; then + printf "Warning, MCE is running; " + printf "this will most likely interfere with testing\n" +fi + +${engine}_${enable} diff --git a/tests/verifyled b/tests/verifyled new file mode 100644 index 00000000..1fb76f1d --- /dev/null +++ b/tests/verifyled @@ -0,0 +1,118 @@ +#! /bin/sh +# Verify that the kernel LED support works + +engine=lysti +enable=disable + +LED_PATH_LYSTI_DIRECT=/sys/class/leds/lp5523 +LED_PATH_LYSTI_DIRECT_R_BRIGHTNESS=$LED_PATH_LYSTI_DIRECT:r/brightness +LED_PATH_LYSTI_DIRECT_G_BRIGHTNESS=$LED_PATH_LYSTI_DIRECT:g/brightness +LED_PATH_LYSTI_DIRECT_B_BRIGHTNESS=$LED_PATH_LYSTI_DIRECT:b/brightness +LED_PATH_LYSTI_DIRECT_R_CURRENT=$LED_PATH_LYSTI_DIRECT:r/led_current +LED_PATH_LYSTI_DIRECT_G_CURRENT=$LED_PATH_LYSTI_DIRECT:g/led_current +LED_PATH_LYSTI_DIRECT_B_CURRENT=$LED_PATH_LYSTI_DIRECT:b/led_current + +LED_PATH_LYSTI_ENGINE=/sys/class/i2c-adapter/i2c-2/2-0032 +LED_PATH_LYSTI_ENGINE1_MODE=$LED_PATH_LYSTI_ENGINE/engine1_mode +LED_PATH_LYSTI_ENGINE1_LOAD=$LED_PATH_LYSTI_ENGINE/engine1_load +LED_PATH_LYSTI_ENGINE1_LEDS=$LED_PATH_LYSTI_ENGINE/engine1_leds + +LYSTI_MASK_RGB=000011100 + +LYSTI_MODE_DISABLED=disabled +LYSTI_MODE_DIRECT=direct +LYSTI_MODE_LOAD=load +LYSTI_MODE_RUN=run + +LYSTI_TEST_PATTERN=9d80400044ff45ff + +RGB_LED_CURRENT=50 +RGB_LED_BRIGHTNESS=127 + +lysti_disable() +{ + # disable engine 1 + printf $LYSTI_MODE_DISABLED > $LED_PATH_LYSTI_ENGINE1_MODE + + # turn off RGB leds + printf 0 > $LED_PATH_LYSTI_DIRECT_R_BRIGHTNESS + printf 0 > $LED_PATH_LYSTI_DIRECT_G_BRIGHTNESS + printf 0 > $LED_PATH_LYSTI_DIRECT_B_BRIGHTNESS + + # set led current to 0 + printf 0 > $LED_PATH_LYSTI_DIRECT_R_CURRENT + printf 0 > $LED_PATH_LYSTI_DIRECT_G_CURRENT + printf 0 > $LED_PATH_LYSTI_DIRECT_B_CURRENT +} + +lysti_enable_direct() +{ + lysti_disable + + # set led current + printf $RGB_LED_CURRENT > $LED_PATH_LYSTI_DIRECT_R_CURRENT + printf $RGB_LED_CURRENT > $LED_PATH_LYSTI_DIRECT_G_CURRENT + printf $RGB_LED_CURRENT > $LED_PATH_LYSTI_DIRECT_B_CURRENT + + # set brightness + printf $RGB_LED_BRIGHTNESS > $LED_PATH_LYSTI_DIRECT_R_BRIGHTNESS + printf $RGB_LED_BRIGHTNESS > $LED_PATH_LYSTI_DIRECT_G_BRIGHTNESS + printf $RGB_LED_BRIGHTNESS > $LED_PATH_LYSTI_DIRECT_B_BRIGHTNESS +} + +lysti_enable() +{ + lysti_disable + + # set led current to 42 + printf $RGB_LED_CURRENT > $LED_PATH_LYSTI_DIRECT_R_CURRENT + printf $RGB_LED_CURRENT > $LED_PATH_LYSTI_DIRECT_G_CURRENT + printf $RGB_LED_CURRENT > $LED_PATH_LYSTI_DIRECT_B_CURRENT + + # set engine 1 to load + printf $LYSTI_MODE_LOAD > $LED_PATH_LYSTI_ENGINE1_MODE + + # set engine 1 to control the RGB leds + printf $LYSTI_MASK_RGB > $LED_PATH_LYSTI_ENGINE1_LEDS + + # load test pattern to engine 1 + printf $LYSTI_TEST_PATTERN > $LED_PATH_LYSTI_ENGINE1_LOAD + + # enable engine 1 + printf $LYSTI_MODE_RUN > $LED_PATH_LYSTI_ENGINE1_MODE +} + +# setup command line options +while ! [ $# -eq 0 ]; do + case $1 in + lysti) + engine=${1} + ;; + enable|disable) + enable=${1} + ;; + enable-direct) + enable=enable_direct + ;; +# --help) +# usage +# exit 0 +# ;; +# --version) +# version +# exit 0 +# ;; +# *) +# usage +# exit 1 +# ;; + esac + shift +done + +if [ x"$(pidof mce)" != x"" ]; then + printf "Warning, MCE is running; " + printf "this will most likely interfere with testing\n" +fi + +${engine}_${enable} diff --git a/tklock.c b/tklock.c new file mode 100644 index 00000000..9a872386 --- /dev/null +++ b/tklock.c @@ -0,0 +1,2625 @@ +/** + * @file tklock.c + * This file implements the touchscreen/keypad lock component + * of the Mode Control Entity + *

+ * Copyright © 2004-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include +#include /* g_access() */ + +#include /* strcmp() */ +#include /* W_OK */ +#include /* struct input_event */ + +#include +#include +#include + +#include "mce.h" +#include "tklock.h" + +#include "mce-io.h" /* mce_write_number_string_to_file */ +#include "mce-log.h" /* mce_log(), LL_* */ +#include "datapipe.h" /* execute_datapipe(), + * datapipe_get_gint(), + * append_input_trigger_to_datapipe(), + * append_output_trigger_to_datapipe(), + * remove_input_trigger_from_datapipe(), + * remove_output_trigger_from_datapipe() + */ +#include "mce-conf.h" /* mce_conf_get_bool() */ +#include "mce-dbus.h" /* mce_dbus_handler_add(), + * dbus_send(), + * dbus_send_message(), + * dbus_new_method_reply(), + * dbus_new_signal(), + * dbus_message_get_no_reply(), + * dbus_message_get_args(), + * dbus_message_append_args(), + * dbus_message_unref(), + * dbus_error_init(), + * dbus_error_free(), + * DBUS_MESSAGE_TYPE_METHOD_CALL, + * DBUS_TYPE_BOOLEAN, + * DBUS_TYPE_UINT32, DBUS_TYPE_INT32, + * DBUS_TYPE_STRING, + * DBUS_TYPE_INVALID + * DBusMessage, DBusError, + * dbus_bool_t, + * dbus_uint32_t, dbus_int32_t + */ +#include "mce-gconf.h" /* mce_gconf_notifier_add(), + * mce_gconf_get_bool(), + * gconf_entry_get_key(), + * gconf_entry_get_value(), + * gconf_value_get_bool(), + * GConfClient, GConfEntry, GConfValue + */ + +/** + * TRUE if the touchscreen/keypad autolock is enabled, + * FALSE if the touchscreen/keypad autolock is disabled + */ +static gboolean tk_autolock_enabled = DEFAULT_TK_AUTOLOCK; + +/** GConf callback ID for the autolock entry */ +static guint tk_autolock_enabled_cb_id = 0; + +/** Blanking timeout ID for the visual tklock */ +static guint tklock_visual_blank_timeout_cb_id = 0; + +/** Forced blanking timeout ID for the visual tklock */ +static guint tklock_visual_forced_blank_timeout_cb_id = 0; + +/** Dimming timeout ID for the tklock */ +static guint tklock_dim_timeout_cb_id = 0; + +/** ID for touchscreen/keypad unlock source */ +static guint tklock_unlock_timeout_cb_id = 0; + +/** Blank immediately on tklock instead of dim/blank */ +static gboolean blank_immediately = DEFAULT_BLANK_IMMEDIATELY; + +/** Dim immediately on tklock instead of timeout */ +static gboolean dim_immediately = DEFAULT_DIM_IMMEDIATELY; + +/** Touchscreen/keypad dim timeout */ +static gint dim_delay = DEFAULT_DIM_DELAY; + +/** Disable touchscreen immediately on tklock instead of at blank */ +static gboolean disable_ts_immediately = DEFAULT_TS_OFF_IMMEDIATELY; + +/** Disable keypad immediately on tklock instead of at blank */ +static gint disable_kp_immediately = DEFAULT_KP_OFF_IMMEDIATELY; + +/** Inhibit autolock when slide is open */ +static gboolean autolock_with_open_slide = DEFAULT_AUTOLOCK_SLIDE_OPEN; + +/** Inhibit proximity lock when slide is open */ +static gboolean proximity_lock_with_open_slide = DEFAULT_PROXIMITY_LOCK_SLIDE_OPEN; + +/** Unconditionally enable lock when keyboard slide is closed */ +static gboolean always_lock_on_slide_close = DEFAULT_LOCK_ON_SLIDE_CLOSE; + +/** Unlock the TKLock when the lens cover is opened */ +static gboolean lens_cover_unlock = DEFAULT_LENS_COVER_UNLOCK; + +/** Proximity based locking when the phone is ringing */ +static gboolean proximity_lock_when_ringing = DEFAULT_PROXIMITY_LOCK_WHEN_RINGING; + +/** Trigger unlock screen when volume keys are pressed */ +static gboolean volkey_visual_trigger = DEFAULT_VOLKEY_VISUAL_TRIGGER; + +/** SysFS path to touchscreen event disable */ +static const gchar *mce_touchscreen_sysfs_disable_path = NULL; + +/** SysFS path to keypad event disable */ +static const gchar *mce_keypad_sysfs_disable_path = NULL; + +/** Submode at the beginning of a call */ +static submode_t saved_submode = MCE_INVALID_SUBMODE; + +/** TKLock UI state type */ +typedef enum { + /** TKLock UI state unknown */ + MCE_TKLOCK_UI_UNSET = -1, + /** No TKLock UI active */ + MCE_TKLOCK_UI_NONE = 0, + /** Normal TKLock UI active */ + MCE_TKLOCK_UI_NORMAL = 1, + /** Event eater UI active */ + MCE_TKLOCK_UI_EVENT_EATER = 2, + /** Slider UI active */ + MCE_TKLOCK_UI_SLIDER = 3 +} tklock_ui_state_t; + +/** TKLock UI state */ +static tklock_ui_state_t tklock_ui_state = MCE_TKLOCK_UI_UNSET; + +/* Valid triggers for autorelock */ + +/** No autorelock triggers */ +#define AUTORELOCK_NO_TRIGGERS 0 +/** Autorelock on keyboard slide closed */ +#define AUTORELOCK_KBD_SLIDE (1 << 0) +/** Autorelock on lens cover */ +#define AUTORELOCK_LENS_COVER (1 << 1) +/** Autorelock on proximity sensor */ +#define AUTORELOCK_ON_PROXIMITY (1 << 2) + +/** Inhibit proximity relock type */ +typedef enum { + /** Inhibit proximity relock */ + MCE_INHIBIT_PROXIMITY_RELOCK = 0, + /** Allow proximity relock */ + MCE_ALLOW_PROXIMITY_RELOCK = 1, + /** Temporarily inhibit proximity relock */ + MCE_TEMP_INHIBIT_PROXIMITY_RELOCK = 2 +} inhibit_proximity_relock_t; + +/** Inhibit autorelock using proximity sensor */ +static inhibit_proximity_relock_t inhibit_proximity_relock = MCE_ALLOW_PROXIMITY_RELOCK; + +/** TKLock activated due to proximity */ +static gboolean tklock_proximity = FALSE; + +/** Autorelock triggers */ +static gint autorelock_triggers = AUTORELOCK_NO_TRIGGERS; + +static void set_tklock_state(lock_state_t lock_state); +static void touchscreen_trigger(gconstpointer const data); +static void cancel_tklock_dim_timeout(void); +static void cancel_tklock_unlock_timeout(void); + +/** + * Query the event eater status + * + * @return TRUE if the event eater is enabled, + * FALSE if the event eater is disabled + */ +static gboolean is_eveater_enabled(void) G_GNUC_PURE; +static gboolean is_eveater_enabled(void) +{ + return ((mce_get_submode_int32() & MCE_EVEATER_SUBMODE) != 0); +} + +/** + * Query the touchscreen/keypad lock status + * + * @return TRUE if the touchscreen/keypad lock is enabled, + * FALSE if the touchscreen/keypad lock is disabled + */ +static gboolean is_tklock_enabled(void) G_GNUC_PURE; +static gboolean is_tklock_enabled(void) +{ + return ((mce_get_submode_int32() & MCE_TKLOCK_SUBMODE) != 0); +} + +/** + * Query the visual touchscreen/keypad lock status + * + * @return TRUE if the visual touchscreen/keypad lock is enabled, + * FALSE if the visual touchscreen/keypad lock is disabled + */ +static gboolean is_visual_tklock_enabled(void) G_GNUC_PURE; +static gboolean is_visual_tklock_enabled(void) +{ + return ((mce_get_submode_int32() & MCE_VISUAL_TKLOCK_SUBMODE) != 0); +} + +/** + * Query the autorelock status + * + * @return TRUE if the autorelock is enabled, + * FALSE if the autorelock is disabled + */ +static gboolean is_autorelock_enabled(void) G_GNUC_PURE; +static gboolean is_autorelock_enabled(void) +{ + return ((mce_get_submode_int32() & MCE_AUTORELOCK_SUBMODE) != 0); +} + +/** + * Enable auto-relock + */ +static void enable_autorelock(void) +{ + cover_state_t kbd_slide_state = datapipe_get_gint(keyboard_slide_pipe); + cover_state_t lens_cover_state = datapipe_get_gint(lens_cover_pipe); + + if (autorelock_triggers != AUTORELOCK_ON_PROXIMITY) { + /* Reset autorelock triggers */ + autorelock_triggers = AUTORELOCK_NO_TRIGGERS; + + /* If the keyboard slide is closed, use it as a trigger */ + if (kbd_slide_state == COVER_CLOSED) + autorelock_triggers |= AUTORELOCK_KBD_SLIDE; + + /* If the lens cover is closed, use it as a trigger */ + if (lens_cover_state == COVER_CLOSED) + autorelock_triggers |= AUTORELOCK_LENS_COVER; + } + + /* Only setup touchscreen monitoring once, + * and only if there are autorelock triggers + * and it's not the proximity sensor + */ + if ((is_autorelock_enabled() == FALSE) && + (autorelock_triggers != AUTORELOCK_NO_TRIGGERS) && + (autorelock_triggers != AUTORELOCK_ON_PROXIMITY)) { + append_input_trigger_to_datapipe(&touchscreen_pipe, + touchscreen_trigger); + } + + mce_add_submode_int32(MCE_AUTORELOCK_SUBMODE); +} + +/** + * Disable auto-relock + */ +static void disable_autorelock(void) +{ + /* Touchscreen monitoring is only needed for the autorelock */ + remove_input_trigger_from_datapipe(&touchscreen_pipe, + touchscreen_trigger); + mce_rem_submode_int32(MCE_AUTORELOCK_SUBMODE); + + /* Reset autorelock triggers */ + autorelock_triggers = AUTORELOCK_NO_TRIGGERS; +} + +/** + * Disable auto-relock based on policy + */ +static void disable_autorelock_policy(void) +{ + alarm_ui_state_t alarm_ui_state = + datapipe_get_gint(alarm_ui_state_pipe); + + /* Don't disable autorelock if the alarm UI is visible */ + if ((alarm_ui_state == MCE_ALARM_UI_VISIBLE_INT32) || + (alarm_ui_state == MCE_ALARM_UI_RINGING_INT32)) + goto EXIT; + + /* If the tklock is enabled + * or proximity autorelock is active, don't disable + */ + if ((is_tklock_enabled() == TRUE) || + (autorelock_triggers == AUTORELOCK_ON_PROXIMITY)) + goto EXIT; + + disable_autorelock(); + +EXIT: + return; +} + +/** + * Enable/disable touchscreen/keypad events + * + * @param file Path to enable/disable file + * @param enable TRUE enable events, FALSE disable events + * @return TRUE on success, FALSE on failure + */ +static gboolean generic_event_control(const gchar *const file, + const gboolean enable) +{ + gboolean status = FALSE; + + /* If no filename is specified, there is no interface + * for event control available; just smile and be happy + */ + if (file == NULL) { + mce_log(LL_DEBUG, + "No event control interface available; " + "request ignored"); + status = TRUE; + goto EXIT; + } + + if (mce_write_number_string_to_file(file, !enable ? 1 : 0, + NULL, TRUE, TRUE) == FALSE) { + mce_log(LL_ERR, + "%s: Event status *not* modified", + file); + goto EXIT; + } + + mce_log(LL_DEBUG, + "%s: events %s\n", + file, enable ? "enabled" : "disabled"); + status = TRUE; + +EXIT: + return status; +} + +/** + * Enable/disable touchscreen events + * + * @param enable TRUE enable events, FALSE disable events + * @return TRUE on success, FALSE on failure + */ +static gboolean ts_event_control(gboolean enable) +{ + return generic_event_control(mce_touchscreen_sysfs_disable_path, + enable); +} + +/** + * Enable/disable keypad events + * + * @param enable TRUE enable events, FALSE disable events + * @return TRUE on success, FALSE on failure + */ +static gboolean kp_event_control(gboolean enable) +{ + return generic_event_control(mce_keypad_sysfs_disable_path, enable); +} + +/** + * Enable touchscreen (events will be generated by kernel) + * + * @return TRUE on success, FALSE on failure + */ +static gboolean ts_enable(void) +{ + return ts_event_control(TRUE); +} + +/** + * Disable touchscreen (no events will be generated by kernel) + * + * @return TRUE on success, FALSE on failure + */ +static gboolean ts_disable(void) +{ + return ts_event_control(FALSE); +} + +/** + * Enable keypad (events will be generated by kernel) + * + * @return TRUE on success, FALSE on failure + */ +static gboolean kp_enable(void) +{ + return kp_event_control(TRUE); +} + +/** + * Disable keypad (no events will be generated by kernel) + * + * @return TRUE on success, FALSE on failure + */ +static gboolean kp_disable(void) +{ + return kp_event_control(FALSE); +} + +/** + * Enable touchscreen and keypad + * + * @return TRUE on success, FALSE on failure or partial failure + */ +static gboolean ts_kp_enable(void) +{ + gboolean status = TRUE; + + if (kp_enable() == FALSE) + status = FALSE; + + if (ts_enable() == FALSE) + status = FALSE; + + return status; +} + +/** + * Disable touchscreen and keypad + * + * @return TRUE on success, FALSE on failure or partial failure + */ +static gboolean ts_kp_disable(void) +{ + gboolean status = TRUE; + + if (kp_disable() == FALSE) + status = FALSE; + + if (ts_disable() == FALSE) + status = FALSE; + + return status; +} + +/** + * Policy based enabling of touchscreen and keypad + * + * @return TRUE on success, FALSE on failure or partial failure + */ +static gboolean ts_kp_enable_policy(void) +{ + system_state_t system_state = datapipe_get_gint(system_state_pipe); + cover_state_t lid_cover_state = datapipe_get_gint(lid_cover_pipe); + alarm_ui_state_t alarm_ui_state = + datapipe_get_gint(alarm_ui_state_pipe); + gboolean status = FALSE; + + /* If the cover is closed, don't bother */ + if (lid_cover_state == COVER_CLOSED) + goto EXIT2; + + if ((system_state == MCE_STATE_USER) || + (alarm_ui_state == MCE_ALARM_UI_RINGING_INT32) || + (alarm_ui_state == MCE_ALARM_UI_VISIBLE_INT32)) { + if (ts_kp_enable() == FALSE) + goto EXIT; + } + +EXIT2: + status = TRUE; + +EXIT: + return status; +} + +/** + * Policy based disabling of touchscreen and keypad + * + * @return TRUE on success, FALSE on failure + */ +static gboolean ts_kp_disable_policy(void) +{ + display_state_t display_state = datapipe_get_gint(display_state_pipe); + system_state_t system_state = datapipe_get_gint(system_state_pipe); + alarm_ui_state_t alarm_ui_state = + datapipe_get_gint(alarm_ui_state_pipe); + submode_t submode = mce_get_submode_int32(); + call_state_t call_state = datapipe_get_gint(call_state_pipe); + gboolean status = FALSE; + + /* If we're in softoff submode, always disable */ + if ((submode & MCE_SOFTOFF_SUBMODE) != 0) { + if (ts_kp_disable() == FALSE) + goto EXIT; + + goto EXIT2; + } + + /* If the Alarm UI is visible, don't disable, + * unless the tklock UI is active + */ + if (((alarm_ui_state == MCE_ALARM_UI_VISIBLE_INT32) || + (alarm_ui_state == MCE_ALARM_UI_RINGING_INT32)) && + (tklock_ui_state != MCE_TKLOCK_UI_NORMAL)) { + mce_log(LL_DEBUG, + "Alarm UI visible; refusing to disable touchscreen " + "and keypad events"); + goto EXIT2; + } + + if (system_state != MCE_STATE_USER) { + if (ts_kp_disable() == FALSE) + goto EXIT; + } else if ((display_state == MCE_DISPLAY_OFF) && + (is_tklock_enabled() == TRUE)) { + if (disable_kp_immediately == 2) { + if (ts_disable() == FALSE) + goto EXIT; + } else if (disable_kp_immediately == 1) { + /* Don't disable kp during call (volume must work) */ + if (call_state != CALL_STATE_NONE) { + if (ts_disable() == FALSE) + goto EXIT; + } else { + if (ts_kp_disable() == FALSE) { + goto EXIT; + } + } + } else if (ts_kp_disable() == FALSE) { + goto EXIT; + } + } else if (is_tklock_enabled() == TRUE) { + /* Don't disable kp during call (volume must work) */ + if ((disable_kp_immediately == 1) && + (call_state == CALL_STATE_NONE)) { + if (kp_disable() == FALSE) + goto EXIT; + } + + if (disable_ts_immediately == TRUE) + if (ts_disable() == FALSE) + goto EXIT; + } + +EXIT2: + status = TRUE; + +EXIT: + if (status == FALSE) { + mce_log(LL_ERR, "Failed to disable ts/kp events!"); + } + + return status; +} + +/** + * Synthesise activity, since activity is filtered when tklock is active; + * also, the lock key doesn't normally generate activity + */ +static void synthesise_activity(void) +{ + (void)execute_datapipe(&device_inactive_pipe, + GINT_TO_POINTER(FALSE), + USE_INDATA, CACHE_INDATA); +} + +/** + * Synthesise inactivity, since we want immediate inactivity + * when the tklock is activated + */ +static void synthesise_inactivity(void) +{ + (void)execute_datapipe(&device_inactive_pipe, + GINT_TO_POINTER(TRUE), + USE_INDATA, CACHE_INDATA); +} + +/** + * Send the touchscreen/keypad lock mode + * + * @param method_call A DBusMessage to reply to; + * pass NULL to send a tklock mode signal instead + * @return TRUE on success, FALSE on failure + */ +static gboolean send_tklock_mode(DBusMessage *const method_call) +{ + DBusMessage *msg = NULL; + const gchar *modestring; + gboolean status = FALSE; + + if (is_tklock_enabled() == TRUE) + modestring = MCE_TK_LOCKED; + else + modestring = MCE_TK_UNLOCKED; + + /* If method_call is set, send a reply, + * otherwise, send a signal + */ + if (method_call != NULL) { + msg = dbus_new_method_reply(method_call); + } else { + /* tklock_mode_ind */ + msg = dbus_new_signal(MCE_SIGNAL_PATH, MCE_SIGNAL_IF, + MCE_TKLOCK_MODE_SIG); + } + + /* Append the new mode */ + if (dbus_message_append_args(msg, + DBUS_TYPE_STRING, &modestring, + DBUS_TYPE_INVALID) == FALSE) { + mce_log(LL_CRIT, + "Failed to append %sargument to D-Bus message " + "for %s.%s", + method_call ? "reply " : "", + method_call ? MCE_REQUEST_IF : + MCE_SIGNAL_IF, + method_call ? MCE_TKLOCK_MODE_GET : + MCE_TKLOCK_MODE_SIG); + dbus_message_unref(msg); + goto EXIT; + } + + /* Send the message */ + status = dbus_send_message(msg); + +EXIT: + return status; +} + +/** + * D-Bus reply handler for touchscreen/keypad lock UI + * + * @param pending_call The DBusPendingCall + * @param data Unused + */ +static void tklock_reply_dbus_cb(DBusPendingCall *pending_call, + void *data) +{ + DBusMessage *reply; + dbus_int32_t retval; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + (void)data; + + mce_log(LL_DEBUG, "Received TKLock UI reply"); + + if ((reply = dbus_pending_call_steal_reply(pending_call)) == NULL) { + mce_log(LL_ERR, + "TKLock UI reply callback invoked, " + "but no pending call available"); + goto EXIT; + } + + /* Make sure we didn't get an error message */ + if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) { + char *error_msg; + + /* If we got an error, it's a string */ + if (dbus_message_get_args(reply, &error, + DBUS_TYPE_STRING, &error_msg, + DBUS_TYPE_INVALID) == FALSE) { + mce_log(LL_CRIT, + "Failed to get error reply argument " + "from %s.%s: %s", + SYSTEMUI_REQUEST_IF, SYSTEMUI_TKLOCK_OPEN_REQ, + error.message); + dbus_error_free(&error); + } else { + mce_log(LL_ERR, + "D-Bus call to %s.%s failed: %s", + SYSTEMUI_REQUEST_IF, SYSTEMUI_TKLOCK_OPEN_REQ, + error_msg); + + /* If the call failed, disable tklock */ + set_tklock_state(LOCK_OFF); + } + + goto EXIT2; + } + + /* Extract reply */ + if (dbus_message_get_args(reply, &error, + DBUS_TYPE_INT32, &retval, + DBUS_TYPE_INVALID) == FALSE) { + mce_log(LL_CRIT, + "Failed to get reply argument from %s.%s: %s", + SYSTEMUI_REQUEST_IF, SYSTEMUI_TKLOCK_OPEN_REQ, + error.message); + dbus_error_free(&error); + goto EXIT2; + } + + mce_log(LL_DEBUG, + "Return value: %d", + retval); + +EXIT2: + dbus_message_unref(reply); + +EXIT: + dbus_pending_call_unref(pending_call); + + return; +} + +/** + * Show the touchscreen/keypad lock UI + * + * @param mode The mode to open in; valid modes: + * TKLOCK_ENABLE_VISUAL (show the gesture unlock interface) + * TKLOCK_ONEINPUT (open the tklock in event eater mode) + * @param silent TRUE to disable infoprints, + * FALSE to enable infoprints + * @return TRUE on success, FALSE on FAILURE + */ +static gboolean open_tklock_ui(const dbus_uint32_t mode, + const dbus_bool_t silent) +{ + const gchar *const cb_service = MCE_SERVICE; + const gchar *const cb_path = MCE_REQUEST_PATH; + const gchar *const cb_interface = MCE_REQUEST_IF; + const gchar *const cb_method = MCE_TKLOCK_CB_REQ; + dbus_bool_t flicker_key = has_flicker_key; + tklock_ui_state_t new_tklock_ui_state; + gboolean status = FALSE; + + switch (mode) { + case TKLOCK_ONEINPUT: + new_tklock_ui_state = MCE_TKLOCK_UI_EVENT_EATER; + break; + + case TKLOCK_ENABLE_VISUAL: + new_tklock_ui_state = MCE_TKLOCK_UI_SLIDER; + break; + + default: + mce_log(LL_ERR, "Invalid TKLock UI mode requested"); + goto EXIT; + } + + /* com.nokia.system_ui.request.tklock_open */ + status = dbus_send(SYSTEMUI_SERVICE, SYSTEMUI_REQUEST_PATH, + SYSTEMUI_REQUEST_IF, SYSTEMUI_TKLOCK_OPEN_REQ, + tklock_reply_dbus_cb, + DBUS_TYPE_STRING, &cb_service, + DBUS_TYPE_STRING, &cb_path, + DBUS_TYPE_STRING, &cb_interface, + DBUS_TYPE_STRING, &cb_method, + DBUS_TYPE_UINT32, &mode, + DBUS_TYPE_BOOLEAN, &silent, + DBUS_TYPE_BOOLEAN, &flicker_key, + DBUS_TYPE_INVALID); + + if (status == FALSE) + goto EXIT; + + /* We managed to open the new UI; update accordingly */ + tklock_ui_state = new_tklock_ui_state; + +EXIT: + return status; +} + +/** + * Hide the touchscreen/keypad lock UI + * + * @param silent TRUE to disable infoprints, + * FALSE to enable infoprints + * @return TRUE on success, FALSE on failure + */ +static gboolean close_tklock_ui(const dbus_bool_t silent) +{ + gboolean status = FALSE; + + /* com.nokia.system_ui.request.tklock_close */ + status = dbus_send(SYSTEMUI_SERVICE, SYSTEMUI_REQUEST_PATH, + SYSTEMUI_REQUEST_IF, SYSTEMUI_TKLOCK_CLOSE_REQ, + NULL, + DBUS_TYPE_BOOLEAN, &silent, + DBUS_TYPE_INVALID); + + if (status == FALSE) + goto EXIT; + + /* TKLock UI closed */ + tklock_ui_state = MCE_TKLOCK_UI_NONE; + +EXIT: + return status; +} + +/** + * Enable the touchscreen/keypad lock without UI + * + * If the internal state indicates that the tklock is already enabled, + * silent mode will always be used; calling enable_tklock_raw() when + * the UI is already on-screen will NOT close the UI + * + * @return TRUE on success, FALSE on failure + */ +static void enable_tklock_raw(void) +{ + mce_add_submode_int32(MCE_TKLOCK_SUBMODE); + mce_rem_submode_int32(MCE_EVEATER_SUBMODE); + mce_rem_submode_int32(MCE_VISUAL_TKLOCK_SUBMODE); + (void)send_tklock_mode(NULL); + + /* Enable automagic relock */ + enable_autorelock(); +} + +/** + * Enable the touchscreen/keypad lock + * + * If the internal state indicates that the tklock is already enabled, + * silent mode will always be used + * + * @param silent TRUE to disable infoprints, FALSE to enable infoprints + * @return TRUE on success, FALSE on failure + */ +static gboolean enable_tklock(gboolean silent) +{ + gboolean status = FALSE; + + if (is_tklock_enabled() == TRUE) { + silent = TRUE; + } + + if (open_tklock_ui(TKLOCK_ENABLE_VISUAL, silent) == FALSE) + goto EXIT; + + enable_tklock_raw(); + + status = TRUE; + +EXIT: + return status; +} + +/** + * Cancel timeout for visual touchscreen/keypad lock forced blanking + */ +static void cancel_tklock_visual_forced_blank_timeout(void) +{ + /* Remove the timer source for visual tklock forced blanking */ + if (tklock_visual_forced_blank_timeout_cb_id != 0) { + g_source_remove(tklock_visual_forced_blank_timeout_cb_id); + tklock_visual_forced_blank_timeout_cb_id = 0; + } +} + +/** + * Cancel timeout for visual touchscreen/keypad lock blanking + */ +static void cancel_tklock_visual_blank_timeout(void) +{ + /* Remove the timer source for visual tklock blanking */ + if (tklock_visual_blank_timeout_cb_id != 0) { + g_source_remove(tklock_visual_blank_timeout_cb_id); + tklock_visual_blank_timeout_cb_id = 0; + } +} + +/** + * Timeout callback for visual touchscreen/keypad lock blanking + * + * @param data Unused + * @return Always returns FALSE, to disable the timeout + */ +static gboolean tklock_visual_blank_timeout_cb(gpointer data) +{ + (void)data; + + cancel_tklock_visual_blank_timeout(); + cancel_tklock_visual_forced_blank_timeout(); + + (void)execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_OFF), + USE_INDATA, CACHE_INDATA); + + return FALSE; +} + +/** + * Setup the timeout for touchscreen/keypad lock blanking + */ +static void setup_tklock_visual_blank_timeout(void) +{ + cancel_tklock_dim_timeout(); + cancel_tklock_visual_blank_timeout(); + + /* Setup blank timeout */ + tklock_visual_blank_timeout_cb_id = + g_timeout_add_seconds(DEFAULT_VISUAL_BLANK_DELAY, tklock_visual_blank_timeout_cb, NULL); + + /* Setup forced blank timeout */ + if (tklock_visual_forced_blank_timeout_cb_id == 0) { + tklock_visual_forced_blank_timeout_cb_id = + g_timeout_add_seconds(DEFAULT_VISUAL_FORCED_BLANK_DELAY, tklock_visual_blank_timeout_cb, NULL); + } +} + +/** + * Timeout callback for touchscreen/keypad lock dim + * + * @param data Unused + * @return Always returns FALSE, to disable the timeout + */ +static gboolean tklock_dim_timeout_cb(gpointer data) +{ + (void)data; + + tklock_dim_timeout_cb_id = 0; + + if (blank_immediately == TRUE) { + (void)execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_OFF), + USE_INDATA, CACHE_INDATA); + } else { + (void)execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_DIM), + USE_INDATA, CACHE_INDATA); + } + + return FALSE; +} + +/** + * Cancel timeout for tklock dimming + */ +static void cancel_tklock_dim_timeout(void) +{ + /* Remove the timer source for tklock dimming */ + if (tklock_dim_timeout_cb_id != 0) { + g_source_remove(tklock_dim_timeout_cb_id); + tklock_dim_timeout_cb_id = 0; + } +} + +/** + * Setup a timeout for tklock dimming + */ +static void setup_tklock_dim_timeout(void) +{ + cancel_tklock_dim_timeout(); + + /* Setup new timeout */ + tklock_dim_timeout_cb_id = + g_timeout_add_seconds(dim_delay, tklock_dim_timeout_cb, NULL); +} + +/** + * Helper function to setup dim/blank timeouts according to policies + * + * @param force Force immediate dimming/blanking; + * MCE_DISPLAY_OFF -- force immediate display off + * MCE_DISPLAY_DIM -- force immediate display dim + * MCE_DISPLAY_ON -- N/A + * MCE_DISPLAY_UNDEF -- keep current display state + */ +static void setup_dim_blank_timeout_policy(display_state_t force) +{ + display_state_t display_state = datapipe_get_gint(display_state_pipe); + + cancel_tklock_visual_forced_blank_timeout(); + cancel_tklock_visual_blank_timeout(); + + /* If the display is already blank, don't bother */ + if (display_state == MCE_DISPLAY_OFF) + goto EXIT; + + /* If we're forcing blank, + * or if the display is already dimmed and we blank immediately, + * or if the we dim and blank immediately, then blank + * + * If we dim immediately, dim the screen (blank timeout takes care + * of the rest) else use the dim timeout + */ + if ((force == MCE_DISPLAY_OFF) || + (((display_state == MCE_DISPLAY_DIM) || + (dim_immediately == TRUE)) && + (blank_immediately == TRUE))) { + (void)execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_OFF), + USE_INDATA, CACHE_INDATA); + } else if ((force == MCE_DISPLAY_DIM) || (dim_immediately == TRUE)) { + (void)execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_DIM), + USE_INDATA, CACHE_INDATA); + } else { + setup_tklock_dim_timeout(); + } + +EXIT: + return; +} + +/** + * Enable the touchscreen/keypad lock with policy + * + * If the internal state indicates that the tklock is already enabled, + * silent mode will always be used + * + * @param force_blank Force immediate blanking + * @return TRUE on success, FALSE on failure + */ +static gboolean enable_tklock_policy(gboolean force_blank) +{ + system_state_t system_state = datapipe_get_gint(system_state_pipe); + gboolean status = FALSE; + + /* If we're in any other state than USER, don't enable tklock */ + if (system_state != MCE_STATE_USER) { + status = TRUE; + goto EXIT; + } + + /* Enable lock */ + if (enable_tklock(force_blank | + dim_immediately | + blank_immediately) == FALSE) + goto EXIT; + + setup_dim_blank_timeout_policy(MCE_DISPLAY_OFF); + + /* Disable touchscreen and keypad */ + (void)ts_kp_disable_policy(); + + status = TRUE; + +EXIT: + return status; +} + +/** + * Disable the touchscreen/keypad lock + * + * If the internal state indicates that the tklock is already disabled, + * silent mode will always be used + * + * @param silent Enable without infoprint + * @return TRUE on success, FALSE on failure + */ +static gboolean disable_tklock(gboolean silent) +{ + gboolean status = FALSE; + + /* On startup of MCE, we always disable + * the touchscreen/keypad lock and single event eater + */ + if (is_tklock_enabled() == FALSE) { + silent = TRUE; + } + + /* Only disable the UI if the active UI is the tklock */ + if ((tklock_ui_state == MCE_TKLOCK_UI_NORMAL) || + (tklock_ui_state == MCE_TKLOCK_UI_SLIDER)) { + if (close_tklock_ui(silent) == FALSE) + goto EXIT; + } + + /* Disable timeouts, just to be sure */ + cancel_tklock_visual_forced_blank_timeout(); + cancel_tklock_visual_blank_timeout(); + cancel_tklock_unlock_timeout(); + cancel_tklock_dim_timeout(); + + mce_rem_submode_int32(MCE_VISUAL_TKLOCK_SUBMODE); + mce_rem_submode_int32(MCE_TKLOCK_SUBMODE); + (void)send_tklock_mode(NULL); + (void)ts_kp_enable(); + status = TRUE; + +EXIT: + return status; +} + +/** + * Enable the touchscreen/keypad single event eater + * + * @return TRUE on success, FALSE on failure + */ +static gboolean enable_eveater(void) +{ + system_state_t system_state = datapipe_get_gint(system_state_pipe); + alarm_ui_state_t alarm_ui_state = + datapipe_get_gint(alarm_ui_state_pipe); + gboolean status = TRUE; + + /* If we're in acting dead and no alarm is visible, + * don't activate the event eater + */ + if ((system_state == MCE_STATE_ACTDEAD) && + ((alarm_ui_state != MCE_ALARM_UI_VISIBLE_INT32) || + (alarm_ui_state != MCE_ALARM_UI_RINGING_INT32))) + goto EXIT; + + /* If we're already showing a tklock UI, exit */ + if ((tklock_ui_state != MCE_TKLOCK_UI_NONE) && + (tklock_ui_state != MCE_TKLOCK_UI_UNSET)) + goto EXIT; + + if ((status = open_tklock_ui(TKLOCK_ONEINPUT, TRUE)) == TRUE) + mce_add_submode_int32(MCE_EVEATER_SUBMODE); + +EXIT: + return status; +} + +/** + * Disable the touchscreen/keypad single event eater + * + * @return TRUE on success, FALSE on failure + */ +static gboolean disable_eveater(void) +{ + gboolean status = FALSE; + + /* If the event eater isn't enabled, ignore the request */ + if (is_eveater_enabled() == FALSE) { + status = TRUE; + goto EXIT; + } + + /* Only disable the UI if the active UI is the event eater */ + if (tklock_ui_state == MCE_TKLOCK_UI_EVENT_EATER) { + if (close_tklock_ui(TRUE) == FALSE) + goto EXIT; + } + + mce_rem_submode_int32(MCE_EVEATER_SUBMODE); + status = TRUE; + +EXIT: + return status; +} + +/** + * Timeout callback for tklock unlock + * + * @param data Unused + * @return Always returns FALSE, to disable the timeout + */ +static gboolean tklock_unlock_timeout_cb(gpointer data) +{ + (void)data; + + tklock_unlock_timeout_cb_id = 0; + + set_tklock_state(LOCK_OFF); + + return FALSE; +} + +/** + * Cancel timeout for delayed unlocking of touchscreen/keypad lock + */ +static void cancel_tklock_unlock_timeout(void) +{ + /* Remove the timer source for delayed tklock unlocking */ + if (tklock_unlock_timeout_cb_id != 0) { + g_source_remove(tklock_unlock_timeout_cb_id); + tklock_unlock_timeout_cb_id = 0; + } +} + +/** + * Setup a timeout for delayed unlocking of touchscreen/keypad lock + */ +static void setup_tklock_unlock_timeout(void) +{ + cancel_tklock_unlock_timeout(); + + /* Setup new timeout */ + tklock_unlock_timeout_cb_id = + g_timeout_add(MCE_TKLOCK_UNLOCK_DELAY, + tklock_unlock_timeout_cb, NULL); +} + +/** + * Enable the touchscreen/keypad autolock + * + * Will enable touchscreen/keypad lock if tk_autolock_enabled is TRUE, + * and enable the touchscreen/keypad single event eater if FALSE + * + * @return TRUE on success, FALSE on failure + */ +static gboolean enable_autokeylock(void) +{ + system_state_t system_state = datapipe_get_gint(system_state_pipe); + cover_state_t slide_state = datapipe_get_gint(keyboard_slide_pipe); + alarm_ui_state_t alarm_ui_state = + datapipe_get_gint(alarm_ui_state_pipe); + call_state_t call_state = datapipe_get_gint(call_state_pipe); + submode_t submode = datapipe_get_gint(submode_pipe); + gboolean status = TRUE; + + /* Don't enable automatic tklock during bootup */ + if ((submode & MCE_BOOTUP_SUBMODE) != 0) + goto EXIT; + + if ((system_state == MCE_STATE_USER) && + ((slide_state != COVER_OPEN) || + (autolock_with_open_slide == TRUE)) && + (tk_autolock_enabled == TRUE) && + (alarm_ui_state != MCE_ALARM_UI_VISIBLE_INT32) && + (alarm_ui_state != MCE_ALARM_UI_RINGING_INT32) && + ((call_state == CALL_STATE_INVALID) || + (call_state == CALL_STATE_NONE))) { + if ((status = enable_tklock(TRUE)) == TRUE) + (void)ts_kp_disable_policy(); + } else { + if (((alarm_ui_state == MCE_ALARM_UI_VISIBLE_INT32) || + (alarm_ui_state == MCE_ALARM_UI_RINGING_INT32)) && + ((tklock_ui_state == MCE_TKLOCK_UI_NONE) || + (tklock_ui_state == MCE_TKLOCK_UI_EVENT_EATER))) + disable_autorelock(); + + status = enable_eveater(); + } + +EXIT: + return status; +} + +/** + * State machine for lock change requests + * + * @param lock_state The requested touchscreen/keypad lock state + */ +static void set_tklock_state(lock_state_t lock_state) +{ + submode_t submode = mce_get_submode_int32(); + + /* Ignore requests to enable tklock during bootup */ + switch (lock_state) { + case LOCK_TOGGLE: + if (is_tklock_enabled() == TRUE) + break; + + case LOCK_ON: + case LOCK_ON_DIMMED: + case LOCK_ON_SILENT: + case LOCK_ON_SILENT_DIMMED: + case LOCK_ON_PROXIMITY: + if ((submode & MCE_BOOTUP_SUBMODE) != 0) + goto EXIT; + + default: + break; + } + + switch (lock_state) { + case LOCK_OFF: + (void)disable_tklock(FALSE); + (void)disable_eveater(); + disable_autorelock(); + synthesise_activity(); + break; + + case LOCK_OFF_NO_ACTIVITY: + (void)disable_tklock(FALSE); + (void)disable_eveater(); + disable_autorelock(); + break; + + case LOCK_OFF_SILENT: + (void)disable_tklock(TRUE); + (void)disable_eveater(); + disable_autorelock(); + synthesise_activity(); + break; + + case LOCK_OFF_DELAYED: + setup_tklock_unlock_timeout(); + break; + + case LOCK_OFF_PROXIMITY: + (void)disable_tklock(FALSE); + (void)disable_eveater(); + synthesise_activity(); + break; + + case LOCK_ON: + synthesise_inactivity(); + + if (enable_tklock(FALSE) == TRUE) + setup_dim_blank_timeout_policy(MCE_DISPLAY_UNDEF); + + break; + + case LOCK_ON_DIMMED: + synthesise_inactivity(); + + if (enable_tklock(FALSE) == TRUE) + setup_dim_blank_timeout_policy(MCE_DISPLAY_DIM); + + break; + + case LOCK_ON_SILENT: + synthesise_inactivity(); + + if (enable_tklock(TRUE) == TRUE) + setup_dim_blank_timeout_policy(MCE_DISPLAY_UNDEF); + + break; + + case LOCK_ON_SILENT_DIMMED: + synthesise_inactivity(); + + if (enable_tklock(TRUE) == TRUE) + setup_dim_blank_timeout_policy(MCE_DISPLAY_DIM); + + break; + + case LOCK_ON_PROXIMITY: + synthesise_inactivity(); + enable_tklock_raw(); + setup_dim_blank_timeout_policy(MCE_DISPLAY_UNDEF); + break; + + case LOCK_TOGGLE: + /* Touchscreen/keypad lock */ + if ((is_tklock_enabled() == FALSE) || + ((is_tklock_enabled() == TRUE) && + (tklock_ui_state == MCE_TKLOCK_UI_NONE))) { + synthesise_inactivity(); + + /* XXX: Should this be a duplicate of LOCK_ON? */ + (void)enable_tklock_policy(FALSE); + } else { + /* Exact duplicate of LOCK_OFF */ + (void)disable_tklock(FALSE); + (void)disable_eveater(); + disable_autorelock(); + synthesise_activity(); + } + + break; + + default: + break; + } + +EXIT: + return; +} + +/** + * Visual touchscreen/keypad lock logic + * + * @param powerkey TRUE if the visual tklock was triggered by the powerkey + * FALSE if not + */ +static void trigger_visual_tklock(gboolean powerkey) +{ + alarm_ui_state_t alarm_ui_state = + datapipe_get_gint(alarm_ui_state_pipe); + system_state_t system_state = datapipe_get_gint(system_state_pipe); + display_state_t display_state = datapipe_get_gint(display_state_pipe); + + if ((is_tklock_enabled() == FALSE) || + (is_autorelock_enabled() == FALSE) || + (system_state != MCE_STATE_USER) || + (alarm_ui_state == MCE_ALARM_UI_VISIBLE_INT32) || + (alarm_ui_state == MCE_ALARM_UI_RINGING_INT32)) { + goto EXIT; + } + + /* Only activate visual tklock if the display is off; + * else blank the screen again + */ + if (display_state == MCE_DISPLAY_OFF) { + if (open_tklock_ui(TKLOCK_ENABLE_VISUAL, FALSE) == TRUE) { + mce_add_submode_int32(MCE_VISUAL_TKLOCK_SUBMODE); + setup_tklock_visual_blank_timeout(); + (void)execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_ON), + USE_INDATA, CACHE_INDATA); + } + } else if (powerkey == TRUE) { + /* XXX: we probably want to make this configurable */ + /* Blank screen */ + if (tklock_dim_timeout_cb_id == 0) { + (void)execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_OFF), + USE_INDATA, CACHE_INDATA); + cancel_tklock_visual_blank_timeout(); + } + } else { + /* If visual tklock is enabled, reset the timeout */ + if (is_visual_tklock_enabled()) { + setup_tklock_visual_blank_timeout(); + } + } + +EXIT: + return; +} + +/** + * D-Bus callback for the get tklock mode method call + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean tklock_mode_get_req_dbus_cb(DBusMessage *const msg) +{ + gboolean status = FALSE; + + mce_log(LL_DEBUG, "Received tklock mode get request"); + + /* Try to send a reply that contains the current tklock mode */ + if (send_tklock_mode(msg) == FALSE) + goto EXIT; + + status = TRUE; + +EXIT: + return status; +} + +/** + * D-Bus callback for the tklock mode change method call + * + * @param msg The D-Bus message + * @return TRUE on success, FALSE on failure + */ +static gboolean tklock_mode_change_req_dbus_cb(DBusMessage *const msg) +{ + dbus_bool_t no_reply = dbus_message_get_no_reply(msg); + const gchar *mode = NULL; + gboolean status = FALSE; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + mce_log(LL_DEBUG, "Received tklock mode change request"); + + if (dbus_message_get_args(msg, &error, + DBUS_TYPE_STRING, &mode, + DBUS_TYPE_INVALID) == FALSE) { + // XXX: should we return an error instead? + mce_log(LL_CRIT, + "Failed to get argument from %s.%s: %s", + MCE_REQUEST_IF, MCE_TKLOCK_MODE_CHANGE_REQ, + error.message); + dbus_error_free(&error); + goto EXIT; + } + + /* Try to change to the requested tklock mode + * XXX: right now we silently ignore invalid modes; + * should we return an error? + */ + if (strcmp(MCE_TK_LOCKED, mode) == 0) { + set_tklock_state(LOCK_ON); + } else if (strcmp(MCE_TK_LOCKED_DIM, mode) == 0) { + set_tklock_state(LOCK_ON_DIMMED); + } else if (strcmp(MCE_TK_SILENT_LOCKED, mode) == 0) { + set_tklock_state(LOCK_ON_SILENT); + } else if (strcmp(MCE_TK_SILENT_LOCKED_DIM, mode) == 0) { + set_tklock_state(LOCK_ON_SILENT_DIMMED); + } else if (strcmp(MCE_TK_UNLOCKED, mode) == 0) { + set_tklock_state(LOCK_OFF); + + /* Clear the tklock submode; external unlock + * requests overrides automagic relocking + */ + saved_submode = ~(~saved_submode | MCE_TKLOCK_SUBMODE); + } else if (strcmp(MCE_TK_SILENT_UNLOCKED, mode) == 0) { + set_tklock_state(LOCK_OFF_SILENT); + + /* Clear the tklock submode; external unlock + * requests overrides automagic relocking + */ + saved_submode = ~(~saved_submode | MCE_TKLOCK_SUBMODE); + } else { + mce_log(LL_ERR, + "Received an invalid tklock mode; ignoring"); + } + + if (no_reply == FALSE) { + DBusMessage *reply = dbus_new_method_reply(msg); + + status = dbus_send_message(reply); + } else { + status = TRUE; + } + +EXIT: + return status; +} + +/** + * D-Bus callback from SystemUI touchscreen/keypad lock + * + * @todo the calls to disable_tklock/open_tklock_ui need error handling + * + * @param msg D-Bus message with the lock status + * @return TRUE on success, FALSE on failure + */ +static gboolean systemui_tklock_dbus_cb(DBusMessage *const msg) +{ + dbus_int32_t result = INT_MAX; + gboolean status = FALSE; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + mce_log(LL_DEBUG, "Received tklock callback"); + + if (dbus_message_get_args(msg, &error, + DBUS_TYPE_INT32, &result, + DBUS_TYPE_INVALID) == FALSE) { + // XXX: should we return an error instead? + mce_log(LL_CRIT, + "Failed to get argument from %s.%s: %s", + MCE_REQUEST_IF, MCE_TKLOCK_CB_REQ, + error.message); + dbus_error_free(&error); + goto EXIT; + } + + mce_log(LL_DEBUG, "tklock callback value: %d", result); + + switch (result) { + case TKLOCK_UNLOCK: + /* Unlock the tklock */ + if ((tklock_ui_state == MCE_TKLOCK_UI_NORMAL) || + (tklock_ui_state == MCE_TKLOCK_UI_SLIDER)) { + (void)execute_datapipe(&tk_lock_pipe, + GINT_TO_POINTER(LOCK_OFF), + USE_INDATA, CACHE_INDATA); + } else { + disable_eveater(); + } + + break; + + case TKLOCK_CLOSED: + default: + break; + } + + status = TRUE; + +EXIT: + return status; +} + +/** + * GConf callback for touchscreen/keypad lock related settings + * + * @param gcc Unused + * @param id Connection ID from gconf_client_notify_add() + * @param entry The modified GConf entry + * @param data Unused + */ +static void tklock_gconf_cb(GConfClient *const gcc, const guint id, + GConfEntry *const entry, gpointer const data) +{ + const GConfValue *gcv = gconf_entry_get_value(entry); + + (void)gcc; + (void)data; + + /* Key is unset */ + if (gcv == NULL) { + mce_log(LL_DEBUG, + "GConf Key `%s' has been unset", + gconf_entry_get_key(entry)); + goto EXIT; + } + + if (id == tk_autolock_enabled_cb_id) { + tk_autolock_enabled = gconf_value_get_bool(gcv) ? 1 : 0; + } else { + mce_log(LL_WARN, "Spurious GConf value received; confused!"); + } + +EXIT: + return; +} + +/** + * Process the proximity state + */ +static void process_proximity_state(void) +{ + cover_state_t slide_state = datapipe_get_gint(keyboard_slide_pipe); + cover_state_t proximity_sensor_state = + datapipe_get_gint(proximity_sensor_pipe); + audio_route_t audio_route = datapipe_get_gint(audio_route_pipe); + alarm_ui_state_t alarm_ui_state = + datapipe_get_gint(alarm_ui_state_pipe); + call_state_t call_state = datapipe_get_gint(call_state_pipe); + + if (((alarm_ui_state == MCE_ALARM_UI_VISIBLE_INT32) || + (alarm_ui_state == MCE_ALARM_UI_RINGING_INT32)) && + (call_state == CALL_STATE_NONE) && + ((autorelock_triggers & AUTORELOCK_ON_PROXIMITY) == 0)) + goto EXIT; + + /* If there's an incoming call or an alarm is visible, + * the proximity sensor reports open, and the tklock + * or event eater is active, unblank and unlock the display + */ + if (((call_state == CALL_STATE_RINGING) || + ((alarm_ui_state == MCE_ALARM_UI_VISIBLE_INT32) || + (alarm_ui_state == MCE_ALARM_UI_RINGING_INT32))) && + (proximity_sensor_state == COVER_OPEN)) { + (void)ts_kp_enable_policy(); + + if (is_tklock_enabled() || is_eveater_enabled()) { + /* Disable tklock/event eater */ + if (close_tklock_ui(TRUE) == FALSE) + goto EXIT; + + /* Disable timeouts, just to be sure */ + cancel_tklock_visual_forced_blank_timeout(); + cancel_tklock_visual_blank_timeout(); + cancel_tklock_unlock_timeout(); + cancel_tklock_dim_timeout(); + } + + /* Unblank screen */ + (void)execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_ON), + USE_INDATA, CACHE_INDATA); + + if ((alarm_ui_state != MCE_ALARM_UI_VISIBLE_INT32) || + (alarm_ui_state != MCE_ALARM_UI_RINGING_INT32)) + autorelock_triggers = AUTORELOCK_ON_PROXIMITY; + else + autorelock_triggers = ~(~autorelock_triggers | + AUTORELOCK_ON_PROXIMITY); + + tklock_proximity = FALSE; + goto EXIT; + } + + /* If there's no incoming or active call, or the audio isn't + * routed to the handset or headset, or if the slide is open, exit + */ + if (((((call_state != CALL_STATE_RINGING) || + (proximity_lock_when_ringing != TRUE)) && + (call_state != CALL_STATE_ACTIVE)) || + ((audio_route != AUDIO_ROUTE_HANDSET) && + (audio_route != AUDIO_ROUTE_HEADSET) && + ((audio_route != AUDIO_ROUTE_SPEAKER) || + (call_state != CALL_STATE_RINGING)))) || + ((proximity_lock_with_open_slide == FALSE) && + (slide_state == COVER_OPEN))) { + goto EXIT; + } + + switch (proximity_sensor_state) { + case COVER_OPEN: + if (autorelock_triggers == AUTORELOCK_ON_PROXIMITY) { + if ((is_tklock_enabled() == TRUE) && + (is_autorelock_enabled() == TRUE)) + /* Disable tklock */ + set_tklock_state(LOCK_OFF_PROXIMITY); + + /* Unblank screen */ + (void)execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_ON), + USE_INDATA, CACHE_INDATA); + + tklock_proximity = FALSE; + } + + break; + + case COVER_CLOSED: + if ((inhibit_proximity_relock == MCE_ALLOW_PROXIMITY_RELOCK) && + (((is_tklock_enabled() == FALSE) && + (is_autorelock_enabled() == FALSE)) || + ((is_autorelock_enabled() == TRUE) && + (autorelock_triggers == AUTORELOCK_ON_PROXIMITY)))) { + tklock_proximity = TRUE; + + if ((alarm_ui_state != MCE_ALARM_UI_VISIBLE_INT32) && + (alarm_ui_state != MCE_ALARM_UI_RINGING_INT32)) + autorelock_triggers = AUTORELOCK_ON_PROXIMITY; + + /* Enable proximity tklock */ + set_tklock_state(LOCK_ON_PROXIMITY); + } + + break; + + default: + break; + } + +EXIT: + return; +} + +/** + * Datapipe trigger for device inactivity + * + * @param data The inactivity stored in a pointer; + * TRUE if the device is inactive, + * FALSE if the device is active + */ +static void device_inactive_trigger(gconstpointer const data) +{ + gboolean device_inactive = GPOINTER_TO_INT(data); + + if (device_inactive == FALSE) { + if ((is_tklock_enabled() == TRUE) && + (tklock_visual_blank_timeout_cb_id != 0)) { + setup_tklock_visual_blank_timeout(); + } + } +} + +/** + * Datapipe trigger for the keyboard slide + * + * @param data COVER_OPEN if the keyboard slide is open, + * COVER_CLOSED if the keyboard slide is closed + */ +static void keyboard_slide_trigger(gconstpointer const data) +{ + display_state_t display_state = datapipe_get_gint(display_state_pipe); + system_state_t system_state = datapipe_get_gint(system_state_pipe); + cover_state_t kbd_slide_state = GPOINTER_TO_INT(data); + + if ((system_state != MCE_STATE_USER)) + goto EXIT; + + switch (kbd_slide_state) { + case COVER_OPEN: + if (is_tklock_enabled() == TRUE) { + /* Only the trigger that caused the unlock + * should trigger autorelock + */ + if ((autorelock_triggers & AUTORELOCK_KBD_SLIDE) != 0) + autorelock_triggers = AUTORELOCK_KBD_SLIDE; + + /* Disable tklock */ + (void)disable_tklock(FALSE); + synthesise_activity(); + } + + break; + + case COVER_CLOSED: + if (((tk_autolock_enabled == TRUE) && + (display_state == MCE_DISPLAY_OFF)) || + ((is_autorelock_enabled() == TRUE) && + ((autorelock_triggers & AUTORELOCK_KBD_SLIDE) != 0)) || + (always_lock_on_slide_close == TRUE)) { + synthesise_inactivity(); + + /* This will also reset the autorelock policy */ + (void)enable_tklock_policy(FALSE); + } + + break; + + default: + break; + } + + process_proximity_state(); + +EXIT: + return; +} + +/** + * Datapipe trigger for the [lock] flicker key + * + * @param data 1 if the key was pressed, 0 if the key was released + */ +static void lockkey_trigger(gconstpointer const data) +{ + system_state_t system_state = datapipe_get_gint(system_state_pipe); + call_state_t call_state = datapipe_get_gint(call_state_pipe); + + /* Only react on the [lock] flicker key in USER state */ + if ((GPOINTER_TO_INT(data) == 1) && (system_state == MCE_STATE_USER)) { + /* Using the flicker key during a call + * disables proximity based locking/unlocking + */ + if (call_state == CALL_STATE_ACTIVE) { + autorelock_triggers = ~(~autorelock_triggers | + AUTORELOCK_ON_PROXIMITY); + inhibit_proximity_relock = MCE_INHIBIT_PROXIMITY_RELOCK; + } + + /* Execute lock action */ + (void)execute_datapipe(&tk_lock_pipe, + GINT_TO_POINTER(LOCK_TOGGLE), + USE_INDATA, CACHE_INDATA); + } +} + +/** + * Datapipe trigger for keypresses + * + * @param data Keypress state + */ +static void keypress_trigger(gconstpointer const data) +{ + struct input_event const *const *evp; + struct input_event const *ev; + + /* Don't dereference until we know it's safe */ + if (data == NULL) + goto EXIT; + + evp = data; + ev = *evp; + + disable_autorelock_policy(); + + /* If the keypress is any of: + * KEY_POWER, KEY_CAMERA, KEY_VOLUMEDOWN, KEY_VOLUMEUP + * trigger the visual unlock UI + */ + if (((ev != NULL) && + ((ev->code == KEY_POWER) || (ev->code == KEY_CAMERA) || + ((volkey_visual_trigger == TRUE) && + ((ev->code == KEY_VOLUMEDOWN) || (ev->code == KEY_VOLUMEUP)))) && + (ev->value == 1))) { + trigger_visual_tklock(ev->code == KEY_POWER); + } + +EXIT: + return; +} + +/** + * Datapipe trigger for camera button + * + * @param data Unused + */ +static void camera_button_trigger(gconstpointer const data) +{ + (void)data; + + disable_autorelock_policy(); + trigger_visual_tklock(FALSE); +} + +/** + * Datapipe trigger for touchscreen events + * + * @param data Unused + */ +static void touchscreen_trigger(gconstpointer const data) +{ + (void)data; + + disable_autorelock_policy(); +} + +/** + * Handle system state change + * + * @param data The system state stored in a pointer + */ +static void system_state_trigger(gconstpointer data) +{ + system_state_t system_state = GPOINTER_TO_INT(data); + + switch (system_state) { + case MCE_STATE_SHUTDOWN: + case MCE_STATE_REBOOT: + case MCE_STATE_ACTDEAD: + (void)ts_kp_disable_policy(); + break; + + case MCE_STATE_USER: + default: + (void)ts_kp_enable_policy(); + break; + } +} + +/** + * Handle display state change + * + * @param data The display state stored in a pointer + */ +static void display_state_trigger(gconstpointer data) +{ + alarm_ui_state_t alarm_ui_state = + datapipe_get_gint(alarm_ui_state_pipe); + static display_state_t old_display_state = MCE_DISPLAY_UNDEF; + display_state_t display_state = GPOINTER_TO_INT(data); + + if (old_display_state == display_state) + goto EXIT; + + switch (display_state) { + case MCE_DISPLAY_OFF: + if (tklock_proximity == TRUE) { + (void)ts_kp_disable_policy(); + } else if ((alarm_ui_state != MCE_ALARM_UI_RINGING_INT32) && + (is_tklock_enabled() == TRUE)) { + if (enable_tklock(TRUE) == TRUE) + (void)ts_kp_disable_policy(); + } else { + (void)enable_autokeylock(); + } + + break; + + case MCE_DISPLAY_DIM: + if (tklock_proximity == FALSE) + enable_eveater(); + + /* If the display transitions from OFF or UNDEF, + * to DIM or ON, do policy based enable + */ + if ((old_display_state == MCE_DISPLAY_UNDEF) || + (old_display_state == MCE_DISPLAY_OFF)) { + (void)ts_kp_enable_policy(); + } + + break; + + case MCE_DISPLAY_ON: + default: + /* If the display transitions from OFF or UNDEF, + * to DIM or ON, do policy based enable + */ + if ((old_display_state == MCE_DISPLAY_UNDEF) || + (old_display_state == MCE_DISPLAY_OFF)) { + (void)ts_kp_enable_policy(); + + /* If visual tklock is enabled, reset the timeout, + * and attempt to reopen the visual tklock, + * just in case sysuid has been malfunctioning + */ + if (is_visual_tklock_enabled() == TRUE) { + open_tklock_ui(TKLOCK_ENABLE_VISUAL, FALSE); + setup_tklock_visual_blank_timeout(); + } + } + + (void)disable_eveater(); + break; + } + + old_display_state = display_state; + +EXIT: + return; +} + +/** + * Handle alarm UI state change + * + * @param data The alarm state stored in a pointer + */ +static void alarm_ui_state_trigger(gconstpointer data) +{ + system_state_t system_state = datapipe_get_gint(system_state_pipe); + cover_state_t proximity_sensor_state = + datapipe_get_gint(proximity_sensor_pipe); + alarm_ui_state_t alarm_ui_state = GPOINTER_TO_INT(data); + call_state_t call_state = datapipe_get_gint(call_state_pipe); + + switch (alarm_ui_state) { + case MCE_ALARM_UI_VISIBLE_INT32: + tklock_proximity = FALSE; + + if (is_tklock_enabled() == TRUE) { + /* Event eater is used when tklock is disabled, + * so make sure to disable it if we enable the tklock + */ + disable_eveater(); + + if (open_tklock_ui(TKLOCK_ENABLE_VISUAL, + TRUE) == FALSE) { + disable_tklock(TRUE); + goto EXIT; + } + + enable_autorelock(); + setup_dim_blank_timeout_policy(MCE_DISPLAY_OFF); + } else if (is_eveater_enabled() == TRUE) { + (void)ts_kp_enable_policy(); + + if (open_tklock_ui(TKLOCK_ONEINPUT, TRUE) == FALSE) { + disable_eveater(); + goto EXIT; + } + + setup_dim_blank_timeout_policy(MCE_DISPLAY_UNDEF); + } + + break; + + case MCE_ALARM_UI_RINGING_INT32: + /* If the proximity state is "open", + * disable tklock/event eater UI and proximity sensor + */ + if (proximity_sensor_state == COVER_OPEN) { + (void)ts_kp_enable_policy(); + + autorelock_triggers = ~(~autorelock_triggers | + AUTORELOCK_ON_PROXIMITY); + tklock_proximity = FALSE; + + /* Disable tklock/event eater */ + if (close_tklock_ui(TRUE) == FALSE) + goto EXIT; + + /* Disable timeouts, just to be sure */ + cancel_tklock_visual_forced_blank_timeout(); + cancel_tklock_visual_blank_timeout(); + cancel_tklock_unlock_timeout(); + cancel_tklock_dim_timeout(); + + /* Unblank screen */ + (void)execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_ON), + USE_INDATA, CACHE_INDATA); + } else { + autorelock_triggers = (autorelock_triggers | + AUTORELOCK_ON_PROXIMITY); + tklock_proximity = is_tklock_enabled(); + } + + break; + + case MCE_ALARM_UI_OFF_INT32: + (void)ts_kp_disable_policy(); + tklock_proximity = FALSE; + + /* In acting dead the event eater is only + * used when showing the alarm UI + */ + if (system_state != MCE_STATE_USER) { + disable_eveater(); + } else if ((call_state != CALL_STATE_INVALID) && + (call_state != CALL_STATE_NONE) && + (is_tklock_enabled() == TRUE)) { + disable_eveater(); + set_tklock_state(LOCK_OFF); + } else if (is_tklock_enabled() == TRUE) { + /* Event eater is used when tklock is disabled, + * so make sure to disable it if we enable the tklock + */ + disable_eveater(); + + if (open_tklock_ui(TKLOCK_ENABLE_VISUAL, + TRUE) == FALSE) { + disable_tklock(TRUE); + goto EXIT; + } + + enable_autorelock(); + setup_dim_blank_timeout_policy(MCE_DISPLAY_OFF); + } else if (is_eveater_enabled() == TRUE) { + if (open_tklock_ui(TKLOCK_ONEINPUT, TRUE) == FALSE) { + disable_eveater(); + goto EXIT; + } + + setup_dim_blank_timeout_policy(MCE_DISPLAY_UNDEF); + } + + break; + + default: + break; + } + +EXIT: + return; +} + +/** + * Handle lid cover sensor state change + * + * @param data The lid cover state stored in a pointer + */ +static void lid_cover_trigger(gconstpointer data) +{ + system_state_t system_state = datapipe_get_gint(system_state_pipe); + cover_state_t lid_cover_state = GPOINTER_TO_INT(data); + + switch (lid_cover_state) { + case COVER_OPEN: + if (system_state == MCE_STATE_USER) { + setup_tklock_unlock_timeout(); + (void)execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_ON), + USE_INDATA, CACHE_INDATA); + } + + break; + + case COVER_CLOSED: + if (system_state == MCE_STATE_USER) { + synthesise_inactivity(); + + if (enable_tklock_policy(FALSE) == TRUE) { + /* Blank screen */ + (void)execute_datapipe(&display_state_pipe, + GINT_TO_POINTER(MCE_DISPLAY_OFF), + USE_INDATA, CACHE_INDATA); + } + } + + break; + + default: + break; + } +} + +/** + * Handle proximity sensor state change + * + * @param data Unused + */ +static void proximity_sensor_trigger(gconstpointer data) +{ + (void)data; + + process_proximity_state(); +} + +/** + * Handle lens cover state change + * + * @param data The lens cover state stored in a pointer + */ +static void lens_cover_trigger(gconstpointer data) +{ + system_state_t system_state = datapipe_get_gint(system_state_pipe); + cover_state_t lens_cover_state = GPOINTER_TO_INT(data); + + if ((system_state != MCE_STATE_USER)) + goto EXIT; + + if (lens_cover_unlock == FALSE) + goto EXIT; + + switch (lens_cover_state) { + case COVER_OPEN: + if (is_tklock_enabled() == TRUE) { + /* Only the trigger that caused the unlock + * should trigger autorelock + */ + if ((autorelock_triggers & AUTORELOCK_LENS_COVER) != 0) + autorelock_triggers = AUTORELOCK_LENS_COVER; + + /* Disable tklock */ + (void)disable_tklock(FALSE); + synthesise_activity(); + } + + break; + + case COVER_CLOSED: + if ((is_autorelock_enabled() == TRUE) && + ((autorelock_triggers & AUTORELOCK_LENS_COVER) != 0)) { + synthesise_inactivity(); + + /* This will also reset the autorelock policy */ + (void)enable_tklock_policy(FALSE); + } + + break; + + default: + break; + } + +EXIT: + return; +} + +/** + * Handle touchscreen/keypad lock state + * + * @param data The touchscreen/keypad lock state stored in a pointer + */ +static void tk_lock_trigger(gconstpointer data) +{ + lock_state_t tk_lock_state = GPOINTER_TO_INT(data); + + set_tklock_state(tk_lock_state); +} + +/** + * Handle submode change + * + * @param data The submode stored in a pointer + */ +static void submode_trigger(gconstpointer data) +{ + static submode_t old_submode = MCE_NORMAL_SUBMODE; + submode_t submode = GPOINTER_TO_INT(data); + + /* If we transition from !softoff to softoff, + * disable touchscreen and keypad events, + * otherwise enable them + */ + if ((submode & MCE_SOFTOFF_SUBMODE) != 0) { + if ((old_submode & MCE_SOFTOFF_SUBMODE) == 0) { + (void)ts_kp_disable(); + } + } else { + if ((old_submode & MCE_SOFTOFF_SUBMODE) != 0) { + (void)ts_kp_enable(); + } + } + + old_submode = submode; +} + +/** + * Handle call state change + * + * @param data The call state stored in a pointer + */ +static void call_state_trigger(gconstpointer data) +{ + static call_state_t old_call_state = CALL_STATE_INVALID; + call_state_t call_state = GPOINTER_TO_INT(data); + + switch (call_state) { + case CALL_STATE_RINGING: + if (proximity_lock_when_ringing == TRUE) + inhibit_proximity_relock = MCE_ALLOW_PROXIMITY_RELOCK; + + /* Incoming call, update the submode, + * unless there's already a call ongoing + */ + if (old_call_state != CALL_STATE_ACTIVE) { + saved_submode = mce_get_submode_int32(); + } + + break; + + case CALL_STATE_ACTIVE: + if (old_call_state != CALL_STATE_ACTIVE) + inhibit_proximity_relock = MCE_ALLOW_PROXIMITY_RELOCK; + + /* If we're answering a call, don't alter anything */ + if (old_call_state == CALL_STATE_RINGING) + break; + + /* Call initiated on our end, update the submode, + * unless we're just upgrading a normal call to + * an emergency call + */ + if (old_call_state != CALL_STATE_ACTIVE) + saved_submode = mce_get_submode_int32(); + + break; + + case CALL_STATE_NONE: + default: + /* Submode not set, update submode */ + if (saved_submode == MCE_INVALID_SUBMODE) + saved_submode = mce_get_submode_int32(); + + if (autorelock_triggers == AUTORELOCK_ON_PROXIMITY) + autorelock_triggers = AUTORELOCK_NO_TRIGGERS; + + tklock_proximity = FALSE; + + if ((saved_submode & MCE_TKLOCK_SUBMODE) != 0) { + synthesise_inactivity(); + + /* Enable the tklock again, show the banner */ + enable_tklock_policy(FALSE); + } else { + /* Disable autorelock unless tklock is active */ + if (is_tklock_enabled() == FALSE) { + disable_autorelock(); + + /* Unblank screen */ + (void)execute_datapipe(&display_state_pipe, GINT_TO_POINTER(MCE_DISPLAY_ON), USE_INDATA, CACHE_INDATA); + } + } + + break; + } + + process_proximity_state(); + old_call_state = call_state; +} + +/** + * Handle audio routing changes + * + * @param data The audio route stored in a pointer + */ +static void audio_route_trigger(gconstpointer data) +{ + audio_route_t audio_route = GPOINTER_TO_INT(data); + + switch (audio_route) { + case AUDIO_ROUTE_HANDSET: + case AUDIO_ROUTE_HEADSET: + if (inhibit_proximity_relock == + MCE_TEMP_INHIBIT_PROXIMITY_RELOCK) + inhibit_proximity_relock = MCE_ALLOW_PROXIMITY_RELOCK; + break; + + case AUDIO_ROUTE_SPEAKER: + case AUDIO_ROUTE_UNDEF: + default: + if (inhibit_proximity_relock == MCE_ALLOW_PROXIMITY_RELOCK) + inhibit_proximity_relock = + MCE_TEMP_INHIBIT_PROXIMITY_RELOCK; + break; + } + + process_proximity_state(); +} + +/** + * Handle USB cable connection change + * + * @param data The usb cable state stored in a pointer + */ +static void usb_cable_trigger(gconstpointer data) +{ + system_state_t system_state = datapipe_get_gint(system_state_pipe); + usb_cable_state_t usb_cable_state = GPOINTER_TO_INT(data); + + if ((system_state != MCE_STATE_USER)) + goto EXIT; + + switch (usb_cable_state) { + case USB_CABLE_CONNECTED: + case USB_CABLE_DISCONNECTED: + trigger_visual_tklock(FALSE); + break; + + default: + break; + } + +EXIT: + return; +} + +/** + * Handle jack sense change + * + * @param data The jack sense state stored in a pointer + */ +static void jack_sense_trigger(gconstpointer data) +{ + system_state_t system_state = datapipe_get_gint(system_state_pipe); + cover_state_t jack_sense_state = GPOINTER_TO_INT(data); + + if ((system_state != MCE_STATE_USER)) + goto EXIT; + + switch (jack_sense_state) { + case COVER_OPEN: + case COVER_CLOSED: + trigger_visual_tklock(FALSE); + break; + + default: + break; + } + +EXIT: + return; +} + +/** + * Init function for the touchscreen/keypad lock component + * + * @todo the call to disable_tklock needs error handling + * + * @return TRUE on success, FALSE on failure + */ +gboolean mce_tklock_init(void) +{ + gboolean status = FALSE; + + /* Init event control files */ + if (g_access(MCE_RX51_KEYBOARD_SYSFS_DISABLE_PATH, W_OK) == 0) { + mce_keypad_sysfs_disable_path = + MCE_RX51_KEYBOARD_SYSFS_DISABLE_PATH; + } else if (g_access(MCE_RX44_KEYBOARD_SYSFS_DISABLE_PATH, W_OK) == 0) { + mce_keypad_sysfs_disable_path = + MCE_RX44_KEYBOARD_SYSFS_DISABLE_PATH; + } else if (g_access(MCE_KEYPAD_SYSFS_DISABLE_PATH, W_OK) == 0) { + mce_keypad_sysfs_disable_path = + MCE_KEYPAD_SYSFS_DISABLE_PATH; + } + + if (g_access(MCE_RM680_TOUCHSCREEN_SYSFS_DISABLE_PATH, W_OK) == 0) { + mce_touchscreen_sysfs_disable_path = + MCE_RM680_TOUCHSCREEN_SYSFS_DISABLE_PATH; + } else if (g_access(MCE_RX44_TOUCHSCREEN_SYSFS_DISABLE_PATH, W_OK) == 0) { + mce_touchscreen_sysfs_disable_path = + MCE_RX44_TOUCHSCREEN_SYSFS_DISABLE_PATH; + } + + /* Close the touchscreen/keypad lock and event eater UI, + * to make sure MCE doesn't end up in a confused state + * if restarted + */ + // FIXME: error handling? + (void)disable_tklock(TRUE); + (void)disable_eveater(); + disable_autorelock(); + + /* Append triggers/filters to datapipes */ + append_input_trigger_to_datapipe(&device_inactive_pipe, + device_inactive_trigger); + append_input_trigger_to_datapipe(&keyboard_slide_pipe, + keyboard_slide_trigger); + append_input_trigger_to_datapipe(&lockkey_pipe, + lockkey_trigger); + append_input_trigger_to_datapipe(&keypress_pipe, + keypress_trigger); + append_input_trigger_to_datapipe(&camera_button_pipe, + camera_button_trigger); + append_output_trigger_to_datapipe(&system_state_pipe, + system_state_trigger); + append_output_trigger_to_datapipe(&display_state_pipe, + display_state_trigger); + append_output_trigger_to_datapipe(&alarm_ui_state_pipe, + alarm_ui_state_trigger); + append_output_trigger_to_datapipe(&lid_cover_pipe, + lid_cover_trigger); + append_output_trigger_to_datapipe(&proximity_sensor_pipe, + proximity_sensor_trigger); + append_output_trigger_to_datapipe(&lens_cover_pipe, + lens_cover_trigger); + append_output_trigger_to_datapipe(&tk_lock_pipe, + tk_lock_trigger); + append_output_trigger_to_datapipe(&submode_pipe, + submode_trigger); + append_output_trigger_to_datapipe(&call_state_pipe, + call_state_trigger); + append_output_trigger_to_datapipe(&audio_route_pipe, + audio_route_trigger); + append_output_trigger_to_datapipe(&jack_sense_pipe, + jack_sense_trigger); + append_output_trigger_to_datapipe(&usb_cable_pipe, + usb_cable_trigger); + + /* Touchscreen/keypad autolock */ + /* Since we've set a default, error handling is unnecessary */ + (void)mce_gconf_get_bool(MCE_GCONF_TK_AUTOLOCK_ENABLED_PATH, + &tk_autolock_enabled); + + /* Touchscreen/keypad autolock enabled/disabled */ + if (mce_gconf_notifier_add(MCE_GCONF_LOCK_PATH, + MCE_GCONF_TK_AUTOLOCK_ENABLED_PATH, + tklock_gconf_cb, + &tk_autolock_enabled_cb_id) == FALSE) + goto EXIT; + + /* get_tklock_mode */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_TKLOCK_MODE_GET, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + tklock_mode_get_req_dbus_cb) == NULL) + goto EXIT; + + /* req_tklock_mode_change */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_TKLOCK_MODE_CHANGE_REQ, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + tklock_mode_change_req_dbus_cb) == NULL) + goto EXIT; + + /* tklock_callback */ + if (mce_dbus_handler_add(MCE_REQUEST_IF, + MCE_TKLOCK_CB_REQ, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + systemui_tklock_dbus_cb) == NULL) + goto EXIT; + + /* Get configuration options */ + blank_immediately = + mce_conf_get_bool(MCE_CONF_TKLOCK_GROUP, + MCE_CONF_BLANK_IMMEDIATELY, + DEFAULT_BLANK_IMMEDIATELY, + NULL); + + dim_immediately = + mce_conf_get_bool(MCE_CONF_TKLOCK_GROUP, + MCE_CONF_DIM_IMMEDIATELY, + DEFAULT_DIM_IMMEDIATELY, + NULL); + + dim_delay = mce_conf_get_int(MCE_CONF_TKLOCK_GROUP, + MCE_CONF_DIM_DELAY, + DEFAULT_DIM_DELAY, + NULL); + + disable_ts_immediately = + mce_conf_get_bool(MCE_CONF_TKLOCK_GROUP, + MCE_CONF_TS_OFF_IMMEDIATELY, + DEFAULT_TS_OFF_IMMEDIATELY, + NULL); + + disable_kp_immediately = + mce_conf_get_int(MCE_CONF_TKLOCK_GROUP, + MCE_CONF_KP_OFF_IMMEDIATELY, + DEFAULT_KP_OFF_IMMEDIATELY, + NULL); + + autolock_with_open_slide = + mce_conf_get_bool(MCE_CONF_TKLOCK_GROUP, + MCE_CONF_AUTOLOCK_SLIDE_OPEN, + DEFAULT_AUTOLOCK_SLIDE_OPEN, + NULL); + + proximity_lock_with_open_slide = + mce_conf_get_bool(MCE_CONF_TKLOCK_GROUP, + MCE_CONF_PROXIMITY_LOCK_SLIDE_OPEN, + DEFAULT_PROXIMITY_LOCK_SLIDE_OPEN, + NULL); + + always_lock_on_slide_close = + mce_conf_get_bool(MCE_CONF_TKLOCK_GROUP, + MCE_CONF_LOCK_ON_SLIDE_CLOSE, + DEFAULT_LOCK_ON_SLIDE_CLOSE, + NULL); + + lens_cover_unlock = + mce_conf_get_bool(MCE_CONF_TKLOCK_GROUP, + MCE_CONF_LENS_COVER_UNLOCK, + DEFAULT_LENS_COVER_UNLOCK, + NULL); + + volkey_visual_trigger = + mce_conf_get_bool(MCE_CONF_TKLOCK_GROUP, + MCE_CONF_VOLKEY_VISUAL_TRIGGER, + DEFAULT_VOLKEY_VISUAL_TRIGGER, + NULL); + + status = TRUE; + +EXIT: + return status; +} + +/** + * Exit function for the touchscreen/keypad lock component + * + * @todo D-Bus unregistration + */ +void mce_tklock_exit(void) +{ + /* Remove triggers/filters from datapipes */ + remove_output_trigger_from_datapipe(&usb_cable_pipe, + usb_cable_trigger); + remove_output_trigger_from_datapipe(&jack_sense_pipe, + jack_sense_trigger); + remove_output_trigger_from_datapipe(&audio_route_pipe, + audio_route_trigger); + remove_output_trigger_from_datapipe(&call_state_pipe, + call_state_trigger); + remove_output_trigger_from_datapipe(&submode_pipe, + submode_trigger); + remove_output_trigger_from_datapipe(&tk_lock_pipe, + tk_lock_trigger); + remove_output_trigger_from_datapipe(&lens_cover_pipe, + lens_cover_trigger); + remove_output_trigger_from_datapipe(&proximity_sensor_pipe, + proximity_sensor_trigger); + remove_output_trigger_from_datapipe(&lid_cover_pipe, + lid_cover_trigger); + remove_output_trigger_from_datapipe(&alarm_ui_state_pipe, + alarm_ui_state_trigger); + remove_output_trigger_from_datapipe(&display_state_pipe, + display_state_trigger); + remove_output_trigger_from_datapipe(&system_state_pipe, + system_state_trigger); + remove_input_trigger_from_datapipe(&camera_button_pipe, + camera_button_trigger); + remove_input_trigger_from_datapipe(&keypress_pipe, + keypress_trigger); + remove_input_trigger_from_datapipe(&lockkey_pipe, + lockkey_trigger); + remove_input_trigger_from_datapipe(&keyboard_slide_pipe, + keyboard_slide_trigger); + remove_input_trigger_from_datapipe(&device_inactive_pipe, + device_inactive_trigger); + + /* This trigger is conditional; attempt to remove it anyway */ + remove_input_trigger_from_datapipe(&touchscreen_pipe, + touchscreen_trigger); + + /* Remove all timeout sources */ + cancel_tklock_visual_forced_blank_timeout(); + cancel_tklock_visual_blank_timeout(); + cancel_tklock_unlock_timeout(); + cancel_tklock_dim_timeout(); + + return; +} diff --git a/tklock.h b/tklock.h new file mode 100644 index 00000000..5174a833 --- /dev/null +++ b/tklock.h @@ -0,0 +1,138 @@ +/** + * @file tklock.h + * Headers for the touchscreen/keypad lock component + * of the Mode Control Entity + *

+ * Copyright © 2004-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _TKLOCK_H_ +#define _TKLOCK_H_ + +#include + +#ifndef MCE_GCONF_LOCK_PATH +/** Path to the GConf settings for the touchscreen/keypad lock */ +#define MCE_GCONF_LOCK_PATH "/system/osso/dsm/locks" +#endif /* MCE_GCONF_LOCK_PATH */ + +/** SysFS interface to enable/disable RX-51 keyboard IRQs */ +#define MCE_RX51_KEYBOARD_SYSFS_DISABLE_PATH "/sys/class/i2c-adapter/i2c-1/1-004a/twl4030_keypad/disable_kp" +/** SysFS interface to enable/disable keypad IRQs */ +#define MCE_KEYPAD_SYSFS_DISABLE_PATH "/sys/devices/platform/omap2_mcspi.1/spi1.0/disable_kp" +/** SysFS interface to enable/disable the RX-44/RX-48 keyboard IRQs */ +#define MCE_RX44_KEYBOARD_SYSFS_DISABLE_PATH "/sys/devices/platform/i2c_omap.2/i2c-0/0-0045/disable_kp" + +/** + * SysFS interface to enable/disable + * RM-680/RM-690/RM-696/RM-716 touchscreen IRQs + */ +#define MCE_RM680_TOUCHSCREEN_SYSFS_DISABLE_PATH "/sys/class/i2c-adapter/i2c-2/2-004b/disable_ts" + +/** SysFS interface to enable/disable RX-44/RX-48/RX-51 touchscreen IRQs */ +#define MCE_RX44_TOUCHSCREEN_SYSFS_DISABLE_PATH "/sys/devices/platform/omap2_mcspi.1/spi1.0/disable_ts" + +/** Default fallback setting for the touchscreen/keypad autolock */ +#define DEFAULT_TK_AUTOLOCK FALSE /* FALSE / TRUE */ + +/** Path to the touchscreen/keypad autolock GConf setting */ +#define MCE_GCONF_TK_AUTOLOCK_ENABLED_PATH MCE_GCONF_LOCK_PATH "/touchscreen_keypad_autolock_enabled" + +/** Name of D-Bus callback to provide to Touchscreen/Keypad Lock SystemUI */ +#define MCE_TKLOCK_CB_REQ "tklock_callback" +/** Delay before the touchscreen/keypad is unlocked */ +#define MCE_TKLOCK_UNLOCK_DELAY 500 /**< 0.5 seconds */ + +#ifndef MCE_CONF_TKLOCK_GROUP +/** Name of Touchscreen/Keypad lock configuration group */ +#define MCE_CONF_TKLOCK_GROUP "TKLock" +#endif /* MCE_CONF_TKLOCK_GROUP */ + +/** Name of configuration key for touchscreen/keypad immediate blanking */ +#define MCE_CONF_BLANK_IMMEDIATELY "BlankImmediately" + +/** Name of configuration key for touchscreen/keypad immediate dimming */ +#define MCE_CONF_DIM_IMMEDIATELY "DimImmediately" + +/** Name of configuration key for touchscreen/keypad dim timeout */ +#define MCE_CONF_DIM_DELAY "DimDelay" + +/** Name of configuration key for touchscreen immediate disabling */ +#define MCE_CONF_TS_OFF_IMMEDIATELY "DisableTSImmediately" + +/** Name of configuration key for keypad immediate disabling */ +#define MCE_CONF_KP_OFF_IMMEDIATELY "DisableKPImmediately" + +/** Name of configuration key for keyboard slide autolock */ +#define MCE_CONF_AUTOLOCK_SLIDE_OPEN "AutolockWhenSlideOpen" + +/** Name of configuration key for keyboard slide proximity lock */ +#define MCE_CONF_PROXIMITY_LOCK_SLIDE_OPEN "ProximityLockWhenSlideOpen" + +/** Name of configuration key for keyboard slide unconditional lock on close */ +#define MCE_CONF_LOCK_ON_SLIDE_CLOSE "AlwaysLockOnSlideClose" + +/** Name of configuration key for lens cover triggered tklock unlocking */ +#define MCE_CONF_LENS_COVER_UNLOCK "LensCoverUnlock" + +/** Name of configuration key for volume key triggered unlock screen */ +#define MCE_CONF_VOLKEY_VISUAL_TRIGGER "TriggerUnlockScreenWithVolumeKeys" + +/** Default fallback setting for tklock immediate blanking */ +#define DEFAULT_BLANK_IMMEDIATELY FALSE /* FALSE / TRUE */ + +/** Default fallback setting for tklock immediate dimming */ +#define DEFAULT_DIM_IMMEDIATELY FALSE /* FALSE / TRUE */ + +/** Default visual lock blank timeout */ +#define DEFAULT_VISUAL_BLANK_DELAY 3 /* 3 seconds */ + +/** Default visual lock blank timeout */ +#define DEFAULT_VISUAL_FORCED_BLANK_DELAY 30 /* 30 seconds */ + +/** Default delay before the display dims */ +#define DEFAULT_DIM_DELAY 3 /* 3 seconds */ + +/** Default fallback setting for touchscreen immediate disabling */ +#define DEFAULT_TS_OFF_IMMEDIATELY TRUE /* FALSE / TRUE */ + +/** Default fallback setting for keypad immediate disabling */ +#define DEFAULT_KP_OFF_IMMEDIATELY 2 /* 0 / 1 / 2 */ + +/** Default fallback setting for autolock with open keyboard slide */ +#define DEFAULT_AUTOLOCK_SLIDE_OPEN FALSE /* FALSE */ + +/** Default fallback setting for proximity lock with open keyboard slide */ +#define DEFAULT_PROXIMITY_LOCK_SLIDE_OPEN FALSE /* FALSE */ + +/** Default fallback setting for unconditional lock on close keyboard slide */ +#define DEFAULT_LOCK_ON_SLIDE_CLOSE FALSE /* FALSE */ + +/** Default fallback setting for lens cover triggered tklock unlocking */ +#define DEFAULT_LENS_COVER_UNLOCK TRUE /* TRUE */ + +/** Default fallback setting for proximity lock when callstate == ringing */ +#define DEFAULT_PROXIMITY_LOCK_WHEN_RINGING FALSE /* FALSE */ + +/** Default fallback setting for volume key triggered unlock screen */ +#define DEFAULT_VOLKEY_VISUAL_TRIGGER TRUE /* TRUE */ + +/* when MCE is made modular, this will be handled differently + */ +gboolean mce_tklock_init(void); +void mce_tklock_exit(void); + +#endif /* _TKLOCK_H_ */ diff --git a/tools/mcetool.c b/tools/mcetool.c new file mode 100644 index 00000000..a801eaa7 --- /dev/null +++ b/tools/mcetool.c @@ -0,0 +1,1908 @@ +/** + * @file mcetool.c + * Tool to test and remote control the Mode Control Entity + *

+ * Copyright © 2005-2010 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#include +#include /* g_type_init(), g_object_unref() */ + +#include /* errno, + * ENOMEM + */ +#include /* fprintf() */ +#include /* getopt_long(), + * struct option + */ +#include /* exit(), EXIT_FAILURE */ +#include /* strcmp(), strlen(), strdup() */ +#include +#include + +#include +#include + +#include "mcetool.h" + +#include "tklock.h" /* For GConf paths */ +#include "modules/display.h" /* For GConf paths */ +#include "modules/powersavemode.h" /* For GConf paths */ + +/** Name shown by --help etc. */ +#define PRG_NAME "mcetool" + +/** Short powerkey event string; used for arg-parsing */ +#define SHORT_EVENT_STR "short" +/** Double powerkey event string; used for arg-parsing */ +#define DOUBLE_EVENT_STR "double" +/** Long powerkey event string; used for arg-parsing */ +#define LONG_EVENT_STR "long" + +/** Blanking inhibit disabled string; used for arg-parsing */ +#define BLANKING_INHIBIT_DISABLED "disabled" +/** Blanking inhibit stay on with charger string; used for arg-parsing */ +#define BLANKING_INHIBIT_STAY_ON_WITH_CHARGER "stay-on-with-charger" +/** Blanking inhibit stay dim with charger string; used for arg-parsing */ +#define BLANKING_INHIBIT_STAY_DIM_WITH_CHARGER "stay-dim-with-charger" +/** Blanking inhibit stay on string; used for arg-parsing */ +#define BLANKING_INHIBIT_STAY_ON "stay-on" +/** Blanking inhibit stay dim string; used for arg-parsing */ +#define BLANKING_INHIBIT_STAY_DIM "stay-dim" + +/** Enabled string; used for arg-parsing */ +#define ENABLED_STRING "enabled" +/** Disabled string; used for arg-parsing */ +#define DISABLED_STRING "disabled" + +/** Master string; used for arg-parsing */ +#define RADIO_MASTER "master" +/** Cellular string; used for arg-parsing */ +#define RADIO_CELLULAR "cellular" +/** WLAN string; used for arg-parsing */ +#define RADIO_WLAN "wlan" +/** Bluetooth string; used for arg-parsing */ +#define RADIO_BLUETOOTH "bluetooth" + +/** Enums for powerkey events */ +enum { + INVALID_EVENT = -1, /**< Event not set */ + SHORT_EVENT = 0, /**< Short powerkey press event */ + LONG_EVENT = 1, /**< Long powerkey press event */ + DOUBLE_EVENT = 2 /**< Double powerkey press event */ +}; + +extern int optind; /**< Used by getopt */ +extern char *optarg; /**< Used by getopt */ + +static const gchar *progname; /**< Used to store the name of the program */ + +static DBusConnection *dbus_connection; /**< D-Bus connection */ + +static GConfClient *gconf_client = NULL; /**< GConf client */ + +/** + * Display usage information + */ +static void usage(void) +{ + fprintf(stdout, + _("Usage: %s [OPTION]\n" + "Mode Control Entity tool\n" + "\n" + " --blank-prevent send blank prevent " + "request to MCE\n" + " --cancel-blank-prevent send cancel blank prevent " + "request to MCE\n" + " --unblank-screen send unblank request to " + "MCE\n" + " --dim-screen send dim request " + "to MCE\n" + " --blank-screen send blank request " + "to MCE\n" + " --set-display-brightness=BRIGHTNESS\n" + " set the display " + "brightness to BRIGHTNESS;\n" + " valid values are: " + "1-5\n" + " --set-inhibit-mode=MODE\n" + " set the blanking inhibit " + "mode to MODE;\n" + " valid modes " + "are:\n" + " ``disabled'',\n" + " ``stay-on-with-charger" + "'', ``stay-on'',\n" + " ``stay-dim-with-charger" + "'', ``stay-dim''\n" + " --set-cabc-mode=MODE\n" + " set the CABC mode to " + "MODE;\n" + " valid modes " + "are:\n" + " ``off'', " + "``ui'',\n" + " ``still-image' and " + "``moving-image''\n" + " --set-call-state=STATE:TYPE\n" + " set the call state to " + "STATE and the call type\n" + " to TYPE; valid states " + "are:\n" + " ``none'', " + "``ringing'',\n" + " ``active'' and " + "``service''\n" + " valid types are:\n" + " ``normal'' and " + "``emergency''\n" + " --enable-radio=RADIO\n" + " enable the specified " + "radio; valid radios are:\n" + " ``master'', " + "``cellular'',\n" + " ``wlan'' and " + "``bluetooth'';\n" + " ``master'' affects " + "all radios\n" + " --disable-radio=RADIO\n" + " disable the specified " + "radio; valid radios are:\n" + " ``master'', " + "``cellular'',\n" + " ``wlan'' and " + "``bluetooth'';\n" + " ``master'' affects " + "all radios\n" + " --set-power-saving-mode=MODE\n" + " set the power saving " + "mode; valid modes are:\n" + " ``enabled'' and " + "``disabled''\n" + " --set-forced-psm=MODE\n" + " the forced " + "power saving mode to MODE;\n" + " valid modes are:\n" + " ``enabled'' and " + "``disabled''\n" + " --set-psm-threshold=VALUE\n" + " set the threshold for " + "the power saving mode;\n" + " valid values are:\n" + " 10, 20, 30, 40, 50\n" + " --set-tklock-mode=MODE\n" + " set the touchscreen/" + "keypad lock mode;\n" + " valid modes are:\n" + " ``locked'', " + "``locked-dim'',\n" + " ``silent-locked'', " + "``silent-locked-dim'',\n" + " ``unlocked'' and " + "``silent-unlocked''\n" + " --enable-led enable LED framework\n" + " --disable-led disable LED framework\n" + " --activate-led-pattern=PATTERN\n" + " activate a LED pattern\n" + " --deactivate-led-pattern=PATTERN\n" + " deactivate a LED " + "pattern\n" + " --powerkey-event=TYPE trigger a powerkey " + "event; valid types are:\n" + " ``short'', ``double'' " + "and ``long''\n" + " --status output MCE status\n" + " --block block after executing " + "commands\n" + " -S, --session use the session bus " + "instead of the system bus\n" + " for D-Bus\n" + " --help display this help and " + "exit\n" + " --version output version " + "information and exit\n" + "\n" + "If no option is specified, the status is output.\n" + "\n" + "Report bugs to \n"), + progname); +} + +/** + * Display version information + */ +static void version(void) +{ + fprintf(stdout, _("%s v%s\n%s"), + progname, + G_STRINGIFY(PRG_VERSION), + _("Written by David Weinehall.\n" + "\n" + "Copyright (C) 2005-2010 Nokia Corporation. " + "All rights reserved.\n")); +} + +/** + * Initialise locale support + * + * @param name The program name to output in usage/version information + * @return 0 on success, non-zero on failure + */ +static gint init_locales(const gchar *const name) +{ + gint status = 0; + +#ifdef ENABLE_NLS + setlocale(LC_ALL, ""); + + if ((bindtextdomain(name, LOCALEDIR) == 0) && (errno == ENOMEM)) { + status = errno; + goto EXIT; + } + + if ((textdomain(name) == 0) && (errno == ENOMEM)) { + status = errno; + return 0; + } + +EXIT: + /* In this error-message we don't use _(), since we don't + * know where the locales failed, and we probably won't + * get a reasonable result if we try to use them. + */ + if (status != 0) { + fprintf(stderr, + "%s: `%s' failed; %s. Aborting.\n", + name, "init_locales", g_strerror(errno)); + } else { + progname = name; + errno = 0; + } +#else + progname = name; +#endif /* ENABLE_NLS */ + + return status; +} + +/** + * Create a new D-Bus signal, with proper error checking + * will exit if an error occurs + * + * @param path The signal path + * @param interface The signal interface + * @param name The name of the signal to send + * @return A new DBusMessage + */ +static DBusMessage *dbus_new_signal(const gchar *const path, + const gchar *const interface, + const gchar *const name) +{ + DBusMessage *msg; + + if ((msg = dbus_message_new_signal(path, interface, name)) == NULL) { + fprintf(stderr, "No memory for new signal!"); + exit(EXIT_FAILURE); + } + + return msg; +} + +/** + * Create a new D-Bus method call, with proper error checking + * will exit if an error occurs + * + * @param service The method call service + * @param path The method call path + * @param interface The method call interface + * @param name The name of the method to call + * @return A new DBusMessage + */ +static DBusMessage *dbus_new_method_call(const gchar *const service, + const gchar *const path, + const gchar *const interface, + const gchar *const name) +{ + DBusMessage *msg; + + if ((msg = dbus_message_new_method_call(service, path, + interface, name)) == NULL) { + fprintf(stderr, + "Cannot allocate memory for D-Bus method call!"); + exit(EXIT_FAILURE); + } + + return msg; +} + +/** + * Send a D-Bus message + * Side effects: frees msg + * + * @param msg The D-Bus message to send + * @return TRUE on success, FALSE on out of memory + */ +static gboolean dbus_send_message(DBusMessage *const msg) +{ + if (dbus_connection_send(dbus_connection, msg, NULL) == FALSE) { + dbus_message_unref(msg); + return FALSE; + } + + dbus_connection_flush(dbus_connection); + dbus_message_unref(msg); + + return TRUE; +} + +/** + * Call a D-Bus method and return the reply as a DBusMessage + * + * @param method The method to call + * @param arg A pointer to the string to append; + * the string is freed after use + * @return a DbusMessage on success, NULL on failure + */ +static DBusMessage *mcetool_dbus_call_with_reply(const gchar *const method, + gchar **arg) +{ + DBusMessage *reply = NULL; + DBusMessage *msg; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + if ((msg = dbus_message_new_method_call(MCE_SERVICE, + MCE_REQUEST_PATH, + MCE_REQUEST_IF, + method)) == NULL) { + fprintf(stderr, + "Cannot allocate memory for D-Bus method call!"); + goto EXIT; + } + + /* Is there an argument to append? */ + if (arg != NULL && *arg != NULL) { + if (dbus_message_append_args(msg, + DBUS_TYPE_STRING, arg, + DBUS_TYPE_INVALID) != TRUE) { + dbus_message_unref(msg); + fprintf(stderr, + "Failed to append argument to D-Bus message " + "for %s", + method); + goto EXIT; + } + } + + reply = dbus_connection_send_with_reply_and_block(dbus_connection, msg, -1, &error); + + dbus_message_unref(msg); + + if (arg && *arg) { + free(*arg); + *arg = NULL; + } + + if (dbus_error_is_set(&error) == TRUE) { + fprintf(stderr, + "Could not call method %s: %s; exiting", + method, error.message); + dbus_error_free(&error); + reply = NULL; + goto EXIT; + } + +EXIT: + return reply; +} + +/** + * Call a D-Bus method and return the reply as a string + * + * @param method The method to call + * @param[in,out] arg An pointer to the string to append; + * the string is freed after use. + * if a reply is expected, a string with the reply will be + * returned through this pointer + * @param no_reply TRUE if no reply is expected, FALSE if a reply is expected + * @return 0 on success, EXIT_FAILURE on failure + */ +static gint mcetool_dbus_call_string(const gchar *const method, + gchar **arg, const gboolean no_reply) +{ + DBusMessage *reply = NULL; + DBusMessage *msg; + gint status = 0; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + if ((msg = dbus_message_new_method_call(MCE_SERVICE, + MCE_REQUEST_PATH, + MCE_REQUEST_IF, + method)) == NULL) { + fprintf(stderr, + "Cannot allocate memory for D-Bus method call!"); + status = EXIT_FAILURE; + goto EXIT; + } + + /* Is there an argument to append? */ + if (arg != NULL && *arg != NULL) { + if (dbus_message_append_args(msg, + DBUS_TYPE_STRING, arg, + DBUS_TYPE_INVALID) != TRUE) { + dbus_message_unref(msg); + fprintf(stderr, + "Failed to append argument to D-Bus message " + "for %s", + method); + status = EXIT_FAILURE; + goto EXIT; + } + } + + if (no_reply == TRUE) { + dbus_message_set_no_reply(msg, TRUE); + + if (dbus_connection_send(dbus_connection, msg, NULL) == FALSE) { + dbus_message_unref(msg); + goto EXIT; + } + } else { + reply = dbus_connection_send_with_reply_and_block(dbus_connection, msg, -1, &error); + } + + dbus_message_unref(msg); + + if (arg && *arg) { + free(*arg); + *arg = NULL; + } + + if (dbus_error_is_set(&error) == TRUE) { + fprintf(stderr, + "Could not call method %s: %s; exiting", + method, error.message); + dbus_error_free(&error); + status = EXIT_FAILURE; + goto EXIT; + } else if (reply) { + gchar *tmp = NULL; + + if (dbus_message_get_args(reply, &error, + DBUS_TYPE_STRING, &tmp, + DBUS_TYPE_INVALID) == FALSE) { + fprintf(stderr, + "Failed to get reply argument from %s: " + "%s; exiting", + method, error.message); + dbus_message_unref(reply); + dbus_error_free(&error); + status = EXIT_FAILURE; + goto EXIT; + } + + *arg = strdup(tmp); + dbus_message_unref(reply); + } + +EXIT: + return status; +} + +/** + * Call a D-Bus method and return the reply as a boolean + * + * @param method The method to call + * @param arg A pointer to the string to append; + * the string is freed after use + * @param[out] retval A pointer to pass the reply through + * @return 0 on success, EXIT_FAILURE on failure + */ +static gint mcetool_dbus_call_bool(const gchar *const method, + gchar **arg, gboolean *retval) +{ + DBusMessage *reply = NULL; + DBusMessage *msg; + gint status = 0; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + if ((msg = dbus_message_new_method_call(MCE_SERVICE, + MCE_REQUEST_PATH, + MCE_REQUEST_IF, + method)) == NULL) { + fprintf(stderr, + "Cannot allocate memory for D-Bus method call!"); + status = EXIT_FAILURE; + goto EXIT; + } + + /* Is there an argument to append? */ + if (arg != NULL && *arg != NULL) { + if (dbus_message_append_args(msg, + DBUS_TYPE_STRING, arg, + DBUS_TYPE_INVALID) != TRUE) { + dbus_message_unref(msg); + fprintf(stderr, + "Failed to append argument to D-Bus message " + "for %s", + method); + status = EXIT_FAILURE; + goto EXIT; + } + } + + reply = dbus_connection_send_with_reply_and_block(dbus_connection, msg, -1, &error); + + dbus_message_unref(msg); + + if (arg && *arg) { + free(*arg); + *arg = NULL; + } + + if (dbus_error_is_set(&error) == TRUE) { + fprintf(stderr, + "Could not call method %s: %s; exiting", + method, error.message); + dbus_error_free(&error); + status = EXIT_FAILURE; + goto EXIT; + } else if (reply) { + gboolean tmp; + + if (dbus_message_get_args(reply, &error, + DBUS_TYPE_BOOLEAN, &tmp, + DBUS_TYPE_INVALID) == FALSE) { + fprintf(stderr, + "Failed to get reply argument from %s: " + "%s; exiting", + method, error.message); + dbus_message_unref(reply); + dbus_error_free(&error); + status = EXIT_FAILURE; + goto EXIT; + } + + *retval = tmp; + dbus_message_unref(reply); + } + +EXIT: + return status; +} + +/** + * Call a D-Bus method and return the reply as an unsigned integer + * + * @param method The method to call + * @param arg A pointer to the string to append; + * the string is freed after use + * @param[out] retval A pointer to pass the reply through + * @return 0 on success, EXIT_FAILURE on failure + */ +static gint mcetool_dbus_call_uint(const gchar *const method, + gchar **arg, guint32 *retval) +{ + DBusMessage *reply = NULL; + DBusMessage *msg; + gint status = 0; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + if ((msg = dbus_message_new_method_call(MCE_SERVICE, + MCE_REQUEST_PATH, + MCE_REQUEST_IF, + method)) == NULL) { + fprintf(stderr, + "Cannot allocate memory for D-Bus method call!"); + status = EXIT_FAILURE; + goto EXIT; + } + + /* Is there an argument to append? */ + if (arg != NULL && *arg != NULL) { + if (dbus_message_append_args(msg, + DBUS_TYPE_STRING, arg, + DBUS_TYPE_INVALID) != TRUE) { + dbus_message_unref(msg); + fprintf(stderr, + "Failed to append argument to D-Bus message " + "for %s", + method); + status = EXIT_FAILURE; + goto EXIT; + } + } + + reply = dbus_connection_send_with_reply_and_block(dbus_connection, msg, -1, &error); + + dbus_message_unref(msg); + + if (arg && *arg) { + free(*arg); + *arg = NULL; + } + + if (dbus_error_is_set(&error) == TRUE) { + fprintf(stderr, + "Could not call method %s: %s; exiting", + method, error.message); + dbus_error_free(&error); + status = EXIT_FAILURE; + goto EXIT; + } else if (reply) { + guint32 tmp; + + if (dbus_message_get_args(reply, &error, + DBUS_TYPE_UINT32, &tmp, + DBUS_TYPE_INVALID) == FALSE) { + fprintf(stderr, + "Failed to get reply argument from %s: " + "%s; exiting", + method, error.message); + dbus_message_unref(reply); + dbus_error_free(&error); + status = EXIT_FAILURE; + goto EXIT; + } + + *retval = tmp; + dbus_message_unref(reply); + } + +EXIT: + return status; +} + +/** + * Generic function to send D-Bus messages and signals + * to send a signal, call dbus_send with service == NULL + * + * @param service D-Bus service; for signals, set to NULL + * @param path D-Bus path + * @param interface D-Bus interface + * @param name D-Bus method or signal name to send to + * @param no_reply FALSE if a reply is expected, TRUE if reply is ignored; + * for signals this is ignored, but for consistency, + * please use TRUE + * @param first_arg_type The DBUS_TYPE of the first argument in the list + * @param ... The arguments to append to the D-Bus message; terminate with NULL + * Note: the arguments MUST be passed by reference + * @return TRUE on success, FALSE on failure + */ +static gboolean dbus_send(const gchar *const service, const gchar *const path, + const gchar *const interface, const gchar *const name, + const gboolean no_reply, int first_arg_type, ...) +{ + DBusMessage *msg; + gboolean status = FALSE; + va_list var_args; + + if (service != NULL) { + msg = dbus_new_method_call(service, path, interface, name); + + if (no_reply == TRUE) + dbus_message_set_no_reply(msg, TRUE); + } else { + msg = dbus_new_signal(path, interface, name); + } + + /* Append the arguments, if any */ + va_start(var_args, first_arg_type); + + if (first_arg_type != DBUS_TYPE_INVALID) { + if (dbus_message_append_args_valist(msg, + first_arg_type, + var_args) == FALSE) { + fprintf(stderr, + "Failed to append arguments to D-Bus message"); + dbus_message_unref(msg); + goto EXIT; + } + } + + /* Send the signal / call the method */ + if (dbus_send_message(msg) == FALSE) { + if (service != NULL) + fprintf(stderr, "Cannot call method %s", name); + else + fprintf(stderr, "Cannot send signal %s", name); + + goto EXIT; + } + + status = TRUE; + +EXIT: + va_end(var_args); + + return status; +} + +/** + * Acquire D-Bus services + * + * @return TRUE on success, FALSE on failure + */ +static gboolean dbus_acquire_services(void) +{ + gboolean status = FALSE; + int ret; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + ret = dbus_bus_request_name(dbus_connection, MCETOOL_SERVICE, 0, + &error); + + if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + fprintf(stderr, + "Cannot acquire service: %s", + error.message); + dbus_error_free(&error); + goto EXIT; + } + + status = TRUE; + +EXIT: + return status; +} + +/** + * D-Bus initialisation + * + * @param bus_type "system" or "session" bus + * @return 0 on success, non-zero on failure + */ +static gint mcetool_dbus_init(DBusBusType bus_type) +{ + gint status = 0; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + /* Establish D-Bus connection */ + if ((dbus_connection = dbus_bus_get(bus_type, &error)) == 0) { + fprintf(stderr, + "Failed to open connection to message bus; %s", + error.message); + dbus_error_free(&error); + status = EXIT_FAILURE; + goto EXIT; + } + + /* Acquire D-Bus service */ + if (dbus_acquire_services() == FALSE) { + status = EXIT_FAILURE; + goto EXIT; + } + +EXIT: + return status; +} + +/** + * D-Bus exit + */ +static void mcetool_dbus_exit(void) +{ + /* If there is an established D-Bus connection, unreference it */ + if (dbus_connection != NULL) { + dbus_connection_unref(dbus_connection); + dbus_connection = NULL; + } +} + +/** + * Enable/disable the tklock + * + * @param mode The mode to change to; valid modes: + * "locked", "locked-dim", "silent-locked", "silent-locked-dim", + * "unlocked", "silent-unlocked" + * @return TRUE on success, FALSE on FAILURE + */ +static gboolean set_tklock_mode(gchar **mode) +{ + /* com.nokia.mce.request.req_tklock_mode_change */ + return mcetool_dbus_call_string(MCE_TKLOCK_MODE_CHANGE_REQ, mode, TRUE); +} + +/** + * Trigger a powerkey event + * + * @param type The type of event to trigger; valid types: + * "short", "double", "long" + * @return TRUE on success, FALSE on FAILURE + */ +static gboolean trigger_powerkey_event(const gint type) +{ + /* com.nokia.mce.request.req_trigger_powerkey_event */ + return dbus_send(MCE_SERVICE, MCE_REQUEST_PATH, MCE_REQUEST_IF, + MCE_TRIGGER_POWERKEY_EVENT_REQ, TRUE, + DBUS_TYPE_UINT32, &type, + DBUS_TYPE_INVALID); +} + +/** + * Enable/Disable the LED + * + * @param enable TRUE to enable the LED, FALSE to disable the LED + * @return TRUE on success, FALSE on FAILURE + */ +static gboolean set_led_state(const gboolean enable) +{ + /* com.nokia.mce.request.req_led_{enable,disable} */ + return dbus_send(MCE_SERVICE, MCE_REQUEST_PATH, MCE_REQUEST_IF, + enable ? MCE_ENABLE_LED : MCE_DISABLE_LED, TRUE, + DBUS_TYPE_INVALID); +} + +/** + * Activate/Deactivate a LED pattern + * + * @param pattern The name of the pattern to activate/deactivate + * @param activate TRUE to activate pattern, FALSE to deactivate pattern + * @return TRUE on success, FALSE on FAILURE + */ +static gboolean set_led_pattern_state(const gchar *const pattern, + const gboolean activate) +{ + /* com.nokia.mce.request.req_{activate,deactivate}_led_pattern */ + return dbus_send(MCE_SERVICE, MCE_REQUEST_PATH, MCE_REQUEST_IF, + activate ? MCE_ACTIVATE_LED_PATTERN : + MCE_DEACTIVATE_LED_PATTERN, TRUE, + DBUS_TYPE_STRING, &pattern, + DBUS_TYPE_INVALID); +} + +/** + * Init function for the mcetool GConf handling + * + * @return TRUE on success, FALSE on failure + */ +static gint mcetool_gconf_init(void) +{ + gint status = 0; + + /* Init GType */ + g_type_init(); + + gconf_client = gconf_client_get_default(); + + if (gconf_client == NULL) { + fprintf(stderr, "Could not get default GConf client"); + status = EXIT_FAILURE; + goto EXIT; + } + +EXIT: + return status; +} + +/** + * Exit function for the mcetool GConf handling + */ +static void mcetool_gconf_exit(void) +{ + /* Unreference GConf */ + if (gconf_client != NULL) + g_object_unref(gconf_client); +} + +/** + * Return a boolean from the specified GConf key + * + * @param key The GConf key to get the value from + * @param value Will contain the value on return + * @return TRUE on success, FALSE on failure + */ +static gboolean mcetool_gconf_get_bool(const gchar *const key, gboolean *value) +{ + gboolean status = FALSE; + GError *error = NULL; + GConfValue *gcv; + + gcv = gconf_client_get(gconf_client, key, &error); + + /* If the value isn't set, just return */ + if (gcv == NULL) + goto EXIT; + + if (gcv->type != GCONF_VALUE_BOOL) { + fprintf(stderr, + "\n" + "GConf key %s should have type: %d, " + "but has type: %d\n" + "\n", + key, GCONF_VALUE_BOOL, gcv->type); + goto EXIT; + } + + *value = gconf_value_get_bool(gcv); + + status = TRUE; + +EXIT: + return status; +} + +/** + * Return an integer from the specified GConf key + * + * @param key The GConf key to get the value from + * @param value Will contain the value on return + * @return TRUE on success, FALSE on failure + */ +static gboolean mcetool_gconf_get_int(const gchar *const key, gint *value) +{ + gboolean status = FALSE; + GError *error = NULL; + GConfValue *gcv; + + gcv = gconf_client_get(gconf_client, key, &error); + + /* If the value isn't set, just return */ + if (gcv == NULL) + goto EXIT; + + if (gcv->type != GCONF_VALUE_INT) { + fprintf(stderr, + "\n" + "GConf key %s should have type: %d, " + "but has type: %d\n" + "\n", + key, GCONF_VALUE_INT, gcv->type); + goto EXIT; + } + + *value = gconf_value_get_int(gcv); + + status = TRUE; + +EXIT: + g_clear_error(&error); + + return status; +} + +/** + * Set a boolean GConf key to the specified value + * + * @param key The GConf key to set the value of + * @param value The value to set the key to + * @return TRUE on success, FALSE on failure + */ +static gboolean mcetool_gconf_set_bool(const gchar *const key, + const gboolean value) +{ + gboolean status = FALSE; + + if (gconf_client_set_bool(gconf_client, key, value, NULL) == FALSE) { + fprintf(stderr, + "Failed to write %s to GConf\n", key); + goto EXIT; + } + + /* synchronise if possible, ignore errors */ + gconf_client_suggest_sync(gconf_client, NULL); + + status = TRUE; + +EXIT: + return status; +} + +/** + * Set an integer GConf key to the specified value + * + * @param key The GConf key to set the value of + * @param value The value to set the key to + * @return TRUE on success, FALSE on failure + */ +static gboolean mcetool_gconf_set_int(const gchar *const key, const gint value) +{ + gboolean status = FALSE; + + if (gconf_client_set_int(gconf_client, key, value, NULL) == FALSE) { + fprintf(stderr, + "Failed to write %s to GConf\n", key); + goto EXIT; + } + + /* synchronise if possible, ignore errors */ + gconf_client_suggest_sync(gconf_client, NULL); + + status = TRUE; + +EXIT: + return status; +} + +/** + * Print mce related information + * + * @return 0 on success, EXIT_FAILURE on failure + */ +static gint mcetool_get_status(void) +{ + gint status = 0; + gchar *cabc = NULL; + gchar *tklock = NULL; + gchar *display = NULL; + const gchar *inhibit_string = NULL; + gchar *mce_version = NULL; + gchar *callstate = NULL; + gchar *calltype = NULL; + gint brightness = DEFAULT_DISP_BRIGHTNESS; + gint dim_timeout = DEFAULT_DIM_TIMEOUT; + gint blank_timeout = DEFAULT_BLANK_TIMEOUT; + gint adaptive_dimming_threshold = -1; + gboolean retval; + gboolean inactive = FALSE; + gboolean keyboard_backlight = FALSE; + gboolean power_saving_mode = TRUE; + gboolean adaptive_dimming_enabled = TRUE; + gboolean active_psm_state = DEFAULT_POWER_SAVING_MODE; + gboolean forced_psm = FALSE; + gboolean tklock_autolock = DEFAULT_TK_AUTOLOCK; + gint blank_inhibit = -1; + gint psm_threshold = DEFAULT_PSM_THRESHOLD; + dbus_uint32_t radio_states = 0; + DBusMessage *reply = NULL; + DBusError error; + + /* Register error channel */ + dbus_error_init(&error); + + /* Get radio states */ + status = mcetool_dbus_call_uint(MCE_RADIO_STATES_GET, NULL, + &radio_states); + + if (status != 0) + goto EXIT; + + fprintf(stdout, + _("\n" + "MCE status:\n" + "-----------\n")); + + /* Get the version; just ignore if no reply */ + status = mcetool_dbus_call_string(MCE_VERSION_GET, &mce_version, FALSE); + + if (status == 0) { + fprintf(stdout, + " %-40s %s\n", + _("MCE version:"), + (mce_version == NULL) ? _("unknown") : mce_version); + } + + free(mce_version); + + fprintf(stdout, + " %-40s\n", + _("Radio states:")); + fprintf(stdout, + " %-32s %s\n", + _("Master:"), + radio_states & MCE_RADIO_STATE_MASTER ? _("enabled (Online)") : _("disabled (Offline)")); + fprintf(stdout, + " %-32s %s\n", + _("Cellular:"), + radio_states & MCE_RADIO_STATE_CELLULAR ? _("enabled") : _("disabled")); + fprintf(stdout, + " %-32s %s\n", + _("WLAN:"), + radio_states & MCE_RADIO_STATE_WLAN ? _("enabled") : _("disabled")); + fprintf(stdout, + " %-32s %s\n", + _("Bluetooth:"), + radio_states & MCE_RADIO_STATE_BLUETOOTH ? _("enabled") : _("disabled")); + + /* Get the call state; just ignore if no reply */ + reply = mcetool_dbus_call_with_reply(MCE_CALL_STATE_GET, NULL); + + if (dbus_message_get_args(reply, &error, + DBUS_TYPE_STRING, &callstate, + DBUS_TYPE_STRING, &calltype, + DBUS_TYPE_INVALID) == FALSE) { + fprintf(stderr, + "Failed to get reply argument from %s: " + "%s; exiting", + MCE_CALL_STATE_GET, error.message); + dbus_message_unref(reply); + dbus_error_free(&error); + reply = NULL; + } + + fprintf(stdout, + " %-40s %s (%s)\n", + _("Call state (type):"), + (callstate == NULL) ? _("unknown") : callstate, + (calltype == NULL) ? _("unknown") : calltype); + + dbus_message_unref(reply); + + /* Get display state */ + status = mcetool_dbus_call_string(MCE_DISPLAY_STATUS_GET, + &display, FALSE); + + if (status != 0) + goto EXIT; + + fprintf(stdout, + " %-40s %s\n", + _("Display state:"), + display); + + /* Display brightness */ + retval = mcetool_gconf_get_int(MCE_GCONF_DISPLAY_BRIGHTNESS_PATH, + &brightness); + + if (retval == TRUE) { + fprintf(stdout, + " %-40s %d %s\n", + _("Brightness:"), + brightness, + _("(1-5)")); + } else { + fprintf(stdout, + " %-40s %s\n", + _("Brightness:"), + _("")); + } + + /* Get CABC mode */ + status = mcetool_dbus_call_string(MCE_CABC_MODE_GET, &cabc, FALSE); + + if (status != 0) + goto EXIT; + + fprintf(stdout, + " %-40s %s\n", + _("CABC mode:"), + cabc); + + /* Get dim timeout */ + retval = mcetool_gconf_get_int(MCE_GCONF_DISPLAY_DIM_TIMEOUT_PATH, + &dim_timeout); + + if (retval == TRUE) { + fprintf(stdout, + " %-40s %d %s\n", + _("Dim timeout:"), + dim_timeout, + _("seconds")); + } else { + fprintf(stdout, + " %-40s %s\n", + _("Dim timeout:"), + _("")); + } + + /* Get the adaptive dimming setting */ + retval = mcetool_gconf_get_bool(MCE_GCONF_DISPLAY_ADAPTIVE_DIMMING_PATH, + &adaptive_dimming_enabled); + + if (retval == TRUE) { + fprintf(stdout, + " %-40s %s\n", + _("Adaptive dimming:"), + adaptive_dimming_enabled ? _("enabled") : + _("disabled")); + } else { + fprintf(stdout, + " %-40s %s\n", + _("Adaptive dimming:"), + _("")); + } + + /* Get the adaptive dimming threshold */ + retval = mcetool_gconf_get_int(MCE_GCONF_DISPLAY_ADAPTIVE_DIM_THRESHOLD_PATH, &adaptive_dimming_threshold); + + if (retval == TRUE) { + fprintf(stdout, + " %-40s %d %s\n", + _("Adaptive dimming threshold:"), + adaptive_dimming_threshold, + _("milliseconds")); + } else { + fprintf(stdout, + " %-40s %s\n", + _("Adaptive dimming threshold:"), + _("")); + } + + /* Get blank timeout */ + retval = mcetool_gconf_get_int(MCE_GCONF_DISPLAY_BLANK_TIMEOUT_PATH, + &blank_timeout); + + if (retval == TRUE) { + fprintf(stdout, + " %-40s %d %s\n", + _("Blank timeout:"), + blank_timeout, + _("seconds")); + } else { + fprintf(stdout, + " %-40s %s\n", + _("Blank timeout:"), + _("")); + } + + /* Get blanking inhibit policy */ + (void)mcetool_gconf_get_int(MCE_GCONF_BLANKING_INHIBIT_MODE_PATH, + &blank_inhibit); + + switch (blank_inhibit) { + case 0: + inhibit_string = _("disabled"); + break; + + case 1: + inhibit_string = _("stay on with charger"); + break; + + case 2: + inhibit_string = _("stay dim with charger"); + break; + + case 3: + inhibit_string = _("stay on"); + break; + + case 4: + inhibit_string = _("stay dim"); + break; + + case -1: + inhibit_string = _(""); + break; + + default: + inhibit_string = _(""); + break; + } + + fprintf(stdout, + " %-40s %s\n", + _("Blank inhibit:"), + inhibit_string); + + /* Get keyboard backlight state */ + status = mcetool_dbus_call_bool(MCE_KEY_BACKLIGHT_STATE_GET, NULL, + &keyboard_backlight); + + if (status != 0) + goto EXIT; + + fprintf(stdout, + " %-40s %s\n", + _("Keyboard backlight:"), + keyboard_backlight ? _("enabled") : _("disabled")); + + /* Get inactivity status */ + status = mcetool_dbus_call_bool(MCE_INACTIVITY_STATUS_GET, NULL, + &inactive); + + if (status != 0) + goto EXIT; + + fprintf(stdout, + " %-40s %s\n", + _("Inactivity status:"), + inactive ? _("inactive") : _("active")); + + /* Get the automatic power saving mode setting */ + retval = mcetool_gconf_get_bool(MCE_GCONF_PSM_PATH, + &power_saving_mode); + + /* Get PSM state */ + status = mcetool_dbus_call_bool(MCE_PSM_STATE_GET, NULL, + &active_psm_state); + + if (status != 0) + goto EXIT; + + fprintf(stdout, + " %-40s %s (%s)\n", + _("Power saving mode:"), + retval ? (power_saving_mode ? _("enabled") : _("disabled")) : _(""), + active_psm_state ? _("active") : _("inactive")); + + /* Get the forced power saving mode setting */ + retval = mcetool_gconf_get_bool(MCE_GCONF_FORCED_PSM_PATH, + &forced_psm); + + fprintf(stdout, + " %-40s %s\n", + _("Forced power saving mode:"), + retval ? (forced_psm ? _("enabled") : _("disabled")) : _("")); + + /* Get PSM threshold */ + retval = mcetool_gconf_get_int(MCE_GCONF_PSM_THRESHOLD_PATH, + &psm_threshold); + + if (retval == TRUE) { + fprintf(stdout, + " %-40s %d%%\n", + _("PSM threshold:"), + psm_threshold); + } else { + fprintf(stdout, + " %-40s %s\n", + _("PSM threshold:"), + _("")); + } + + /* Get touchscreen/keypad lock mode */ + status = mcetool_dbus_call_string(MCE_TKLOCK_MODE_GET, &tklock, FALSE); + + if (status != 0) + goto EXIT; + + fprintf(stdout, + " %-40s %s\n", + _("Touchscreen/Keypad lock:"), + tklock); + + /* Get touchscreen/keypad autolock mode */ + retval = mcetool_gconf_get_bool(MCE_GCONF_TK_AUTOLOCK_ENABLED_PATH, + &tklock_autolock); + + fprintf(stdout, + " %-40s %s\n", + _("Touchscreen/Keypad autolock:"), + retval ? (tklock_autolock ? _("enabled") : _("disabled")) : _("")); + +EXIT: + fprintf(stdout, "\n"); + + return status; +} + +/** + * Main + * + * @param argc Number of command line arguments + * @param argv Array with command line arguments + * @return 0 on success, non-zero on failure + */ +int main(int argc, char **argv) +{ + int optc; + int opt_index; + + int status = 0; + + gint powerkeyevent = INVALID_EVENT; + gint newinhibitmode = -1; + gint newpsm = -1; + gint newforcedpsm = -1; + gint newpsmthreshold = -1; + gint newbrightness = -1; + gchar *newcabcmode = NULL; + gchar *newcallstate = NULL; + gchar *newcalltype = NULL; + gchar *newtklockmode = NULL; + gchar *ledpattern = NULL; + gint led_enable = -1; + gboolean block = FALSE; + gboolean ledpattern_activate = TRUE; + gboolean get_mce_status = TRUE; + gboolean force_mce_status = FALSE; + gboolean send_prevent = FALSE; + gboolean send_cancel_prevent = FALSE; + gboolean send_unblank = FALSE; + gboolean send_dim = FALSE; + gboolean send_blank = FALSE; + dbus_uint32_t new_radio_states; + dbus_uint32_t radio_states_mask; + + DBusBusType bus_type = DBUS_BUS_SYSTEM; + + const char optline[] = "S"; + + struct option const options[] = { + { "block", no_argument, 0, 'B' }, + { "blank-prevent", no_argument, 0, 'P' }, + { "cancel-blank-prevent", no_argument, 0, 'v' }, + { "unblank-screen", no_argument, 0, 'U' }, + { "dim-screen", no_argument, 0, 'd' }, + { "blank-screen", no_argument, 0, 'n' }, + { "set-display-brightness", required_argument, 0, 'b' }, + { "set-inhibit-mode", required_argument, 0, 'I' }, + { "set-cabc-mode", required_argument, 0, 'C' }, + { "set-call-state", required_argument, 0, 'c' }, + { "enable-radio", required_argument, 0, 'r' }, + { "disable-radio", required_argument, 0, 'R' }, + { "set-power-saving-mode", required_argument, 0, 'p' }, + { "set-forced-psm", required_argument, 0, 'F' }, + { "set-psm-threshold", required_argument, 0, 'T' }, + { "set-tklock-mode", required_argument, 0, 'k' }, + { "enable-led", no_argument, 0, 'l' }, + { "disable-led", no_argument, 0, 'L' }, + { "activate-led-pattern", required_argument, 0, 'y' }, + { "deactivate-led-pattern", required_argument, 0, 'Y' }, + { "powerkey-event", required_argument, 0, 'e' }, + { "modinfo", required_argument, 0, 'M' }, + { "status", no_argument, 0, 'N' }, + { "session", no_argument, 0, 'S' }, + { "help", no_argument, 0, 'h' }, + { "version", no_argument, 0, 'V' }, + { 0, 0, 0, 0 } + }; + + /* By default, don't change any radio state */ + new_radio_states = 0; + radio_states_mask = 0; + + /* Initialise support for locales, and set the program-name */ + if (init_locales(PRG_NAME) != 0) + goto EXIT; + + /* Parse the command-line options */ + while ((optc = getopt_long(argc, argv, optline, + options, &opt_index)) != -1) { + switch (optc) { + case 'B': + block = TRUE; + break; + + case 'P': + send_prevent = TRUE; + get_mce_status = FALSE; + break; + + case 'v': + send_cancel_prevent = TRUE; + get_mce_status = FALSE; + break; + + case 'U': + send_unblank = TRUE; + get_mce_status = FALSE; + break; + + case 'd': + send_dim = TRUE; + get_mce_status = FALSE; + break; + + case 'n': + send_blank = TRUE; + get_mce_status = FALSE; + break; + + case 'r': + if (!strcmp(optarg, RADIO_MASTER)) { + new_radio_states |= MCE_RADIO_STATE_MASTER; + radio_states_mask |= MCE_RADIO_STATE_MASTER; + } else if (!strcmp(optarg, RADIO_CELLULAR)) { + new_radio_states |= MCE_RADIO_STATE_CELLULAR; + radio_states_mask |= MCE_RADIO_STATE_CELLULAR; + } else if (!strcmp(optarg, RADIO_WLAN)) { + new_radio_states |= MCE_RADIO_STATE_WLAN; + radio_states_mask |= MCE_RADIO_STATE_WLAN; + } else if (!strcmp(optarg, RADIO_BLUETOOTH)) { + new_radio_states |= MCE_RADIO_STATE_BLUETOOTH; + radio_states_mask |= MCE_RADIO_STATE_BLUETOOTH; + } else { + usage(); + status = EINVAL; + goto EXIT; + } + + break; + + case 'R': + if (!strcmp(optarg, RADIO_MASTER)) { + new_radio_states &= ~MCE_RADIO_STATE_MASTER; + radio_states_mask |= MCE_RADIO_STATE_MASTER; + } else if (!strcmp(optarg, RADIO_CELLULAR)) { + new_radio_states &= ~MCE_RADIO_STATE_CELLULAR; + radio_states_mask |= MCE_RADIO_STATE_CELLULAR; + } else if (!strcmp(optarg, RADIO_WLAN)) { + new_radio_states &= ~MCE_RADIO_STATE_WLAN; + radio_states_mask |= MCE_RADIO_STATE_WLAN; + } else if (!strcmp(optarg, RADIO_BLUETOOTH)) { + new_radio_states &= ~MCE_RADIO_STATE_BLUETOOTH; + radio_states_mask |= MCE_RADIO_STATE_BLUETOOTH; + } else { + usage(); + status = EINVAL; + goto EXIT; + } + + break; + + case 'p': + if (!strcmp(optarg, ENABLED_STRING)) { + newpsm = TRUE; + } else if (!strcmp(optarg, DISABLED_STRING)) { + newpsm = FALSE; + } else { + usage(); + status = EINVAL; + goto EXIT; + } + + get_mce_status = FALSE; + break; + + case 'F': + if (!strcmp(optarg, ENABLED_STRING)) { + newforcedpsm = TRUE; + } else if (!strcmp(optarg, DISABLED_STRING)) { + newforcedpsm = FALSE; + } else { + usage(); + status = EINVAL; + goto EXIT; + } + + get_mce_status = FALSE; + break; + + case 'T': + { + gint tmp; + + if (sscanf(optarg, "%d", &tmp) != 1) { + usage(); + status = EINVAL; + goto EXIT; + } + + if ((tmp < 10) || (tmp > 50) || + ((tmp % 10) != 0)) { + usage(); + status = EINVAL; + goto EXIT; + } + + newpsmthreshold = tmp; + } + + get_mce_status = FALSE; + break; + + case 'b': + { + gint tmp; + + if (sscanf(optarg, "%d", &tmp) != 1) { + usage(); + status = EINVAL; + goto EXIT; + } + + if ((tmp < 1) || (tmp > 5)) { + usage(); + status = EINVAL; + goto EXIT; + } + + newbrightness = tmp; + } + + get_mce_status = FALSE; + break; + + case 'c': + { + char *s1 = strdup(optarg); + char *s2 = strchr(s1, ':'); + + if (s2 == NULL) { + usage(); + status = EINVAL; + goto EXIT; + } + + *s2 = '\0'; + + if (++s2 == '\0') { + usage(); + status = EINVAL; + goto EXIT; + } + + newcallstate = s1; + newcalltype = strdup(s2); + } + + get_mce_status = FALSE; + break; + + case 'I': + if (!strcmp(optarg, BLANKING_INHIBIT_DISABLED)) { + newinhibitmode = 0; + } else if (!strcmp(optarg, BLANKING_INHIBIT_STAY_ON_WITH_CHARGER)) { + newinhibitmode = 1; + } else if (!strcmp(optarg, BLANKING_INHIBIT_STAY_DIM_WITH_CHARGER)) { + newinhibitmode = 2; + } else if (!strcmp(optarg, BLANKING_INHIBIT_STAY_ON)) { + newinhibitmode = 3; + } else if (!strcmp(optarg, BLANKING_INHIBIT_STAY_DIM)) { + newinhibitmode = 4; + } else { + usage(); + status = EINVAL; + goto EXIT; + } + + get_mce_status = FALSE; + break; + + case 'C': + newcabcmode = strdup(optarg); + get_mce_status = FALSE; + break; + + case 'k': + newtklockmode = strdup(optarg); + get_mce_status = FALSE; + break; + + case 'l': + if (led_enable != -1) { + usage(); + status = EINVAL; + goto EXIT; + } + + led_enable = 1; + get_mce_status = FALSE; + break; + + case 'L': + if (led_enable != -1) { + usage(); + status = EINVAL; + goto EXIT; + } + + led_enable = 0; + get_mce_status = FALSE; + break; + + case 'y': + if (ledpattern != NULL) { + usage(); + status = EINVAL; + goto EXIT; + } + + ledpattern = strdup(optarg); + ledpattern_activate = TRUE; + get_mce_status = FALSE; + break; + + case 'Y': + if (ledpattern != NULL) { + usage(); + status = EINVAL; + goto EXIT; + } + + ledpattern = strdup(optarg); + ledpattern_activate = FALSE; + get_mce_status = FALSE; + break; + + case 'e': + if (!strcmp(optarg, LONG_EVENT_STR)) { + powerkeyevent = LONG_EVENT; + } else if (!strcmp(optarg, DOUBLE_EVENT_STR)) { + powerkeyevent = DOUBLE_EVENT; + } else if (!strcmp(optarg, SHORT_EVENT_STR)) { + powerkeyevent = SHORT_EVENT; + } else { + usage(); + status = EINVAL; + goto EXIT; + } + + get_mce_status = FALSE; + break; + + case 'N': + force_mce_status = TRUE; + break; + + case 'S': + bus_type = DBUS_BUS_SESSION; + break; + + case 'h': + usage(); + goto EXIT; + + case 'V': + version(); + goto EXIT; + + default: + usage(); + status = EINVAL; + goto EXIT; + } + } + + /* Any non-flag arguments is an error */ + if ((argc - optind) > 0) { + usage(); + status = EINVAL; + goto EXIT; + } + + /* Initialise D-Bus */ + if ((status = mcetool_dbus_init(bus_type)) != 0) + goto EXIT; + + /* Init GConf */ + if ((status = mcetool_gconf_init()) != 0) + goto EXIT; + + if (send_prevent == TRUE) { + if ((status = mcetool_dbus_call_string(MCE_PREVENT_BLANK_REQ, + NULL, TRUE)) != 0) + goto EXIT; + + fprintf(stdout, "Blank prevent requested\n"); + } + + if (send_cancel_prevent == TRUE) { + if ((status = mcetool_dbus_call_string(MCE_CANCEL_PREVENT_BLANK_REQ, + NULL, TRUE)) != 0) + goto EXIT; + + fprintf(stdout, "Cancel blank prevent requested\n"); + } + + if (send_unblank == TRUE) { + if ((status = mcetool_dbus_call_string(MCE_DISPLAY_ON_REQ, + NULL, TRUE)) != 0) + goto EXIT; + + fprintf(stdout, "Display on requested\n"); + } + + if (send_dim == TRUE) { + if ((status = mcetool_dbus_call_string(MCE_DISPLAY_DIM_REQ, + NULL, TRUE)) != 0) + goto EXIT; + + fprintf(stdout, "Display dim requested\n"); + } + + if (send_blank == TRUE) { + if ((status = mcetool_dbus_call_string(MCE_DISPLAY_OFF_REQ, + NULL, TRUE)) != 0) + goto EXIT; + + fprintf(stdout, "Display off requested\n"); + } + + /* Change the display brightness */ + if (newbrightness != -1) { + if (mcetool_gconf_set_int(MCE_GCONF_DISPLAY_BRIGHTNESS_PATH, + newbrightness) == FALSE) + goto EXIT; + } + + /* Change the tklock mode */ + if (newtklockmode != NULL) { + set_tklock_mode(&newtklockmode); + } + + if (powerkeyevent != INVALID_EVENT) { + trigger_powerkey_event(powerkeyevent); + } + + if (led_enable != -1) { + set_led_state(led_enable); + } + + if (ledpattern != NULL) { + set_led_pattern_state(ledpattern, + ledpattern_activate); + } + + if (newinhibitmode != -1) { + if (mcetool_gconf_set_int(MCE_GCONF_BLANKING_INHIBIT_MODE_PATH, + newinhibitmode) == FALSE) + goto EXIT; + } + + if (radio_states_mask != 0) { + /* Change radio states */ + if (dbus_send(MCE_SERVICE, MCE_REQUEST_PATH, + MCE_REQUEST_IF, MCE_RADIO_STATES_CHANGE_REQ, TRUE, + DBUS_TYPE_UINT32, &new_radio_states, + DBUS_TYPE_UINT32, &radio_states_mask, + DBUS_TYPE_INVALID) == FALSE) { + status = EXIT_FAILURE; + goto EXIT; + } + } + + if (newpsm != -1) { + if (mcetool_gconf_set_bool(MCE_GCONF_PSM_PATH, + newpsm) == FALSE) + goto EXIT; + } + + if (newforcedpsm != -1) { + if (mcetool_gconf_set_bool(MCE_GCONF_FORCED_PSM_PATH, + newforcedpsm) == FALSE) + goto EXIT; + } + + if (newpsmthreshold != -1) { + if (mcetool_gconf_set_int(MCE_GCONF_PSM_THRESHOLD_PATH, + newpsmthreshold) == FALSE) + goto EXIT; + } + + if (newcabcmode != NULL) { + /* Change the cabc mode */ + if (dbus_send(MCE_SERVICE, MCE_REQUEST_PATH, + MCE_REQUEST_IF, MCE_CABC_MODE_REQ, TRUE, + DBUS_TYPE_STRING, &newcabcmode, + DBUS_TYPE_INVALID) == FALSE) { + status = EXIT_FAILURE; + goto EXIT; + } + } + + if ((newcallstate != NULL) && (newcalltype != NULL)) { + /* Change the call state/type */ + if (dbus_send(MCE_SERVICE, MCE_REQUEST_PATH, + MCE_REQUEST_IF, MCE_CALL_STATE_CHANGE_REQ, TRUE, + DBUS_TYPE_STRING, &newcallstate, + DBUS_TYPE_STRING, &newcalltype, + DBUS_TYPE_INVALID) == FALSE) { + status = EXIT_FAILURE; + goto EXIT; + } + } + + if ((get_mce_status == TRUE) || (force_mce_status == TRUE)) + mcetool_get_status(); + + while (block == TRUE) + /* Do nothing */; + +EXIT: + mcetool_gconf_exit(); + mcetool_dbus_exit(); + + return status; +} diff --git a/tools/mcetool.h b/tools/mcetool.h new file mode 100644 index 00000000..80a9bbc1 --- /dev/null +++ b/tools/mcetool.h @@ -0,0 +1,53 @@ +/** + * @file mcetool.h + * Headers for the mcetool + *

+ * Copyright © 2005-2008 Nokia Corporation and/or its subsidiary(-ies). + *

+ * @author David Weinehall + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce 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 mce. If not, see . + */ +#ifndef _MCETOOL_H_ +#define _MCETOOL_H_ + +#include +#include + +#ifdef ENABLE_NLS +#include +/** _() to use when NLS is enabled */ +#define _(__str) gettext(__str) +#else +#undef bindtextdomain +/** Dummy bindtextdomain to use when NLS is disabled */ +#define bindtextdomain(__domain, __directory) +#undef textdomain +/** Dummy textdomain to use when NLS is disabled */ +#define textdomain(__domain) +/** Dummy _() to use when NLS is disabled */ +#define _(__str) __str +#endif /* ENABLE_NLS */ + +/** Path to the GConf entry for the devicelock autolock setting */ +#define SYSTEMUI_GCONF_DEVICE_AUTOLOCK_ENABLED_PATH "/system/systemui/devlock/devicelock_autolock_enabled" + +/** The mcetool DBus service */ +#define MCETOOL_SERVICE "com.nokia.mcetool" + +/** The mcetool DBus Request interface */ +#define MCETOOL_REQUEST_IF "com.nokia.mcetool.request" +/** The mcetool DBus Signal interface */ +#define MCETOOL_REQUEST_PATH "/com/nokia/mcetool/request" + +#endif /* _MCETOOL_H_ */