├── .github ├── dependabot.yml └── workflows │ └── test.yml ├── .gitignore ├── AUTHORS ├── COPYING ├── ChangeLog ├── Makefile.am ├── NEWS ├── README ├── README.md ├── autogen.sh ├── configure.ac ├── doc ├── Makefile.am └── hdapsd.man ├── misc ├── Makefile.am ├── hdapsd.conf ├── hdapsd.rules ├── hdapsd.service.in └── hdapsd@.service.in ├── src ├── Makefile.am ├── hdapsd.c ├── hdapsd.h ├── input-helper.c └── input-helper.h └── tests ├── acer.umockdev ├── ams.umockdev ├── applesmc.umockdev ├── hdaps-accel.umockdev ├── hdaps-joystick.umockdev ├── hdaps.umockdev ├── sata-disk.umockdev ├── toshiba_acpi.umockdev └── toshiba_haps.umockdev /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | updates: 4 | 5 | # Maintain dependencies for GitHub Actions 6 | - package-ecosystem: "github-actions" 7 | directory: "/" 8 | schedule: 9 | interval: "daily" 10 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test 3 | 4 | on: 5 | pull_request: 6 | push: 7 | branches: 8 | - master 9 | 10 | concurrency: 11 | group: ${{ github.ref_name }}-${{ github.workflow }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | test: 16 | name: Test 17 | runs-on: ubuntu-latest 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | config: 22 | - none 23 | - libconfig 24 | cc: 25 | - gcc 26 | - clang 27 | steps: 28 | - uses: actions/checkout@v4 29 | - name: Install pkg-config 30 | run: sudo apt-get install -y pkg-config 31 | - name: Install libconfig 32 | if: matrix.config == 'libconfig' 33 | run: sudo apt-get install -y libconfig-dev 34 | - run: ./autogen.sh 35 | env: 36 | CC: ${{ matrix.cc }} 37 | - run: make 38 | env: 39 | CC: ${{ matrix.cc }} 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.bak 3 | .deps/* 4 | src/.deps/* 5 | INSTALL 6 | Makefile 7 | Makefile.in 8 | aclocal.m4 9 | autom4te.cache/* 10 | config.* 11 | configure 12 | depcomp 13 | compile 14 | hdapsd 15 | *.o 16 | install-sh 17 | missing 18 | stamp-h1 19 | *.tar.gz 20 | doc/hdapsd.8 21 | misc/hdapsd@.service 22 | misc/hdapsd.service 23 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Jon Escombe 2 | Robert Love 3 | Shem Multinymous 4 | Elias Oltmanns 5 | Evgeni Golov 6 | Brice Arnould 7 | Thomas Weißschuh 8 | Tomasz Torcz 9 | Whoopie 10 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | hdapsd 20141203: 2 | Evgeni Golov : 3 | * Fix cleanup of generated .service files for systemd 4 | 5 | Whoopie : 6 | * Use sigaction(2) instead of signal(2) for handling SIGUSR1 7 | and SIGTERM. This helps stoping hdapsd properly when using 8 | the DELL freefall sensor. 9 | 10 | hdapsd 20141024: 11 | Brice Arnould : 12 | * Support for the HP3D sensor from Hewlett-Packard laptops. 13 | 14 | Evgeni Golov : 15 | * Support for Apple MacBooks and MacBooks Pro with Intel CPUs. 16 | * Support for the generic FREEFALL sensor. 17 | * Support for Toshiba laptops (using both ACPI and HAPS). 18 | * Support for the generic input/joystick sensor. 19 | * Config file support using libconfig. 20 | 21 | Tomasz Torcz and 22 | Thomas Weißschuh : 23 | * systemd support 24 | 25 | Whoopie : 26 | * Support for the DELL freefall sensor (smo8800) 27 | 28 | hdapsd 20090401: 29 | Evgeni Golov : 30 | * Support for the Apple Motion Sensor. 31 | * Udev rules obsolete, we can find the device on our own now. 32 | * Support autodetection of drives. 33 | * Try to autoload the needed modules, when neither HDAPS nor AMS 34 | is found at startup. 35 | * Option to forcely enable UNLOAD on disks where the kernel thinks 36 | it's unsupported. 37 | 38 | hdapsd 20090129: 39 | Evgeni Golov : 40 | * Support for multiple disks. 41 | * Support for logging to syslog when running in background. 42 | * Use autotools. 43 | * Make the error-messages more user-friendly. 44 | * -s is optional now. Sensitivity defaults to 15. 45 | 46 | hdapsd 20081004: 47 | Elias Oltmanns : 48 | * Prepare for the new interface in kernel 2.6.28 49 | 50 | hdapsd 20070803: 51 | Shem Multinymous : 52 | * Read the accelerometer data from the input device provided by 53 | tp_smapi>=0.32, instead of sysfs. This reduce timer interrupts 54 | and improves temporal synchronization of user-space<->kernel vs. 55 | kernel<->hardware. If the input device doesn't exist, it falls 56 | back to sysfs. 57 | * Add a new new --poll-sysfs (-y) parameter to force using sysfs 58 | instead of the input device. 59 | * Read the sampling rate from the sysfs file provided by tp_smapi 60 | (if it exists). 61 | * Add a --dry-run (-t) parameter, telling hdapsd to do everything 62 | except actually parking the drive. Very useful for testing. 63 | 64 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS=src doc misc 2 | doc_DATA = README.md ChangeLog 3 | EXTRA_DIST = README.md 4 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linux-thinkpad/hdapsd/3364df98103120ae8b1bd94485fb2f2d14cc706f/NEWS -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | README.md -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | `hdapsd` - Hard Drive Active Protection System Daemon 2 | =================================================== 3 | 4 | This is a disk protection user-space daemon. It monitors the acceleration 5 | values through the various motion interfaces and automatically initiates 6 | disk head parking if a fall or sliding of the laptop is detected. 7 | 8 | Currently, the following motion interfaces are supported: 9 | * HDAPS on IBM/Lenovo ThinkPads 10 | * AMS on Apple iBooks and PowerBooks (G4) 11 | * FREEFALL on Hewlett-Packard and DELL laptops 12 | * HP3D on Hewlett-Packard laptops 13 | * APPLESMC on Apple MacBooks and MacBooks Pro (Intel) (UNTESTED!) 14 | * Toshiba HAPS and Toshiba ACPI on Toshiba laptops (UNTESTED!) 15 | * Generic input/joystick on ACER laptops (UNTESTED!) 16 | 17 | Compilation 18 | ----------- 19 | 20 | ./configure 21 | make 22 | make install 23 | 24 | ### Configure parameters 25 | 26 | The following parameters to `./configure` are probably interesting: 27 | 28 | * `--with-systemdsystemunitdir` lets you specify the directory for 29 | systemd unit files. It defaults to the output of 30 | `pkg-config --variable=systemdsystemunitdir systemd`. 31 | * `--with-udevdir` lets you specify the directory for udev rules files. 32 | It defaults to the output of `pkg-config --variable=udevdir udev`. 33 | 34 | Packages 35 | -------- 36 | * [Arch](https://www.archlinux.org/packages/hdapsd) and [AUR](https://aur.archlinux.org/packages/hdapsd-git/) 37 | * [Debian](http://packages.debian.org/hdapsd) 38 | * [Gentoo](https://packages.gentoo.org/package/app-laptop/hdapsd) 39 | * [Slackbuilds](http://slackbuilds.org/result/?search=hdapsd) 40 | * [Ubuntu](http://packages.ubuntu.com/hdapsd) 41 | 42 | Usage 43 | ----- 44 | 45 | In most cases, just running `hdapsd` as root should be enough, as it will 46 | try to autodetect everything itself. 47 | 48 | If you want to adjust stuff, these are the most commonly used options: 49 | 50 | * `--cfgfile` which allows to load a configuration from a file. 51 | * `--device` which device to protect, e.g. `--device=sda`. Defaults to 52 | autodetection of all rotating devices. 53 | * `--sensitivity` adjusts the sensitivity of the algorithmus. Defaults to 15. 54 | * `--adaptive` enables adaptive mode, where `hdapsd` adjusts the sensitity 55 | while the mouse and keyboard are used. 56 | * `--background` sends `hdapsd` into the background as a daemon. 57 | 58 | For more options, please read `man hdapsd`. 59 | 60 | systemd and udev integration 61 | ---------------------------- 62 | 63 | `hdapsd` comes with `systemd` and `udev` integration. This means when those two 64 | are found on your system, `misc/hdapsd@.service` and `misc/hdapsd.rules` are 65 | installed and used. `udev` will start one `hdapsd` instance for each 66 | rotational, non-removable disk it finds. 67 | 68 | If you want to disable this automation for a certain disk, you can mask 69 | the `systemd` unit by calling: 70 | 71 | systemctl mask hdapsd@sdX 72 | 73 | If you want to disable this automation at all, you can create an empty 74 | `/etc/udev/rules.d/hdapsd.rules`, which will override the system-installed 75 | udev rule. You can still enable `hdapsd` for certain devices by creating 76 | `hdapsd@sdX.service` symlinks in `/etc/systemd/system/multi-user.target.wants/` 77 | 78 | If you want to customize the parameters `hdapsd` is using, you can edit 79 | `/etc/hdapsd.conf` (preferred) or by customizing `hdapsd@.service` in 80 | `/etc/systemd/system/`. 81 | 82 | As an alternative, you could also use `misc/hdapsd.service`, which you'd 83 | have to install yourself. This unit will just start `hdapsd` the same way 84 | good old `sysvinit` would do. 85 | 86 | Compatibility 87 | ------------- 88 | Since kernel 2.6.28 you don't need to patch your kernel, as support for 89 | IDLE_IMMEDIATE is present in mainline. 90 | 91 | **NOTE**: The new interface only allows IDLE_IMMEDIATE for drives that 92 | announce to be ATA-7 conform. But threre are also drives that support ATA-6 93 | only but do IDLE_IMMEDIATE fine. For those you need to force the interface 94 | with: `echo -1 > /sys/block/$DISK/device/unload_heads`. 95 | Or you can call `hdapsd` like this: `hdapsd -f -d $DISK`, to achieve the same 96 | result. 97 | 98 | For kernels <2.6.28, please have a look at 99 | http://www.thinkwiki.org/wiki/HDAPS#Kernel_patch 100 | and patch your kernel with the appropriate patch before using `hdapsd`. 101 | 102 | mainline hdaps module vs tp_smapi (ThinkPad only) 103 | ------------------------------------------------- 104 | The mainline hdaps module present in Linux kernels does not support all 105 | hdaps-enabled ThinkPads, thus it is recommended to use the one provided 106 | by tp_smapi. 107 | Additionally the tp_smapi version provides an input interface to the data, 108 | which stops `hdapsd` from polling the data itself all the time, saving your 109 | battery. 110 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | srcdir=`dirname $0` 4 | test -z "$srcdir" && srcdir=. 5 | 6 | ORIGDIR=`pwd` 7 | cd $srcdir 8 | 9 | autoreconf -v --install || exit 1 10 | cd $ORIGDIR || exit $? 11 | 12 | $srcdir/configure --enable-maintainer-mode "$@" 13 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | 4 | AC_PREREQ(2.61) 5 | AC_INIT(hdapsd, 20141203, https://github.com/linux-thinkpad/hdapsd) 6 | AM_INIT_AUTOMAKE([foreign]) 7 | AC_CONFIG_SRCDIR([src/hdapsd.c]) 8 | AC_CONFIG_HEADERS([src/config.h]) 9 | AC_CONFIG_FILES([Makefile src/Makefile doc/Makefile misc/Makefile]) 10 | 11 | AM_MAINTAINER_MODE([enable]) 12 | 13 | # Check for pkg-config/pkg.m4. 14 | m4_pattern_forbid([^PKG_[A-Z_]+$], [pkg.m4 missing, please install pkg-config]) 15 | 16 | # Checks for programs. 17 | AC_PROG_CC 18 | 19 | # Checks for libraries. 20 | 21 | # Checks for header files. 22 | AC_HEADER_STDC 23 | AC_CHECK_HEADERS([fcntl.h stdlib.h string.h sys/time.h unistd.h syslog.h linux/input.h dirent.h]) 24 | 25 | # Checks for typedefs, structures, and compiler characteristics. 26 | AC_C_CONST 27 | AC_HEADER_TIME 28 | 29 | # Checks for library functions. 30 | AC_TYPE_SIGNAL 31 | AC_CHECK_FUNCS([gettimeofday strerror uname]) 32 | 33 | # check for systemd 34 | PKG_PROG_PKG_CONFIG 35 | AC_ARG_WITH([systemdsystemunitdir], 36 | AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]), 37 | [], [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)]) 38 | if test "x$with_systemdsystemunitdir" != xno; then 39 | AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir]) 40 | fi 41 | AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != xno ]) 42 | 43 | # check for udevdir 44 | AC_ARG_WITH([udevdir], 45 | AS_HELP_STRING([--with-udevdir=DIR], [Directory for udev]), 46 | [], [with_udevdir=$($PKG_CONFIG --variable=udevdir udev)]) 47 | if test -z "$with_udevdir"; then 48 | with_udevdir="/lib/udev" 49 | fi 50 | AC_SUBST([udevdir], [$with_udevdir]) 51 | 52 | AC_ARG_ENABLE([libconfig], 53 | AS_HELP_STRING([--disable-libconfig], [Build without libconfig support])) 54 | 55 | AS_IF([test "x$enable_libconfig" != "xno"], [ 56 | PKG_CHECK_MODULES([LIBCONFIG], [libconfig], 57 | [AC_DEFINE([HAVE_LIBCONFIG], [1], [Have libconfig]) 58 | have_libconfig=yes], 59 | [AC_MSG_NOTICE([libconfig was not found on your system, config file support will be disabled.])]) 60 | ]) 61 | AM_CONDITIONAL(HAVE_LIBCONFIG, [test -n "$have_libconfig" -a "x$have_libconfig" = xyes ]) 62 | 63 | AC_OUTPUT 64 | -------------------------------------------------------------------------------- /doc/Makefile.am: -------------------------------------------------------------------------------- 1 | MAN_SUBSTS = \ 2 | -e 's|__VERSION__|@PACKAGE_VERSION@|' \ 3 | -e "s|__DATE__|`date -d @PACKAGE_VERSION@ '+%Y-%m-%d'`|" 4 | 5 | CLEANFILES = @PACKAGE_NAME@.8 6 | 7 | man_MANS = @PACKAGE_NAME@.8 8 | man_PRE = @PACKAGE_NAME@.man 9 | EXTRA_DIST = @PACKAGE_NAME@.man 10 | 11 | .man.8: 12 | sed $(MAN_SUBSTS) < $< > $@ 13 | -------------------------------------------------------------------------------- /doc/hdapsd.man: -------------------------------------------------------------------------------- 1 | .TH "HDAPSD" 8 "__DATE__" "hdapsd __VERSION__" "hdapsd" 2 | .SH NAME 3 | hdapsd \- park the drive in case of an emergency 4 | .SH SYNOPSIS 5 | .B hdapsd \fR[\fI\-f\fR|\fI\-r\fR|\fI\-c \fR|\fI\-d \fR|\fI\-s \fR|\fI\-a\fR|\fI\-v\fR|\fI\-b\fR|\fI\-p\fR|\fI\-t\fR|\fI\-y\fR|\fI\-H\fR|\fI\-S\fR|\fI\-L\fR|\fI\-l\fR|\fI\-V\fR|\fI\-h\fR] 6 | .SH OPTIONS 7 | .TP 8 | \fB\-c\fR \fB\-\-cfgfile=\fR\fI\fR 9 | Load configuration from . Only available if hdapsd was compiled with libconfig support. 10 | .TP 11 | \fB\-d\fR \fB\-\-device=\fR\fI\fR 12 | is likely to be hda or sda. Can be given multiple times to protect multiple devices. 13 | .TP 14 | \fB\-f\fR \fB\-\-force\fR 15 | Force unloading heads, even if kernel thinks different (on pre ATA7 drives). 16 | This only works when adding devices by hand (\-d). 17 | .TP 18 | \fB\-r\fR \fB\-\-force\-rotational\fR 19 | Autodetect drives as rotational, even if kernel thinks they are not. 20 | .TP 21 | \fB\-s\fR \fB\-\-sensitivity=\fR\fI\fR 22 | How sensitive hdapsd should be to movements. 23 | Defaults to 15, higher values mean less sensitive. 24 | .TP 25 | \fB\-a\fR \fB\-\-adaptive\fR 26 | Adaptive threshold (automatic increase when the built\-in keyboard/mouse are used). 27 | .TP 28 | \fB\-v\fR \fB\-\-verbose\fR 29 | Get verbose statistics. 30 | .TP 31 | \fB\-b\fR \fB\-\-background\fR 32 | Run the process in the background. 33 | .TP 34 | \fB\-p\fR \fB\-\-pidfile\fR[\fI=\fR] 35 | Create a pid file when running in background. 36 | If is not specified, it's set to /var/run/hdapsd.pid. 37 | .TP 38 | \fB\-t\fR \fB\-\-dry\-run\fR 39 | Don't actually park the drive. 40 | .TP 41 | \fB\-y\fR \fB\-\-poll\-sysfs\fR 42 | Force use of sysfs interface to accelerometer. 43 | .TP 44 | \fB\-H\fR \fB\-\-hardware\-logic\fR 45 | Uses the hardware fall detection logic instead of the software 46 | one. Options \-\-sensitivity and \-\-adaptive have no effect 47 | in this mode. 48 | .TP 49 | \fB\-S\fR \fB\-\-software\-logic\fR 50 | Uses the software fall detection logic even if the hardware one is 51 | available. 52 | .TP 53 | \fB\-L\fR \fB\-\-no\-leds\fR 54 | Don't blink the LEDs when a shock is detected. 55 | .TP 56 | \fB\-l\fR \fB\-\-syslog\fR 57 | Log to syslog instead of stdout/stderr. 58 | .TP 59 | \fB\-V\fR \fB\-\-version\fR 60 | Display version information and exit. 61 | .TP 62 | \fB\-h\fR \fB\-\-help\fR 63 | Display help and exit. 64 | 65 | .PP 66 | You can send SIGUSR1 to deactivate hdapsd for 8 seconds. 67 | -------------------------------------------------------------------------------- /misc/Makefile.am: -------------------------------------------------------------------------------- 1 | if HAVE_SYSTEMD 2 | udevrulesdir = $(udevdir)/rules.d 3 | udevrules_DATA = hdapsd.rules 4 | systemdsystemunit_DATA = \ 5 | hdapsd@.service 6 | noinst_DATA = hdapsd.service 7 | CLEANFILES = hdapsd@.service hdapsd.service 8 | 9 | edit = sed \ 10 | -e 's|@sbindir[@]|$(sbindir)|g' 11 | 12 | hdapsd@.service: Makefile 13 | rm -f $@ $@.tmp 14 | srcdir=''; \ 15 | test -f ./$@.in || srcdir=$(srcdir)/; \ 16 | $(edit) $${srcdir}$@.in >$@.tmp 17 | 18 | mv $@.tmp $@ 19 | 20 | hdapsd@.service: hdapsd@.service.in 21 | 22 | hdapsd.service: Makefile 23 | rm -f $@ $@.tmp 24 | srcdir=''; \ 25 | test -f ./$@.in || srcdir=$(srcdir)/; \ 26 | $(edit) $${srcdir}$@.in >$@.tmp 27 | 28 | mv $@.tmp $@ 29 | 30 | hdapsd.service: hdapsd.service.in 31 | 32 | endif 33 | 34 | if HAVE_LIBCONFIG 35 | sysconf_DATA = hdapsd.conf 36 | endif 37 | 38 | EXTRA_DIST = \ 39 | hdapsd.conf hdapsd.rules hdapsd@.service.in hdapsd.service.in 40 | -------------------------------------------------------------------------------- /misc/hdapsd.conf: -------------------------------------------------------------------------------- 1 | # Which device(s) should be protected by hdapsd? 2 | # Can be either a single string (one device): 3 | # device="sda"; 4 | # Or an array of strings (multiple devices): 5 | # device=["sda", "sdb"]; 6 | # Will be autodetected if not set. 7 | 8 | # How sensitive should hdapsd be? Defaults to 15. 9 | # You probably want to change this. 10 | # sensitivity=15; 11 | 12 | # Enables adaptive mode, where hdapsd adjusts the sensitity 13 | # while the mouse and keyboard are used. 14 | # You probably want to enable this. 15 | # adaptive=true; 16 | 17 | # Run hdapsd in background as a daemon. 18 | # background=true; 19 | 20 | # Write a pid file. 21 | # pidfile="/var/run/hdapsd.pid"; 22 | 23 | # Enable logging to syslog. 24 | # syslog=true; 25 | -------------------------------------------------------------------------------- /misc/hdapsd.rules: -------------------------------------------------------------------------------- 1 | # we start protection for rotational disks. 2 | # in unlikely event of wanting to protect flash bases disks, you can manually 3 | # symlink hdapsd@.service to /etc/systemd/system/hdapsd@sdc.service , hdapsd@sdd.service etc. 4 | SUBSYSTEM=="block", KERNEL=="sd[a-z]", ATTRS{removable}=="0", ATTRS{queue/rotational}=="1", TAG="systemd", ENV{SYSTEMD_WANTS}+="hdapsd@%k.service" 5 | -------------------------------------------------------------------------------- /misc/hdapsd.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=hdapsd hard drive active protection system daemon 3 | Documentation=man:hdapsd(8) 4 | 5 | [Service] 6 | SyslogIdentifier=%p 7 | Nice=-5 8 | ExecStart=@sbindir@/hdapsd --syslog 9 | Restart=on-abort 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | -------------------------------------------------------------------------------- /misc/hdapsd@.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=hdapsd hard drive active protection system daemon - %I 3 | Documentation=man:hdapsd(8) 4 | 5 | [Service] 6 | SyslogIdentifier=%p(%I) 7 | Nice=-5 8 | ExecStart=@sbindir@/hdapsd --syslog -d %I 9 | Restart=on-abort 10 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CPPFLAGS = -DSYSCONFDIR='"$(sysconfdir)"' 2 | 3 | sbin_PROGRAMS=hdapsd 4 | hdapsd_SOURCES=hdapsd.c hdapsd.h input-helper.c input-helper.h 5 | hdapsd_CFLAGS=$(LIBCONFIG_CFLAGS) 6 | hdapsd_LDADD=$(LIBCONFIG_LIBS) 7 | -------------------------------------------------------------------------------- /src/hdapsd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * hdapsd.c - Read from the HDAPS (Hard Drive Active Protection System) 3 | * and protect the drive if motion over threshold... 4 | * 5 | * Derived from pivot.c by Robert Love. 6 | * 7 | * Copyright (C) 2005-2014 Jon Escombe 8 | * Robert Love 9 | * Shem Multinymous 10 | * Elias Oltmanns 11 | * Evgeni Golov 12 | * Brice Arnould 13 | * 14 | * "Why does that kid keep dropping his laptop?" 15 | * 16 | * This program is free software; you can redistribute it and/or modify 17 | * it under the terms of the GNU General Public License as published by 18 | * the Free Software Foundation; either version 2 of the License, or 19 | * (at your option) any later version. 20 | * 21 | * This program is distributed in the hope that it will be useful, 22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 | * GNU General Public License for more details. 25 | * 26 | * You should have received a copy of the GNU General Public License 27 | * along with this program; if not, write to the Free Software 28 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 29 | */ 30 | 31 | #include "config.h" 32 | #include "hdapsd.h" 33 | #include "input-helper.h" 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | 53 | #ifdef HAVE_LIBCONFIG 54 | # include 55 | 56 | # ifndef config_error_file 57 | # define config_error_file(x) cfg_file 58 | # endif 59 | #endif 60 | 61 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4,16,0) 62 | # define input_event_sec time.tv_sec 63 | # define input_event_usec time.tv_usec 64 | #endif 65 | 66 | static volatile int pause_now = 0; 67 | static volatile int running = 1; 68 | static int verbose = 0; 69 | static int dry_run = 0; 70 | static int poll_sysfs = 0; 71 | static int hardware_logic = 0; 72 | static int force_software_logic = 0; 73 | static int sampling_rate = 0; 74 | static int background = 0; 75 | static int dosyslog = 0; 76 | static int forcerotational = 0; 77 | static int use_leds = 1; 78 | 79 | char pid_file[FILENAME_MAX] = ""; 80 | int hdaps_input_fd = 0; 81 | int hdaps_input_nr = -1; 82 | int freefall_fd = -1; 83 | 84 | struct list *disklist = NULL; 85 | enum kernel kernel_interface = UNLOAD_HEADS; 86 | enum interfaces position_interface = INTERFACE_NONE; 87 | 88 | /* 89 | * printlog (stream, fmt) - print the formatted message to syslog 90 | * or to the defined stream 91 | */ 92 | void printlog (FILE *stream, const char *fmt, ...) 93 | { 94 | time_t now; 95 | int len = sizeof(fmt); 96 | 97 | char msg[len+1024]; 98 | va_list ap; 99 | va_start(ap, fmt); 100 | vsnprintf(msg, len+1024, fmt, ap); 101 | va_end(ap); 102 | 103 | if (dosyslog) 104 | syslog(LOG_INFO, "%s", msg); 105 | else { 106 | now = time((time_t *)NULL); 107 | fprintf(stream, "%.24s: %s\n", ctime(&now), msg); 108 | } 109 | } 110 | 111 | /* 112 | * slurp_file - read the content of a file (up to BUF_LEN-1) into a string. 113 | * 114 | * We open and close the file on every invocation, which is lame but due to 115 | * several features of sysfs files: 116 | * 117 | * (a) Sysfs files are seekable. 118 | * (b) Seeking to zero and then rereading does not seem to work. 119 | * 120 | * If I were king--and I will be one day--I would have made sysfs files 121 | * nonseekable and only able to return full-size reads. 122 | */ 123 | static int slurp_file (const char* filename, char* buf) 124 | { 125 | int ret; 126 | int fd = open (filename, O_RDONLY); 127 | if (fd < 0) { 128 | printlog(stderr, "Could not open %s: %s.\nDo you have the hdaps module loaded?", filename, strerror(errno)); 129 | return fd; 130 | } 131 | 132 | ret = read (fd, buf, BUF_LEN-1); 133 | if (ret < 0) { 134 | printlog(stderr, "Could not read from %s: %s", filename, strerror(errno)); 135 | } else { 136 | buf[ret] = 0; /* null-terminate so we can parse safely */ 137 | ret = 0; 138 | } 139 | 140 | if (close (fd)) 141 | printlog(stderr, "Could not close %s: %s", filename, strerror(errno)); 142 | 143 | return ret; 144 | } 145 | 146 | /* 147 | * read_position_from_hdaps() - read the (x,y) position pair from hdaps via sysfs files 148 | * This method is not recommended for frequent polling, since it causes unnecessary interrupts 149 | * and a phase difference between hdaps-to-EC polling vs. hdapsd-to-hdaps polling. 150 | */ 151 | static int read_position_from_hdaps (int *x, int *y) 152 | { 153 | char buf[BUF_LEN]; 154 | int ret; 155 | if ((ret = slurp_file(HDAPS_POSITION_FILE, buf))) 156 | return ret; 157 | return (sscanf (buf, "(%d,%d)\n", x, y) != 2); 158 | } 159 | 160 | /* 161 | * read_position_from_ams() - read the (x,y,z) position from AMS via sysfs file 162 | */ 163 | static int read_position_from_ams (int *x, int *y, int *z) 164 | { 165 | char buf[BUF_LEN]; 166 | int ret; 167 | if ((ret = slurp_file(AMS_POSITION_FILE, buf))) 168 | return ret; 169 | return (sscanf (buf, "%d %d %d\n", x, y, z) != 3); 170 | } 171 | 172 | /* 173 | * read_position_from_hp3d() - read the (x,y,z) position from HP3D via sysfs file 174 | */ 175 | static int read_position_from_hp3d (int *x, int *y, int *z) 176 | { 177 | char buf[BUF_LEN]; 178 | int ret; 179 | if ((ret = slurp_file(HP3D_POSITION_FILE, buf))) 180 | return ret; 181 | return (sscanf (buf, "(%d,%d,%d)\n", x, y, z) != 3); 182 | } 183 | 184 | /* 185 | * read_position_from_applesmc() - read the (x,y,z) position from APPLESMC 186 | * via sysfs file 187 | */ 188 | static int read_position_from_applesmc (int *x, int *y, int *z) 189 | { 190 | char buf[BUF_LEN]; 191 | int ret; 192 | if ((ret = slurp_file(APPLESMC_POSITION_FILE, buf))) 193 | return ret; 194 | return (sscanf (buf, "(%d,%d,%d)\n", x, y, z) != 3); 195 | } 196 | 197 | /* 198 | * read_position_from_toshiba_acpi() - read the (x,y,z) position from TOSHIBA_ACPI 199 | * via sysfs file 200 | */ 201 | static int read_position_from_toshiba_acpi (int *x, int *y, int *z) 202 | { 203 | char buf[BUF_LEN]; 204 | int ret; 205 | if ((ret = slurp_file(TOSHIBA_POSITION_FILE, buf))) 206 | return ret; 207 | return (sscanf (buf, "%d %d %d\n", x, y, z) != 3); 208 | } 209 | /* 210 | * read_position_from_sysfs() - read the position either from HDAPS or 211 | * from AMS or from HP3D 212 | * depending on the given interface. 213 | */ 214 | static int read_position_from_sysfs (int *x, int *y, int *z) 215 | { 216 | if (position_interface == INTERFACE_HDAPS) 217 | return read_position_from_hdaps(x,y); 218 | else if (position_interface == INTERFACE_AMS) 219 | return read_position_from_ams(x,y,z); 220 | else if (position_interface == INTERFACE_HP3D) 221 | return read_position_from_hp3d(x,y,z); 222 | else if (position_interface == INTERFACE_APPLESMC) 223 | return read_position_from_applesmc(x,y,z); 224 | else if (position_interface == INTERFACE_TOSHIBA_ACPI) 225 | return read_position_from_toshiba_acpi(x,y,z); 226 | return -1; 227 | } 228 | 229 | 230 | /* 231 | * read_int() - read an integer from a file 232 | */ 233 | static int read_int (const char* filename) 234 | { 235 | char buf[BUF_LEN]; 236 | int ret; 237 | if ((ret = slurp_file(filename, buf))) 238 | return ret; 239 | if (sscanf (buf, "%d\n", &ret) != 1) 240 | return -EIO; 241 | return ret; 242 | } 243 | 244 | /* 245 | * write_int() - writes an integer to a file 246 | */ 247 | static int write_int (const char* filename, const int value) 248 | { 249 | char buf[BUF_LEN]; 250 | int fd; 251 | int size; 252 | fd = open (filename, O_WRONLY); 253 | if (fd < 0) 254 | return -1; 255 | size = snprintf (buf, BUF_LEN, "%i\n", value); 256 | if (write (fd, buf, size) < 0) 257 | return -1; 258 | return close (fd); 259 | } 260 | 261 | /* 262 | * get_km_activity() - returns 1 if there is keyboard or mouse activity 263 | */ 264 | static int get_km_activity () 265 | { 266 | if (position_interface != INTERFACE_HDAPS) 267 | return 0; 268 | if (read_int(MOUSE_ACTIVITY_FILE) == 1) 269 | return 1; 270 | if (read_int(KEYBD_ACTIVITY_FILE) == 1) 271 | return 1; 272 | return 0; 273 | } 274 | 275 | /* 276 | * read_position_from_inputdev() - read the (x,y,z) position pair and time from hdaps 277 | * via the hdaps input device. Blocks there is a change in position. 278 | * The x and y arguments should contain the last read values, since if one of them 279 | * doesn't change it will not be assigned. 280 | */ 281 | static int read_position_from_inputdev (int *x, int *y, int *z, double *utime) 282 | { 283 | struct input_event ev; 284 | int len, done = 0; 285 | *utime = 0; 286 | while (1) { 287 | len = read(hdaps_input_fd, &ev, sizeof(struct input_event)); 288 | if (len < 0) { 289 | printlog(stderr, "ERROR: failed reading from input device: /dev/input/event%d (%s).", hdaps_input_nr, strerror(errno)); 290 | return len; 291 | } 292 | if (len < (int)sizeof(struct input_event)) { 293 | printlog(stderr, "ERROR: short read from input device: /dev/input/event%d (%d bytes).", hdaps_input_nr, len); 294 | return -EIO; 295 | } 296 | switch (ev.type) { 297 | case EV_ABS: /* new X, Y or Z */ 298 | switch (ev.code) { 299 | case ABS_X: 300 | *x = ev.value; 301 | break; 302 | case ABS_Y: 303 | *y = ev.value; 304 | break; 305 | case ABS_Z: 306 | *z = ev.value; 307 | break; 308 | default: 309 | continue; 310 | } 311 | break; 312 | case EV_SYN: /* X and Y now reflect latest measurement */ 313 | done = 1; 314 | break; 315 | default: 316 | continue; 317 | } 318 | if (!*utime) /* first event's time is closest to reality */ 319 | *utime = ev.input_event_sec + ev.input_event_usec/1000000.0; 320 | if (done) 321 | return 0; 322 | } 323 | } 324 | 325 | 326 | /* 327 | * write_protect() - park/unpark 328 | */ 329 | static int write_protect (const char *path, int val) 330 | { 331 | int fd, ret; 332 | char buf[BUF_LEN]; 333 | 334 | if (dry_run) 335 | return 0; 336 | 337 | snprintf(buf, sizeof(buf), "%d", val); 338 | 339 | fd = open (path, O_WRONLY); 340 | if (fd < 0) { 341 | printlog (stderr, "Could not open %s", path); 342 | return fd; 343 | } 344 | 345 | ret = write (fd, buf, strlen(buf)); 346 | 347 | if (ret < 0) { 348 | printlog (stderr, "Could not write to %s.\nDoes your kernel/drive support IDLE_IMMEDIATE with UNLOAD?", path); 349 | goto out; 350 | } 351 | ret = 0; 352 | 353 | out: 354 | if (close (fd)) 355 | printlog (stderr, "Could not close %s", path); 356 | 357 | return ret; 358 | } 359 | 360 | double get_utime (void) 361 | { 362 | struct timeval tv; 363 | int ret = gettimeofday(&tv, NULL); 364 | if (ret) { 365 | perror("gettimeofday"); 366 | exit(1); 367 | } 368 | return tv.tv_sec + tv.tv_usec/1000000.0; 369 | } 370 | 371 | /* 372 | * SIGUSR1_handler - Handler for SIGUSR1, sleeps for a few seconds. Useful when suspending laptop. 373 | */ 374 | void SIGUSR1_handler (int sig) 375 | { 376 | pause_now = 1; 377 | if (verbose) 378 | printlog(stdout, "SIGUSR1 called, pause_now is %d", pause_now); 379 | } 380 | 381 | /* 382 | * SIGTERM_handler - Handler for SIGTERM, deletes the pidfile and exits. 383 | */ 384 | void SIGTERM_handler (int sig) 385 | { 386 | running = 0; 387 | if (verbose) 388 | printlog(stdout, "SIGTERM called, running is %d", running); 389 | } 390 | 391 | /* 392 | * version() - display version information and exit 393 | */ 394 | void version () 395 | { 396 | printf(PACKAGE_STRING"\n"); 397 | exit(0); 398 | } 399 | 400 | /* 401 | * usage() - display usage instructions and exit 402 | */ 403 | void usage () 404 | { 405 | printf("Usage: "PACKAGE_NAME" [OPTIONS]\n"); 406 | printf("\n"); 407 | #ifdef HAVE_LIBCONFIG 408 | printf(" -c --cfgfile= Load configuration from .\n"); 409 | printf(" By default, configuration is read from "CONFIG_FILE"\n"); 410 | #endif 411 | printf(" -d --device= is likely to be hda or sda.\n"); 412 | printf(" Can be given multiple times\n"); 413 | printf(" to protect multiple devices.\n"); 414 | printf(" -f --force Force unloading heads, even if kernel thinks\n"); 415 | printf(" differently (on pre ATA7 drives).\n"); 416 | printf(" This only works when adding devices by hand (-d).\n"); 417 | printf(" -r --force-rotational Autodetect drives as rotational, even if\n"); 418 | printf(" kernel thinks they are not.\n"); 419 | printf(" -s --sensitivity= How sensitive "PACKAGE_NAME" should be to movements.\n"); 420 | printf(" Defaults to 15, higher value means less\n"); 421 | printf(" sensitive.\n"); 422 | printf(" -a --adaptive Adaptive threshold (automatic increase\n"); 423 | printf(" when the built-in keyboard/mouse are used).\n"); 424 | printf(" -v --verbose Get verbose statistics.\n"); 425 | printf(" -b --background Run the process in the background.\n"); 426 | printf(" -p --pidfile[=] Create a pid file when running\n"); 427 | printf(" in background.\n"); 428 | printf(" If is not specified,\n"); 429 | printf(" it's set to %s.\n", PID_FILE); 430 | printf(" -t --dry-run Don't actually park the drive.\n"); 431 | printf(" -y --poll-sysfs Force use of sysfs interface to\n"); 432 | printf(" accelerometer.\n"); 433 | printf(" -H --hardware-logic Use the hardware fall detection logic instead of\n"); 434 | printf(" the software one (-s/--sensitivity and -a/--adaptive\n"); 435 | printf(" have no effect in this mode).\n"); 436 | printf(" -S --software-logic Use the software fall detection logic even if the\n"); 437 | printf(" hardware one is available.\n"); 438 | printf(" -L --no-leds Don't blink the LEDs.\n"); 439 | printf(" -l --syslog Log to syslog instead of stdout/stderr.\n"); 440 | printf("\n"); 441 | printf(" -V --version Display version information and exit.\n"); 442 | printf(" -h --help Display this message and exit.\n"); 443 | printf("\n"); 444 | printf("You can send SIGUSR1 to deactivate "PACKAGE_NAME" for %d seconds.\n", 445 | SIGUSR1_SLEEP_SEC); 446 | printf("\n"); 447 | printf("Send bugs, comments and suggestions to "PACKAGE_BUGREPORT"\n"); 448 | exit(1); 449 | } 450 | 451 | /* 452 | * check_thresh() - compare a value to the threshold 453 | */ 454 | void check_thresh (double val_sqr, double thresh, int* above, int* near, 455 | char* reason_out, char reason_mark) 456 | { 457 | if (val_sqr > thresh*thresh*NEAR_THRESH_FACTOR*NEAR_THRESH_FACTOR) { 458 | *near = 1; 459 | *reason_out = tolower(reason_mark); 460 | } 461 | if (val_sqr > thresh*thresh) { 462 | *above = 1; 463 | *reason_out = toupper(reason_mark); 464 | } 465 | } 466 | 467 | /* 468 | * analyze() - make a decision on whether to park given present readouts 469 | * (remembers some past data in local static variables). 470 | * Computes and checks 3 values: 471 | * velocity: current position - prev position / time delta 472 | * acceleration: current velocity - prev velocity / time delta 473 | * average velocity: exponentially decaying average of velocity, 474 | * weighed by time delta. 475 | * The velocity and acceleration tests respond quickly to short sharp shocks, 476 | * while the average velocity test catches long, smooth movements (and 477 | * averages out measurement noise). 478 | * The adaptive threshold, if enabled, increases when (built-in) keyboard or 479 | * mouse activity happens shortly after some value was above or near the 480 | * adaptive threshold. The adaptive threshold slowly decreases back to the 481 | * base threshold when no value approaches it. 482 | */ 483 | int analyze (int x, int y, double unow, double base_threshold, 484 | int adaptive, int parked) 485 | { 486 | static int x_last = 0, y_last = 0; 487 | static double unow_last = 0, x_veloc_last = 0, y_veloc_last = 0; 488 | static double x_avg_veloc = 0, y_avg_veloc = 0; 489 | static int history = 0; /* how many recent valid samples? */ 490 | static double adaptive_threshold = -1; /* current adaptive thresh */ 491 | static int last_thresh_change = 0; /* last adaptive thresh change */ 492 | static int last_near_thresh = 0; /* last time we were near thresh */ 493 | static int last_km_activity; /* last time kbd/mouse activity seen */ 494 | 495 | double udelta, x_delta, y_delta, x_veloc, y_veloc, x_accel, y_accel; 496 | double veloc_sqr, accel_sqr, avg_veloc_sqr; 497 | double exp_weight; 498 | double threshold; /* transient threshold for this iteration */ 499 | char reason[4]; /* "which threshold reached?" string for verbose */ 500 | int recently_near_thresh; 501 | int above = 0, near = 0; /* above threshold, near threshold */ 502 | 503 | /* Adaptive threshold adjustment */ 504 | if (adaptive_threshold<0) /* first invocation */ 505 | adaptive_threshold = base_threshold; 506 | recently_near_thresh = unow < last_near_thresh + RECENT_PARK_SEC; 507 | if (adaptive && recently_near_thresh && get_km_activity()) 508 | last_km_activity = unow; 509 | if (adaptive && unow > last_thresh_change + THRESH_ADAPT_SEC) { 510 | if (recently_near_thresh) { 511 | if (last_km_activity > last_near_thresh && 512 | last_km_activity > last_thresh_change) { 513 | /* Near threshold and k/m activity */ 514 | adaptive_threshold *= THRESH_INCREASE_FACTOR; 515 | last_thresh_change = unow; 516 | } 517 | } else { 518 | /* Recently never near threshold */ 519 | adaptive_threshold *= THRESH_DECREASE_FACTOR; 520 | if (adaptive_threshold < base_threshold) 521 | adaptive_threshold = base_threshold; 522 | last_thresh_change = unow; 523 | } 524 | } 525 | 526 | /* compute deltas */ 527 | udelta = unow - unow_last; 528 | x_delta = x - x_last; 529 | y_delta = y - y_last; 530 | 531 | /* compute velocity */ 532 | x_veloc = x_delta/udelta; 533 | y_veloc = y_delta/udelta; 534 | veloc_sqr = x_veloc*x_veloc + y_veloc*y_veloc; 535 | 536 | /* compute acceleration */ 537 | x_accel = (x_veloc - x_veloc_last)/udelta; 538 | y_accel = (y_veloc - y_veloc_last)/udelta; 539 | accel_sqr = x_accel*x_accel + y_accel*y_accel; 540 | 541 | /* compute exponentially-decaying velocity average */ 542 | exp_weight = udelta/AVG_DEPTH_SEC; /* weight of this sample */ 543 | exp_weight = 1 - 1.0/(1+exp_weight); /* softly clamped to 1 */ 544 | x_avg_veloc = exp_weight*x_veloc + (1-exp_weight)*x_avg_veloc; 545 | y_avg_veloc = exp_weight*y_veloc + (1-exp_weight)*y_avg_veloc; 546 | avg_veloc_sqr = x_avg_veloc*x_avg_veloc + y_avg_veloc*y_avg_veloc; 547 | 548 | threshold = adaptive_threshold; 549 | if (parked) /* when parked, be reluctant to unpark */ 550 | threshold *= PARKED_THRESH_FACTOR; 551 | 552 | /* Threshold test (uses Pythagoras's theorem) */ 553 | strncpy(reason, " ", 4); 554 | 555 | check_thresh(veloc_sqr, threshold*VELOC_ADJUST, 556 | &above, &near, reason+0, 'V'); 557 | check_thresh(accel_sqr, threshold*ACCEL_ADJUST, 558 | &above, &near, reason+1, 'A'); 559 | check_thresh(avg_veloc_sqr, threshold*AVG_VELOC_ADJUST, 560 | &above, &near, reason+2, 'X'); 561 | 562 | if (verbose) { 563 | printf("dt=%5.3f " 564 | "dpos=(%3g,%3g) " 565 | "vel=(%6.1f,%6.1f)*%g " 566 | "acc=(%6.1f,%6.1f)*%g " 567 | "avg_vel=(%6.1f,%6.1f)*%g " 568 | "thr=%.1f " 569 | "%s\n", 570 | udelta, 571 | x_delta, y_delta, 572 | x_veloc/VELOC_ADJUST, 573 | y_veloc/VELOC_ADJUST, 574 | VELOC_ADJUST*1.0, 575 | x_accel/ACCEL_ADJUST, 576 | y_accel/ACCEL_ADJUST, 577 | ACCEL_ADJUST*1.0, 578 | x_avg_veloc/AVG_VELOC_ADJUST, 579 | y_avg_veloc/AVG_VELOC_ADJUST, 580 | AVG_VELOC_ADJUST*1.0, 581 | threshold, 582 | reason); 583 | } 584 | 585 | if (udelta>1.0) { /* Too much time since last (resume from suspend?) */ 586 | history = 0; 587 | x_avg_veloc = y_avg_veloc = 0; 588 | } 589 | 590 | if (history<2) { /* Not enough data for meaningful result */ 591 | above = 0; 592 | near = 0; 593 | ++history; 594 | } 595 | 596 | if (near) 597 | last_near_thresh = unow; 598 | 599 | x_last = x; 600 | y_last = y; 601 | x_veloc_last = x_veloc; 602 | y_veloc_last = y_veloc; 603 | unow_last = unow; 604 | 605 | return above; 606 | } 607 | 608 | /* 609 | * add_disk (disk) - add the given disk to the global disklist 610 | */ 611 | void add_disk (char* disk) 612 | { 613 | char protect_file[FILENAME_MAX] = ""; 614 | if (kernel_interface == UNLOAD_HEADS) 615 | snprintf(protect_file, sizeof(protect_file), UNLOAD_HEADS_FMT, disk); 616 | else { 617 | snprintf(protect_file, sizeof(protect_file), QUEUE_PROTECT_FMT, disk); 618 | } 619 | 620 | if (disklist == NULL) { 621 | disklist = (struct list *)malloc(sizeof(struct list)); 622 | if (disklist == NULL) { 623 | printlog(stderr, "Error allocating memory."); 624 | exit(EXIT_FAILURE); 625 | } 626 | else { 627 | strncpy(disklist->name, disk, sizeof(disklist->name)); 628 | strncpy(disklist->protect_file, protect_file, sizeof(disklist->protect_file)); 629 | disklist->next = NULL; 630 | } 631 | } 632 | else { 633 | struct list *p = disklist; 634 | while (p->next != NULL) 635 | p = p->next; 636 | p->next = (struct list *)malloc(sizeof(struct list)); 637 | if (p->next == NULL) { 638 | printlog(stderr, "Error allocating memory."); 639 | exit(EXIT_FAILURE); 640 | } 641 | else { 642 | strncpy(p->next->name, disk, sizeof(p->next->name)); 643 | strncpy(p->next->protect_file, protect_file, sizeof(p->next->protect_file)); 644 | p->next->next = NULL; 645 | } 646 | } 647 | } 648 | 649 | /* 650 | * free_disk (disk) - free the allocated memory 651 | */ 652 | void free_disk (struct list *disk) 653 | { 654 | if (disk != NULL) { 655 | if (disk->next != NULL) 656 | free_disk(disk->next); 657 | free(disk); 658 | } 659 | } 660 | 661 | /* 662 | * select_interface() - search for an interface we can read our position from 663 | */ 664 | int select_interface (int modprobe) 665 | { 666 | int fd; 667 | 668 | char *modules[] = {"hdaps_ec", "hdaps", "ams", "hp_accel", "applesmc", "smo8800", "toshiba_haps", "toshiba_acpi", "acer_wmi"}; 669 | int mod_index; 670 | int input_index; 671 | char command[64]; 672 | position_interface = INTERFACE_NONE; 673 | 674 | if (modprobe) { 675 | if (verbose) 676 | printlog(stderr, "Trying to load the correct kernel module"); 677 | for (mod_index = 0; mod_index < sizeof(modules)/sizeof(modules[0]); mod_index++) { 678 | if (verbose) 679 | printlog(stderr, "Loading %s", modules[mod_index]); 680 | snprintf(command, sizeof(command), "modprobe %s 1>/dev/null 2>/dev/null", modules[mod_index]); 681 | system(command); 682 | } 683 | } 684 | 685 | /* We don't know yet which interface to use, try HDAPS */ 686 | if (verbose) 687 | printlog(stderr, "Trying INTERFACE_HDAPS"); 688 | fd = open (HDAPS_POSITION_FILE, O_RDONLY); 689 | if (fd >= 0) { /* yes, we are hdaps */ 690 | close(fd); 691 | position_interface = INTERFACE_HDAPS; 692 | } 693 | if (position_interface == INTERFACE_NONE) { 694 | /* We still don't know which interface to use, try AMS */ 695 | if (verbose) 696 | printlog(stderr, "Trying INTERFACE_AMS"); 697 | fd = open(AMS_POSITION_FILE, O_RDONLY); 698 | if (fd >= 0) { /* yes, we are ams */ 699 | close(fd); 700 | position_interface = INTERFACE_AMS; 701 | } 702 | } 703 | if (position_interface == INTERFACE_NONE && !force_software_logic) { 704 | /* We still don't know which interface to use, try FREEFALL */ 705 | if (verbose) 706 | printlog(stderr, "Trying INTERFACE_FREEFALL"); 707 | fd = open(FREEFALL_FILE, FREEFALL_FD_FLAGS); 708 | if (fd >= 0) { /* yes, we are freefall */ 709 | close(fd); 710 | position_interface = INTERFACE_FREEFALL; 711 | hardware_logic = 1; 712 | } 713 | } 714 | if (position_interface == INTERFACE_NONE) { 715 | /* We still don't know which interface to use, try HP3D */ 716 | if (verbose) 717 | printlog(stderr, "Trying INTERFACE_HP3D"); 718 | fd = open(HP3D_POSITION_FILE, O_RDONLY); 719 | if (fd >= 0) { /* yes, we are hp3d */ 720 | close(fd); 721 | position_interface = INTERFACE_HP3D; 722 | } 723 | } 724 | if (position_interface == INTERFACE_NONE) { 725 | /* We still don't know which interface to use, try APPLESMC */ 726 | if (verbose) 727 | printlog(stderr, "Trying INTERFACE_APPLESMC"); 728 | fd = open(APPLESMC_POSITION_FILE, O_RDONLY); 729 | if (fd >= 0) { /* yes, we are applesmc */ 730 | close(fd); 731 | position_interface = INTERFACE_APPLESMC; 732 | } 733 | } 734 | if (position_interface == INTERFACE_NONE) { 735 | /* We still don't know which interface to use, try TOSHIBA_HAPS */ 736 | if (verbose) 737 | printlog(stderr, "Trying INTERFACE_TOSHIBA_HAPS"); 738 | fd = open(TOSHIBA_MOVEMENT_FILE, O_RDONLY); 739 | if (fd >= 0) { /* yes, we are TOSHIBA_HAPS */ 740 | close(fd); 741 | position_interface = INTERFACE_TOSHIBA_HAPS; 742 | hardware_logic = 1; 743 | } 744 | } 745 | if (position_interface == INTERFACE_NONE) { 746 | /* We still don't know which interface to use, try TOSHIBA_ACPI */ 747 | if (verbose) 748 | printlog(stderr, "Trying INTERFACE_TOSHIBA_ACPI"); 749 | fd = open(TOSHIBA_POSITION_FILE, O_RDONLY); 750 | if (fd >= 0) { /* yes, we are TOSHIBA_ACPI */ 751 | close(fd); 752 | position_interface = INTERFACE_TOSHIBA_ACPI; 753 | } 754 | } 755 | if (position_interface == INTERFACE_NONE) { 756 | /* We still don't know which interface to use, try INPUT */ 757 | if (verbose) 758 | printlog(stderr, "Trying INTERFACE_INPUT"); 759 | for (input_index = 0; input_index < sizeof(input_accel_names)/sizeof(input_accel_names[0]); input_index++) { 760 | if (verbose) 761 | printlog(stderr, "Trying input for %s", input_accel_names[input_index]); 762 | hdaps_input_nr = device_find_byname(input_accel_names[input_index]); 763 | if (verbose) 764 | printlog(stderr, "Got hdaps_input_nr=%i", hdaps_input_nr); 765 | hdaps_input_fd = device_open(hdaps_input_nr); 766 | if (hdaps_input_fd != -1) { 767 | printlog(stdout, "Selected accelerometer input device /dev/input/event%d", hdaps_input_nr); 768 | poll_sysfs = 0; 769 | position_interface = INTERFACE_INPUT; 770 | break; 771 | } 772 | } 773 | } 774 | return position_interface; 775 | } 776 | 777 | /* 778 | * autodetect_devices() 779 | */ 780 | int autodetect_devices () 781 | { 782 | int num_devices = 0; 783 | DIR *dp; 784 | struct dirent *ep; 785 | dp = opendir(SYSFS_BLOCK); 786 | if (dp != NULL) { 787 | while ((ep = readdir(dp))) { 788 | char path[FILENAME_MAX]; 789 | char removable[FILENAME_MAX]; 790 | char rotational[FILENAME_MAX]; 791 | snprintf(removable, sizeof(removable), REMOVABLE_FMT, ep->d_name); 792 | snprintf(rotational, sizeof(rotational), ROTATIONAL_FMT, ep->d_name); 793 | 794 | if (kernel_interface == UNLOAD_HEADS) 795 | snprintf(path, sizeof(path), UNLOAD_HEADS_FMT, ep->d_name); 796 | else 797 | snprintf(path, sizeof(path), QUEUE_PROTECT_FMT, ep->d_name); 798 | 799 | if (access(path, F_OK) == 0 && read_int(removable) == 0 && read_int(path) >= 0) { 800 | if (read_int(rotational) == 1 || forcerotational) { 801 | printlog(stdout, "Adding autodetected device: %s", ep->d_name); 802 | add_disk(ep->d_name); 803 | num_devices++; 804 | } 805 | else { 806 | printlog(stdout, "Not adding autodetected device \"%s\", it seems not to be a rotational drive.", ep->d_name); 807 | } 808 | } 809 | } 810 | (void)closedir(dp); 811 | } 812 | return num_devices; 813 | } 814 | 815 | /* 816 | * main() - loop forever, reading the hdaps values and 817 | * parking/unparking as necessary 818 | */ 819 | int main (int argc, char** argv) 820 | { 821 | struct utsname sysinfo; 822 | struct list *p = NULL; 823 | int c, park_now, protect_factor; 824 | int x = 0, y = 0, z = 0; 825 | int fd, i, ret, threshold = 15, adaptive = 0, 826 | pidfile = 0, parked = 0, forceadd = 0; 827 | double unow = 0, parked_utime = 0; 828 | #ifdef HAVE_LIBCONFIG 829 | config_t cfg; 830 | config_setting_t *setting; 831 | char cfg_file[FILENAME_MAX] = CONFIG_FILE; 832 | int cfgfile = 0; 833 | const char *tmpcstr; 834 | #endif 835 | 836 | struct option longopts[] = 837 | { 838 | {"device", required_argument, NULL, 'd'}, 839 | {"sensitivity", required_argument, NULL, 's'}, 840 | {"adaptive", no_argument, NULL, 'a'}, 841 | {"verbose", no_argument, NULL, 'v'}, 842 | {"background", no_argument, NULL, 'b'}, 843 | #ifdef HAVE_LIBCONFIG 844 | {"cfgfile", required_argument, NULL, 'c'}, 845 | #endif 846 | {"pidfile", optional_argument, NULL, 'p'}, 847 | {"dry-run", no_argument, NULL, 't'}, 848 | {"poll-sysfs", no_argument, NULL, 'y'}, 849 | {"hardware-logic", no_argument, NULL, 'H'}, 850 | {"software-logic", no_argument, NULL, 'S'}, 851 | {"version", no_argument, NULL, 'V'}, 852 | {"help", no_argument, NULL, 'h'}, 853 | {"no-leds", no_argument, NULL, 'L'}, 854 | {"syslog", no_argument, NULL, 'l'}, 855 | {"force", no_argument, NULL, 'f'}, 856 | {"force-rotational", no_argument, NULL, 'r'}, 857 | {NULL, 0, NULL, 0} 858 | }; 859 | 860 | if (uname(&sysinfo) < 0 || strcmp("2.6.27", sysinfo.release) <= 0) { 861 | protect_factor = 1000; 862 | kernel_interface = UNLOAD_HEADS; 863 | } 864 | else { 865 | protect_factor = 1; 866 | kernel_interface = PROTECT; 867 | } 868 | 869 | openlog(PACKAGE_NAME, LOG_PID, LOG_DAEMON); 870 | 871 | #ifdef HAVE_LIBCONFIG 872 | while ((c = getopt_long(argc, argv, "d:s:vbac:p::tyHSVhLlfr", longopts, NULL)) != -1) { 873 | #else 874 | while ((c = getopt_long(argc, argv, "d:s:vbap::tyHSVhLlfr", longopts, NULL)) != -1) { 875 | #endif 876 | switch (c) { 877 | case 'd': 878 | add_disk(optarg); 879 | break; 880 | case 's': 881 | threshold = atoi(optarg); 882 | break; 883 | case 'b': 884 | background = 1; 885 | break; 886 | case 'a': 887 | adaptive = 1; 888 | break; 889 | case 'v': 890 | verbose = 1; 891 | break; 892 | #ifdef HAVE_LIBCONFIG 893 | case 'c': 894 | cfgfile = 1; 895 | snprintf(cfg_file, sizeof(cfg_file), "%s", optarg); 896 | break; 897 | #endif 898 | case 'p': 899 | pidfile = 1; 900 | if (optarg == NULL) { 901 | snprintf(pid_file, sizeof(pid_file), "%s", PID_FILE); 902 | } else { 903 | snprintf(pid_file, sizeof(pid_file), "%s", optarg); 904 | } 905 | break; 906 | case 't': 907 | printlog(stdout, "Dry run, will not actually park heads or freeze queue."); 908 | dry_run = 1; 909 | break; 910 | case 'y': 911 | poll_sysfs = 1; 912 | break; 913 | case 'H': 914 | hardware_logic = 1; 915 | break; 916 | case 'S': 917 | force_software_logic = 1; 918 | break; 919 | case 'V': 920 | version(); 921 | break; 922 | case 'l': 923 | dosyslog = 1; 924 | break; 925 | case 'L': 926 | use_leds = 0; 927 | break; 928 | case 'f': 929 | forceadd = 1; 930 | break; 931 | case 'r': 932 | forcerotational = 1; 933 | break; 934 | case 'h': 935 | default: 936 | usage(); 937 | break; 938 | } 939 | } 940 | 941 | printlog(stdout, "Starting "PACKAGE_NAME); 942 | 943 | #ifdef HAVE_LIBCONFIG 944 | config_init(&cfg); 945 | if (access(cfg_file, F_OK) == 0) { 946 | if (!config_read_file(&cfg, cfg_file)) { 947 | printlog(stderr, "%s:%d - %s", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg)); 948 | config_destroy(&cfg); 949 | free_disk(disklist); 950 | return 1; 951 | } 952 | 953 | if (disklist == NULL) { 954 | setting = config_lookup(&cfg, "device"); 955 | if (setting != NULL) { 956 | if (config_setting_is_array(setting)) { 957 | for (i = 0; iname); 1001 | if (kernel_interface == UNLOAD_HEADS) 1002 | fd = open (p->protect_file, O_RDWR); 1003 | else 1004 | fd = open (protect_method, O_RDWR); 1005 | if (fd > 0) { 1006 | if (kernel_interface == UNLOAD_HEADS) 1007 | ret = write(fd, FORCE_UNLOAD_HEADS, strlen(FORCE_UNLOAD_HEADS)); 1008 | else 1009 | ret = write(fd, FORCE_PROTECT_METHOD, strlen(FORCE_PROTECT_METHOD)); 1010 | if (ret == -1) 1011 | printlog(stderr, "Could not forcely enable UNLOAD feature for %s", p->name); 1012 | else 1013 | printlog(stdout, "Forcely enabled UNLOAD for %s", p->name); 1014 | close(fd); 1015 | } 1016 | else 1017 | printlog(stderr, "Could not open %s for forcely enabling UNLOAD feature", p->protect_file); 1018 | 1019 | p = p->next; 1020 | } 1021 | } 1022 | 1023 | if (disklist == NULL) { 1024 | printlog(stdout, "WARNING: You did not supply any devices to protect, trying autodetection."); 1025 | if (autodetect_devices() < 1) 1026 | printlog(stderr, "Could not detect any devices."); 1027 | } 1028 | 1029 | if (disklist == NULL) 1030 | usage(); 1031 | 1032 | /* Let's see if we're on a ThinkPad or on an *Book */ 1033 | if (!position_interface) 1034 | select_interface(0); 1035 | if (!position_interface) 1036 | select_interface(1); 1037 | 1038 | if (!position_interface && !hardware_logic) { 1039 | printlog(stdout, "Could not find a suitable interface"); 1040 | return -1; 1041 | } 1042 | else 1043 | printlog(stdout, "Selected interface: %s", interface_names[position_interface]); 1044 | if (hardware_logic) { 1045 | if (position_interface == INTERFACE_FREEFALL) { 1046 | /* Open the file representing the hardware decision */ 1047 | freefall_fd = open (FREEFALL_FILE, FREEFALL_FD_FLAGS); 1048 | if (freefall_fd < 0) { 1049 | printlog(stdout, 1050 | "ERROR: Failed opening the hardware logic file (%s). " 1051 | "It is probably not supported on your system.", 1052 | strerror(errno)); 1053 | return errno; 1054 | } 1055 | else { 1056 | printlog (stdout, "Uses hardware logic from " FREEFALL_FILE); 1057 | } 1058 | } else if (position_interface == INTERFACE_TOSHIBA_HAPS) { 1059 | printlog (stdout, "Uses hardware logic from " TOSHIBA_MOVEMENT_FILE); 1060 | } else { 1061 | printlog(stderr, "You requested hardware logic, but neither the FREEFALL nor the TOSHIBA_HAPS interface was found."); 1062 | return -1; 1063 | } 1064 | } 1065 | if (!poll_sysfs && !hardware_logic) { 1066 | if (position_interface == INTERFACE_HDAPS) { 1067 | hdaps_input_nr = device_find_byphys("hdaps/input1"); 1068 | hdaps_input_fd = device_open(hdaps_input_nr); 1069 | if (hdaps_input_fd < 0) { 1070 | printlog(stdout, 1071 | "WARNING: Could not find hdaps input device (%s). " 1072 | "You may be using an incompatible version of the hdaps module. " 1073 | "Falling back to reading the position from sysfs (uses more power).\n" 1074 | "Use '-y' to silence this warning.", 1075 | strerror(errno)); 1076 | poll_sysfs = 1; 1077 | } 1078 | else { 1079 | printlog(stdout, "Selected HDAPS input device: /dev/input/event%d", hdaps_input_nr); 1080 | } 1081 | } else if (position_interface == INTERFACE_AMS) { 1082 | hdaps_input_nr = device_find_byname("Apple Motion Sensor"); 1083 | hdaps_input_fd = device_open(hdaps_input_nr); 1084 | if (hdaps_input_fd < 0) { 1085 | printlog(stdout, 1086 | "WARNING: Could not find AMS input device, do you need to set joystick=1?"); 1087 | poll_sysfs = 1; 1088 | } 1089 | else { 1090 | printlog(stdout, "Selected AMS input device /dev/input/event%d", hdaps_input_nr); 1091 | } 1092 | } else if (position_interface == INTERFACE_HP3D) { 1093 | hdaps_input_nr = device_find_byname("ST LIS3LV02DL Accelerometer"); 1094 | hdaps_input_fd = device_open(hdaps_input_nr); 1095 | if (hdaps_input_fd < 0) { 1096 | printlog(stdout, 1097 | "WARNING: Could not find HP3D input device"); 1098 | poll_sysfs = 1; 1099 | } 1100 | else { 1101 | printlog(stdout, "Selected HP3D input device /dev/input/event%d", hdaps_input_nr); 1102 | } 1103 | } else if (position_interface == INTERFACE_APPLESMC) { 1104 | hdaps_input_nr = device_find_byname("applesmc"); 1105 | hdaps_input_fd = device_open(hdaps_input_nr); 1106 | if (hdaps_input_fd < 0) { 1107 | printlog(stdout, 1108 | "WARNING: Could not find APPLESMC input device"); 1109 | poll_sysfs = 1; 1110 | } 1111 | else { 1112 | printlog(stdout, "Selected APPLESMC input device /dev/input/event%d", hdaps_input_nr); 1113 | } 1114 | } else if (position_interface == INTERFACE_INPUT) { 1115 | if (poll_sysfs) { 1116 | printlog(stderr, "ERROR: You cannot use the INPUT interface with poll-sysfs"); 1117 | return -1; 1118 | } 1119 | } 1120 | } 1121 | if (position_interface != INTERFACE_HP3D && position_interface != INTERFACE_FREEFALL) { 1122 | /* LEDs are not supported yet on other systems */ 1123 | use_leds = 0; 1124 | } 1125 | if (use_leds) { 1126 | fd = open(HP3D_LED_FILE, O_WRONLY); 1127 | if (fd < 0) 1128 | use_leds = 0; 1129 | else 1130 | close(fd); 1131 | } 1132 | 1133 | if (background) { 1134 | verbose = 0; 1135 | if (pidfile) { 1136 | fd = open (pid_file, O_WRONLY | O_CREAT, 0644); 1137 | if (fd < 0) { 1138 | printlog (stderr, "Could not create pidfile: %s", pid_file); 1139 | return 1; 1140 | } 1141 | } 1142 | if (daemon(0,0) != 0) { 1143 | printlog (stderr, "Failed to daemonize"); 1144 | return 1; 1145 | } 1146 | if (pidfile) { 1147 | char buf[BUF_LEN]; 1148 | snprintf (buf, sizeof(buf), "%d\n", getpid()); 1149 | ret = write (fd, buf, strlen(buf)); 1150 | if (ret < 0) { 1151 | printlog (stderr, "Could not write to pidfile %s", pid_file); 1152 | return 1; 1153 | } 1154 | if (close (fd)) { 1155 | printlog (stderr, "Could not close pidfile %s", pid_file); 1156 | return 1; 1157 | } 1158 | } 1159 | } 1160 | 1161 | mlockall(MCL_FUTURE); 1162 | 1163 | if (verbose) { 1164 | p = disklist; 1165 | while (p != NULL) { 1166 | printf("disk: %s\n", p->name); 1167 | p = p->next; 1168 | } 1169 | printf("threshold: %i\n", threshold); 1170 | printf("read_method: %s\n", poll_sysfs ? "poll-sysfs" : (hardware_logic ? "hardware-logic" : "input-dev")); 1171 | } 1172 | 1173 | /* check the protect attribute exists */ 1174 | /* wait for it if it's not there (in case the attribute hasn't been created yet) */ 1175 | p = disklist; 1176 | while (p != NULL && !dry_run) { 1177 | fd = open (p->protect_file, O_RDWR); 1178 | if (background) 1179 | for (i = 0; fd < 0 && i < 100; ++i) { 1180 | usleep (100000); /* 10 Hz */ 1181 | fd = open (p->protect_file, O_RDWR); 1182 | } 1183 | if (fd < 0) { 1184 | printlog (stderr, "Could not open %s\nDoes your kernel/drive support IDLE_IMMEDIATE with UNLOAD?", p->protect_file); 1185 | free_disk(disklist); 1186 | #ifdef HAVE_LIBCONFIG 1187 | config_destroy(&cfg); 1188 | #endif 1189 | return 1; 1190 | } 1191 | close (fd); 1192 | p = p->next; 1193 | } 1194 | 1195 | /* see if we can read the sensor */ 1196 | /* wait for it if it's not there (in case the attribute hasn't been created yet) */ 1197 | if (!hardware_logic && position_interface != INTERFACE_INPUT) { 1198 | ret = read_position_from_sysfs (&x, &y, &z); 1199 | if (background || (position_interface == INTERFACE_HDAPS && errno == EBUSY)) 1200 | for (i = 0; ret && i < 100; ++i) { 1201 | usleep (100000); /* 10 Hz */ 1202 | ret = read_position_from_sysfs (&x, &y, &z); 1203 | } 1204 | if (ret) { 1205 | printlog(stderr, "Could not read position from sysfs."); 1206 | return 1; 1207 | } 1208 | } 1209 | 1210 | /* adapt to the driver's sampling rate */ 1211 | if (position_interface == INTERFACE_HDAPS && access(HDAPS_SAMPLING_RATE_FILE, F_OK) == 0) 1212 | sampling_rate = read_int(HDAPS_SAMPLING_RATE_FILE); 1213 | else if (position_interface == INTERFACE_HP3D) 1214 | sampling_rate = read_int(HP3D_SAMPLING_RATE_FILE); 1215 | if (sampling_rate <= 0) 1216 | sampling_rate = DEFAULT_SAMPLING_RATE; 1217 | if (verbose) 1218 | printf("sampling_rate: %d\n", sampling_rate); 1219 | 1220 | struct sigaction sa; 1221 | sigemptyset (&sa.sa_mask); 1222 | sa.sa_flags = 0; 1223 | 1224 | /* Register the handler for SIGUSR1. */ 1225 | sa.sa_handler = SIGUSR1_handler; 1226 | sigaction (SIGUSR1, &sa, NULL); 1227 | 1228 | /* Register the handler for SIGTERM. */ 1229 | sa.sa_handler = SIGTERM_handler; 1230 | sigaction (SIGTERM, &sa, NULL); 1231 | 1232 | while (running) { 1233 | if (!hardware_logic) { /* The decision is made by the software */ 1234 | /* Get statistics and decide what to do */ 1235 | if (poll_sysfs) { 1236 | usleep (1000000/sampling_rate); 1237 | ret = read_position_from_sysfs (&x, &y, &z); 1238 | unow = get_utime(); /* microsec */ 1239 | } else { 1240 | double oldunow = unow; 1241 | int oldx = x, oldy = y; 1242 | ret = read_position_from_inputdev (&x, &y, &z, &unow); 1243 | 1244 | /* 1245 | * The input device issues events only when the position changed. 1246 | * The analysis state needs to know how long the position remained 1247 | * unchanged, so send analyze() a fake retroactive update before sending 1248 | * the new one. 1249 | */ 1250 | if (!ret && oldunow && unow-oldunow > 1.5/sampling_rate) 1251 | analyze(oldx, oldy, unow-1.0/sampling_rate, threshold, adaptive, parked); 1252 | 1253 | } 1254 | 1255 | if (ret) { 1256 | if (verbose) 1257 | printf("readout error (%d)\n", ret); 1258 | continue; 1259 | } 1260 | 1261 | park_now = analyze(x, y, unow, threshold, adaptive, parked); 1262 | } 1263 | else /* if (hardware_logic) */ { 1264 | unsigned char count; /* Number of fall events */ 1265 | if (position_interface == INTERFACE_FREEFALL) { 1266 | if (!parked) { 1267 | /* Wait for the hardware to notify a fall */ 1268 | ret = read(freefall_fd, &count, sizeof(count)); 1269 | } 1270 | else { 1271 | /* 1272 | * Poll to check if we no longer are falling 1273 | * (hardware_logic polls only when parked) 1274 | */ 1275 | usleep (1000000/sampling_rate); 1276 | fcntl (freefall_fd, F_SETFL, FREEFALL_FD_FLAGS|O_NONBLOCK); 1277 | ret = read(freefall_fd, &count, sizeof(count)); 1278 | fcntl (freefall_fd, F_SETFL, FREEFALL_FD_FLAGS); 1279 | /* 1280 | * If the error is EAGAIN then it is not a real error but 1281 | * a sign that the fall has ended 1282 | */ 1283 | if (ret != sizeof(count) && errno == EAGAIN) { 1284 | count = 0; /* set fall events count to 0 */ 1285 | ret = sizeof(count); /* Validate count */ 1286 | } 1287 | } 1288 | } else if (position_interface == INTERFACE_TOSHIBA_HAPS) { 1289 | usleep (1000000/sampling_rate); 1290 | ret = slurp_file(TOSHIBA_MOVEMENT_FILE, &count); 1291 | } 1292 | /* handle read errors */ 1293 | if (ret != sizeof(count)) { 1294 | if (verbose) 1295 | printf("readout error (%d)\n", ret); 1296 | continue; 1297 | } 1298 | /* Display the read values in verbose mode */ 1299 | if (verbose) 1300 | printf ("HW=%u\n", (unsigned) count); 1301 | unow = get_utime(); /* microsec */ 1302 | park_now = (count > 0); 1303 | } 1304 | 1305 | if (park_now && !pause_now) { 1306 | if (!parked || unow>parked_utime+REFREEZE_SECONDS) { 1307 | /* Not frozen or freeze about to expire */ 1308 | p = disklist; 1309 | while (p != NULL) { 1310 | write_protect(p->protect_file, 1311 | (FREEZE_SECONDS+FREEZE_EXTRA_SECONDS) * protect_factor); 1312 | p = p->next; 1313 | } 1314 | /* 1315 | * Write protect before any output (xterm, or 1316 | * whatever else is handling our stdout, may be 1317 | * swapped out). 1318 | */ 1319 | if (!parked) { 1320 | printlog(stdout, "parking"); 1321 | if (use_leds) 1322 | write_int (HP3D_LED_FILE, 1); 1323 | } 1324 | parked = 1; 1325 | parked_utime = unow; 1326 | } 1327 | } else { 1328 | if (parked && 1329 | (pause_now || unow>parked_utime+FREEZE_SECONDS)) { 1330 | /* Sanity check */ 1331 | p = disklist; 1332 | while (p != NULL) { 1333 | if (!dry_run && !read_int(p->protect_file)) 1334 | printlog(stderr, "Error! Not parked when we " 1335 | "thought we were... (paged out " 1336 | "and timer expired?)"); 1337 | /* Freeze has expired */ 1338 | write_protect(p->protect_file, 0); /* unprotect */ 1339 | if (use_leds) 1340 | write_int (HP3D_LED_FILE, 0); 1341 | p = p->next; 1342 | } 1343 | parked = 0; 1344 | printlog(stdout, "un-parking"); 1345 | } 1346 | while (pause_now) { 1347 | pause_now = 0; 1348 | printlog(stdout, "pausing for %d seconds", SIGUSR1_SLEEP_SEC); 1349 | sleep(SIGUSR1_SLEEP_SEC); 1350 | } 1351 | } 1352 | 1353 | } 1354 | 1355 | free_disk(disklist); 1356 | #ifdef HAVE_LIBCONFIG 1357 | config_destroy(&cfg); 1358 | #endif 1359 | printlog(stdout, "Terminating "PACKAGE_NAME); 1360 | closelog(); 1361 | if (pidfile) 1362 | unlink(pid_file); 1363 | munlockall(); 1364 | return ret; 1365 | } 1366 | -------------------------------------------------------------------------------- /src/hdapsd.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define PID_FILE "/var/run/hdapsd.pid" 4 | #define CONFIG_FILE SYSCONFDIR"/hdapsd.conf" 5 | #define HDAPS_POSITION_FILE "/sys/devices/platform/hdaps/position" 6 | #define MOUSE_ACTIVITY_FILE "/sys/devices/platform/hdaps/keyboard_activity" 7 | #define KEYBD_ACTIVITY_FILE "/sys/devices/platform/hdaps/mouse_activity" 8 | #define HDAPS_SAMPLING_RATE_FILE "/sys/devices/platform/hdaps/sampling_rate" 9 | #define HP3D_SAMPLING_RATE_FILE "/sys/devices/platform/lis3lv02d/rate" 10 | #define AMS_POSITION_FILE "/sys/devices/ams/current" 11 | #define HP3D_POSITION_FILE "/sys/devices/platform/lis3lv02d/position" 12 | #define HP3D_LED_FILE "/sys/devices/virtual/leds/hp::hddprotect/brightness" 13 | #define FREEFALL_FILE "/dev/freefall" 14 | #define FREEFALL_FD_FLAGS (O_RDONLY) 15 | #define APPLESMC_POSITION_FILE "/sys/devices/platform/applesmc.768/position" 16 | #define TOSHIBA_MOVEMENT_FILE "/sys/devices/platform/toshiba_haps/movement" 17 | #define TOSHIBA_LEVEL_FILE "/sys/devices/platform/toshiba_haps/protection_level" 18 | #define TOSHIBA_POSITION_FILE "/sys/devices/platform/toshiba_acpi/position" 19 | #define SYSFS_BLOCK "/sys/block" 20 | #define REMOVABLE_FMT SYSFS_BLOCK"/%s/removable" 21 | #define ROTATIONAL_FMT SYSFS_BLOCK"/%s/queue/rotational" 22 | #define UNLOAD_HEADS_FMT SYSFS_BLOCK"/%s/device/unload_heads" 23 | #define QUEUE_PROTECT_FMT SYSFS_BLOCK"/%s/queue/protect" 24 | #define QUEUE_METHOD_FMT SYSFS_BLOCK"/%s/queue/protect_method" 25 | #define BUF_LEN 40 26 | 27 | #define FORCE_PROTECT_METHOD "unload" 28 | #define FORCE_UNLOAD_HEADS "-1" 29 | 30 | #define FREEZE_SECONDS 1 /* period to freeze disk */ 31 | #define REFREEZE_SECONDS 0.1 /* period after which to re-freeze disk */ 32 | #define FREEZE_EXTRA_SECONDS 4 /* additional timeout for kernel timer */ 33 | #define DEFAULT_SAMPLING_RATE 50 /* default sampling frequency */ 34 | #define SIGUSR1_SLEEP_SEC 8 /* how long to sleep upon SIGUSR1 */ 35 | 36 | /* Magic threshold tweak factors, determined experimentally to make a 37 | * threshold of 10-20 behave reasonably. 38 | */ 39 | #define VELOC_ADJUST 30.0 40 | #define ACCEL_ADJUST (VELOC_ADJUST * 60) 41 | #define AVG_VELOC_ADJUST 3.0 42 | 43 | /* History depth for velocity average, in seconds */ 44 | #define AVG_DEPTH_SEC 0.3 45 | 46 | /* Parameters for adaptive threshold */ 47 | #define RECENT_PARK_SEC 3.0 /* How recent is "recently parked"? */ 48 | #define THRESH_ADAPT_SEC 1.0 /* How often to (potentially) change 49 | * the adaptive threshold? */ 50 | #define THRESH_INCREASE_FACTOR 1.1 /* Increase factor when recently 51 | * parked but user is typing */ 52 | #define THRESH_DECREASE_FACTOR 0.9985 /* Decrease factor when not recently 53 | * parked, per THRESH_ADAPT_SEC sec. */ 54 | #define NEAR_THRESH_FACTOR 0.8 /* Fraction of threshold considered 55 | * being near the threshold. */ 56 | 57 | /* Threshold for *continued* parking, as fraction of normal threshold */ 58 | #define PARKED_THRESH_FACTOR NEAR_THRESH_FACTOR /* >= NEAR_THRESH_FACTOR */ 59 | 60 | enum interfaces { 61 | INTERFACE_NONE, 62 | INTERFACE_HDAPS, 63 | INTERFACE_AMS, 64 | INTERFACE_FREEFALL, 65 | INTERFACE_HP3D, 66 | INTERFACE_APPLESMC, 67 | INTERFACE_TOSHIBA_HAPS, 68 | INTERFACE_TOSHIBA_ACPI, 69 | INTERFACE_INPUT 70 | 71 | }; 72 | 73 | char *interface_names[] = {"none", "HDAPS", "AMS", "FREEFALL", "HP3D", "APPLESMC", "TOSHIBA_HAPS", "TOSHIBA_ACPI", "INPUT"}; 74 | 75 | char *input_accel_names[] = {"Acer BMA150 accelerometer"}; 76 | 77 | enum kernel { 78 | PROTECT, 79 | UNLOAD_HEADS 80 | }; 81 | 82 | struct list { 83 | char name[BUF_LEN]; 84 | char protect_file[FILENAME_MAX]; 85 | struct list *next; 86 | }; 87 | -------------------------------------------------------------------------------- /src/input-helper.c: -------------------------------------------------------------------------------- 1 | /* 2 | * input-helper.c - find and open input devices 3 | * 4 | * Copyright (C) 2009-2014 Evgeni Golov 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | int device_open(int id) { 29 | char node[32]; 30 | int fd; 31 | 32 | snprintf(node, 32, "/dev/input/event%d", id); 33 | fd = open(node,O_RDONLY); 34 | if (fd < 0) 35 | return -1; 36 | 37 | return fd; 38 | } 39 | 40 | int device_find_byphys(char *phys) { 41 | int fd, i, rc; 42 | char buf[1024]; 43 | 44 | for (i = 0; i < 32; i++) { 45 | fd = device_open(i); 46 | if (fd > 0) { 47 | rc = ioctl(fd,EVIOCGPHYS(sizeof(buf)),buf); 48 | if (rc >= 0 && strcmp(phys, buf) == 0) { 49 | close(fd); 50 | return i; 51 | } 52 | } 53 | close(fd); 54 | } 55 | return -1; 56 | } 57 | 58 | int device_find_byname(char *name) { 59 | int fd, i, rc; 60 | char buf[1024]; 61 | 62 | for (i = 0; i < 32; i++) { 63 | fd = device_open(i); 64 | if (fd > 0) { 65 | rc = ioctl(fd,EVIOCGNAME(sizeof(buf)),buf); 66 | if (rc >= 0 && strcmp(name, buf) == 0) { 67 | close(fd); 68 | return i; 69 | } 70 | } 71 | close(fd); 72 | } 73 | return -1; 74 | } 75 | -------------------------------------------------------------------------------- /src/input-helper.h: -------------------------------------------------------------------------------- 1 | int device_open(int id); 2 | int device_find_byphys(char *phys); 3 | int device_find_byname(char *name); 4 | -------------------------------------------------------------------------------- /tests/acer.umockdev: -------------------------------------------------------------------------------- 1 | P: /devices/virtual/input/input11 2 | E: ABS=7 3 | E: EV=9 4 | E: ID_INPUT=1 5 | E: ID_INPUT_ACCELEROMETER=1 6 | E: MODALIAS=input:b0019v0000p0000e0000-e0,3,kra0,1,2,mlsfw 7 | E: NAME="Acer BMA150 accelerometer" 8 | E: PHYS="wmi/input1" 9 | E: PRODUCT=19/0/0/0 10 | E: PROP=0 11 | E: SUBSYSTEM=input 12 | E: TAGS=:seat: 13 | A: modalias=input:b0019v0000p0000e0000-e0,3,kra0,1,2,mlsfw 14 | A: name=Acer BMA150 accelerometer 15 | A: phys=wmi/input1 16 | A: properties=0 17 | A: uniq= 18 | -------------------------------------------------------------------------------- /tests/ams.umockdev: -------------------------------------------------------------------------------- 1 | P: /devices/ams 2 | E: DRIVER=ams 3 | E: MODALIAS=ams 4 | E: SUBSYSTEM=platform 5 | L: driver=../../../bus/drivers/ams 6 | A: modalias=ams 7 | A: current=0 0 0 8 | -------------------------------------------------------------------------------- /tests/applesmc.umockdev: -------------------------------------------------------------------------------- 1 | P: /devices/platform/applesmc.768 2 | E: DRIVER=applespmc 3 | E: MODALIAS=platform:applesmc.768 4 | E: SUBSYSTEM=platform 5 | L: driver=../../../bus/platform/drivers/applesmc 6 | A: modalias=platform:applesmc.768 7 | A: position=(0,0,0) 8 | -------------------------------------------------------------------------------- /tests/hdaps-accel.umockdev: -------------------------------------------------------------------------------- 1 | P: /devices/virtual/input/input17 2 | E: ABS=3 3 | E: EV=9 4 | E: ID_INPUT=1 5 | E: MODALIAS=input:b0019v1014p5054e4801-e0,3,kra0,1,mlsfw 6 | E: NAME="ThinkPad HDAPS accelerometer data" 7 | E: PHYS="hdaps/input1" 8 | E: PRODUCT=19/1014/5054/4801 9 | E: PROP=0 10 | E: SUBSYSTEM=input 11 | E: TAGS=:seat: 12 | A: capabilities/abs=3 13 | A: capabilities/ev=9 14 | A: capabilities/ff=0 15 | A: capabilities/key=0 16 | A: capabilities/led=0 17 | A: capabilities/msc=0 18 | A: capabilities/rel=0 19 | A: capabilities/snd=0 20 | A: capabilities/sw=0 21 | A: id/bustype=0019 22 | A: id/product=5054 23 | A: id/vendor=1014 24 | A: id/version=4801 25 | A: modalias=input:b0019v1014p5054e4801-e0,3,kra0,1,mlsfw 26 | A: name=ThinkPad HDAPS accelerometer data 27 | A: phys=hdaps/input1 28 | A: power/async=disabled 29 | A: power/control=auto 30 | A: power/runtime_active_kids=0 31 | A: power/runtime_active_time=0 32 | A: power/runtime_enabled=disabled 33 | A: power/runtime_status=unsupported 34 | A: power/runtime_suspended_time=0 35 | A: power/runtime_usage=0 36 | A: properties=0 37 | A: uniq= 38 | -------------------------------------------------------------------------------- /tests/hdaps-joystick.umockdev: -------------------------------------------------------------------------------- 1 | P: /devices/virtual/input/input16 2 | E: ABS=3 3 | E: EV=9 4 | E: ID_INPUT=1 5 | E: MODALIAS=input:b0019v1014p5054e6801-e0,3,kra0,1,mlsfw 6 | E: NAME="ThinkPad HDAPS joystick emulation" 7 | E: PHYS="hdaps/input0" 8 | E: PRODUCT=19/1014/5054/6801 9 | E: PROP=0 10 | E: SUBSYSTEM=input 11 | E: TAGS=:seat: 12 | A: capabilities/abs=3 13 | A: capabilities/ev=9 14 | A: capabilities/ff=0 15 | A: capabilities/key=0 16 | A: capabilities/led=0 17 | A: capabilities/msc=0 18 | A: capabilities/rel=0 19 | A: capabilities/snd=0 20 | A: capabilities/sw=0 21 | A: id/bustype=0019 22 | A: id/product=5054 23 | A: id/vendor=1014 24 | A: id/version=6801 25 | A: modalias=input:b0019v1014p5054e6801-e0,3,kra0,1,mlsfw 26 | A: name=ThinkPad HDAPS joystick emulation 27 | A: phys=hdaps/input0 28 | A: power/async=disabled 29 | A: power/control=auto 30 | A: power/runtime_active_kids=0 31 | A: power/runtime_active_time=0 32 | A: power/runtime_enabled=disabled 33 | A: power/runtime_status=unsupported 34 | A: power/runtime_suspended_time=0 35 | A: power/runtime_usage=0 36 | A: properties=0 37 | A: uniq= 38 | -------------------------------------------------------------------------------- /tests/hdaps.umockdev: -------------------------------------------------------------------------------- 1 | P: /devices/platform/hdaps 2 | E: DRIVER=hdaps 3 | E: MODALIAS=platform:hdaps 4 | E: SUBSYSTEM=platform 5 | A: calibrate=(-485,-503) 6 | L: driver=../../../bus/platform/drivers/hdaps 7 | A: invert=5 8 | A: keyboard_activity=1 9 | A: modalias=platform:hdaps 10 | A: mouse_activity=1 11 | A: oversampling_ratio=4 12 | A: position=(-498,-439) 13 | A: running_avg_filter_order=2 14 | A: sampling_rate=50 15 | A: temp1=34 16 | -------------------------------------------------------------------------------- /tests/sata-disk.umockdev: -------------------------------------------------------------------------------- 1 | P: /devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda 2 | N: sda 3 | S: disk/by-id/ata-Samsung_SSD_840_PRO_Series 4 | S: disk/by-id/wwn-0x500253855032ae3f 5 | E: DEVLINKS=/dev/disk/by-id/ata-Samsung_SSD_840_PRO_Series /dev/disk/by-id/wwn-0x500253855032ae3f 6 | E: DEVNAME=/dev/sda 7 | E: DEVTYPE=disk 8 | E: SUBSYSTEM=block 9 | A: alignment_offset=0 10 | A: capability=50 11 | A: dev=8:0 12 | L: device=../../../0:0:0:0 13 | A: discard_alignment=0 14 | A: events= 15 | A: events_async= 16 | A: events_poll_msecs=-1 17 | A: ext_range=256 18 | A: inflight= 0 0 19 | A: queue/rotational=1 20 | A: range=16 21 | A: removable=0 22 | A: ro=0 23 | A: size=500118192 24 | A: stat= 121624 4885 4330596 44456 860568 91574 28640798 745620 0 293420 789388 25 | 26 | P: /devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0 27 | E: DEVTYPE=scsi_device 28 | E: DRIVER=sd 29 | E: MODALIAS=scsi:t-0x00 30 | E: SUBSYSTEM=scsi 31 | A: device_blocked=0 32 | A: device_busy=0 33 | L: driver=../../../../../../../bus/scsi/drivers/sd 34 | A: eh_timeout=10 35 | A: evt_capacity_change_reported=0 36 | A: evt_inquiry_change_reported=0 37 | A: evt_lun_change_reported=0 38 | A: evt_media_change=0 39 | A: evt_mode_parameter_change_reported=0 40 | A: evt_soft_threshold_reached=0 41 | L: generic=scsi_generic/sg0 42 | A: iocounterbits=32 43 | A: iodone_cnt=0xf7ed8 44 | A: ioerr_cnt=0xf3 45 | A: iorequest_cnt=0xf8b57 46 | A: modalias=scsi:t-0x00 47 | A: model=Samsung SSD 840 48 | A: queue_depth=31 49 | A: queue_ramp_up_period=120000 50 | A: queue_type=simple 51 | A: rev=5B0Q 52 | A: scsi_level=6 53 | A: state=running 54 | A: timeout=30 55 | A: type=0 56 | A: vendor=ATA 57 | H: vpd_pg80=00800014533141544E454144353430363635542020202020 58 | H: vpd_pg83=0083006C02000014533141544E45414435343036363554202020202002010044415441202020202053616D73756E6720535344203834302050524F205365726965732020202020202020202020202020533141544E45414435343036363554202020202001030008500253855032AE3F 59 | A: unload_heads=0 60 | -------------------------------------------------------------------------------- /tests/toshiba_acpi.umockdev: -------------------------------------------------------------------------------- 1 | P: /devices/platform/toshiba_acpi 2 | E: DRIVER=toshiba_acpi 3 | E: MODALIAS=platform:toshiba_acpi 4 | E: SUBSYSTEM=platform 5 | L: driver=../../../bus/platform/drivers/toshiba_acpi 6 | A: modalias=platform:toshiba_acpi 7 | A: position=0 0 0 8 | -------------------------------------------------------------------------------- /tests/toshiba_haps.umockdev: -------------------------------------------------------------------------------- 1 | P: /devices/platform/toshiba_haps 2 | E: DRIVER=toshiba_haps 3 | E: MODALIAS=platform:toshiba_haps 4 | E: SUBSYSTEM=platform 5 | L: driver=../../../bus/platform/drivers/toshiba_haps 6 | A: modalias=platform:toshiba_haps 7 | A: movement=0 8 | A: protection_level=1 9 | --------------------------------------------------------------------------------