All of lore.kernel.org
 help / color / mirror / Atom feed
From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
To: linux-input@vger.kernel.org, "Pali Rohár" <pali.rohar@gmail.com>
Cc: Yunkang Tang <yunkang.tang@cn.alps.com>,
	Hans de Goede <hdegoede@redhat.com>
Subject: [PATCH 6/6] Input: ALPS - mix trackstick and external PS/2 mouse data
Date: Wed, 14 Jan 2015 14:55:54 -0800	[thread overview]
Message-ID: <1421276154-8689-7-git-send-email-dmitry.torokhov@gmail.com> (raw)
In-Reply-To: <1421276154-8689-1-git-send-email-dmitry.torokhov@gmail.com>

From: Pali Rohár <pali.rohar@gmail.com>

Previously dev2 device was used for both external PS/2 mouse and internal
trackstick device (if available). This change introduces dev3 device which
is used for external PS/2 mouse data and dev2 is now used only for
trackstick.

In case that trackstick is not present dev2 is not created, so userspace
does not see non existent device in system.

Because laptops with ALPS devices often do not use i8042 active
multiplexing all data (from touchpad, trackstick and external PS/2 mouse)
come to one port.  So it is not possible to know if external PS/2 mouse is
connected or not. In most cases external PS/2 mouse is not connected so
driver will create dev3 input device after first bare PS/2 packet will be
received. So there will not be "ghost" input device.

This change also helps in identifying possible problems in future if driver
decides to report 6-bytes trackstick packets as 3-bytes bare PS/2 (data
will be reported to dev3 instead dev2).

Signed-off-by: Pali Rohár <pali.rohar@gmail.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
 drivers/input/mouse/alps.c | 188 +++++++++++++++++++++++++++++++++------------
 drivers/input/mouse/alps.h |  14 +++-
 2 files changed, 149 insertions(+), 53 deletions(-)

diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index fd3303d..48770cf 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -165,8 +165,7 @@ static bool alps_is_valid_first_byte(struct alps_data *priv,
 	return (data & priv->mask0) == priv->byte0;
 }
 
-static void alps_report_buttons(struct psmouse *psmouse,
-				struct input_dev *dev1, struct input_dev *dev2,
+static void alps_report_buttons(struct input_dev *dev1, struct input_dev *dev2,
 				int left, int right, int middle)
 {
 	struct input_dev *dev;
@@ -176,20 +175,21 @@ static void alps_report_buttons(struct psmouse *psmouse,
 	 * other device (dev2) then this event should be also
 	 * sent through that device.
 	 */
-	dev = test_bit(BTN_LEFT, dev2->key) ? dev2 : dev1;
+	dev = (dev2 && test_bit(BTN_LEFT, dev2->key)) ? dev2 : dev1;
 	input_report_key(dev, BTN_LEFT, left);
 
-	dev = test_bit(BTN_RIGHT, dev2->key) ? dev2 : dev1;
+	dev = (dev2 && test_bit(BTN_RIGHT, dev2->key)) ? dev2 : dev1;
 	input_report_key(dev, BTN_RIGHT, right);
 
-	dev = test_bit(BTN_MIDDLE, dev2->key) ? dev2 : dev1;
+	dev = (dev2 && test_bit(BTN_MIDDLE, dev2->key)) ? dev2 : dev1;
 	input_report_key(dev, BTN_MIDDLE, middle);
 
 	/*
 	 * Sync the _other_ device now, we'll do the first
 	 * device later once we report the rest of the events.
 	 */
-	input_sync(dev2);
+	if (dev2)
+		input_sync(dev2);
 }
 
 static void alps_process_packet_v1_v2(struct psmouse *psmouse)
@@ -236,13 +236,13 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse)
 		input_report_rel(dev2, REL_X,  (x > 383 ? (x - 768) : x));
 		input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y));
 
-		alps_report_buttons(psmouse, dev2, dev, left, right, middle);
+		alps_report_buttons(dev2, dev, left, right, middle);
 
 		input_sync(dev2);
 		return;
 	}
 
-	alps_report_buttons(psmouse, dev, dev2, left, right, middle);
+	alps_report_buttons(dev, dev2, left, right, middle);
 
 	/* Convert hardware tap to a reasonable Z value */
 	if (ges && !fin)
@@ -1122,23 +1122,89 @@ static void alps_process_packet_v7(struct psmouse *psmouse)
 		alps_process_touchpad_packet_v7(psmouse);
 }
 
-static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
+static DEFINE_MUTEX(alps_mutex);
+
+static void alps_register_bare_ps2_mouse(struct work_struct *work)
+{
+	struct alps_data *priv =
+		container_of(work, struct alps_data, dev3_register_work.work);
+	struct psmouse *psmouse = priv->psmouse;
+	struct input_dev *dev3;
+	int error = 0;
+
+	mutex_lock(&alps_mutex);
+
+	if (priv->dev3)
+		goto out;
+
+	dev3 = input_allocate_device();
+	if (!dev3) {
+		psmouse_err(psmouse, "failed to alolocate secondary device\n");
+		error = -ENOMEM;
+		goto out;
+	}
+
+	snprintf(priv->phys3, sizeof(priv->phys3), "%s/%s",
+		 psmouse->ps2dev.serio->phys,
+		 (priv->dev2 ? "input2" : "input1"));
+	dev3->phys = priv->phys3;
+
+	/*
+	 * format of input device name is: "protocol vendor name"
+	 * see function psmouse_switch_protocol() in psmouse-base.c
+	 */
+	dev3->name = "PS/2 ALPS Mouse";
+
+	dev3->id.bustype = BUS_I8042;
+	dev3->id.vendor  = 0x0002;
+	dev3->id.product = PSMOUSE_PS2;
+	dev3->id.version = 0x0000;
+	dev3->dev.parent = &psmouse->ps2dev.serio->dev;
+
+	input_set_capability(dev3, EV_REL, REL_X);
+	input_set_capability(dev3, EV_REL, REL_Y);
+	input_set_capability(dev3, EV_KEY, BTN_LEFT);
+	input_set_capability(dev3, EV_KEY, BTN_RIGHT);
+	input_set_capability(dev3, EV_KEY, BTN_MIDDLE);
+
+	__set_bit(INPUT_PROP_POINTER, dev3->propbit);
+
+	error = input_register_device(dev3);
+	if (error) {
+		psmouse_err(psmouse,
+			    "failed to register secondary device: %d\n",
+			    error);
+		input_free_device(dev3);
+		goto out;
+	}
+
+	priv->dev3 = dev3;
+
+out:
+	/*
+	 * Save the error code so that we can detect that we
+	 * already tried to create the device.
+	 */
+	if (error)
+		priv->dev3 = ERR_PTR(error);
+
+	mutex_unlock(&alps_mutex);
+}
+
+static void alps_report_bare_ps2_packet(struct input_dev *dev,
 					unsigned char packet[],
 					bool report_buttons)
 {
-	struct alps_data *priv = psmouse->private;
-	struct input_dev *dev2 = priv->dev2;
-
 	if (report_buttons)
-		alps_report_buttons(psmouse, dev2, psmouse->dev,
+		alps_report_buttons(dev, NULL,
 				packet[0] & 1, packet[0] & 2, packet[0] & 4);
 
-	input_report_rel(dev2, REL_X,
+	input_report_rel(dev, REL_X,
 		packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0);
-	input_report_rel(dev2, REL_Y,
+	input_report_rel(dev, REL_Y,
 		packet[2] ? ((packet[0] << 3) & 0x100) - packet[2] : 0);
 
-	input_sync(dev2);
+	input_sync(dev);
 }
 
 static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse)
@@ -1203,8 +1269,8 @@ static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse)
 		 * de-synchronization.
 		 */
 
-		alps_report_bare_ps2_packet(psmouse, &psmouse->packet[3],
-					    false);
+		alps_report_bare_ps2_packet(priv->dev2,
+					    &psmouse->packet[3], false);
 
 		/*
 		 * Continue with the standard ALPS protocol handling,
@@ -1260,9 +1326,18 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
 	 * properly we only do this if the device is fully synchronized.
 	 */
 	if (!psmouse->out_of_sync_cnt && (psmouse->packet[0] & 0xc8) == 0x08) {
+
+		/* Register dev3 mouse if we received PS/2 packet first time */
+		if (unlikely(!priv->dev3))
+			psmouse_queue_work(psmouse,
+					   &priv->dev3_register_work, 0);
+
 		if (psmouse->pktcnt == 3) {
-			alps_report_bare_ps2_packet(psmouse, psmouse->packet,
-						    true);
+			/* Once dev3 mouse device is registered report data */
+			if (likely(!IS_ERR_OR_NULL(priv->dev3)))
+				alps_report_bare_ps2_packet(priv->dev3,
+							    psmouse->packet,
+							    true);
 			return PSMOUSE_FULL_PACKET;
 		}
 		return PSMOUSE_GOOD_DATA;
@@ -2379,7 +2454,10 @@ static void alps_disconnect(struct psmouse *psmouse)
 
 	psmouse_reset(psmouse);
 	del_timer_sync(&priv->timer);
-	input_unregister_device(priv->dev2);
+	if (priv->dev2)
+		input_unregister_device(priv->dev2);
+	if (!IS_ERR_OR_NULL(priv->dev3))
+		input_unregister_device(priv->dev3);
 	kfree(priv);
 }
 
@@ -2413,17 +2491,9 @@ static void alps_set_abs_params_mt(struct alps_data *priv,
 int alps_init(struct psmouse *psmouse)
 {
 	struct alps_data *priv = psmouse->private;
-	struct input_dev *dev1 = psmouse->dev, *dev2;
+	struct input_dev *dev1 = psmouse->dev;
 	int error;
 
-	dev2 = input_allocate_device();
-	if (!dev2) {
-		error = -ENOMEM;
-		goto init_fail;
-	}
-
-	priv->dev2 = dev2;
-
 	error = priv->hw_init(psmouse);
 	if (error)
 		goto init_fail;
@@ -2475,36 +2545,55 @@ int alps_init(struct psmouse *psmouse)
 	}
 
 	if (priv->flags & ALPS_DUALPOINT) {
+		struct input_dev *dev2;
+
+		dev2 = input_allocate_device();
+		if (!dev2) {
+			psmouse_err(psmouse,
+				    "failed to allocate  trackstick device\n");
+			error = -ENOMEM;
+			goto init_fail;
+		}
+
+		snprintf(priv->phys2, sizeof(priv->phys2), "%s/input1",
+			 psmouse->ps2dev.serio->phys);
+		dev2->phys = priv->phys2;
+
 		/*
 		 * format of input device name is: "protocol vendor name"
 		 * see function psmouse_switch_protocol() in psmouse-base.c
 		 */
 		dev2->name = "AlpsPS/2 ALPS DualPoint Stick";
+
+		dev2->id.bustype = BUS_I8042;
+		dev2->id.vendor  = 0x0002;
 		dev2->id.product = PSMOUSE_ALPS;
 		dev2->id.version = priv->proto_version;
-	} else {
-		dev2->name = "PS/2 ALPS Mouse";
-		dev2->id.product = PSMOUSE_PS2;
-		dev2->id.version = 0x0000;
-	}
-
-	snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys);
-	dev2->phys = priv->phys;
-	dev2->id.bustype = BUS_I8042;
-	dev2->id.vendor  = 0x0002;
-	dev2->dev.parent = &psmouse->ps2dev.serio->dev;
+		dev2->dev.parent = &psmouse->ps2dev.serio->dev;
 
-	dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
-	dev2->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
-	dev2->keybit[BIT_WORD(BTN_LEFT)] =
-		BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+		input_set_capability(dev2, EV_REL, REL_X);
+		input_set_capability(dev2, EV_REL, REL_Y);
+		input_set_capability(dev2, EV_KEY, BTN_LEFT);
+		input_set_capability(dev2, EV_KEY, BTN_RIGHT);
+		input_set_capability(dev2, EV_KEY, BTN_MIDDLE);
 
-	__set_bit(INPUT_PROP_POINTER, dev2->propbit);
-	if (priv->flags & ALPS_DUALPOINT)
+		__set_bit(INPUT_PROP_POINTER, dev2->propbit);
 		__set_bit(INPUT_PROP_POINTING_STICK, dev2->propbit);
 
-	if (input_register_device(priv->dev2))
-		goto init_fail;
+		error = input_register_device(dev2);
+		if (error) {
+			psmouse_err(psmouse,
+				    "failed to register trackstick device: %d\n",
+				    error);
+			input_free_device(dev2);
+			goto init_fail;
+		}
+
+		priv->dev2 = dev2;
+	}
+
+	INIT_DELAYED_WORK(&priv->dev3_register_work,
+			  alps_register_bare_ps2_mouse);
 
 	psmouse->protocol_handler = alps_process_byte;
 	psmouse->poll = alps_poll;
@@ -2522,7 +2611,6 @@ int alps_init(struct psmouse *psmouse)
 
 init_fail:
 	psmouse_reset(psmouse);
-	input_free_device(dev2);
 	/*
 	 * Even though we did not allocate psmouse->private we do free
 	 * it here.
diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h
index c795235..02513c0 100644
--- a/drivers/input/mouse/alps.h
+++ b/drivers/input/mouse/alps.h
@@ -141,8 +141,12 @@ struct alps_fields {
 
 /**
  * struct alps_data - private data structure for the ALPS driver
- * @dev2: "Relative" device used to report trackstick or mouse activity.
- * @phys: Physical path for the relative device.
+ * @psmouse: Pointer to parent psmouse device
+ * @dev2: Trackstick device (can be NULL).
+ * @dev3: Generic PS/2 mouse (can be NULL, delayed registering).
+ * @phys2: Physical path for the trackstick device.
+ * @phys3: Physical path for the generic PS/2 mouse.
+ * @dev3_register_work: Delayed work for registering PS/2 mouse.
  * @nibble_commands: Command mapping used for touchpad register accesses.
  * @addr_command: Command used to tell the touchpad that a register address
  *   follows.
@@ -169,8 +173,12 @@ struct alps_fields {
  * @timer: Timer for flushing out the final report packet in the stream.
  */
 struct alps_data {
+	struct psmouse *psmouse;
 	struct input_dev *dev2;
-	char phys[32];
+	struct input_dev *dev3;
+	char phys2[32];
+	char phys3[32];
+	struct delayed_work dev3_register_work;
 
 	/* these are autodetected when the device is identified */
 	const struct alps_nibble_commands *nibble_commands;
-- 
2.2.0.rc0.207.ga3a616c

--
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

  parent reply	other threads:[~2015-01-14 23:16 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-01-14 22:55 [PATCH 0/6] Fixes for ALPS trackstick Dmitry Torokhov
2015-01-14 22:55 ` [PATCH 1/6] Input: ALPS - renumber protocol numbers Dmitry Torokhov
2015-01-14 22:55 ` [PATCH 2/6] Input: ALPS - make Rushmore a separate protocol Dmitry Torokhov
2015-01-14 22:55 ` [PATCH 3/6] Input: ALPS - split protocol data from model info Dmitry Torokhov
2015-01-14 22:55 ` [PATCH 4/6] Input: ALPS - consolidate setting protocol parameters Dmitry Torokhov
2015-01-14 22:55 ` [PATCH 5/6] Input: ALPS - fix trackstick detection on some Dell Latitudes Dmitry Torokhov
2015-01-15 20:21   ` Pali Rohár
2015-01-15 21:00     ` Dmitry Torokhov
2015-01-17 10:26   ` Pali Rohár
2015-02-02  5:34     ` Dmitry Torokhov
2015-02-02 10:51       ` Pali Rohár
2015-01-14 22:55 ` Dmitry Torokhov [this message]
2015-01-15 20:34   ` [PATCH 6/6] Input: ALPS - mix trackstick and external PS/2 mouse data Pali Rohár
2015-01-15 21:00     ` Dmitry Torokhov
2015-01-18  9:45   ` Pali Rohár
2015-01-15 10:49 ` [PATCH 0/6] Fixes for ALPS trackstick Pali Rohár
2015-01-15 18:18   ` Dmitry Torokhov
2015-01-15 19:19     ` Pali Rohár
2015-01-15 19:38       ` Dmitry Torokhov
2015-01-15 20:28         ` Pali Rohár
2015-01-15 21:02           ` Dmitry Torokhov
2015-01-17 10:01             ` Pali Rohár
2015-01-18  7:22               ` Dmitry Torokhov
2015-01-18  9:47                 ` Pali Rohár
2015-01-24 22:20                   ` Pali Rohár
2015-02-02  5:49                   ` Dmitry Torokhov
2015-02-02 10:49                     ` Pali Rohár
2015-02-02 14:27                       ` Pali Rohár
2015-02-08 12:26                         ` Pali Rohár
2015-02-10  6:32                           ` Dmitry Torokhov
2015-02-11 18:13                             ` Pali Rohár
2015-02-12  7:52                           ` Dmitry Torokhov
2015-02-12 20:25                             ` Pali Rohár
2015-02-05 11:41                     ` Pali Rohár

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1421276154-8689-7-git-send-email-dmitry.torokhov@gmail.com \
    --to=dmitry.torokhov@gmail.com \
    --cc=hdegoede@redhat.com \
    --cc=linux-input@vger.kernel.org \
    --cc=pali.rohar@gmail.com \
    --cc=yunkang.tang@cn.alps.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.