Commit 0d2746a2 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull input fixes from Dmitry Torokhov:

 - a couple fixups for adp5589-keys driver

 - recently added driver for PixArt PS/2 touchpads is dropped
   temporarily because its detection routine is too greedy and
   mis-identifies devices from other vendors as PixArt devices

* tag 'input-for-v6.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input:
  Input: adp5589-keys - fix adp5589_gpio_get_value()
  Input: adp5589-keys - fix NULL pointer dereference
  Revert "Input: Add driver for PixArt PS/2 touchpad"
parents 359cdf5a c6847716
Loading
Loading
Loading
Loading
+13 −9
Original line number Diff line number Diff line
@@ -391,10 +391,17 @@ static int adp5589_gpio_get_value(struct gpio_chip *chip, unsigned off)
	struct adp5589_kpad *kpad = gpiochip_get_data(chip);
	unsigned int bank = kpad->var->bank(kpad->gpiomap[off]);
	unsigned int bit = kpad->var->bit(kpad->gpiomap[off]);
	int val;

	return !!(adp5589_read(kpad->client,
			       kpad->var->reg(ADP5589_GPI_STATUS_A) + bank) &
			       bit);
	mutex_lock(&kpad->gpio_lock);
	if (kpad->dir[bank] & bit)
		val = kpad->dat_out[bank];
	else
		val = adp5589_read(kpad->client,
				   kpad->var->reg(ADP5589_GPI_STATUS_A) + bank);
	mutex_unlock(&kpad->gpio_lock);

	return !!(val & bit);
}

static void adp5589_gpio_set_value(struct gpio_chip *chip,
@@ -936,10 +943,9 @@ static int adp5589_keypad_add(struct adp5589_kpad *kpad, unsigned int revid)

static void adp5589_clear_config(void *data)
{
	struct i2c_client *client = data;
	struct adp5589_kpad *kpad = i2c_get_clientdata(client);
	struct adp5589_kpad *kpad = data;

	adp5589_write(client, kpad->var->reg(ADP5589_GENERAL_CFG), 0);
	adp5589_write(kpad->client, kpad->var->reg(ADP5589_GENERAL_CFG), 0);
}

static int adp5589_probe(struct i2c_client *client)
@@ -983,7 +989,7 @@ static int adp5589_probe(struct i2c_client *client)
	}

	error = devm_add_action_or_reset(&client->dev, adp5589_clear_config,
					 client);
					 kpad);
	if (error)
		return error;

@@ -1010,8 +1016,6 @@ static int adp5589_probe(struct i2c_client *client)
	if (error)
		return error;

	i2c_set_clientdata(client, kpad);

	dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq);
	return 0;
}
+0 −12
Original line number Diff line number Diff line
@@ -69,18 +69,6 @@ config MOUSE_PS2_LOGIPS2PP

	  If unsure, say Y.

config MOUSE_PS2_PIXART
	bool "PixArt PS/2 touchpad protocol extension" if EXPERT
	default y
	depends on MOUSE_PS2
	help
	  This driver supports the PixArt PS/2 touchpad found in some
	  laptops.
	  Say Y here if you have a PixArt PS/2 TouchPad connected to
	  your system.

	  If unsure, say Y.

config MOUSE_PS2_SYNAPTICS
	bool "Synaptics PS/2 mouse protocol extension" if EXPERT
	default y
+0 −1
Original line number Diff line number Diff line
@@ -32,7 +32,6 @@ psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o
psmouse-$(CONFIG_MOUSE_PS2_OLPC)	+= hgpk.o
psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP)	+= logips2pp.o
psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK)	+= lifebook.o
psmouse-$(CONFIG_MOUSE_PS2_PIXART)	+= pixart_ps2.o
psmouse-$(CONFIG_MOUSE_PS2_SENTELIC)	+= sentelic.o
psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT)	+= trackpoint.o
psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT)	+= touchkit_ps2.o

drivers/input/mouse/pixart_ps2.c

deleted100644 → 0
+0 −300
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Pixart Touchpad Controller 1336U PS2 driver
 *
 * Author: Jon Xie <jon_xie@pixart.com>
 *         Jay Lee <jay_lee@pixart.com>
 * Further cleanup and restructuring by:
 *         Binbin Zhou <zhoubinbin@loongson.cn>
 *
 * Copyright (C) 2021-2024 Pixart Imaging.
 * Copyright (C) 2024 Loongson Technology Corporation Limited.
 *
 */

#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/libps2.h>
#include <linux/serio.h>
#include <linux/slab.h>

#include "pixart_ps2.h"

static int pixart_read_tp_mode(struct ps2dev *ps2dev, u8 *mode)
{
	int error;
	u8 param[1] = { 0 };

	error = ps2_command(ps2dev, param, PIXART_CMD_REPORT_FORMAT);
	if (error)
		return error;

	*mode = param[0] == 1 ? PIXART_MODE_ABS : PIXART_MODE_REL;

	return 0;
}

static int pixart_read_tp_type(struct ps2dev *ps2dev, u8 *type)
{
	int error;
	u8 param[3] = { 0 };

	param[0] = 0x0a;
	error = ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
	if (error)
		return error;

	param[0] = 0x0;
	error = ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
	if (error)
		return error;

	error = ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
	if (error)
		return error;

	error = ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
	if (error)
		return error;

	param[0] = 0x03;
	error = ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
	if (error)
		return error;

	error = ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
	if (error)
		return error;

	*type = param[0] == 0x0e ? PIXART_TYPE_TOUCHPAD : PIXART_TYPE_CLICKPAD;

	return 0;
}

static void pixart_reset(struct psmouse *psmouse)
{
	ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);

	/* according to PixArt, 100ms is required for the upcoming reset */
	msleep(100);
	psmouse_reset(psmouse);
}

static void pixart_process_packet(struct psmouse *psmouse)
{
	struct pixart_data *priv = psmouse->private;
	struct input_dev *dev = psmouse->dev;
	const u8 *pkt = psmouse->packet;
	unsigned int contact_cnt = FIELD_GET(CONTACT_CNT_MASK, pkt[0]);
	unsigned int i, id, abs_x, abs_y;
	bool tip;

	for (i = 0; i < contact_cnt; i++) {
		const u8 *p = &pkt[i * 3];

		id = FIELD_GET(SLOT_ID_MASK, p[3]);
		abs_y = FIELD_GET(ABS_Y_MASK, p[3]) << 8 | p[1];
		abs_x = FIELD_GET(ABS_X_MASK, p[3]) << 8 | p[2];

		if (i == PIXART_MAX_FINGERS - 1)
			tip = pkt[14] & BIT(1);
		else
			tip = pkt[3 * contact_cnt + 1] & BIT(2 * i + 1);

		input_mt_slot(dev, id);
		if (input_mt_report_slot_state(dev, MT_TOOL_FINGER, tip)) {
			input_report_abs(dev, ABS_MT_POSITION_Y, abs_y);
			input_report_abs(dev, ABS_MT_POSITION_X, abs_x);
		}
	}

	input_mt_sync_frame(dev);

	if (priv->type == PIXART_TYPE_CLICKPAD) {
		input_report_key(dev, BTN_LEFT, pkt[0] & 0x03);
	} else {
		input_report_key(dev, BTN_LEFT, pkt[0] & BIT(0));
		input_report_key(dev, BTN_RIGHT, pkt[0] & BIT(1));
	}

	input_sync(dev);
}

static psmouse_ret_t pixart_protocol_handler(struct psmouse *psmouse)
{
	u8 *pkt = psmouse->packet;
	u8 contact_cnt;

	if ((pkt[0] & 0x8c) != 0x80)
		return PSMOUSE_BAD_DATA;

	contact_cnt = FIELD_GET(CONTACT_CNT_MASK, pkt[0]);
	if (contact_cnt > PIXART_MAX_FINGERS)
		return PSMOUSE_BAD_DATA;

	if (contact_cnt == PIXART_MAX_FINGERS &&
	    psmouse->pktcnt < psmouse->pktsize) {
		return PSMOUSE_GOOD_DATA;
	}

	if (contact_cnt == 0 && psmouse->pktcnt < 5)
		return PSMOUSE_GOOD_DATA;

	if (psmouse->pktcnt < 3 * contact_cnt + 2)
		return PSMOUSE_GOOD_DATA;

	pixart_process_packet(psmouse);

	return PSMOUSE_FULL_PACKET;
}

static void pixart_disconnect(struct psmouse *psmouse)
{
	pixart_reset(psmouse);
	kfree(psmouse->private);
	psmouse->private = NULL;
}

static int pixart_reconnect(struct psmouse *psmouse)
{
	struct ps2dev *ps2dev = &psmouse->ps2dev;
	u8 mode;
	int error;

	pixart_reset(psmouse);

	error = pixart_read_tp_mode(ps2dev, &mode);
	if (error)
		return error;

	if (mode != PIXART_MODE_ABS)
		return -EIO;

	error = ps2_command(ps2dev, NULL, PIXART_CMD_SWITCH_PROTO);
	if (error)
		return error;

	return 0;
}

static int pixart_set_input_params(struct input_dev *dev,
				   struct pixart_data *priv)
{
	/* No relative support */
	__clear_bit(EV_REL, dev->evbit);
	__clear_bit(REL_X, dev->relbit);
	__clear_bit(REL_Y, dev->relbit);
	__clear_bit(BTN_MIDDLE, dev->keybit);

	/* Buttons */
	__set_bit(EV_KEY, dev->evbit);
	__set_bit(BTN_LEFT, dev->keybit);
	if (priv->type == PIXART_TYPE_CLICKPAD)
		__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
	else
		__set_bit(BTN_RIGHT, dev->keybit);

	/* Absolute position */
	input_set_abs_params(dev, ABS_X, 0, PIXART_PAD_WIDTH, 0, 0);
	input_set_abs_params(dev, ABS_Y, 0, PIXART_PAD_HEIGHT, 0, 0);

	input_set_abs_params(dev, ABS_MT_POSITION_X,
			     0, PIXART_PAD_WIDTH, 0, 0);
	input_set_abs_params(dev, ABS_MT_POSITION_Y,
			     0, PIXART_PAD_HEIGHT, 0, 0);

	return input_mt_init_slots(dev, PIXART_MAX_FINGERS, INPUT_MT_POINTER);
}

static int pixart_query_hardware(struct ps2dev *ps2dev, u8 *mode, u8 *type)
{
	int error;

	error = pixart_read_tp_type(ps2dev, type);
	if (error)
		return error;

	error = pixart_read_tp_mode(ps2dev, mode);
	if (error)
		return error;

	return 0;
}

int pixart_detect(struct psmouse *psmouse, bool set_properties)
{
	u8 type;
	int error;

	pixart_reset(psmouse);

	error = pixart_read_tp_type(&psmouse->ps2dev, &type);
	if (error)
		return error;

	if (set_properties) {
		psmouse->vendor = "PixArt";
		psmouse->name = (type == PIXART_TYPE_TOUCHPAD) ?
				"touchpad" : "clickpad";
	}

	return 0;
}

int pixart_init(struct psmouse *psmouse)
{
	int error;
	struct pixart_data *priv;

	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	psmouse->private = priv;
	pixart_reset(psmouse);

	error = pixart_query_hardware(&psmouse->ps2dev,
				      &priv->mode, &priv->type);
	if (error) {
		psmouse_err(psmouse, "init: Unable to query PixArt touchpad hardware.\n");
		goto err_exit;
	}

	/* Relative mode follows standard PS/2 mouse protocol */
	if (priv->mode != PIXART_MODE_ABS) {
		error = -EIO;
		goto err_exit;
	}

	/* Set absolute mode */
	error = ps2_command(&psmouse->ps2dev, NULL, PIXART_CMD_SWITCH_PROTO);
	if (error) {
		psmouse_err(psmouse, "init: Unable to initialize PixArt absolute mode.\n");
		goto err_exit;
	}

	error = pixart_set_input_params(psmouse->dev, priv);
	if (error) {
		psmouse_err(psmouse, "init: Unable to set input params.\n");
		goto err_exit;
	}

	psmouse->pktsize = 15;
	psmouse->protocol_handler = pixart_protocol_handler;
	psmouse->disconnect = pixart_disconnect;
	psmouse->reconnect = pixart_reconnect;
	psmouse->cleanup = pixart_reset;
	/* resync is not supported yet */
	psmouse->resync_time = 0;

	return 0;

err_exit:
	pixart_reset(psmouse);
	kfree(priv);
	psmouse->private = NULL;
	return error;
}

drivers/input/mouse/pixart_ps2.h

deleted100644 → 0
+0 −36
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef _PIXART_PS2_H
#define _PIXART_PS2_H

#include "psmouse.h"

#define PIXART_PAD_WIDTH		1023
#define PIXART_PAD_HEIGHT		579
#define PIXART_MAX_FINGERS		4

#define PIXART_CMD_REPORT_FORMAT	0x01d8
#define PIXART_CMD_SWITCH_PROTO		0x00de

#define PIXART_MODE_REL			0
#define PIXART_MODE_ABS			1

#define PIXART_TYPE_CLICKPAD		0
#define PIXART_TYPE_TOUCHPAD		1

#define CONTACT_CNT_MASK		GENMASK(6, 4)

#define SLOT_ID_MASK			GENMASK(2, 0)
#define ABS_Y_MASK			GENMASK(5, 4)
#define ABS_X_MASK			GENMASK(7, 6)

struct pixart_data {
	u8 mode;
	u8 type;
	int x_max;
	int y_max;
};

int pixart_detect(struct psmouse *psmouse, bool set_properties);
int pixart_init(struct psmouse *psmouse);

#endif  /* _PIXART_PS2_H */
Loading