Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Detection of configurable touch gestures

  • Loading branch information...
commit 9f57d9efc7458c1a9f540cd04bc5cb14e08fb342 1 parent a9096fd
@Tungstwenty Tungstwenty authored
View
1  arch/arm/configs/siyah_defconfig
@@ -1448,6 +1448,7 @@ CONFIG_INPUT_TOUCHSCREEN=y
# CONFIG_TOUCHSCREEN_AD7879 is not set
# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set
CONFIG_TOUCHSCREEN_ATMEL_MXT224_U1=y
+CONFIG_TOUCHSCREEN_GESTURES=y
# CONFIG_TOUCHSCREEN_ATMEL_MXT224_GRANDE is not set
# CONFIG_TOUCHSCREEN_ATMEL_MXT224_GC is not set
# CONFIG_TOUCHSCREEN_ATMEL_MXT1536E is not set
View
9 drivers/input/touchscreen/Kconfig
@@ -162,6 +162,15 @@ config TOUCHSCREEN_ATMEL_MXT224_U1
To compile this driver as a module, choose M here: the
module will be called atmel_mxt224_u1_ts.
+config TOUCHSCREEN_GESTURES
+ bool "Enable detection of touch gestures"
+ depends on TOUCHSCREEN_ATMEL_MXT224_U1
+ ---help---
+ This option enables the mxt224_u1 touchscreen device to
+ detect and report touch gestures.
+ Definition of the gesture paths and reporting of the
+ detections are available through sysfs entries.
+
config TOUCHSCREEN_ATMEL_MXT224_GRANDE
tristate "Atmel mXT I2C Touchscreen for GRANDE"
depends on I2C
View
470 drivers/input/touchscreen/mxt224_u1.c
@@ -28,6 +28,11 @@
#include <mach/cpufreq.h>
#include <linux/input/mt.h>
+#ifdef CONFIG_TOUCHSCREEN_GESTURES
+#include <linux/spinlock.h>
+#include <linux/miscdevice.h>
+#endif
+
#define OBJECT_TABLE_START_ADDRESS 7
#define OBJECT_TABLE_ELEMENT_SIZE 6
@@ -200,6 +205,40 @@ struct mxt224_data {
bool enabled;
};
+#ifdef CONFIG_TOUCHSCREEN_GESTURES
+
+#define MAX_GESTURES 10
+#define MAX_GESTURE_FINGERS MAX_USING_FINGER_NUM
+#define MAX_GESTURE_STEPS 10
+
+// Definitions
+struct gesture_point {
+ int min_x;
+ int min_y;
+ int max_x;
+ int max_y;
+};
+
+typedef struct gesture_point gesture_points_t[MAX_GESTURES][MAX_GESTURE_FINGERS][MAX_GESTURE_STEPS];
+static gesture_points_t gesture_points = { { { { 0, 0, 0, 0 } } } };
+
+typedef int gestures_step_count_t[MAX_GESTURES][MAX_GESTURE_FINGERS];
+static gestures_step_count_t gestures_step_count = { { 0 } };
+
+// Track state
+static bool gestures_detected[MAX_GESTURES] = { false };
+static bool has_gestures = false;
+
+struct gesture_finger {
+ int finger_order;
+ int current_step;
+};
+static struct gesture_finger gesture_fingers[MAX_GESTURES][MAX_GESTURE_FINGERS] = { { { -1, -1 } } };
+
+DECLARE_WAIT_QUEUE_HEAD(gestures_wq);
+static spinlock_t gestures_lock;
+#endif
+
static u8 mov_hysti = 255;
#define CLEAR_MEDIAN_FILTER_ERROR
@@ -1229,6 +1268,14 @@ static void report_input_data(struct mxt224_data *data)
u16 object_address = 0;
u16 size = 1;
u8 value;
+#ifdef CONFIG_TOUCHSCREEN_GESTURES
+ int gesture_no, finger_no;
+ int finger_pos;
+ struct gesture_point *point;
+ int step;
+ bool fingers_completed;
+ unsigned long flags;
+#endif
touch_is_pressed = 0;
@@ -1241,6 +1288,30 @@ static void report_input_data(struct mxt224_data *data)
/* for release */
if (data->fingers[i].z == TSP_STATE_RELEASE) {
+#ifdef CONFIG_TOUCHSCREEN_GESTURES
+ // When a finger is released and its movement was not completed yet, reset it
+ spin_lock_irqsave(&gestures_lock, flags);
+ for (gesture_no = 0; gesture_no < MAX_GESTURES; gesture_no++) {
+ if (gestures_detected[gesture_no])
+ // Ignore gestures already reported
+ continue;
+
+ for (finger_no = 0; finger_no < MAX_GESTURE_FINGERS; finger_no++) {
+ if (gesture_fingers[gesture_no][finger_no].finger_order == i) {
+ // Found a match for ongoing movement
+ // Reset the finger progress if path not completed
+ if (gesture_fingers[gesture_no][finger_no].current_step <
+ gestures_step_count[gesture_no][finger_no]) {
+ gesture_fingers[gesture_no][finger_no].finger_order = -1;
+ gesture_fingers[gesture_no][finger_no].current_step = -1;
+ }
+ break;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&gestures_lock, flags);
+#endif
+
input_mt_slot(data->input_dev, i);
input_mt_report_slot_state(data->input_dev,
MT_TOOL_FINGER, false);
@@ -1255,6 +1326,71 @@ static void report_input_data(struct mxt224_data *data)
continue;
}
+#ifdef CONFIG_TOUCHSCREEN_GESTURES
+ // Finger being moved, check the gesture steps progress
+ spin_lock_irqsave(&gestures_lock, flags);
+ for (gesture_no = 0; gesture_no < MAX_GESTURES; gesture_no++) {
+ if (gestures_detected[gesture_no])
+ // Ignore further movement for gestures already reported
+ continue;
+
+ // Find which finger definition this touch maps to
+ finger_pos = -1;
+ for (finger_no = 0; finger_no < MAX_GESTURE_FINGERS; finger_no++) {
+ if (gesture_fingers[gesture_no][finger_no].finger_order == i) {
+ // Found a match for ongoing movement
+ finger_pos = finger_no;
+ break;
+ }
+ }
+ if (finger_pos < 0) {
+ // This finger is not yet tracked, check the first zone it matches
+ for (finger_no = 0; finger_no < MAX_GESTURE_FINGERS; finger_no++) {
+ if (gestures_step_count[gesture_no][finger_no] < 1) {
+ // This finger definition has no steps, no more to check
+ break;
+ } else {
+ point = &gesture_points[gesture_no][finger_no][0];
+ if (gesture_fingers[gesture_no][finger_no].finger_order < 0 &&
+ data->fingers[i].x >= point->min_x &&
+ data->fingers[i].x <= point->max_x &&
+ data->fingers[i].y >= point->min_y &&
+ data->fingers[i].y <= point->max_y) {
+ // This finger definition is still pending
+ // and this touch matches the area
+ finger_pos = finger_no;
+ gesture_fingers[gesture_no][finger_pos].finger_order = i;
+ gesture_fingers[gesture_no][finger_pos].current_step = 1;
+ printk("[TSP] Gesture %d, finger %d - Associated index %d\n",
+ gesture_no, finger_pos, i);
+ break;
+ }
+ }
+ }
+ }
+ if (finger_pos >= 0) {
+ // Track next zones where the finger should move
+ for (step = gesture_fingers[gesture_no][finger_pos].current_step;
+ step < gestures_step_count[gesture_no][finger_pos];
+ step++) {
+ point = &gesture_points[gesture_no][finger_pos][step];
+ if (data->fingers[i].x >= point->min_x &&
+ data->fingers[i].x <= point->max_x &&
+ data->fingers[i].y >= point->min_y &&
+ data->fingers[i].y <= point->max_y) {
+ // Next zone reached, keep testing
+ printk("[TSP] Gesture %d, finger %d - Moved through step, next is %d\n",
+ gesture_no, finger_pos, step+1);
+ gesture_fingers[gesture_no][finger_pos].current_step++;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ spin_unlock_irqrestore(&gestures_lock, flags);
+#endif
+
input_mt_slot(data->input_dev, i);
input_mt_report_slot_state(data->input_dev,
MT_TOOL_FINGER, true);
@@ -1335,6 +1471,44 @@ static void report_input_data(struct mxt224_data *data)
}
}
+#ifdef CONFIG_TOUCHSCREEN_GESTURES
+ // Check completed gestures or reset all progress if all fingers released
+ spin_lock_irqsave(&gestures_lock, flags);
+ for (gesture_no = 0; gesture_no < MAX_GESTURES; gesture_no++) {
+ if (gestures_detected[gesture_no])
+ // Gesture already reported, skip
+ continue;
+
+ if (gestures_step_count[gesture_no][0] < 1)
+ continue; // Gesture not configured
+
+ fingers_completed = true;
+ for (finger_no = 0; finger_no < MAX_GESTURE_FINGERS; finger_no++) {
+ if (gestures_step_count[gesture_no][finger_no] > 0 &&
+ gesture_fingers[gesture_no][finger_no].current_step <
+ gestures_step_count[gesture_no][finger_no]) {
+
+ fingers_completed = false;
+ break;
+ }
+ }
+ if (fingers_completed) {
+ // All finger steps completed for this gesture, wake any consumers
+ printk("[TSP] Gesture %d completed, waking consumers\n", gesture_no);
+ gestures_detected[gesture_no] = true;
+ has_gestures = true;
+ wake_up_interruptible_all(&gestures_wq);
+ } else if (!tsp_state) {
+ // All fingers released, reset progress for all paths
+ for (finger_no = 0; finger_no < MAX_GESTURE_FINGERS; finger_no++) {
+ gesture_fingers[gesture_no][finger_no].finger_order = -1;
+ gesture_fingers[gesture_no][finger_no].current_step = -1;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&gestures_lock, flags);
+#endif
+
if (!tsp_state && copy_data->lock_status) {
exynos_cpufreq_lock_free(DVFS_LOCK_ID_TSP);
copy_data->lock_status = 0;
@@ -1966,6 +2140,40 @@ static int mxt224_internal_resume(struct mxt224_data *data)
return ret;
}
+#ifdef CONFIG_TOUCHSCREEN_GESTURES
+// Must be called with the gestures_lock spinlock held
+static void reset_gestures_detection_locked(bool including_detected)
+{
+ int gesture_no, finger_no;
+
+ for (gesture_no = 0; gesture_no < MAX_GESTURES; gesture_no++) {
+ if (gestures_detected[gesture_no] && !including_detected)
+ // Gesture already reported, skip
+ continue;
+
+ gestures_detected[gesture_no] = false;
+
+ if (gestures_step_count[gesture_no][0] < 1)
+ continue; // Gesture not configured
+
+ // Reset progress for all paths of this gesture
+ for (finger_no = 0; finger_no < MAX_GESTURE_FINGERS; finger_no++) {
+ gesture_fingers[gesture_no][finger_no].finger_order = -1;
+ gesture_fingers[gesture_no][finger_no].current_step = -1;
+ }
+ }
+}
+
+static void reset_gestures_detection(bool including_detected)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&gestures_lock, flags);
+ reset_gestures_detection_locked(including_detected);
+ spin_unlock_irqrestore(&gestures_lock, flags);
+}
+#endif
+
#ifdef CONFIG_HAS_EARLYSUSPEND
#define mxt224_suspend NULL
#define mxt224_resume NULL
@@ -1975,6 +2183,10 @@ static void mxt224_early_suspend(struct early_suspend *h)
struct mxt224_data *data = container_of(h, struct mxt224_data,
early_suspend);
+#ifdef CONFIG_TOUCHSCREEN_GESTURES
+ reset_gestures_detection(false);
+#endif
+
copy_data->mxt224_enabled = 0;
touch_is_pressed = 0;
qt_timer_state = 0;
@@ -2018,6 +2230,10 @@ static int mxt224_suspend(struct device *dev)
struct i2c_client *client = to_i2c_client(dev);
struct mxt224_data *data = i2c_get_clientdata(client);
+#ifdef CONFIG_TOUCHSCREEN_GESTURES
+ reset_gestures_detection(false);
+#endif
+
copy_data->mxt224_enabled = 0;
touch_is_pressed = 0;
/* Doing_calibration_falg = 0; */
@@ -3366,6 +3582,235 @@ static const struct attribute_group qt602240_attr_group = {
.attrs = qt602240_attrs,
};
+#ifdef CONFIG_TOUCHSCREEN_GESTURES
+static ssize_t gesture_patterns_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ char *s;
+ int gesture_no, finger_no, step;
+ struct gesture_point *point;
+
+ s = buf;
+ s += sprintf(s, "# Touch gestures\n#\n");
+ s += sprintf(s, "# Syntax\n");
+ s += sprintf(s, "# <gesture_no>:<finger_no>:(x_min|x_max,y_min|y_max)\n");
+ s += sprintf(s, "# ...\n");
+ s += sprintf(s, "# gesture_no: 1 to %d\n", MAX_GESTURES);
+ s += sprintf(s, "# finger_no : 1 to %d\n", MAX_GESTURE_FINGERS);
+ s += sprintf(s, "# max steps per gesture and finger: %d\n\n", MAX_GESTURE_STEPS);
+
+ // No special need for thread safety, at worst there might be incoherent definitions output
+ for (gesture_no = 0; gesture_no < MAX_GESTURES; gesture_no++) {
+ if (gestures_step_count[gesture_no][0] < 1)
+ continue; // Gesture not configured
+ s += sprintf(s, "# Gesture %d:\n", gesture_no+1);
+ for (finger_no = 0; finger_no < MAX_GESTURE_FINGERS; finger_no++) {
+ for (step = 0; step < gestures_step_count[gesture_no][finger_no]; step++) {
+ point = &gesture_points[gesture_no][finger_no][step];
+ s += sprintf(s, "%d:%d:(%d|%d,%d|%d)\n", gesture_no+1, finger_no+1,
+ point->min_x, point->max_x,
+ point->min_y, point->max_y);
+ }
+ }
+ }
+ return strlen(buf);
+}
+
+static ssize_t gesture_patterns_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ gesture_points_t *tmp_gesture_points;
+ gestures_step_count_t *tmp_gestures_step_count;
+ unsigned long flags;
+ int res;
+ int gesture_no, finger_no, min_x, max_x, min_y, max_y;
+ struct gesture_point *point;
+ int step;
+
+ tmp_gesture_points = kmalloc(sizeof(*tmp_gesture_points), GFP_KERNEL);
+ if (!tmp_gesture_points)
+ return -ENOMEM;
+ tmp_gestures_step_count = kmalloc(sizeof(*tmp_gestures_step_count), GFP_KERNEL);
+ if (!tmp_gestures_step_count) {
+ kfree(tmp_gesture_points);
+ return -ENOMEM;
+ }
+ for (gesture_no = 0; gesture_no < MAX_GESTURES; gesture_no++) {
+ for (finger_no = 0; finger_no < MAX_GESTURE_FINGERS; finger_no++) {
+ for (step = 0; step < MAX_GESTURE_STEPS; step++) {
+ point = &(*tmp_gesture_points)[gesture_no][finger_no][step];
+ point->min_x = 0;
+ point->max_x = 0;
+ point->min_y = 0;
+ point->max_y = 0;
+ }
+ (*tmp_gestures_step_count)[gesture_no][finger_no] = 0;
+ }
+ }
+
+ for (;;) {
+ // Skip all whitespace and comment lines
+ for (;;) {
+ while (*buf == ' ' || *buf == '\t' || *buf == '\r' || *buf == '\n')
+ buf++;
+
+ if (*buf == '\0') {
+ // EOF
+ goto finalize;
+ } else if (*buf == '#') {
+ // Comment line
+ buf = strstr(buf, "\n");
+ if (!buf)
+ goto finalize; // No more data
+ } else {
+ break;
+ }
+ }
+
+ res = sscanf(buf, "%d:%d:(%d|%d,%d|%d)", &gesture_no, &finger_no, &min_x, &max_x, &min_y, &max_y);
+ if (res != 6) {
+ printk("[TSP] Only %d gesture tokens read from buffer: %s\n", res, buf);
+ kfree(tmp_gestures_step_count);
+ kfree(tmp_gesture_points);
+ return -EINVAL; // Invalid line format
+ }
+
+ // Validate args boundary
+ if (gesture_no <= 0 || gesture_no > MAX_GESTURES) {
+ kfree(tmp_gestures_step_count);
+ kfree(tmp_gesture_points);
+ return -ENOMEM; // Too many gestures
+ }
+ gesture_no--;
+ if (finger_no <= 0 || finger_no > MAX_GESTURE_FINGERS) {
+ kfree(tmp_gestures_step_count);
+ kfree(tmp_gesture_points);
+ return -ENOMEM; // Too many fingers
+ }
+ finger_no--;
+ if ((*tmp_gestures_step_count)[gesture_no][finger_no] >= MAX_GESTURE_STEPS) {
+ kfree(tmp_gestures_step_count);
+ kfree(tmp_gesture_points);
+ return -ENOMEM; // Too many steps
+ }
+
+ step = (*tmp_gestures_step_count)[gesture_no][finger_no]++;
+ point = &(*tmp_gesture_points)[gesture_no][finger_no][step];
+ point->min_x = min_x;
+ point->max_x = max_x;
+ point->min_y = min_y;
+ point->max_y = max_y;
+ buf = strstr(buf, ")");
+ if (!buf)
+ goto finalize; // No more data
+ else
+ buf++;
+
+ // Continue to next input contents
+ }
+
+finalize:
+ spin_lock_irqsave(&gestures_lock, flags);
+ reset_gestures_detection_locked(true);
+ memcpy(&gesture_points, tmp_gesture_points, sizeof(*tmp_gesture_points));
+ memcpy(&gestures_step_count, tmp_gestures_step_count, sizeof(*tmp_gestures_step_count));
+ spin_unlock_irqrestore(&gestures_lock, flags);
+
+ kfree(tmp_gestures_step_count);
+ kfree(tmp_gesture_points);
+ return size;
+}
+
+static ssize_t wait_for_gesture_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ int gesture_no, finger_no;
+ char *s;
+ int detected_gesture;
+ bool has_more_gestures;
+ unsigned long flags;
+
+ // Does not stop if there's a gesture already reported
+ ret = wait_event_interruptible(gestures_wq,
+ has_gestures);
+ if (ret)
+ return ret; // Interrupted
+
+ s = buf;
+ spin_lock_irqsave(&gestures_lock, flags);
+ for (gesture_no = 0; gesture_no < MAX_GESTURES; gesture_no++) {
+ if (gestures_detected[gesture_no]) {
+ detected_gesture = gesture_no;
+
+ has_more_gestures = false;
+ while (++gesture_no < MAX_GESTURES) {
+ if (gestures_detected[gesture_no]) {
+ has_more_gestures = true;
+ break;
+ }
+ }
+ // In most cases, additional waiting clients will not have run yet and will
+ // keep waiting if this goes back to false
+ has_gestures = has_more_gestures;
+
+ // Reset detection of this gesture
+ for (finger_no = 0; finger_no < MAX_GESTURE_FINGERS; finger_no++) {
+ gesture_fingers[detected_gesture][finger_no].finger_order = -1;
+ gesture_fingers[detected_gesture][finger_no].current_step = -1;
+ }
+ gestures_detected[detected_gesture] = false;
+
+ spin_unlock_irqrestore(&gestures_lock, flags);
+
+ s += sprintf(buf, "%d", detected_gesture + 1);
+ return s - buf;
+ }
+ }
+ spin_unlock_irqrestore(&gestures_lock, flags);
+
+ // Despite waking up, no gestures were detected
+ s += sprintf(buf, "0");
+ return s - buf;
+}
+
+static ssize_t wait_for_gesture_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ if (!strncmp(buf, "reset", 5)) {
+ // Clear any pending gestures and reset detection
+ reset_gestures_detection(true);
+ return size;
+
+ } else {
+ return -EINVAL;
+ }
+}
+
+static DEVICE_ATTR(gesture_patterns, S_IRUGO | S_IWUSR,
+ gesture_patterns_show, gesture_patterns_store);
+static DEVICE_ATTR(wait_for_gesture, S_IRUGO | S_IWUSR,
+ wait_for_gesture_show, wait_for_gesture_store);
+
+static struct attribute *gestures_attrs[] = {
+ &dev_attr_gesture_patterns.attr,
+ &dev_attr_wait_for_gesture.attr,
+ NULL
+};
+
+static const struct attribute_group gestures_attr_group = {
+ .attrs = gestures_attrs,
+};
+
+static struct miscdevice gestures_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "touch_gestures",
+};
+static bool gestures_device_registered = false;
+#endif
+
static int __devinit mxt224_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -3399,6 +3844,10 @@ static int __devinit mxt224_probe(struct i2c_client *client,
if (!data)
return -ENOMEM;
+#ifdef CONFIG_TOUCHSCREEN_GESTURES
+ spin_lock_init(&gestures_lock);
+#endif
+
data->num_fingers = pdata->max_finger_touches;
data->power_on = pdata->power_on;
data->power_off = pdata->power_off;
@@ -3652,6 +4101,20 @@ static int __devinit mxt224_probe(struct i2c_client *client,
if (ret)
printk(KERN_ERR "[TSP] sysfs_create_group()is falled\n");
+#ifdef CONFIG_TOUCHSCREEN_GESTURES
+ ret = misc_register(&gestures_device);
+ if (ret) {
+ printk(KERN_ERR "[TSP] gestures misc_register failed.\n");
+ } else {
+ if (sysfs_create_group(&gestures_device.this_device->kobj, &gestures_attr_group)) {
+ printk(KERN_ERR "[TSP] sysfs_create_group() has failed\n");
+ misc_deregister(&gestures_device);
+ } else {
+ gestures_device_registered = true;
+ }
+ }
+#endif
+
#ifdef ENABLE_NOISE_TEST_MODE
/*
20110222 N1_firmware_sync
@@ -3869,6 +4332,13 @@ static int __init mxt224_init(void)
static void __exit mxt224_exit(void)
{
+#ifdef CONFIG_TOUCHSCREEN_GESTURES
+ if (gestures_device_registered) {
+ sysfs_remove_group(&gestures_device.this_device->kobj, &gestures_attr_group);
+ misc_deregister(&gestures_device);
+ gestures_device_registered = false;
+ }
+#endif
i2c_del_driver(&mxt224_i2c_driver);
}
Please sign in to comment.
Something went wrong with that request. Please try again.