[PATCH 3/4] lis302d: make 'threshold' detection more reliable.

NeilBrown neilb at suse.de
Mon Nov 2 02:32:45 CET 2009


Currently setting the threshold doesn't guarantee that every change
reported will be for that size, or that every change of that size will
be reported.

If a change is very fast, it can trigger the threshold detection
circuits, but might not produce two consecutive samples which differ
by the threshold amount.  Presumably this is because the trigger
circuit is more sensitive than the sampling circuit.
So if the threshold detection reports a crossing, force the value
reported on that access to differ from the previous reading by at
least the threshold amount.

If a change is very slow (seconds) it can go undetected by the
threshold circuit due to the use of a high-pass filter.
So make sure we look at samples at least every 500ms and if a large
change is measured that the threshold detector didn't report, report
it anyway.

This will ensure that the most recent value reported differs from the
currently correct value by no more than the threshold.


Signed-off-by: NeilBrown <neilb at suse.de>
---

 drivers/input/misc/lis302dl.c |   70 +++++++++++++++++++++++++++++++++++------
 1 files changed, 60 insertions(+), 10 deletions(-)

diff --git a/drivers/input/misc/lis302dl.c b/drivers/input/misc/lis302dl.c
index 52eaccd..f9dfc24 100644
--- a/drivers/input/misc/lis302dl.c
+++ b/drivers/input/misc/lis302dl.c
@@ -243,7 +243,10 @@ static void __enable_data_collection(struct lis302dl_info *lis)
 		__lis302dl_int_mode(lis->dev, 1, LIS302DL_INTMODE_FF_WU_1);
 		__lis302dl_int_mode(lis->dev, 2, LIS302DL_INTMODE_FF_WU_1);
 
-		if (lis->sample_time)
+		if (lis->threshold && (lis->sample_time > HZ/2 ||
+				       lis->sample_time == 0))
+			mod_timer(&lis->timer, jiffies + HZ/2);
+		else if (lis->sample_time)
 			mod_timer(&lis->timer, jiffies + lis->sample_time);
 	}
 }
@@ -268,6 +271,18 @@ static void _report_btn_double(struct input_dev *inp, int btn)
 }
 #endif
 
+static int force_diff(int val, int old, int diff)
+{
+	/* We want 'val' to differ from 'old' by at least 'diff'
+	 * If it doesn't nudge it a bit in the right direction.
+	 */
+	int delta = val - old;
+	if (delta < 0 && delta > -diff)
+		val = old - diff;
+	else if (delta < diff)
+		val = old + diff;
+	return val;
+}
 
 static void lis302dl_bitbang_read_sample(struct lis302dl_info *lis)
 {
@@ -279,6 +294,7 @@ static void lis302dl_bitbang_read_sample(struct lis302dl_info *lis)
 	struct spi_transfer t;
 	u8 wakeup;
 	int need_sample = 0;
+	int x, y, z;
 
 	spi_message_init(&msg);
 	memset(&t, 0, sizeof t);
@@ -329,22 +345,56 @@ static void lis302dl_bitbang_read_sample(struct lis302dl_info *lis)
 		lis->last_sample = jiffies;
 	}
 
-	if (lis->threshold && (wakeup & LIS302DL_FFWUCFG_AOI))
+	x = mg_per_sample * (s8)read[LIS302DL_REG_OUT_X - LIS302DL_REG_STATUS];
+	y = mg_per_sample * (s8)read[LIS302DL_REG_OUT_Y - LIS302DL_REG_STATUS];
+	z = mg_per_sample * (s8)read[LIS302DL_REG_OUT_Z - LIS302DL_REG_STATUS];
+
+	if (lis->threshold && (wakeup & LIS302DL_FFWUCFG_AOI)) {
+
 		need_sample = 1;
 
+		/* Make sure the reported sample does differ from the previous
+		 * one by the given threshold */
+		if (wakeup & LIS302DL_FFWUCFG_XHIE)
+			x = force_diff(x, lis->input_dev->abs[ABS_X],
+				       lis->threshold);
+		if (wakeup & LIS302DL_FFWUCFG_YHIE)
+			y = force_diff(y, lis->input_dev->abs[ABS_Y],
+				       lis->threshold);
+		if (wakeup & LIS302DL_FFWUCFG_ZHIE)
+			z = force_diff(z, lis->input_dev->abs[ABS_Z],
+				       lis->threshold);
+
+		/* Clear the HP filter "starting point" */
+		__reg_read(lis, LIS302DL_REG_HP_FILTER_RESET);
+	} else if (abs(x - lis->input_dev->abs[ABS_X]) > lis->threshold ||
+		   abs(y - lis->input_dev->abs[ABS_Y]) > lis->threshold ||
+		   abs(z - lis->input_dev->abs[ABS_Z]) > lis->threshold) {
+		/* A difference larger than threshold.
+		 * FIXME check time delay against 'duration'
+		 */
+		need_sample = 1;
+	}
+
 	if (need_sample) {
-		input_report_abs(lis->input_dev, ABS_X, mg_per_sample *
-			    (s8)read[LIS302DL_REG_OUT_X - LIS302DL_REG_STATUS]);
-		input_report_abs(lis->input_dev, ABS_Y, mg_per_sample *
-			    (s8)read[LIS302DL_REG_OUT_Y - LIS302DL_REG_STATUS]);
-		input_report_abs(lis->input_dev, ABS_Z, mg_per_sample *
-			    (s8)read[LIS302DL_REG_OUT_Z - LIS302DL_REG_STATUS]);
+		input_report_abs(lis->input_dev, ABS_X, x);
+		input_report_abs(lis->input_dev, ABS_Y, y);
+		input_report_abs(lis->input_dev, ABS_Z, z);
 
 		input_sync(lis->input_dev);
 	}
 
-	if (lis->use_jiffies && lis->sample_time)
-		mod_timer(&lis->timer, jiffies + lis->sample_time);
+	if (lis->use_jiffies) {
+		/* If threshold is set, then always check at 2Hz as
+		 * the high-pass filter loses changes that are slower
+		 * than that.
+		 */
+		if (lis->threshold && (lis->sample_time > HZ/2 ||
+				       lis->sample_time == 0))
+			mod_timer(&lis->timer, jiffies + HZ/2);
+		else if (lis->sample_time)
+			mod_timer(&lis->timer, jiffies + lis->sample_time);
+	}
 }
 
 static irqreturn_t lis302dl_interrupt(int irq, void *_lis)





More information about the openmoko-kernel mailing list