summaryrefslogtreecommitdiffstats
path: root/drivers/input/mouse
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/mouse')
-rw-r--r--drivers/input/mouse/alps.c699
-rw-r--r--drivers/input/mouse/alps.h60
-rw-r--r--drivers/input/mouse/elantech.c148
-rw-r--r--drivers/input/mouse/elantech.h3
-rw-r--r--drivers/input/mouse/psmouse-base.c2
-rw-r--r--drivers/input/mouse/synaptics.c140
-rw-r--r--drivers/input/mouse/synaptics.h11
-rw-r--r--drivers/input/mouse/synaptics_usb.c6
-rw-r--r--drivers/input/mouse/trackpoint.c3
9 files changed, 799 insertions, 273 deletions
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index fb15c64ffb95..35a49bf57227 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -99,6 +99,8 @@ static const struct alps_nibble_commands alps_v6_nibble_commands[] = {
#define ALPS_FOUR_BUTTONS 0x40 /* 4 direction button present */
#define ALPS_PS2_INTERLEAVED 0x80 /* 3-byte PS/2 packet interleaved with
6-byte ALPS packet */
+#define ALPS_IS_RUSHMORE 0x100 /* device is a rushmore */
+#define ALPS_BUTTONPAD 0x200 /* device is a clickpad */
static const struct alps_model_info alps_model_data[] = {
{ { 0x32, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */
@@ -281,11 +283,10 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse)
*
* The bitmaps don't have enough data to track fingers, so this function
* only generates points representing a bounding box of at most two contacts.
- * These two points are returned in x1, y1, x2, and y2.
+ * These two points are returned in fields->mt.
*/
static void alps_process_bitmap_dolphin(struct alps_data *priv,
- struct alps_fields *fields,
- int *x1, int *y1, int *x2, int *y2)
+ struct alps_fields *fields)
{
int box_middle_x, box_middle_y;
unsigned int x_map, y_map;
@@ -308,8 +309,6 @@ static void alps_process_bitmap_dolphin(struct alps_data *priv,
if (x_msb > priv->x_bits || y_msb > priv->y_bits)
return;
- *x1 = *y1 = *x2 = *y2 = 0;
-
if (fields->fingers > 1) {
start_bit = priv->x_bits - x_msb;
end_bit = priv->x_bits - x_lsb;
@@ -320,10 +319,35 @@ static void alps_process_bitmap_dolphin(struct alps_data *priv,
end_bit = y_msb - 1;
box_middle_y = (priv->y_max * (start_bit + end_bit)) /
(2 * (priv->y_bits - 1));
- *x1 = fields->x;
- *y1 = fields->y;
- *x2 = 2 * box_middle_x - *x1;
- *y2 = 2 * box_middle_y - *y1;
+ fields->mt[0] = fields->st;
+ fields->mt[1].x = 2 * box_middle_x - fields->mt[0].x;
+ fields->mt[1].y = 2 * box_middle_y - fields->mt[0].y;
+ }
+}
+
+static void alps_get_bitmap_points(unsigned int map,
+ struct alps_bitmap_point *low,
+ struct alps_bitmap_point *high,
+ int *fingers)
+{
+ struct alps_bitmap_point *point;
+ int i, bit, prev_bit = 0;
+
+ point = low;
+ for (i = 0; map != 0; i++, map >>= 1) {
+ bit = map & 1;
+ if (bit) {
+ if (!prev_bit) {
+ point->start_bit = i;
+ point->num_bits = 0;
+ (*fingers)++;
+ }
+ point->num_bits++;
+ } else {
+ if (prev_bit)
+ point = high;
+ }
+ prev_bit = bit;
}
}
@@ -334,71 +358,21 @@ static void alps_process_bitmap_dolphin(struct alps_data *priv,
*
* The bitmaps don't have enough data to track fingers, so this function
* only generates points representing a bounding box of all contacts.
- * These points are returned in x1, y1, x2, and y2 when the return value
+ * These points are returned in fields->mt when the return value
* is greater than 0.
*/
static int alps_process_bitmap(struct alps_data *priv,
- unsigned int x_map, unsigned int y_map,
- int *x1, int *y1, int *x2, int *y2)
+ struct alps_fields *fields)
{
- struct alps_bitmap_point {
- int start_bit;
- int num_bits;
- };
-
- int fingers_x = 0, fingers_y = 0, fingers;
- int i, bit, prev_bit;
+ int i, fingers_x = 0, fingers_y = 0, fingers;
struct alps_bitmap_point x_low = {0,}, x_high = {0,};
struct alps_bitmap_point y_low = {0,}, y_high = {0,};
- struct alps_bitmap_point *point;
- if (!x_map || !y_map)
+ if (!fields->x_map || !fields->y_map)
return 0;
- *x1 = *y1 = *x2 = *y2 = 0;
-
- prev_bit = 0;
- point = &x_low;
- for (i = 0; x_map != 0; i++, x_map >>= 1) {
- bit = x_map & 1;
- if (bit) {
- if (!prev_bit) {
- point->start_bit = i;
- fingers_x++;
- }
- point->num_bits++;
- } else {
- if (prev_bit)
- point = &x_high;
- else
- point->num_bits = 0;
- }
- prev_bit = bit;
- }
-
- /*
- * y bitmap is reversed for what we need (lower positions are in
- * higher bits), so we process from the top end.
- */
- y_map = y_map << (sizeof(y_map) * BITS_PER_BYTE - priv->y_bits);
- prev_bit = 0;
- point = &y_low;
- for (i = 0; y_map != 0; i++, y_map <<= 1) {
- bit = y_map & (1 << (sizeof(y_map) * BITS_PER_BYTE - 1));
- if (bit) {
- if (!prev_bit) {
- point->start_bit = i;
- fingers_y++;
- }
- point->num_bits++;
- } else {
- if (prev_bit)
- point = &y_high;
- else
- point->num_bits = 0;
- }
- prev_bit = bit;
- }
+ alps_get_bitmap_points(fields->x_map, &x_low, &x_high, &fingers_x);
+ alps_get_bitmap_points(fields->y_map, &y_low, &y_high, &fingers_y);
/*
* Fingers can overlap, so we use the maximum count of fingers
@@ -407,58 +381,91 @@ static int alps_process_bitmap(struct alps_data *priv,
fingers = max(fingers_x, fingers_y);
/*
- * If total fingers is > 1 but either axis reports only a single
- * contact, we have overlapping or adjacent fingers. For the
- * purposes of creating a bounding box, divide the single contact
- * (roughly) equally between the two points.
+ * If an axis reports only a single contact, we have overlapping or
+ * adjacent fingers. Divide the single contact between the two points.
*/
- if (fingers > 1) {
- if (fingers_x == 1) {
- i = x_low.num_bits / 2;
- x_low.num_bits = x_low.num_bits - i;
- x_high.start_bit = x_low.start_bit + i;
- x_high.num_bits = max(i, 1);
- } else if (fingers_y == 1) {
- i = y_low.num_bits / 2;
- y_low.num_bits = y_low.num_bits - i;
- y_high.start_bit = y_low.start_bit + i;
- y_high.num_bits = max(i, 1);
- }
+ if (fingers_x == 1) {
+ i = (x_low.num_bits - 1) / 2;
+ x_low.num_bits = x_low.num_bits - i;
+ x_high.start_bit = x_low.start_bit + i;
+ x_high.num_bits = max(i, 1);
+ }
+ if (fingers_y == 1) {
+ i = (y_low.num_bits - 1) / 2;
+ y_low.num_bits = y_low.num_bits - i;
+ y_high.start_bit = y_low.start_bit + i;
+ y_high.num_bits = max(i, 1);
}
- *x1 = (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) /
- (2 * (priv->x_bits - 1));
- *y1 = (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) /
- (2 * (priv->y_bits - 1));
-
- if (fingers > 1) {
- *x2 = (priv->x_max *
- (2 * x_high.start_bit + x_high.num_bits - 1)) /
- (2 * (priv->x_bits - 1));
- *y2 = (priv->y_max *
- (2 * y_high.start_bit + y_high.num_bits - 1)) /
- (2 * (priv->y_bits - 1));
+ fields->mt[0].x =
+ (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) /
+ (2 * (priv->x_bits - 1));
+ fields->mt[0].y =
+ (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) /
+ (2 * (priv->y_bits - 1));
+
+ fields->mt[1].x =
+ (priv->x_max * (2 * x_high.start_bit + x_high.num_bits - 1)) /
+ (2 * (priv->x_bits - 1));
+ fields->mt[1].y =
+ (priv->y_max * (2 * y_high.start_bit + y_high.num_bits - 1)) /
+ (2 * (priv->y_bits - 1));
+
+ /* y-bitmap order is reversed, except on rushmore */
+ if (!(priv->flags & ALPS_IS_RUSHMORE)) {
+ fields->mt[0].y = priv->y_max - fields->mt[0].y;
+ fields->mt[1].y = priv->y_max - fields->mt[1].y;
}
return fingers;
}
-static void alps_set_slot(struct input_dev *dev, int slot, bool active,
- int x, int y)
+static void alps_set_slot(struct input_dev *dev, int slot, int x, int y)
{
input_mt_slot(dev, slot);
- input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
- if (active) {
- input_report_abs(dev, ABS_MT_POSITION_X, x);
- input_report_abs(dev, ABS_MT_POSITION_Y, y);
- }
+ input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
+ input_report_abs(dev, ABS_MT_POSITION_X, x);
+ input_report_abs(dev, ABS_MT_POSITION_Y, y);
}
-static void alps_report_semi_mt_data(struct input_dev *dev, int num_fingers,
- int x1, int y1, int x2, int y2)
+static void alps_report_mt_data(struct psmouse *psmouse, int n)
{
- alps_set_slot(dev, 0, num_fingers != 0, x1, y1);
- alps_set_slot(dev, 1, num_fingers == 2, x2, y2);
+ struct alps_data *priv = psmouse->private;
+ struct input_dev *dev = psmouse->dev;
+ struct alps_fields *f = &priv->f;
+ int i, slot[MAX_TOUCHES];
+
+ input_mt_assign_slots(dev, slot, f->mt, n);
+ for (i = 0; i < n; i++)
+ alps_set_slot(dev, slot[i], f->mt[i].x, f->mt[i].y);
+
+ input_mt_sync_frame(dev);
+}
+
+static void alps_report_semi_mt_data(struct psmouse *psmouse, int fingers)
+{
+ struct alps_data *priv = psmouse->private;
+ struct input_dev *dev = psmouse->dev;
+ struct alps_fields *f = &priv->f;
+
+ /* Use st data when we don't have mt data */
+ if (fingers < 2) {
+ f->mt[0].x = f->st.x;
+ f->mt[0].y = f->st.y;
+ fingers = f->pressure > 0 ? 1 : 0;
+ }
+
+ alps_report_mt_data(psmouse, (fingers <= 2) ? fingers : 2);
+
+ input_mt_report_finger_count(dev, fingers);
+
+ input_report_key(dev, BTN_LEFT, f->left);
+ input_report_key(dev, BTN_RIGHT, f->right);
+ input_report_key(dev, BTN_MIDDLE, f->middle);
+
+ input_report_abs(dev, ABS_PRESSURE, f->pressure);
+
+ input_sync(dev);
}
static void alps_process_trackstick_packet_v3(struct psmouse *psmouse)
@@ -532,7 +539,7 @@ static void alps_decode_buttons_v3(struct alps_fields *f, unsigned char *p)
f->ts_middle = !!(p[3] & 0x40);
}
-static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
+static int alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
struct psmouse *psmouse)
{
f->first_mp = !!(p[4] & 0x40);
@@ -546,24 +553,31 @@ static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
((p[2] & 0x7f) << 1) |
(p[4] & 0x01);
- f->x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
+ f->st.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
((p[0] & 0x30) >> 4);
- f->y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
- f->z = p[5] & 0x7f;
+ f->st.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
+ f->pressure = p[5] & 0x7f;
alps_decode_buttons_v3(f, p);
+
+ return 0;
}
-static void alps_decode_rushmore(struct alps_fields *f, unsigned char *p,
+static int alps_decode_rushmore(struct alps_fields *f, unsigned char *p,
struct psmouse *psmouse)
{
alps_decode_pinnacle(f, p, psmouse);
+ /* Rushmore's packet decode has a bit difference with Pinnacle's */
+ f->is_mp = !!(p[5] & 0x40);
+ f->fingers = max((p[5] & 0x3), ((p[5] >> 2) & 0x3)) + 1;
f->x_map |= (p[5] & 0x10) << 11;
f->y_map |= (p[5] & 0x20) << 6;
+
+ return 0;
}
-static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p,
+static int alps_decode_dolphin(struct alps_fields *f, unsigned char *p,
struct psmouse *psmouse)
{
u64 palm_data = 0;
@@ -573,9 +587,9 @@ static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p,
f->is_mp = !!(p[0] & 0x20);
if (!f->is_mp) {
- f->x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7));
- f->y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3));
- f->z = (p[0] & 4) ? 0 : p[5] & 0x7f;
+ f->st.x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7));
+ f->st.y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3));
+ f->pressure = (p[0] & 4) ? 0 : p[5] & 0x7f;
alps_decode_buttons_v3(f, p);
} else {
f->fingers = ((p[0] & 0x6) >> 1 |
@@ -596,19 +610,21 @@ static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p,
f->x_map = (palm_data >> priv->y_bits) &
(BIT(priv->x_bits) - 1);
}
+
+ return 0;
}
static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
{
struct alps_data *priv = psmouse->private;
unsigned char *packet = psmouse->packet;
- struct input_dev *dev = psmouse->dev;
struct input_dev *dev2 = priv->dev2;
- int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
- int fingers = 0, bmap_fn;
- struct alps_fields f = {0};
+ struct alps_fields *f = &priv->f;
+ int fingers = 0;
- priv->decode_fields(&f, packet, psmouse);
+ memset(f, 0, sizeof(*f));
+
+ priv->decode_fields(f, packet, psmouse);
/*
* There's no single feature of touchpad position and bitmap packets
@@ -623,22 +639,14 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
* packet. Check for this, and when it happens process the
* position packet as usual.
*/
- if (f.is_mp) {
- fingers = f.fingers;
+ if (f->is_mp) {
+ fingers = f->fingers;
if (priv->proto_version == ALPS_PROTO_V3) {
- bmap_fn = alps_process_bitmap(priv, f.x_map,
- f.y_map, &x1, &y1,
- &x2, &y2);
-
- /*
- * We shouldn't report more than one finger if
- * we don't have two coordinates.
- */
- if (fingers > 1 && bmap_fn < 2)
- fingers = bmap_fn;
+ if (alps_process_bitmap(priv, f) == 0)
+ fingers = 0; /* Use st data */
/* Now process position packet */
- priv->decode_fields(&f, priv->multi_data,
+ priv->decode_fields(f, priv->multi_data,
psmouse);
} else {
/*
@@ -647,15 +655,14 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
* calculate Pt2, so we need to do position
* packet decode first.
*/
- priv->decode_fields(&f, priv->multi_data,
+ priv->decode_fields(f, priv->multi_data,
psmouse);
/*
* Since Dolphin's finger number is reliable,
* there is no need to compare with bmap_fn.
*/
- alps_process_bitmap_dolphin(priv, &f, &x1, &y1,
- &x2, &y2);
+ alps_process_bitmap_dolphin(priv, f);
}
} else {
priv->multi_packet = 0;
@@ -670,10 +677,10 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
* out misidentified bitmap packets, we reject anything with this
* bit set.
*/
- if (f.is_mp)
+ if (f->is_mp)
return;
- if (!priv->multi_packet && f.first_mp) {
+ if (!priv->multi_packet && f->first_mp) {
priv->multi_packet = 1;
memcpy(priv->multi_data, packet, sizeof(priv->multi_data));
return;
@@ -687,44 +694,15 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
* with x, y, and z all zero, so these seem to be flukes.
* Ignore them.
*/
- if (f.x && f.y && !f.z)
+ if (f->st.x && f->st.y && !f->pressure)
return;
- /*
- * If we don't have MT data or the bitmaps were empty, we have
- * to rely on ST data.
- */
- if (!fingers) {
- x1 = f.x;
- y1 = f.y;
- fingers = f.z > 0 ? 1 : 0;
- }
-
- if (f.z >= 64)
- input_report_key(dev, BTN_TOUCH, 1);
- else
- input_report_key(dev, BTN_TOUCH, 0);
-
- alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
-
- input_mt_report_finger_count(dev, fingers);
-
- input_report_key(dev, BTN_LEFT, f.left);
- input_report_key(dev, BTN_RIGHT, f.right);
- input_report_key(dev, BTN_MIDDLE, f.middle);
-
- if (f.z > 0) {
- input_report_abs(dev, ABS_X, f.x);
- input_report_abs(dev, ABS_Y, f.y);
- }
- input_report_abs(dev, ABS_PRESSURE, f.z);
-
- input_sync(dev);
+ alps_report_semi_mt_data(psmouse, fingers);
if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) {
- input_report_key(dev2, BTN_LEFT, f.ts_left);
- input_report_key(dev2, BTN_RIGHT, f.ts_right);
- input_report_key(dev2, BTN_MIDDLE, f.ts_middle);
+ input_report_key(dev2, BTN_LEFT, f->ts_left);
+ input_report_key(dev2, BTN_RIGHT, f->ts_right);
+ input_report_key(dev2, BTN_MIDDLE, f->ts_middle);
input_sync(dev2);
}
}
@@ -823,13 +801,8 @@ static void alps_process_packet_v4(struct psmouse *psmouse)
{
struct alps_data *priv = psmouse->private;
unsigned char *packet = psmouse->packet;
- struct input_dev *dev = psmouse->dev;
+ struct alps_fields *f = &priv->f;
int offset;
- int x, y, z;
- int left, right;
- int x1, y1, x2, y2;
- int fingers = 0;
- unsigned int x_bitmap, y_bitmap;
/*
* v4 has a 6-byte encoding for bitmap data, but this data is
@@ -851,71 +824,207 @@ static void alps_process_packet_v4(struct psmouse *psmouse)
if (++priv->multi_packet > 2) {
priv->multi_packet = 0;
- x_bitmap = ((priv->multi_data[2] & 0x1f) << 10) |
+ f->x_map = ((priv->multi_data[2] & 0x1f) << 10) |
((priv->multi_data[3] & 0x60) << 3) |
((priv->multi_data[0] & 0x3f) << 2) |
((priv->multi_data[1] & 0x60) >> 5);
- y_bitmap = ((priv->multi_data[5] & 0x01) << 10) |
+ f->y_map = ((priv->multi_data[5] & 0x01) << 10) |
((priv->multi_data[3] & 0x1f) << 5) |
(priv->multi_data[1] & 0x1f);
- fingers = alps_process_bitmap(priv, x_bitmap, y_bitmap,
- &x1, &y1, &x2, &y2);
-
- /* Store MT data.*/
- priv->fingers = fingers;
- priv->x1 = x1;
- priv->x2 = x2;
- priv->y1 = y1;
- priv->y2 = y2;
+ f->fingers = alps_process_bitmap(priv, f);
}
- left = packet[4] & 0x01;
- right = packet[4] & 0x02;
+ f->left = packet[4] & 0x01;
+ f->right = packet[4] & 0x02;
- x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) |
- ((packet[0] & 0x30) >> 4);
- y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f);
- z = packet[5] & 0x7f;
+ f->st.x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) |
+ ((packet[0] & 0x30) >> 4);
+ f->st.y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f);
+ f->pressure = packet[5] & 0x7f;
- /*
- * If there were no contacts in the bitmap, use ST
- * points in MT reports.
- * If there were two contacts or more, report MT data.
- */
- if (priv->fingers < 2) {
- x1 = x;
- y1 = y;
- fingers = z > 0 ? 1 : 0;
- } else {
- fingers = priv->fingers;
- x1 = priv->x1;
- x2 = priv->x2;
- y1 = priv->y1;
- y2 = priv->y2;
+ alps_report_semi_mt_data(psmouse, f->fingers);
+}
+
+static bool alps_is_valid_package_v7(struct psmouse *psmouse)
+{
+ switch (psmouse->pktcnt) {
+ case 3:
+ return (psmouse->packet[2] & 0x40) == 0x40;
+ case 4:
+ return (psmouse->packet[3] & 0x48) == 0x48;
+ case 6:
+ return (psmouse->packet[5] & 0x40) == 0x00;
}
+ return true;
+}
- if (z >= 64)
- input_report_key(dev, BTN_TOUCH, 1);
+static unsigned char alps_get_packet_id_v7(char *byte)
+{
+ unsigned char packet_id;
+
+ if (byte[4] & 0x40)
+ packet_id = V7_PACKET_ID_TWO;
+ else if (byte[4] & 0x01)
+ packet_id = V7_PACKET_ID_MULTI;
+ else if ((byte[0] & 0x10) && !(byte[4] & 0x43))
+ packet_id = V7_PACKET_ID_NEW;
+ else if (byte[1] == 0x00 && byte[4] == 0x00)
+ packet_id = V7_PACKET_ID_IDLE;
else
- input_report_key(dev, BTN_TOUCH, 0);
+ packet_id = V7_PACKET_ID_UNKNOWN;
- alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
+ return packet_id;
+}
- input_mt_report_finger_count(dev, fingers);
+static void alps_get_finger_coordinate_v7(struct input_mt_pos *mt,
+ unsigned char *pkt,
+ unsigned char pkt_id)
+{
+ mt[0].x = ((pkt[2] & 0x80) << 4);
+ mt[0].x |= ((pkt[2] & 0x3F) << 5);
+ mt[0].x |= ((pkt[3] & 0x30) >> 1);
+ mt[0].x |= (pkt[3] & 0x07);
+ mt[0].y = (pkt[1] << 3) | (pkt[0] & 0x07);
+
+ mt[1].x = ((pkt[3] & 0x80) << 4);
+ mt[1].x |= ((pkt[4] & 0x80) << 3);
+ mt[1].x |= ((pkt[4] & 0x3F) << 4);
+ mt[1].y = ((pkt[5] & 0x80) << 3);
+ mt[1].y |= ((pkt[5] & 0x3F) << 4);
+
+ switch (pkt_id) {
+ case V7_PACKET_ID_TWO:
+ mt[1].x &= ~0x000F;
+ mt[1].y |= 0x000F;
+ break;
- input_report_key(dev, BTN_LEFT, left);
- input_report_key(dev, BTN_RIGHT, right);
+ case V7_PACKET_ID_MULTI:
+ mt[1].x &= ~0x003F;
+ mt[1].y &= ~0x0020;
+ mt[1].y |= ((pkt[4] & 0x02) << 4);
+ mt[1].y |= 0x001F;
+ break;
- if (z > 0) {
- input_report_abs(dev, ABS_X, x);
- input_report_abs(dev, ABS_Y, y);
+ case V7_PACKET_ID_NEW:
+ mt[1].x &= ~0x003F;
+ mt[1].x |= (pkt[0] & 0x20);
+ mt[1].y |= 0x000F;
+ break;
}
- input_report_abs(dev, ABS_PRESSURE, z);
+
+ mt[0].y = 0x7FF - mt[0].y;
+ mt[1].y = 0x7FF - mt[1].y;
+}
+
+static int alps_get_mt_count(struct input_mt_pos *mt)
+{
+ int i;
+
+ for (i = 0; i < MAX_TOUCHES && mt[i].x != 0 && mt[i].y != 0; i++)
+ /* empty */;
+
+ return i;
+}
+
+static int alps_decode_packet_v7(struct alps_fields *f,
+ unsigned char *p,
+ struct psmouse *psmouse)
+{
+ unsigned char pkt_id;
+
+ pkt_id = alps_get_packet_id_v7(p);
+ if (pkt_id == V7_PACKET_ID_IDLE)
+ return 0;
+ if (pkt_id == V7_PACKET_ID_UNKNOWN)
+ return -1;
+
+ alps_get_finger_coordinate_v7(f->mt, p, pkt_id);
+
+ if (pkt_id == V7_PACKET_ID_TWO || pkt_id == V7_PACKET_ID_MULTI) {
+ f->left = (p[0] & 0x80) >> 7;
+ f->right = (p[0] & 0x20) >> 5;
+ f->middle = (p[0] & 0x10) >> 4;
+ }
+
+ if (pkt_id == V7_PACKET_ID_TWO)
+ f->fingers = alps_get_mt_count(f->mt);
+ else if (pkt_id == V7_PACKET_ID_MULTI)
+ f->fingers = 3 + (p[5] & 0x03);
+
+ return 0;
+}
+
+static void alps_process_trackstick_packet_v7(struct psmouse *psmouse)
+{
+ struct alps_data *priv = psmouse->private;
+ unsigned char *packet = psmouse->packet;
+ struct input_dev *dev2 = priv->dev2;
+ int x, y, z, left, right, middle;
+
+ /*
+ * b7 b6 b5 b4 b3 b2 b1 b0
+ * Byte0 0 1 0 0 1 0 0 0
+ * Byte1 1 1 * * 1 M R L
+ * Byte2 X7 1 X5 X4 X3 X2 X1 X0
+ * Byte3 Z6 1 Y6 X6 1 Y2 Y1 Y0
+ * Byte4 Y7 0 Y5 Y4 Y3 1 1 0
+ * Byte5 T&P 0 Z5 Z4 Z3 Z2 Z1 Z0
+ * M / R / L: Middle / Right / Left button
+ */
+
+ x = ((packet[2] & 0xbf)) | ((packet[3] & 0x10) << 2);
+ y = (packet[3] & 0x07) | (packet[4] & 0xb8) |
+ ((packet[3] & 0x20) << 1);
+ z = (packet[5] & 0x3f) | ((packet[3] & 0x80) >> 1);
+
+ left = (packet[1] & 0x01);
+ right = (packet[1] & 0x02) >> 1;
+ middle = (packet[1] & 0x04) >> 2;
+
+ /* Divide 2 since trackpoint's speed is too fast */
+ input_report_rel(dev2, REL_X, (char)x / 2);
+ input_report_rel(dev2, REL_Y, -((char)y / 2));
+
+ input_report_key(dev2, BTN_LEFT, left);
+ input_report_key(dev2, BTN_RIGHT, right);
+ input_report_key(dev2, BTN_MIDDLE, middle);
+
+ input_sync(dev2);
+}
+
+static void alps_process_touchpad_packet_v7(struct psmouse *psmouse)
+{
+ struct alps_data *priv = psmouse->private;
+ struct input_dev *dev = psmouse->dev;
+ struct alps_fields *f = &priv->f;
+
+ memset(f, 0, sizeof(*f));
+
+ if (priv->decode_fields(f, psmouse->packet, psmouse))
+ return;
+
+ alps_report_mt_data(psmouse, alps_get_mt_count(f->mt));
+
+ input_mt_report_finger_count(dev, f->fingers);
+
+ input_report_key(dev, BTN_LEFT, f->left);
+ input_report_key(dev, BTN_RIGHT, f->right);
+ input_report_key(dev, BTN_MIDDLE, f->middle);
input_sync(dev);
}
+static void alps_process_packet_v7(struct psmouse *psmouse)
+{
+ unsigned char *packet = psmouse->packet;
+
+ if (packet[0] == 0x48 && (packet[4] & 0x47) == 0x06)
+ alps_process_trackstick_packet_v7(psmouse);
+ else
+ alps_process_touchpad_packet_v7(psmouse);
+}
+
static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
unsigned char packet[],
bool report_buttons)
@@ -1080,6 +1189,14 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
return PSMOUSE_BAD_DATA;
}
+ if (priv->proto_version == ALPS_PROTO_V7 &&
+ !alps_is_valid_package_v7(psmouse)) {
+ psmouse_dbg(psmouse, "refusing packet[%i] = %x\n",
+ psmouse->pktcnt - 1,
+ psmouse->packet[psmouse->pktcnt - 1]);
+ return PSMOUSE_BAD_DATA;
+ }
+
if (psmouse->pktcnt == psmouse->pktsize) {
priv->process_packet(psmouse);
return PSMOUSE_FULL_PACKET;
@@ -1192,6 +1309,22 @@ static int alps_rpt_cmd(struct psmouse *psmouse, int init_command,
return 0;
}
+static bool alps_check_valid_firmware_id(unsigned char id[])
+{
+ if (id[0] == 0x73)
+ return true;
+
+ if (id[0] == 0x88 &&
+ (id[1] == 0x07 ||
+ id[1] == 0x08 ||
+ (id[1] & 0xf0) == 0xb0 ||
+ (id[1] & 0xf0) == 0xc0)) {
+ return true;
+ }
+
+ return false;
+}
+
static int alps_enter_command_mode(struct psmouse *psmouse)
{
unsigned char param[4];
@@ -1201,8 +1334,7 @@ static int alps_enter_command_mode(struct psmouse *psmouse)
return -1;
}
- if ((param[0] != 0x88 || (param[1] != 0x07 && param[1] != 0x08)) &&
- param[0] != 0x73) {
+ if (!alps_check_valid_firmware_id(param)) {
psmouse_dbg(psmouse,
"unknown response while entering command mode\n");
return -1;
@@ -1660,6 +1792,45 @@ error:
return -1;
}
+static int alps_get_v3_v7_resolution(struct psmouse *psmouse, int reg_pitch)
+{
+ int reg, x_pitch, y_pitch, x_electrode, y_electrode, x_phys, y_phys;
+ struct alps_data *priv = psmouse->private;
+
+ reg = alps_command_mode_read_reg(psmouse, reg_pitch);
+ if (reg < 0)
+ return reg;
+
+ x_pitch = (char)(reg << 4) >> 4; /* sign extend lower 4 bits */
+ x_pitch = 50 + 2 * x_pitch; /* In 0.1 mm units */
+
+ y_pitch = (char)reg >> 4; /* sign extend upper 4 bits */
+ y_pitch = 36 + 2 * y_pitch; /* In 0.1 mm units */
+
+ reg = alps_command_mode_read_reg(psmouse, reg_pitch + 1);
+ if (reg < 0)
+ return reg;
+
+ x_electrode = (char)(reg << 4) >> 4; /* sign extend lower 4 bits */
+ x_electrode = 17 + x_electrode;
+
+ y_electrode = (char)reg >> 4; /* sign extend upper 4 bits */
+ y_electrode = 13 + y_electrode;
+
+ x_phys = x_pitch * (x_electrode - 1); /* In 0.1 mm units */
+ y_phys = y_pitch * (y_electrode - 1); /* In 0.1 mm units */
+
+ priv->x_res = priv->x_max * 10 / x_phys; /* units / mm */
+ priv->y_res = priv->y_max * 10 / y_phys; /* units / mm */
+
+ psmouse_dbg(psmouse,
+ "pitch %dx%d num-electrodes %dx%d physical size %dx%d mm res %dx%d\n",
+ x_pitch, y_pitch, x_electrode, y_electrode,
+ x_phys / 10, y_phys / 10, priv->x_res, priv->y_res);
+
+ return 0;
+}
+
static int alps_hw_init_rushmore_v3(struct psmouse *psmouse)
{
struct alps_data *priv = psmouse->private;
@@ -1680,6 +1851,9 @@ static int alps_hw_init_rushmore_v3(struct psmouse *psmouse)
alps_command_mode_write_reg(psmouse, 0xc2cb, 0x00))
goto error;
+ if (alps_get_v3_v7_resolution(psmouse, 0xc2da))
+ goto error;
+
reg_val = alps_command_mode_read_reg(psmouse, 0xc2c6);
if (reg_val == -1)
goto error;
@@ -1856,6 +2030,35 @@ static int alps_hw_init_dolphin_v1(struct psmouse *psmouse)
return 0;
}
+static int alps_hw_init_v7(struct psmouse *psmouse)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ int reg_val, ret = -1;
+
+ if (alps_enter_command_mode(psmouse) ||
+ alps_command_mode_read_reg(psmouse, 0xc2d9) == -1)
+ goto error;
+
+ if (alps_get_v3_v7_resolution(psmouse, 0xc397))
+ goto error;
+
+ if (alps_command_mode_write_reg(psmouse, 0xc2c9, 0x64))
+ goto error;
+
+ reg_val = alps_command_mode_read_reg(psmouse, 0xc2c4);
+ if (reg_val == -1)
+ goto error;
+ if (__alps_command_mode_write_reg(psmouse, reg_val | 0x02))
+ goto error;
+
+ alps_exit_command_mode(psmouse);
+ return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
+
+error:
+ alps_exit_command_mode(psmouse);
+ return ret;
+}
+
static void alps_set_defaults(struct alps_data *priv)
{
priv->byte0 = 0x8f;
@@ -1914,6 +2117,21 @@ static void alps_set_defaults(struct alps_data *priv)
priv->x_max = 2047;
priv->y_max = 1535;
break;
+ case ALPS_PROTO_V7:
+ priv->hw_init = alps_hw_init_v7;
+ priv->process_packet = alps_process_packet_v7;
+ priv->decode_fields = alps_decode_packet_v7;
+ priv->set_abs_params = alps_set_abs_params_mt;
+ priv->nibble_commands = alps_v3_nibble_commands;
+ priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
+ priv->x_max = 0xfff;
+ priv->y_max = 0x7ff;
+ priv->byte0 = 0x48;
+ priv->mask0 = 0x48;
+
+ if (priv->fw_ver[1] != 0xba)
+ priv->flags |= ALPS_BUTTONPAD;
+ break;
}
}
@@ -1972,6 +2190,9 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
alps_exit_command_mode(psmouse))
return -EIO;
+ /* Save the Firmware version */
+ memcpy(priv->fw_ver, ec, 3);
+
if (alps_match_table(psmouse, priv, e7, ec) == 0) {
return 0;
} else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x50 &&
@@ -1982,6 +2203,12 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
return -EIO;
else
return 0;
+ } else if (ec[0] == 0x88 &&
+ ((ec[1] & 0xf0) == 0xb0 || (ec[1] & 0xf0) == 0xc0)) {
+ priv->proto_version = ALPS_PROTO_V7;
+ alps_set_defaults(priv);
+
+ return 0;
} else if (ec[0] == 0x88 && ec[1] == 0x08) {
priv->proto_version = ALPS_PROTO_V3;
alps_set_defaults(priv);
@@ -1990,6 +2217,7 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
priv->decode_fields = alps_decode_rushmore;
priv->x_bits = 16;
priv->y_bits = 12;
+ priv->flags |= ALPS_IS_RUSHMORE;
/* hack to make addr_command, nibble_command available */
psmouse->private = priv;
@@ -2006,8 +2234,8 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
return 0;
}
- psmouse_info(psmouse,
- "Unknown ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec);
+ psmouse_dbg(psmouse,
+ "Likely not an ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec);
return -EINVAL;
}
@@ -2044,17 +2272,21 @@ static void alps_set_abs_params_st(struct alps_data *priv,
static void alps_set_abs_params_mt(struct alps_data *priv,
struct input_dev *dev1)
{
- set_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
- input_mt_init_slots(dev1, 2, 0);
input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
- set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit);
+ input_abs_set_res(dev1, ABS_MT_POSITION_X, priv->x_res);
+ input_abs_set_res(dev1, ABS_MT_POSITION_Y, priv->y_res);
+
+ input_mt_init_slots(dev1, MAX_TOUCHES, INPUT_MT_POINTER |
+ INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK | INPUT_MT_SEMI_MT);
+
set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit);
set_bit(BTN_TOOL_QUADTAP, dev1->keybit);
- input_set_abs_params(dev1, ABS_X, 0, priv->x_max, 0, 0);
- input_set_abs_params(dev1, ABS_Y, 0, priv->y_max, 0, 0);
+ /* V7 is real multi-touch */
+ if (priv->proto_version == ALPS_PROTO_V7)
+ clear_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
}
int alps_init(struct psmouse *psmouse)
@@ -2100,7 +2332,9 @@ int alps_init(struct psmouse *psmouse)
dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS);
priv->set_abs_params(priv, dev1);
- input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
+ /* No pressure on V7 */
+ if (priv->proto_version != ALPS_PROTO_V7)
+ input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
if (priv->flags & ALPS_WHEEL) {
dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL);
@@ -2117,6 +2351,9 @@ int alps_init(struct psmouse *psmouse)
dev1->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1);
dev1->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2);
dev1->keybit[BIT_WORD(BTN_3)] |= BIT_MASK(BTN_3);
+ } else if (priv->flags & ALPS_BUTTONPAD) {
+ set_bit(INPUT_PROP_BUTTONPAD, dev1->propbit);
+ clear_bit(BTN_RIGHT, dev1->keybit);
} else {
dev1->keybit[BIT_WORD(BTN_MIDDLE)] |= BIT_MASK(BTN_MIDDLE);
}
@@ -2136,6 +2373,10 @@ int alps_init(struct psmouse *psmouse)
dev2->keybit[BIT_WORD(BTN_LEFT)] =
BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+ __set_bit(INPUT_PROP_POINTER, dev2->propbit);
+ if (priv->flags & ALPS_DUALPOINT)
+ __set_bit(INPUT_PROP_POINTING_STICK, dev2->propbit);
+
if (input_register_device(priv->dev2))
goto init_fail;
diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h
index 03f88b6940c7..66240b47819a 100644
--- a/drivers/input/mouse/alps.h
+++ b/drivers/input/mouse/alps.h
@@ -12,17 +12,39 @@
#ifndef _ALPS_H
#define _ALPS_H
+#include <linux/input/mt.h>
+
#define ALPS_PROTO_V1 1
#define ALPS_PROTO_V2 2
#define ALPS_PROTO_V3 3
#define ALPS_PROTO_V4 4
#define ALPS_PROTO_V5 5
#define ALPS_PROTO_V6 6
+#define ALPS_PROTO_V7 7 /* t3btl t4s */
+
+#define MAX_TOUCHES 2
#define DOLPHIN_COUNT_PER_ELECTRODE 64
#define DOLPHIN_PROFILE_XOFFSET 8 /* x-electrode offset */
#define DOLPHIN_PROFILE_YOFFSET 1 /* y-electrode offset */
+/*
+ * enum V7_PACKET_ID - defines the packet type for V7
+ * V7_PACKET_ID_IDLE: There's no finger and no button activity.
+ * V7_PACKET_ID_TWO: There's one or two non-resting fingers on touchpad
+ * or there's button activities.
+ * V7_PACKET_ID_MULTI: There are at least three non-resting fingers.
+ * V7_PACKET_ID_NEW: The finger position in slot is not continues from
+ * previous packet.
+*/
+enum V7_PACKET_ID {
+ V7_PACKET_ID_IDLE,
+ V7_PACKET_ID_TWO,
+ V7_PACKET_ID_MULTI,
+ V7_PACKET_ID_NEW,
+ V7_PACKET_ID_UNKNOWN,
+};
+
/**
* struct alps_model_info - touchpad ID table
* @signature: E7 response string to match.
@@ -46,7 +68,7 @@ struct alps_model_info {
unsigned char command_mode_resp;
unsigned char proto_version;
unsigned char byte0, mask0;
- unsigned char flags;
+ int flags;
};
/**
@@ -65,14 +87,19 @@ struct alps_nibble_commands {
unsigned char data;
};
+struct alps_bitmap_point {
+ int start_bit;
+ int num_bits;
+};
+
/**
* struct alps_fields - decoded version of the report packet
* @x_map: Bitmap of active X positions for MT.
* @y_map: Bitmap of active Y positions for MT.
* @fingers: Number of fingers for MT.
- * @x: X position for ST.
- * @y: Y position for ST.
- * @z: Z position for ST.
+ * @pressure: Pressure.
+ * @st: position for ST.
+ * @mt: position for MT.
* @first_mp: Packet is the first of a multi-packet report.
* @is_mp: Packet is part of a multi-packet report.
* @left: Left touchpad button is active.
@@ -86,9 +113,11 @@ struct alps_fields {
unsigned int x_map;
unsigned int y_map;
unsigned int fingers;
- unsigned int x;
- unsigned int y;
- unsigned int z;
+
+ int pressure;
+ struct input_mt_pos st;
+ struct input_mt_pos mt[MAX_TOUCHES];
+
unsigned int first_mp:1;
unsigned int is_mp:1;
@@ -113,6 +142,7 @@ struct alps_fields {
* known format for this model. The first byte of the report, ANDed with
* mask0, should match byte0.
* @mask0: The mask used to check the first byte of the report.
+ * @fw_ver: cached copy of firmware version (EC report)
* @flags: Additional device capabilities (passthrough port, trackstick, etc.).
* @x_max: Largest possible X position value.
* @y_max: Largest possible Y position value.
@@ -125,11 +155,7 @@ struct alps_fields {
* @prev_fin: Finger bit from previous packet.
* @multi_packet: Multi-packet data in progress.
* @multi_data: Saved multi-packet data.
- * @x1: First X coordinate from last MT report.
- * @x2: Second X coordinate from last MT report.
- * @y1: First Y coordinate from last MT report.
- * @y2: Second Y coordinate from last MT report.
- * @fingers: Number of fingers from last MT report.
+ * @f: Decoded packet data fields.
* @quirks: Bitmap of ALPS_QUIRK_*.
* @timer: Timer for flushing out the final report packet in the stream.
*/
@@ -142,23 +168,25 @@ struct alps_data {
int addr_command;
unsigned char proto_version;
unsigned char byte0, mask0;
- unsigned char flags;
+ unsigned char fw_ver[3];
+ int flags;
int x_max;
int y_max;
int x_bits;
int y_bits;
+ unsigned int x_res;
+ unsigned int y_res;
int (*hw_init)(struct psmouse *psmouse);
void (*process_packet)(struct psmouse *psmouse);
- void (*decode_fields)(struct alps_fields *f, unsigned char *p,
+ int (*decode_fields)(struct alps_fields *f, unsigned char *p,
struct psmouse *psmouse);
void (*set_abs_params)(struct alps_data *priv, struct input_dev *dev1);
int prev_fin;
int multi_packet;
unsigned char multi_data[6];
- int x1, x2, y1, y2;
- int fingers;
+ struct alps_fields f;
u8 quirks;
struct timer_list timer;
};
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index ee2a04d90d20..06fc6e76ffbe 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -18,6 +18,7 @@
#include <linux/input/mt.h>
#include <linux/serio.h>
#include <linux/libps2.h>
+#include <asm/unaligned.h>
#include "psmouse.h"
#include "elantech.h"
@@ -403,6 +404,68 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
input_sync(dev);
}
+static void elantech_report_trackpoint(struct psmouse *psmouse,
+ int packet_type)
+{
+ /*
+ * byte 0: 0 0 sx sy 0 M R L
+ * byte 1:~sx 0 0 0 0 0 0 0
+ * byte 2:~sy 0 0 0 0 0 0 0
+ * byte 3: 0 0 ~sy ~sx 0 1 1 0
+ * byte 4: x7 x6 x5 x4 x3 x2 x1 x0
+ * byte 5: y7 y6 y5 y4 y3 y2 y1 y0
+ *
+ * x and y are written in two's complement spread
+ * over 9 bits with sx/sy the relative top bit and
+ * x7..x0 and y7..y0 the lower bits.
+ * The sign of y is opposite to what the input driver
+ * expects for a relative movement
+ */
+
+ struct elantech_data *etd = psmouse->private;
+ struct input_dev *tp_dev = etd->tp_dev;
+ unsigned char *packet = psmouse->packet;
+ int x, y;
+ u32 t;
+
+ if (dev_WARN_ONCE(&psmouse->ps2dev.serio->dev,
+ !tp_dev,
+ psmouse_fmt("Unexpected trackpoint message\n"))) {
+ if (etd->debug == 1)
+ elantech_packet_dump(psmouse);
+ return;
+ }
+
+ t = get_unaligned_le32(&packet[0]);
+
+ switch (t & ~7U) {
+ case 0x06000030U:
+ case 0x16008020U:
+ case 0x26800010U:
+ case 0x36808000U:
+ x = packet[4] - (int)((packet[1]^0x80) << 1);
+ y = (int)((packet[2]^0x80) << 1) - packet[5];
+
+ input_report_key(tp_dev, BTN_LEFT, packet[0] & 0x01);
+ input_report_key(tp_dev, BTN_RIGHT, packet[0] & 0x02);
+ input_report_key(tp_dev, BTN_MIDDLE, packet[0] & 0x04);
+
+ input_report_rel(tp_dev, REL_X, x);
+ input_report_rel(tp_dev, REL_Y, y);
+
+ input_sync(tp_dev);
+
+ break;
+
+ default:
+ /* Dump unexpected packet sequences if debug=1 (default) */
+ if (etd->debug == 1)
+ elantech_packet_dump(psmouse);
+
+ break;
+ }
+}
+
/*
* Interpret complete data packets and report absolute mode input events for
* hardware version 3. (12 byte packets for two fingers)
@@ -715,6 +778,8 @@ static int elantech_packet_check_v3(struct psmouse *psmouse)
if ((packet[0] & 0x0c) == 0x0c && (packet[3] & 0xce) == 0x0c)
return PACKET_V3_TAIL;
+ if ((packet[3] & 0x0f) == 0x06)
+ return PACKET_TRACKPOINT;
}
return PACKET_UNKNOWN;
@@ -791,14 +856,23 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
case 3:
packet_type = elantech_packet_check_v3(psmouse);
- /* ignore debounce */
- if (packet_type == PACKET_DEBOUNCE)
- return PSMOUSE_FULL_PACKET;
-
- if (packet_type == PACKET_UNKNOWN)
+ switch (packet_type) {
+ case PACKET_UNKNOWN:
return PSMOUSE_BAD_DATA;
- elantech_report_absolute_v3(psmouse, packet_type);
+ case PACKET_DEBOUNCE:
+ /* ignore debounce */
+ break;
+
+ case PACKET_TRACKPOINT:
+ elantech_report_trackpoint(psmouse, packet_type);
+ break;
+
+ default:
+ elantech_report_absolute_v3(psmouse, packet_type);
+ break;
+ }
+
break;
case 4:
@@ -1018,8 +1092,10 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
* Asus UX31 0x361f00 20, 15, 0e clickpad
* Asus UX32VD 0x361f02 00, 15, 0e clickpad
* Avatar AVIU-145A2 0x361f00 ? clickpad
+ * Fujitsu H730 0x570f00 c0, 14, 0c 3 hw buttons (**)
* Gigabyte U2442 0x450f01 58, 17, 0c 2 hw buttons
* Lenovo L430 0x350f02 b9, 15, 0c 2 hw buttons (*)
+ * Lenovo L530 0x350f02 b9, 15, 0c 2 hw buttons (*)
* Samsung NF210 0x150b00 78, 14, 0a 2 hw buttons
* Samsung NP770Z5E 0x575f01 10, 15, 0f clickpad
* Samsung NP700Z5B 0x361f06 21, 15, 0f clickpad
@@ -1029,6 +1105,8 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
* Samsung RF710 0x450f00 ? 2 hw buttons
* System76 Pangolin 0x250f01 ? 2 hw buttons
* (*) + 3 trackpoint buttons
+ * (**) + 0 trackpoint buttons
+ * Note: Lenovo L430 and Lenovo L430 have the same fw_version/caps
*/
static void elantech_set_buttonpad_prop(struct psmouse *psmouse)
{
@@ -1253,6 +1331,13 @@ static bool elantech_is_signature_valid(const unsigned char *param)
if (param[1] == 0)
return true;
+ /*
+ * Some models have a revision higher then 20. Meaning param[2] may
+ * be 10 or 20, skip the rates check for these.
+ */
+ if (param[0] == 0x46 && (param[1] & 0xef) == 0x0f && param[2] < 40)
+ return true;
+
for (i = 0; i < ARRAY_SIZE(rates); i++)
if (param[2] == rates[i])
return false;
@@ -1324,6 +1409,10 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties)
*/
static void elantech_disconnect(struct psmouse *psmouse)
{
+ struct elantech_data *etd = psmouse->private;
+
+ if (etd->tp_dev)
+ input_unregister_device(etd->tp_dev);
sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
&elantech_attr_group);
kfree(psmouse->private);
@@ -1438,8 +1527,10 @@ static int elantech_set_properties(struct elantech_data *etd)
int elantech_init(struct psmouse *psmouse)
{
struct elantech_data *etd;
- int i, error;
+ int i;
+ int error = -EINVAL;
unsigned char param[3];
+ struct input_dev *tp_dev;
psmouse->private = etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL);
if (!etd)
@@ -1498,14 +1589,53 @@ int elantech_init(struct psmouse *psmouse)
goto init_fail;
}
+ /* The MSB indicates the presence of the trackpoint */
+ if ((etd->capabilities[0] & 0x80) == 0x80) {
+ tp_dev = input_allocate_device();
+
+ if (!tp_dev) {
+ error = -ENOMEM;
+ goto init_fail_tp_alloc;
+ }
+
+ etd->tp_dev = tp_dev;
+ snprintf(etd->tp_phys, sizeof(etd->tp_phys), "%s/input1",
+ psmouse->ps2dev.serio->phys);
+ tp_dev->phys = etd->tp_phys;
+ tp_dev->name = "Elantech PS/2 TrackPoint";
+ tp_dev->id.bustype = BUS_I8042;
+ tp_dev->id.vendor = 0x0002;
+ tp_dev->id.product = PSMOUSE_ELANTECH;
+ tp_dev->id.version = 0x0000;
+ tp_dev->dev.parent = &psmouse->ps2dev.serio->dev;
+ tp_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+ tp_dev->relbit[BIT_WORD(REL_X)] =
+ BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+ tp_dev->keybit[BIT_WORD(BTN_LEFT)] =
+ BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) |
+ BIT_MASK(BTN_RIGHT);
+
+ __set_bit(INPUT_PROP_POINTER, tp_dev->propbit);
+ __set_bit(INPUT_PROP_POINTING_STICK, tp_dev->propbit);
+
+ error = input_register_device(etd->tp_dev);
+ if (error < 0)
+ goto init_fail_tp_reg;
+ }
+
psmouse->protocol_handler = elantech_process_byte;
psmouse->disconnect = elantech_disconnect;
psmouse->reconnect = elantech_reconnect;
psmouse->pktsize = etd->hw_version > 1 ? 6 : 4;
return 0;
-
+ init_fail_tp_reg:
+ input_free_device(tp_dev);
+ init_fail_tp_alloc:
+ sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
+ &elantech_attr_group);
init_fail:
+ psmouse_reset(psmouse);
kfree(etd);
- return -1;
+ return error;
}
diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
index 9e0e2a1f340d..6f3afec02f03 100644
--- a/drivers/input/mouse/elantech.h
+++ b/drivers/input/mouse/elantech.h
@@ -94,6 +94,7 @@
#define PACKET_V4_HEAD 0x05
#define PACKET_V4_MOTION 0x06
#define PACKET_V4_STATUS 0x07
+#define PACKET_TRACKPOINT 0x08
/*
* track up to 5 fingers for v4 hardware
@@ -114,6 +115,8 @@ struct finger_pos {
};
struct elantech_data {
+ struct input_dev *tp_dev; /* Relative device for trackpoint */
+ char tp_phys[32];
unsigned char reg_07;
unsigned char reg_10;
unsigned char reg_11;
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index cff065f6261c..b4e1f014ddc2 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -670,6 +670,8 @@ static void psmouse_apply_defaults(struct psmouse *psmouse)
__set_bit(REL_X, input_dev->relbit);
__set_bit(REL_Y, input_dev->relbit);
+ __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+
psmouse->set_rate = psmouse_set_rate;
psmouse->set_resolution = psmouse_set_resolution;
psmouse->poll = psmouse_poll;
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index ef9e0b8a9aa7..fd23181c1fb7 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -117,6 +117,9 @@ void synaptics_reset(struct psmouse *psmouse)
}
#ifdef CONFIG_MOUSE_PS2_SYNAPTICS
+
+static bool cr48_profile_sensor;
+
struct min_max_quirk {
const char * const *pnp_ids;
int x_min, x_max, y_min, y_max;
@@ -626,10 +629,61 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
((buf[0] & 0x04) >> 1) |
((buf[3] & 0x04) >> 2));
+ if ((SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) ||
+ SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) &&
+ hw->w == 2) {
+ synaptics_parse_agm(buf, priv, hw);
+ return 1;
+ }
+
+ hw->x = (((buf[3] & 0x10) << 8) |
+ ((buf[1] & 0x0f) << 8) |
+ buf[4]);
+ hw->y = (((buf[3] & 0x20) << 7) |
+ ((buf[1] & 0xf0) << 4) |
+ buf[5]);
+ hw->z = buf[2];
+
hw->left = (buf[0] & 0x01) ? 1 : 0;
hw->right = (buf[0] & 0x02) ? 1 : 0;
- if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) {
+ if (SYN_CAP_FORCEPAD(priv->ext_cap_0c)) {
+ /*
+ * ForcePads, like Clickpads, use middle button
+ * bits to report primary button clicks.
+ * Unfortunately they report primary button not
+ * only when user presses on the pad above certain
+ * threshold, but also when there are more than one
+ * finger on the touchpad, which interferes with
+ * out multi-finger gestures.
+ */
+ if (hw->z == 0) {
+ /* No contacts */
+ priv->press = priv->report_press = false;
+ } else if (hw->w >= 4 && ((buf[0] ^ buf[3]) & 0x01)) {
+ /*
+ * Single-finger touch with pressure above
+ * the threshold. If pressure stays long
+ * enough, we'll start reporting primary
+ * button. We rely on the device continuing
+ * sending data even if finger does not
+ * move.
+ */
+ if (!priv->press) {
+ priv->press_start = jiffies;
+ priv->press = true;
+ } else if (time_after(jiffies,
+ priv->press_start +
+ msecs_to_jiffies(50))) {
+ priv->report_press = true;
+ }
+ } else {
+ priv->press = false;
+ }
+
+ hw->left = priv->report_press;
+
+ } else if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) {
/*
* Clickpad's button is transmitted as middle button,
* however, since it is primary button, we will report
@@ -648,21 +702,6 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0;
}
- if ((SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) ||
- SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) &&
- hw->w == 2) {
- synaptics_parse_agm(buf, priv, hw);
- return 1;
- }
-
- hw->x = (((buf[3] & 0x10) << 8) |
- ((buf[1] & 0x0f) << 8) |
- buf[4]);
- hw->y = (((buf[3] & 0x20) << 7) |
- ((buf[1] & 0xf0) << 4) |
- buf[5]);
- hw->z = buf[2];
-
if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) &&
((buf[0] ^ buf[3]) & 0x02)) {
switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) {
@@ -1152,6 +1191,42 @@ static void synaptics_image_sensor_process(struct psmouse *psmouse,
priv->agm_pending = false;
}
+static void synaptics_profile_sensor_process(struct psmouse *psmouse,
+ struct synaptics_hw_state *sgm,
+ int num_fingers)
+{
+ struct input_dev *dev = psmouse->dev;
+ struct synaptics_data *priv = psmouse->private;
+ struct synaptics_hw_state *hw[2] = { sgm, &priv->agm };
+ struct input_mt_pos pos[2];
+ int slot[2], nsemi, i;
+
+ nsemi = clamp_val(num_fingers, 0, 2);
+
+ for (i = 0; i < nsemi; i++) {
+ pos[i].x = hw[i]->x;
+ pos[i].y = synaptics_invert_y(hw[i]->y);
+ }
+
+ input_mt_assign_slots(dev, slot, pos, nsemi);
+
+ for (i = 0; i < nsemi; i++) {
+ input_mt_slot(dev, slot[i]);
+ input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
+ input_report_abs(dev, ABS_MT_POSITION_X, pos[i].x);
+ input_report_abs(dev, ABS_MT_POSITION_Y, pos[i].y);
+ input_report_abs(dev, ABS_MT_PRESSURE, hw[i]->z);
+ }
+
+ input_mt_drop_unused(dev);
+ input_mt_report_pointer_emulation(dev, false);
+ input_mt_report_finger_count(dev, num_fingers);
+
+ synaptics_report_buttons(psmouse, sgm);
+
+ input_sync(dev);
+}
+
/*
* called for each full received packet from the touchpad
*/
@@ -1215,6 +1290,11 @@ static void synaptics_process_packet(struct psmouse *psmouse)
finger_width = 0;
}
+ if (cr48_profile_sensor) {
+ synaptics_profile_sensor_process(psmouse, &hw, num_fingers);
+ return;
+ }
+
if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
synaptics_report_semi_mt_data(dev, &hw, &priv->agm,
num_fingers);
@@ -1360,6 +1440,9 @@ static void set_input_params(struct psmouse *psmouse,
set_abs_position_params(dev, priv, ABS_X, ABS_Y);
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
+ if (cr48_profile_sensor)
+ input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
+
if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
ABS_MT_POSITION_Y);
@@ -1371,11 +1454,16 @@ static void set_input_params(struct psmouse *psmouse,
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
__set_bit(BTN_TOOL_QUINTTAP, dev->keybit);
} else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
- /* Non-image sensors with AGM use semi-mt */
- __set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
- input_mt_init_slots(dev, 2, 0);
set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
ABS_MT_POSITION_Y);
+ /*
+ * Profile sensor in CR-48 tracks contacts reasonably well,
+ * other non-image sensors with AGM use semi-mt.
+ */
+ input_mt_init_slots(dev, 2,
+ INPUT_MT_POINTER |
+ (cr48_profile_sensor ?
+ INPUT_MT_TRACK : INPUT_MT_SEMI_MT));
}
if (SYN_CAP_PALMDETECT(priv->capabilities))
@@ -1577,10 +1665,24 @@ static const struct dmi_system_id olpc_dmi_table[] __initconst = {
{ }
};
+static const struct dmi_system_id __initconst cr48_dmi_table[] = {
+#if defined(CONFIG_DMI) && defined(CONFIG_X86)
+ {
+ /* Cr-48 Chromebook (Codename Mario) */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "IEC"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Mario"),
+ },
+ },
+#endif
+ { }
+};
+
void __init synaptics_module_init(void)
{
impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table);
broken_olpc_ec = dmi_check_system(olpc_dmi_table);
+ cr48_profile_sensor = dmi_check_system(cr48_dmi_table);
}
static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index e594af0b264b..fb2e076738ae 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -78,6 +78,11 @@
* 2 0x08 image sensor image sensor tracks 5 fingers, but only
* reports 2.
* 2 0x20 report min query 0x0f gives min coord reported
+ * 2 0x80 forcepad forcepad is a variant of clickpad that
+ * does not have physical buttons but rather
+ * uses pressure above certain threshold to
+ * report primary clicks. Forcepads also have
+ * clickpad bit set.
*/
#define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & 0x100000) /* 1-button ClickPad */
#define SYN_CAP_CLICKPAD2BTN(ex0c) ((ex0c) & 0x000100) /* 2-button ClickPad */
@@ -86,6 +91,7 @@
#define SYN_CAP_ADV_GESTURE(ex0c) ((ex0c) & 0x080000)
#define SYN_CAP_REDUCED_FILTERING(ex0c) ((ex0c) & 0x000400)
#define SYN_CAP_IMAGE_SENSOR(ex0c) ((ex0c) & 0x000800)
+#define SYN_CAP_FORCEPAD(ex0c) ((ex0c) & 0x008000)
/* synaptics modes query bits */
#define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7))
@@ -177,6 +183,11 @@ struct synaptics_data {
*/
struct synaptics_hw_state agm;
bool agm_pending; /* new AGM packet received */
+
+ /* ForcePad handling */
+ unsigned long press_start;
+ bool press;
+ bool report_press;
};
void synaptics_module_init(void);
diff --git a/drivers/input/mouse/synaptics_usb.c b/drivers/input/mouse/synaptics_usb.c
index e122bda16aab..6bcc0189c1c9 100644
--- a/drivers/input/mouse/synaptics_usb.c
+++ b/drivers/input/mouse/synaptics_usb.c
@@ -387,6 +387,7 @@ static int synusb_probe(struct usb_interface *intf,
__set_bit(EV_REL, input_dev->evbit);
__set_bit(REL_X, input_dev->relbit);
__set_bit(REL_Y, input_dev->relbit);
+ __set_bit(INPUT_PROP_POINTING_STICK, input_dev->propbit);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, 127, 0, 0);
} else {
input_set_abs_params(input_dev, ABS_X,
@@ -401,6 +402,11 @@ static int synusb_probe(struct usb_interface *intf,
__set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
}
+ if (synusb->flags & SYNUSB_TOUCHSCREEN)
+ __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+ else
+ __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+
__set_bit(BTN_LEFT, input_dev->keybit);
__set_bit(BTN_RIGHT, input_dev->keybit);
__set_bit(BTN_MIDDLE, input_dev->keybit);
diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c
index ca843b6cf6bd..30c8b6998808 100644
--- a/drivers/input/mouse/trackpoint.c
+++ b/drivers/input/mouse/trackpoint.c
@@ -393,6 +393,9 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
if ((button_info & 0x0f) >= 3)
__set_bit(BTN_MIDDLE, psmouse->dev->keybit);
+ __set_bit(INPUT_PROP_POINTER, psmouse->dev->propbit);
+ __set_bit(INPUT_PROP_POINTING_STICK, psmouse->dev->propbit);
+
trackpoint_defaults(psmouse->private);
error = trackpoint_power_on_reset(&psmouse->ps2dev);
OpenPOWER on IntegriCloud