All of lore.kernel.org
 help / color / mirror / Atom feed
* ALPS v4 Semi-mt Support
@ 2012-04-10 14:01 George Pantalos
  0 siblings, 0 replies; 10+ messages in thread
From: George Pantalos @ 2012-04-10 14:01 UTC (permalink / raw)
  To: linux-input

Hello,

I have created a crude patch to add Semi-MT support for v4 ALPS touchpads 
based on the work by Seth Forshee. [1]

This patch works ok for Single-Touch Events however the Left Half side of the 
touchpad is far less accurate than the Right Half for Semi-mt events 
(synclient -m 10 shows f alternating constantly between 1 and 2 fingers with 
two fingers touching it and one finger touch erroneously reports as 2 finger 
touch sometimes.)

I hope this is useful and maybe it could be improved and approved :)

Sorry for the code quality, I am not a programmer.

[1]  http://people.canonical.com/~sforshee/alps-touchpad/psmouse-
alps-0.10/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: ALPS v4 Semi-mt Support
  2012-04-17  0:52         ` George Pantalos
@ 2012-04-17 15:22           ` Seth Forshee
  0 siblings, 0 replies; 10+ messages in thread
From: Seth Forshee @ 2012-04-17 15:22 UTC (permalink / raw)
  To: George Pantalos; +Cc: linux-input

On Tue, Apr 17, 2012 at 03:52:34AM +0300, George Pantalos wrote:
> On Monday 16 of April 2012 16:24:07 Seth Forshee wrote:
> > If the latency really is noticible when you stash the ST points, here's
> > what I'd suggest trying instead. Stash away the last set of MT data you
> > saw and repeat it with each of the next two ST coordinates. I suspect
> > that will probably work well enough, and will allow every ST point to
> > still be reported. And it should significantly simplify the code as
> > well.
> 
> I tried your suggestion and I have to say that it works great. 
> Thank you, Seth.
> I hope my execution is ok. 

The implementation looks good to me. If you're confident that the
interpretation of the sync is correct, submit the patch according to the
guidelines in Documentation/SubmittingPatches and I'll be happy to give
my ack.

Seth


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: ALPS v4 Semi-mt Support
  2012-04-16 22:21         ` George Pantalos
@ 2012-04-17 13:16           ` Seth Forshee
  0 siblings, 0 replies; 10+ messages in thread
From: Seth Forshee @ 2012-04-17 13:16 UTC (permalink / raw)
  To: George Pantalos; +Cc: linux-input

On Tue, Apr 17, 2012 at 01:21:45AM +0300, George Pantalos wrote:
> I have noticed that the sync bit can at times be set in the second packet of 
> the sequence. Couldn't this reset the position to priv->multi_packet=0 when 
> in fact we are in priv->multi_packet=1  position?
> I have also thought about "if((packet[6] & 0x40) && (priv->multi_packet == 
> 0))" so that sync is not lost. 

I think there are two possibilities for when you see the sync bit set in
the second packet.

The first is that the touchpad aborted the previous sequence and started
a new one. In this case you really do want to reset priv->multi_packet.

The other option is that it really isn't a sync bit, and that it has
some other meaning. In that case you'll need to find some other reliable
method for assembling the MT data in the correct order.

The only way you'll be able to tell is by inspecting the packets after
the one with the unexpected sync. I suspect you'll either see one of two
sequences. This one:

  sync -> sync -> non-sync -> non-sync -> sync -> ...

Which would appear to be an aborted sequence followed by the start of a
new one. Or this one:

  sync -> sync -> non-sync -> sync -> ...

Which would appear to indicate that what we're calling the sync bit
isn't really a sync after all.

Seth


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: ALPS v4 Semi-mt Support
  2012-04-16 21:24       ` Seth Forshee
  2012-04-16 22:21         ` George Pantalos
@ 2012-04-17  0:52         ` George Pantalos
  2012-04-17 15:22           ` Seth Forshee
  1 sibling, 1 reply; 10+ messages in thread
From: George Pantalos @ 2012-04-17  0:52 UTC (permalink / raw)
  To: Seth Forshee; +Cc: linux-input

On Monday 16 of April 2012 16:24:07 Seth Forshee wrote:
> If the latency really is noticible when you stash the ST points, here's
> what I'd suggest trying instead. Stash away the last set of MT data you
> saw and repeat it with each of the next two ST coordinates. I suspect
> that will probably work well enough, and will allow every ST point to
> still be reported. And it should significantly simplify the code as
> well.

I tried your suggestion and I have to say that it works great. 
Thank you, Seth.
I hope my execution is ok. 

--- linux-3.3/drivers/input/mouse/alps.c.orig	2012-04-15 21:45:18.083826446 
+0300
+++ linux-3.3/drivers/input/mouse/alps.c	2012-04-17 03:18:51.806595048 
+0300
@@ -604,10 +604,56 @@
 
 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;
+	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
+	 * broken up between 3 normal packets. Use priv->multi_packet to
+	 * track our position in the bitmap packet.
+	 */
+	if (packet[6] & 0x40) {
+		/* sync, reset position */
+		priv->multi_packet = 0;
+	}
+
+	if (WARN_ON_ONCE(priv->multi_packet > 2))
+		return;
+
+	offset = 2 * priv->multi_packet;
+	priv->multi_data[offset] = packet[6];
+	priv->multi_data[offset + 1] = packet[7];
+
+	if (++priv->multi_packet > 2) {
+		priv->multi_packet = 0;
+
+		x_bitmap = ((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) |
+			   ((priv->multi_data[3] & 0x1f) << 5) |
+			   (priv->multi_data[1] & 0x1f);
+
+		fingers = alps_process_bitmap(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;
+	}
 
 	left = packet[4] & 0x01;
 	right = packet[4] & 0x02;
@@ -617,21 +663,44 @@
 	y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f);
 	z = 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;
+	}
+
 	if (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_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
+	input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
+	input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
+	input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4);
+
+	input_report_key(dev, BTN_LEFT, left);
+	input_report_key(dev, BTN_RIGHT, right);
+
 	if (z > 0) {
 		input_report_abs(dev, ABS_X, x);
 		input_report_abs(dev, ABS_Y, y);
 	}
 	input_report_abs(dev, ABS_PRESSURE, z);
 
-	input_report_key(dev, BTN_TOOL_FINGER, z > 0);
-	input_report_key(dev, BTN_LEFT, left);
-	input_report_key(dev, BTN_RIGHT, right);
-
 	input_sync(dev);
 }
 
@@ -1557,6 +1626,7 @@
 		input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0);
 		break;
 	case ALPS_PROTO_V3:
+	case ALPS_PROTO_V4:
 		set_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
 		input_mt_init_slots(dev1, 2);
 		input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, 
ALPS_V3_X_MAX, 0, 0);
@@ -1565,8 +1635,7 @@
 		set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit);
 		set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit);
 		set_bit(BTN_TOOL_QUADTAP, dev1->keybit);
-		/* fall through */
-	case ALPS_PROTO_V4:
+
 		input_set_abs_params(dev1, ABS_X, 0, ALPS_V3_X_MAX, 0, 0);
 		input_set_abs_params(dev1, ABS_Y, 0, ALPS_V3_Y_MAX, 0, 0);
 		break;
--- linux-3.3/drivers/input/mouse/alps.h.orig	2012-04-16 
16:39:49.948823942 +0300
+++ linux-3.3/drivers/input/mouse/alps.h	2012-04-17 03:20:03.656594368 
+0300
@@ -39,6 +39,8 @@
 	int prev_fin;			/* Finger bit from previous packet */
 	int multi_packet;		/* Multi-packet data in progress */
 	unsigned char multi_data[6];	/* Saved multi-packet data */
+	int x1, x2, y1, y2;		/* Coordinates from last MT report */
+	int fingers;			/* Number of fingers from MT report */
 	u8 quirks;
 	struct timer_list timer;
 };


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: ALPS v4 Semi-mt Support
  2012-04-16 21:24       ` Seth Forshee
@ 2012-04-16 22:21         ` George Pantalos
  2012-04-17 13:16           ` Seth Forshee
  2012-04-17  0:52         ` George Pantalos
  1 sibling, 1 reply; 10+ messages in thread
From: George Pantalos @ 2012-04-16 22:21 UTC (permalink / raw)
  To: Seth Forshee; +Cc: linux-input

On Monday 16 of April 2012 16:24:07 Seth Forshee wrote:
> If the latency really is noticible when you stash the ST points, here's
> what I'd suggest trying instead. Stash away the last set of MT data you
> saw and repeat it with each of the next two ST coordinates. I suspect
> that will probably work well enough, and will allow every ST point to
> still be reported. And it should significantly simplify the code as
> well.

I will try to do that, thanks.

> If you see the sync bit set, it's _always_ the first fragment of the MT
> data, so you shoule _always_ reset the position. Why should past data
> have any effect on this decision?

I have noticed that the sync bit can at times be set in the second packet of 
the sequence. Couldn't this reset the position to priv->multi_packet=0 when 
in fact we are in priv->multi_packet=1  position?
I have also thought about "if((packet[6] & 0x40) && (priv->multi_packet == 
0))" so that sync is not lost. 
 
> This doesn't really re-sync the position, and the sync bit is sufficient
> for this purpose anyway. I'd propose that if you really think checking
> multi_data[4] is beneficial, use it only for validating the MT packet
> before parsing it.

OK, I have tried that before, thanks for the suggestion.

> Even if you use a separate case here you need to update the other
> BTN_TOOL keys. The 1 to 0 transition is needed for userspace to know
> that the situation has changed. Failing to report any value is not the
> same as reporting a value of 0.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: ALPS v4 Semi-mt Support
  2012-04-16 14:24     ` George Pantalos
@ 2012-04-16 21:24       ` Seth Forshee
  2012-04-16 22:21         ` George Pantalos
  2012-04-17  0:52         ` George Pantalos
  0 siblings, 2 replies; 10+ messages in thread
From: Seth Forshee @ 2012-04-16 21:24 UTC (permalink / raw)
  To: George Pantalos; +Cc: linux-input

On Mon, Apr 16, 2012 at 05:24:55PM +0300, George Pantalos wrote:
>  I decided to try and improve upon your patch which has superior state 
> processing. I followed your proposal initially and stashed the first and 
> second packet ST data in alps_data and if bitmap fingers < 2 report all the ST 
> data.
> The problem with this approach is that it introduces a noticeable delay/lag in 
> mouse pointer movement.
> 
> I then tried using the last_fingers approach.  This way we report ST data as 
> they come but only if the last MT report had 1 finger present and we always 
> (as far as I can tell) must have calculated bitmap finger count before 
> reporting ST events. I have posted this patch below. I also observed jumpy 
> behavior with the xf86-input-multitouch driver when the MT report had 1 
> finger, so if bitmap fingers are 1 , it uses x,y instead of x1,y1.
> This approach has the benefit of producing smooth pointer movement and 
> accurately reporting MT events like the other approach.

If the latency really is noticible when you stash the ST points, here's
what I'd suggest trying instead. Stash away the last set of MT data you
saw and repeat it with each of the next two ST coordinates. I suspect
that will probably work well enough, and will allow every ST point to
still be reported. And it should significantly simplify the code as
well.

> To handle inconsistent data I used your patch to dump raw packets.  I noticed 
> ,as your documentation also states, that the condition (packet[6] & 0x40) 
> could also be triggered by the second MT packet and then reset 
> multi_packet to 0.
> So I used the fact that byte 5 of the MT data, priv->multi_data [4] must 
> always be 0x00. A kind of second sync byte to ensure we have correct MT 
> reports. Unless the MT data is consistent we report nothing.

You should keep in mind that the documentation is based purely on my
observations. I observed that the 4th byte of the assembled MT data
packet is alwyas 0, so that's how I documented it. That doesn't
necessarily mean that it really is always 0, just that I could never get
it to assume any other value.

> +	if ((packet[6] & 0x40) && (priv->multi_data[4] == 0x00)) {
> +		/* sync, reset position */
> +		priv->multi_packet = 0;
> +	}

If you see the sync bit set, it's _always_ the first fragment of the MT
data, so you shoule _always_ reset the position. Why should past data
have any effect on this decision?

> +	/*
> +	 * The 5th byte of the MT data must always be 0x00.
> +	 * Try to re-sync our position if MT data is inconsistent.
> +	 */
> +	if (priv->multi_data[4] != 0x00)
> +		return;

This doesn't really re-sync the position, and the sync bit is sufficient
for this purpose anyway. I'd propose that if you really think checking
multi_data[4] is beneficial, use it only for validating the MT packet
before parsing it.

> +	} else {
> +		if (priv->last_fingers == 1) {
> +			if (z >= 64)
> +				input_report_key(dev, BTN_TOUCH, 1);
> +			else
> +				input_report_key(dev, BTN_TOUCH, 0);
> +
> +			alps_report_semi_mt_data(dev, 1, x, y, 0, 0);
> +
> +			input_report_key(dev, BTN_TOOL_FINGER, z > 0);

Even if you use a separate case here you need to update the other
BTN_TOOL keys. The 1 to 0 transition is needed for userspace to know
that the situation has changed. Failing to report any value is not the
same as reporting a value of 0.


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: ALPS v4 Semi-mt Support
  2012-04-10 21:59   ` Seth Forshee
@ 2012-04-16 14:24     ` George Pantalos
  2012-04-16 21:24       ` Seth Forshee
  0 siblings, 1 reply; 10+ messages in thread
From: George Pantalos @ 2012-04-16 14:24 UTC (permalink / raw)
  To: Seth Forshee; +Cc: linux-input

> On Tue, Apr 10, 2012 at 10:21:13AM -0500, Seth Forshee wrote:
> > I definitely think your state processing could be improved. My
> > suggestion would be to treat multi_packet as a counter. Reset it to 0
> > when you see the sync bit is set, and increment it for each packet until
> > you have a full set of MT data. That way you know for sure which section
> > of the MT data you're working with for any given packet. But be prepared
> > to handle an incomplete packet sequence as well (I'm pretty sure I saw
> > some of these when I was working with a v4 touchpad).
> 
> I found an old patch I had from working on the semi-MT support
> previously that demonstrates the multi_packet-as-counter approach. I
> ported the relevant code on top of 3.4-rc2, cleaned it up, compile
> tested it, and pasted the resulting patch below.
> 
> Note that this is ignoring the ST coordinates except from the final
> packet of the MT sequence, which isn't ideal. A better approach might be
> to stash the ST data from each packet in alps_data, then when you have
> all three packets process the bitmaps and report all three ST points
> with the same set of MT data.

Hello Seth,

First of all, sorry for answering so late, I had no internet access for the 
past week.

Thank you for your comments and for posting your patch. You are of course 
correct in your remarks.

 I decided to try and improve upon your patch which has superior state 
processing. I followed your proposal initially and stashed the first and 
second packet ST data in alps_data and if bitmap fingers < 2 report all the ST 
data.
The problem with this approach is that it introduces a noticeable delay/lag in 
mouse pointer movement.

I then tried using the last_fingers approach.  This way we report ST data as 
they come but only if the last MT report had 1 finger present and we always 
(as far as I can tell) must have calculated bitmap finger count before 
reporting ST events. I have posted this patch below. I also observed jumpy 
behavior with the xf86-input-multitouch driver when the MT report had 1 
finger, so if bitmap fingers are 1 , it uses x,y instead of x1,y1.
This approach has the benefit of producing smooth pointer movement and 
accurately reporting MT events like the other approach.

To handle inconsistent data I used your patch to dump raw packets.  I noticed 
,as your documentation also states, that the condition (packet[6] & 0x40) 
could also be triggered by the second MT packet and then reset 
multi_packet to 0.
So I used the fact that byte 5 of the MT data, priv->multi_data [4] must 
always be 0x00. A kind of second sync byte to ensure we have correct MT 
reports. Unless the MT data is consistent we report nothing.

Thanks for your help and guidance.

--- linux-3.3/drivers/input/mouse/alps.c.orig	2012-04-15 21:45:18.083826446 
+0300
+++ linux-3.3/drivers/input/mouse/alps.c	2012-04-16 16:33:11.412181964 
+0300
@@ -604,10 +604,39 @@
 
 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;
+	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
+	 * broken up between 3 normal packets. Use priv->multi_packet to
+	 * track our position in the bitmap packet.
+	 */
+	if ((packet[6] & 0x40) && (priv->multi_data[4] == 0x00)) {
+		/* sync, reset position */
+		priv->multi_packet = 0;
+	}
+
+	if (WARN_ON_ONCE(priv->multi_packet > 2))
+		return;
+
+	offset = 2 * priv->multi_packet;
+	priv->multi_data[offset] = packet[6];
+	priv->multi_data[offset + 1] = packet[7];
+
+	/*
+	 * The 5th byte of the MT data must always be 0x00.
+	 * Try to re-sync our position if MT data is inconsistent.
+	 */
+	if (priv->multi_data[4] != 0x00)
+		return;
 
 	left = packet[4] & 0x01;
 	right = packet[4] & 0x02;
@@ -617,22 +646,78 @@
 	y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f);
 	z = packet[5] & 0x7f;
 
-	if (z >= 64)
-		input_report_key(dev, BTN_TOUCH, 1);
-	else
-		input_report_key(dev, BTN_TOUCH, 0);
 
-	if (z > 0) {
-		input_report_abs(dev, ABS_X, x);
-		input_report_abs(dev, ABS_Y, y);
-	}
-	input_report_abs(dev, ABS_PRESSURE, z);
+	if (++priv->multi_packet > 2) {
+		priv->multi_packet = 0;
+
+		x_bitmap = ((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) |
+			   ((priv->multi_data[3] & 0x1f) << 5) |
+			   (priv->multi_data[1] & 0x1f);
+
+		fingers = alps_process_bitmap(x_bitmap, y_bitmap,
+					      &x1, &y1, &x2, &y2);
 
-	input_report_key(dev, BTN_TOOL_FINGER, z > 0);
-	input_report_key(dev, BTN_LEFT, left);
-	input_report_key(dev, BTN_RIGHT, right);
+		/*
+		 * If there were no contacts in the bitmap, use ST
+		 * points in MT reports.
+		 */
+		if (fingers < 2) {
+			x1 = x;
+			y1 = y;
+			fingers = z > 0 ? 1 : 0;
+		}
+
+		priv->last_fingers = fingers;
+
+		if (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_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
+		input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
+		input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
+		input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4);
+
+		input_report_key(dev, BTN_LEFT, left);
+		input_report_key(dev, BTN_RIGHT, right);
+
+		if (z > 0) {
+			input_report_abs(dev, ABS_X, x);
+			input_report_abs(dev, ABS_Y, y);
+		}
+		input_report_abs(dev, ABS_PRESSURE, z);
+
+		input_sync(dev);
+	} else {
+		if (priv->last_fingers == 1) {
+			if (z >= 64)
+				input_report_key(dev, BTN_TOUCH, 1);
+			else
+				input_report_key(dev, BTN_TOUCH, 0);
+
+			alps_report_semi_mt_data(dev, 1, x, y, 0, 0);
+
+			input_report_key(dev, BTN_TOOL_FINGER, z > 0);
+
+			input_report_key(dev, BTN_LEFT, left);
+			input_report_key(dev, BTN_RIGHT, right);
+
+			if (z > 0) {
+				input_report_abs(dev, ABS_X, x);
+				input_report_abs(dev, ABS_Y, y);
+			}
+			input_report_abs(dev, ABS_PRESSURE, z);
 
-	input_sync(dev);
+			input_sync(dev);
+		}
+	}
 }
 
 static void alps_process_packet(struct psmouse *psmouse)
@@ -1557,6 +1642,7 @@
 		input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0);
 		break;
 	case ALPS_PROTO_V3:
+	case ALPS_PROTO_V4:
 		set_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
 		input_mt_init_slots(dev1, 2);
 		input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, 
ALPS_V3_X_MAX, 0, 0);
@@ -1565,8 +1651,7 @@
 		set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit);
 		set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit);
 		set_bit(BTN_TOOL_QUADTAP, dev1->keybit);
-		/* fall through */
-	case ALPS_PROTO_V4:
+
 		input_set_abs_params(dev1, ABS_X, 0, ALPS_V3_X_MAX, 0, 0);
 		input_set_abs_params(dev1, ABS_Y, 0, ALPS_V3_Y_MAX, 0, 0);
 		break;
--- linux-3.3/drivers/input/mouse/alps.h.orig	2012-04-16 
16:39:49.948823942 +0300
+++ linux-3.3/drivers/input/mouse/alps.h	2012-04-16 16:41:28.228817760 
+0300
@@ -39,6 +39,7 @@
 	int prev_fin;			/* Finger bit from previous packet */
 	int multi_packet;		/* Multi-packet data in progress */
 	unsigned char multi_data[6];	/* Saved multi-packet data */
+	int last_fingers;		/* Number of fingers from MT report*/
 	u8 quirks;
 	struct timer_list timer;
 };



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: ALPS v4 Semi-mt Support
  2012-04-10 15:21 ` Seth Forshee
@ 2012-04-10 21:59   ` Seth Forshee
  2012-04-16 14:24     ` George Pantalos
  0 siblings, 1 reply; 10+ messages in thread
From: Seth Forshee @ 2012-04-10 21:59 UTC (permalink / raw)
  To: George Pantalos; +Cc: linux-input

On Tue, Apr 10, 2012 at 10:21:13AM -0500, Seth Forshee wrote:
> I definitely think your state processing could be improved. My
> suggestion would be to treat multi_packet as a counter. Reset it to 0
> when you see the sync bit is set, and increment it for each packet until
> you have a full set of MT data. That way you know for sure which section
> of the MT data you're working with for any given packet. But be prepared
> to handle an incomplete packet sequence as well (I'm pretty sure I saw
> some of these when I was working with a v4 touchpad).

I found an old patch I had from working on the semi-MT support
previously that demonstrates the multi_packet-as-counter approach. I
ported the relevant code on top of 3.4-rc2, cleaned it up, compile
tested it, and pasted the resulting patch below.

Note that this is ignoring the ST coordinates except from the final
packet of the MT sequence, which isn't ideal. A better approach might be
to stash the ST data from each packet in alps_data, then when you have
all three packets process the bitmaps and report all three ST points
with the same set of MT data.

Cheers,
Seth


diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index 4c6a72d..8cc0dbf 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -604,35 +604,88 @@ static void alps_process_packet_v3(struct psmouse *psmouse)
 
 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;
+	int offset;
 	int x, y, z;
 	int left, right;
+	int x1, y1, x2, y2;
+	int fingers = 0;
+	unsigned int x_bitmap, y_bitmap;
 
-	left = packet[4] & 0x01;
-	right = packet[4] & 0x02;
+	/*
+	 * v4 has a 6-byte encoding for bitmap data, but this data is
+	 * broken up between 3 normal packets. Use priv->multi_packet to
+	 * track our position in the bitmap packet.
+	 */
+	if (packet[6] & 0x40) {
+		/* sync, reset position */
+		priv->multi_packet = 0;
+	}
 
-	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;
+	if (WARN_ON_ONCE(priv->multi_packet > 2))
+		return;
 
-	if (z >= 64)
-		input_report_key(dev, BTN_TOUCH, 1);
-	else
-		input_report_key(dev, BTN_TOUCH, 0);
+	offset = 2 * priv->multi_packet;
+	priv->multi_data[offset] = packet[6];
+	priv->multi_data[offset + 1] = packet[7];
 
-	if (z > 0) {
-		input_report_abs(dev, ABS_X, x);
-		input_report_abs(dev, ABS_Y, y);
-	}
-	input_report_abs(dev, ABS_PRESSURE, z);
+	if (++priv->multi_packet > 2) {
+		priv->multi_packet = 0;
 
-	input_report_key(dev, BTN_TOOL_FINGER, z > 0);
-	input_report_key(dev, BTN_LEFT, left);
-	input_report_key(dev, BTN_RIGHT, right);
+		left = packet[4] & 0x01;
+		right = packet[4] & 0x02;
 
-	input_sync(dev);
+		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;
+
+		x_bitmap = ((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) |
+			   ((priv->multi_data[3] & 0x1f) << 5) |
+			   (priv->multi_data[1] & 0x1f);
+
+		fingers = alps_process_bitmap(x_bitmap, y_bitmap,
+					      &x1, &y1, &x2, &y2);
+
+		/*
+		 * If there were no contacts in the bitmap, use ST
+		 * points in MT reports.
+		 */
+		if (fingers == 0) {
+			x1 = x;
+			y1 = y;
+			fingers = 1;
+		}
+
+		if (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_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
+		input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
+		input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
+		input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4);
+
+		input_report_key(dev, BTN_LEFT, left);
+		input_report_key(dev, BTN_RIGHT, right);
+
+		if (z > 0) {
+			input_report_abs(dev, ABS_X, x);
+			input_report_abs(dev, ABS_Y, y);
+		}
+		input_report_abs(dev, ABS_PRESSURE, z);
+
+		input_sync(dev);
+	}
 }
 
 static void alps_process_packet(struct psmouse *psmouse)
@@ -1557,6 +1610,7 @@ int alps_init(struct psmouse *psmouse)
 		input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0);
 		break;
 	case ALPS_PROTO_V3:
+	case ALPS_PROTO_V4:
 		set_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
 		input_mt_init_slots(dev1, 2);
 		input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, ALPS_V3_X_MAX, 0, 0);
@@ -1565,8 +1619,7 @@ int alps_init(struct psmouse *psmouse)
 		set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit);
 		set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit);
 		set_bit(BTN_TOOL_QUADTAP, dev1->keybit);
-		/* fall through */
-	case ALPS_PROTO_V4:
+
 		input_set_abs_params(dev1, ABS_X, 0, ALPS_V3_X_MAX, 0, 0);
 		input_set_abs_params(dev1, ABS_Y, 0, ALPS_V3_Y_MAX, 0, 0);
 		break;

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: ALPS v4 Semi-mt Support
  2012-04-10 14:02 George Pantalos
@ 2012-04-10 15:21 ` Seth Forshee
  2012-04-10 21:59   ` Seth Forshee
  0 siblings, 1 reply; 10+ messages in thread
From: Seth Forshee @ 2012-04-10 15:21 UTC (permalink / raw)
  To: George Pantalos; +Cc: linux-input

Hi George,

Thanks for the patch. I've got some specific comments inline below, but
here are some general comments.

The reason I've never followed through with the v4 semi-MT support is
that I lost access to the hardware before I could complete it. When I
had the hardware the thing I struggled with was how to handle receiving
only one full set of MT data for every 3 sets of ST data. It looks like
you've opted to only report every third ST data point (if the previous
MT packet showed more than 1 touch, more on that below), when you also
have a complete set of MT data. That's one possible approach, but I'm
not sure that it's the best. Hopefully some of the input gurus on the
list can comment. As I recall the main difficulty with reporting all ST
points was not knowing the finger count until the third packet of the
sequence, so I was leaning towards accumulating the data and processing
it all in one go after the third packet in the sequence arrived.

I definitely think your state processing could be improved. My
suggestion would be to treat multi_packet as a counter. Reset it to 0
when you see the sync bit is set, and increment it for each packet until
you have a full set of MT data. That way you know for sure which section
of the MT data you're working with for any given packet. But be prepared
to handle an incomplete packet sequence as well (I'm pretty sure I saw
some of these when I was working with a v4 touchpad).

On Tue, Apr 10, 2012 at 05:02:41PM +0300, George Pantalos wrote:
> --- drivers/input/mouse/alps.c	2012-03-19 01:15:34.000000000 +0200
> +++ drivers/input/mouse/alps.c.new	2012-04-09 17:14:25.193621808 +0300
> @@ -604,11 +604,59 @@
>  
>  static void alps_process_packet_v4(struct psmouse *psmouse)
>  {
> +        struct alps_data *priv = psmouse->private;

Should be a tab instead of spaces. You've got spaces for indentation at
some other places as well. Try running checkpatch.pl (in the scripts
directory of the kernel source tree) to catch these types of problems.

>  	unsigned char *packet = psmouse->packet;
>  	struct input_dev *dev = psmouse->dev;
>  	int x, y, z;
>  	int left, right;
> +	int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
> +	int fingers = 0, bmap_fingers;
> +	unsigned int x_bitmap, y_bitmap;
> +
> +	if (priv->multi_packet) {
> +          if (packet[6] != 0x00) { 

This condition may not work all the time. Based on the documentaiton I
wrote at least it looks like byte 6 in the second packet of the sequence
could be zero, but I can't remember for sure.

> +		priv->multi_data_v4[2] = packet[6];
> +		priv->multi_data_v4[3] = packet[7];
> +		priv->multi_packet = 1;
> +		if (priv->last_fingers > 1)
> +		return;

The body of if statements needs to be indented.

> +          } else {
> +		priv->multi_data_v4[4] = packet[6];
> +		priv->multi_data_v4[5] = packet[7];
> +
> +		fingers = 0;
> +		priv->multi_packet = 0;
> +		x_bitmap = ((priv->multi_data_v4[2] & 0x1f) << 10) | /*0x1f*/
> +			   ((priv->multi_data_v4[3] & 0x60) << 3) |
> +			   ((priv->multi_data_v4[0] & 0x3f) << 2) |
> +			   ((priv->multi_data_v4[1] & 0x60) >> 5);
> +		y_bitmap = ((priv->multi_data_v4[5] & 0x01) << 10) |
> +			   ((priv->multi_data_v4[3] & 0x1f) << 5) |
> +			    (priv->multi_data_v4[1] & 0x1f);
> +	  
> +		bmap_fingers = alps_process_bitmap(x_bitmap, y_bitmap,
> +						  &x1, &y1, &x2, &y2);
> +		if (bmap_fingers > 1) {
> +			fingers = bmap_fingers;
> +		}

Braces aren't needed when there's only a single line in the body of an
if statement.

> +	  
> +		priv->last_fingers = fingers;
>  
> +          /*packet = priv->multi_data;*/
> +          /*packet = packet;*/

Delete the commented out lines if they aren't needed.

> +	  }
> +	}
> +
> +	if (!priv->multi_packet && (packet[6] & 0x40)) {
> +		priv->multi_packet = 1;
> +		priv->multi_data_v4[0] = packet[6];
> +		priv->multi_data_v4[1] = packet[7];
> +		if (priv->last_fingers > 1)
> +		return;

So you're deciding how to handle the current packet sequence based on
the number of touch points in the previous sequence. You lose a couple
of ST points then each time you transition from multiple touches to a
single touch. That might be okay, depending on what kind of feedback is
received on how to handle the packet sequences.

> +	}
> +	
> +        /*priv->multi_packet = 0;*/

Again, if this isn't needed just delete it.

> +        
>  	left = packet[4] & 0x01;
>  	right = packet[4] & 0x02;
>  
> @@ -617,21 +665,33 @@
>  	y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f);
>  	z = packet[5] & 0x7f;
>  
> +	if (!fingers) {
> +		x1 = x;
> +		y1 = y;
> +		fingers = z > 0 ? 1 : 0;
> +	}
> +
>  	if (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_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
> +	input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
> +	input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
> +	input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4);
> +
> +	input_report_key(dev, BTN_LEFT, left);
> +	input_report_key(dev, BTN_RIGHT, right);
> +
>  	if (z > 0) {
>  		input_report_abs(dev, ABS_X, x);
>  		input_report_abs(dev, ABS_Y, y);
>  	}
>  	input_report_abs(dev, ABS_PRESSURE, z);
>  
> -	input_report_key(dev, BTN_TOOL_FINGER, z > 0);
> -	input_report_key(dev, BTN_LEFT, left);
> -	input_report_key(dev, BTN_RIGHT, right);
> -
>  	input_sync(dev);
>  }
>  
> @@ -1567,8 +1627,18 @@
>  		set_bit(BTN_TOOL_QUADTAP, dev1->keybit);
>  		/* fall through */
>  	case ALPS_PROTO_V4:
> -		input_set_abs_params(dev1, ABS_X, 0, ALPS_V3_X_MAX, 0, 0);
> -		input_set_abs_params(dev1, ABS_Y, 0, ALPS_V3_Y_MAX, 0, 0);
> +		set_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
> +		input_mt_init_slots(dev1, 2);
> +                input_set_abs_params(dev1, ABS_X, 0, ALPS_V3_X_MAX, 0, 0);
> +                input_set_abs_params(dev1, ABS_Y, 0, ALPS_V3_Y_MAX, 0, 0);
> +
> +		input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, 
> ALPS_V3_X_MAX, 0, 0);
> +		input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, 
> ALPS_V3_Y_MAX, 0, 0);
> +
> +		set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit);
> +		set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit);
> +		set_bit(BTN_TOOL_QUADTAP, dev1->keybit);
> +		/* fall through */
>  		break;
>  	}
>  
> --- drivers/input/mouse/alps.h	2012-03-19 01:15:34.000000000 +0200
> +++ drivers/input/mouse/alps.h.new	2012-04-09 17:17:32.733618703 +0300
> @@ -39,6 +39,8 @@
>  	int prev_fin;			/* Finger bit from previous packet */
>  	int multi_packet;		/* Multi-packet data in progress */
>  	unsigned char multi_data[6];	/* Saved multi-packet data */
> +	unsigned char multi_data_v4[6];

Why do you need multi_data_v4? You should be able to use multi_data.

> +	int last_fingers;
>  	u8 quirks;
>  	struct timer_list timer;
>  };
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 10+ messages in thread

* ALPS v4 Semi-mt Support
@ 2012-04-10 14:02 George Pantalos
  2012-04-10 15:21 ` Seth Forshee
  0 siblings, 1 reply; 10+ messages in thread
From: George Pantalos @ 2012-04-10 14:02 UTC (permalink / raw)
  To: linux-input

--- drivers/input/mouse/alps.c	2012-03-19 01:15:34.000000000 +0200
+++ drivers/input/mouse/alps.c.new	2012-04-09 17:14:25.193621808 +0300
@@ -604,11 +604,59 @@
 
 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;
 	int x, y, z;
 	int left, right;
+	int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
+	int fingers = 0, bmap_fingers;
+	unsigned int x_bitmap, y_bitmap;
+
+	if (priv->multi_packet) {
+          if (packet[6] != 0x00) { 
+		priv->multi_data_v4[2] = packet[6];
+		priv->multi_data_v4[3] = packet[7];
+		priv->multi_packet = 1;
+		if (priv->last_fingers > 1)
+		return;
+          } else {
+		priv->multi_data_v4[4] = packet[6];
+		priv->multi_data_v4[5] = packet[7];
+
+		fingers = 0;
+		priv->multi_packet = 0;
+		x_bitmap = ((priv->multi_data_v4[2] & 0x1f) << 10) | /*0x1f*/
+			   ((priv->multi_data_v4[3] & 0x60) << 3) |
+			   ((priv->multi_data_v4[0] & 0x3f) << 2) |
+			   ((priv->multi_data_v4[1] & 0x60) >> 5);
+		y_bitmap = ((priv->multi_data_v4[5] & 0x01) << 10) |
+			   ((priv->multi_data_v4[3] & 0x1f) << 5) |
+			    (priv->multi_data_v4[1] & 0x1f);
+	  
+		bmap_fingers = alps_process_bitmap(x_bitmap, y_bitmap,
+						  &x1, &y1, &x2, &y2);
+		if (bmap_fingers > 1) {
+			fingers = bmap_fingers;
+		}
+	  
+		priv->last_fingers = fingers;
 
+          /*packet = priv->multi_data;*/
+          /*packet = packet;*/
+	  }
+	}
+
+	if (!priv->multi_packet && (packet[6] & 0x40)) {
+		priv->multi_packet = 1;
+		priv->multi_data_v4[0] = packet[6];
+		priv->multi_data_v4[1] = packet[7];
+		if (priv->last_fingers > 1)
+		return;
+	}
+	
+        /*priv->multi_packet = 0;*/
+        
 	left = packet[4] & 0x01;
 	right = packet[4] & 0x02;
 
@@ -617,21 +665,33 @@
 	y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f);
 	z = packet[5] & 0x7f;
 
+	if (!fingers) {
+		x1 = x;
+		y1 = y;
+		fingers = z > 0 ? 1 : 0;
+	}
+
 	if (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_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
+	input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
+	input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
+	input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4);
+
+	input_report_key(dev, BTN_LEFT, left);
+	input_report_key(dev, BTN_RIGHT, right);
+
 	if (z > 0) {
 		input_report_abs(dev, ABS_X, x);
 		input_report_abs(dev, ABS_Y, y);
 	}
 	input_report_abs(dev, ABS_PRESSURE, z);
 
-	input_report_key(dev, BTN_TOOL_FINGER, z > 0);
-	input_report_key(dev, BTN_LEFT, left);
-	input_report_key(dev, BTN_RIGHT, right);
-
 	input_sync(dev);
 }
 
@@ -1567,8 +1627,18 @@
 		set_bit(BTN_TOOL_QUADTAP, dev1->keybit);
 		/* fall through */
 	case ALPS_PROTO_V4:
-		input_set_abs_params(dev1, ABS_X, 0, ALPS_V3_X_MAX, 0, 0);
-		input_set_abs_params(dev1, ABS_Y, 0, ALPS_V3_Y_MAX, 0, 0);
+		set_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
+		input_mt_init_slots(dev1, 2);
+                input_set_abs_params(dev1, ABS_X, 0, ALPS_V3_X_MAX, 0, 0);
+                input_set_abs_params(dev1, ABS_Y, 0, ALPS_V3_Y_MAX, 0, 0);
+
+		input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, 
ALPS_V3_X_MAX, 0, 0);
+		input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, 
ALPS_V3_Y_MAX, 0, 0);
+
+		set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit);
+		set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit);
+		set_bit(BTN_TOOL_QUADTAP, dev1->keybit);
+		/* fall through */
 		break;
 	}
 
--- drivers/input/mouse/alps.h	2012-03-19 01:15:34.000000000 +0200
+++ drivers/input/mouse/alps.h.new	2012-04-09 17:17:32.733618703 +0300
@@ -39,6 +39,8 @@
 	int prev_fin;			/* Finger bit from previous packet */
 	int multi_packet;		/* Multi-packet data in progress */
 	unsigned char multi_data[6];	/* Saved multi-packet data */
+	unsigned char multi_data_v4[6];
+	int last_fingers;
 	u8 quirks;
 	struct timer_list timer;
 };


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2012-04-17 15:22 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-04-10 14:01 ALPS v4 Semi-mt Support George Pantalos
2012-04-10 14:02 George Pantalos
2012-04-10 15:21 ` Seth Forshee
2012-04-10 21:59   ` Seth Forshee
2012-04-16 14:24     ` George Pantalos
2012-04-16 21:24       ` Seth Forshee
2012-04-16 22:21         ` George Pantalos
2012-04-17 13:16           ` Seth Forshee
2012-04-17  0:52         ` George Pantalos
2012-04-17 15:22           ` Seth Forshee

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.