Home
Reading
Searching
Subscribe
Sponsors
Statistics
Posting
Contact
Spam
Lists
Links
About
Hosting
Filtering
Features Download
Marketing
Archives
FAQ
Blog
 
Gmane
From: Shem Multinymous <multinymous-Re5JQEeQqe8AvxtiuMwx3w <at> public.gmane.org>
Subject: hdapsd with input device readouts
Newsgroups: gmane.linux.drivers.hdaps.devel
Date: Saturday 28th July 2007 04:30:44 UTC (over 10 years ago)
Hi Jon,

Here's a version of hdapsd (patch below, full hdapsd.c file attached).

It includes several changes:

- Read the accelerometer data from the input device provided by
tp_smapi>=0.32, instead of sysfs. This reduce timer interrupts and
improves temporal synchronization of userspace<->kernel vs.
kernel<->hardware. If the input device doesn't exist, it falls back to
sysfs.

- Add a new new --poll-sysfs (-y) parameter to force using sysfs
instead of the input device.

- Read the sampling rate from the sysfs file provided by tp_smapi (if
it exists).

- Add a --dry-run (-t) parameter, telling hdapsd to do everything
except actually parking the drive. Very useful for testing.

- Fix the "char reason[3]" stack corruption bug identified by Dylan
Taft and discussed yesterday.

- Tweaks to some comments and an arithmetic expression.

I can break these into separate patches if needed, but it's all pretty
staightforward.


Usage:

In order to use the new-and-improved input device interface, you need
tp_smapi >= 0.32. Also, you need to add a udev rule to help hdapsd
find the input device. Do this:

# echo 'KERNEL=="event[0-9]*", ATTRS{phys}=="hdaps/input1",
ATTRS{modalias}=="input:b0019v1014p5054e4801-*",
SYMLINK+="input/hdaps/accelerometer-event"' >
/etc/udev/rules.d/51-hdaps.rules

Then reboot or run "/sbin/udevtrigger", and verify that the
"/dev/input/hdaps/accelerometer-event" symlink exists.


  Shem


--- hdapsd-20070524.c	2007-05-24 10:25:16.000000000 -0400
+++ hdapsd.c	2007-07-28 00:02:03.000000000 -0400
@@ -37,19 +37,21 @@
 #include 
 #include 
 #include 
+#include 

 #define PID_FILE                "/var/run/hdapsd.pid"
 #define SYSFS_POSITION_FILE	    "/sys/devices/platform/hdaps/position"
 #define MOUSE_ACTIVITY_FILE    
"/sys/devices/platform/hdaps/keyboard_activity"
 #define KEYBD_ACTIVITY_FILE    
"/sys/devices/platform/hdaps/mouse_activity"
+#define SAMPLING_RATE_FILE     
"/sys/devices/platform/hdaps/sampling_rate"
+#define POSITION_INPUTDEV       "/dev/input/hdaps/accelerometer-event"
 #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 DEFAULT_SAMPLING_RATE   50   /* default 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.
  */
@@ -76,8 +78,13 @@

 static int verbose = 0;
 static int pause_now = 0;
+static int dry_run = 0;
+static int poll_sysfs = 0;
+static int sampling_rate;

 char pid_file[BUF_LEN] = "";
+int hdaps_input_fd = 0;
+

 /*
  * slurp_file - read the content of a file (up to BUF_LEN-1) into a
string.
@@ -115,9 +122,11 @@ static int slurp_file(const char* filena
 }

 /*
- * read_position() - read the (x,y) position pair from hdaps.
+ * read_position_from_sysfs() - read the (x,y) position pair from
hdaps via sysfs files
+ * This method is not recommended for frequent polling, since it
causes unnecessary interrupts
+ * and a phase difference between hdaps-to-EC polling vs.
hdapsd-to-hdaps polling.
  */
-static int read_position (int *x, int *y)
+static int read_position_from_sysfs (int *x, int *y)
 {
 	char buf[BUF_LEN];
 	int ret;
@@ -152,6 +161,55 @@ static int get_km_activity()
 	return 0;
 }

+
+/*
+ * read_position_from_inputdev() - read the (x,y) position pair and
time from hdaps
+ * via the hdaps input device. Blocks there is a change in position.
+ * The x and y arguments should contain the last read values, since
if one of them
+ * doesn't change it will not be assigned.
+ */
+static int read_position_from_inputdev (int *x, int *y, double *utime)
+{
+	struct input_event ev;
+	int len, done = 0;
+	*utime = 0;
+	while (1) {
+		len = read(hdaps_input_fd, &ev, sizeof(struct input_event));
+		if (len < 0) {
+			fprintf(stderr, "ERROR: failed reading %s (%s).\n",
POSITION_INPUTDEV, strerror(errno));
+			return len;
+		}
+		if (len < (int)sizeof(struct input_event)) {
+			fprintf(stderr, "ERROR: short read from %s (%d bytes).\n",
POSITION_INPUTDEV, len);
+			return -EIO;
+		}
+		switch (ev.type) {
+			case EV_ABS: /* new X or Y */
+				switch (ev.code) {
+					case ABS_X:
+						*x = ev.value;
+						break;
+					case ABS_Y:
+						*y = ev.value;
+						break;
+					default:
+						continue;
+				}
+				break;
+			case EV_SYN: /* X and Y now reflect latest measurement */
+				done = 1;
+				break;
+			default:
+				continue;
+		}
+		if (!*utime) /* first event's time is closest to reality */
+			*utime = ev.time.tv_sec + ev.time.tv_usec/1000000.0;
+		if (done)
+			return 0;
+	}
+}
+
+
 /*
  * write_protect() - park/unpark
  */
@@ -160,6 +218,9 @@ static int write_protect (const char *pa
 	int fd, ret;
 	char buf[BUF_LEN];

+	if (dry_run)
+		return 0;
+
 	snprintf(buf, BUF_LEN, "%d", val);

 	fd = open (path, O_WRONLY);
@@ -229,6 +290,8 @@ void usage()
 	printf("                                     in background.\n");
 	printf("                                     If  is not
specified,\n");
 	printf("                                     it's set to %s.\n",
PID_FILE);
+	printf("   -t --dry-run                      Don't actually park the
drive.\n");
+	printf("   -y --poll-sysfs                   Force use of sysfs
interface to accelerometer.\n");
 	printf("\n");
 	printf("You can send SIGUSR1 to deactivate hdapsd for %d seconds.\n",
 		SIGUSR1_SLEEP_SEC);
@@ -278,14 +341,14 @@ int analyze(int x, int y, double unow, d
 	static int history = 0; /* how many recent valid samples? */
 	static double adaptive_threshold = -1; /* current adaptive thresh */
 	static int last_thresh_change = 0; /* last adaptive thresh change */
-	static int last_near_thresh = 0;      /* last time we near thresh */
+	static int last_near_thresh = 0; /* last time we were near thresh */
 	static int last_km_activity; /* last time kbd/mouse activity seen */

 	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;
 	double threshold; /* transient threshold for this iteration */
-	char reason[3];
+	char reason[4]; /* "which threshold reached?" string for verbose */
 	int recently_near_thresh;
 	int above=0, near=0; /* above threshold, near threshold */

@@ -329,7 +392,7 @@ int analyze(int x, int y, double unow, d

 	/* 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 */
+	exp_weight = 1 - 1.0/(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;
@@ -401,11 +464,11 @@ int analyze(int x, int y, double unow, d
 int main (int argc, char** argv)
 {
 	int c, park_now;
-	int x, y;
+	int x=0, y=0;
 	int fd, i, ret, threshold = 0, background = 0, adaptive=0,
 	  pidfile = 0, parked = 0;
 	char protect_file[BUF_LEN] = "";
-	double unow, parked_utime = 0;
+	double unow = 0, parked_utime = 0;
 	time_t now;

 	for (;;) {
@@ -418,10 +481,12 @@ int main (int argc, char** argv)
 			{"verbose", no_argument, NULL, 'v'},
 			{"background", no_argument, NULL, 'b'},
 			{"pidfile", optional_argument, NULL, 'p'},
+			{"dry-run", no_argument, NULL, 't'},
+			{"poll-sysfs", no_argument, NULL, 'y'},
 			{NULL, 0, NULL, 0}
 		};

-		c = getopt_long(argc, argv, "d:s:vbap::", longopts, NULL);
+		c = getopt_long(argc, argv, "d:s:vbap::ty", longopts, NULL);
 		if (c < 0)
 			break;
 		switch (c) {
@@ -449,6 +514,13 @@ int main (int argc, char** argv)
 					snprintf(pid_file, BUF_LEN, "%s", optarg);
 				}
 				break;
+			case 't':
+				printf("Dry run, will not actually park heads or freeze queue.\n");
+				dry_run = 1;
+				break;
+			case 'y':
+				poll_sysfs = 1;
+				break;
 			default:
 				usage();
 				break;
@@ -458,6 +530,20 @@ int main (int argc, char** argv)
 	if (!threshold || !strlen(protect_file))
 		usage(argv);

+	if (!poll_sysfs) {
+		hdaps_input_fd = open(POSITION_INPUTDEV, O_RDONLY);
+		if (hdaps_input_fd<0) {
+			fprintf(stderr,
+			        "WARNING: Cannot open hdaps position input file %s (%s). "
+			        "You may be using an incompatible version of the hdaps module,
"
+			        "or missing the required udev rule. "
+			        "Falling back to reading the position from sysfs (uses
more power). "
+			        "Use '-y' to silence this warning.\n",
+			        POSITION_INPUTDEV, strerror(errno));
+			poll_sysfs = 1;
+		}
+	}
+
 	if (background) {
 		verbose = 0;
 		if (pidfile) {
@@ -488,6 +574,7 @@ int main (int argc, char** argv)
 	if (verbose) {
 		printf("protect_file: %s\n", protect_file);
 		printf("threshold: %i\n", threshold);
+		printf("read_method: %s\n", poll_sysfs ? "poll-sysfs" : "input-dev");
 	}

 	/* check the protect attribute exists */
@@ -506,15 +593,22 @@ int main (int argc, char** argv)

 	/* 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);
+	ret = read_position_from_sysfs (&x, &y);
 	if (background)
 		for (i=0; ret && i < 100; ++i) {
 			usleep (100000);	/* 10 Hz */
-			ret = read_position (&x, &y);
+			ret = read_position_from_sysfs (&x, &y);
 		}
 	if (ret)
 		return 1;

+    /* adapt to the driver's sampling rate */
+	sampling_rate = read_int(SAMPLING_RATE_FILE);
+	if (sampling_rate <= 0)
+		sampling_rate = DEFAULT_SAMPLING_RATE;;
+	if (verbose)
+		printf("sampling_rate: %d\n", sampling_rate);
+
 	signal(SIGUSR1, SIGUSR1_handler);

 	if (background && pidfile) {
@@ -522,14 +616,31 @@ int main (int argc, char** argv)
 	}

 	while (1) {
-		usleep (1000000/FREQ_HZ);
+		if (poll_sysfs) {
+			usleep (1000000/sampling_rate);
+			ret = read_position_from_sysfs (&x, &y);
+			unow = get_utime(); /* microsec */
+		} else {
+			double oldunow = unow;
+			int oldx = x, oldy = y;
+			ret = read_position_from_inputdev (&x, &y, &unow);
+
+			/* The input device issues events only when the position changed.
+			 * The analysis state needs to know how long the position remained
+			 * unchanged, so send analyze() a fake retroactive update before
sending
+			 * the new one. */
+			if (!ret && !oldunow && unow-oldunow > 1.5/sampling_rate)
+				analyze(oldx, oldy, unow-1.0/sampling_rate, threshold, adaptive,
parked);
+				
+		}

-		ret = read_position (&x, &y);
-		if (ret)
+		if (ret) {
+			if (verbose)
+				printf("readout error (%d)\n", ret);
 			continue;
+		}

 		now = time((time_t *)NULL); /* sec */
-		unow = get_utime(); /* microsec */

 		park_now = analyze(x, y, unow, threshold, adaptive, parked);

@@ -551,7 +662,7 @@ int main (int argc, char** argv)
 			if (parked &&
 			    (pause_now || unow>parked_utime+FREEZE_SECONDS)) {
 				/* Sanity check */
-				if (!read_int(protect_file))
+				if (!dry_run && !read_int(protect_file))
 					printf("\nError! Not parked when we "
 					       "thought we were... (paged out "
 				               "and timer expired?)\n");
 
CD: 3ms