SOURCES: hdapsd-20060322.c (NEW) new

arekm arekm at pld-linux.org
Sun Mar 26 14:29:34 CEST 2006


Author: arekm                        Date: Sun Mar 26 12:29:34 2006 GMT
Module: SOURCES                       Tag: HEAD
---- Log message:
new

---- Files affected:
SOURCES:
   hdapsd-20060322.c (NONE -> 1.1)  (NEW)

---- Diffs:

================================================================
Index: SOURCES/hdapsd-20060322.c
diff -u /dev/null SOURCES/hdapsd-20060322.c:1.1
--- /dev/null	Sun Mar 26 14:29:34 2006
+++ SOURCES/hdapsd-20060322.c	Sun Mar 26 14:29:29 2006
@@ -0,0 +1,402 @@
+/*
+ * hdapsd.c - Read from the HDAPS (HardDrive Active Protection System)
+ *            and protect the drive if motion over threshold...
+ *
+ *            Derived from pivot.c by Robert Love.
+ *
+ * Copyright (C) 2005-2006 Jon Escombe <lists at dresco.co.uk>
+ *                         Robert Love <rml at novell.com>
+ *                         Shem Multinymous <multinymous at gmail.com>
+ *
+ * "Why does that kid keep dropping his laptop?"
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <signal.h>
+#include <sys/mman.h>
+
+#define SYSFS_POSITION_FILE	"/sys/devices/platform/hdaps/position"
+
+#define BUF_LEN                 32
+
+#define FREEZE_SECONDS          1    /* period to freeze disk */
+#define REFREEZE_SECONDS        0.1  /* period after which to re-freeze disk */
+#define FREEZE_EXTRA_SECONDS    4    /* additional timeout for kernel timer */
+#define FREQ_HZ                 50   /* sampling frequency */
+#define SIGUSR1_SLEEP_SEC       8    /* how long to sleep upon SIGUSR1 */
+
+/* Magic threshold tweak factors, determined experimentally to make a
+ * threshold of 10-20 behave reasonably.
+ */
+#define VELOC_ADJUST            30.0
+#define ACCEL_ADJUST            (VELOC_ADJUST * 60)
+#define AVG_VELOC_ADJUST        3.0
+
+/* History depth for velocity average, in seconds */
+#define AVG_DEPTH_SEC           0.3
+
+static verbose = 0;
+static pause_now = 0;
+
+/*
+ * read_position - read the (x,y) position pair from hdaps.
+ *
+ * We open and close the file on every invocation, which is lame but due to
+ * several features of sysfs files:
+ *
+ *	(a) Sysfs files are seekable.
+ *	(b) Seeking to zero and then rereading does not seem to work.
+ *
+ * If I were king--and I will be one day--I would have made sysfs files
+ * nonseekable and only able to return full-size reads.
+ */
+static int read_position (int *x, int *y)
+{
+	char buf[BUF_LEN];
+	int fd, ret;
+
+	fd = open (SYSFS_POSITION_FILE, O_RDONLY);
+	if (fd < 0) {
+		perror ("open(\"" SYSFS_POSITION_FILE "\")");
+		return fd;
+	}	
+
+	ret = read (fd, buf, BUF_LEN);
+	if (ret < 0) {
+		perror ("read(\"" SYSFS_POSITION_FILE "\")");
+		goto out;
+	} else if (ret == 0) {
+		fprintf (stderr, "error: unexpectedly read zero!\n");
+		ret = 1;
+		goto out;
+	}
+	ret = 0;
+
+	if (sscanf (buf, "(%d,%d)\n", x, y) != 2)
+		ret = 1;
+
+out:
+	if (close (fd))
+		perror ("close(\"" SYSFS_POSITION_FILE "\")");
+
+	return ret;
+}
+
+/*
+ * read_protect() - read the current protection status
+ */
+static int read_protect (const char *path)
+{
+	int fd, ret;
+	char buf[BUF_LEN];
+
+	memset(buf, 0, sizeof(buf));
+	fd = open (path, O_RDONLY);
+	ret = read (fd, buf, BUF_LEN);
+	close (fd);
+	if (ret<0)
+		return (ret);
+	return atoi(buf);
+}
+
+/*
+ * write_protect() - park/unpark
+ */
+static int write_protect (const char *path, int val)
+{
+	int fd, ret;
+	char buf[BUF_LEN];
+
+	snprintf(buf, BUF_LEN, "%d", val);
+
+	fd = open (path, O_WRONLY);
+	if (fd < 0) {
+		perror ("open");
+		return fd;
+	}	
+
+	ret = write (fd, buf, strlen(buf));
+
+	if (ret < 0) {
+		perror ("write");
+		goto out;
+	}
+	ret = 0;
+
+out:
+	if (close (fd))
+		perror ("close");
+
+	return ret;
+}
+
+double get_utime (void)
+{
+	struct timeval tv;
+	int ret = gettimeofday(&tv, NULL);
+	if (ret) {
+		perror("gettimeofday");
+		exit(1);
+	}
+	return tv.tv_sec + tv.tv_usec/1000000.0;
+}
+
+/* Handler for SIGUSR1, sleeps for a few seconds. Useful when suspending laptop. */
+void SIGUSR1_handler(int sig)
+{
+	signal(SIGUSR1, SIGUSR1_handler);
+	pause_now=1;
+}
+
+/*
+ * usage() - display usage instructions and exit 
+ */
+void usage()
+{
+	printf("usage: hdapsd -d <device> -s <sensitivity> [-b] [-v]\n");
+	printf("       <device> is likely to be hda or sda.\n");
+	printf("       A suggested starting <sensitivity> is 15.\n");
+	printf("       Use -b will run the process in the background.\n");
+	printf("       Use -v to get verbose statistics.\n");
+	printf("       Send SIGUSR1 to deactivate for %d seconds.\n",
+	       SIGUSR1_SLEEP_SEC);
+	exit(1);
+}
+
+/*
+ * analyze() - make a decision on whether to park given present readouts
+ *             (remembers some past data in local static variables).
+ * Computes and checks 3 values:
+ *   velocity:     current position - prev position / time delta
+ *   acceleration: current velocity - prev velocity / time delta
+ *   average velocity: exponentially decaying average of velocity,
+ *                     weighed by time delta.
+ * The velocity and acceleration tests respond quickly to short sharp shocks,
+ * while the average velocity test catches long, smooth movements (and
+ * averages out measurement noise).
+ */
+int analyze(int x, int y, double unow, double threshold) {
+	static int x_last = 0, y_last = 0;
+	static double unow_last = 0, x_veloc_last = 0, y_veloc_last = 0;
+	static double x_avg_veloc = 0, y_avg_veloc = 0;
+	static int history = 0; /* how many recent valid samples? */
+
+	double udelta, x_delta, y_delta, x_veloc, y_veloc, x_accel, y_accel;
+	double veloc_sqr, accel_sqr, avg_veloc_sqr;
+	double exp_weight;
+	char reason[3];
+	int ret = 0;
+	
+	/* compute deltas */
+	udelta = unow - unow_last;
+	x_delta = x - x_last;
+	y_delta = y - y_last;
+
+	/* compute velocity */
+	x_veloc = x_delta/udelta;
+	y_veloc = y_delta/udelta;
+	veloc_sqr = x_veloc*x_veloc + y_veloc*y_veloc;
+
+	/* compute acceleration */
+	x_accel = (x_veloc - x_veloc_last)/udelta;
+	y_accel = (y_veloc - y_veloc_last)/udelta;
+	accel_sqr = x_accel*x_accel + y_accel*y_accel;
+
+	/* compute exponentially-decaying velocity average */
+	exp_weight = udelta/AVG_DEPTH_SEC; /* weight of this sample */
+	exp_weight = exp_weight/(1+exp_weight); /* softly clamped to 1 */
+	x_avg_veloc = exp_weight*x_veloc + (1-exp_weight)*x_avg_veloc;
+	y_avg_veloc = exp_weight*y_veloc + (1-exp_weight)*y_avg_veloc;
+	avg_veloc_sqr = x_avg_veloc*x_avg_veloc + y_avg_veloc*y_avg_veloc;
+
+	/* Threshold test (uses Pythagoras's theorem) */
+	strcpy(reason, "   ");
+	if (veloc_sqr > threshold*threshold * VELOC_ADJUST*VELOC_ADJUST ) {
+		ret = 1;
+		reason[0]='V';
+	}
+	if (accel_sqr > threshold*threshold * ACCEL_ADJUST*ACCEL_ADJUST ) {
+		ret = 1;
+		reason[1]='A';
+	}
+	if (avg_veloc_sqr > 
+	    threshold*threshold * AVG_VELOC_ADJUST*AVG_VELOC_ADJUST ) {
+		ret = 1;
+		reason[2]='X';
+	}
+
+	if (verbose) {
+		printf("dt=%5.3f  "
+		       "delta=(%3g,%3g)  "
+		       "veloc=(%6.1f,%6.1f)*%g  " /* For easy comparison to threshold. */
+		       "accel=(%6.1f,%6.1f)*%g  " 
+		       "avg_veloc=(%6.1f,%6.1f)*%g  "
+		       "%s\n",
+		       udelta,
+		       x_delta, y_delta,
+		       x_veloc/VELOC_ADJUST,
+		       y_veloc/VELOC_ADJUST,
+		       VELOC_ADJUST*1.0,
+		       x_accel/ACCEL_ADJUST,
+		       y_accel/ACCEL_ADJUST,
+		       ACCEL_ADJUST*1.0,
+		       x_avg_veloc/AVG_VELOC_ADJUST,
+		       y_avg_veloc/AVG_VELOC_ADJUST,
+		       AVG_VELOC_ADJUST*1.0,
+		       reason);
+	}
+
+	if (udelta>1.0) { /* Too much time since last (resume from suspend?) */
+		history = 0;
+		x_avg_veloc = y_avg_veloc = 0;
+	}
+
+	if (history<2) { /* Not enough data for meaningful result */
+		ret = 0;
+		++history;
+	}
+
+	x_last = x;
+	y_last = y;
+	x_veloc_last = x_veloc;
+	y_veloc_last = y_veloc;
+	unow_last = unow;
+
+	return ret;
+}
+
+/*
+ * main() - loop forever, reading the hdaps values and 
+ *          parking/unparking as necessary
+ */
+int main (int argc, char** argv)
+{
+	int c, park_now;
+	int x, y;
+	int fd, i, ret, threshold = 0, background = 0, parked = 0;
+	char protect_file[32] = "";
+	double unow, parked_utime;
+	time_t now;
+
+	for (;;) {
+		c = getopt(argc, argv, "d:s:bv");
+		if (c < 0)
+			break;
+		switch (c) {
+			case 'd':
+				sprintf(protect_file, "/sys/block/%s/queue/protect", optarg);
+				break;
+			case 's':
+				threshold = atoi(optarg);
+				break;
+			case 'b':
+				background = 1;
+				break;
+			case 'v':
+				verbose = 1;
+				break;
+			default:
+				usage();
+				break;
+		}
+	}
+
+	if (!threshold || !strlen(protect_file))
+		usage(argv);
+
+	if (background) {
+		verbose = 0;
+		daemon(0,0);
+	}
+
+	mlockall(MCL_FUTURE);
+
+	if (verbose) {
+		printf("protect_file: %s\n", protect_file);
+		printf("threshold: %i\n", threshold);
+	}
+
+	/* check the protect attribute exists */
+	/* wait for it if it's not there (in case the attribute hasn't been created yet) */
+	fd = open (protect_file, O_RDWR);
+	if (background)
+		for (i=0; fd < 0 && i < 100; ++i) {
+			usleep (100000);	/* 10 Hz */
+			fd = open (protect_file, O_RDWR);
+		}
+	if (fd < 0) {
+		perror ("open(protect_file)");
+		return 1;
+	}
+	close (fd);
+
+	/* see if we can read the sensor */
+	/* wait for it if it's not there (in case the attribute hasn't been created yet) */
+	ret = read_position (&x, &y);
+	if (background)
+		for (i=0; ret && i < 100; ++i) {
+			usleep (100000);	/* 10 Hz */
+			ret = read_position (&x, &y);
+		}
+	if (ret)
+		return 1;
+
+	signal(SIGUSR1, SIGUSR1_handler);
+
+	while (1) {
+		usleep (1000000/FREQ_HZ);
+
+		ret = read_position (&x, &y);
+		if (ret)
+			continue;
+
+		now = time((time_t *)NULL); /* sec */
+		unow = get_utime(); /* microsec */
+
+		park_now = analyze(x, y, unow, threshold);
+
+		if (park_now && !pause_now) {
+			if (!parked || unow>parked_utime+REFREEZE_SECONDS) {
+				/* Not frozen or freeze about to expire */
+				write_protect(protect_file,
+				              FREEZE_SECONDS+FREEZE_EXTRA_SECONDS);
+				/* Write protect before any output (xterm, or 
+				 * whatever else is handling our stdout, may be 
+				 * swapped out).
+				*/
+				if (!parked)
+					printf("%.24s: parking\n", ctime(&now));
+				parked = 1;
+				parked_utime = unow;
+			} 
+		} else {
+			if (parked &&
+			    (pause_now || unow>parked_utime+FREEZE_SECONDS)) {
+				/* Sanity check */
+				if (!read_protect(protect_file))
+					printf("\nError! Not parked when we "
+					       "thought we were... (paged out "
+				               "and timer expired?)\n");
+				/* Freeze has expired */
+				write_protect(protect_file, 0); /* unprotect */
+				parked = 0;
+				printf("%.24s: un-parking\n", ctime(&now));
+			}
+			while (pause_now) {
+				pause_now=0;
+				printf("%.24s: pausing for %d seconds\n", 
+				       ctime(&now), SIGUSR1_SLEEP_SEC);
+				sleep(SIGUSR1_SLEEP_SEC);
+			}
+		}
+
+	}
+
+	munlockall();
+	return ret;
+}
================================================================


More information about the pld-cvs-commit mailing list