Created
December 26, 2011 17:42
-
-
Save pamaury/1521701 to your computer and use it in GitHub Desktop.
"Natural" Touchpad Driver
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/firmware/export/config/sansafuzeplus.h b/firmware/export/config/sansafuzeplus.h | |
index 880bbae..f16f9f1 100644 | |
--- a/firmware/export/config/sansafuzeplus.h | |
+++ b/firmware/export/config/sansafuzeplus.h | |
@@ -180,7 +180,7 @@ | |
#define USE_ROCKBOX_USB | |
#define USB_VENDOR_ID 0x0781 | |
#define USB_PRODUCT_ID 0x74e1 | |
-#define HAVE_USB_HID_MOUSE | |
+#define HAVE_USB_HID_TOUCHPAD | |
#define HAVE_BOOTLOADER_USB_MODE | |
/* The fuze+ actually interesting partition table does not use 512-byte sector | |
diff --git a/firmware/target/arm/imx233/sansa-fuzeplus/button-fuzeplus.c b/firmware/target/arm/imx233/sansa-fuzeplus/button-fuzeplus.c | |
index 0250d27..41a2a97 100644 | |
--- a/firmware/target/arm/imx233/sansa-fuzeplus/button-fuzeplus.c | |
+++ b/firmware/target/arm/imx233/sansa-fuzeplus/button-fuzeplus.c | |
@@ -199,9 +199,40 @@ static struct button_area_t button_areas[] = | |
#define RMI_INTERRUPT 1 | |
+enum touchpad_mode_t | |
+{ | |
+ /* Two fingers reported, locked until no finger touch */ | |
+ MODE_TWO_FINGERS, | |
+ /* One finger report, report touch after a while and wait any transition */ | |
+ MODE_BUTTON_WAIT, | |
+ /* One finger sliding, don't interpret touch */ | |
+ MODE_SLIDING, | |
+ /* No finger reported */ | |
+ MODE_IDLE | |
+}; | |
+ | |
+/* Delay before interpreting a single touch as a button. Any move during | |
+ * that time will be interpreted as sliding */ | |
+#define TOUCHPAD_BUTTON_DELAY (HZ / 10) | |
+/* Normal delay when no action is detected (useful for low power mode) */ | |
+#define TOUCHPAD_IDLE_DELAY HZ | |
+/* Arrow subdivision */ | |
+#define TOUCHPAD_ARROW_SUBDIV 4 | |
+/* Minimum arrow distance before sliding */ | |
+#define TOUCHPAD_SLIDING_THRESH 2 | |
+ | |
+/* touchpad button bitmap for button_read_device() */ | |
static int touchpad_btns = 0; | |
-static bool two_fingers_mode = 0; | |
-static int button_delay = 0; | |
+static enum touchpad_mode_t touchpad_mode = MODE_IDLE; | |
+/* tick when the key was first hold */ | |
+static int button_first_tick = 0; | |
+static int button_detected = 0; | |
+static int next_action_tick = 0; | |
+static bool button_arrow = false; | |
+static int button_arrow_x = 0; | |
+static int button_arrow_y = 0; | |
+static int sliding_button = 0; | |
+ | |
static long rmi_stack [DEFAULT_STACK_SIZE/sizeof(long)]; | |
static const char rmi_thread_name[] = "rmi"; | |
static struct event_queue rmi_queue; | |
@@ -218,6 +249,54 @@ static int find_button(int x, int y) | |
return 0; | |
} | |
+static bool is_button_arrow(int button) | |
+{ | |
+ return button == BUTTON_LEFT || button == BUTTON_RIGHT || | |
+ button == BUTTON_DOWN || button == BUTTON_UP || button == BUTTON_SELECT; | |
+} | |
+ | |
+static void get_arrow_pos(int x, int y, int *ar_x, int *ar_y) | |
+{ | |
+ *ar_x = (x - 1500) / TOUCHPAD_ARROW_SUBDIV; | |
+ *ar_y = (y - 1500) / TOUCHPAD_ARROW_SUBDIV; | |
+} | |
+ | |
+static int find_sliding(int old_x, int old_y, int new_x, int new_y) | |
+{ | |
+ if(new_x <= old_x - TOUCHPAD_SLIDING_THRESH) | |
+ return BUTTON_LEFT; | |
+ else if(new_x >= old_x + TOUCHPAD_SLIDING_THRESH) | |
+ return BUTTON_RIGHT; | |
+ else if(new_y <= old_y - TOUCHPAD_SLIDING_THRESH) | |
+ return BUTTON_DOWN; | |
+ else if(new_y >= old_y + TOUCHPAD_SLIDING_THRESH) | |
+ return BUTTON_UP; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static int update_sliding(int old_x, int old_y, int new_x, int new_y, int sliding_button) | |
+{ | |
+ if(sliding_button == BUTTON_LEFT || sliding_button == BUTTON_RIGHT) | |
+ { | |
+ if(new_x <= old_x - TOUCHPAD_SLIDING_THRESH) | |
+ return BUTTON_LEFT; | |
+ else if(new_x >= old_x + TOUCHPAD_SLIDING_THRESH) | |
+ return BUTTON_RIGHT; | |
+ else | |
+ return 0; | |
+ } | |
+ else | |
+ { | |
+ if(new_y <= old_y - TOUCHPAD_SLIDING_THRESH) | |
+ return BUTTON_DOWN; | |
+ else if(new_y >= old_y + TOUCHPAD_SLIDING_THRESH) | |
+ return BUTTON_UP; | |
+ else | |
+ return 0; | |
+ } | |
+} | |
+ | |
static int touchpad_read_device(void) | |
{ | |
return touchpad_btns; | |
@@ -232,13 +311,17 @@ static void rmi_attn_cb(int bank, int pin) | |
queue_post(&rmi_queue, RMI_INTERRUPT, 0); | |
} | |
+#define ABS(x) ((x) < 0 ? -(x) : (x)) | |
+ | |
static void rmi_thread(void) | |
{ | |
struct queue_event ev; | |
- | |
+ | |
+ next_action_tick = current_tick + TOUCHPAD_IDLE_DELAY; | |
while(1) | |
{ | |
- queue_wait(&rmi_queue, &ev); | |
+ queue_wait_w_tmo(&rmi_queue, &ev, next_action_tick - current_tick); | |
+ next_action_tick = current_tick + TOUCHPAD_IDLE_DELAY; | |
/* handle usb connect and ignore all messages except rmi interrupts */ | |
if(ev.id == SYS_USB_CONNECTED) | |
{ | |
@@ -265,46 +348,148 @@ static void rmi_thread(void) | |
int absolute_y = u.s.absolute.y_msb << 8 | u.s.absolute.y_lsb; | |
int nr_fingers = u.s.absolute.misc & 7; | |
- /* Handle the single vs two fingers event considering the following issues: | |
- - When they are two fingers on the touchpad the signal often | |
- switch between 1 and 2 fingers. We use the bool | |
- two_fingers_mode to "lock" the two fingers's signal | |
- as long as the user doesn't release the touchpad | |
- - User can hit the device at first with only one finger while | |
- trying to do a double fingers's touch. In order to "smooth" | |
- the signal, we set a delay on single finger so that user as | |
- time to actually touch with 2 finger if he meant to. | |
- */ | |
- | |
- switch(nr_fingers) | |
+ switch(touchpad_mode) | |
{ | |
- case 2: | |
- /* enter two fingers mode */ | |
- two_fingers_mode = 1; | |
- touchpad_btns = BUTTON_TWO_FINGERS; | |
+ case MODE_TWO_FINGERS: | |
+ /* go back to idle mode when there is no finger */ | |
+ if(nr_fingers == 0) | |
+ touchpad_mode = MODE_IDLE; | |
+ touchpad_btns = (nr_fingers == 2) ? BUTTON_TWO_FINGERS : 0; | |
break; | |
- case 1: | |
- /* Ignore any touch when in two fingers mode */ | |
- if (two_fingers_mode) | |
- touchpad_btns = BUTTON_TWO_FINGERS; | |
- else | |
+ case MODE_IDLE: | |
+ touchpad_btns = 0; | |
+ /* consider the number of fingers to determine next transition */ | |
+ if(nr_fingers == 1) | |
+ { | |
+ button_detected = find_button(absolute_x, absolute_y); | |
+ button_first_tick = current_tick; | |
+ touchpad_mode = MODE_BUTTON_WAIT; | |
+ button_arrow = is_button_arrow(button_detected); | |
+ get_arrow_pos(absolute_x, absolute_y, &button_arrow_x, &button_arrow_y); | |
+ /* make sure we'll "think" again at the end of the delay */ | |
+ next_action_tick = current_tick + TOUCHPAD_BUTTON_DELAY; | |
+ } | |
+ else if(nr_fingers == 2) | |
+ { | |
+ touchpad_mode = MODE_TWO_FINGERS; | |
+ } | |
+ break; | |
+ case MODE_BUTTON_WAIT: | |
+ { | |
+ /* check number of fingers */ | |
+ if(nr_fingers == 1) | |
{ | |
- if(button_delay > 2) | |
- touchpad_btns = find_button(absolute_x, absolute_y); | |
+ /* check position */ | |
+ int new_button = find_button(absolute_x, absolute_y); | |
+ bool new_ar = is_button_arrow(new_button); | |
+ int new_ar_x, new_ar_y; | |
+ get_arrow_pos(absolute_x, absolute_y, &new_ar_x, &new_ar_y); | |
+ int dist = MAX(ABS(button_arrow_x - new_ar_x), ABS(button_arrow_y - new_ar_y)); | |
+ /* same position as before ? */ | |
+ if(new_button != button_detected) | |
+ { | |
+ button_detected = new_button; | |
+ button_arrow = new_ar; | |
+ button_arrow_x = new_ar_x; | |
+ button_arrow_y = new_ar_y; | |
+ button_first_tick = current_tick; | |
+ } | |
else | |
- button_delay++; | |
+ { | |
+ /* sliding ? */ | |
+ if(button_arrow && new_ar && dist >= TOUCHPAD_SLIDING_THRESH) | |
+ { | |
+ touchpad_mode = MODE_SLIDING; | |
+ sliding_button = find_sliding(button_arrow_x, button_arrow_y, | |
+ new_ar_x, new_ar_y); | |
+ touchpad_btns = sliding_button; | |
+ button_arrow_x = new_ar_x; | |
+ button_arrow_y = new_ar_y; | |
+ /* setup button in case sliding stops */ | |
+ button_detected = new_button; | |
+ button_first_tick = current_tick; | |
+ next_action_tick = current_tick + TOUCHPAD_BUTTON_DELAY; | |
+ } | |
+ /* time elapsed ? */ | |
+ if(current_tick - button_first_tick >= TOUCHPAD_BUTTON_DELAY) | |
+ touchpad_btns = button_detected; | |
+ else | |
+ touchpad_btns = 0; | |
+ } | |
+ } | |
+ else if(nr_fingers == 2) | |
+ { | |
+ touchpad_btns = 0; | |
+ touchpad_mode = MODE_TWO_FINGERS; | |
+ } | |
+ else | |
+ { | |
+ /* if it was on a button, it's a "tap", ie a short press */ | |
+ /* FIXME: generate tap, doesn't work, should check for timing */ | |
+ queue_post(&button_queue,button_detected,0); | |
+ touchpad_btns = 0; | |
+ touchpad_mode = MODE_IDLE; | |
} | |
break; | |
- case 0: | |
- /* reset two fingers mode and delay */ | |
- two_fingers_mode = 0; | |
- button_delay = 0; | |
+ } | |
+ case MODE_SLIDING: | |
touchpad_btns = 0; | |
+ if(nr_fingers == 1) | |
+ { | |
+ /* check position */ | |
+ int new_button = find_button(absolute_x, absolute_y); | |
+ bool new_ar = is_button_arrow(new_button); | |
+ int new_ar_x, new_ar_y; | |
+ get_arrow_pos(absolute_x, absolute_y, &new_ar_x, &new_ar_y); | |
+ /* not in the arrows or time elapsed ? no more sliding ! */ | |
+ if(!new_ar) | |
+ { | |
+ button_detected = new_button; | |
+ button_arrow = new_ar; | |
+ button_arrow_x = new_ar_x; | |
+ button_arrow_y = new_ar_y; | |
+ button_first_tick = current_tick; | |
+ touchpad_mode = MODE_BUTTON_WAIT; | |
+ next_action_tick = current_tick + TOUCHPAD_BUTTON_DELAY; | |
+ } | |
+ /* time elapsed ? */ | |
+ else if(current_tick - button_first_tick >= TOUCHPAD_BUTTON_DELAY) | |
+ { | |
+ button_detected = new_button; | |
+ button_arrow = new_ar; | |
+ button_arrow_x = new_ar_x; | |
+ button_arrow_y = new_ar_y; | |
+ touchpad_mode = MODE_BUTTON_WAIT; | |
+ next_action_tick = current_tick + TOUCHPAD_IDLE_DELAY; | |
+ touchpad_btns = button_detected; | |
+ } | |
+ /* still sliding */ | |
+ else | |
+ { | |
+ /* Allow change of direction (UP/DOWN or LEFT/RIGHT) | |
+ * or idle for a short time */ | |
+ int new_sliding = update_sliding(button_arrow_x, button_arrow_y, | |
+ new_ar_x, new_ar_y, sliding_button); | |
+ if(new_sliding != 0) | |
+ { | |
+ button_arrow_x = new_ar_x; | |
+ button_arrow_y = new_ar_y; | |
+ sliding_button = new_sliding; | |
+ button_first_tick = current_tick; | |
+ /* FIXME: generate buton repeat */ | |
+ } | |
+ } | |
+ } | |
+ else | |
+ { | |
+ touchpad_btns = 0; | |
+ touchpad_mode = (nr_fingers == 2) ? MODE_TWO_FINGERS : MODE_IDLE; | |
+ } | |
break; | |
default: | |
- break; | |
- } | |
- | |
+ touchpad_mode = MODE_IDLE; | |
+ } | |
+ | |
/* enable interrupt */ | |
imx233_setup_pin_irq(0, 27, true, true, false, &rmi_attn_cb); | |
} | |
diff --git a/firmware/usbstack/usb_hid.c b/firmware/usbstack/usb_hid.c | |
index 213f971..c6a9440 100644 | |
--- a/firmware/usbstack/usb_hid.c | |
+++ b/firmware/usbstack/usb_hid.c | |
@@ -109,6 +109,9 @@ typedef enum | |
#ifdef HAVE_USB_HID_MOUSE | |
REPORT_ID_MOUSE, | |
#endif | |
+#ifdef HAVE_USB_HID_TOUCHPAD | |
+ REPORT_ID_TOUCHPAD, | |
+#endif | |
REPORT_ID_COUNT, | |
} report_id_t; | |
@@ -552,6 +555,13 @@ static size_t descriptor_report_get(unsigned char *dest) | |
PACK_VAL(report, END_COLLECTION); | |
#endif /* HAVE_USB_HID_MOUSE */ | |
+#ifdef HAVE_USB_HID_TOUCHPAD | |
+ pack_parameter(&report, 0, 1, USAGE_PAGE, HID_USAGE_PAGE_DIGITIZER); | |
+ pack_parameter(&report, 0, 1, CONSUMER_USAGE, HID_CONSUMER_USAGE_DIGITIZER_TOUCHPAD); | |
+ pack_parameter(&report, 0, 1, COLLECTION, COLLECTION_APPLICATION); | |
+ PACK_VAL(report, END_COLLECTION); | |
+#endif | |
+ | |
return (size_t)(report - dest); | |
} | |
diff --git a/firmware/usbstack/usb_hid_usage_tables.h b/firmware/usbstack/usb_hid_usage_tables.h | |
index d23c704..9adf50e 100644 | |
--- a/firmware/usbstack/usb_hid_usage_tables.h | |
+++ b/firmware/usbstack/usb_hid_usage_tables.h | |
@@ -700,6 +700,7 @@ typedef enum usage_page | |
#define HID_CONSUMER_USAGE_AC_SPLIT 0x29A | |
#define HID_CONSUMER_USAGE_AC_DISRIBUTE_HORIZONTALLY 0x29B | |
#define HID_CONSUMER_USAGE_AC_DISTRIBUTE_VERTICALLY 0x29C | |
+#define HID_CONSUMER_USAGE_DIGITIZER_TOUCHPAD 0x5 | |
#ifdef HAVE_USB_HID_MOUSE | |
/* Mouse defines (custom made - Rockbox specific) */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment