even more doc changes, including line-length fixes
[hdapsd.git] / src / hdapsd.c
blobd06112b7a549259eda317310095db7cf324324d6
1 /*
2 * hdapsd.c - Read from the HDAPS (Hard Drive Active Protection System)
3 * and protect the drive if motion over threshold...
5 * Derived from pivot.c by Robert Love.
7 * Copyright (C) 2005-2009 Jon Escombe <lists@dresco.co.uk>
8 * Robert Love <rml@novell.com>
9 * Shem Multinymous <multinymous@gmail.com>
10 * Elias Oltmanns <eo@nebensachen.de>
11 * Evgeni Golov <sargentd@die-welt.net>
13 * "Why does that kid keep dropping his laptop?"
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
30 #include "config.h"
31 #include <fcntl.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <stdarg.h>
35 #include <unistd.h>
36 #include <string.h>
37 #include <time.h>
38 #include <signal.h>
39 #include <errno.h>
40 #include <ctype.h>
41 #include <sys/utsname.h>
42 #include <sys/mman.h>
43 #include <sys/time.h>
44 #include <getopt.h>
45 #include <linux/input.h>
46 #include <syslog.h>
48 #define PID_FILE "/var/run/hdapsd.pid"
49 #define SYSFS_POSITION_FILE "/sys/devices/platform/hdaps/position"
50 #define MOUSE_ACTIVITY_FILE "/sys/devices/platform/hdaps/keyboard_activity"
51 #define KEYBD_ACTIVITY_FILE "/sys/devices/platform/hdaps/mouse_activity"
52 #define SAMPLING_RATE_FILE "/sys/devices/platform/hdaps/sampling_rate"
53 #define POSITION_INPUTDEV "/dev/input/hdaps/accelerometer-event"
54 #define BUF_LEN 40
56 #define FREEZE_SECONDS 1 /* period to freeze disk */
57 #define REFREEZE_SECONDS 0.1 /* period after which to re-freeze disk */
58 #define FREEZE_EXTRA_SECONDS 4 /* additional timeout for kernel timer */
59 #define DEFAULT_SAMPLING_RATE 50 /* default sampling frequency */
60 #define SIGUSR1_SLEEP_SEC 8 /* how long to sleep upon SIGUSR1 */
61 /* Magic threshold tweak factors, determined experimentally to make a
62 * threshold of 10-20 behave reasonably.
64 #define VELOC_ADJUST 30.0
65 #define ACCEL_ADJUST (VELOC_ADJUST * 60)
66 #define AVG_VELOC_ADJUST 3.0
68 /* History depth for velocity average, in seconds */
69 #define AVG_DEPTH_SEC 0.3
71 /* Parameters for adaptive threshold */
72 #define RECENT_PARK_SEC 3.0 /* How recent is "recently parked"? */
73 #define THRESH_ADAPT_SEC 1.0 /* How often to (potentially) change
74 * the adaptive threshold? */
75 #define THRESH_INCREASE_FACTOR 1.1 /* Increase factor when recently
76 * parked but user is typing */
77 #define THRESH_DECREASE_FACTOR 0.9985 /* Decrease factor when not recently
78 * parked, per THRESH_ADAPT_SEC sec. */
79 #define NEAR_THRESH_FACTOR 0.8 /* Fraction of threshold considered
80 * being near the threshold. */
82 /* Threshold for *continued* parking, as fraction of normal threshold */
83 #define PARKED_THRESH_FACTOR NEAR_THRESH_FACTOR /* >= NEAR_THRESH_FACTOR */
85 static int verbose = 0;
86 static int pause_now = 0;
87 static int dry_run = 0;
88 static int poll_sysfs = 0;
89 static int sampling_rate;
90 static int running = 1;
91 static int background = 0;
92 static int dosyslog = 0;
94 char pid_file[BUF_LEN] = "";
95 int hdaps_input_fd = 0;
97 struct list {
98 char name[BUF_LEN];
99 char protect_file[BUF_LEN];
100 struct list *next;
103 struct list *disklist = NULL;
106 * printlog (stream, fmt) - print the formatted message to syslog
107 * or to the defined stream
110 void printlog (FILE *stream, const char *fmt, ...)
112 time_t now;
113 int len = sizeof(fmt);
115 char msg[len+1024];
116 va_list ap;
117 va_start(ap, fmt);
118 vsnprintf(msg, len+1024, fmt, ap);
119 va_end(ap);
121 if (dosyslog)
122 syslog(LOG_INFO, msg);
123 else {
124 now = time((time_t *)NULL);
125 fprintf(stream, "%.24s: %s\n", ctime(&now), msg);
130 * slurp_file - read the content of a file (up to BUF_LEN-1) into a string.
132 * We open and close the file on every invocation, which is lame but due to
133 * several features of sysfs files:
135 * (a) Sysfs files are seekable.
136 * (b) Seeking to zero and then rereading does not seem to work.
138 * If I were king--and I will be one day--I would have made sysfs files
139 * nonseekable and only able to return full-size reads.
141 static int slurp_file(const char* filename, char* buf)
143 int ret;
144 int fd = open (filename, O_RDONLY);
145 if (fd < 0) {
146 printlog(stderr, "Could not open %s: %s.\nDo you have the hdaps module loaded?", filename, strerror(errno));
147 return fd;
150 ret = read (fd, buf, BUF_LEN-1);
151 if (ret < 0) {
152 printlog(stderr, "Could not read from %s: %s", filename, strerror(errno));
153 } else {
154 buf[ret] = 0; /* null-terminate so we can parse safely */
155 ret = 0;
158 if (close (fd))
159 printlog(stderr, "Could not close %s: %s", filename, strerror(errno));
161 return ret;
165 * read_position_from_sysfs() - read the (x,y) position pair from hdaps via sysfs files
166 * This method is not recommended for frequent polling, since it causes unnecessary interrupts
167 * and a phase difference between hdaps-to-EC polling vs. hdapsd-to-hdaps polling.
169 static int read_position_from_sysfs (int *x, int *y)
171 char buf[BUF_LEN];
172 int ret;
173 if ((ret = slurp_file(SYSFS_POSITION_FILE, buf)))
174 return ret;
175 return (sscanf (buf, "(%d,%d)\n", x, y) != 2);
179 * read_int() - read an integer from a file
181 static int read_int (const char* filename)
183 char buf[BUF_LEN];
184 int ret;
185 if ((ret = slurp_file(filename, buf)))
186 return ret;
187 if (sscanf (buf, "%d\n", &ret) != 1)
188 return -EIO;
189 return ret;
193 * get_km_activity() - returns 1 if there is keyboard or mouse activity
195 static int get_km_activity()
197 if (read_int(MOUSE_ACTIVITY_FILE)==1)
198 return 1;
199 if (read_int(KEYBD_ACTIVITY_FILE)==1)
200 return 1;
201 return 0;
206 * read_position_from_inputdev() - read the (x,y) position pair and time from hdaps
207 * via the hdaps input device. Blocks there is a change in position.
208 * The x and y arguments should contain the last read values, since if one of them
209 * doesn't change it will not be assigned.
211 static int read_position_from_inputdev (int *x, int *y, double *utime)
213 struct input_event ev;
214 int len, done = 0;
215 *utime = 0;
216 while (1) {
217 len = read(hdaps_input_fd, &ev, sizeof(struct input_event));
218 if (len < 0) {
219 printlog(stderr, "ERROR: failed reading %s (%s).", POSITION_INPUTDEV, strerror(errno));
220 return len;
222 if (len < (int)sizeof(struct input_event)) {
223 printlog(stderr, "ERROR: short read from %s (%d bytes).", POSITION_INPUTDEV, len);
224 return -EIO;
226 switch (ev.type) {
227 case EV_ABS: /* new X or Y */
228 switch (ev.code) {
229 case ABS_X:
230 *x = ev.value;
231 break;
232 case ABS_Y:
233 *y = ev.value;
234 break;
235 default:
236 continue;
238 break;
239 case EV_SYN: /* X and Y now reflect latest measurement */
240 done = 1;
241 break;
242 default:
243 continue;
245 if (!*utime) /* first event's time is closest to reality */
246 *utime = ev.time.tv_sec + ev.time.tv_usec/1000000.0;
247 if (done)
248 return 0;
254 * write_protect() - park/unpark
256 static int write_protect (const char *path, int val)
258 int fd, ret;
259 char buf[BUF_LEN];
261 if (dry_run)
262 return 0;
264 snprintf(buf, BUF_LEN, "%d", val);
266 fd = open (path, O_WRONLY);
267 if (fd < 0) {
268 printlog (stderr, "Could not open %s", path);
269 return fd;
272 ret = write (fd, buf, strlen(buf));
274 if (ret < 0) {
275 printlog (stderr, "Could not write to %s.\nDoes your kernel/drive support IDLE_IMMEDIATE with UNLOAD?", path);
276 goto out;
278 ret = 0;
280 out:
281 if (close (fd))
282 printlog (stderr, "Could not close %s", path);
284 return ret;
287 double get_utime (void)
289 struct timeval tv;
290 int ret = gettimeofday(&tv, NULL);
291 if (ret) {
292 perror("gettimeofday");
293 exit(1);
295 return tv.tv_sec + tv.tv_usec/1000000.0;
298 /* Handler for SIGUSR1, sleeps for a few seconds. Useful when suspending laptop. */
299 void SIGUSR1_handler(int sig)
301 signal(SIGUSR1, SIGUSR1_handler);
302 pause_now=1;
305 /* Handler for SIGTERM, deletes the pidfile and exits. */
306 void SIGTERM_handler(int sig)
308 signal(SIGTERM, SIGTERM_handler);
309 running = 0;
313 * version() - display version information and exit
315 void version()
317 printf(PACKAGE_STRING"\n");
318 exit(0);
322 * usage() - display usage instructions and exit
324 void usage()
326 printf("Usage: "PACKAGE_NAME" [OPTIONS]\n");
327 printf("\n");
328 printf("Required options:\n");
329 printf(" -d --device=<device> <device> is likely to be hda or sda.\n");
330 printf(" Can be given multiple times\n");
331 printf(" to protect multiple devices.\n");
332 printf("\n");
333 printf("Additional options:\n");
334 printf(" -s --sensitivity=<sensitivity> How sensitive "PACKAGE_NAME" should be to movements.\n");
335 printf(" Defaults to 15, higher value means less\n");
336 printf(" sensitive.\n");
337 printf(" -a --adaptive Adaptive threshold (automatic increase\n");
338 printf(" when the built-in keyboard/mouse are used).\n");
339 printf(" -v --verbose Get verbose statistics.\n");
340 printf(" -b --background Run the process in the background.\n");
341 printf(" -p --pidfile[=<pidfile>] Create a pid file when running\n");
342 printf(" in background.\n");
343 printf(" If <pidfile> is not specified,\n");
344 printf(" it's set to %s.\n", PID_FILE);
345 printf(" -t --dry-run Don't actually park the drive.\n");
346 printf(" -y --poll-sysfs Force use of sysfs interface to\n");
347 printf(" accelerometer.\n");
348 printf(" -l --syslog Log to syslog instead of stdout/stderr.\n");
349 printf("\n");
350 printf(" -V --version Display version information and exit.\n");
351 printf(" -h --help Display this message and exit.\n");
352 printf("\n");
353 printf("You can send SIGUSR1 to deactivate "PACKAGE_NAME" for %d seconds.\n",
354 SIGUSR1_SLEEP_SEC);
355 printf("\n");
356 printf("Send bugs, comments and suggestions to "PACKAGE_BUGREPORT"\n");
357 exit(1);
361 * check_thresh() - compare a value to the threshold
363 void check_thresh(double val_sqr, double thresh, int* above, int* near,
364 char* reason_out, char reason_mark)
366 if (val_sqr > thresh*thresh*NEAR_THRESH_FACTOR*NEAR_THRESH_FACTOR) {
367 *near = 1;
368 *reason_out = tolower(reason_mark);
370 if (val_sqr > thresh*thresh) {
371 *above = 1;
372 *reason_out = toupper(reason_mark);
377 * analyze() - make a decision on whether to park given present readouts
378 * (remembers some past data in local static variables).
379 * Computes and checks 3 values:
380 * velocity: current position - prev position / time delta
381 * acceleration: current velocity - prev velocity / time delta
382 * average velocity: exponentially decaying average of velocity,
383 * weighed by time delta.
384 * The velocity and acceleration tests respond quickly to short sharp shocks,
385 * while the average velocity test catches long, smooth movements (and
386 * averages out measurement noise).
387 * The adaptive threshold, if enabled, increases when (built-in) keyboard or
388 * mouse activity happens shortly after some value was above or near the
389 * adaptive threshold. The adaptive threshold slowly decreases back to the
390 * base threshold when no value approaches it.
393 int analyze(int x, int y, double unow, double base_threshold,
394 int adaptive, int parked)
396 static int x_last = 0, y_last = 0;
397 static double unow_last = 0, x_veloc_last = 0, y_veloc_last = 0;
398 static double x_avg_veloc = 0, y_avg_veloc = 0;
399 static int history = 0; /* how many recent valid samples? */
400 static double adaptive_threshold = -1; /* current adaptive thresh */
401 static int last_thresh_change = 0; /* last adaptive thresh change */
402 static int last_near_thresh = 0; /* last time we were near thresh */
403 static int last_km_activity; /* last time kbd/mouse activity seen */
405 double udelta, x_delta, y_delta, x_veloc, y_veloc, x_accel, y_accel;
406 double veloc_sqr, accel_sqr, avg_veloc_sqr;
407 double exp_weight;
408 double threshold; /* transient threshold for this iteration */
409 char reason[4]; /* "which threshold reached?" string for verbose */
410 int recently_near_thresh;
411 int above=0, near=0; /* above threshold, near threshold */
413 /* Adaptive threshold adjustment */
414 if (adaptive_threshold<0) /* first invocation */
415 adaptive_threshold = base_threshold;
416 recently_near_thresh = unow < last_near_thresh + RECENT_PARK_SEC;
417 if (adaptive && recently_near_thresh && get_km_activity())
418 last_km_activity = unow;
419 if (adaptive && unow > last_thresh_change + THRESH_ADAPT_SEC) {
420 if (recently_near_thresh) {
421 if (last_km_activity > last_near_thresh &&
422 last_km_activity > last_thresh_change) {
423 /* Near threshold and k/m activity */
424 adaptive_threshold *= THRESH_INCREASE_FACTOR;
425 last_thresh_change = unow;
427 } else {
428 /* Recently never near threshold */
429 adaptive_threshold *= THRESH_DECREASE_FACTOR;
430 if (adaptive_threshold < base_threshold)
431 adaptive_threshold = base_threshold;
432 last_thresh_change = unow;
436 /* compute deltas */
437 udelta = unow - unow_last;
438 x_delta = x - x_last;
439 y_delta = y - y_last;
441 /* compute velocity */
442 x_veloc = x_delta/udelta;
443 y_veloc = y_delta/udelta;
444 veloc_sqr = x_veloc*x_veloc + y_veloc*y_veloc;
446 /* compute acceleration */
447 x_accel = (x_veloc - x_veloc_last)/udelta;
448 y_accel = (y_veloc - y_veloc_last)/udelta;
449 accel_sqr = x_accel*x_accel + y_accel*y_accel;
451 /* compute exponentially-decaying velocity average */
452 exp_weight = udelta/AVG_DEPTH_SEC; /* weight of this sample */
453 exp_weight = 1 - 1.0/(1+exp_weight); /* softly clamped to 1 */
454 x_avg_veloc = exp_weight*x_veloc + (1-exp_weight)*x_avg_veloc;
455 y_avg_veloc = exp_weight*y_veloc + (1-exp_weight)*y_avg_veloc;
456 avg_veloc_sqr = x_avg_veloc*x_avg_veloc + y_avg_veloc*y_avg_veloc;
458 threshold = adaptive_threshold;
459 if (parked) /* when parked, be reluctant to unpark */
460 threshold *= PARKED_THRESH_FACTOR;
462 /* Threshold test (uses Pythagoras's theorem) */
463 strcpy(reason, " ");
465 check_thresh(veloc_sqr, threshold*VELOC_ADJUST,
466 &above, &near, reason+0, 'V');
467 check_thresh(accel_sqr, threshold*ACCEL_ADJUST,
468 &above, &near, reason+1, 'A');
469 check_thresh(avg_veloc_sqr, threshold*AVG_VELOC_ADJUST,
470 &above, &near, reason+2, 'X');
472 if (verbose) {
473 printf("dt=%5.3f "
474 "dpos=(%3g,%3g) "
475 "vel=(%6.1f,%6.1f)*%g "
476 "acc=(%6.1f,%6.1f)*%g "
477 "avg_vel=(%6.1f,%6.1f)*%g "
478 "thr=%.1f "
479 "%s\n",
480 udelta,
481 x_delta, y_delta,
482 x_veloc/VELOC_ADJUST,
483 y_veloc/VELOC_ADJUST,
484 VELOC_ADJUST*1.0,
485 x_accel/ACCEL_ADJUST,
486 y_accel/ACCEL_ADJUST,
487 ACCEL_ADJUST*1.0,
488 x_avg_veloc/AVG_VELOC_ADJUST,
489 y_avg_veloc/AVG_VELOC_ADJUST,
490 AVG_VELOC_ADJUST*1.0,
491 threshold,
492 reason);
495 if (udelta>1.0) { /* Too much time since last (resume from suspend?) */
496 history = 0;
497 x_avg_veloc = y_avg_veloc = 0;
500 if (history<2) { /* Not enough data for meaningful result */
501 above = 0;
502 near = 0;
503 ++history;
506 if (near)
507 last_near_thresh = unow;
509 x_last = x;
510 y_last = y;
511 x_veloc_last = x_veloc;
512 y_veloc_last = y_veloc;
513 unow_last = unow;
515 return above;
519 * add_disk (disk) - add the given disk to the global disklist
521 void add_disk (char* disk) {
522 struct utsname sysinfo;
523 char protect_file[BUF_LEN] = "";
524 if (uname(&sysinfo) < 0 || strcmp("2.6.27", sysinfo.release) <= 0)
525 snprintf(protect_file, BUF_LEN, "/sys/block/%s/device/unload_heads", disk);
526 else
527 snprintf(protect_file, BUF_LEN, "/sys/block/%s/queue/protect", disk);
529 if (disklist == NULL) {
530 disklist = (struct list *)malloc(sizeof(struct list));
531 if (disklist == NULL) {
532 printlog(stderr, "Error allocating memory.");
533 exit(EXIT_FAILURE);
535 else {
536 strncpy(disklist->name,disk,BUF_LEN);
537 strncpy(disklist->protect_file,protect_file,BUF_LEN);
538 disklist->next = NULL;
541 else {
542 struct list *p = disklist;
543 while (p->next != NULL)
544 p = p->next;
545 p->next = (struct list *)malloc(sizeof(struct list));
546 if (p->next == NULL) {
547 printlog(stderr, "Error allocating memory.");
548 exit(EXIT_FAILURE);
550 else {
551 strncpy(p->next->name,disk,BUF_LEN);
552 strncpy(p->next->protect_file,protect_file,BUF_LEN);
553 p->next->next = NULL;
559 * free_disk (disk) - free the allocated memory
561 void free_disk (struct list *disk) {
562 if (disk != NULL) {
563 if (disk->next != NULL)
564 free_disk(disk->next);
565 free(disk);
570 * main() - loop forever, reading the hdaps values and
571 * parking/unparking as necessary
573 int main (int argc, char** argv)
575 struct utsname sysinfo;
576 int c, park_now, protect_factor;
577 int x=0, y=0;
578 int fd, i, ret, threshold = 15, adaptive=0,
579 pidfile = 0, parked = 0;
580 double unow = 0, parked_utime = 0;
582 if (uname(&sysinfo) < 0 || strcmp("2.6.27", sysinfo.release) <= 0)
583 protect_factor = 1000;
584 else
585 protect_factor = 1;
587 struct option longopts[] =
589 {"device", required_argument, NULL, 'd'},
590 {"sensitivity", required_argument, NULL, 's'},
591 {"adaptive", no_argument, NULL, 'a'},
592 {"verbose", no_argument, NULL, 'v'},
593 {"background", no_argument, NULL, 'b'},
594 {"pidfile", optional_argument, NULL, 'p'},
595 {"dry-run", no_argument, NULL, 't'},
596 {"poll-sysfs", no_argument, NULL, 'y'},
597 {"version", no_argument, NULL, 'V'},
598 {"help", no_argument, NULL, 'h'},
599 {"syslog", no_argument, NULL, 'l'},
600 {NULL, 0, NULL, 0}
603 openlog(PACKAGE_NAME, LOG_PID, LOG_DAEMON);
605 while ((c = getopt_long(argc, argv, "d:s:vbap::tyVhl", longopts, NULL)) != -1) {
606 switch (c) {
607 case 'd':
608 add_disk(optarg);
609 break;
610 case 's':
611 threshold = atoi(optarg);
612 break;
613 case 'b':
614 background = 1;
615 break;
616 case 'a':
617 adaptive = 1;
618 break;
619 case 'v':
620 verbose = 1;
621 break;
622 case 'p':
623 pidfile = 1;
624 if (optarg == NULL) {
625 snprintf(pid_file, BUF_LEN, "%s", PID_FILE);
626 } else {
627 snprintf(pid_file, BUF_LEN, "%s", optarg);
629 break;
630 case 't':
631 printlog(stdout, "Dry run, will not actually park heads or freeze queue.");
632 dry_run = 1;
633 break;
634 case 'y':
635 poll_sysfs = 1;
636 break;
637 case 'V':
638 version();
639 break;
640 case 'l':
641 dosyslog = 1;
642 break;
643 case 'h':
644 default:
645 usage();
646 break;
650 if (!threshold || disklist == NULL)
651 usage(argv);
653 if (!poll_sysfs) {
654 hdaps_input_fd = open(POSITION_INPUTDEV, O_RDONLY);
655 if (hdaps_input_fd<0) {
656 printlog(stdout,
657 "WARNING: Cannot open hdaps position input file %s (%s). "
658 "You may be using an incompatible version of the hdaps module, "
659 "or missing the required udev rule.\n"
660 "Falling back to reading the position from sysfs (uses more power).\n"
661 "Use '-y' to silence this warning.",
662 POSITION_INPUTDEV, strerror(errno));
663 poll_sysfs = 1;
667 if (background) {
668 verbose = 0;
669 if (pidfile) {
670 fd = open (pid_file, O_WRONLY | O_CREAT, 0644);
671 if (fd < 0) {
672 printlog (stderr, "Could not create pidfile: %s", pid_file);
673 return 1;
676 daemon(0,0);
677 if (pidfile) {
678 char buf[BUF_LEN];
679 snprintf (buf, BUF_LEN, "%d\n", getpid());
680 ret = write (fd, buf, strlen(buf));
681 if (ret < 0) {
682 printlog (stderr, "Could not write to pidfile %s", pid_file);
683 return 1;
685 if (close (fd)) {
686 printlog (stderr, "Could not close pidfile %s", pid_file);
687 return 1;
692 mlockall(MCL_FUTURE);
694 if (verbose) {
695 struct list *p = disklist;
696 while (p != NULL) {
697 printf("disk: %s\n", p->name);
698 p = p->next;
700 printf("threshold: %i\n", threshold);
701 printf("read_method: %s\n", poll_sysfs ? "poll-sysfs" : "input-dev");
704 printlog(stdout, "Starting "PACKAGE_NAME);
706 /* check the protect attribute exists */
707 /* wait for it if it's not there (in case the attribute hasn't been created yet) */
708 struct list *p = disklist;
709 while (p != NULL) {
710 fd = open (p->protect_file, O_RDWR);
711 if (background)
712 for (i=0; fd < 0 && i < 100; ++i) {
713 usleep (100000); /* 10 Hz */
714 fd = open (p->protect_file, O_RDWR);
716 if (fd < 0) {
717 printlog (stderr, "Could not open %s\nDoes your kernel/drive support IDLE_IMMEDIATE with UNLOAD?", p->protect_file);
718 free_disk(disklist);
719 return 1;
721 close (fd);
722 p = p->next;
725 /* see if we can read the sensor */
726 /* wait for it if it's not there (in case the attribute hasn't been created yet) */
727 ret = read_position_from_sysfs (&x, &y);
728 if (background)
729 for (i=0; ret && i < 100; ++i) {
730 usleep (100000); /* 10 Hz */
731 ret = read_position_from_sysfs (&x, &y);
733 if (ret)
734 return 1;
736 /* adapt to the driver's sampling rate */
737 sampling_rate = read_int(SAMPLING_RATE_FILE);
738 if (sampling_rate <= 0)
739 sampling_rate = DEFAULT_SAMPLING_RATE;;
740 if (verbose)
741 printf("sampling_rate: %d\n", sampling_rate);
743 signal(SIGUSR1, SIGUSR1_handler);
745 signal(SIGTERM, SIGTERM_handler);
747 while (running) {
748 if (poll_sysfs) {
749 usleep (1000000/sampling_rate);
750 ret = read_position_from_sysfs (&x, &y);
751 unow = get_utime(); /* microsec */
752 } else {
753 double oldunow = unow;
754 int oldx = x, oldy = y;
755 ret = read_position_from_inputdev (&x, &y, &unow);
757 /* The input device issues events only when the position changed.
758 * The analysis state needs to know how long the position remained
759 * unchanged, so send analyze() a fake retroactive update before sending
760 * the new one. */
761 if (!ret && oldunow && unow-oldunow > 1.5/sampling_rate)
762 analyze(oldx, oldy, unow-1.0/sampling_rate, threshold, adaptive, parked);
766 if (ret) {
767 if (verbose)
768 printf("readout error (%d)\n", ret);
769 continue;
772 park_now = analyze(x, y, unow, threshold, adaptive, parked);
774 if (park_now && !pause_now) {
775 if (!parked || unow>parked_utime+REFREEZE_SECONDS) {
776 /* Not frozen or freeze about to expire */
777 struct list *p = disklist;
778 while (p != NULL) {
779 write_protect(p->protect_file,
780 (FREEZE_SECONDS+FREEZE_EXTRA_SECONDS) * protect_factor);
781 p = p->next;
783 /* Write protect before any output (xterm, or
784 * whatever else is handling our stdout, may be
785 * swapped out).
787 if (!parked)
788 printlog(stdout, "parking");
789 parked = 1;
790 parked_utime = unow;
792 } else {
793 if (parked &&
794 (pause_now || unow>parked_utime+FREEZE_SECONDS)) {
795 /* Sanity check */
796 struct list *p = disklist;
797 while (p != NULL) {
798 if (!dry_run && !read_int(p->protect_file))
799 printlog(stderr, "Error! Not parked when we "
800 "thought we were... (paged out "
801 "and timer expired?)");
802 /* Freeze has expired */
803 write_protect(p->protect_file, 0); /* unprotect */
804 p = p->next;
806 parked = 0;
807 printlog(stdout, "un-parking");
809 while (pause_now) {
810 pause_now=0;
811 printlog(stdout, "pausing for %d seconds", SIGUSR1_SLEEP_SEC);
812 sleep(SIGUSR1_SLEEP_SEC);
818 free_disk(disklist);
819 printlog(stdout, "Terminating "PACKAGE_NAME);
820 closelog();
821 if (pidfile)
822 unlink(pid_file);
823 munlockall();
824 return ret;