Re: [PATCH v3] psmouse: Add some support for the FocalTech PS/2 protocol extensions.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

On 10/31/2014 11:57 PM, Mathias Gottschlag wrote:
> Am 31.10.2014 um 14:29 schrieb Hans de Goede:
>> Hi Mathias,
>>
>> Thanks for your continued work on this. 3 remarks online + 1 at the bottom.
>>
>> On 10/30/2014 07:33 PM, Mathias Gottschlag wrote:
>>> Most of the protocol for these touchpads has been reverse engineered. This
>>> commit adds a basic multitouch-capable driver.
>>>
>>> A lot of the protocol is still unknown. Especially, we don't know how to
>>> identify the device yet apart from the PNP ID.
>>>
>>> The previous workaround for these devices has been left in place in case
>>> the driver is not compiled into the kernel or in case some other device
>>> with the same PNP ID is not recognized by the driver yet still has the
>>> same
>>> problems with the device probing code.
>> Missing "Signed-off-by: Mathias Gottschlag <mgottschlag@xxxxxxxxx>", we need
>> this before this patch can be merged (just put it at the end of the commit
>> message (official end, before the changelog) when you send the next version).
>>
>>
>>> ---
>>>
>>> (Sorry, some serious incompetence caused me to always test an old
>>> version of the module, so I overlooked an embarrasing pagefault right
>>> during initialization, where memory allocation did not happen early
>>> enough)
>>>
>>> Thanks for the first round of review, I hope I addressed all comments.
>>> Changes compared to the last version:
>>> - The detection code does not compare all registers anymore.
>>> - The driver now reads the size of the touchpad during initialization.
>>> This should add support for Asus X450JN, where the touchpad is a bit
>>> wider.
>>> - set_input_params has been simplified.
>>> - fingers are now valid=0 when the touchpad reports a large object.
>>>
>>>  drivers/input/mouse/Kconfig        |  10 ++
>>>  drivers/input/mouse/focaltech.c    | 300
>>> ++++++++++++++++++++++++++++++++++++-
>> Your mail client has line-wrapped this line, and many others further below,
>> making it impossible to apply this, please resend using "git send-email" .
>>
>>>  drivers/input/mouse/focaltech.h    |  60 ++++++++
>>>  drivers/input/mouse/psmouse-base.c |  32 ++--
>>>  drivers/input/mouse/psmouse.h      |   1 +
>>>  5 files changed, 386 insertions(+), 17 deletions(-)
>>>
>>> diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
>>> index 366fc7a..db973e5 100644
>>> --- a/drivers/input/mouse/Kconfig
>>> +++ b/drivers/input/mouse/Kconfig
>>> @@ -146,6 +146,16 @@ config MOUSE_PS2_OLPC
>>>   	  If unsure, say N.
>>>  +config MOUSE_PS2_FOCALTECH
>>> +	bool "FocalTech PS/2 mouse protocol extension" if EXPERT
>>> +	default y
>>> +	depends on MOUSE_PS2
>>> +	help
>>> +	  Say Y here if you have a FocalTech PS/2 TouchPad connected to
>>> +	  your system.
>>> +
>>> +	  If unsure, say Y.
>>> +
>>>  config MOUSE_SERIAL
>>>  	tristate "Serial mouse"
>>>  	select SERIO
>>> diff --git a/drivers/input/mouse/focaltech.c
>>> b/drivers/input/mouse/focaltech.c
>>> index f4d657e..26bc5b7 100644
>>> --- a/drivers/input/mouse/focaltech.c
>>> +++ b/drivers/input/mouse/focaltech.c
>>> @@ -2,6 +2,7 @@
>>>   * Focaltech TouchPad PS/2 mouse driver
>>>   *
>>>   * Copyright (c) 2014 Red Hat Inc.
>>> + * Copyright (c) 2014 Mathias Gottschlag <mgottschlag@xxxxxxxxx>
>>>   *
>>>   * This program is free software; you can redistribute it and/or modify
>>>   * it under the terms of the GNU General Public License as published by
>>> @@ -13,15 +14,14 @@
>>>   * Hans de Goede <hdegoede@xxxxxxxxxx>
>>>   */
>>>  -/*
>>> - * The Focaltech PS/2 touchpad protocol is unknown. This drivers
>>> deals with
>>> - * detection only, to avoid further detection attempts confusing the
>>> touchpad
>>> - * this way it at least works in PS/2 mouse compatibility mode.
>>> - */
>>>   #include <linux/device.h>
>>>  #include <linux/libps2.h>
>>> +#include <linux/input/mt.h>
>>> +#include <linux/serio.h>
>>> +#include <linux/slab.h>
>>>  #include "psmouse.h"
>>> +#include "focaltech.h"
>>>   static const char * const focaltech_pnp_ids[] = {
>>>  	"FLT0101",
>>> @@ -30,6 +30,12 @@ static const char * const focaltech_pnp_ids[] = {
>>>  	NULL
>>>  };
>>>  +/*
>>> + * Even if the kernel is built without support for Focaltech PS/2
>>> touchpads (or
>>> + * when the real driver fails to recognize the device), we still have
>>> to detect
>>> + * them in order to avoid further detection attempts confusing the
>>> touchpad.
>>> + * This way it at least works in PS/2 mouse compatibility mode.
>>> + */
>>>  int focaltech_detect(struct psmouse *psmouse, bool set_properties)
>>>  {
>>>  	if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids))
>>> @@ -37,16 +43,296 @@ int focaltech_detect(struct psmouse *psmouse,
>>> bool set_properties)
>>>   	if (set_properties) {
>>>  		psmouse->vendor = "FocalTech";
>>> -		psmouse->name = "FocalTech Touchpad in mouse emulation mode";
>>> +		psmouse->name = "FocalTech Touchpad";
>>>  	}
>>>   	return 0;
>>>  }
>>>  -int focaltech_init(struct psmouse *psmouse)
>>> +static void focaltech_reset(struct psmouse *psmouse)
>>>  {
>>>  	ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
>>>  	psmouse_reset(psmouse);
>>> +}
>>> +
>>> +#ifdef CONFIG_MOUSE_PS2_FOCALTECH
>>> +
>>> +static void focaltech_report_state(struct psmouse *psmouse)
>>> +{
>>> +	int i;
>>> +	struct focaltech_data *priv = psmouse->private;
>>> +	struct focaltech_hw_state *state = &priv->state;
>>> +	struct input_dev *dev = psmouse->dev;
>>> +	int finger_count = 0;
>>> +
>>> +	for (i = 0; i < FOC_MAX_FINGERS; i++) {
>>> +		struct focaltech_finger_state *finger = &state->fingers[i];
>>> +		int active = finger->active && finger->valid;
>>> +		input_mt_slot(dev, i);
>>> +		input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
>>> +		if (active) {
>>> +			finger_count++;
>>> +			input_report_abs(dev, ABS_MT_POSITION_X, finger->x);
>>> +			input_report_abs(dev, ABS_MT_POSITION_Y,
>>> +					focaltech_invert_y(finger->y));
>>> +		}
>>> +	}
>>> +	input_mt_report_pointer_emulation(dev, finger_count);
>>> +
>>> +	input_report_key(psmouse->dev, BTN_LEFT, state->pressed);
>>> +	input_sync(psmouse->dev);
>>> +}
>>> +
>>> +static void process_touch_packet(struct focaltech_hw_state *state,
>>> +		unsigned char *packet)
>>> +{
>>> +	int i;
>>> +	unsigned char fingers = packet[1];
>>> +
>>> +	state->pressed = (packet[0] >> 4) & 1;
>>> +	/* the second byte contains a bitmap of all fingers touching the pad */
>>> +	for (i = 0; i < FOC_MAX_FINGERS; i++) {
>>> +		if ((fingers & 0x1) && !state->fingers[i].active) {
>>> +			/* we do not have a valid position for the finger yet */
>>> +			state->fingers[i].valid = 0;
>>> +		}
>>> +		state->fingers[i].active = fingers & 0x1;
>>> +		fingers >>= 1;
>>> +	}
>>> +}
>>> +
>>> +static void process_abs_packet(struct focaltech_hw_state *state,
>>> +		unsigned char *packet)
>>> +{
>>> +	unsigned int finger = (packet[1] >> 4) - 1;
>>> +
>>> +	state->pressed = (packet[0] >> 4) & 1;
>>> +	if (finger >= FOC_MAX_FINGERS)
>>> +		return;
>>> +	/*
>>> +	 * packet[5] contains some kind of tool size in the most significant
>>> +	 * nibble. 0xff is a special value (latching) that signals a large
>>> +	 * contact area.
>>> +	 */
>>> +	if (packet[5] == 0xff) {
>>> +		state->fingers[finger].valid = 0;
>>> +		return;
>>> +	}
>>> +	state->fingers[finger].x = ((packet[1] & 0xf) << 8) | packet[2];
>>> +	state->fingers[finger].y = (packet[3] << 8) | packet[4];
>>> +	state->fingers[finger].valid = 1;
>>> +}
>>> +static void process_rel_packet(struct focaltech_hw_state *state,
>>> +		unsigned char *packet)
>>> +{
>>> +	int finger1 = ((packet[0] >> 4) & 0x7) - 1;
>>> +	int finger2 = ((packet[3] >> 4) & 0x7) - 1;
>>> +
>>> +	state->pressed = packet[0] >> 7;
>>> +	if (finger1 < FOC_MAX_FINGERS) {
>>> +		state->fingers[finger1].x += (char)packet[1];
>>> +		state->fingers[finger1].y += (char)packet[2];
>>> +	}
>>> +	/*
>>> +	 * If there is an odd number of fingers, the last relative packet only
>>> +	 * contains one finger. In this case, the second finger index in the
>>> +	 * packet is 0 (we subtract 1 in the lines above to create array
>>> +	 * indices).
>>> +	 */
>>> +	if (finger2 != -1 && finger2 < FOC_MAX_FINGERS) {
>>> +		state->fingers[finger2].x += (char)packet[4];
>>> +		state->fingers[finger2].y += (char)packet[5];
>>> +	}
>>> +}
>>> +
>>> +static void focaltech_process_packet(struct psmouse *psmouse)
>>> +{
>>> +	struct focaltech_data *priv = psmouse->private;
>>> +	unsigned char *packet = psmouse->packet;
>>> +
>>> +	switch (packet[0] & 0xf) {
>>> +	case FOC_TOUCH:
>>> +		process_touch_packet(&priv->state, packet);
>>> +		break;
>>> +	case FOC_ABS:
>>> +		process_abs_packet(&priv->state, packet);
>>> +		break;
>>> +	case FOC_REL:
>>> +		process_rel_packet(&priv->state, packet);
>>> +		break;
>>> +	default:
>>> +		psmouse_err(psmouse, "Unknown packet type: %02x", packet[0]);
>>> +		break;
>>> +	}
>>> +
>>> +	focaltech_report_state(psmouse);
>>> +}
>>> +
>>> +static psmouse_ret_t focaltech_process_byte(struct psmouse *psmouse)
>>> +{
>>> +	if (psmouse->pktcnt >= 6) { /* Full packet received */
>>> +		focaltech_process_packet(psmouse);
>>> +		return PSMOUSE_FULL_PACKET;
>>> +	}
>>> +	/*
>>> +	 * we might want to do some validation of the data here, but we do not
>>> +	 * know the protocoll well enough
>>> +	 */
>>> +	return PSMOUSE_GOOD_DATA;
>>> +}
>>> +
>>> +static int focaltech_switch_protocol(struct psmouse *psmouse)
>>> +{
>>> +	struct ps2dev *ps2dev = &psmouse->ps2dev;
>>> +	unsigned char param[3];
>>> +
>>> +	param[0] = 0;
>>> +	if (ps2_command(ps2dev, param, 0x10f8))
>>> +		return -EIO;
>>> +	if (ps2_command(ps2dev, param, 0x10f8))
>>> +		return -EIO;
>>> +	if (ps2_command(ps2dev, param, 0x10f8))
>>> +		return -EIO;
>>> +	param[0] = 1;
>>> +	if (ps2_command(ps2dev, param, 0x10f8))
>>> +		return -EIO;
>>> +	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
>>> +		return -EIO;
>>> +
>>> +	if (ps2_command(ps2dev, param, PSMOUSE_CMD_ENABLE))
>>> +		return -EIO;
>>>   	return 0;
>>>  }
>>> +
>>> +static void focaltech_disconnect(struct psmouse *psmouse)
>>> +{
>>> +	focaltech_reset(psmouse);
>>> +	kfree(psmouse->private);
>>> +	psmouse->private = NULL;
>>> +}
>>> +
>>> +static int focaltech_reconnect(struct psmouse *psmouse)
>>> +{
>>> +	focaltech_reset(psmouse);
>>> +	if (focaltech_switch_protocol(psmouse)) {
>>> +		psmouse_err(psmouse,
>>> +			    "Unable to initialize the device.");
>>> +		return -1;
>>> +	}
>>> +	return 0;
>>> +}
>>> +
>>> +static void set_input_params(struct psmouse *psmouse)
>>> +{
>>> +	struct input_dev *dev = psmouse->dev;
>>> +	struct focaltech_data *priv = psmouse->private;
>>> +
>>> +	__set_bit(EV_ABS, dev->evbit);
>>> +	input_set_abs_params(dev, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
>>> +	input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
>>> +	input_mt_init_slots(dev, 5, INPUT_MT_POINTER);
>>> +	__clear_bit(EV_REL, dev->evbit);
>>> +	__clear_bit(REL_X, dev->relbit);
>>> +	__clear_bit(REL_Y, dev->relbit);
>>> +	__clear_bit(BTN_RIGHT, dev->keybit);
>>> +	__clear_bit(BTN_MIDDLE, dev->keybit);
>>> +	__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
>>> +}
>>> +
>>> +static int focaltech_read_register(struct ps2dev *ps2dev, int reg,
>>> +		unsigned char *param)
>>> +{
>>> +	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
>>> +		return -1;
>>> +	param[0] = 0;
>>> +	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
>>> +		return -1;
>>> +	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
>>> +		return -1;
>>> +	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
>>> +		return -1;
>>> +	param[0] = reg;
>>> +	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
>>> +		return -1;
>>> +	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
>>> +		return -1;
>>> +	return 0;
>>> +}
>>> +
>>> +static int focaltech_read_size(struct psmouse *psmouse)
>>> +{
>>> +	struct ps2dev *ps2dev = &psmouse->ps2dev;
>>> +	struct focaltech_data *priv = psmouse->private;
>>> +	char param[3];
>>> +
>>> +	if (focaltech_read_register(ps2dev, 2, param))
>>> +		return -EIO;
>>> +	/* not sure whether this is 100% correct */
>>> +	priv->x_max = (unsigned char)param[1] * 128;
>>> +	priv->y_max = (unsigned char)param[2] * 128;
>> Hmm, I assume it is 99% correct for the X450 and X550 ?  In that case this is
>> good enough for now.
> It seems to be correct for Asus R405LD, X450, K750 (=X750?), UX303. Good
> enough, or should I wait for more feedback?

Sounds good enough to me.

Regards,

Hans
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux