From 6075dd69c57d203e6a5f97067860afe90c682e47 Mon Sep 17 00:00:00 2001 From: Max Brener Date: Sat, 14 Feb 2026 22:37:24 +0200 Subject: Input: libps2 - embed WARN_ON(1) macros into their enclosing if statements Make WARN_ON(1) statements embedded inside their respective 'if' expressions, to improve code clarity. Signed-off-by: Max Brener Link: https://patch.msgid.link/20260214203725.6463-1-linmaxi@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/serio/libps2.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index 269df83a167d..05b64277aecd 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -154,10 +154,8 @@ EXPORT_SYMBOL(ps2_end_command); */ void ps2_drain(struct ps2dev *ps2dev, size_t maxbytes, unsigned int timeout) { - if (maxbytes > sizeof(ps2dev->cmdbuf)) { - WARN_ON(1); + if (WARN_ON(maxbytes > sizeof(ps2dev->cmdbuf))) maxbytes = sizeof(ps2dev->cmdbuf); - } ps2_begin_command(ps2dev); @@ -270,15 +268,11 @@ int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command) int i; u8 send_param[16]; - if (receive > sizeof(ps2dev->cmdbuf)) { - WARN_ON(1); + if (WARN_ON(receive > sizeof(ps2dev->cmdbuf))) return -EINVAL; - } - if (send && !param) { - WARN_ON(1); + if (WARN_ON(send && !param)) return -EINVAL; - } memcpy(send_param, param, send); -- cgit v1.2.3 From 004703baa5a9352182307dd9a747e9411802df32 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 9 Feb 2026 22:21:00 -0800 Subject: Input: st1232 - read firmware version and revision According to the data sheet, the st1332 contains a firmware, which may be updated. The version and revision of the firmware may be read from the registers of the device. Read the firmware version and revision and report it to the log when probing the device. Suggested-by: Khalid Talash Signed-off-by: Michael Tretter Link: https://patch.msgid.link/20260123-input-st1232-firmware-version-v1-1-32df7eefdafe@pengutronix.de Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/st1232.c | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c index 9b3901eec0a5..3dfee15fb3ad 100644 --- a/drivers/input/touchscreen/st1232.c +++ b/drivers/input/touchscreen/st1232.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -22,11 +23,14 @@ #include #include #include -#include +#include #define ST1232_TS_NAME "st1232-ts" #define ST1633_TS_NAME "st1633-ts" +#define REG_FIRMWARE_VERSION 0x00 +#define REG_FIRMWARE_REVISION_3 0x0C + #define REG_STATUS 0x01 /* Device Status | Error Code */ #define STATUS_NORMAL 0x00 @@ -61,6 +65,8 @@ struct st1232_ts_data { struct list_head touch_overlay_list; int read_buf_len; u8 *read_buf; + u8 fw_version; + u32 fw_revision; }; static int st1232_ts_read_data(struct st1232_ts_data *ts, u8 reg, @@ -110,6 +116,26 @@ static int st1232_ts_wait_ready(struct st1232_ts_data *ts) return -ENXIO; } +static int st1232_ts_read_fw_version(struct st1232_ts_data *ts, + u8 *fw_version, u32 *fw_revision) +{ + int error; + + /* select firmware version register */ + error = st1232_ts_read_data(ts, REG_FIRMWARE_VERSION, 1); + if (error) + return error; + *fw_version = ts->read_buf[0]; + + /* select firmware revision register */ + error = st1232_ts_read_data(ts, REG_FIRMWARE_REVISION_3, 4); + if (error) + return error; + *fw_revision = le32_to_cpup((__le32 *)ts->read_buf); + + return 0; +} + static int st1232_ts_read_resolution(struct st1232_ts_data *ts, u16 *max_x, u16 *max_y) { @@ -299,6 +325,16 @@ static int st1232_ts_probe(struct i2c_client *client) if (error) return error; + /* Read firmware version from the chip */ + error = st1232_ts_read_fw_version(ts, &ts->fw_version, &ts->fw_revision); + if (error) { + dev_err(&client->dev, + "Failed to read firmware version: %d\n", error); + return error; + } + dev_dbg(&client->dev, "Detected firmware version %u, rev %08x\n", + ts->fw_version, ts->fw_revision); + if (ts->chip_info->have_z) input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, ts->chip_info->max_area, 0, 0); -- cgit v1.2.3 From ffcdb6856a1be07ad46b38926dfb3bc053e98a8f Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Tue, 17 Feb 2026 22:53:08 -0800 Subject: Input: st1232 - expose firmware version via sysfs Allow user space programs to read the firmware version and revision of the touch controller from the sysfs. Suggested-by: Khalid Talash Signed-off-by: Michael Tretter Link: https://patch.msgid.link/20260123-input-st1232-firmware-version-v1-2-32df7eefdafe@pengutronix.de Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/st1232.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c index 3dfee15fb3ad..9b266927b7fe 100644 --- a/drivers/input/touchscreen/st1232.c +++ b/drivers/input/touchscreen/st1232.c @@ -69,6 +69,34 @@ struct st1232_ts_data { u32 fw_revision; }; +static ssize_t fw_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct st1232_ts_data *st1232_ts = i2c_get_clientdata(client); + + return sysfs_emit(buf, "%u\n", st1232_ts->fw_version); +} + +static ssize_t fw_revision_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct st1232_ts_data *st1232_ts = i2c_get_clientdata(client); + + return sysfs_emit(buf, "%08x\n", st1232_ts->fw_revision); +} + +static DEVICE_ATTR_RO(fw_version); +static DEVICE_ATTR_RO(fw_revision); + +static struct attribute *st1232_attrs[] = { + &dev_attr_fw_version.attr, + &dev_attr_fw_revision.attr, + NULL, +}; +ATTRIBUTE_GROUPS(st1232); + static int st1232_ts_read_data(struct st1232_ts_data *ts, u8 reg, unsigned int n) { @@ -444,6 +472,7 @@ static struct i2c_driver st1232_ts_driver = { .driver = { .name = ST1232_TS_NAME, .of_match_table = st1232_ts_dt_ids, + .dev_groups = st1232_groups, .probe_type = PROBE_PREFER_ASYNCHRONOUS, .pm = pm_sleep_ptr(&st1232_ts_pm_ops), }, -- cgit v1.2.3 From c88c63f2710dae9a0e19d86ec7ff7038314a5603 Mon Sep 17 00:00:00 2001 From: Yauhen Kharuzhy Date: Tue, 17 Feb 2026 10:08:52 -0800 Subject: Input: drv260x - add I2C IDs for all device variants Add drv2604(L) and drv2605 to the list of supported I2C device IDs for clarity. Signed-off-by: Yauhen Kharuzhy Link: https://patch.msgid.link/20260215141435.727872-2-jekhor@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/misc/drv260x.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c index 96cd6a078c8a..18360bdfe877 100644 --- a/drivers/input/misc/drv260x.c +++ b/drivers/input/misc/drv260x.c @@ -598,6 +598,9 @@ static int drv260x_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(drv260x_pm_ops, drv260x_suspend, drv260x_resume); static const struct i2c_device_id drv260x_id[] = { + { "drv2604" }, + { "drv2604l" }, + { "drv2605" }, { "drv2605l" }, { } }; -- cgit v1.2.3 From c0347a38be6665986520abeba24a19cebfae68e3 Mon Sep 17 00:00:00 2001 From: Yauhen Kharuzhy Date: Tue, 17 Feb 2026 10:09:05 -0800 Subject: Input: drv260x - sort all #include alphabetically Sort all #include directives alphabetically before adding new entries. Signed-off-by: Yauhen Kharuzhy Link: https://patch.msgid.link/20260215141435.727872-3-jekhor@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/misc/drv260x.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c index 18360bdfe877..c352ff2859e2 100644 --- a/drivers/input/misc/drv260x.c +++ b/drivers/input/misc/drv260x.c @@ -7,14 +7,14 @@ * Copyright: (C) 2014 Texas Instruments, Inc. */ +#include +#include #include #include #include #include -#include -#include -#include #include +#include #include -- cgit v1.2.3 From 029edcfc4331f404570affbbd4f7d11f0a7fb13e Mon Sep 17 00:00:00 2001 From: Yauhen Kharuzhy Date: Tue, 17 Feb 2026 10:09:20 -0800 Subject: Input: drv260x - add support for ACPI-enumerated devices Add ACPI ID for drv2604 haptics device found in Lenovo Yoga Book YB1-X91L tablet. Signed-off-by: Yauhen Kharuzhy Link: https://patch.msgid.link/20260215141435.727872-4-jekhor@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/misc/drv260x.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c index c352ff2859e2..d73175024abb 100644 --- a/drivers/input/misc/drv260x.c +++ b/drivers/input/misc/drv260x.c @@ -7,6 +7,7 @@ * Copyright: (C) 2014 Texas Instruments, Inc. */ +#include #include #include #include @@ -606,6 +607,14 @@ static const struct i2c_device_id drv260x_id[] = { }; MODULE_DEVICE_TABLE(i2c, drv260x_id); +#ifdef CONFIG_ACPI +static const struct acpi_device_id drv260x_acpi_match[] = { + { "DRV2604" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, drv260x_acpi_match); +#endif + static const struct of_device_id drv260x_of_match[] = { { .compatible = "ti,drv2604", }, { .compatible = "ti,drv2604l", }, @@ -619,6 +628,7 @@ static struct i2c_driver drv260x_driver = { .probe = drv260x_probe, .driver = { .name = "drv260x-haptics", + .acpi_match_table = ACPI_PTR(drv260x_acpi_match), .of_match_table = drv260x_of_match, .pm = pm_sleep_ptr(&drv260x_pm_ops), }, -- cgit v1.2.3 From 710a1a8c591e93fa49946b68a4f1e25ae9687ecf Mon Sep 17 00:00:00 2001 From: Yauhen Kharuzhy Date: Tue, 17 Feb 2026 10:10:27 -0800 Subject: Input: drv260x - handle calibration timeout If something goes wrong during calibration (for instance, if the 'enable' GPIO was not properly defined), the GO bit may not be cleared after some time, causing the driver to get stuck. To prevent this, add a timeout check to the waiting loop and return an error if it times out. Signed-off-by: Yauhen Kharuzhy Link: https://patch.msgid.link/20260215141435.727872-6-jekhor@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/misc/drv260x.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c index d73175024abb..b3076aa682c4 100644 --- a/drivers/input/misc/drv260x.c +++ b/drivers/input/misc/drv260x.c @@ -166,6 +166,12 @@ #define DRV260X_AUTOCAL_TIME_500MS (2 << 4) #define DRV260X_AUTOCAL_TIME_1000MS (3 << 4) +/* + * Timeout for waiting for the GO status bit, in seconds. Should be reasonably + * large to wait for a auto-calibration cycle completion. + */ +#define DRV260X_GO_TIMEOUT_S 5 + /** * struct drv260x_data - * @input_dev: Pointer to the input device @@ -309,6 +315,7 @@ static int drv260x_init(struct drv260x_data *haptics) { int error; unsigned int cal_buf; + unsigned long timeout; error = regmap_write(haptics->regmap, DRV260X_RATED_VOLT, haptics->rated_voltage); @@ -398,6 +405,7 @@ static int drv260x_init(struct drv260x_data *haptics) return error; } + timeout = jiffies + DRV260X_GO_TIMEOUT_S * HZ; do { usleep_range(15000, 15500); error = regmap_read(haptics->regmap, DRV260X_GO, &cal_buf); @@ -407,6 +415,11 @@ static int drv260x_init(struct drv260x_data *haptics) error); return error; } + if (time_after(jiffies, timeout)) { + dev_err(&haptics->client->dev, + "Calibration timeout. The device cannot be used.\n"); + return -ETIMEDOUT; + } } while (cal_buf == DRV260X_GO_BIT); return 0; -- cgit v1.2.3 From e7b53288d9ea899abc6d47a7f20065ab511a810c Mon Sep 17 00:00:00 2001 From: Yauhen Kharuzhy Date: Tue, 17 Feb 2026 10:12:27 -0800 Subject: Input: drv260x - fix unbalanced regulator_disable() call The driver acquires the 'vbat' regulator during probing but never enables it. Consequently, in the suspend method, the driver disables the regulator without enabling it first, causing an 'Unbalanced regulator_disable()' error. Enable the regulator in the probe() method and add the remove() method with regulator disabling to fix this. Signed-off-by: Yauhen Kharuzhy Link: https://patch.msgid.link/20260215141435.727872-5-jekhor@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/misc/drv260x.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c index b3076aa682c4..e2089d6ac850 100644 --- a/drivers/input/misc/drv260x.c +++ b/drivers/input/misc/drv260x.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -433,6 +434,13 @@ static const struct regmap_config drv260x_regmap_config = { .cache_type = REGCACHE_NONE, }; +static void drv260x_power_off(void *data) +{ + struct drv260x_data *haptics = data; + + regulator_disable(haptics->regulator); +} + static int drv260x_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -498,6 +506,16 @@ static int drv260x_probe(struct i2c_client *client) return error; } + error = regulator_enable(haptics->regulator); + if (error) { + dev_err(dev, "Failed to enable regulator: %d\n", error); + return error; + } + + error = devm_add_action_or_reset(dev, drv260x_power_off, haptics); + if (error) + return error; + haptics->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); if (IS_ERR(haptics->enable_gpio)) -- cgit v1.2.3 From de716275941af60247967887be3303e144ed57d7 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 23 Feb 2026 01:03:04 +0100 Subject: Input: adxl34x - drop redundant error variable in adxl34x_i2c_probe Inline i2c_check_functionality(), which really returns a bool and not an error code, and remove the local variable. No functional changes. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260223000308.319335-1-thorsten.blum@linux.dev Signed-off-by: Dmitry Torokhov --- drivers/input/misc/adxl34x-i2c.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/input/misc/adxl34x-i2c.c b/drivers/input/misc/adxl34x-i2c.c index c05d898898e8..5ea0ce42a507 100644 --- a/drivers/input/misc/adxl34x-i2c.c +++ b/drivers/input/misc/adxl34x-i2c.c @@ -77,11 +77,8 @@ static const struct adxl34x_bus_ops adxl34x_i2c_bops = { static int adxl34x_i2c_probe(struct i2c_client *client) { struct adxl34x *ac; - int error; - error = i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE_DATA); - if (!error) { + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); return -EIO; } -- cgit v1.2.3 From 3d35d41169d000f4fbf3c23999b8443e1173efce Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Mon, 23 Feb 2026 13:25:55 -0800 Subject: Input: export input_default_setkeycode Export input_default_setkeycode so that a driver can set a custom setkeycode handler to take some driver specific action but still call the default handler at some point. Signed-off-by: Fabio Baltieri Reviewed-by: Tzung-Bi Shih Link: https://patch.msgid.link/20260222003717.471977-1-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/input.c | 23 ++++++++++++++++++++--- include/linux/input.h | 4 ++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/input/input.c b/drivers/input/input.c index a500e1e276c2..c227eaa6271a 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -800,14 +800,30 @@ static int input_default_getkeycode(struct input_dev *dev, return 0; } -static int input_default_setkeycode(struct input_dev *dev, - const struct input_keymap_entry *ke, - unsigned int *old_keycode) +/** + * input_default_setkeycode - default setkeycode method + * @dev: input device which keymap is being updated. + * @ke: new keymap entry. + * @old_keycode: pointer to the location where old keycode should be stored. + * + * This function is the default implementation of &input_dev.setkeycode() + * method. It is typically used when a driver does not provide its own + * implementation, but it is also exported so drivers can extend it. + * + * The function must be called with &input_dev.event_lock held. + * + * Return: 0 on success, or a negative error code on failure. + */ +int input_default_setkeycode(struct input_dev *dev, + const struct input_keymap_entry *ke, + unsigned int *old_keycode) { unsigned int index; int error; int i; + lockdep_assert_held(&dev->event_lock); + if (!dev->keycodesize) return -EINVAL; @@ -861,6 +877,7 @@ static int input_default_setkeycode(struct input_dev *dev, __set_bit(ke->keycode, dev->keybit); return 0; } +EXPORT_SYMBOL(input_default_setkeycode); /** * input_get_keycode - retrieve keycode currently mapped to a given scancode diff --git a/include/linux/input.h b/include/linux/input.h index 7d7cb0593a63..06ca62328db1 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -517,6 +517,10 @@ INPUT_GENERATE_ABS_ACCESSORS(res, resolution) int input_scancode_to_scalar(const struct input_keymap_entry *ke, unsigned int *scancode); +int input_default_setkeycode(struct input_dev *dev, + const struct input_keymap_entry *ke, + unsigned int *old_keycode); + int input_get_keycode(struct input_dev *dev, struct input_keymap_entry *ke); int input_set_keycode(struct input_dev *dev, const struct input_keymap_entry *ke); -- cgit v1.2.3 From d8df89904cb46bd7995db1dda3405cbbe34247d7 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Mon, 23 Feb 2026 13:27:15 -0800 Subject: Input: cros_ec_keyb - add function key support Add support for handling an Fn button and sending separate keycodes for a subset of keys in the matrix defined in the upper half of the keymap. Signed-off-by: Fabio Baltieri Reviewed-by: Simon Glass Reviewed-by: Tzung-Bi Shih Link: https://patch.msgid.link/20260222003717.471977-2-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/cros_ec_keyb.c | 182 +++++++++++++++++++++++++++++++--- 1 file changed, 166 insertions(+), 16 deletions(-) diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index 2822c592880b..817272e54454 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -29,6 +29,12 @@ #include +/* + * Maximum size of the normal key matrix, this is limited by the host command + * key_matrix field defined in ec_response_get_next_data_v3 + */ +#define CROS_EC_KEYBOARD_COLS_MAX 18 + /** * struct cros_ec_keyb - Structure representing EC keyboard device * @@ -44,6 +50,9 @@ * @bs_idev: The input device for non-matrix buttons and switches (or NULL). * @notifier: interrupt event notifier for transport devices * @vdata: vivaldi function row data + * @has_fn_map: whether the driver uses an fn function-map layer + * @fn_active: tracks whether the function key is currently pressed + * @fn_combo_active: tracks whether another key was pressed while fn is active */ struct cros_ec_keyb { unsigned int rows; @@ -61,6 +70,10 @@ struct cros_ec_keyb { struct notifier_block notifier; struct vivaldi_data vdata; + + bool has_fn_map; + bool fn_active; + bool fn_combo_active; }; /** @@ -166,16 +179,89 @@ static bool cros_ec_keyb_has_ghosting(struct cros_ec_keyb *ckdev, uint8_t *buf) return false; } +static void cros_ec_emit_fn_key(struct input_dev *input, unsigned int pos) +{ + input_event(input, EV_MSC, MSC_SCAN, pos); + input_report_key(input, KEY_FN, true); + input_sync(input); + + input_event(input, EV_MSC, MSC_SCAN, pos); + input_report_key(input, KEY_FN, false); +} + +static void cros_ec_keyb_process_key_plain(struct cros_ec_keyb *ckdev, + int row, int col, bool state) +{ + struct input_dev *idev = ckdev->idev; + const unsigned short *keycodes = idev->keycode; + int pos = MATRIX_SCAN_CODE(row, col, ckdev->row_shift); + + input_event(idev, EV_MSC, MSC_SCAN, pos); + input_report_key(idev, keycodes[pos], state); +} + +static void cros_ec_keyb_process_key_fn_map(struct cros_ec_keyb *ckdev, + int row, int col, bool state) +{ + struct input_dev *idev = ckdev->idev; + const unsigned short *keycodes = idev->keycode; + unsigned int pos, fn_pos; + unsigned int code, fn_code; + + pos = MATRIX_SCAN_CODE(row, col, ckdev->row_shift); + code = keycodes[pos]; + + if (code == KEY_FN) { + ckdev->fn_active = state; + if (state) { + ckdev->fn_combo_active = false; + } else if (!ckdev->fn_combo_active) { + /* + * Send both Fn press and release events if nothing + * else has been pressed together with Fn. + */ + cros_ec_emit_fn_key(idev, pos); + } + return; + } + + fn_pos = MATRIX_SCAN_CODE(row + ckdev->rows, col, ckdev->row_shift); + fn_code = keycodes[fn_pos]; + + if (state) { + if (ckdev->fn_active) { + ckdev->fn_combo_active = true; + if (!fn_code) + return; /* Discard if no Fn mapping exists */ + + pos = fn_pos; + code = fn_code; + } + } else { + /* + * If the Fn-remapped code is currently pressed, release it. + * Otherwise, release the standard code (if it was pressed). + */ + if (fn_code && test_bit(fn_code, idev->key)) { + pos = fn_pos; + code = fn_code; + } else if (!test_bit(code, idev->key)) { + return; /* Discard, key press code was not sent */ + } + } + + input_event(idev, EV_MSC, MSC_SCAN, pos); + input_report_key(idev, code, state); +} /* * Compares the new keyboard state to the old one and produces key - * press/release events accordingly. The keyboard state is 13 bytes (one byte - * per column) + * press/release events accordingly. The keyboard state is one byte + * per column. */ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, uint8_t *kb_state, int len) { - struct input_dev *idev = ckdev->idev; int col, row; int new_state; int old_state; @@ -192,20 +278,19 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, for (col = 0; col < ckdev->cols; col++) { for (row = 0; row < ckdev->rows; row++) { - int pos = MATRIX_SCAN_CODE(row, col, ckdev->row_shift); - const unsigned short *keycodes = idev->keycode; - new_state = kb_state[col] & (1 << row); old_state = ckdev->old_kb_state[col] & (1 << row); - if (new_state != old_state) { - dev_dbg(ckdev->dev, - "changed: [r%d c%d]: byte %02x\n", - row, col, new_state); - - input_event(idev, EV_MSC, MSC_SCAN, pos); - input_report_key(idev, keycodes[pos], - new_state); - } + + if (new_state == old_state) + continue; + + dev_dbg(ckdev->dev, "changed: [r%d c%d]: byte %02x\n", + row, col, new_state); + + if (ckdev->has_fn_map) + cros_ec_keyb_process_key_fn_map(ckdev, row, col, new_state); + else + cros_ec_keyb_process_key_plain(ckdev, row, col, new_state); } ckdev->old_kb_state[col] = kb_state[col]; } @@ -583,6 +668,62 @@ static void cros_ec_keyb_parse_vivaldi_physmap(struct cros_ec_keyb *ckdev) ckdev->vdata.num_function_row_keys = n_physmap; } +/* Returns true if there is a KEY_FN code defined in the normal keymap */ +static bool cros_ec_keyb_has_fn_key(struct cros_ec_keyb *ckdev) +{ + const unsigned short *keycodes = ckdev->idev->keycode; + int i; + + for (i = 0; i < MATRIX_SCAN_CODE(ckdev->rows, 0, ckdev->row_shift); i++) { + if (keycodes[i] == KEY_FN) + return true; + } + + return false; +} + +/* + * Returns true if there is a KEY_FN defined and at least one key in the fn + * layer keymap + */ +static bool cros_ec_keyb_has_fn_map(struct cros_ec_keyb *ckdev) +{ + struct input_dev *idev = ckdev->idev; + const unsigned short *keycodes = ckdev->idev->keycode; + int i; + + if (!cros_ec_keyb_has_fn_key(ckdev)) + return false; + + for (i = MATRIX_SCAN_CODE(ckdev->rows, 0, ckdev->row_shift); + i < idev->keycodemax; i++) { + if (keycodes[i] != KEY_RESERVED) + return true; + } + + return false; +} + +/* + * Custom handler for the set keycode ioctl, calls the default handler and + * recomputes has_fn_map. + */ +static int cros_ec_keyb_setkeycode(struct input_dev *idev, + const struct input_keymap_entry *ke, + unsigned int *old_keycode) +{ + struct cros_ec_keyb *ckdev = input_get_drvdata(idev); + int ret; + + ret = input_default_setkeycode(idev, ke, old_keycode); + if (ret) + return ret; + + ckdev->has_fn_map = cros_ec_keyb_has_fn_map(ckdev); + + return 0; +} + /** * cros_ec_keyb_register_matrix - Register matrix keys * @@ -604,6 +745,12 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) if (err) return err; + if (ckdev->cols > CROS_EC_KEYBOARD_COLS_MAX) { + dev_err(dev, "keypad,num-columns too large: %d (max: %d)\n", + ckdev->cols, CROS_EC_KEYBOARD_COLS_MAX); + return -EINVAL; + } + ckdev->valid_keys = devm_kzalloc(dev, ckdev->cols, GFP_KERNEL); if (!ckdev->valid_keys) return -ENOMEM; @@ -632,11 +779,12 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) idev->id.version = 1; idev->id.product = 0; idev->dev.parent = dev; + idev->setkeycode = cros_ec_keyb_setkeycode; ckdev->ghost_filter = device_property_read_bool(dev, "google,needs-ghost-filter"); - err = matrix_keypad_build_keymap(NULL, NULL, ckdev->rows, ckdev->cols, + err = matrix_keypad_build_keymap(NULL, NULL, ckdev->rows * 2, ckdev->cols, NULL, idev); if (err) { dev_err(dev, "cannot build key matrix\n"); @@ -651,6 +799,8 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) cros_ec_keyb_compute_valid_keys(ckdev); cros_ec_keyb_parse_vivaldi_physmap(ckdev); + ckdev->has_fn_map = cros_ec_keyb_has_fn_map(ckdev); + err = input_register_device(ckdev->idev); if (err) { dev_err(dev, "cannot register input device\n"); -- cgit v1.2.3 From 2c33bd615e75771b50db416f2854ba51e2d83b3a Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 23 Feb 2026 13:28:08 -0800 Subject: Input: cros_ec_keyb - use u8 instead of uint8_t In the kernel u8/u16/u32 are preferred to the uint*_t variants. Reviewed-by: Tzung-Bi Shih Link: https://patch.msgid.link/20260222003717.471977-3-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/cros_ec_keyb.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index 817272e54454..b81ea319ad84 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -59,8 +59,8 @@ struct cros_ec_keyb { unsigned int cols; int row_shift; bool ghost_filter; - uint8_t *valid_keys; - uint8_t *old_kb_state; + u8 *valid_keys; + u8 *old_kb_state; struct device *dev; struct cros_ec_device *ec; @@ -145,11 +145,11 @@ static const struct cros_ec_bs_map cros_ec_keyb_bs[] = { * Returns true when there is at least one combination of pressed keys that * results in ghosting. */ -static bool cros_ec_keyb_has_ghosting(struct cros_ec_keyb *ckdev, uint8_t *buf) +static bool cros_ec_keyb_has_ghosting(struct cros_ec_keyb *ckdev, u8 *buf) { int col1, col2, buf1, buf2; struct device *dev = ckdev->dev; - uint8_t *valid_keys = ckdev->valid_keys; + u8 *valid_keys = ckdev->valid_keys; /* * Ghosting happens if for any pressed key X there are other keys @@ -259,8 +259,7 @@ static void cros_ec_keyb_process_key_fn_map(struct cros_ec_keyb *ckdev, * press/release events accordingly. The keyboard state is one byte * per column. */ -static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, - uint8_t *kb_state, int len) +static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, u8 *kb_state, int len) { int col, row; int new_state; -- cgit v1.2.3 From 2384e0e69275672485b3215c7d284ac51d4e869c Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 23 Feb 2026 13:28:38 -0800 Subject: Input: cros_ec_keyb - use BIT() macro instead of open-coding shifts Using the macro clarifies the code. Reviewed-by: Tzung-Bi Shih Link: https://patch.msgid.link/20260222003717.471977-4-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/cros_ec_keyb.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index b81ea319ad84..9446f1a40892 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -277,8 +277,8 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, u8 *kb_state, int l for (col = 0; col < ckdev->cols; col++) { for (row = 0; row < ckdev->rows; row++) { - new_state = kb_state[col] & (1 << row); - old_state = ckdev->old_kb_state[col] & (1 << row); + new_state = kb_state[col] & BIT(row); + old_state = ckdev->old_kb_state[col] & BIT(row); if (new_state == old_state) continue; @@ -411,7 +411,7 @@ static void cros_ec_keyb_compute_valid_keys(struct cros_ec_keyb *ckdev) for (row = 0; row < ckdev->rows; row++) { code = keymap[MATRIX_SCAN_CODE(row, col, row_shift)]; if (code && (code != KEY_BATTERY)) - ckdev->valid_keys[col] |= 1 << row; + ckdev->valid_keys[col] |= BIT(row); } dev_dbg(ckdev->dev, "valid_keys[%02d] = 0x%02x\n", col, ckdev->valid_keys[col]); @@ -780,8 +780,7 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) idev->dev.parent = dev; idev->setkeycode = cros_ec_keyb_setkeycode; - ckdev->ghost_filter = device_property_read_bool(dev, - "google,needs-ghost-filter"); + ckdev->ghost_filter = device_property_read_bool(dev, "google,needs-ghost-filter"); err = matrix_keypad_build_keymap(NULL, NULL, ckdev->rows * 2, ckdev->cols, NULL, idev); -- cgit v1.2.3 From 7d2657320c788318a5ddf670441aba83211c7a4d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 23 Feb 2026 13:28:59 -0800 Subject: Input: cros_ec_keyb - simplify cros_ec_keyb_work() Introduce temporaries for event_data pointer and event_size to simplify the code a bit. In cros_ec_keyb_compute_valid_keys() explicitly compare with KEY_RESERVED to make the intent of the comparison more clear. Reviewed-by: Tzung-Bi Shih Link: https://patch.msgid.link/20260222003717.471977-5-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/cros_ec_keyb.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index 9446f1a40892..16bb0d0a0e74 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -330,8 +330,10 @@ static int cros_ec_keyb_work(struct notifier_block *nb, { struct cros_ec_keyb *ckdev = container_of(nb, struct cros_ec_keyb, notifier); - u32 val; + struct ec_response_get_next_event_v3 *event_data; + unsigned int event_size; unsigned int ev_type; + u32 val; /* * If not wake enabled, discard key state changes during @@ -341,32 +343,32 @@ static int cros_ec_keyb_work(struct notifier_block *nb, if (queued_during_suspend && !device_may_wakeup(ckdev->dev)) return NOTIFY_OK; - switch (ckdev->ec->event_data.event_type) { + event_data = &ckdev->ec->event_data; + event_size = ckdev->ec->event_size; + + switch (event_data->event_type) { case EC_MKBP_EVENT_KEY_MATRIX: pm_wakeup_event(ckdev->dev, 0); if (!ckdev->idev) { - dev_warn_once(ckdev->dev, - "Unexpected key matrix event\n"); + dev_warn_once(ckdev->dev, "Unexpected key matrix event\n"); return NOTIFY_OK; } - if (ckdev->ec->event_size != ckdev->cols) { + if (event_size != ckdev->cols) { dev_err(ckdev->dev, "Discarded key matrix event, unexpected length: %d != %d\n", ckdev->ec->event_size, ckdev->cols); return NOTIFY_OK; } - cros_ec_keyb_process(ckdev, - ckdev->ec->event_data.data.key_matrix, - ckdev->ec->event_size); + cros_ec_keyb_process(ckdev, event_data->data.key_matrix, event_size); break; case EC_MKBP_EVENT_SYSRQ: pm_wakeup_event(ckdev->dev, 0); - val = get_unaligned_le32(&ckdev->ec->event_data.data.sysrq); + val = get_unaligned_le32(&event_data->data.sysrq); dev_dbg(ckdev->dev, "sysrq code from EC: %#x\n", val); handle_sysrq(val); break; @@ -375,13 +377,11 @@ static int cros_ec_keyb_work(struct notifier_block *nb, case EC_MKBP_EVENT_SWITCH: pm_wakeup_event(ckdev->dev, 0); - if (ckdev->ec->event_data.event_type == EC_MKBP_EVENT_BUTTON) { - val = get_unaligned_le32( - &ckdev->ec->event_data.data.buttons); + if (event_data->event_type == EC_MKBP_EVENT_BUTTON) { + val = get_unaligned_le32(&event_data->data.buttons); ev_type = EV_KEY; } else { - val = get_unaligned_le32( - &ckdev->ec->event_data.data.switches); + val = get_unaligned_le32(&event_data->data.switches); ev_type = EV_SW; } cros_ec_keyb_report_bs(ckdev, ev_type, val); @@ -410,7 +410,7 @@ static void cros_ec_keyb_compute_valid_keys(struct cros_ec_keyb *ckdev) for (col = 0; col < ckdev->cols; col++) { for (row = 0; row < ckdev->rows; row++) { code = keymap[MATRIX_SCAN_CODE(row, col, row_shift)]; - if (code && (code != KEY_BATTERY)) + if (code != KEY_RESERVED && code != KEY_BATTERY) ckdev->valid_keys[col] |= BIT(row); } dev_dbg(ckdev->dev, "valid_keys[%02d] = 0x%02x\n", -- cgit v1.2.3 From fd3c7a4522dc75b8828e03b26ae7a84fee9cb4a0 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 23 Feb 2026 13:30:19 -0800 Subject: Input: cros_ec_keyb - do not allocate keyboard state separately Now that we know the upper bound for the number of columns, and know that it is pretty small, there is no point in allocating it separately. We are wasting more memory tracking the allocations. Embed valid_keys and old_kb_state directly into cros_ec_keyb structure. Reviewed-by: Tzung-Bi Shih Link: https://patch.msgid.link/20260222003717.471977-6-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/cros_ec_keyb.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index 16bb0d0a0e74..d11c9d494004 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -59,8 +59,8 @@ struct cros_ec_keyb { unsigned int cols; int row_shift; bool ghost_filter; - u8 *valid_keys; - u8 *old_kb_state; + u8 valid_keys[CROS_EC_KEYBOARD_COLS_MAX]; + u8 old_kb_state[CROS_EC_KEYBOARD_COLS_MAX]; struct device *dev; struct cros_ec_device *ec; @@ -750,14 +750,6 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) return -EINVAL; } - ckdev->valid_keys = devm_kzalloc(dev, ckdev->cols, GFP_KERNEL); - if (!ckdev->valid_keys) - return -ENOMEM; - - ckdev->old_kb_state = devm_kzalloc(dev, ckdev->cols, GFP_KERNEL); - if (!ckdev->old_kb_state) - return -ENOMEM; - /* * We call the keyboard matrix 'input0'. Allocate phys before input * dev, to ensure correct tear-down ordering. -- cgit v1.2.3 From 21a60fcd24bae2ecdb96351463618647e1edf871 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 23 Feb 2026 13:31:12 -0800 Subject: Input: cros_ec_keyb - factor out column processing Factor out column processing and eagerly skip processing columns that do not have any changes in them. Reviewed-by: Tzung-Bi Shih Link: https://patch.msgid.link/20260222003717.471977-7-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/cros_ec_keyb.c | 47 ++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index d11c9d494004..177e5d4a3382 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -254,6 +254,26 @@ static void cros_ec_keyb_process_key_fn_map(struct cros_ec_keyb *ckdev, input_report_key(idev, code, state); } +static void cros_ec_keyb_process_col(struct cros_ec_keyb *ckdev, int col, + u8 col_state, u8 changed) +{ + for (int row = 0; row < ckdev->rows; row++) { + if (changed & BIT(row)) { + u8 key_state = col_state & BIT(row); + + dev_dbg(ckdev->dev, "changed: [r%d c%d]: byte %02x\n", + row, col, key_state); + + if (ckdev->has_fn_map) + cros_ec_keyb_process_key_fn_map(ckdev, row, col, + key_state); + else + cros_ec_keyb_process_key_plain(ckdev, row, col, + key_state); + } + } +} + /* * Compares the new keyboard state to the old one and produces key * press/release events accordingly. The keyboard state is one byte @@ -261,10 +281,6 @@ static void cros_ec_keyb_process_key_fn_map(struct cros_ec_keyb *ckdev, */ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, u8 *kb_state, int len) { - int col, row; - int new_state; - int old_state; - if (ckdev->ghost_filter && cros_ec_keyb_has_ghosting(ckdev, kb_state)) { /* * Simple-minded solution: ignore this state. The obvious @@ -275,24 +291,15 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, u8 *kb_state, int l return; } - for (col = 0; col < ckdev->cols; col++) { - for (row = 0; row < ckdev->rows; row++) { - new_state = kb_state[col] & BIT(row); - old_state = ckdev->old_kb_state[col] & BIT(row); - - if (new_state == old_state) - continue; - - dev_dbg(ckdev->dev, "changed: [r%d c%d]: byte %02x\n", - row, col, new_state); + for (int col = 0; col < ckdev->cols; col++) { + u8 changed = kb_state[col] ^ ckdev->old_kb_state[col]; - if (ckdev->has_fn_map) - cros_ec_keyb_process_key_fn_map(ckdev, row, col, new_state); - else - cros_ec_keyb_process_key_plain(ckdev, row, col, new_state); - } - ckdev->old_kb_state[col] = kb_state[col]; + if (changed) + cros_ec_keyb_process_col(ckdev, col, kb_state[col], + changed); } + + memcpy(ckdev->old_kb_state, kb_state, sizeof(ckdev->old_kb_state)); input_sync(ckdev->idev); } -- cgit v1.2.3 From b0ef098d5fc1d2f75f4fcc6ca7ad41f29210c4d0 Mon Sep 17 00:00:00 2001 From: Langyan Ye Date: Thu, 8 Jan 2026 14:35:23 +0800 Subject: dt-bindings: input: Add Parade TC3408 touchscreen controller The tc3408 touch screen chip same as Elan eKTH6915 controller has a reset gpio. The difference is that they have different post_power_delay_ms. Signed-off-by: Langyan Ye Reviewed-by: Conor Dooley Link: https://patch.msgid.link/20260108063524.742464-2-yelangyan@huaqin.corp-partner.google.com Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/parade,tc3408.yaml | 68 ++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/parade,tc3408.yaml diff --git a/Documentation/devicetree/bindings/input/parade,tc3408.yaml b/Documentation/devicetree/bindings/input/parade,tc3408.yaml new file mode 100644 index 000000000000..30ffefb96c68 --- /dev/null +++ b/Documentation/devicetree/bindings/input/parade,tc3408.yaml @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/parade,tc3408.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Parade TC3408 touchscreen controller + +maintainers: + - Langyan Ye + +description: | + Parade TC3408 is a touchscreen controller supporting the I2C-HID protocol. + It requires a reset GPIO and two power supplies (3.3V and 1.8V). + +allOf: + - $ref: /schemas/input/touchscreen/touchscreen.yaml# + +properties: + compatible: + const: parade,tc3408 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + reset-gpios: + maxItems: 1 + + vcc33-supply: + description: The 3.3V supply to the touchscreen. + + vccio-supply: + description: The 1.8V supply to the touchscreen. + +required: + - compatible + - reg + - interrupts + - reset-gpios + - vcc33-supply + - vccio-supply + +unevaluatedProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + touchscreen: touchscreen@24 { + compatible = "parade,tc3408"; + reg = <0x24>; + + interrupt-parent = <&pio>; + interrupts = <15 IRQ_TYPE_LEVEL_LOW>; + + reset-gpios = <&pio 126 GPIO_ACTIVE_LOW>; + vcc33-supply = <&pp3300_tchscr_x>; + vccio-supply = <&pp1800_tchscr_report_disable>; + }; + }; -- cgit v1.2.3 From 4410a3f14c305de493036b7d982f24b84e4c8e03 Mon Sep 17 00:00:00 2001 From: Langyan Ye Date: Thu, 8 Jan 2026 14:35:24 +0800 Subject: HID: i2c-hid: elan: Add parade-tc3408 timing Parade-tc3408 requires reset to pull down time greater than 10ms, so the configuration post_power_delay_ms is 10, and the chipset initial time is required to be greater than 300ms, so the post_gpio_reset_on_delay_ms is set to 300. Signed-off-by: Langyan Ye Reviewed-by: Douglas Anderson Acked-by: Jiri Kosina Link: https://patch.msgid.link/20260108063524.742464-3-yelangyan@huaqin.corp-partner.google.com Signed-off-by: Dmitry Torokhov --- drivers/hid/i2c-hid/i2c-hid-of-elan.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/hid/i2c-hid/i2c-hid-of-elan.c b/drivers/hid/i2c-hid/i2c-hid-of-elan.c index b81fcc6ff49e..919e32c47e12 100644 --- a/drivers/hid/i2c-hid/i2c-hid-of-elan.c +++ b/drivers/hid/i2c-hid/i2c-hid-of-elan.c @@ -195,12 +195,20 @@ static const struct elan_i2c_hid_chip_data ilitek_ili2901_chip_data = { .main_supply_name = "vcc33", }; +static const struct elan_i2c_hid_chip_data parade_tc3408_chip_data = { + .post_power_delay_ms = 10, + .post_gpio_reset_on_delay_ms = 300, + .hid_descriptor_address = 0x0001, + .main_supply_name = "vcc33", +}; + static const struct of_device_id elan_i2c_hid_of_match[] = { { .compatible = "elan,ekth6915", .data = &elan_ekth6915_chip_data }, { .compatible = "elan,ekth6a12nay", .data = &elan_ekth6a12nay_chip_data }, { .compatible = "focaltech,ft8112", .data = &focaltech_ft8112_chip_data }, { .compatible = "ilitek,ili9882t", .data = &ilitek_ili9882t_chip_data }, { .compatible = "ilitek,ili2901", .data = &ilitek_ili2901_chip_data }, + { .compatible = "parade,tc3408", .data = ¶de_tc3408_chip_data }, { } }; MODULE_DEVICE_TABLE(of, elan_i2c_hid_of_match); -- cgit v1.2.3 From 0c695e6b90674c67e03866123456cabfe1f74d9c Mon Sep 17 00:00:00 2001 From: Ariel Silver Date: Fri, 20 Feb 2026 10:44:28 +0200 Subject: Input: atkbd - validate scancode in firmware keymap entries The SCANCODE() macro extracts a 16-bit value (0..65535) from firmware device property data, but atkbd_get_keymap_from_fwnode() uses it directly to index atkbd->keycode[], which only has ATKBD_KEYMAP_SIZE (512) elements. A firmware-supplied scancode >= 512 causes a heap out-of-bounds write that can corrupt adjacent struct atkbd fields and neighboring slab objects. Add a bounds check that rejects the entire firmware keymap if any entry contains an out-of-range scancode, consistent with the validation performed by matrix_keypad_parse_keymap() in drivers/input/matrix-keymap.c for the same "linux,keymap" property format. When rejected, the driver falls back to the default keycode table. Fixes: 9d17ad2369dc ("Input: atkbd - receive and use physcode->keycode mapping from FW") Signed-off-by: Ariel Silver Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/atkbd.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 6c999d89ee4b..7e6fa0e3cbd8 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -1110,6 +1110,12 @@ static int atkbd_get_keymap_from_fwnode(struct atkbd *atkbd) for (i = 0; i < n; i++) { scancode = SCANCODE(ptr[i]); keycode = KEYCODE(ptr[i]); + if (scancode >= ATKBD_KEYMAP_SIZE) { + dev_warn(dev, "invalid scancode %#x in FW keymap entry %d\n", + scancode, i); + kfree(ptr); + return -EINVAL; + } atkbd->keycode[scancode] = keycode; } -- cgit v1.2.3 From 1fe01b817921d2bbb10cc9c83d36364738ecfe5d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 20 Feb 2026 18:57:58 -0800 Subject: Input: atkbd - use __free() cleanup facility in when parsing FW keymap Annotating the temporary keymap pointer as __free(kfree) ensures that it will get released when exiting the function and explicit freeing in all the return paths can be removed. Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/atkbd.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 7e6fa0e3cbd8..4459de0e6615 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -1088,7 +1088,6 @@ static int atkbd_get_keymap_from_fwnode(struct atkbd *atkbd) { struct device *dev = &atkbd->ps2dev.serio->dev; int i, n; - u32 *ptr; u16 scancode, keycode; /* Parse "linux,keymap" property */ @@ -1096,13 +1095,12 @@ static int atkbd_get_keymap_from_fwnode(struct atkbd *atkbd) if (n <= 0 || n > ATKBD_KEYMAP_SIZE) return -ENXIO; - ptr = kcalloc(n, sizeof(u32), GFP_KERNEL); + u32 *ptr __free(kfree) = kcalloc(n, sizeof(*ptr), GFP_KERNEL); if (!ptr) return -ENOMEM; if (device_property_read_u32_array(dev, "linux,keymap", ptr, n)) { dev_err(dev, "problem parsing FW keymap property\n"); - kfree(ptr); return -EINVAL; } @@ -1113,13 +1111,11 @@ static int atkbd_get_keymap_from_fwnode(struct atkbd *atkbd) if (scancode >= ATKBD_KEYMAP_SIZE) { dev_warn(dev, "invalid scancode %#x in FW keymap entry %d\n", scancode, i); - kfree(ptr); return -EINVAL; } atkbd->keycode[scancode] = keycode; } - kfree(ptr); return 0; } -- cgit v1.2.3 From 9df4a9d2129f779449c0cbc1bd9ce37451d8b4f3 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 20 Feb 2026 19:02:45 -0800 Subject: Input: atkbd - use dev_warn_ratelimited() Instead of explicitly using printk_ratelimit() switch to using dev_warn_ratelimited(). Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/atkbd.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 4459de0e6615..2d80a06e4b07 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -486,11 +486,9 @@ static void atkbd_receive_byte(struct ps2dev *ps2dev, u8 data) return; case ATKBD_RET_ACK: case ATKBD_RET_NAK: - if (printk_ratelimit()) - dev_warn(&serio->dev, - "Spurious %s on %s. " - "Some program might be trying to access hardware directly.\n", - data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys); + dev_warn_ratelimited(&serio->dev, + "Spurious %s on %s. Some program might be trying to access hardware directly.\n", + data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys); return; case ATKBD_RET_ERR: atkbd->err_count++; -- cgit v1.2.3 From b96fee7ddb0d1908d3466cc8848e6336b8b7f467 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 20 Feb 2026 19:12:12 -0800 Subject: Input: atkbd - switch to using explicitly sized types Instead of using "unsigned short" and "unsigned char" for holding 16-bit and 8-bit data, switch to using common in kernel u16 and u8. Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/atkbd.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 2d80a06e4b07..58c20a5bd9e9 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -122,7 +122,7 @@ static const unsigned short atkbd_set3_keycode[ATKBD_KEYMAP_SIZE] = { 148,149,147,140 }; -static const unsigned short atkbd_unxlate_table[128] = { +static const u8 atkbd_unxlate_table[128] = { 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13, 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, @@ -184,7 +184,7 @@ static const unsigned short atkbd_unxlate_table[128] = { static const struct { unsigned short keycode; - unsigned char set2; + u8 set2; } atkbd_scroll_keys[] = { { ATKBD_SCR_1, 0xc5 }, { ATKBD_SCR_2, 0x9d }, @@ -211,7 +211,7 @@ struct atkbd { unsigned short id; unsigned short keycode[ATKBD_KEYMAP_SIZE]; DECLARE_BITMAP(force_release_mask, ATKBD_KEYMAP_SIZE); - unsigned char set; + u8 set; bool translated; bool extra; bool write; @@ -221,7 +221,7 @@ struct atkbd { bool enabled; /* Accessed only from interrupt */ - unsigned char emul; + u8 emul; bool resend; bool release; unsigned long xl_bit; @@ -337,7 +337,7 @@ static const struct attribute_group atkbd_attribute_group = { __ATTRIBUTE_GROUPS(atkbd_attribute); -static const unsigned int xl_table[] = { +static const u8 xl_table[] = { ATKBD_RET_BAT, ATKBD_RET_ERR, ATKBD_RET_ACK, ATKBD_RET_NAK, ATKBD_RET_HANJA, ATKBD_RET_HANGEUL, }; @@ -346,7 +346,7 @@ static const unsigned int xl_table[] = { * Checks if we should mangle the scancode to extract 'release' bit * in translated mode. */ -static bool atkbd_need_xlate(unsigned long xl_bit, unsigned char code) +static bool atkbd_need_xlate(unsigned long xl_bit, u8 code) { int i; @@ -365,7 +365,7 @@ static bool atkbd_need_xlate(unsigned long xl_bit, unsigned char code) * between make/break pair of scancodes for select keys and PS/2 * protocol responses. */ -static void atkbd_calculate_xl_bit(struct atkbd *atkbd, unsigned char code) +static void atkbd_calculate_xl_bit(struct atkbd *atkbd, u8 code) { int i; @@ -587,7 +587,7 @@ static int atkbd_set_repeat_rate(struct atkbd *atkbd) { 250, 500, 750, 1000 }; struct input_dev *dev = atkbd->dev; - unsigned char param; + u8 param; int i = 0, j = 0; while (i < ARRAY_SIZE(period) - 1 && period[i] < dev->rep[REP_PERIOD]) @@ -605,7 +605,7 @@ static int atkbd_set_repeat_rate(struct atkbd *atkbd) static int atkbd_set_leds(struct atkbd *atkbd) { struct input_dev *dev = atkbd->dev; - unsigned char param[2]; + u8 param[2]; param[0] = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0) | (test_bit(LED_NUML, dev->led) ? 2 : 0) @@ -806,7 +806,7 @@ static inline bool atkbd_skip_getid(struct atkbd *atkbd) { return false; } static int atkbd_probe(struct atkbd *atkbd) { struct ps2dev *ps2dev = &atkbd->ps2dev; - unsigned char param[2]; + u8 param[2]; /* * Some systems, where the bit-twiddling when testing the io-lines of the @@ -879,7 +879,7 @@ deactivate_kbd: static int atkbd_select_set(struct atkbd *atkbd, int target_set, int allow_extra) { struct ps2dev *ps2dev = &atkbd->ps2dev; - unsigned char param[2]; + u8 param[2]; atkbd->extra = false; /* @@ -940,7 +940,7 @@ static int atkbd_select_set(struct atkbd *atkbd, int target_set, int allow_extra static int atkbd_reset_state(struct atkbd *atkbd) { struct ps2dev *ps2dev = &atkbd->ps2dev; - unsigned char param[1]; + u8 param[1]; /* * Set the LEDs to a predefined state (all off). @@ -1235,7 +1235,7 @@ static void atkbd_set_device_attrs(struct atkbd *atkbd) } input_dev->keycode = atkbd->keycode; - input_dev->keycodesize = sizeof(unsigned short); + input_dev->keycodesize = sizeof(atkbd->keycode[0]); input_dev->keycodemax = ARRAY_SIZE(atkbd_set2_keycode); for (i = 0; i < ATKBD_KEYMAP_SIZE; i++) { @@ -1482,7 +1482,7 @@ static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t coun unsigned int value; int err; bool old_extra; - unsigned char old_set; + u8 old_set; if (!atkbd->write) return -EIO; @@ -1617,7 +1617,7 @@ static ssize_t atkbd_set_set(struct atkbd *atkbd, const char *buf, size_t count) struct input_dev *old_dev, *new_dev; unsigned int value; int err; - unsigned char old_set; + u8 old_set; bool old_extra; if (!atkbd->write) -- cgit v1.2.3 From 3bf5404fc93825ddde89992acad095a297ed9a31 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 20 Feb 2026 19:27:06 -0800 Subject: Input: atkbd - fix various formatting issues Over the years we accumulated a number of formatting issues, fix them. Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/atkbd.c | 85 ++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 53 deletions(-) diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 58c20a5bd9e9..4560d3964eee 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -3,10 +3,7 @@ * AT and PS/2 keyboard driver * * Copyright (c) 1999-2002 Vojtech Pavlik - */ - - -/* + * * This driver can handle standard AT keyboards and PS/2 keyboards in * Translated and Raw Set 2 and Set 3, as well as AT keyboards on dumb * input-only controllers and AT keyboards connected over a one way RS232 @@ -65,8 +62,8 @@ static bool atkbd_terminal; module_param_named(terminal, atkbd_terminal, bool, 0); MODULE_PARM_DESC(terminal, "Enable break codes on an IBM Terminal keyboard connected via AT/PS2"); -#define SCANCODE(keymap) ((keymap >> 16) & 0xFFFF) -#define KEYCODE(keymap) (keymap & 0xFFFF) +#define SCANCODE(keymap) (((keymap) >> 16) & 0xFFFF) +#define KEYCODE(keymap) ((keymap) & 0xFFFF) /* * Scancode to keycode tables. These are just the default setting, and @@ -76,7 +73,6 @@ MODULE_PARM_DESC(terminal, "Enable break codes on an IBM Terminal keyboard conne #define ATKBD_KEYMAP_SIZE 512 static const unsigned short atkbd_set2_keycode[ATKBD_KEYMAP_SIZE] = { - #ifdef CONFIG_KEYBOARD_ATKBD_HP_KEYCODES /* XXX: need a more general approach */ @@ -107,7 +103,6 @@ static const unsigned short atkbd_set2_keycode[ATKBD_KEYMAP_SIZE] = { }; static const unsigned short atkbd_set3_keycode[ATKBD_KEYMAP_SIZE] = { - 0, 0, 0, 0, 0, 0, 0, 59, 1,138,128,129,130, 15, 41, 60, 131, 29, 42, 86, 58, 16, 2, 61,133, 56, 44, 31, 30, 17, 3, 62, 134, 46, 45, 32, 18, 5, 4, 63,135, 57, 47, 33, 20, 19, 6, 64, @@ -123,14 +118,14 @@ static const unsigned short atkbd_set3_keycode[ATKBD_KEYMAP_SIZE] = { }; static const u8 atkbd_unxlate_table[128] = { - 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13, - 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, - 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, - 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3, - 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105, - 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63, - 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111, - 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110 + 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13, + 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, + 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, + 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3, + 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105, + 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63, + 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111, + 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110 }; #define ATKBD_CMD_SETLEDS 0x10ed @@ -200,7 +195,6 @@ static const struct { */ struct atkbd { - struct ps2dev ps2dev; struct input_dev *dev; @@ -253,9 +247,9 @@ static unsigned int (*atkbd_platform_scancode_fixup)(struct atkbd *, unsigned in static bool atkbd_skip_deactivate; static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, - ssize_t (*handler)(struct atkbd *, char *)); + ssize_t (*handler)(struct atkbd *, char *)); static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count, - ssize_t (*handler)(struct atkbd *, const char *, size_t)); + ssize_t (*handler)(struct atkbd *, const char *, size_t)); #define ATKBD_DEFINE_ATTR(_name) \ static ssize_t atkbd_show_##_name(struct atkbd *, char *); \ static ssize_t atkbd_set_##_name(struct atkbd *, const char *, size_t); \ @@ -270,7 +264,7 @@ static ssize_t atkbd_do_set_##_name(struct device *d, \ return atkbd_attr_set_helper(d, b, s, atkbd_set_##_name); \ } \ static struct device_attribute atkbd_attr_##_name = \ - __ATTR(_name, S_IWUSR | S_IRUGO, atkbd_do_show_##_name, atkbd_do_set_##_name); + __ATTR(_name, S_IWUSR | S_IRUGO, atkbd_do_show_##_name, atkbd_do_set_##_name) ATKBD_DEFINE_ATTR(extra); ATKBD_DEFINE_ATTR(force_release); @@ -287,7 +281,7 @@ static ssize_t atkbd_do_show_##_name(struct device *d, \ return atkbd_attr_show_helper(d, b, atkbd_show_##_name); \ } \ static struct device_attribute atkbd_attr_##_name = \ - __ATTR(_name, S_IRUGO, atkbd_do_show_##_name, NULL); + __ATTR(_name, S_IRUGO, atkbd_do_show_##_name, NULL) ATKBD_DEFINE_RO_ATTR(err_count); ATKBD_DEFINE_RO_ATTR(function_row_physmap); @@ -317,7 +311,7 @@ static struct atkbd *atkbd_from_serio(struct serio *serio) } static umode_t atkbd_attr_is_visible(struct kobject *kobj, - struct attribute *attr, int i) + struct attribute *attr, int i) { struct device *dev = kobj_to_dev(kobj); struct serio *serio = to_serio_port(dev); @@ -389,7 +383,7 @@ static unsigned int atkbd_compat_scancode(struct atkbd *atkbd, unsigned int code if (atkbd->set == 3) { if (atkbd->emul == 1) code |= 0x100; - } else { + } else { code = (code & 0x7f) | ((code & 0x80) << 1); if (atkbd->emul == 1) code |= 0x80; @@ -431,7 +425,7 @@ static enum ps2_disposition atkbd_pre_receive_byte(struct ps2dev *ps2dev, dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, flags); -#if !defined(__i386__) && !defined (__x86_64__) +#if !defined(__i386__) && !defined(__x86_64__) if (atkbd_handle_frame_error(ps2dev, data, flags)) return PS2_IGNORE; #endif @@ -460,7 +454,6 @@ static void atkbd_receive_byte(struct ps2dev *ps2dev, u8 data) code = atkbd_platform_scancode_fixup(atkbd, code); if (atkbd->translated) { - if (atkbd->emul || atkbd_need_xlate(atkbd->xl_bit, code)) { atkbd->release = code >> 7; code &= 0x7f; @@ -580,11 +573,11 @@ static void atkbd_receive_byte(struct ps2dev *ps2dev, u8 data) static int atkbd_set_repeat_rate(struct atkbd *atkbd) { - const short period[32] = - { 33, 37, 42, 46, 50, 54, 58, 63, 67, 75, 83, 92, 100, 109, 116, 125, - 133, 149, 167, 182, 200, 217, 232, 250, 270, 303, 333, 370, 400, 435, 470, 500 }; - const short delay[4] = - { 250, 500, 750, 1000 }; + const short period[32] = { + 33, 37, 42, 46, 50, 54, 58, 63, 67, 75, 83, 92, 100, 109, 116, 125, + 133, 149, 167, 182, 200, 217, 232, 250, 270, 303, 333, 370, 400, 435, 470, 500 + }; + const short delay[4] = { 250, 500, 750, 1000 }; struct input_dev *dev = atkbd->dev; u8 param; @@ -646,8 +639,7 @@ static void atkbd_event_work(struct work_struct *work) * it may not be ready yet. In this case we need to keep * rescheduling till reconnect completes. */ - schedule_delayed_work(&atkbd->event_work, - msecs_to_jiffies(100)); + schedule_delayed_work(&atkbd->event_work, msecs_to_jiffies(100)); } else { if (test_and_clear_bit(ATKBD_LED_EVENT_BIT, &atkbd->event_mask)) atkbd_set_leds(atkbd); @@ -681,7 +673,7 @@ static void atkbd_schedule_event_work(struct atkbd *atkbd, int event_bit) */ static int atkbd_event(struct input_dev *dev, - unsigned int type, unsigned int code, int value) + unsigned int type, unsigned int code, int value) { struct atkbd *atkbd = input_get_drvdata(dev); @@ -689,7 +681,6 @@ static int atkbd_event(struct input_dev *dev, return -1; switch (type) { - case EV_LED: atkbd_schedule_event_work(atkbd, ATKBD_LED_EVENT_BIT); return 0; @@ -834,7 +825,6 @@ static int atkbd_probe(struct atkbd *atkbd) param[0] = param[1] = 0xa5; /* initialize with invalid values */ if (ps2_command(ps2dev, param, ATKBD_CMD_GETID)) { - /* * If the get ID command failed, we check if we can at least set * the LEDs on the keyboard. This should work on every keyboard out there. @@ -854,8 +844,7 @@ static int atkbd_probe(struct atkbd *atkbd) if (atkbd->id == 0xaca1 && atkbd->translated) { dev_err(&ps2dev->serio->dev, - "NCD terminal keyboards are only supported on non-translating controllers. " - "Use i8042.direct=1 to disable translation.\n"); + "NCD terminal keyboards are only supported on non-translating controllers. Use i8042.direct=1 to disable translation.\n"); return -1; } @@ -939,7 +928,7 @@ static int atkbd_select_set(struct atkbd *atkbd, int target_set, int allow_extra static int atkbd_reset_state(struct atkbd *atkbd) { - struct ps2dev *ps2dev = &atkbd->ps2dev; + struct ps2dev *ps2dev = &atkbd->ps2dev; u8 param[1]; /* @@ -965,7 +954,6 @@ static int atkbd_reset_state(struct atkbd *atkbd) * atkbd_cleanup() restores the keyboard state so that BIOS is happy after a * reboot. */ - static void atkbd_cleanup(struct serio *serio) { struct atkbd *atkbd = atkbd_from_serio(serio); @@ -974,11 +962,9 @@ static void atkbd_cleanup(struct serio *serio) ps2_command(&atkbd->ps2dev, NULL, ATKBD_CMD_RESET_DEF); } - /* * atkbd_disconnect() closes and frees. */ - static void atkbd_disconnect(struct serio *serio) { struct atkbd *atkbd = atkbd_from_serio(serio); @@ -1003,8 +989,7 @@ static void atkbd_disconnect(struct serio *serio) /* * generate release events for the keycodes given in data */ -static void atkbd_apply_forced_release_keylist(struct atkbd* atkbd, - const void *data) +static void atkbd_apply_forced_release_keylist(struct atkbd *atkbd, const void *data) { const unsigned int *keys = data; unsigned int i; @@ -1289,7 +1274,6 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv) mutex_init(&atkbd->mutex); switch (serio->id.type) { - case SERIO_8042_XL: atkbd->translated = true; fallthrough; @@ -1314,7 +1298,6 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv) goto fail2; if (atkbd->write) { - if (atkbd_probe(atkbd)) { err = -ENODEV; goto fail3; @@ -1354,7 +1337,6 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv) * atkbd_reconnect() tries to restore keyboard into a sane state and is * most likely called on resume. */ - static int atkbd_reconnect(struct serio *serio) { struct atkbd *atkbd = atkbd_from_serio(serio); @@ -1389,7 +1371,6 @@ static int atkbd_reconnect(struct serio *serio) atkbd_set_leds(atkbd); if (!atkbd->softrepeat) atkbd_set_repeat_rate(atkbd); - } /* @@ -1445,7 +1426,7 @@ static struct serio_driver atkbd_drv = { }; static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, - ssize_t (*handler)(struct atkbd *, char *)) + ssize_t (*handler)(struct atkbd *, char *)) { struct serio *serio = to_serio_port(dev); struct atkbd *atkbd = atkbd_from_serio(serio); @@ -1454,7 +1435,7 @@ static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, } static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count, - ssize_t (*handler)(struct atkbd *, const char *, size_t)) + ssize_t (*handler)(struct atkbd *, const char *, size_t)) { struct serio *serio = to_serio_port(dev); struct atkbd *atkbd = atkbd_from_serio(serio); @@ -1527,8 +1508,8 @@ static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t coun return err; } input_unregister_device(old_dev); - } + return count; } @@ -1544,7 +1525,7 @@ static ssize_t atkbd_show_force_release(struct atkbd *atkbd, char *buf) } static ssize_t atkbd_set_force_release(struct atkbd *atkbd, - const char *buf, size_t count) + const char *buf, size_t count) { /* 64 bytes on stack should be acceptable */ DECLARE_BITMAP(new_mask, ATKBD_KEYMAP_SIZE); @@ -1558,7 +1539,6 @@ static ssize_t atkbd_set_force_release(struct atkbd *atkbd, return count; } - static ssize_t atkbd_show_scroll(struct atkbd *atkbd, char *buf) { return sprintf(buf, "%d\n", atkbd->scroll ? 1 : 0); @@ -1715,7 +1695,6 @@ static ssize_t atkbd_set_softrepeat(struct atkbd *atkbd, const char *buf, size_t return count; } - static ssize_t atkbd_show_softraw(struct atkbd *atkbd, char *buf) { return sprintf(buf, "%d\n", atkbd->softraw ? 1 : 0); -- cgit v1.2.3 From 37e89761380b5e65dccf3f0e5fa494f2514a08a2 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 9 Mar 2026 07:03:18 +0700 Subject: dt-bindings: input: touchscreen: sitronix,st1232: Add wakeup-source Document the 'wakeup-source' property for Sitronix ST1232 touchscreen controllers to allow the device to wake the system from suspend. Acked-by: Krzysztof Kozlowski Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260309000319.74880-2-phucduc.bui@gmail.com Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/touchscreen/sitronix,st1232.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/input/touchscreen/sitronix,st1232.yaml b/Documentation/devicetree/bindings/input/touchscreen/sitronix,st1232.yaml index 978afaa4fcef..fe1fa217d842 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/sitronix,st1232.yaml +++ b/Documentation/devicetree/bindings/input/touchscreen/sitronix,st1232.yaml @@ -32,6 +32,9 @@ properties: description: A phandle to the reset GPIO maxItems: 1 + wakeup-source: + type: boolean + required: - compatible - reg @@ -51,6 +54,7 @@ examples: reg = <0x55>; interrupts = <2 0>; gpios = <&gpio1 166 0>; + wakeup-source; touch-overlay { segment-0 { -- cgit v1.2.3 From 6d4b67a2a76a4ff2393fe88119ae4332821b82b4 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 9 Mar 2026 14:14:13 +0700 Subject: Input: mpr121 - drop redundant wakeup handling The driver currently calls device_init_wakeup() and manually toggles IRQ wake in suspend and resume paths. This is unnecessary since the I2C core already handles wakeup configuration when the device is described in Device Tree with the "wakeup-source" property. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260309071413.92709-1-phucduc.bui@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/mpr121_touchkey.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/input/keyboard/mpr121_touchkey.c b/drivers/input/keyboard/mpr121_touchkey.c index bd1a944ded46..47edc161ec77 100644 --- a/drivers/input/keyboard/mpr121_touchkey.c +++ b/drivers/input/keyboard/mpr121_touchkey.c @@ -295,8 +295,6 @@ static int mpr_touchkey_probe(struct i2c_client *client) return error; i2c_set_clientdata(client, mpr121); - device_init_wakeup(dev, - device_property_read_bool(dev, "wakeup-source")); return 0; } @@ -305,9 +303,6 @@ static int mpr_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); - if (device_may_wakeup(&client->dev)) - enable_irq_wake(client->irq); - i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, 0x00); return 0; @@ -318,9 +313,6 @@ static int mpr_resume(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct mpr121_touchkey *mpr121 = i2c_get_clientdata(client); - if (device_may_wakeup(&client->dev)) - disable_irq_wake(client->irq); - i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, mpr121->keycount); -- cgit v1.2.3 From e732b2ac0a18b64369c0def65b6214f6748d0d73 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 10 Mar 2026 17:43:29 -0700 Subject: Input: hgpk - remove protocol support The protocol flavor for ALPS touchpads found in OLPC laptops has been broken since 2015 commit c378b5119eb0 ("Input: psmouse - factor out common protocol probing code") that forgot to add hgpk_init() to HGPK entry in psmouse_protocols array. Since nobody complained for 10 years let's remove it. Acked-by: Andres Salomon Link: https://patch.msgid.link/abC5U_JigA9TrGYu@google.com Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/Kconfig | 10 - drivers/input/mouse/Makefile | 1 - drivers/input/mouse/hgpk.c | 1063 ------------------------------------ drivers/input/mouse/hgpk.h | 61 --- drivers/input/mouse/psmouse-base.c | 21 +- drivers/input/mouse/psmouse.h | 2 +- 6 files changed, 2 insertions(+), 1156 deletions(-) delete mode 100644 drivers/input/mouse/hgpk.c delete mode 100644 drivers/input/mouse/hgpk.h diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 833b643f0616..30d119d4634b 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -164,16 +164,6 @@ config MOUSE_PS2_TOUCHKIT If unsure, say N. -config MOUSE_PS2_OLPC - bool "OLPC PS/2 mouse protocol extension" - depends on MOUSE_PS2 && OLPC - help - Say Y here if you have an OLPC XO-1 laptop (with built-in - PS/2 touchpad/tablet device). The manufacturer calls the - touchpad an HGPK. - - If unsure, say N. - config MOUSE_PS2_FOCALTECH bool "FocalTech PS/2 mouse protocol extension" if EXPERT default y diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index a1336d5bee6f..137ed0c32d90 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -29,7 +29,6 @@ psmouse-objs := psmouse-base.o synaptics.o focaltech.o psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o psmouse-$(CONFIG_MOUSE_PS2_BYD) += byd.o 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_SENTELIC) += sentelic.o diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c deleted file mode 100644 index 3c4d16ed88a9..000000000000 --- a/drivers/input/mouse/hgpk.c +++ /dev/null @@ -1,1063 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * OLPC HGPK (XO-1) touchpad PS/2 mouse driver - * - * Copyright (c) 2006-2008 One Laptop Per Child - * Authors: - * Zephaniah E. Hull - * Andres Salomon - * - * This driver is partly based on the ALPS driver, which is: - * - * Copyright (c) 2003 Neil Brown - * Copyright (c) 2003-2005 Peter Osterlund - * Copyright (c) 2004 Dmitry Torokhov - * Copyright (c) 2005 Vojtech Pavlik - */ - -/* - * The spec from ALPS is available from - * . It refers to this - * device as HGPK (Hybrid GS, PT, and Keymatrix). - * - * The earliest versions of the device had simultaneous reporting; that - * was removed. After that, the device used the Advanced Mode GS/PT streaming - * stuff. That turned out to be too buggy to support, so we've finally - * switched to Mouse Mode (which utilizes only the center 1/3 of the touchpad). - */ - -#define DEBUG -#include -#include -#include -#include -#include -#include -#include - -#include "psmouse.h" -#include "hgpk.h" - -#define ILLEGAL_XY 999999 - -static bool tpdebug; -module_param(tpdebug, bool, 0644); -MODULE_PARM_DESC(tpdebug, "enable debugging, dumping packets to KERN_DEBUG."); - -static int recalib_delta = 100; -module_param(recalib_delta, int, 0644); -MODULE_PARM_DESC(recalib_delta, - "packets containing a delta this large will be discarded, and a " - "recalibration may be scheduled."); - -static int jumpy_delay = 20; -module_param(jumpy_delay, int, 0644); -MODULE_PARM_DESC(jumpy_delay, - "delay (ms) before recal after jumpiness detected"); - -static int spew_delay = 1; -module_param(spew_delay, int, 0644); -MODULE_PARM_DESC(spew_delay, - "delay (ms) before recal after packet spew detected"); - -static int recal_guard_time; -module_param(recal_guard_time, int, 0644); -MODULE_PARM_DESC(recal_guard_time, - "interval (ms) during which recal will be restarted if packet received"); - -static int post_interrupt_delay = 40; -module_param(post_interrupt_delay, int, 0644); -MODULE_PARM_DESC(post_interrupt_delay, - "delay (ms) before recal after recal interrupt detected"); - -static bool autorecal = true; -module_param(autorecal, bool, 0644); -MODULE_PARM_DESC(autorecal, "enable recalibration in the driver"); - -static char hgpk_mode_name[16]; -module_param_string(hgpk_mode, hgpk_mode_name, sizeof(hgpk_mode_name), 0644); -MODULE_PARM_DESC(hgpk_mode, - "default hgpk mode: mouse, glidesensor or pentablet"); - -static int hgpk_default_mode = HGPK_MODE_MOUSE; - -static const char * const hgpk_mode_names[] = { - [HGPK_MODE_MOUSE] = "Mouse", - [HGPK_MODE_GLIDESENSOR] = "GlideSensor", - [HGPK_MODE_PENTABLET] = "PenTablet", -}; - -static int hgpk_mode_from_name(const char *buf, int len) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(hgpk_mode_names); i++) { - const char *name = hgpk_mode_names[i]; - if (strlen(name) == len && !strncasecmp(name, buf, len)) - return i; - } - - return HGPK_MODE_INVALID; -} - -/* - * see if new value is within 20% of half of old value - */ -static int approx_half(int curr, int prev) -{ - int belowhalf, abovehalf; - - if (curr < 5 || prev < 5) - return 0; - - belowhalf = (prev * 8) / 20; - abovehalf = (prev * 12) / 20; - - return belowhalf < curr && curr <= abovehalf; -} - -/* - * Throw out oddly large delta packets, and any that immediately follow whose - * values are each approximately half of the previous. It seems that the ALPS - * firmware emits errant packets, and they get averaged out slowly. - */ -static int hgpk_discard_decay_hack(struct psmouse *psmouse, int x, int y) -{ - struct hgpk_data *priv = psmouse->private; - int avx, avy; - bool do_recal = false; - - avx = abs(x); - avy = abs(y); - - /* discard if too big, or half that but > 4 times the prev delta */ - if (avx > recalib_delta || - (avx > recalib_delta / 2 && ((avx / 4) > priv->xlast))) { - psmouse_warn(psmouse, "detected %dpx jump in x\n", x); - priv->xbigj = avx; - } else if (approx_half(avx, priv->xbigj)) { - psmouse_warn(psmouse, "detected secondary %dpx jump in x\n", x); - priv->xbigj = avx; - priv->xsaw_secondary++; - } else { - if (priv->xbigj && priv->xsaw_secondary > 1) - do_recal = true; - priv->xbigj = 0; - priv->xsaw_secondary = 0; - } - - if (avy > recalib_delta || - (avy > recalib_delta / 2 && ((avy / 4) > priv->ylast))) { - psmouse_warn(psmouse, "detected %dpx jump in y\n", y); - priv->ybigj = avy; - } else if (approx_half(avy, priv->ybigj)) { - psmouse_warn(psmouse, "detected secondary %dpx jump in y\n", y); - priv->ybigj = avy; - priv->ysaw_secondary++; - } else { - if (priv->ybigj && priv->ysaw_secondary > 1) - do_recal = true; - priv->ybigj = 0; - priv->ysaw_secondary = 0; - } - - priv->xlast = avx; - priv->ylast = avy; - - if (do_recal && jumpy_delay) { - psmouse_warn(psmouse, "scheduling recalibration\n"); - psmouse_queue_work(psmouse, &priv->recalib_wq, - msecs_to_jiffies(jumpy_delay)); - } - - return priv->xbigj || priv->ybigj; -} - -static void hgpk_reset_spew_detection(struct hgpk_data *priv) -{ - priv->spew_count = 0; - priv->dupe_count = 0; - priv->x_tally = 0; - priv->y_tally = 0; - priv->spew_flag = NO_SPEW; -} - -static void hgpk_reset_hack_state(struct psmouse *psmouse) -{ - struct hgpk_data *priv = psmouse->private; - - priv->abs_x = priv->abs_y = -1; - priv->xlast = priv->ylast = ILLEGAL_XY; - priv->xbigj = priv->ybigj = 0; - priv->xsaw_secondary = priv->ysaw_secondary = 0; - hgpk_reset_spew_detection(priv); -} - -/* - * We have no idea why this particular hardware bug occurs. The touchpad - * will randomly start spewing packets without anything touching the - * pad. This wouldn't necessarily be bad, but it's indicative of a - * severely miscalibrated pad; attempting to use the touchpad while it's - * spewing means the cursor will jump all over the place, and act "drunk". - * - * The packets that are spewed tend to all have deltas between -2 and 2, and - * the cursor will move around without really going very far. It will - * tend to end up in the same location; if we tally up the changes over - * 100 packets, we end up w/ a final delta of close to 0. This happens - * pretty regularly when the touchpad is spewing, and is pretty hard to - * manually trigger (at least for *my* fingers). So, it makes a perfect - * scheme for detecting spews. - */ -static void hgpk_spewing_hack(struct psmouse *psmouse, - int l, int r, int x, int y) -{ - struct hgpk_data *priv = psmouse->private; - - /* ignore button press packets; many in a row could trigger - * a false-positive! */ - if (l || r) - return; - - /* don't track spew if the workaround feature has been turned off */ - if (!spew_delay) - return; - - if (abs(x) > 3 || abs(y) > 3) { - /* no spew, or spew ended */ - hgpk_reset_spew_detection(priv); - return; - } - - /* Keep a tally of the overall delta to the cursor position caused by - * the spew */ - priv->x_tally += x; - priv->y_tally += y; - - switch (priv->spew_flag) { - case NO_SPEW: - /* we're not spewing, but this packet might be the start */ - priv->spew_flag = MAYBE_SPEWING; - - fallthrough; - - case MAYBE_SPEWING: - priv->spew_count++; - - if (priv->spew_count < SPEW_WATCH_COUNT) - break; - - /* excessive spew detected, request recalibration */ - priv->spew_flag = SPEW_DETECTED; - - fallthrough; - - case SPEW_DETECTED: - /* only recalibrate when the overall delta to the cursor - * is really small. if the spew is causing significant cursor - * movement, it is probably a case of the user moving the - * cursor very slowly across the screen. */ - if (abs(priv->x_tally) < 3 && abs(priv->y_tally) < 3) { - psmouse_warn(psmouse, "packet spew detected (%d,%d)\n", - priv->x_tally, priv->y_tally); - priv->spew_flag = RECALIBRATING; - psmouse_queue_work(psmouse, &priv->recalib_wq, - msecs_to_jiffies(spew_delay)); - } - - break; - case RECALIBRATING: - /* we already detected a spew and requested a recalibration, - * just wait for the queue to kick into action. */ - break; - } -} - -/* - * HGPK Mouse Mode format (standard mouse format, sans middle button) - * - * byte 0: y-over x-over y-neg x-neg 1 0 swr swl - * byte 1: x7 x6 x5 x4 x3 x2 x1 x0 - * byte 2: y7 y6 y5 y4 y3 y2 y1 y0 - * - * swr/swl are the left/right buttons. - * x-neg/y-neg are the x and y delta negative bits - * x-over/y-over are the x and y overflow bits - * - * --- - * - * HGPK Advanced Mode - single-mode format - * - * byte 0(PT): 1 1 0 0 1 1 1 1 - * byte 0(GS): 1 1 1 1 1 1 1 1 - * byte 1: 0 x6 x5 x4 x3 x2 x1 x0 - * byte 2(PT): 0 0 x9 x8 x7 ? pt-dsw 0 - * byte 2(GS): 0 x10 x9 x8 x7 ? gs-dsw pt-dsw - * byte 3: 0 y9 y8 y7 1 0 swr swl - * byte 4: 0 y6 y5 y4 y3 y2 y1 y0 - * byte 5: 0 z6 z5 z4 z3 z2 z1 z0 - * - * ?'s are not defined in the protocol spec, may vary between models. - * - * swr/swl are the left/right buttons. - * - * pt-dsw/gs-dsw indicate that the pt/gs sensor is detecting a - * pen/finger - */ -static bool hgpk_is_byte_valid(struct psmouse *psmouse, unsigned char *packet) -{ - struct hgpk_data *priv = psmouse->private; - int pktcnt = psmouse->pktcnt; - bool valid; - - switch (priv->mode) { - case HGPK_MODE_MOUSE: - valid = (packet[0] & 0x0C) == 0x08; - break; - - case HGPK_MODE_GLIDESENSOR: - valid = pktcnt == 1 ? - packet[0] == HGPK_GS : !(packet[pktcnt - 1] & 0x80); - break; - - case HGPK_MODE_PENTABLET: - valid = pktcnt == 1 ? - packet[0] == HGPK_PT : !(packet[pktcnt - 1] & 0x80); - break; - - default: - valid = false; - break; - } - - if (!valid) - psmouse_dbg(psmouse, - "bad data, mode %d (%d) %*ph\n", - priv->mode, pktcnt, 6, psmouse->packet); - - return valid; -} - -static void hgpk_process_advanced_packet(struct psmouse *psmouse) -{ - struct hgpk_data *priv = psmouse->private; - struct input_dev *idev = psmouse->dev; - unsigned char *packet = psmouse->packet; - int down = !!(packet[2] & 2); - int left = !!(packet[3] & 1); - int right = !!(packet[3] & 2); - int x = packet[1] | ((packet[2] & 0x78) << 4); - int y = packet[4] | ((packet[3] & 0x70) << 3); - - if (priv->mode == HGPK_MODE_GLIDESENSOR) { - int pt_down = !!(packet[2] & 1); - int finger_down = !!(packet[2] & 2); - int z = packet[5]; - - input_report_abs(idev, ABS_PRESSURE, z); - if (tpdebug) - psmouse_dbg(psmouse, "pd=%d fd=%d z=%d", - pt_down, finger_down, z); - } else { - /* - * PenTablet mode does not report pressure, so we don't - * report it here - */ - if (tpdebug) - psmouse_dbg(psmouse, "pd=%d ", down); - } - - if (tpdebug) - psmouse_dbg(psmouse, "l=%d r=%d x=%d y=%d\n", - left, right, x, y); - - input_report_key(idev, BTN_TOUCH, down); - input_report_key(idev, BTN_LEFT, left); - input_report_key(idev, BTN_RIGHT, right); - - /* - * If this packet says that the finger was removed, reset our position - * tracking so that we don't erroneously detect a jump on next press. - */ - if (!down) { - hgpk_reset_hack_state(psmouse); - goto done; - } - - /* - * Weed out duplicate packets (we get quite a few, and they mess up - * our jump detection) - */ - if (x == priv->abs_x && y == priv->abs_y) { - if (++priv->dupe_count > SPEW_WATCH_COUNT) { - if (tpdebug) - psmouse_dbg(psmouse, "hard spew detected\n"); - priv->spew_flag = RECALIBRATING; - psmouse_queue_work(psmouse, &priv->recalib_wq, - msecs_to_jiffies(spew_delay)); - } - goto done; - } - - /* not a duplicate, continue with position reporting */ - priv->dupe_count = 0; - - /* Don't apply hacks in PT mode, it seems reliable */ - if (priv->mode != HGPK_MODE_PENTABLET && priv->abs_x != -1) { - int x_diff = priv->abs_x - x; - int y_diff = priv->abs_y - y; - if (hgpk_discard_decay_hack(psmouse, x_diff, y_diff)) { - if (tpdebug) - psmouse_dbg(psmouse, "discarding\n"); - goto done; - } - hgpk_spewing_hack(psmouse, left, right, x_diff, y_diff); - } - - input_report_abs(idev, ABS_X, x); - input_report_abs(idev, ABS_Y, y); - priv->abs_x = x; - priv->abs_y = y; - -done: - input_sync(idev); -} - -static void hgpk_process_simple_packet(struct psmouse *psmouse) -{ - struct input_dev *dev = psmouse->dev; - unsigned char *packet = psmouse->packet; - int left = packet[0] & 1; - int right = (packet[0] >> 1) & 1; - int x = packet[1] - ((packet[0] << 4) & 0x100); - int y = ((packet[0] << 3) & 0x100) - packet[2]; - - if (packet[0] & 0xc0) - psmouse_dbg(psmouse, - "overflow -- 0x%02x 0x%02x 0x%02x\n", - packet[0], packet[1], packet[2]); - - if (hgpk_discard_decay_hack(psmouse, x, y)) { - if (tpdebug) - psmouse_dbg(psmouse, "discarding\n"); - return; - } - - hgpk_spewing_hack(psmouse, left, right, x, y); - - if (tpdebug) - psmouse_dbg(psmouse, "l=%d r=%d x=%d y=%d\n", - left, right, x, y); - - input_report_key(dev, BTN_LEFT, left); - input_report_key(dev, BTN_RIGHT, right); - - input_report_rel(dev, REL_X, x); - input_report_rel(dev, REL_Y, y); - - input_sync(dev); -} - -static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse) -{ - struct hgpk_data *priv = psmouse->private; - - if (!hgpk_is_byte_valid(psmouse, psmouse->packet)) - return PSMOUSE_BAD_DATA; - - if (psmouse->pktcnt >= psmouse->pktsize) { - if (priv->mode == HGPK_MODE_MOUSE) - hgpk_process_simple_packet(psmouse); - else - hgpk_process_advanced_packet(psmouse); - return PSMOUSE_FULL_PACKET; - } - - if (priv->recalib_window) { - if (time_before(jiffies, priv->recalib_window)) { - /* - * ugh, got a packet inside our recalibration - * window, schedule another recalibration. - */ - psmouse_dbg(psmouse, - "packet inside calibration window, queueing another recalibration\n"); - psmouse_queue_work(psmouse, &priv->recalib_wq, - msecs_to_jiffies(post_interrupt_delay)); - } - priv->recalib_window = 0; - } - - return PSMOUSE_GOOD_DATA; -} - -static int hgpk_select_mode(struct psmouse *psmouse) -{ - struct ps2dev *ps2dev = &psmouse->ps2dev; - struct hgpk_data *priv = psmouse->private; - int i; - int cmd; - - /* - * 4 disables to enable advanced mode - * then 3 0xf2 bytes as the preamble for GS/PT selection - */ - const int advanced_init[] = { - PSMOUSE_CMD_DISABLE, PSMOUSE_CMD_DISABLE, - PSMOUSE_CMD_DISABLE, PSMOUSE_CMD_DISABLE, - 0xf2, 0xf2, 0xf2, - }; - - switch (priv->mode) { - case HGPK_MODE_MOUSE: - psmouse->pktsize = 3; - break; - - case HGPK_MODE_GLIDESENSOR: - case HGPK_MODE_PENTABLET: - psmouse->pktsize = 6; - - /* Switch to 'Advanced mode.', four disables in a row. */ - for (i = 0; i < ARRAY_SIZE(advanced_init); i++) - if (ps2_command(ps2dev, NULL, advanced_init[i])) - return -EIO; - - /* select between GlideSensor (mouse) or PenTablet */ - cmd = priv->mode == HGPK_MODE_GLIDESENSOR ? - PSMOUSE_CMD_SETSCALE11 : PSMOUSE_CMD_SETSCALE21; - - if (ps2_command(ps2dev, NULL, cmd)) - return -EIO; - break; - - default: - return -EINVAL; - } - - return 0; -} - -static void hgpk_setup_input_device(struct input_dev *input, - struct input_dev *old_input, - enum hgpk_mode mode) -{ - if (old_input) { - input->name = old_input->name; - input->phys = old_input->phys; - input->id = old_input->id; - input->dev.parent = old_input->dev.parent; - } - - memset(input->evbit, 0, sizeof(input->evbit)); - memset(input->relbit, 0, sizeof(input->relbit)); - memset(input->keybit, 0, sizeof(input->keybit)); - - /* All modes report left and right buttons */ - __set_bit(EV_KEY, input->evbit); - __set_bit(BTN_LEFT, input->keybit); - __set_bit(BTN_RIGHT, input->keybit); - - switch (mode) { - case HGPK_MODE_MOUSE: - __set_bit(EV_REL, input->evbit); - __set_bit(REL_X, input->relbit); - __set_bit(REL_Y, input->relbit); - break; - - case HGPK_MODE_GLIDESENSOR: - __set_bit(BTN_TOUCH, input->keybit); - __set_bit(BTN_TOOL_FINGER, input->keybit); - - __set_bit(EV_ABS, input->evbit); - - /* GlideSensor has pressure sensor, PenTablet does not */ - input_set_abs_params(input, ABS_PRESSURE, 0, 15, 0, 0); - - /* From device specs */ - input_set_abs_params(input, ABS_X, 0, 399, 0, 0); - input_set_abs_params(input, ABS_Y, 0, 290, 0, 0); - - /* Calculated by hand based on usable size (52mm x 38mm) */ - input_abs_set_res(input, ABS_X, 8); - input_abs_set_res(input, ABS_Y, 8); - break; - - case HGPK_MODE_PENTABLET: - __set_bit(BTN_TOUCH, input->keybit); - __set_bit(BTN_TOOL_FINGER, input->keybit); - - __set_bit(EV_ABS, input->evbit); - - /* From device specs */ - input_set_abs_params(input, ABS_X, 0, 999, 0, 0); - input_set_abs_params(input, ABS_Y, 5, 239, 0, 0); - - /* Calculated by hand based on usable size (156mm x 38mm) */ - input_abs_set_res(input, ABS_X, 6); - input_abs_set_res(input, ABS_Y, 8); - break; - - default: - BUG(); - } -} - -static int hgpk_reset_device(struct psmouse *psmouse, bool recalibrate) -{ - int err; - - psmouse_reset(psmouse); - - if (recalibrate) { - struct ps2dev *ps2dev = &psmouse->ps2dev; - - /* send the recalibrate request */ - if (ps2_command(ps2dev, NULL, 0xf5) || - ps2_command(ps2dev, NULL, 0xf5) || - ps2_command(ps2dev, NULL, 0xe6) || - ps2_command(ps2dev, NULL, 0xf5)) { - return -1; - } - - /* according to ALPS, 150mS is required for recalibration */ - msleep(150); - } - - err = hgpk_select_mode(psmouse); - if (err) { - psmouse_err(psmouse, "failed to select mode\n"); - return err; - } - - hgpk_reset_hack_state(psmouse); - - return 0; -} - -static int hgpk_force_recalibrate(struct psmouse *psmouse) -{ - struct hgpk_data *priv = psmouse->private; - int err; - - /* C-series touchpads added the recalibrate command */ - if (psmouse->model < HGPK_MODEL_C) - return 0; - - if (!autorecal) { - psmouse_dbg(psmouse, "recalibration disabled, ignoring\n"); - return 0; - } - - psmouse_dbg(psmouse, "recalibrating touchpad..\n"); - - /* we don't want to race with the irq handler, nor with resyncs */ - psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); - - /* start by resetting the device */ - err = hgpk_reset_device(psmouse, true); - if (err) - return err; - - /* - * XXX: If a finger is down during this delay, recalibration will - * detect capacitance incorrectly. This is a hardware bug, and - * we don't have a good way to deal with it. The 2s window stuff - * (below) is our best option for now. - */ - if (psmouse_activate(psmouse)) - return -1; - - if (tpdebug) - psmouse_dbg(psmouse, "touchpad reactivated\n"); - - /* - * If we get packets right away after recalibrating, it's likely - * that a finger was on the touchpad. If so, it's probably - * miscalibrated, so we optionally schedule another. - */ - if (recal_guard_time) - priv->recalib_window = jiffies + - msecs_to_jiffies(recal_guard_time); - - return 0; -} - -/* - * This puts the touchpad in a power saving mode; according to ALPS, current - * consumption goes down to 50uA after running this. To turn power back on, - * we drive MS-DAT low. Measuring with a 1mA resolution ammeter says that - * the current on the SUS_3.3V rail drops from 3mA or 4mA to 0 when we do this. - * - * We have no formal spec that details this operation -- the low-power - * sequence came from a long-lost email trail. - */ -static int hgpk_toggle_powersave(struct psmouse *psmouse, int enable) -{ - struct ps2dev *ps2dev = &psmouse->ps2dev; - int timeo; - int err; - - /* Added on D-series touchpads */ - if (psmouse->model < HGPK_MODEL_D) - return 0; - - if (enable) { - psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); - - /* - * Sending a byte will drive MS-DAT low; this will wake up - * the controller. Once we get an ACK back from it, it - * means we can continue with the touchpad re-init. ALPS - * tells us that 1s should be long enough, so set that as - * the upper bound. (in practice, it takes about 3 loops.) - */ - for (timeo = 20; timeo > 0; timeo--) { - if (!ps2_sendbyte(ps2dev, PSMOUSE_CMD_DISABLE, 20)) - break; - msleep(25); - } - - err = hgpk_reset_device(psmouse, false); - if (err) { - psmouse_err(psmouse, "Failed to reset device!\n"); - return err; - } - - /* should be all set, enable the touchpad */ - psmouse_activate(psmouse); - psmouse_dbg(psmouse, "Touchpad powered up.\n"); - } else { - psmouse_dbg(psmouse, "Powering off touchpad.\n"); - - if (ps2_command(ps2dev, NULL, 0xec) || - ps2_command(ps2dev, NULL, 0xec) || - ps2_command(ps2dev, NULL, 0xea)) { - return -1; - } - - psmouse_set_state(psmouse, PSMOUSE_IGNORE); - - /* probably won't see an ACK, the touchpad will be off */ - ps2_sendbyte(ps2dev, 0xec, 20); - } - - return 0; -} - -static int hgpk_poll(struct psmouse *psmouse) -{ - /* We can't poll, so always return failure. */ - return -1; -} - -static int hgpk_reconnect(struct psmouse *psmouse) -{ - struct hgpk_data *priv = psmouse->private; - - /* - * During suspend/resume the ps2 rails remain powered. We don't want - * to do a reset because it's flush data out of buffers; however, - * earlier prototypes (B1) had some brokenness that required a reset. - */ - if (olpc_board_at_least(olpc_board(0xb2))) - if (psmouse->ps2dev.serio->dev.power.power_state.event != - PM_EVENT_ON) - return 0; - - priv->powered = 1; - return hgpk_reset_device(psmouse, false); -} - -static ssize_t hgpk_show_powered(struct psmouse *psmouse, void *data, char *buf) -{ - struct hgpk_data *priv = psmouse->private; - - return sprintf(buf, "%d\n", priv->powered); -} - -static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data, - const char *buf, size_t count) -{ - struct hgpk_data *priv = psmouse->private; - unsigned int value; - int err; - - err = kstrtouint(buf, 10, &value); - if (err) - return err; - - if (value > 1) - return -EINVAL; - - if (value != priv->powered) { - /* - * hgpk_toggle_power will deal w/ state so - * we're not racing w/ irq - */ - err = hgpk_toggle_powersave(psmouse, value); - if (!err) - priv->powered = value; - } - - return err ? err : count; -} - -__PSMOUSE_DEFINE_ATTR(powered, S_IWUSR | S_IRUGO, NULL, - hgpk_show_powered, hgpk_set_powered, false); - -static ssize_t attr_show_mode(struct psmouse *psmouse, void *data, char *buf) -{ - struct hgpk_data *priv = psmouse->private; - - return sprintf(buf, "%s\n", hgpk_mode_names[priv->mode]); -} - -static ssize_t attr_set_mode(struct psmouse *psmouse, void *data, - const char *buf, size_t len) -{ - struct hgpk_data *priv = psmouse->private; - enum hgpk_mode old_mode = priv->mode; - enum hgpk_mode new_mode = hgpk_mode_from_name(buf, len); - struct input_dev *old_dev = psmouse->dev; - struct input_dev *new_dev; - int err; - - if (new_mode == HGPK_MODE_INVALID) - return -EINVAL; - - if (old_mode == new_mode) - return len; - - new_dev = input_allocate_device(); - if (!new_dev) - return -ENOMEM; - - psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); - - /* Switch device into the new mode */ - priv->mode = new_mode; - err = hgpk_reset_device(psmouse, false); - if (err) - goto err_try_restore; - - hgpk_setup_input_device(new_dev, old_dev, new_mode); - - psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); - - err = input_register_device(new_dev); - if (err) - goto err_try_restore; - - psmouse->dev = new_dev; - input_unregister_device(old_dev); - - return len; - -err_try_restore: - input_free_device(new_dev); - priv->mode = old_mode; - hgpk_reset_device(psmouse, false); - - return err; -} - -PSMOUSE_DEFINE_ATTR(hgpk_mode, S_IWUSR | S_IRUGO, NULL, - attr_show_mode, attr_set_mode); - -static ssize_t hgpk_trigger_recal_show(struct psmouse *psmouse, - void *data, char *buf) -{ - return -EINVAL; -} - -static ssize_t hgpk_trigger_recal(struct psmouse *psmouse, void *data, - const char *buf, size_t count) -{ - struct hgpk_data *priv = psmouse->private; - unsigned int value; - int err; - - err = kstrtouint(buf, 10, &value); - if (err) - return err; - - if (value != 1) - return -EINVAL; - - /* - * We queue work instead of doing recalibration right here - * to avoid adding locking to hgpk_force_recalibrate() - * since workqueue provides serialization. - */ - psmouse_queue_work(psmouse, &priv->recalib_wq, 0); - return count; -} - -__PSMOUSE_DEFINE_ATTR(recalibrate, S_IWUSR | S_IRUGO, NULL, - hgpk_trigger_recal_show, hgpk_trigger_recal, false); - -static void hgpk_disconnect(struct psmouse *psmouse) -{ - struct hgpk_data *priv = psmouse->private; - - device_remove_file(&psmouse->ps2dev.serio->dev, - &psmouse_attr_powered.dattr); - device_remove_file(&psmouse->ps2dev.serio->dev, - &psmouse_attr_hgpk_mode.dattr); - - if (psmouse->model >= HGPK_MODEL_C) - device_remove_file(&psmouse->ps2dev.serio->dev, - &psmouse_attr_recalibrate.dattr); - - psmouse_reset(psmouse); - kfree(priv); -} - -static void hgpk_recalib_work(struct work_struct *work) -{ - struct delayed_work *w = to_delayed_work(work); - struct hgpk_data *priv = container_of(w, struct hgpk_data, recalib_wq); - struct psmouse *psmouse = priv->psmouse; - - if (hgpk_force_recalibrate(psmouse)) - psmouse_err(psmouse, "recalibration failed!\n"); -} - -static int hgpk_register(struct psmouse *psmouse) -{ - struct hgpk_data *priv = psmouse->private; - int err; - - /* register handlers */ - psmouse->protocol_handler = hgpk_process_byte; - psmouse->poll = hgpk_poll; - psmouse->disconnect = hgpk_disconnect; - psmouse->reconnect = hgpk_reconnect; - - /* Disable the idle resync. */ - psmouse->resync_time = 0; - /* Reset after a lot of bad bytes. */ - psmouse->resetafter = 1024; - - hgpk_setup_input_device(psmouse->dev, NULL, priv->mode); - - err = device_create_file(&psmouse->ps2dev.serio->dev, - &psmouse_attr_powered.dattr); - if (err) { - psmouse_err(psmouse, "Failed creating 'powered' sysfs node\n"); - return err; - } - - err = device_create_file(&psmouse->ps2dev.serio->dev, - &psmouse_attr_hgpk_mode.dattr); - if (err) { - psmouse_err(psmouse, - "Failed creating 'hgpk_mode' sysfs node\n"); - goto err_remove_powered; - } - - /* C-series touchpads added the recalibrate command */ - if (psmouse->model >= HGPK_MODEL_C) { - err = device_create_file(&psmouse->ps2dev.serio->dev, - &psmouse_attr_recalibrate.dattr); - if (err) { - psmouse_err(psmouse, - "Failed creating 'recalibrate' sysfs node\n"); - goto err_remove_mode; - } - } - - return 0; - -err_remove_mode: - device_remove_file(&psmouse->ps2dev.serio->dev, - &psmouse_attr_hgpk_mode.dattr); -err_remove_powered: - device_remove_file(&psmouse->ps2dev.serio->dev, - &psmouse_attr_powered.dattr); - return err; -} - -int hgpk_init(struct psmouse *psmouse) -{ - struct hgpk_data *priv; - int err; - - priv = kzalloc_obj(*priv); - if (!priv) { - err = -ENOMEM; - goto alloc_fail; - } - - psmouse->private = priv; - - priv->psmouse = psmouse; - priv->powered = true; - priv->mode = hgpk_default_mode; - INIT_DELAYED_WORK(&priv->recalib_wq, hgpk_recalib_work); - - err = hgpk_reset_device(psmouse, false); - if (err) - goto init_fail; - - err = hgpk_register(psmouse); - if (err) - goto init_fail; - - return 0; - -init_fail: - kfree(priv); -alloc_fail: - return err; -} - -static enum hgpk_model_t hgpk_get_model(struct psmouse *psmouse) -{ - struct ps2dev *ps2dev = &psmouse->ps2dev; - unsigned char param[3]; - - /* E7, E7, E7, E9 gets us a 3 byte identifier */ - if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || - ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { - return -EIO; - } - - psmouse_dbg(psmouse, "ID: %*ph\n", 3, param); - - /* HGPK signature: 0x67, 0x00, 0x */ - if (param[0] != 0x67 || param[1] != 0x00) - return -ENODEV; - - psmouse_info(psmouse, "OLPC touchpad revision 0x%x\n", param[2]); - - return param[2]; -} - -int hgpk_detect(struct psmouse *psmouse, bool set_properties) -{ - int version; - - version = hgpk_get_model(psmouse); - if (version < 0) - return version; - - if (set_properties) { - psmouse->vendor = "ALPS"; - psmouse->name = "HGPK"; - psmouse->model = version; - } - - return 0; -} - -void hgpk_module_init(void) -{ - hgpk_default_mode = hgpk_mode_from_name(hgpk_mode_name, - strlen(hgpk_mode_name)); - if (hgpk_default_mode == HGPK_MODE_INVALID) { - hgpk_default_mode = HGPK_MODE_MOUSE; - strscpy(hgpk_mode_name, hgpk_mode_names[HGPK_MODE_MOUSE], - sizeof(hgpk_mode_name)); - } -} diff --git a/drivers/input/mouse/hgpk.h b/drivers/input/mouse/hgpk.h deleted file mode 100644 index ce041591f1a8..000000000000 --- a/drivers/input/mouse/hgpk.h +++ /dev/null @@ -1,61 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * OLPC HGPK (XO-1) touchpad PS/2 mouse driver - */ - -#ifndef _HGPK_H -#define _HGPK_H - -#define HGPK_GS 0xff /* The GlideSensor */ -#define HGPK_PT 0xcf /* The PenTablet */ - -enum hgpk_model_t { - HGPK_MODEL_PREA = 0x0a, /* pre-B1s */ - HGPK_MODEL_A = 0x14, /* found on B1s, PT disabled in hardware */ - HGPK_MODEL_B = 0x28, /* B2s, has capacitance issues */ - HGPK_MODEL_C = 0x3c, - HGPK_MODEL_D = 0x50, /* C1, mass production */ -}; - -enum hgpk_spew_flag { - NO_SPEW, - MAYBE_SPEWING, - SPEW_DETECTED, - RECALIBRATING, -}; - -#define SPEW_WATCH_COUNT 42 /* at 12ms/packet, this is 1/2 second */ - -enum hgpk_mode { - HGPK_MODE_MOUSE, - HGPK_MODE_GLIDESENSOR, - HGPK_MODE_PENTABLET, - HGPK_MODE_INVALID -}; - -struct hgpk_data { - struct psmouse *psmouse; - enum hgpk_mode mode; - bool powered; - enum hgpk_spew_flag spew_flag; - int spew_count, x_tally, y_tally; /* spew detection */ - unsigned long recalib_window; - struct delayed_work recalib_wq; - int abs_x, abs_y; - int dupe_count; - int xbigj, ybigj, xlast, ylast; /* jumpiness detection */ - int xsaw_secondary, ysaw_secondary; /* jumpiness detection */ -}; - -int hgpk_detect(struct psmouse *psmouse, bool set_properties); -int hgpk_init(struct psmouse *psmouse); - -#ifdef CONFIG_MOUSE_PS2_OLPC -void hgpk_module_init(void); -#else -static inline void hgpk_module_init(void) -{ -} -#endif - -#endif diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index ff8cd2d68a3c..0e02c997c7e5 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -26,7 +26,6 @@ #include "synaptics.h" #include "logips2pp.h" #include "alps.h" -#include "hgpk.h" #include "lifebook.h" #include "trackpoint.h" #include "touchkit_ps2.h" @@ -393,9 +392,7 @@ static void psmouse_receive_byte(struct ps2dev *ps2dev, u8 data) return; } - if (psmouse->packet[1] == PSMOUSE_RET_ID || - (psmouse->protocol->type == PSMOUSE_HGPK && - psmouse->packet[1] == PSMOUSE_RET_BAT)) { + if (psmouse->packet[1] == PSMOUSE_RET_ID) { __psmouse_set_state(psmouse, PSMOUSE_IGNORE); serio_reconnect(ps2dev->serio); return; @@ -837,14 +834,6 @@ static const struct psmouse_protocol psmouse_protocols[] = { .detect = touchkit_ps2_detect, }, #endif -#ifdef CONFIG_MOUSE_PS2_OLPC - { - .type = PSMOUSE_HGPK, - .name = "OLPC HGPK", - .alias = "hgpk", - .detect = hgpk_detect, - }, -#endif #ifdef CONFIG_MOUSE_PS2_ELANTECH { .type = PSMOUSE_ELANTECH, @@ -1153,13 +1142,6 @@ static int psmouse_extensions(struct psmouse *psmouse, return PSMOUSE_ALPS; } - /* Try OLPC HGPK touchpad */ - if (max_proto > PSMOUSE_IMEX && - psmouse_try_protocol(psmouse, PSMOUSE_HGPK, &max_proto, - set_properties, true)) { - return PSMOUSE_HGPK; - } - /* Try Elantech touchpad */ if (max_proto > PSMOUSE_IMEX && psmouse_try_protocol(psmouse, PSMOUSE_ELANTECH, @@ -2035,7 +2017,6 @@ static int __init psmouse_init(void) lifebook_module_init(); synaptics_module_init(); - hgpk_module_init(); err = psmouse_smbus_module_init(); if (err) diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 4d8acfe0d82a..783201301e5c 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -59,7 +59,7 @@ enum psmouse_type { PSMOUSE_TRACKPOINT, PSMOUSE_TOUCHKIT_PS2, PSMOUSE_CORTRON, - PSMOUSE_HGPK, + PSMOUSE_HGPK, /* No longer used */ PSMOUSE_ELANTECH, PSMOUSE_FSP, PSMOUSE_SYNAPTICS_RELATIVE, -- cgit v1.2.3 From 3873f16d4936690ddd7b35231fa5c712a3c63a54 Mon Sep 17 00:00:00 2001 From: Duoming Zhou Date: Wed, 17 Dec 2025 11:00:18 +0800 Subject: Input: psmouse - replace flush_workqueue() with disable_delayed_work_sync() The original code uses flush_workqueue() in psmouse_disconnect() to ensure the completion of both resync_work and dev3_register_work. Given that alps_disconnect() already uses disable_delayed_work_sync() to cancel dev3_register_work, replacing flush_workqueue() with disable_delayed_work_sync(&psmouse->resync_work) is more robust and efficient. Signed-off-by: Duoming Zhou Link: https://patch.msgid.link/6e40a46e5d9e6e3237702958b8f641263c28d2e4.1765939397.git.duoming@zju.edu.cn Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/psmouse-base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 0e02c997c7e5..3f34825c8691 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -1466,7 +1466,7 @@ static void psmouse_disconnect(struct serio *serio) /* make sure we don't have a resync in progress */ mutex_unlock(&psmouse_mutex); - flush_workqueue(kpsmoused_wq); + disable_delayed_work_sync(&psmouse->resync_work); mutex_lock(&psmouse_mutex); if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { -- cgit v1.2.3 From d4904a3d7159b342d45de8495046d33d1c18998e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 11 Mar 2026 22:39:58 -0700 Subject: Input: alps - use standard workqueue when registering supplemental device Registering supplemental bare PS/2 device does not need to be ordered relative to attempt to resynchronization done in psmouse core. Switch to the default workqueue and use normal (non-delayed) work. Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 11 +++++------ drivers/input/mouse/alps.h | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index f3d3b6b4e02d..0ee1a30b9aaf 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -12,6 +12,7 @@ * tpconfig utility (by C. Scott Ananian and Bruce Kall). */ +#include "linux/workqueue.h" #include #include #include @@ -1452,7 +1453,7 @@ err_free_input: 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); + dev3_register_work); int error; guard(mutex)(&alps_mutex); @@ -1485,8 +1486,7 @@ static void alps_report_bare_ps2_packet(struct psmouse *psmouse, } else if (unlikely(IS_ERR_OR_NULL(priv->dev3))) { /* Register dev3 mouse if we received PS/2 packet first time */ if (!IS_ERR(priv->dev3)) - psmouse_queue_work(psmouse, &priv->dev3_register_work, - 0); + schedule_work(&priv->dev3_register_work); return; } else { dev = priv->dev3; @@ -2975,7 +2975,7 @@ static void alps_disconnect(struct psmouse *psmouse) psmouse_reset(psmouse); timer_shutdown_sync(&priv->timer); - disable_delayed_work_sync(&priv->dev3_register_work); + disable_work_sync(&priv->dev3_register_work); if (priv->dev2) input_unregister_device(priv->dev2); if (!IS_ERR_OR_NULL(priv->dev3)) @@ -3147,8 +3147,7 @@ int alps_init(struct psmouse *psmouse) priv->psmouse = psmouse; - INIT_DELAYED_WORK(&priv->dev3_register_work, - alps_register_bare_ps2_mouse); + INIT_WORK(&priv->dev3_register_work, alps_register_bare_ps2_mouse); psmouse->protocol_handler = alps_process_byte; psmouse->poll = alps_poll; diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index 0a1048cf23f6..17bbf6cdba55 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -257,7 +257,7 @@ struct alps_fields { * @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. + * @dev3_register_work: A work instance 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. @@ -289,7 +289,7 @@ struct alps_data { struct input_dev *dev3; char phys2[32]; char phys3[32]; - struct delayed_work dev3_register_work; + struct work_struct dev3_register_work; /* these are autodetected when the device is identified */ const struct alps_nibble_commands *nibble_commands; -- cgit v1.2.3 From beb2b0a26c3a1021421e8db40154c3b6687b6621 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 11 Mar 2026 22:50:57 -0700 Subject: Input: psmouse - remove dedicated kpsmoused workqueue The only user of psmouse_queue_work() and therefore kpsmoused workqueue is psmouse-base itself, when it tries to schedule the resync work. Since resyncing is not going to race with itself we no longer need the dedicated ordered workqueue. Remove it and switch to using regular (non-delayed) work structure scheduled on the default workqueue. Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/psmouse-base.c | 30 ++++++------------------------ drivers/input/mouse/psmouse.h | 4 +--- 2 files changed, 7 insertions(+), 27 deletions(-) diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 3f34825c8691..6ab5f1d96eae 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -113,8 +113,6 @@ ATTRIBUTE_GROUPS(psmouse_dev); */ static DEFINE_MUTEX(psmouse_mutex); -static struct workqueue_struct *kpsmoused_wq; - struct psmouse *psmouse_from_serio(struct serio *serio) { struct ps2dev *ps2dev = serio_get_drvdata(serio); @@ -240,12 +238,6 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) return PSMOUSE_FULL_PACKET; } -void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work, - unsigned long delay) -{ - queue_delayed_work(kpsmoused_wq, work, delay); -} - /* * __psmouse_set_state() sets new psmouse state and resets all flags. */ @@ -379,7 +371,7 @@ static void psmouse_receive_byte(struct ps2dev *ps2dev, u8 data) psmouse->name, psmouse->phys, psmouse->pktcnt); psmouse->badbyte = psmouse->packet[0]; __psmouse_set_state(psmouse, PSMOUSE_RESYNCING); - psmouse_queue_work(psmouse, &psmouse->resync_work, 0); + schedule_work(&psmouse->resync_work); return; } @@ -415,7 +407,7 @@ static void psmouse_receive_byte(struct ps2dev *ps2dev, u8 data) time_after(jiffies, psmouse->last + psmouse->resync_time * HZ)) { psmouse->badbyte = psmouse->packet[0]; __psmouse_set_state(psmouse, PSMOUSE_RESYNCING); - psmouse_queue_work(psmouse, &psmouse->resync_work, 0); + schedule_work(&psmouse->resync_work); return; } @@ -1313,7 +1305,7 @@ int psmouse_deactivate(struct psmouse *psmouse) static void psmouse_resync(struct work_struct *work) { struct psmouse *parent = NULL, *psmouse = - container_of(work, struct psmouse, resync_work.work); + container_of(work, struct psmouse, resync_work); struct serio *serio = psmouse->ps2dev.serio; psmouse_ret_t rc = PSMOUSE_GOOD_DATA; bool failed = false, enabled = false; @@ -1466,7 +1458,7 @@ static void psmouse_disconnect(struct serio *serio) /* make sure we don't have a resync in progress */ mutex_unlock(&psmouse_mutex); - disable_delayed_work_sync(&psmouse->resync_work); + disable_work_sync(&psmouse->resync_work); mutex_lock(&psmouse_mutex); if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { @@ -1580,7 +1572,7 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) ps2_init(&psmouse->ps2dev, serio, psmouse_pre_receive_byte, psmouse_receive_byte); - INIT_DELAYED_WORK(&psmouse->resync_work, psmouse_resync); + INIT_WORK(&psmouse->resync_work, psmouse_resync); psmouse->dev = input_dev; scnprintf(psmouse->phys, sizeof(psmouse->phys), "%s/input0", serio->phys); @@ -2022,21 +2014,12 @@ static int __init psmouse_init(void) if (err) return err; - kpsmoused_wq = alloc_ordered_workqueue("kpsmoused", 0); - if (!kpsmoused_wq) { - pr_err("failed to create kpsmoused workqueue\n"); - err = -ENOMEM; - goto err_smbus_exit; - } - err = serio_register_driver(&psmouse_drv); if (err) - goto err_destroy_wq; + goto err_smbus_exit; return 0; -err_destroy_wq: - destroy_workqueue(kpsmoused_wq); err_smbus_exit: psmouse_smbus_module_exit(); return err; @@ -2045,7 +2028,6 @@ err_smbus_exit: static void __exit psmouse_exit(void) { serio_unregister_driver(&psmouse_drv); - destroy_workqueue(kpsmoused_wq); psmouse_smbus_module_exit(); } diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 783201301e5c..90ed8cd15d85 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -90,7 +90,7 @@ struct psmouse { void *private; struct input_dev *dev; struct ps2dev ps2dev; - struct delayed_work resync_work; + struct work_struct resync_work; const char *vendor; const char *name; const struct psmouse_protocol *protocol; @@ -132,8 +132,6 @@ struct psmouse { struct psmouse *psmouse_from_serio(struct serio *serio); -void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work, - unsigned long delay); int psmouse_reset(struct psmouse *psmouse); void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state); void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution); -- cgit v1.2.3 From b8303880b641fa12db4e752b19f1b5160f0fa965 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 14 Mar 2026 12:54:58 +0100 Subject: Input: atlas - convert ACPI driver to a platform one In all cases in which a struct acpi_driver is used for binding a driver to an ACPI device object, a corresponding platform device is created by the ACPI core and that device is regarded as a proper representation of underlying hardware. Accordingly, a struct platform_driver should be used by driver code to bind to that device. There are multiple reasons why drivers should not bind directly to ACPI device objects [1]. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert the ACPI Atlas button driver to a platform one. While this is not expected to alter functionality, it changes sysfs layout and so it will be visible to user space. Link: https://lore.kernel.org/all/2396510.ElGaqSPkdT@rafael.j.wysocki/ [1] Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/3429591.aeNJFYEL58@rafael.j.wysocki Signed-off-by: Dmitry Torokhov --- drivers/input/misc/atlas_btns.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/input/misc/atlas_btns.c b/drivers/input/misc/atlas_btns.c index 5b9be2957746..47b31725e850 100644 --- a/drivers/input/misc/atlas_btns.c +++ b/drivers/input/misc/atlas_btns.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #define ACPI_ATLAS_NAME "Atlas ACPI" @@ -57,8 +58,9 @@ static acpi_status acpi_atlas_button_handler(u32 function, return status; } -static int atlas_acpi_button_add(struct acpi_device *device) +static int atlas_acpi_button_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); acpi_status status; int i; int err; @@ -106,8 +108,9 @@ static int atlas_acpi_button_add(struct acpi_device *device) return err; } -static void atlas_acpi_button_remove(struct acpi_device *device) +static void atlas_acpi_button_remove(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); acpi_status status; status = acpi_remove_address_space_handler(device->handle, @@ -124,16 +127,15 @@ static const struct acpi_device_id atlas_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, atlas_device_ids); -static struct acpi_driver atlas_acpi_driver = { - .name = ACPI_ATLAS_NAME, - .class = ACPI_ATLAS_CLASS, - .ids = atlas_device_ids, - .ops = { - .add = atlas_acpi_button_add, - .remove = atlas_acpi_button_remove, +static struct platform_driver atlas_acpi_driver = { + .probe = atlas_acpi_button_probe, + .remove = atlas_acpi_button_remove, + .driver = { + .name = ACPI_ATLAS_NAME, + .acpi_match_table = atlas_device_ids, }, }; -module_acpi_driver(atlas_acpi_driver); +module_platform_driver(atlas_acpi_driver); MODULE_AUTHOR("Jaya Kumar"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 35e688ec5010bd246cd3106b0c8a65df130a9a79 Mon Sep 17 00:00:00 2001 From: Eduard Bostina Date: Mon, 16 Mar 2026 20:10:37 +0200 Subject: dt-bindings: input: touchscreen: Convert TS-4800 to DT schema Convert the TS-4800 touchscreen bindings to DT schema. Signed-off-by: Eduard Bostina Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260316181038.9771-1-egbostina@gmail.com Signed-off-by: Dmitry Torokhov --- .../input/touchscreen/technologic,ts4800-ts.yaml | 42 ++++++++++++++++++++++ .../bindings/input/touchscreen/ts4800-ts.txt | 11 ------ 2 files changed, 42 insertions(+), 11 deletions(-) create mode 100644 Documentation/devicetree/bindings/input/touchscreen/technologic,ts4800-ts.yaml delete mode 100644 Documentation/devicetree/bindings/input/touchscreen/ts4800-ts.txt diff --git a/Documentation/devicetree/bindings/input/touchscreen/technologic,ts4800-ts.yaml b/Documentation/devicetree/bindings/input/touchscreen/technologic,ts4800-ts.yaml new file mode 100644 index 000000000000..c033774b4f44 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/technologic,ts4800-ts.yaml @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/touchscreen/technologic,ts4800-ts.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TS-4800 Touchscreen + +maintainers: + - Eduard Bostina + +properties: + compatible: + const: technologic,ts4800-ts + + reg: + maxItems: 1 + + syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: Phandle to the FPGA's syscon + - description: Offset to the touchscreen register + - description: Offset to the touchscreen enable bit + description: Phandle / integers array that points to the syscon node which + describes the FPGA's syscon registers. + +required: + - compatible + - reg + - syscon + +additionalProperties: false + +examples: + - | + touchscreen@1000 { + compatible = "technologic,ts4800-ts"; + reg = <0x1000 0x100>; + syscon = <&fpga_syscon 0x20 3>; + }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/ts4800-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/ts4800-ts.txt deleted file mode 100644 index 4c1c092c276b..000000000000 --- a/Documentation/devicetree/bindings/input/touchscreen/ts4800-ts.txt +++ /dev/null @@ -1,11 +0,0 @@ -* TS-4800 Touchscreen bindings - -Required properties: -- compatible: must be "technologic,ts4800-ts" -- reg: physical base address of the controller and length of memory mapped - region. -- syscon: phandle / integers array that points to the syscon node which - describes the FPGA's syscon registers. - - phandle to FPGA's syscon - - offset to the touchscreen register - - offset to the touchscreen enable bit -- cgit v1.2.3 From 0f9bcf224f983e27f29fb0349c113b4817d5357c Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 12 Mar 2026 23:49:01 +0100 Subject: dt-bindings: touchscreen: trivial-touch: Move allOf: after required: Majority of schemas place allOf: after required: . Documentation Documentation/devicetree/bindings/writing-schema.rst also hints at this ordering. Trivially update this schema. No functional change. Signed-off-by: Marek Vasut Acked-by: Conor Dooley Reviewed-by: Frank Li Link: https://patch.msgid.link/20260312224925.186077-1-marek.vasut+renesas@mailbox.org Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/touchscreen/trivial-touch.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/input/touchscreen/trivial-touch.yaml b/Documentation/devicetree/bindings/input/touchscreen/trivial-touch.yaml index 6441d21223ca..6316a8d32f39 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/trivial-touch.yaml +++ b/Documentation/devicetree/bindings/input/touchscreen/trivial-touch.yaml @@ -53,14 +53,14 @@ properties: wakeup-source: true -allOf: - - $ref: touchscreen.yaml - required: - compatible - reg - interrupts +allOf: + - $ref: touchscreen.yaml + unevaluatedProperties: false examples: -- cgit v1.2.3 From 731c634ea95ebf2eb0162174f14c6f341c44f71e Mon Sep 17 00:00:00 2001 From: Bhushan Shah Date: Sat, 14 Mar 2026 20:27:58 +0530 Subject: dt-bindings: input: touchscreen: edt-ft5x06: Add FocalTech FT3519 Document FocalTech FT3519 support by adding the compatible. It's 10 point touchscreen, which is compatible with FT3518 Signed-off-by: Bhushan Shah Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260314-edt-ft3519-v3-1-5ee91b408ed6@machinesoul.in Signed-off-by: Dmitry Torokhov --- .../bindings/input/touchscreen/edt-ft5x06.yaml | 30 ++++++++++++---------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml index 6f90522de8c0..68b2f1601654 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml +++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml @@ -33,19 +33,23 @@ allOf: properties: compatible: - enum: - - edt,edt-ft5206 - - edt,edt-ft5306 - - edt,edt-ft5406 - - edt,edt-ft5506 - - evervision,ev-ft5726 - - focaltech,ft3518 - - focaltech,ft5426 - - focaltech,ft5452 - - focaltech,ft6236 - - focaltech,ft8201 - - focaltech,ft8716 - - focaltech,ft8719 + oneOf: + - enum: + - edt,edt-ft5206 + - edt,edt-ft5306 + - edt,edt-ft5406 + - edt,edt-ft5506 + - evervision,ev-ft5726 + - focaltech,ft3518 + - focaltech,ft5426 + - focaltech,ft5452 + - focaltech,ft6236 + - focaltech,ft8201 + - focaltech,ft8716 + - focaltech,ft8719 + - items: + - const: focaltech,ft3519 + - const: focaltech,ft3518 reg: maxItems: 1 -- cgit v1.2.3 From 824d679941c9bf098214e8fbacbce9b7213e07ce Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Mon, 23 Mar 2026 10:00:21 -0400 Subject: dt-bindings: input: matrix-keymap: fix key board wording The correct wording is keyboard, without a space. Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260323140024.104475-1-hugo@hugovil.com Signed-off-by: Dmitry Torokhov --- Documentation/devicetree/bindings/input/matrix-keymap.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/input/matrix-keymap.yaml b/Documentation/devicetree/bindings/input/matrix-keymap.yaml index a715c2a773fe..ce910e4ac823 100644 --- a/Documentation/devicetree/bindings/input/matrix-keymap.yaml +++ b/Documentation/devicetree/bindings/input/matrix-keymap.yaml @@ -4,13 +4,13 @@ $id: http://devicetree.org/schemas/input/matrix-keymap.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Common Key Matrices on Matrix-connected Key Boards +title: Common Key Matrices on Matrix-connected Keyboards maintainers: - Olof Johansson description: | - A simple common binding for matrix-connected key boards. Currently targeted at + A simple common binding for matrix-connected keyboards. Currently targeted at defining the keys in the scope of linux key codes since that is a stable and standardized interface at this time. -- cgit v1.2.3 From f3488759a5c141d68a8660d1ca858353e97994a1 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 30 Jun 2024 22:30:26 -0700 Subject: Input: ad7877 - use guard notation when acquiring mutexes/locks This makes the code more compact and error handling more robust. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ad7877.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c index 33c5eb522389..cc39057d0117 100644 --- a/drivers/input/touchscreen/ad7877.c +++ b/drivers/input/touchscreen/ad7877.c @@ -376,17 +376,14 @@ static inline void ad7877_ts_event_release(struct ad7877 *ts) static void ad7877_timer(struct timer_list *t) { struct ad7877 *ts = timer_container_of(ts, t, timer); - unsigned long flags; - spin_lock_irqsave(&ts->lock, flags); + guard(spinlock_irqsave)(&ts->lock); ad7877_ts_event_release(ts); - spin_unlock_irqrestore(&ts->lock, flags); } static irqreturn_t ad7877_irq(int irq, void *handle) { struct ad7877 *ts = handle; - unsigned long flags; int error; error = spi_sync(ts->spi, &ts->msg); @@ -395,11 +392,13 @@ static irqreturn_t ad7877_irq(int irq, void *handle) goto out; } - spin_lock_irqsave(&ts->lock, flags); - error = ad7877_process_data(ts); - if (!error) + scoped_guard(spinlock_irqsave, &ts->lock) { + error = ad7877_process_data(ts); + if (error) + goto out; + mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); - spin_unlock_irqrestore(&ts->lock, flags); + } out: return IRQ_HANDLED; @@ -409,7 +408,7 @@ static void ad7877_disable(void *data) { struct ad7877 *ts = data; - mutex_lock(&ts->mutex); + guard(mutex)(&ts->mutex); if (!ts->disabled) { ts->disabled = true; @@ -423,20 +422,16 @@ static void ad7877_disable(void *data) * We know the chip's in lowpower mode since we always * leave it that way after every request */ - - mutex_unlock(&ts->mutex); } static void ad7877_enable(struct ad7877 *ts) { - mutex_lock(&ts->mutex); + guard(mutex)(&ts->mutex); if (ts->disabled) { ts->disabled = false; enable_irq(ts->spi->irq); } - - mutex_unlock(&ts->mutex); } #define SHOW(name) static ssize_t \ @@ -509,10 +504,9 @@ static ssize_t ad7877_dac_store(struct device *dev, if (error) return error; - mutex_lock(&ts->mutex); + guard(mutex)(&ts->mutex); ts->dac = val & 0xFF; ad7877_write(ts->spi, AD7877_REG_DAC, (ts->dac << 4) | AD7877_DAC_CONF); - mutex_unlock(&ts->mutex); return count; } @@ -539,11 +533,10 @@ static ssize_t ad7877_gpio3_store(struct device *dev, if (error) return error; - mutex_lock(&ts->mutex); + guard(mutex)(&ts->mutex); ts->gpio3 = !!val; ad7877_write(ts->spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_DATA | (ts->gpio4 << 4) | (ts->gpio3 << 5)); - mutex_unlock(&ts->mutex); return count; } @@ -570,11 +563,10 @@ static ssize_t ad7877_gpio4_store(struct device *dev, if (error) return error; - mutex_lock(&ts->mutex); + guard(mutex)(&ts->mutex); ts->gpio4 = !!val; ad7877_write(ts->spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_DATA | (ts->gpio4 << 4) | (ts->gpio3 << 5)); - mutex_unlock(&ts->mutex); return count; } -- cgit v1.2.3 From ab2a8300179b80c7d05b460cbf319cd56c0eaf4d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 30 Jun 2024 22:40:19 -0700 Subject: Input: ad7879 - use guard notation when acquiring mutexes This makes the code more compact and error handling more robust. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ad7879.c | 46 +++++++++++++------------------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index 4c448f39bf57..31d2a3029d5f 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -305,15 +305,13 @@ static int __maybe_unused ad7879_suspend(struct device *dev) { struct ad7879 *ts = dev_get_drvdata(dev); - mutex_lock(&ts->input->mutex); + guard(mutex)(&ts->input->mutex); if (!ts->suspended && !ts->disabled && input_device_enabled(ts->input)) __ad7879_disable(ts); ts->suspended = true; - mutex_unlock(&ts->input->mutex); - return 0; } @@ -321,15 +319,13 @@ static int __maybe_unused ad7879_resume(struct device *dev) { struct ad7879 *ts = dev_get_drvdata(dev); - mutex_lock(&ts->input->mutex); + guard(mutex)(&ts->input->mutex); if (ts->suspended && !ts->disabled && input_device_enabled(ts->input)) __ad7879_enable(ts); ts->suspended = false; - mutex_unlock(&ts->input->mutex); - return 0; } @@ -338,7 +334,7 @@ EXPORT_SYMBOL(ad7879_pm_ops); static void ad7879_toggle(struct ad7879 *ts, bool disable) { - mutex_lock(&ts->input->mutex); + guard(mutex)(&ts->input->mutex); if (!ts->suspended && input_device_enabled(ts->input)) { @@ -352,8 +348,6 @@ static void ad7879_toggle(struct ad7879 *ts, bool disable) } ts->disabled = disable; - - mutex_unlock(&ts->input->mutex); } static ssize_t ad7879_disable_show(struct device *dev, @@ -403,23 +397,20 @@ static int ad7879_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) { struct ad7879 *ts = gpiochip_get_data(chip); - int err; - mutex_lock(&ts->mutex); - ts->cmd_crtl2 |= AD7879_GPIO_EN | AD7879_GPIODIR | AD7879_GPIOPOL; - err = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); - mutex_unlock(&ts->mutex); + guard(mutex)(&ts->mutex); - return err; + ts->cmd_crtl2 |= AD7879_GPIO_EN | AD7879_GPIODIR | AD7879_GPIOPOL; + return ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); } static int ad7879_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, int level) { struct ad7879 *ts = gpiochip_get_data(chip); - int err; - mutex_lock(&ts->mutex); + guard(mutex)(&ts->mutex); + ts->cmd_crtl2 &= ~AD7879_GPIODIR; ts->cmd_crtl2 |= AD7879_GPIO_EN | AD7879_GPIOPOL; if (level) @@ -427,21 +418,17 @@ static int ad7879_gpio_direction_output(struct gpio_chip *chip, else ts->cmd_crtl2 &= ~AD7879_GPIO_DATA; - err = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); - mutex_unlock(&ts->mutex); - - return err; + return ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); } -static int ad7879_gpio_get_value(struct gpio_chip *chip, unsigned gpio) +static int ad7879_gpio_get_value(struct gpio_chip *chip, unsigned int gpio) { struct ad7879 *ts = gpiochip_get_data(chip); u16 val; - mutex_lock(&ts->mutex); - val = ad7879_read(ts, AD7879_REG_CTRL2); - mutex_unlock(&ts->mutex); + guard(mutex)(&ts->mutex); + val = ad7879_read(ts, AD7879_REG_CTRL2); return !!(val & AD7879_GPIO_DATA); } @@ -449,18 +436,15 @@ static int ad7879_gpio_set_value(struct gpio_chip *chip, unsigned int gpio, int value) { struct ad7879 *ts = gpiochip_get_data(chip); - int ret; - mutex_lock(&ts->mutex); + guard(mutex)(&ts->mutex); + if (value) ts->cmd_crtl2 |= AD7879_GPIO_DATA; else ts->cmd_crtl2 &= ~AD7879_GPIO_DATA; - ret = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); - mutex_unlock(&ts->mutex); - - return ret; + return ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); } static int ad7879_gpio_add(struct ad7879 *ts) -- cgit v1.2.3 From d77c45c8f0fd6a8cbbcaceb181633c092856f1c7 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 30 Jun 2024 22:47:44 -0700 Subject: Input: ads7846 - switch to using cleanup functions Start using __free() and guard() primitives to simplify the code and error handling. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ads7846.c | 44 ++++++++++++++----------------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 0963b1a78a0c..4b39f7212d35 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -289,7 +289,7 @@ static void __ads7846_enable(struct ads7846 *ts) static void ads7846_disable(struct ads7846 *ts) { - mutex_lock(&ts->lock); + guard(mutex)(&ts->lock); if (!ts->disabled) { @@ -298,13 +298,11 @@ static void ads7846_disable(struct ads7846 *ts) ts->disabled = true; } - - mutex_unlock(&ts->lock); } static void ads7846_enable(struct ads7846 *ts) { - mutex_lock(&ts->lock); + guard(mutex)(&ts->lock); if (ts->disabled) { @@ -313,8 +311,6 @@ static void ads7846_enable(struct ads7846 *ts) if (!ts->suspended) __ads7846_enable(ts); } - - mutex_unlock(&ts->lock); } /*--------------------------------------------------------------------------*/ @@ -354,10 +350,9 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) { struct spi_device *spi = to_spi_device(dev); struct ads7846 *ts = dev_get_drvdata(dev); - struct ser_req *req; int status; - req = kzalloc_obj(*req); + struct ser_req *req __free(kfree) = kzalloc_obj(*req); if (!req) return -ENOMEM; @@ -418,11 +413,11 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) CS_CHANGE(req->xfer[7]); spi_message_add_tail(&req->xfer[7], &req->msg); - mutex_lock(&ts->lock); - ads7846_stop(ts); - status = spi_sync(spi, &req->msg); - ads7846_restart(ts); - mutex_unlock(&ts->lock); + scoped_guard(mutex, &ts->lock) { + ads7846_stop(ts); + status = spi_sync(spi, &req->msg); + ads7846_restart(ts); + } if (status == 0) { /* on-wire is a must-ignore bit, a BE12 value, then padding */ @@ -431,7 +426,6 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) status &= 0x0fff; } - kfree(req); return status; } @@ -439,10 +433,9 @@ static int ads7845_read12_ser(struct device *dev, unsigned command) { struct spi_device *spi = to_spi_device(dev); struct ads7846 *ts = dev_get_drvdata(dev); - struct ads7845_ser_req *req; int status; - req = kzalloc_obj(*req); + struct ads7845_ser_req *req __free(kfree) = kzalloc_obj(*req); if (!req) return -ENOMEM; @@ -454,11 +447,11 @@ static int ads7845_read12_ser(struct device *dev, unsigned command) req->xfer[0].len = 3; spi_message_add_tail(&req->xfer[0], &req->msg); - mutex_lock(&ts->lock); - ads7846_stop(ts); - status = spi_sync(spi, &req->msg); - ads7846_restart(ts); - mutex_unlock(&ts->lock); + scoped_guard(mutex, &ts->lock) { + ads7846_stop(ts); + status = spi_sync(spi, &req->msg); + ads7846_restart(ts); + } if (status == 0) { /* BE12 value, then padding */ @@ -467,7 +460,6 @@ static int ads7845_read12_ser(struct device *dev, unsigned command) status &= 0x0fff; } - kfree(req); return status; } @@ -966,7 +958,7 @@ static int ads7846_suspend(struct device *dev) { struct ads7846 *ts = dev_get_drvdata(dev); - mutex_lock(&ts->lock); + guard(mutex)(&ts->lock); if (!ts->suspended) { @@ -979,8 +971,6 @@ static int ads7846_suspend(struct device *dev) ts->suspended = true; } - mutex_unlock(&ts->lock); - return 0; } @@ -988,7 +978,7 @@ static int ads7846_resume(struct device *dev) { struct ads7846 *ts = dev_get_drvdata(dev); - mutex_lock(&ts->lock); + guard(mutex)(&ts->lock); if (ts->suspended) { @@ -1001,8 +991,6 @@ static int ads7846_resume(struct device *dev) __ads7846_enable(ts); } - mutex_unlock(&ts->lock); - return 0; } -- cgit v1.2.3 From d911a55b29bc393cccdd9236bbbd7333eaeafe3c Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 29 May 2024 14:37:21 -0700 Subject: Input: atmel_mxt_ts - switch to using cleanup functions Start using __free() and guard() primitives to simplify the code and error handling. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 290 +++++++++++++++---------------- 1 file changed, 136 insertions(+), 154 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index dd0544cc1bc1..87c6a10381f2 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -713,12 +713,11 @@ static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len, const void *val) { bool retried = false; - u8 *buf; - size_t count; + size_t count = len + 2; + int error; int ret; - count = len + 2; - buf = kmalloc(count, GFP_KERNEL); + u8 *buf __free(kfree) = kmalloc(count, GFP_KERNEL); if (!buf) return -ENOMEM; @@ -728,20 +727,17 @@ static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len, retry: ret = i2c_master_send(client, buf, count); - if (ret == count) { - ret = 0; - } else if (!retried && mxt_wakeup_toggle(client, true, true)) { + if (ret == count) + return 0; + + if (!retried && mxt_wakeup_toggle(client, true, true)) { retried = true; goto retry; - } else { - if (ret >= 0) - ret = -EIO; - dev_err(&client->dev, "%s: i2c send failed (%d)\n", - __func__, ret); } - kfree(buf); - return ret; + error = ret < 0 ? ret : -EIO; + dev_err(&client->dev, "%s: i2c send failed (%d)\n", __func__, error); + return error; } static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val) @@ -1547,14 +1543,15 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw) { struct device *dev = &data->client->dev; struct mxt_cfg cfg; - int ret; + int error; int offset; int i; u32 info_crc, config_crc, calculated_crc; u16 crc_start = 0; /* Make zero terminated copy of the OBP_RAW file */ - cfg.raw = kmemdup_nul(fw->data, fw->size, GFP_KERNEL); + u8 *raw_buf __free(kfree) = cfg.raw = kmemdup_nul(fw->data, fw->size, + GFP_KERNEL); if (!cfg.raw) return -ENOMEM; @@ -1564,21 +1561,17 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw) if (strncmp(cfg.raw, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) { dev_err(dev, "Unrecognised config file\n"); - ret = -EINVAL; - goto release_raw; + return -EINVAL; } cfg.raw_pos = strlen(MXT_CFG_MAGIC); /* Load information block and check */ for (i = 0; i < sizeof(struct mxt_info); i++) { - ret = sscanf(cfg.raw + cfg.raw_pos, "%hhx%n", - (unsigned char *)&cfg.info + i, - &offset); - if (ret != 1) { + if (sscanf(cfg.raw + cfg.raw_pos, "%hhx%n", + (unsigned char *)&cfg.info + i, &offset) != 1) { dev_err(dev, "Bad format\n"); - ret = -EINVAL; - goto release_raw; + return -EINVAL; } cfg.raw_pos += offset; @@ -1586,30 +1579,24 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw) if (cfg.info.family_id != data->info->family_id) { dev_err(dev, "Family ID mismatch!\n"); - ret = -EINVAL; - goto release_raw; + return -EINVAL; } if (cfg.info.variant_id != data->info->variant_id) { dev_err(dev, "Variant ID mismatch!\n"); - ret = -EINVAL; - goto release_raw; + return -EINVAL; } /* Read CRCs */ - ret = sscanf(cfg.raw + cfg.raw_pos, "%x%n", &info_crc, &offset); - if (ret != 1) { + if (sscanf(cfg.raw + cfg.raw_pos, "%x%n", &info_crc, &offset) != 1) { dev_err(dev, "Bad format: failed to parse Info CRC\n"); - ret = -EINVAL; - goto release_raw; + return -EINVAL; } cfg.raw_pos += offset; - ret = sscanf(cfg.raw + cfg.raw_pos, "%x%n", &config_crc, &offset); - if (ret != 1) { + if (sscanf(cfg.raw + cfg.raw_pos, "%x%n", &config_crc, &offset) != 1) { dev_err(dev, "Bad format: failed to parse Config CRC\n"); - ret = -EINVAL; - goto release_raw; + return -EINVAL; } cfg.raw_pos += offset; @@ -1625,8 +1612,7 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw) } else if (config_crc == data->config_crc) { dev_dbg(dev, "Config CRC 0x%06X: OK\n", data->config_crc); - ret = 0; - goto release_raw; + return 0; } else { dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n", data->config_crc, config_crc); @@ -1642,15 +1628,14 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw) data->info->object_num * sizeof(struct mxt_object) + MXT_INFO_CHECKSUM_SIZE; cfg.mem_size = data->mem_size - cfg.start_ofs; - cfg.mem = kzalloc(cfg.mem_size, GFP_KERNEL); - if (!cfg.mem) { - ret = -ENOMEM; - goto release_raw; - } - ret = mxt_prepare_cfg_mem(data, &cfg); - if (ret) - goto release_mem; + u8 *mem_buf __free(kfree) = cfg.mem = kzalloc(cfg.mem_size, GFP_KERNEL); + if (!cfg.mem) + return -ENOMEM; + + error = mxt_prepare_cfg_mem(data, &cfg); + if (error) + return error; /* Calculate crc of the received configs (not the raw config file) */ if (data->T71_address) @@ -1670,30 +1655,26 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw) calculated_crc, config_crc); } - ret = mxt_upload_cfg_mem(data, &cfg); - if (ret) - goto release_mem; + error = mxt_upload_cfg_mem(data, &cfg); + if (error) + return error; mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE); - ret = mxt_check_retrigen(data); - if (ret) - goto release_mem; + error = mxt_check_retrigen(data); + if (error) + return error; - ret = mxt_soft_reset(data); - if (ret) - goto release_mem; + error = mxt_soft_reset(data); + if (error) + return error; dev_info(dev, "Config successfully updated\n"); /* T7 config may have changed */ mxt_init_t7_power_cfg(data); -release_mem: - kfree(cfg.mem); -release_raw: - kfree(cfg.raw); - return ret; + return 0; } static void mxt_free_input_device(struct mxt_data *data) @@ -1857,7 +1838,6 @@ static int mxt_read_info_block(struct mxt_data *data) struct i2c_client *client = data->client; int error; size_t size; - void *id_buf, *buf; uint8_t num_objects; u32 calculated_crc; u8 *crc_ptr; @@ -1868,24 +1848,23 @@ static int mxt_read_info_block(struct mxt_data *data) /* Read 7-byte ID information block starting at address 0 */ size = sizeof(struct mxt_info); - id_buf = kzalloc(size, GFP_KERNEL); + void *id_buf __free(kfree) = kzalloc(size, GFP_KERNEL); if (!id_buf) return -ENOMEM; error = __mxt_read_reg(client, 0, size, id_buf); if (error) - goto err_free_mem; + return error; /* Resize buffer to give space for rest of info block */ num_objects = ((struct mxt_info *)id_buf)->object_num; size += (num_objects * sizeof(struct mxt_object)) + MXT_INFO_CHECKSUM_SIZE; - buf = krealloc(id_buf, size, GFP_KERNEL); - if (!buf) { - error = -ENOMEM; - goto err_free_mem; - } + void *buf = krealloc(id_buf, size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + id_buf = buf; /* Read rest of info block */ @@ -1893,7 +1872,7 @@ static int mxt_read_info_block(struct mxt_data *data) size - MXT_OBJECT_START, id_buf + MXT_OBJECT_START); if (error) - goto err_free_mem; + return error; /* Extract & calculate checksum */ crc_ptr = id_buf + size - MXT_INFO_CHECKSUM_SIZE; @@ -1910,12 +1889,11 @@ static int mxt_read_info_block(struct mxt_data *data) dev_err(&client->dev, "Info Block CRC error calculated=0x%06X read=0x%06X\n", calculated_crc, data->info_crc); - error = -EIO; - goto err_free_mem; + return -EIO; } - data->raw_info_block = id_buf; - data->info = (struct mxt_info *)id_buf; + data->raw_info_block = no_free_ptr(id_buf); + data->info = (struct mxt_info *)data->raw_info_block; dev_info(&client->dev, "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n", @@ -1924,20 +1902,18 @@ static int mxt_read_info_block(struct mxt_data *data) data->info->build, data->info->object_num); /* Parse object table information */ - error = mxt_parse_object_table(data, id_buf + MXT_OBJECT_START); + error = mxt_parse_object_table(data, + data->raw_info_block + MXT_OBJECT_START); if (error) { dev_err(&client->dev, "Error %d parsing object table\n", error); mxt_free_object_table(data); return error; } - data->object_table = (struct mxt_object *)(id_buf + MXT_OBJECT_START); + data->object_table = + (struct mxt_object *)(data->raw_info_block + MXT_OBJECT_START); return 0; - -err_free_mem: - kfree(id_buf); - return error; } static int mxt_read_t9_resolution(struct mxt_data *data) @@ -2914,70 +2890,38 @@ static int mxt_check_firmware_format(struct device *dev, return -EINVAL; } -static int mxt_load_fw(struct device *dev, const char *fn) +static int mxt_flash_fw(struct mxt_data *data, const struct firmware *fw) { - struct mxt_data *data = dev_get_drvdata(dev); - const struct firmware *fw = NULL; + struct device *dev = &data->client->dev; unsigned int frame_size; unsigned int pos = 0; unsigned int retry = 0; unsigned int frame = 0; - int ret; - - ret = request_firmware(&fw, fn, dev); - if (ret) { - dev_err(dev, "Unable to open firmware %s\n", fn); - return ret; - } - - /* Check for incorrect enc file */ - ret = mxt_check_firmware_format(dev, fw); - if (ret) - goto release_firmware; - - if (!data->in_bootloader) { - /* Change to the bootloader mode */ - data->in_bootloader = true; - - ret = mxt_t6_command(data, MXT_COMMAND_RESET, - MXT_BOOT_VALUE, false); - if (ret) - goto release_firmware; - - msleep(MXT_RESET_TIME); - - /* Do not need to scan since we know family ID */ - ret = mxt_lookup_bootloader_address(data, 0); - if (ret) - goto release_firmware; - - mxt_free_input_device(data); - mxt_free_object_table(data); - } else { - enable_irq(data->irq); - } + int error; reinit_completion(&data->bl_completion); - ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false); - if (ret) { + error = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false); + if (error) { /* Bootloader may still be unlocked from previous attempt */ - ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, false); - if (ret) - goto disable_irq; + error = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, + false); + if (error) + return error; } else { dev_info(dev, "Unlocking bootloader\n"); /* Unlock bootloader */ - ret = mxt_send_bootloader_cmd(data, true); - if (ret) - goto disable_irq; + error = mxt_send_bootloader_cmd(data, true); + if (error) + return error; } while (pos < fw->size) { - ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, true); - if (ret) - goto disable_irq; + error = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, + true); + if (error) + return error; frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); @@ -2985,12 +2929,12 @@ static int mxt_load_fw(struct device *dev, const char *fn) frame_size += 2; /* Write one frame to device */ - ret = mxt_bootloader_write(data, fw->data + pos, frame_size); - if (ret) - goto disable_irq; + error = mxt_bootloader_write(data, fw->data + pos, frame_size); + if (error) + return error; - ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS, true); - if (ret) { + error = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS, true); + if (error) { retry++; /* Back off by 20ms per retry */ @@ -2998,7 +2942,7 @@ static int mxt_load_fw(struct device *dev, const char *fn) if (retry > 20) { dev_err(dev, "Retry count exceeded\n"); - goto disable_irq; + return error; } } else { retry = 0; @@ -3012,10 +2956,10 @@ static int mxt_load_fw(struct device *dev, const char *fn) } /* Wait for flash. */ - ret = mxt_wait_for_completion(data, &data->bl_completion, - MXT_FW_RESET_TIME); - if (ret) - goto disable_irq; + error = mxt_wait_for_completion(data, &data->bl_completion, + MXT_FW_RESET_TIME); + if (error) + return error; dev_dbg(dev, "Sent %d frames, %d bytes\n", frame, pos); @@ -3025,14 +2969,56 @@ static int mxt_load_fw(struct device *dev, const char *fn) * errors. */ mxt_wait_for_completion(data, &data->bl_completion, MXT_FW_RESET_TIME); - data->in_bootloader = false; -disable_irq: + return 0; +} + +static int mxt_load_fw(struct device *dev, const char *fn) +{ + struct mxt_data *data = dev_get_drvdata(dev); + int retval; + int error; + + const struct firmware *fw __free(firmware) = NULL; + error = request_firmware(&fw, fn, dev); + if (error) { + dev_err(dev, "Unable to open firmware %s\n", fn); + return error; + } + + /* Check for incorrect enc file */ + error = mxt_check_firmware_format(dev, fw); + if (error) + return error; + + if (!data->in_bootloader) { + /* Change to the bootloader mode */ + data->in_bootloader = true; + + error = mxt_t6_command(data, MXT_COMMAND_RESET, + MXT_BOOT_VALUE, false); + if (error) + return error; + + msleep(MXT_RESET_TIME); + + /* Do not need to scan since we know family ID */ + error = mxt_lookup_bootloader_address(data, 0); + if (error) + return error; + + mxt_free_input_device(data); + mxt_free_object_table(data); + } else { + enable_irq(data->irq); + } + + retval = mxt_flash_fw(data, fw); + disable_irq(data->irq); -release_firmware: - release_firmware(fw); - return ret; + + return retval; } static ssize_t mxt_update_fw_store(struct device *dev, @@ -3375,12 +3361,10 @@ static int mxt_suspend(struct device *dev) if (!input_dev) return 0; - mutex_lock(&input_dev->mutex); - - if (input_device_enabled(input_dev)) - mxt_stop(data); - - mutex_unlock(&input_dev->mutex); + scoped_guard(mutex, &input_dev->mutex) { + if (input_device_enabled(input_dev)) + mxt_stop(data); + } disable_irq(data->irq); @@ -3398,12 +3382,10 @@ static int mxt_resume(struct device *dev) enable_irq(data->irq); - mutex_lock(&input_dev->mutex); - - if (input_device_enabled(input_dev)) - mxt_start(data); - - mutex_unlock(&input_dev->mutex); + scoped_guard(mutex, &input_dev->mutex) { + if (input_device_enabled(input_dev)) + mxt_start(data); + } return 0; } -- cgit v1.2.3 From 24b3bc4a8f1bf90d742de14b664845deba2e52ab Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 30 Jun 2024 22:51:52 -0700 Subject: Input: auo-pixcir-ts - use guard notation when acquiring mutexes This makes the code more compact and error handling more robust. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/auo-pixcir-ts.c | 43 ++++++++++++++++--------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/drivers/input/touchscreen/auo-pixcir-ts.c b/drivers/input/touchscreen/auo-pixcir-ts.c index 363a4a1f1560..401b2264f585 100644 --- a/drivers/input/touchscreen/auo-pixcir-ts.c +++ b/drivers/input/touchscreen/auo-pixcir-ts.c @@ -415,9 +415,9 @@ static int auo_pixcir_suspend(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct auo_pixcir_ts *ts = i2c_get_clientdata(client); struct input_dev *input = ts->input; - int ret = 0; + int error; - mutex_lock(&input->mutex); + guard(mutex)(&input->mutex); /* when configured as wakeup source, device should always wake system * therefore start device if necessary @@ -425,21 +425,23 @@ static int auo_pixcir_suspend(struct device *dev) if (device_may_wakeup(&client->dev)) { /* need to start device if not open, to be wakeup source */ if (!input_device_enabled(input)) { - ret = auo_pixcir_start(ts); - if (ret) - goto unlock; + error = auo_pixcir_start(ts); + if (error) + return error; } enable_irq_wake(client->irq); - ret = auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_SLEEP); + error = auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_SLEEP); + if (error) + return error; + } else if (input_device_enabled(input)) { - ret = auo_pixcir_stop(ts); + error = auo_pixcir_stop(ts); + if (error) + return error; } -unlock: - mutex_unlock(&input->mutex); - - return ret; + return 0; } static int auo_pixcir_resume(struct device *dev) @@ -447,29 +449,28 @@ static int auo_pixcir_resume(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct auo_pixcir_ts *ts = i2c_get_clientdata(client); struct input_dev *input = ts->input; - int ret = 0; + int error; - mutex_lock(&input->mutex); + guard(mutex)(&input->mutex); if (device_may_wakeup(&client->dev)) { disable_irq_wake(client->irq); /* need to stop device if it was not open on suspend */ if (!input_device_enabled(input)) { - ret = auo_pixcir_stop(ts); - if (ret) - goto unlock; + error = auo_pixcir_stop(ts); + if (error) + return error; } /* device wakes automatically from SLEEP */ } else if (input_device_enabled(input)) { - ret = auo_pixcir_start(ts); + error = auo_pixcir_start(ts); + if (error) + return error; } -unlock: - mutex_unlock(&input->mutex); - - return ret; + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(auo_pixcir_pm_ops, -- cgit v1.2.3 From b29be7bae37086fa04ffc52d6f1d761e5be811a3 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 17 Aug 2024 17:08:28 -0700 Subject: Input: bu21029_ts - use guard notation when acquiring mutex Guard notation simplifies code. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/bu21029_ts.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/bu21029_ts.c b/drivers/input/touchscreen/bu21029_ts.c index 64f474e67312..68d9cd55ceeb 100644 --- a/drivers/input/touchscreen/bu21029_ts.c +++ b/drivers/input/touchscreen/bu21029_ts.c @@ -416,10 +416,10 @@ static int bu21029_suspend(struct device *dev) struct bu21029_ts_data *bu21029 = i2c_get_clientdata(i2c); if (!device_may_wakeup(dev)) { - mutex_lock(&bu21029->in_dev->mutex); + guard(mutex)(&bu21029->in_dev->mutex); + if (input_device_enabled(bu21029->in_dev)) bu21029_stop_chip(bu21029->in_dev); - mutex_unlock(&bu21029->in_dev->mutex); } return 0; @@ -431,10 +431,10 @@ static int bu21029_resume(struct device *dev) struct bu21029_ts_data *bu21029 = i2c_get_clientdata(i2c); if (!device_may_wakeup(dev)) { - mutex_lock(&bu21029->in_dev->mutex); + guard(mutex)(&bu21029->in_dev->mutex); + if (input_device_enabled(bu21029->in_dev)) bu21029_start_chip(bu21029->in_dev); - mutex_unlock(&bu21029->in_dev->mutex); } return 0; -- cgit v1.2.3 From 37115e7df5d0e75c661aa65f7ac9fa0991759c6d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 17 Aug 2024 17:10:01 -0700 Subject: Input: chipone_icn8318 - use guard notation when acquiring mutex Guard notation simplifies code. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/chipone_icn8318.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/chipone_icn8318.c b/drivers/input/touchscreen/chipone_icn8318.c index d6876d10b252..1b10a757313c 100644 --- a/drivers/input/touchscreen/chipone_icn8318.c +++ b/drivers/input/touchscreen/chipone_icn8318.c @@ -152,10 +152,10 @@ static int icn8318_suspend(struct device *dev) { struct icn8318_data *data = i2c_get_clientdata(to_i2c_client(dev)); - mutex_lock(&data->input->mutex); + guard(mutex)(&data->input->mutex); + if (input_device_enabled(data->input)) icn8318_stop(data->input); - mutex_unlock(&data->input->mutex); return 0; } @@ -164,10 +164,10 @@ static int icn8318_resume(struct device *dev) { struct icn8318_data *data = i2c_get_clientdata(to_i2c_client(dev)); - mutex_lock(&data->input->mutex); + guard(mutex)(&data->input->mutex); + if (input_device_enabled(data->input)) icn8318_start(data->input); - mutex_unlock(&data->input->mutex); return 0; } -- cgit v1.2.3 From a0a92414af42b79f4e2829adfd55478a8e74eb33 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 17 Aug 2024 17:14:57 -0700 Subject: Input: cyttsp - use guard notation when acquiring mutex Guard notation simplifies code. Also fix the touchscreen not being marked as suspended when noone has opened/is using it. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/cyttsp_core.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c index 9e729910fbc8..012dfcae01cc 100644 --- a/drivers/input/touchscreen/cyttsp_core.c +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -494,34 +494,30 @@ static int cyttsp_disable(struct cyttsp *ts) static int cyttsp_suspend(struct device *dev) { struct cyttsp *ts = dev_get_drvdata(dev); - int retval = 0; + int error; - mutex_lock(&ts->input->mutex); + guard(mutex)(&ts->input->mutex); if (input_device_enabled(ts->input)) { - retval = cyttsp_disable(ts); - if (retval == 0) - ts->suspended = true; + error = cyttsp_disable(ts); + if (error) + return error; } - mutex_unlock(&ts->input->mutex); - - return retval; + ts->suspended = true; + return 0; } static int cyttsp_resume(struct device *dev) { struct cyttsp *ts = dev_get_drvdata(dev); - mutex_lock(&ts->input->mutex); + guard(mutex)(&ts->input->mutex); if (input_device_enabled(ts->input)) cyttsp_enable(ts); ts->suspended = false; - - mutex_unlock(&ts->input->mutex); - return 0; } -- cgit v1.2.3 From df2e75e070a82fc02eac99743d0823972da293b1 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 17 Aug 2024 17:05:00 -0700 Subject: Input: edt-ft5x06 - use guard notation when acquiring mutex Guard notation simplifies code. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/edt-ft5x06.c | 87 ++++++++++++---------------------- 1 file changed, 31 insertions(+), 56 deletions(-) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index d0ab644be006..ba8ff65f7ea6 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -380,16 +380,13 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev, container_of(dattr, struct edt_ft5x06_attribute, dattr); u8 *field = (u8 *)tsdata + attr->field_offset; unsigned int val; - size_t count = 0; - int error = 0; + int error; u8 addr; - mutex_lock(&tsdata->mutex); + guard(mutex)(&tsdata->mutex); - if (tsdata->factory_mode) { - error = -EIO; - goto out; - } + if (tsdata->factory_mode) + return -EIO; switch (tsdata->version) { case EDT_M06: @@ -407,8 +404,7 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev, break; default: - error = -ENODEV; - goto out; + return -ENODEV; } if (addr != NO_REGISTER) { @@ -417,7 +413,7 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev, dev_err(&tsdata->client->dev, "Failed to fetch attribute %s, error %d\n", dattr->attr.name, error); - goto out; + return error; } } else { val = *field; @@ -430,10 +426,7 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev, *field = val; } - count = sysfs_emit(buf, "%d\n", val); -out: - mutex_unlock(&tsdata->mutex); - return error ?: count; + return sysfs_emit(buf, "%d\n", val); } static ssize_t edt_ft5x06_setting_store(struct device *dev, @@ -449,21 +442,17 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev, int error; u8 addr; - mutex_lock(&tsdata->mutex); + guard(mutex)(&tsdata->mutex); - if (tsdata->factory_mode) { - error = -EIO; - goto out; - } + if (tsdata->factory_mode) + return -EIO; error = kstrtouint(buf, 0, &val); if (error) - goto out; + return error; - if (val < attr->limit_low || val > attr->limit_high) { - error = -ERANGE; - goto out; - } + if (val < attr->limit_low || val > attr->limit_high) + return -ERANGE; switch (tsdata->version) { case EDT_M06: @@ -481,8 +470,7 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev, break; default: - error = -ENODEV; - goto out; + return -ENODEV; } if (addr != NO_REGISTER) { @@ -491,14 +479,12 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev, dev_err(&tsdata->client->dev, "Failed to update attribute %s, error: %d\n", dattr->attr.name, error); - goto out; + return error; } } *field = val; -out: - mutex_unlock(&tsdata->mutex); - return error ?: count; + return count; } /* m06, m09: range 0-31, m12: range 0-5 */ @@ -714,21 +700,17 @@ static int edt_ft5x06_debugfs_mode_get(void *data, u64 *mode) static int edt_ft5x06_debugfs_mode_set(void *data, u64 mode) { struct edt_ft5x06_ts_data *tsdata = data; - int retval = 0; if (mode > 1) return -ERANGE; - mutex_lock(&tsdata->mutex); + guard(mutex)(&tsdata->mutex); - if (mode != tsdata->factory_mode) { - retval = mode ? edt_ft5x06_factory_mode(tsdata) : - edt_ft5x06_work_mode(tsdata); - } - - mutex_unlock(&tsdata->mutex); + if (mode == tsdata->factory_mode) + return 0; - return retval; + return mode ? edt_ft5x06_factory_mode(tsdata) : + edt_ft5x06_work_mode(tsdata); }; DEFINE_SIMPLE_ATTRIBUTE(debugfs_mode_fops, edt_ft5x06_debugfs_mode_get, @@ -750,18 +732,16 @@ static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file, if (*off < 0 || *off >= tsdata->raw_bufsize) return 0; - mutex_lock(&tsdata->mutex); + guard(mutex)(&tsdata->mutex); - if (!tsdata->factory_mode || !tsdata->raw_buffer) { - error = -EIO; - goto out; - } + if (!tsdata->factory_mode || !tsdata->raw_buffer) + return -EIO; error = regmap_write(tsdata->regmap, 0x08, 0x01); if (error) { dev_err(&client->dev, "failed to write 0x08 register, error %d\n", error); - goto out; + return error; } do { @@ -771,7 +751,7 @@ static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file, dev_err(&client->dev, "failed to read 0x08 register, error %d\n", error); - goto out; + return error; } if (val == 1) @@ -781,8 +761,7 @@ static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file, if (retries == 0) { dev_err(&client->dev, "timed out waiting for register to settle\n"); - error = -ETIMEDOUT; - goto out; + return -ETIMEDOUT; } rdbuf = tsdata->raw_buffer; @@ -792,21 +771,17 @@ static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file, rdbuf[0] = i; /* column index */ error = regmap_bulk_read(tsdata->regmap, 0xf5, rdbuf, colbytes); if (error) - goto out; + return error; rdbuf += colbytes; } read = min_t(size_t, count, tsdata->raw_bufsize - *off); - if (copy_to_user(buf, tsdata->raw_buffer + *off, read)) { - error = -EFAULT; - goto out; - } + if (copy_to_user(buf, tsdata->raw_buffer + *off, read)) + return -EFAULT; *off += read; -out: - mutex_unlock(&tsdata->mutex); - return error ?: read; + return read; }; static const struct file_operations debugfs_raw_data_fops = { -- cgit v1.2.3 From 6e9b9192d69d5d206afc502a06569a1650e41ef0 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 30 Jun 2024 19:26:14 -0700 Subject: Input: eeti_ts - use guard notation when acquiring mutexes This makes the code more compact and error handling more robust. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/eeti_ts.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c index 87eb18977b71..492e19a28a74 100644 --- a/drivers/input/touchscreen/eeti_ts.c +++ b/drivers/input/touchscreen/eeti_ts.c @@ -89,7 +89,7 @@ static irqreturn_t eeti_ts_isr(int irq, void *dev_id) struct eeti_ts *eeti = dev_id; int error; - mutex_lock(&eeti->mutex); + guard(mutex)(&eeti->mutex); do { /* @@ -109,13 +109,12 @@ static irqreturn_t eeti_ts_isr(int irq, void *dev_id) } while (eeti->running && eeti->attn_gpio); - mutex_unlock(&eeti->mutex); return IRQ_HANDLED; } static void eeti_ts_start(struct eeti_ts *eeti) { - mutex_lock(&eeti->mutex); + guard(mutex)(&eeti->mutex); eeti->running = true; enable_irq(eeti->client->irq); @@ -127,8 +126,6 @@ static void eeti_ts_start(struct eeti_ts *eeti) */ if (eeti->attn_gpio && gpiod_get_value_cansleep(eeti->attn_gpio)) eeti_ts_read(eeti); - - mutex_unlock(&eeti->mutex); } static void eeti_ts_stop(struct eeti_ts *eeti) @@ -238,12 +235,10 @@ static int eeti_ts_suspend(struct device *dev) struct eeti_ts *eeti = i2c_get_clientdata(client); struct input_dev *input_dev = eeti->input; - mutex_lock(&input_dev->mutex); - - if (input_device_enabled(input_dev)) - eeti_ts_stop(eeti); - - mutex_unlock(&input_dev->mutex); + scoped_guard(mutex, &input_dev->mutex) { + if (input_device_enabled(input_dev)) + eeti_ts_stop(eeti); + } if (device_may_wakeup(&client->dev)) enable_irq_wake(client->irq); @@ -260,12 +255,10 @@ static int eeti_ts_resume(struct device *dev) if (device_may_wakeup(&client->dev)) disable_irq_wake(client->irq); - mutex_lock(&input_dev->mutex); - - if (input_device_enabled(input_dev)) - eeti_ts_start(eeti); - - mutex_unlock(&input_dev->mutex); + scoped_guard(mutex, &input_dev->mutex) { + if (input_device_enabled(input_dev)) + eeti_ts_start(eeti); + } return 0; } -- cgit v1.2.3 From 8c187a4c1592c483e95fc14fb800272cb41395a4 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 17 Aug 2024 17:17:10 -0700 Subject: Input: ektf2127 - use guard notation when acquiring mutex Guard notation simplifies code. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ektf2127.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/ektf2127.c b/drivers/input/touchscreen/ektf2127.c index 46a0611fac82..572a37d4e0aa 100644 --- a/drivers/input/touchscreen/ektf2127.c +++ b/drivers/input/touchscreen/ektf2127.c @@ -187,10 +187,10 @@ static int ektf2127_suspend(struct device *dev) { struct ektf2127_ts *ts = i2c_get_clientdata(to_i2c_client(dev)); - mutex_lock(&ts->input->mutex); + guard(mutex)(&ts->input->mutex); + if (input_device_enabled(ts->input)) ektf2127_stop(ts->input); - mutex_unlock(&ts->input->mutex); return 0; } @@ -199,10 +199,10 @@ static int ektf2127_resume(struct device *dev) { struct ektf2127_ts *ts = i2c_get_clientdata(to_i2c_client(dev)); - mutex_lock(&ts->input->mutex); + guard(mutex)(&ts->input->mutex); + if (input_device_enabled(ts->input)) ektf2127_start(ts->input); - mutex_unlock(&ts->input->mutex); return 0; } -- cgit v1.2.3 From e5c79d9f65a1211c56a41ca27136c985842fb1b5 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 6 Jun 2024 23:32:05 -0700 Subject: Input: elants_i2c - switch to using cleanup facilities Start using __free() and guard() primitives to simplify the code and error handling. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/elants_i2c.c | 91 +++++++++++++++------------------- 1 file changed, 39 insertions(+), 52 deletions(-) diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index 3fd170f75b4a..835d91dff0a4 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c @@ -303,15 +303,13 @@ static int elants_i2c_calibrate(struct elants_data *ts) static const u8 rek[] = { CMD_HEADER_WRITE, 0x29, 0x00, 0x01 }; static const u8 rek_resp[] = { CMD_HEADER_REK, 0x66, 0x66, 0x66 }; - disable_irq(client->irq); - - ts->state = ELAN_WAIT_RECALIBRATION; - reinit_completion(&ts->cmd_done); - - elants_i2c_send(client, w_flashkey, sizeof(w_flashkey)); - elants_i2c_send(client, rek, sizeof(rek)); + scoped_guard(disable_irq, &client->irq) { + ts->state = ELAN_WAIT_RECALIBRATION; + reinit_completion(&ts->cmd_done); - enable_irq(client->irq); + elants_i2c_send(client, w_flashkey, sizeof(w_flashkey)); + elants_i2c_send(client, rek, sizeof(rek)); + } ret = wait_for_completion_interruptible_timeout(&ts->cmd_done, msecs_to_jiffies(ELAN_CALI_TIMEOUT_MSEC)); @@ -906,17 +904,17 @@ static int elants_i2c_do_update_firmware(struct i2c_client *client, static int elants_i2c_fw_update(struct elants_data *ts) { struct i2c_client *client = ts->client; - const struct firmware *fw; - char *fw_name; int error; - fw_name = kasprintf(GFP_KERNEL, "elants_i2c_%04x.bin", ts->hw_version); + const char *fw_name __free(kfree) = + kasprintf(GFP_KERNEL, "elants_i2c_%04x.bin", ts->hw_version); if (!fw_name) return -ENOMEM; dev_info(&client->dev, "requesting fw name = %s\n", fw_name); + + const struct firmware *fw __free(firmware) = NULL; error = request_firmware(&fw, fw_name, &client->dev); - kfree(fw_name); if (error) { dev_err(&client->dev, "failed to request firmware: %d\n", error); @@ -926,40 +924,32 @@ static int elants_i2c_fw_update(struct elants_data *ts) if (fw->size % ELAN_FW_PAGESIZE) { dev_err(&client->dev, "invalid firmware length: %zu\n", fw->size); - error = -EINVAL; - goto out; + return -EINVAL; } - disable_irq(client->irq); + scoped_guard(disable_irq, &client->irq) { + bool force_update = ts->iap_mode == ELAN_IAP_RECOVERY; - error = elants_i2c_do_update_firmware(client, fw, - ts->iap_mode == ELAN_IAP_RECOVERY); - if (error) { - dev_err(&client->dev, "firmware update failed: %d\n", error); - ts->iap_mode = ELAN_IAP_RECOVERY; - goto out_enable_irq; - } + error = elants_i2c_do_update_firmware(client, fw, force_update); + if (error) { + dev_err(&client->dev, "firmware update failed: %d\n", + error); + } else { + error = elants_i2c_initialize(ts); + if (error) + dev_err(&client->dev, + "failed to initialize device after firmware update: %d\n", + error); + } - error = elants_i2c_initialize(ts); - if (error) { - dev_err(&client->dev, - "failed to initialize device after firmware update: %d\n", - error); - ts->iap_mode = ELAN_IAP_RECOVERY; - goto out_enable_irq; + ts->iap_mode = error ? ELAN_IAP_RECOVERY : ELAN_IAP_OPERATIONAL; + ts->state = ELAN_STATE_NORMAL; } - - ts->iap_mode = ELAN_IAP_OPERATIONAL; - -out_enable_irq: - ts->state = ELAN_STATE_NORMAL; - enable_irq(client->irq); msleep(100); if (!error) elants_i2c_calibrate(ts); -out: - release_firmware(fw); + return error; } @@ -1186,14 +1176,13 @@ static ssize_t calibrate_store(struct device *dev, struct elants_data *ts = i2c_get_clientdata(client); int error; - error = mutex_lock_interruptible(&ts->sysfs_mutex); - if (error) - return error; - - error = elants_i2c_calibrate(ts); + scoped_cond_guard(mutex_intr, return -EINTR, &ts->sysfs_mutex) { + error = elants_i2c_calibrate(ts); + if (error) + return error; + } - mutex_unlock(&ts->sysfs_mutex); - return error ?: count; + return count; } static ssize_t write_update_fw(struct device *dev, @@ -1204,15 +1193,13 @@ static ssize_t write_update_fw(struct device *dev, struct elants_data *ts = i2c_get_clientdata(client); int error; - error = mutex_lock_interruptible(&ts->sysfs_mutex); - if (error) - return error; - - error = elants_i2c_fw_update(ts); - dev_dbg(dev, "firmware update result: %d\n", error); + scoped_cond_guard(mutex_intr, return -EINTR, &ts->sysfs_mutex) { + error = elants_i2c_fw_update(ts); + if (error) + return error; + } - mutex_unlock(&ts->sysfs_mutex); - return error ?: count; + return count; } static ssize_t show_iap_mode(struct device *dev, -- cgit v1.2.3 From cec3bcec6fd54cd1bdcb8786ca661912d879d399 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 17 Aug 2024 17:20:30 -0700 Subject: Input: elo - use guard notation when acquiring mutex Guard notation simplifies code. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/elo.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c index 434b9b47e964..6814d5789b6f 100644 --- a/drivers/input/touchscreen/elo.c +++ b/drivers/input/touchscreen/elo.c @@ -219,40 +219,40 @@ static irqreturn_t elo_interrupt(struct serio *serio, static int elo_command_10(struct elo *elo, unsigned char *packet) { - int rc = -1; + int error; int i; unsigned char csum = 0xaa + ELO10_LEAD_BYTE; - mutex_lock(&elo->cmd_mutex); + guard(mutex)(&elo->cmd_mutex); scoped_guard(serio_pause_rx, elo->serio) { elo->expected_packet = toupper(packet[0]); init_completion(&elo->cmd_done); } - if (serio_write(elo->serio, ELO10_LEAD_BYTE)) - goto out; + error = serio_write(elo->serio, ELO10_LEAD_BYTE); + if (error) + return error; for (i = 0; i < ELO10_PACKET_LEN; i++) { csum += packet[i]; - if (serio_write(elo->serio, packet[i])) - goto out; + error = serio_write(elo->serio, packet[i]); + if (error) + return error; } - if (serio_write(elo->serio, csum)) - goto out; + error = serio_write(elo->serio, csum); + if (error) + return error; wait_for_completion_timeout(&elo->cmd_done, HZ); - if (elo->expected_packet == ELO10_TOUCH_PACKET) { - /* We are back in reporting mode, the command was ACKed */ - memcpy(packet, elo->response, ELO10_PACKET_LEN); - rc = 0; - } + if (elo->expected_packet != ELO10_TOUCH_PACKET) + return -EIO; - out: - mutex_unlock(&elo->cmd_mutex); - return rc; + /* We are back in reporting mode, the command was ACKed */ + memcpy(packet, elo->response, ELO10_PACKET_LEN); + return 0; } static int elo_setup_10(struct elo *elo) -- cgit v1.2.3 From 576c99f1a34da9618a5df8ed8648f2beee7e5411 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 17 Aug 2024 17:26:09 -0700 Subject: Input: exc3000 - use guard notation when acquiring mutex Guard notation simplifies code. Note that callers of exc3000_vendor_data_request() always expect response, so it was adjusted to always wait for it. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/exc3000.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/drivers/input/touchscreen/exc3000.c b/drivers/input/touchscreen/exc3000.c index 28da7ba55a4b..78c0911ba6e2 100644 --- a/drivers/input/touchscreen/exc3000.c +++ b/drivers/input/touchscreen/exc3000.c @@ -234,7 +234,7 @@ static int exc3000_vendor_data_request(struct exc3000_data *data, u8 *request, int ret; unsigned long time_left; - mutex_lock(&data->query_lock); + guard(mutex)(&data->query_lock); reinit_completion(&data->wait_event); @@ -243,29 +243,18 @@ static int exc3000_vendor_data_request(struct exc3000_data *data, u8 *request, ret = i2c_master_send(data->client, buf, EXC3000_LEN_VENDOR_REQUEST); if (ret < 0) - goto out_unlock; - - if (response) { - time_left = wait_for_completion_timeout(&data->wait_event, - timeout * HZ); - if (time_left == 0) { - ret = -ETIMEDOUT; - goto out_unlock; - } - - if (data->buf[3] >= EXC3000_LEN_FRAME) { - ret = -ENOSPC; - goto out_unlock; - } + return ret; - memcpy(response, &data->buf[4], data->buf[3]); - ret = data->buf[3]; - } + time_left = wait_for_completion_timeout(&data->wait_event, + timeout * HZ); + if (time_left == 0) + return -ETIMEDOUT; -out_unlock: - mutex_unlock(&data->query_lock); + if (data->buf[3] >= EXC3000_LEN_FRAME) + return -ENOSPC; - return ret; + memcpy(response, &data->buf[4], data->buf[3]); + return data->buf[3]; } static ssize_t fw_version_show(struct device *dev, -- cgit v1.2.3 From 777f5b42f895a1c3693ec4968023d02722acdd7c Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 29 May 2024 14:43:30 -0700 Subject: Input: goodix - switch to using cleanup functions in firmware code Start using __free(firmware) to simplify the code and error handling. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/goodix_fwupload.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/input/touchscreen/goodix_fwupload.c b/drivers/input/touchscreen/goodix_fwupload.c index 191d4f38d991..5ae9a109ba0d 100644 --- a/drivers/input/touchscreen/goodix_fwupload.c +++ b/drivers/input/touchscreen/goodix_fwupload.c @@ -188,13 +188,13 @@ static int goodix_start_firmware(struct i2c_client *client) static int goodix_firmware_upload(struct goodix_ts_data *ts) { - const struct firmware *fw; char fw_name[64]; const u8 *data; int error; snprintf(fw_name, sizeof(fw_name), "goodix/%s", ts->firmware_name); + const struct firmware *fw __free(firmware) = NULL; error = request_firmware(&fw, fw_name, &ts->client->dev); if (error) { dev_err(&ts->client->dev, "Firmware request error %d\n", error); @@ -203,60 +203,61 @@ static int goodix_firmware_upload(struct goodix_ts_data *ts) error = goodix_firmware_verify(&ts->client->dev, fw); if (error) - goto release; + return error; error = goodix_reset_no_int_sync(ts); if (error) - goto release; + return error; error = goodix_enter_upload_mode(ts->client); if (error) - goto release; + return error; /* Select SRAM bank 0 and upload section 1 & 2 */ error = goodix_i2c_write_u8(ts->client, GOODIX_REG_MISCTL_SRAM_BANK, 0x00); if (error) - goto release; + return error; data = fw->data + GOODIX_FW_HEADER_LENGTH; error = goodix_i2c_write(ts->client, GOODIX_FW_UPLOAD_ADDRESS, data, 2 * GOODIX_FW_SECTION_LENGTH); if (error) - goto release; + return error; /* Select SRAM bank 1 and upload section 3 & 4 */ error = goodix_i2c_write_u8(ts->client, GOODIX_REG_MISCTL_SRAM_BANK, 0x01); if (error) - goto release; + return error; data += 2 * GOODIX_FW_SECTION_LENGTH; error = goodix_i2c_write(ts->client, GOODIX_FW_UPLOAD_ADDRESS, data, 2 * GOODIX_FW_SECTION_LENGTH); if (error) - goto release; + return error; /* Select SRAM bank 2 and upload the DSP firmware */ error = goodix_i2c_write_u8(ts->client, GOODIX_REG_MISCTL_SRAM_BANK, 0x02); if (error) - goto release; + return error; data += 2 * GOODIX_FW_SECTION_LENGTH; error = goodix_i2c_write(ts->client, GOODIX_FW_UPLOAD_ADDRESS, data, GOODIX_FW_DSP_LENGTH); if (error) - goto release; + return error; error = goodix_start_firmware(ts->client); if (error) - goto release; + return error; error = goodix_int_sync(ts); -release: - release_firmware(fw); - return error; + if (error) + return error; + + return 0; } static int goodix_prepare_bak_ref(struct goodix_ts_data *ts) -- cgit v1.2.3 From 5568c1aeb33b008b1f643b1fa16360cdb6ccadf7 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 16 Jan 2024 11:35:26 -0800 Subject: Input: hideep - switch to using cleanup functions Start using __free() and guard() primitives to simplify the code and error handling. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/hideep.c | 54 ++++++++++++++------------------------ 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/drivers/input/touchscreen/hideep.c b/drivers/input/touchscreen/hideep.c index a73369e15dda..62041bcca83d 100644 --- a/drivers/input/touchscreen/hideep.c +++ b/drivers/input/touchscreen/hideep.c @@ -869,8 +869,6 @@ static ssize_t hideep_update_fw(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct hideep_ts *ts = i2c_get_clientdata(client); - const struct firmware *fw_entry; - char *fw_name; int mode; int error; @@ -878,46 +876,42 @@ static ssize_t hideep_update_fw(struct device *dev, if (error) return error; - fw_name = kasprintf(GFP_KERNEL, "hideep_ts_%04x.bin", - be16_to_cpu(ts->dwz_info.product_id)); + const char *fw_name __free(kfree) = + kasprintf(GFP_KERNEL, "hideep_ts_%04x.bin", + be16_to_cpu(ts->dwz_info.product_id)); if (!fw_name) return -ENOMEM; + const struct firmware *fw_entry __free(firmware) = NULL; error = request_firmware(&fw_entry, fw_name, dev); if (error) { dev_err(dev, "failed to request firmware %s: %d", fw_name, error); - goto out_free_fw_name; + return error; } if (fw_entry->size % sizeof(__be32)) { dev_err(dev, "invalid firmware size %zu\n", fw_entry->size); - error = -EINVAL; - goto out_release_fw; + return -EINVAL; } if (fw_entry->size > ts->fw_size) { dev_err(dev, "fw size (%zu) is too big (memory size %d)\n", fw_entry->size, ts->fw_size); - error = -EFBIG; - goto out_release_fw; + return -EFBIG; } - mutex_lock(&ts->dev_mutex); - disable_irq(client->irq); - - error = hideep_update_firmware(ts, (const __be32 *)fw_entry->data, - fw_entry->size); - - enable_irq(client->irq); - mutex_unlock(&ts->dev_mutex); + scoped_guard(mutex, &ts->dev_mutex) { + guard(disable_irq)(&client->irq); -out_release_fw: - release_firmware(fw_entry); -out_free_fw_name: - kfree(fw_name); + error = hideep_update_firmware(ts, + (const __be32 *)fw_entry->data, + fw_entry->size); + if (error) + return error; + } - return error ?: count; + return count; } static ssize_t hideep_fw_version_show(struct device *dev, @@ -925,13 +919,9 @@ static ssize_t hideep_fw_version_show(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct hideep_ts *ts = i2c_get_clientdata(client); - ssize_t len; - mutex_lock(&ts->dev_mutex); - len = sysfs_emit(buf, "%04x\n", be16_to_cpu(ts->dwz_info.release_ver)); - mutex_unlock(&ts->dev_mutex); - - return len; + guard(mutex)(&ts->dev_mutex); + return sysfs_emit(buf, "%04x\n", be16_to_cpu(ts->dwz_info.release_ver)); } static ssize_t hideep_product_id_show(struct device *dev, @@ -939,13 +929,9 @@ static ssize_t hideep_product_id_show(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct hideep_ts *ts = i2c_get_clientdata(client); - ssize_t len; - - mutex_lock(&ts->dev_mutex); - len = sysfs_emit(buf, "%04x\n", be16_to_cpu(ts->dwz_info.product_id)); - mutex_unlock(&ts->dev_mutex); - return len; + guard(mutex)(&ts->dev_mutex); + return sysfs_emit(buf, "%04x\n", be16_to_cpu(ts->dwz_info.product_id)); } static DEVICE_ATTR(version, 0664, hideep_fw_version_show, NULL); -- cgit v1.2.3 From ded32cc611ef48a47d1ff6d424151e3d0c82a3a8 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 17 Aug 2024 18:55:08 -0700 Subject: Input: hycon-hy46xx - use guard notation when acquiring mutex Guard notation simplifies code. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/hycon-hy46xx.c | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/drivers/input/touchscreen/hycon-hy46xx.c b/drivers/input/touchscreen/hycon-hy46xx.c index b2ff7a45b908..1513f20cbf51 100644 --- a/drivers/input/touchscreen/hycon-hy46xx.c +++ b/drivers/input/touchscreen/hycon-hy46xx.c @@ -181,18 +181,17 @@ static ssize_t hycon_hy46xx_setting_show(struct device *dev, struct hycon_hy46xx_attribute *attr = container_of(dattr, struct hycon_hy46xx_attribute, dattr); u8 *field = (u8 *)tsdata + attr->field_offset; - size_t count = 0; int error = 0; int val; - mutex_lock(&tsdata->mutex); + guard(mutex)(&tsdata->mutex); error = regmap_read(tsdata->regmap, attr->address, &val); - if (error < 0) { + if (error) { dev_err(&tsdata->client->dev, "Failed to fetch attribute %s, error %d\n", dattr->attr.name, error); - goto out; + return error; } if (val != *field) { @@ -202,11 +201,7 @@ static ssize_t hycon_hy46xx_setting_show(struct device *dev, *field = val; } - count = sysfs_emit(buf, "%d\n", val); - -out: - mutex_unlock(&tsdata->mutex); - return error ?: count; + return sysfs_emit(buf, "%d\n", val); } static ssize_t hycon_hy46xx_setting_store(struct device *dev, @@ -221,29 +216,25 @@ static ssize_t hycon_hy46xx_setting_store(struct device *dev, unsigned int val; int error; - mutex_lock(&tsdata->mutex); + guard(mutex)(&tsdata->mutex); error = kstrtouint(buf, 0, &val); if (error) - goto out; + return error; - if (val < attr->limit_low || val > attr->limit_high) { - error = -ERANGE; - goto out; - } + if (val < attr->limit_low || val > attr->limit_high) + return -ERANGE; error = regmap_write(tsdata->regmap, attr->address, val); - if (error < 0) { + if (error) { dev_err(&tsdata->client->dev, "Failed to update attribute %s, error: %d\n", dattr->attr.name, error); - goto out; + return error; } *field = val; -out: - mutex_unlock(&tsdata->mutex); - return error ?: count; + return count; } static HYCON_ATTR_U8(threshold, 0644, HY46XX_THRESHOLD, 0, 255); -- cgit v1.2.3 From d2862b87add9966bc23af73a033f27a296bdbb55 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 17 Aug 2024 18:58:22 -0700 Subject: Input: imagis - use guard notation when acquiring mutex Guard notation simplifies code. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/imagis.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/input/touchscreen/imagis.c b/drivers/input/touchscreen/imagis.c index 3c8bbe284b73..7bbb00beec3b 100644 --- a/drivers/input/touchscreen/imagis.c +++ b/drivers/input/touchscreen/imagis.c @@ -366,32 +366,34 @@ static int imagis_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct imagis_ts *ts = i2c_get_clientdata(client); - int retval = 0; - - mutex_lock(&ts->input_dev->mutex); + int error; - if (input_device_enabled(ts->input_dev)) - retval = imagis_stop(ts); + guard(mutex)(&ts->input_dev->mutex); - mutex_unlock(&ts->input_dev->mutex); + if (input_device_enabled(ts->input_dev)) { + error = imagis_stop(ts); + if (error) + return error; + } - return retval; + return 0; } static int imagis_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct imagis_ts *ts = i2c_get_clientdata(client); - int retval = 0; - - mutex_lock(&ts->input_dev->mutex); + int error; - if (input_device_enabled(ts->input_dev)) - retval = imagis_start(ts); + guard(mutex)(&ts->input_dev->mutex); - mutex_unlock(&ts->input_dev->mutex); + if (input_device_enabled(ts->input_dev)) { + error = imagis_start(ts); + if (error) + return error; + } - return retval; + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(imagis_pm_ops, imagis_suspend, imagis_resume); -- cgit v1.2.3 From 445dcfc7f676842994eb7f011924ff0e3a90c13b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 17 Aug 2024 19:00:21 -0700 Subject: Input: imx6ul_tsc - use guard notation when acquiring mutex Guard notation simplifies code. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/imx6ul_tsc.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/input/touchscreen/imx6ul_tsc.c b/drivers/input/touchscreen/imx6ul_tsc.c index 85f697de2b7e..43d6c69e3088 100644 --- a/drivers/input/touchscreen/imx6ul_tsc.c +++ b/drivers/input/touchscreen/imx6ul_tsc.c @@ -551,13 +551,11 @@ static int imx6ul_tsc_suspend(struct device *dev) struct imx6ul_tsc *tsc = platform_get_drvdata(pdev); struct input_dev *input_dev = tsc->input; - mutex_lock(&input_dev->mutex); + guard(mutex)(&input_dev->mutex); if (input_device_enabled(input_dev)) imx6ul_tsc_stop(tsc); - mutex_unlock(&input_dev->mutex); - return 0; } @@ -566,16 +564,17 @@ static int imx6ul_tsc_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct imx6ul_tsc *tsc = platform_get_drvdata(pdev); struct input_dev *input_dev = tsc->input; - int retval = 0; - - mutex_lock(&input_dev->mutex); + int error; - if (input_device_enabled(input_dev)) - retval = imx6ul_tsc_start(tsc); + guard(mutex)(&input_dev->mutex); - mutex_unlock(&input_dev->mutex); + if (input_device_enabled(input_dev)) { + error = imx6ul_tsc_start(tsc); + if (error) + return error; + } - return retval; + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(imx6ul_tsc_pm_ops, -- cgit v1.2.3 From f1324109d1a41125815eb4bd1666c6b02a3ac7d4 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 17 Aug 2024 19:01:18 -0700 Subject: Input: ipaq-micro-ts - use guard notation when acquiring mutex/spinlock Guard notation simplifies code. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ipaq-micro-ts.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/input/touchscreen/ipaq-micro-ts.c b/drivers/input/touchscreen/ipaq-micro-ts.c index 94720c41c9be..243e6465d9bd 100644 --- a/drivers/input/touchscreen/ipaq-micro-ts.c +++ b/drivers/input/touchscreen/ipaq-micro-ts.c @@ -47,7 +47,7 @@ static void micro_ts_toggle_receive(struct touchscreen_data *ts, bool enable) { struct ipaq_micro *micro = ts->micro; - spin_lock_irq(µ->lock); + guard(spinlock_irq)(µ->lock); if (enable) { micro->ts = micro_ts_receive; @@ -56,8 +56,6 @@ static void micro_ts_toggle_receive(struct touchscreen_data *ts, bool enable) micro->ts = NULL; micro->ts_data = NULL; } - - spin_unlock_irq(&ts->micro->lock); } static int micro_ts_open(struct input_dev *input) @@ -133,13 +131,11 @@ static int micro_ts_resume(struct device *dev) struct touchscreen_data *ts = dev_get_drvdata(dev); struct input_dev *input = ts->input; - mutex_lock(&input->mutex); + guard(mutex)(&input->mutex); if (input_device_enabled(input)) micro_ts_toggle_receive(ts, true); - mutex_unlock(&input->mutex); - return 0; } -- cgit v1.2.3 From 582f32aa89e66ab1e16aab157fdd483b5acc86fa Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 16 Jan 2024 13:52:33 -0800 Subject: Input: iqs5xx - switch to using cleanup functions Start using __free() and guard() primitives to simplify the code and error handling. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/iqs5xx.c | 145 ++++++++++++++++--------------------- 1 file changed, 64 insertions(+), 81 deletions(-) diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c index c63819abaf9b..c8cd3f3ca421 100644 --- a/drivers/input/touchscreen/iqs5xx.c +++ b/drivers/input/touchscreen/iqs5xx.c @@ -356,7 +356,7 @@ static int iqs5xx_bl_open(struct i2c_client *client) } static int iqs5xx_bl_write(struct i2c_client *client, - u16 bl_addr, u8 *pmap_data, u16 pmap_len) + u16 bl_addr, const u8 *pmap_data, u16 pmap_len) { struct i2c_msg msg; int ret, i; @@ -395,7 +395,7 @@ msg_fail: } static int iqs5xx_bl_verify(struct i2c_client *client, - u16 bl_addr, u8 *pmap_data, u16 pmap_len) + u16 bl_addr, const u8 *pmap_data, u16 pmap_len) { struct i2c_msg msg; int ret, i; @@ -446,27 +446,21 @@ static int iqs5xx_set_state(struct i2c_client *client, u8 state) if (!iqs5xx->dev_id_info.bl_status) return 0; - mutex_lock(&iqs5xx->lock); + guard(mutex)(&iqs5xx->lock); /* * Addressing the device outside of a communication window prompts it * to assert the RDY output, so disable the interrupt line to prevent * the handler from servicing a false interrupt. */ - disable_irq(client->irq); + guard(disable_irq)(&client->irq); error1 = iqs5xx_write_byte(client, IQS5XX_SYS_CTRL1, state); error2 = iqs5xx_write_byte(client, IQS5XX_END_COMM, 0); usleep_range(50, 100); - enable_irq(client->irq); - mutex_unlock(&iqs5xx->lock); - - if (error1) - return error1; - - return error2; + return error1 ?: error2; } static int iqs5xx_open(struct input_dev *input) @@ -703,7 +697,6 @@ static irqreturn_t iqs5xx_irq(int irq, void *data) static int iqs5xx_fw_file_parse(struct i2c_client *client, const char *fw_file, u8 *pmap) { - const struct firmware *fw; struct iqs5xx_ihex_rec *rec; size_t pos = 0; int error, i; @@ -722,6 +715,7 @@ static int iqs5xx_fw_file_parse(struct i2c_client *client, * Because the ihex2fw tool tolerates neither (1) nor (2), the slightly * nonstandard ihex firmware is parsed directly by the driver. */ + const struct firmware *fw __free(firmware) = NULL; error = request_firmware(&fw, fw_file, &client->dev); if (error) { dev_err(&client->dev, "Failed to request firmware %s: %d\n", @@ -732,8 +726,7 @@ static int iqs5xx_fw_file_parse(struct i2c_client *client, do { if (pos + sizeof(*rec) > fw->size) { dev_err(&client->dev, "Insufficient firmware size\n"); - error = -EINVAL; - break; + return -EINVAL; } rec = (struct iqs5xx_ihex_rec *)(fw->data + pos); pos += sizeof(*rec); @@ -741,15 +734,14 @@ static int iqs5xx_fw_file_parse(struct i2c_client *client, if (rec->start != ':') { dev_err(&client->dev, "Invalid start at record %u\n", rec_num); - error = -EINVAL; - break; + return -EINVAL; } error = hex2bin(rec_hdr, rec->len, sizeof(rec_hdr)); if (error) { dev_err(&client->dev, "Invalid header at record %u\n", rec_num); - break; + return error; } rec_len = *rec_hdr; @@ -758,8 +750,7 @@ static int iqs5xx_fw_file_parse(struct i2c_client *client, if (pos + rec_len * 2 > fw->size) { dev_err(&client->dev, "Insufficient firmware size\n"); - error = -EINVAL; - break; + return -EINVAL; } pos += (rec_len * 2); @@ -767,7 +758,7 @@ static int iqs5xx_fw_file_parse(struct i2c_client *client, if (error) { dev_err(&client->dev, "Invalid data at record %u\n", rec_num); - break; + return error; } error = hex2bin(&rec_chksm, @@ -775,7 +766,7 @@ static int iqs5xx_fw_file_parse(struct i2c_client *client, if (error) { dev_err(&client->dev, "Invalid checksum at record %u\n", rec_num); - break; + return error; } chksm = 0; @@ -800,23 +791,22 @@ static int iqs5xx_fw_file_parse(struct i2c_client *client, dev_err(&client->dev, "Invalid address at record %u\n", rec_num); - error = -EINVAL; - } else { - memcpy(pmap + rec_addr - IQS5XX_CHKSM, - rec_data, rec_len); + return -EINVAL; } + + memcpy(pmap + rec_addr - IQS5XX_CHKSM, + rec_data, rec_len); break; + case IQS5XX_REC_TYPE_EOF: break; + default: dev_err(&client->dev, "Invalid type at record %u\n", rec_num); - error = -EINVAL; + return -EINVAL; } - if (error) - break; - rec_num++; while (pos < fw->size) { if (*(fw->data + pos) == ':') @@ -825,33 +815,13 @@ static int iqs5xx_fw_file_parse(struct i2c_client *client, } } while (rec_type != IQS5XX_REC_TYPE_EOF); - release_firmware(fw); - - return error; + return 0; } -static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file) +static int iqs5xx_update_firmware(struct iqs5xx_private *iqs5xx, const u8 *pmap) { - struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client); - int error, error_init = 0; - u8 *pmap; - - pmap = kzalloc(IQS5XX_PMAP_LEN, GFP_KERNEL); - if (!pmap) - return -ENOMEM; - - error = iqs5xx_fw_file_parse(client, fw_file, pmap); - if (error) - goto err_kfree; - - mutex_lock(&iqs5xx->lock); - - /* - * Disable the interrupt line in case the first attempt(s) to enter the - * bootloader don't happen quickly enough, in which case the device may - * assert the RDY output until the next attempt. - */ - disable_irq(client->irq); + struct i2c_client *client = iqs5xx->client; + int error; iqs5xx->dev_id_info.bl_status = 0; @@ -859,22 +829,50 @@ static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file) if (error) { error = iqs5xx_bl_open(client); if (error) - goto err_reset; + return error; } error = iqs5xx_bl_write(client, IQS5XX_CHKSM, pmap, IQS5XX_PMAP_LEN); if (error) - goto err_reset; + return error; error = iqs5xx_bl_cmd(client, IQS5XX_BL_CMD_CRC, 0); if (error) - goto err_reset; + return error; error = iqs5xx_bl_verify(client, IQS5XX_CSTM, pmap + IQS5XX_CHKSM_LEN + IQS5XX_APP_LEN, IQS5XX_CSTM_LEN); + if (error) + return error; + + return 0; +} + +static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file) +{ + struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client); + int error, error_init = 0; + + u8 *pmap __free(kfree) = kzalloc(IQS5XX_PMAP_LEN, GFP_KERNEL); + if (!pmap) + return -ENOMEM; + + error = iqs5xx_fw_file_parse(client, fw_file, pmap); + if (error) + return error; + + guard(mutex)(&iqs5xx->lock); + + /* + * Disable the interrupt line in case the first attempt(s) to enter the + * bootloader don't happen quickly enough, in which case the device may + * assert the RDY output until the next attempt. + */ + guard(disable_irq)(&client->irq); + + error = iqs5xx_update_firmware(iqs5xx, pmap); -err_reset: iqs5xx_reset(client); usleep_range(15000, 15100); @@ -882,14 +880,7 @@ err_reset: if (!iqs5xx->dev_id_info.bl_status) error_init = error_init ? : -EINVAL; - enable_irq(client->irq); - - mutex_unlock(&iqs5xx->lock); - -err_kfree: - kfree(pmap); - - return error ? : error_init; + return error ?: error_init; } static ssize_t fw_file_store(struct device *dev, @@ -985,38 +976,30 @@ static int iqs5xx_suspend(struct device *dev) { struct iqs5xx_private *iqs5xx = dev_get_drvdata(dev); struct input_dev *input = iqs5xx->input; - int error = 0; if (!input || device_may_wakeup(dev)) - return error; - - mutex_lock(&input->mutex); + return 0; + guard(mutex)(&input->mutex); if (input_device_enabled(input)) - error = iqs5xx_set_state(iqs5xx->client, IQS5XX_SUSPEND); - - mutex_unlock(&input->mutex); + return iqs5xx_set_state(iqs5xx->client, IQS5XX_SUSPEND); - return error; + return 0; } static int iqs5xx_resume(struct device *dev) { struct iqs5xx_private *iqs5xx = dev_get_drvdata(dev); struct input_dev *input = iqs5xx->input; - int error = 0; if (!input || device_may_wakeup(dev)) - return error; - - mutex_lock(&input->mutex); + return 0; + guard(mutex)(&input->mutex); if (input_device_enabled(input)) - error = iqs5xx_set_state(iqs5xx->client, IQS5XX_RESUME); - - mutex_unlock(&input->mutex); + return iqs5xx_set_state(iqs5xx->client, IQS5XX_RESUME); - return error; + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(iqs5xx_pm, iqs5xx_suspend, iqs5xx_resume); -- cgit v1.2.3 From 3b5e7a62651ec2c1a8b515ffec14cd7262dda4a7 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 28 Apr 2025 17:53:02 -0700 Subject: Input: iqs5xx - simplify parsing of firmware blob Do not define or use iqs5xx_ihex_rec structure: the original code was using just a couple of fields in it and instead used it to calculate offset to record data. The data field was actually reserving space for checksum. Instead iterate through fields and advance pointer explicitly. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/iqs5xx.c | 45 +++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c index c8cd3f3ca421..587665e4e6fd 100644 --- a/drivers/input/touchscreen/iqs5xx.c +++ b/drivers/input/touchscreen/iqs5xx.c @@ -73,8 +73,11 @@ #define IQS5XX_CSTM_LEN (IQS5XX_PMAP_END + 1 - IQS5XX_CSTM) #define IQS5XX_PMAP_LEN (IQS5XX_PMAP_END + 1 - IQS5XX_CHKSM) -#define IQS5XX_REC_HDR_LEN 4 -#define IQS5XX_REC_LEN_MAX 255 +/* Length of firmware header in hexadecimal characters */ +#define IQS5XX_REC_HDR_LEN_HEX (1 /* start */ + 2 /* size */ + \ + 4 /* addr */ + 2 /* type */) +#define IQS5XX_REC_HDR_SIZE 4 /* size + addr (2 bytes) + type, in bytes*/ +#define IQS5XX_REC_DATA_SIZE 255 /* maximum size of the data portion */ #define IQS5XX_REC_TYPE_DATA 0x00 #define IQS5XX_REC_TYPE_EOF 0x01 @@ -98,14 +101,6 @@ struct iqs5xx_dev_id_info { u8 bl_status; } __packed; -struct iqs5xx_ihex_rec { - char start; - char len[2]; - char addr[4]; - char type[2]; - char data[2]; -} __packed; - struct iqs5xx_touch_data { __be16 abs_x; __be16 abs_y; @@ -697,14 +692,13 @@ static irqreturn_t iqs5xx_irq(int irq, void *data) static int iqs5xx_fw_file_parse(struct i2c_client *client, const char *fw_file, u8 *pmap) { - struct iqs5xx_ihex_rec *rec; size_t pos = 0; int error, i; u16 rec_num = 1; u16 rec_addr; u8 rec_len, rec_type, rec_chksm, chksm; - u8 rec_hdr[IQS5XX_REC_HDR_LEN]; - u8 rec_data[IQS5XX_REC_LEN_MAX]; + u8 rec_hdr[IQS5XX_REC_HDR_SIZE]; + u8 rec_data[IQS5XX_REC_DATA_SIZE]; /* * Firmware exported from the vendor's configuration tool deviates from @@ -724,50 +718,55 @@ static int iqs5xx_fw_file_parse(struct i2c_client *client, } do { - if (pos + sizeof(*rec) > fw->size) { + if (pos + IQS5XX_REC_HDR_LEN_HEX > fw->size) { dev_err(&client->dev, "Insufficient firmware size\n"); return -EINVAL; } - rec = (struct iqs5xx_ihex_rec *)(fw->data + pos); - pos += sizeof(*rec); - if (rec->start != ':') { + if (fw->data[pos] != ':') { dev_err(&client->dev, "Invalid start at record %u\n", rec_num); return -EINVAL; } - error = hex2bin(rec_hdr, rec->len, sizeof(rec_hdr)); + /* Convert all 3 fields (length, address, and type) in one go */ + error = hex2bin(rec_hdr, &fw->data[pos + 1], sizeof(rec_hdr)); if (error) { dev_err(&client->dev, "Invalid header at record %u\n", rec_num); return error; } + pos += IQS5XX_REC_HDR_LEN_HEX; rec_len = *rec_hdr; rec_addr = get_unaligned_be16(rec_hdr + sizeof(rec_len)); rec_type = *(rec_hdr + sizeof(rec_len) + sizeof(rec_addr)); - if (pos + rec_len * 2 > fw->size) { + /* + * Check if we have enough data for the data portion of the + * record, as well as the checksum byte. Everything is doubled + * because data is in ASCII HEX and not binary format. + */ + if (pos + (rec_len + sizeof(rec_chksm)) * 2 > fw->size) { dev_err(&client->dev, "Insufficient firmware size\n"); return -EINVAL; } - pos += (rec_len * 2); - error = hex2bin(rec_data, rec->data, rec_len); + error = hex2bin(rec_data, &fw->data[pos], rec_len); if (error) { dev_err(&client->dev, "Invalid data at record %u\n", rec_num); return error; } + pos += rec_len * 2; - error = hex2bin(&rec_chksm, - rec->data + rec_len * 2, sizeof(rec_chksm)); + error = hex2bin(&rec_chksm, &fw->data[pos], sizeof(rec_chksm)); if (error) { dev_err(&client->dev, "Invalid checksum at record %u\n", rec_num); return error; } + pos += 2; chksm = 0; for (i = 0; i < sizeof(rec_hdr); i++) -- cgit v1.2.3 From 3092610fdc62b798f648b6de59a83ecd70315913 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 21 Aug 2024 15:48:31 -0700 Subject: Input: iqs7211 - use cleanup facility for fwnodes Use __free(fwnode_handle) cleanup facility to ensure that references to acquired fwnodes are dropped at appropriate times automatically. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/iqs7211.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/drivers/input/touchscreen/iqs7211.c b/drivers/input/touchscreen/iqs7211.c index c5d447ee6f53..26e0b501c5fc 100644 --- a/drivers/input/touchscreen/iqs7211.c +++ b/drivers/input/touchscreen/iqs7211.c @@ -2060,19 +2060,16 @@ static int iqs7211_parse_reg_grp(struct iqs7211_private *iqs7211, for (i = 0; i < dev_desc->num_kp_events; i++) { const char *event_name = dev_desc->kp_events[i].name; - struct fwnode_handle *event_node; if (dev_desc->kp_events[i].reg_grp != reg_grp) continue; reg_field.mask |= dev_desc->kp_events[i].enable; - if (event_name) - event_node = fwnode_get_named_child_node(reg_grp_node, - event_name); - else - event_node = fwnode_handle_get(reg_grp_node); - + struct fwnode_handle *event_node __free(fwnode_handle) = + event_name ? fwnode_get_named_child_node(reg_grp_node, + event_name) : + fwnode_handle_get(reg_grp_node); if (!event_node) continue; @@ -2080,7 +2077,6 @@ static int iqs7211_parse_reg_grp(struct iqs7211_private *iqs7211, dev_desc->kp_events[i].reg_grp, dev_desc->kp_events[i].reg_key, &iqs7211->kp_code[i]); - fwnode_handle_put(event_node); if (error) return error; @@ -2496,19 +2492,15 @@ static int iqs7211_probe(struct i2c_client *client) for (reg_grp = 0; reg_grp < IQS7211_NUM_REG_GRPS; reg_grp++) { const char *reg_grp_name = iqs7211_reg_grp_names[reg_grp]; - struct fwnode_handle *reg_grp_node; - - if (reg_grp_name) - reg_grp_node = device_get_named_child_node(&client->dev, - reg_grp_name); - else - reg_grp_node = fwnode_handle_get(dev_fwnode(&client->dev)); + struct fwnode_handle *reg_grp_node __free(fwnode_handle) = + reg_grp_name ? device_get_named_child_node(&client->dev, + reg_grp_name) : + fwnode_handle_get(dev_fwnode(&client->dev)); if (!reg_grp_node) continue; error = iqs7211_parse_reg_grp(iqs7211, reg_grp_node, reg_grp); - fwnode_handle_put(reg_grp_node); if (error) return error; } -- cgit v1.2.3 From a00a9fad1c05293859c25b30621dde0f34290121 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 16 Aug 2024 14:11:25 -0700 Subject: Input: lpc32xx_ts - use guard notation when acquiring mutex Guard notation simplifies code. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/lpc32xx_ts.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/input/touchscreen/lpc32xx_ts.c b/drivers/input/touchscreen/lpc32xx_ts.c index 9bad8b93c039..5de752a06b26 100644 --- a/drivers/input/touchscreen/lpc32xx_ts.c +++ b/drivers/input/touchscreen/lpc32xx_ts.c @@ -279,7 +279,7 @@ static int lpc32xx_ts_suspend(struct device *dev) * avoid calling the TSC stop and start functions as the TSC * isn't yet clocked. */ - mutex_lock(&input->mutex); + guard(mutex)(&input->mutex); if (input_device_enabled(input)) { if (device_may_wakeup(dev)) @@ -288,8 +288,6 @@ static int lpc32xx_ts_suspend(struct device *dev) lpc32xx_stop_tsc(tsc); } - mutex_unlock(&input->mutex); - return 0; } @@ -298,7 +296,7 @@ static int lpc32xx_ts_resume(struct device *dev) struct lpc32xx_tsc *tsc = dev_get_drvdata(dev); struct input_dev *input = tsc->dev; - mutex_lock(&input->mutex); + guard(mutex)(&input->mutex); if (input_device_enabled(input)) { if (device_may_wakeup(dev)) @@ -307,8 +305,6 @@ static int lpc32xx_ts_resume(struct device *dev) lpc32xx_setup_tsc(tsc); } - mutex_unlock(&input->mutex); - return 0; } -- cgit v1.2.3 From 8e4ae01d84cd0879636c333e0707c86074b00405 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 29 May 2024 11:58:26 -0700 Subject: Input: melfas_mip4 - switch to using cleanup functions Start using __free() and guard() primitives to simplify the code and error handling. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/melfas_mip4.c | 121 ++++++++++++-------------------- 1 file changed, 44 insertions(+), 77 deletions(-) diff --git a/drivers/input/touchscreen/melfas_mip4.c b/drivers/input/touchscreen/melfas_mip4.c index 869884219908..10fccf4e5bb1 100644 --- a/drivers/input/touchscreen/melfas_mip4.c +++ b/drivers/input/touchscreen/melfas_mip4.c @@ -881,8 +881,6 @@ static int mip4_bl_program_page(struct mip4_ts *ts, int offset, const u8 *data, int length, u16 buf_addr) { u8 cmd[6]; - u8 *data_buf; - u16 buf_offset; int ret; int error; @@ -895,7 +893,8 @@ static int mip4_bl_program_page(struct mip4_ts *ts, int offset, return -EINVAL; } - data_buf = kmalloc(2 + MIP4_BL_PACKET_SIZE, GFP_KERNEL); + u8 *data_buf __free(kfree) = kmalloc(2 + MIP4_BL_PACKET_SIZE, + GFP_KERNEL); if (!data_buf) return -ENOMEM; @@ -908,7 +907,7 @@ static int mip4_bl_program_page(struct mip4_ts *ts, int offset, error = ret < 0 ? ret : -EIO; dev_err(&ts->client->dev, "Failed to send write page address: %d\n", error); - goto out; + return error; } /* Size */ @@ -920,11 +919,11 @@ static int mip4_bl_program_page(struct mip4_ts *ts, int offset, error = ret < 0 ? ret : -EIO; dev_err(&ts->client->dev, "Failed to send write page size: %d\n", error); - goto out; + return error; } /* Data */ - for (buf_offset = 0; + for (int buf_offset = 0; buf_offset < length; buf_offset += MIP4_BL_PACKET_SIZE) { dev_dbg(&ts->client->dev, @@ -939,7 +938,7 @@ static int mip4_bl_program_page(struct mip4_ts *ts, int offset, dev_err(&ts->client->dev, "Failed to read chunk at %#04x (size %d): %d\n", buf_offset, MIP4_BL_PACKET_SIZE, error); - goto out; + return error; } } @@ -952,35 +951,21 @@ static int mip4_bl_program_page(struct mip4_ts *ts, int offset, error = ret < 0 ? ret : -EIO; dev_err(&ts->client->dev, "Failed to send 'write' command: %d\n", error); - goto out; + return error; } /* Status */ error = mip4_bl_read_status(ts); + if (error) + return error; -out: - kfree(data_buf); - return error ? error : 0; + return 0; } static int mip4_bl_verify_page(struct mip4_ts *ts, int offset, const u8 *data, int length, int buf_addr) { u8 cmd[8]; - u8 *read_buf; - int buf_offset; - struct i2c_msg msg[] = { - { - .addr = ts->client->addr, - .flags = 0, - .buf = cmd, - .len = 2, - }, { - .addr = ts->client->addr, - .flags = I2C_M_RD, - .len = MIP4_BL_PACKET_SIZE, - }, - }; int ret; int error; @@ -1029,11 +1014,25 @@ static int mip4_bl_verify_page(struct mip4_ts *ts, int offset, return error; /* Read */ - msg[1].buf = read_buf = kmalloc(MIP4_BL_PACKET_SIZE, GFP_KERNEL); + u8 *read_buf __free(kfree) = kmalloc(MIP4_BL_PACKET_SIZE, GFP_KERNEL); if (!read_buf) return -ENOMEM; - for (buf_offset = 0; + struct i2c_msg msg[] = { + { + .addr = ts->client->addr, + .flags = 0, + .buf = cmd, + .len = 2, + }, { + .addr = ts->client->addr, + .flags = I2C_M_RD, + .buf = read_buf, + .len = MIP4_BL_PACKET_SIZE, + }, + }; + + for (int buf_offset = 0; buf_offset < length; buf_offset += MIP4_BL_PACKET_SIZE) { dev_dbg(&ts->client->dev, @@ -1046,7 +1045,7 @@ static int mip4_bl_verify_page(struct mip4_ts *ts, int offset, dev_err(&ts->client->dev, "Failed to read chunk at %#04x (size %d): %d\n", buf_offset, MIP4_BL_PACKET_SIZE, error); - break; + return error; } if (memcmp(&data[buf_offset], read_buf, MIP4_BL_PACKET_SIZE)) { @@ -1064,13 +1063,11 @@ static int mip4_bl_verify_page(struct mip4_ts *ts, int offset, DUMP_PREFIX_OFFSET, 16, 1, read_buf, MIP4_BL_PAGE_SIZE, false); #endif - error = -EINVAL; - break; + return -EINVAL; } } - kfree(read_buf); - return error ? error : 0; + return 0; } /* @@ -1290,9 +1287,9 @@ static ssize_t mip4_sysfs_fw_update(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct mip4_ts *ts = i2c_get_clientdata(client); - const struct firmware *fw; int error; + const struct firmware *fw __free(firmware) = NULL; error = request_firmware(&fw, ts->fw_name, dev); if (error) { dev_err(&ts->client->dev, @@ -1306,14 +1303,9 @@ static ssize_t mip4_sysfs_fw_update(struct device *dev, * userspace opening and closing the device and also suspend/resume * transitions. */ - mutex_lock(&ts->input->mutex); + guard(mutex)(&ts->input->mutex); error = mip4_execute_fw_update(ts, fw); - - mutex_unlock(&ts->input->mutex); - - release_firmware(fw); - if (error) { dev_err(&ts->client->dev, "Firmware update failed: %d\n", error); @@ -1331,18 +1323,13 @@ static ssize_t mip4_sysfs_read_fw_version(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct mip4_ts *ts = i2c_get_clientdata(client); - size_t count; /* Take lock to prevent racing with firmware update */ - mutex_lock(&ts->input->mutex); - - count = sysfs_emit(buf, "%04X %04X %04X %04X\n", - ts->fw_version.boot, ts->fw_version.core, - ts->fw_version.app, ts->fw_version.param); + guard(mutex)(&ts->input->mutex); - mutex_unlock(&ts->input->mutex); - - return count; + return sysfs_emit(buf, "%04X %04X %04X %04X\n", + ts->fw_version.boot, ts->fw_version.core, + ts->fw_version.app, ts->fw_version.param); } static DEVICE_ATTR(fw_version, S_IRUGO, mip4_sysfs_read_fw_version, NULL); @@ -1353,21 +1340,16 @@ static ssize_t mip4_sysfs_read_hw_version(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct mip4_ts *ts = i2c_get_clientdata(client); - size_t count; /* Take lock to prevent racing with firmware update */ - mutex_lock(&ts->input->mutex); + guard(mutex)(&ts->input->mutex); /* * product_name shows the name or version of the hardware * paired with current firmware in the chip. */ - count = sysfs_emit(buf, "%.*s\n", - (int)sizeof(ts->product_name), ts->product_name); - - mutex_unlock(&ts->input->mutex); - - return count; + return sysfs_emit(buf, "%.*s\n", + (int)sizeof(ts->product_name), ts->product_name); } static DEVICE_ATTR(hw_version, S_IRUGO, mip4_sysfs_read_hw_version, NULL); @@ -1378,15 +1360,10 @@ static ssize_t mip4_sysfs_read_product_id(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct mip4_ts *ts = i2c_get_clientdata(client); - size_t count; - - mutex_lock(&ts->input->mutex); - count = sysfs_emit(buf, "%04X\n", ts->product_id); + guard(mutex)(&ts->input->mutex); - mutex_unlock(&ts->input->mutex); - - return count; + return sysfs_emit(buf, "%04X\n", ts->product_id); } static DEVICE_ATTR(product_id, S_IRUGO, mip4_sysfs_read_product_id, NULL); @@ -1397,16 +1374,10 @@ static ssize_t mip4_sysfs_read_ic_name(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct mip4_ts *ts = i2c_get_clientdata(client); - size_t count; - - mutex_lock(&ts->input->mutex); - - count = sysfs_emit(buf, "%.*s\n", - (int)sizeof(ts->ic_name), ts->ic_name); - mutex_unlock(&ts->input->mutex); + guard(mutex)(&ts->input->mutex); - return count; + return sysfs_emit(buf, "%.*s\n", (int)sizeof(ts->ic_name), ts->ic_name); } static DEVICE_ATTR(ic_name, S_IRUGO, mip4_sysfs_read_ic_name, NULL); @@ -1520,15 +1491,13 @@ static int mip4_suspend(struct device *dev) struct mip4_ts *ts = i2c_get_clientdata(client); struct input_dev *input = ts->input; - mutex_lock(&input->mutex); + guard(mutex)(&input->mutex); if (device_may_wakeup(dev)) ts->wake_irq_enabled = enable_irq_wake(client->irq) == 0; else if (input_device_enabled(input)) mip4_disable(ts); - mutex_unlock(&input->mutex); - return 0; } @@ -1538,15 +1507,13 @@ static int mip4_resume(struct device *dev) struct mip4_ts *ts = i2c_get_clientdata(client); struct input_dev *input = ts->input; - mutex_lock(&input->mutex); + guard(mutex)(&input->mutex); if (ts->wake_irq_enabled) disable_irq_wake(client->irq); else if (input_device_enabled(input)) mip4_enable(ts); - mutex_unlock(&input->mutex); - return 0; } -- cgit v1.2.3 From 7e1e5722e859e6624c9509a55d1f302b44d1853b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 1 Sep 2024 16:11:38 -0700 Subject: Input: mk712 - use guard notation when acquiring spinlock Using guard notation makes the code more compact and error handling more robust by ensuring that locks are released in all code paths when control leaves critical section. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/mk712.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/drivers/input/touchscreen/mk712.c b/drivers/input/touchscreen/mk712.c index 753d9cc1de1f..a36fea2d5a4e 100644 --- a/drivers/input/touchscreen/mk712.c +++ b/drivers/input/touchscreen/mk712.c @@ -82,7 +82,7 @@ static irqreturn_t mk712_interrupt(int irq, void *dev_id) static unsigned short last_x; static unsigned short last_y; - spin_lock(&mk712_lock); + guard(spinlock)(&mk712_lock); status = inb(mk712_io + MK712_STATUS); @@ -110,15 +110,13 @@ static irqreturn_t mk712_interrupt(int irq, void *dev_id) last_x = inw(mk712_io + MK712_X) & 0x0fff; last_y = inw(mk712_io + MK712_Y) & 0x0fff; input_sync(mk712_dev); - spin_unlock(&mk712_lock); + return IRQ_HANDLED; } static int mk712_open(struct input_dev *dev) { - unsigned long flags; - - spin_lock_irqsave(&mk712_lock, flags); + guard(spinlock_irqsave)(&mk712_lock); outb(0, mk712_io + MK712_CONTROL); /* Reset */ @@ -129,20 +127,14 @@ static int mk712_open(struct input_dev *dev) outb(10, mk712_io + MK712_RATE); /* 187 points per second */ - spin_unlock_irqrestore(&mk712_lock, flags); - return 0; } static void mk712_close(struct input_dev *dev) { - unsigned long flags; - - spin_lock_irqsave(&mk712_lock, flags); + guard(spinlock_irqsave)(&mk712_lock); outb(0, mk712_io + MK712_CONTROL); - - spin_unlock_irqrestore(&mk712_lock, flags); } static int __init mk712_init(void) -- cgit v1.2.3 From 11a64d6bb70bad120468f39ba1358b449dca4167 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 16 Aug 2024 13:56:28 -0700 Subject: Input: mms114 - use guard notation when acquiring mutex Guard notation simplifies code. Also stop trying to check if input device is opened/in use in the interrupt handler - the interrupt is disabled when device is closed or suspended. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/mms114.c | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index 9f947044c4d9..af462086a65c 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -216,20 +216,12 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id) { struct mms114_data *data = dev_id; struct i2c_client *client = data->client; - struct input_dev *input_dev = data->input_dev; struct mms114_touch touch[MMS114_MAX_TOUCH]; int packet_size; int touch_size; int index; int error; - mutex_lock(&input_dev->mutex); - if (!input_device_enabled(input_dev)) { - mutex_unlock(&input_dev->mutex); - goto out; - } - mutex_unlock(&input_dev->mutex); - packet_size = mms114_read_reg(data, MMS114_PACKET_SIZE); if (packet_size <= 0) goto out; @@ -646,10 +638,10 @@ static int mms114_suspend(struct device *dev) input_mt_report_pointer_emulation(input_dev, true); input_sync(input_dev); - mutex_lock(&input_dev->mutex); + guard(mutex)(&input_dev->mutex); + if (input_device_enabled(input_dev)) mms114_stop(data); - mutex_unlock(&input_dev->mutex); return 0; } @@ -661,15 +653,13 @@ static int mms114_resume(struct device *dev) struct input_dev *input_dev = data->input_dev; int error; - mutex_lock(&input_dev->mutex); + guard(mutex)(&input_dev->mutex); + if (input_device_enabled(input_dev)) { error = mms114_start(data); - if (error < 0) { - mutex_unlock(&input_dev->mutex); + if (error) return error; - } } - mutex_unlock(&input_dev->mutex); return 0; } -- cgit v1.2.3 From 03bf327434456c3bde003509d57169ea1ebbe9a3 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 17 Aug 2024 19:02:31 -0700 Subject: Input: msg2638 - use guard notation when acquiring mutex Guard notation simplifies code. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/msg2638.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/input/touchscreen/msg2638.c b/drivers/input/touchscreen/msg2638.c index a38af3fee34a..240d2eebf1c9 100644 --- a/drivers/input/touchscreen/msg2638.c +++ b/drivers/input/touchscreen/msg2638.c @@ -446,13 +446,11 @@ static int msg2638_suspend(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct msg2638_ts_data *msg2638 = i2c_get_clientdata(client); - mutex_lock(&msg2638->input_dev->mutex); + guard(mutex)(&msg2638->input_dev->mutex); if (input_device_enabled(msg2638->input_dev)) msg2638_stop(msg2638); - mutex_unlock(&msg2638->input_dev->mutex); - return 0; } @@ -460,16 +458,17 @@ static int msg2638_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct msg2638_ts_data *msg2638 = i2c_get_clientdata(client); - int ret = 0; - - mutex_lock(&msg2638->input_dev->mutex); + int error; - if (input_device_enabled(msg2638->input_dev)) - ret = msg2638_start(msg2638); + guard(mutex)(&msg2638->input_dev->mutex); - mutex_unlock(&msg2638->input_dev->mutex); + if (input_device_enabled(msg2638->input_dev)) { + error = msg2638_start(msg2638); + if (error) + return error; + } - return ret; + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(msg2638_pm_ops, msg2638_suspend, msg2638_resume); -- cgit v1.2.3 From 7c011b6ddbe8350ad9b69295520553d461fcabba Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 22 Aug 2024 17:12:08 -0700 Subject: Input: mxs-lradc-ts - use guard notation when acquiring spinlock Guard notation simplifies code and shows critical section more clearly. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/mxs-lradc-ts.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/mxs-lradc-ts.c b/drivers/input/touchscreen/mxs-lradc-ts.c index 9e36fee38d61..137ab3b60f2d 100644 --- a/drivers/input/touchscreen/mxs-lradc-ts.c +++ b/drivers/input/touchscreen/mxs-lradc-ts.c @@ -500,15 +500,14 @@ static irqreturn_t mxs_lradc_ts_handle_irq(int irq, void *data) LRADC_CTRL1_TOUCH_DETECT_IRQ | LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) | LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2); - unsigned long flags; if (!(reg & mxs_lradc_irq_mask(lradc))) return IRQ_NONE; if (reg & ts_irq_mask) { - spin_lock_irqsave(&ts->lock, flags); - mxs_lradc_handle_touch(ts); - spin_unlock_irqrestore(&ts->lock, flags); + scoped_guard(spinlock_irqsave, &ts->lock) { + mxs_lradc_handle_touch(ts); + } /* Make sure we don't clear the next conversion's interrupt. */ clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) | LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2)); -- cgit v1.2.3 From 9f33f4fd39964d13379af396f3bfe3d3617a33f5 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 17 Aug 2024 19:04:21 -0700 Subject: Input: novatek-nvt-ts - use guard notation when acquiring mutex Guard notation simplifies code. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/novatek-nvt-ts.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/novatek-nvt-ts.c b/drivers/input/touchscreen/novatek-nvt-ts.c index 3e6e2ee0ba8f..708bfb933ddd 100644 --- a/drivers/input/touchscreen/novatek-nvt-ts.c +++ b/drivers/input/touchscreen/novatek-nvt-ts.c @@ -170,10 +170,10 @@ static int nvt_ts_suspend(struct device *dev) { struct nvt_ts_data *data = i2c_get_clientdata(to_i2c_client(dev)); - mutex_lock(&data->input->mutex); + guard(mutex)(&data->input->mutex); + if (input_device_enabled(data->input)) nvt_ts_stop(data->input); - mutex_unlock(&data->input->mutex); return 0; } @@ -182,10 +182,10 @@ static int nvt_ts_resume(struct device *dev) { struct nvt_ts_data *data = i2c_get_clientdata(to_i2c_client(dev)); - mutex_lock(&data->input->mutex); + guard(mutex)(&data->input->mutex); + if (input_device_enabled(data->input)) nvt_ts_start(data->input); - mutex_unlock(&data->input->mutex); return 0; } -- cgit v1.2.3 From 738de07ddf0926b01fd8d3ba24f0c65fa5b418f5 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 17 Aug 2024 19:07:03 -0700 Subject: Input: pixcir_i2c_ts - use guard notation when acquiring mutex Guard notation simplifies code. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/pixcir_i2c_ts.c | 38 +++++++++++++++---------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index dad5786e82a4..c6b3615c8775 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -410,26 +410,25 @@ static int pixcir_i2c_ts_suspend(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct pixcir_i2c_ts_data *ts = i2c_get_clientdata(client); struct input_dev *input = ts->input; - int ret = 0; + int error; - mutex_lock(&input->mutex); + guard(mutex)(&input->mutex); if (device_may_wakeup(&client->dev)) { if (!input_device_enabled(input)) { - ret = pixcir_start(ts); - if (ret) { + error = pixcir_start(ts); + if (error) { dev_err(dev, "Failed to start\n"); - goto unlock; + return error; } } } else if (input_device_enabled(input)) { - ret = pixcir_stop(ts); + error = pixcir_stop(ts); + if (error) + return error; } -unlock: - mutex_unlock(&input->mutex); - - return ret; + return 0; } static int pixcir_i2c_ts_resume(struct device *dev) @@ -437,26 +436,25 @@ static int pixcir_i2c_ts_resume(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct pixcir_i2c_ts_data *ts = i2c_get_clientdata(client); struct input_dev *input = ts->input; - int ret = 0; + int error; - mutex_lock(&input->mutex); + guard(mutex)(&input->mutex); if (device_may_wakeup(&client->dev)) { if (!input_device_enabled(input)) { - ret = pixcir_stop(ts); - if (ret) { + error = pixcir_stop(ts); + if (error) { dev_err(dev, "Failed to stop\n"); - goto unlock; + return error; } } } else if (input_device_enabled(input)) { - ret = pixcir_start(ts); + error = pixcir_start(ts); + if (error) + return error; } -unlock: - mutex_unlock(&input->mutex); - - return ret; + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(pixcir_dev_pm_ops, -- cgit v1.2.3 From e3e82a9d08fcfebc9ece8333711658ab8d7f3684 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 29 May 2024 11:22:15 -0700 Subject: Input: raydium_i2c_ts - switch to using cleanup functions Start using __free() and guard() primitives to simplify the code and error handling. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/raydium_i2c_ts.c | 56 +++++++++++++----------------- 1 file changed, 24 insertions(+), 32 deletions(-) diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c index f975b53e8825..f2d33ad86fd2 100644 --- a/drivers/input/touchscreen/raydium_i2c_ts.c +++ b/drivers/input/touchscreen/raydium_i2c_ts.c @@ -169,10 +169,9 @@ static int raydium_i2c_send(struct i2c_client *client, { int tries = 0; int error; - u8 *tx_buf; u8 reg_addr = addr & 0xff; - tx_buf = kmalloc(len + 1, GFP_KERNEL); + u8 *tx_buf __free(kfree) = kmalloc(len + 1, GFP_KERNEL); if (!tx_buf) return -ENOMEM; @@ -210,14 +209,12 @@ static int raydium_i2c_send(struct i2c_client *client, error = raydium_i2c_xfer(client, addr, xfer, ARRAY_SIZE(xfer)); if (likely(!error)) - goto out; + return 0; msleep(RM_RETRY_DELAY_MS); } while (++tries < RM_MAX_RETRIES); dev_err(&client->dev, "%s failed: %d\n", __func__, error); -out: - kfree(tx_buf); return error; } @@ -815,21 +812,21 @@ static int raydium_i2c_do_update_firmware(struct raydium_data *ts, static int raydium_i2c_fw_update(struct raydium_data *ts) { struct i2c_client *client = ts->client; - const struct firmware *fw = NULL; - char *fw_file; int error; - fw_file = kasprintf(GFP_KERNEL, "raydium_%#04x.fw", - le32_to_cpu(ts->info.hw_ver)); + const char *fw_file __free(kfree) = + kasprintf(GFP_KERNEL, "raydium_%#04x.fw", + le32_to_cpu(ts->info.hw_ver)); if (!fw_file) return -ENOMEM; dev_dbg(&client->dev, "firmware name: %s\n", fw_file); + const struct firmware *fw __free(firmware) = NULL; error = request_firmware(&fw, fw_file, &client->dev); if (error) { dev_err(&client->dev, "Unable to open firmware %s\n", fw_file); - goto out_free_fw_file; + return error; } disable_irq(client->irq); @@ -856,11 +853,6 @@ out_enable_irq: enable_irq(client->irq); msleep(100); - release_firmware(fw); - -out_free_fw_file: - kfree(fw_file); - return error; } @@ -965,15 +957,12 @@ static ssize_t raydium_i2c_update_fw_store(struct device *dev, struct raydium_data *ts = i2c_get_clientdata(client); int error; - error = mutex_lock_interruptible(&ts->sysfs_mutex); - if (error) - return error; - - error = raydium_i2c_fw_update(ts); - - mutex_unlock(&ts->sysfs_mutex); + scoped_guard(mutex_intr, &ts->sysfs_mutex) { + error = raydium_i2c_fw_update(ts); + return error ?: count; + } - return error ?: count; + return -EINTR; } static ssize_t raydium_i2c_calibrate_store(struct device *dev, @@ -985,17 +974,20 @@ static ssize_t raydium_i2c_calibrate_store(struct device *dev, static const u8 cal_cmd[] = { 0x00, 0x01, 0x9E }; int error; - error = mutex_lock_interruptible(&ts->sysfs_mutex); - if (error) - return error; + scoped_guard(mutex_intr, &ts->sysfs_mutex) { + error = raydium_i2c_write_object(client, + cal_cmd, sizeof(cal_cmd), + RAYDIUM_WAIT_READY); + if (error) { + dev_err(&client->dev, + "calibrate command failed: %d\n", error); + return error; + } - error = raydium_i2c_write_object(client, cal_cmd, sizeof(cal_cmd), - RAYDIUM_WAIT_READY); - if (error) - dev_err(&client->dev, "calibrate command failed: %d\n", error); + return count; + } - mutex_unlock(&ts->sysfs_mutex); - return error ?: count; + return -EINTR; } static DEVICE_ATTR(fw_version, S_IRUGO, raydium_i2c_fw_ver_show, NULL); -- cgit v1.2.3 From 8665ceb926ec9d302ca94e46a2fe07afc08f56d0 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 16 Aug 2024 14:09:22 -0700 Subject: Input: stmfts - use guard notation when acquiring mutex Guard notation simplifies code. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/stmfts.c | 63 +++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c index 4b166b0a9a5a..8af87d0b6eb6 100644 --- a/drivers/input/touchscreen/stmfts.c +++ b/drivers/input/touchscreen/stmfts.c @@ -302,7 +302,7 @@ static irqreturn_t stmfts_irq_handler(int irq, void *dev) struct stmfts_data *sdata = dev; int err; - mutex_lock(&sdata->mutex); + guard(mutex)(&sdata->mutex); err = stmfts_read_events(sdata); if (unlikely(err)) @@ -311,7 +311,6 @@ static irqreturn_t stmfts_irq_handler(int irq, void *dev) else stmfts_parse_events(sdata); - mutex_unlock(&sdata->mutex); return IRQ_HANDLED; } @@ -347,17 +346,17 @@ static int stmfts_input_open(struct input_dev *dev) return err; } - mutex_lock(&sdata->mutex); - sdata->running = true; + scoped_guard(mutex, &sdata->mutex) { + sdata->running = true; - if (sdata->hover_enabled) { - err = i2c_smbus_write_byte(sdata->client, - STMFTS_SS_HOVER_SENSE_ON); - if (err) - dev_warn(&sdata->client->dev, - "failed to enable hover\n"); + if (sdata->hover_enabled) { + err = i2c_smbus_write_byte(sdata->client, + STMFTS_SS_HOVER_SENSE_ON); + if (err) + dev_warn(&sdata->client->dev, + "failed to enable hover\n"); + } } - mutex_unlock(&sdata->mutex); if (sdata->use_key) { err = i2c_smbus_write_byte(sdata->client, @@ -381,18 +380,17 @@ static void stmfts_input_close(struct input_dev *dev) dev_warn(&sdata->client->dev, "failed to disable touchscreen: %d\n", err); - mutex_lock(&sdata->mutex); + scoped_guard(mutex, &sdata->mutex) { + sdata->running = false; - sdata->running = false; - - if (sdata->hover_enabled) { - err = i2c_smbus_write_byte(sdata->client, - STMFTS_SS_HOVER_SENSE_OFF); - if (err) - dev_warn(&sdata->client->dev, - "failed to disable hover: %d\n", err); + if (sdata->hover_enabled) { + err = i2c_smbus_write_byte(sdata->client, + STMFTS_SS_HOVER_SENSE_OFF); + if (err) + dev_warn(&sdata->client->dev, + "failed to disable hover: %d\n", err); + } } - mutex_unlock(&sdata->mutex); if (sdata->use_key) { err = i2c_smbus_write_byte(sdata->client, @@ -474,26 +472,27 @@ static ssize_t stmfts_sysfs_hover_enable_write(struct device *dev, { struct stmfts_data *sdata = dev_get_drvdata(dev); unsigned long value; - int err = 0; + bool hover; + int err; if (kstrtoul(buf, 0, &value)) return -EINVAL; - mutex_lock(&sdata->mutex); + hover = !!value; - if (value && sdata->hover_enabled) - goto out; + guard(mutex)(&sdata->mutex); - if (sdata->running) - err = i2c_smbus_write_byte(sdata->client, + if (hover != sdata->hover_enabled) { + if (sdata->running) { + err = i2c_smbus_write_byte(sdata->client, value ? STMFTS_SS_HOVER_SENSE_ON : STMFTS_SS_HOVER_SENSE_OFF); + if (err) + return err; + } - if (!err) - sdata->hover_enabled = !!value; - -out: - mutex_unlock(&sdata->mutex); + sdata->hover_enabled = hover; + } return len; } -- cgit v1.2.3 From dc05a01180814440aee1959721528012dcda4461 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 14 Aug 2024 14:38:11 -0700 Subject: Input: sur40 - use guard notation when acquiring spinlock Guard notation simplifies code. Also use list_first_entry() instead of list_entry() to emphasize intent. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/sur40.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index 877eae34fb5a..fe63d53d56db 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -538,15 +538,15 @@ static void sur40_process_video(struct sur40_state *sur40) return; /* get a new buffer from the list */ - spin_lock(&sur40->qlock); - if (list_empty(&sur40->buf_list)) { - dev_dbg(sur40->dev, "buffer queue empty\n"); - spin_unlock(&sur40->qlock); - return; + scoped_guard(spinlock, &sur40->qlock) { + if (list_empty(&sur40->buf_list)) { + dev_dbg(sur40->dev, "buffer queue empty\n"); + return; + } + new_buf = list_first_entry(&sur40->buf_list, + struct sur40_buffer, list); + list_del(&new_buf->list); } - new_buf = list_entry(sur40->buf_list.next, struct sur40_buffer, list); - list_del(&new_buf->list); - spin_unlock(&sur40->qlock); dev_dbg(sur40->dev, "buffer acquired\n"); @@ -888,9 +888,8 @@ static void sur40_buffer_queue(struct vb2_buffer *vb) struct sur40_state *sur40 = vb2_get_drv_priv(vb->vb2_queue); struct sur40_buffer *buf = (struct sur40_buffer *)vb; - spin_lock(&sur40->qlock); + guard(spinlock)(&sur40->qlock); list_add_tail(&buf->list, &sur40->buf_list); - spin_unlock(&sur40->qlock); } static void return_all_buffers(struct sur40_state *sur40, @@ -898,12 +897,12 @@ static void return_all_buffers(struct sur40_state *sur40, { struct sur40_buffer *buf, *node; - spin_lock(&sur40->qlock); + guard(spinlock)(&sur40->qlock); + list_for_each_entry_safe(buf, node, &sur40->buf_list, list) { vb2_buffer_done(&buf->vb.vb2_buf, state); list_del(&buf->list); } - spin_unlock(&sur40->qlock); } /* -- cgit v1.2.3 From a8f56931c432c8c9a5198887bdd0f5d181b75495 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 14 Aug 2024 14:13:33 -0700 Subject: Input: sx8654 - use guard notation when acquiring spinlock Guard notation simplifies code. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/sx8654.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c index 5fa47a1a6fdc..b5fe750e42ad 100644 --- a/drivers/input/touchscreen/sx8654.c +++ b/drivers/input/touchscreen/sx8654.c @@ -117,12 +117,11 @@ static inline void sx865x_penrelease(struct sx8654 *ts) static void sx865x_penrelease_timer_handler(struct timer_list *t) { struct sx8654 *ts = timer_container_of(ts, t, timer); - unsigned long flags; - spin_lock_irqsave(&ts->lock, flags); - sx865x_penrelease(ts); - spin_unlock_irqrestore(&ts->lock, flags); dev_dbg(&ts->client->dev, "penrelease by timer\n"); + + guard(spinlock_irqsave)(&ts->lock); + sx865x_penrelease(ts); } static irqreturn_t sx8650_irq(int irq, void *handle) @@ -130,7 +129,6 @@ static irqreturn_t sx8650_irq(int irq, void *handle) struct sx8654 *ts = handle; struct device *dev = &ts->client->dev; int len, i; - unsigned long flags; u8 stat; u16 x, y; u16 ch; @@ -153,7 +151,7 @@ static irqreturn_t sx8650_irq(int irq, void *handle) return IRQ_HANDLED; } - spin_lock_irqsave(&ts->lock, flags); + guard(spinlock_irqsave)(&ts->lock); x = 0; y = 0; @@ -184,7 +182,6 @@ static irqreturn_t sx8650_irq(int irq, void *handle) dev_dbg(dev, "point(%4d,%4d)\n", x, y); mod_timer(&ts->timer, jiffies + SX8650_PENIRQ_TIMEOUT); - spin_unlock_irqrestore(&ts->lock, flags); return IRQ_HANDLED; } -- cgit v1.2.3 From 600a2db76bf28ab791774ed378d74c50f16e24d5 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 14 Aug 2024 14:22:13 -0700 Subject: Input: sx8654 - use IRQF_NOAUTOEN when requesting interrupt Instead of requesting interrupt normally and immediately disabling it with call to disable_irq() use IRQF_NOAUTOEN to keep it disabled until it is needed. This avoids a tiny window when interrupt is enabled but not needed. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/sx8654.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c index b5fe750e42ad..0d92aaeea3e0 100644 --- a/drivers/input/touchscreen/sx8654.c +++ b/drivers/input/touchscreen/sx8654.c @@ -391,9 +391,13 @@ static int sx8654_probe(struct i2c_client *client) return error; } + /* + * Start with the interrupt disabled, it will be enabled in + * sx8654_open(). + */ error = devm_request_threaded_irq(&client->dev, client->irq, NULL, sx8654->data->irqh, - IRQF_ONESHOT, + IRQF_ONESHOT | IRQF_NO_AUTOEN, client->name, sx8654); if (error) { dev_err(&client->dev, @@ -402,9 +406,6 @@ static int sx8654_probe(struct i2c_client *client) return error; } - /* Disable the IRQ, we'll enable it in sx8654_open() */ - disable_irq(client->irq); - error = input_register_device(sx8654->input); if (error) return error; -- cgit v1.2.3 From e65407f838368fb7ab98fb7f02cbf5b9377976d3 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 6 Aug 2024 16:48:00 -0700 Subject: Input: tsc2007 - use guard notation when acquiring mutexes This makes the code more compact and error handling more robust. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/tsc2007_core.c | 7 ++++--- drivers/input/touchscreen/tsc2007_iio.c | 9 ++------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/input/touchscreen/tsc2007_core.c b/drivers/input/touchscreen/tsc2007_core.c index 948935de894b..524f14eb3da2 100644 --- a/drivers/input/touchscreen/tsc2007_core.c +++ b/drivers/input/touchscreen/tsc2007_core.c @@ -122,9 +122,10 @@ static irqreturn_t tsc2007_soft_irq(int irq, void *handle) /* pen is down, continue with the measurement */ - mutex_lock(&ts->mlock); - tsc2007_read_values(ts, &tc); - mutex_unlock(&ts->mlock); + /* Serialize access between the ISR and IIO reads. */ + scoped_guard(mutex, &ts->mlock) { + tsc2007_read_values(ts, &tc); + } rt = tsc2007_calculate_resistance(ts, &tc); diff --git a/drivers/input/touchscreen/tsc2007_iio.c b/drivers/input/touchscreen/tsc2007_iio.c index 752eb7fe5da3..e9e495ec0b6c 100644 --- a/drivers/input/touchscreen/tsc2007_iio.c +++ b/drivers/input/touchscreen/tsc2007_iio.c @@ -41,7 +41,6 @@ static int tsc2007_read_raw(struct iio_dev *indio_dev, struct tsc2007_iio *iio = iio_priv(indio_dev); struct tsc2007 *tsc = iio->ts; int adc_chan = chan->channel; - int ret = 0; if (adc_chan >= ARRAY_SIZE(tsc2007_iio_channel)) return -EINVAL; @@ -49,7 +48,7 @@ static int tsc2007_read_raw(struct iio_dev *indio_dev, if (mask != IIO_CHAN_INFO_RAW) return -EINVAL; - mutex_lock(&tsc->mlock); + guard(mutex)(&tsc->mlock); switch (chan->channel) { case 0: @@ -92,11 +91,7 @@ static int tsc2007_read_raw(struct iio_dev *indio_dev, /* Prepare for next touch reading - power down ADC, enable PENIRQ */ tsc2007_xfer(tsc, PWRDOWN); - mutex_unlock(&tsc->mlock); - - ret = IIO_VAL_INT; - - return ret; + return IIO_VAL_INT; } static const struct iio_info tsc2007_iio_info = { -- cgit v1.2.3 From da52f4b27a798304a7f4639ac5addc5574242cdf Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 29 May 2024 11:08:15 -0700 Subject: Input: wdt87xx_i2c - switch to using cleanup functions Start using __free() and guard() primitives to simplify the code and error handling. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wdt87xx_i2c.c | 44 ++++++++++++++++----------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/drivers/input/touchscreen/wdt87xx_i2c.c b/drivers/input/touchscreen/wdt87xx_i2c.c index 1e2a6bbb88cb..bdaabb14dc8c 100644 --- a/drivers/input/touchscreen/wdt87xx_i2c.c +++ b/drivers/input/touchscreen/wdt87xx_i2c.c @@ -813,56 +813,46 @@ static int wdt87xx_load_chunk(struct i2c_client *client, return 0; } -static int wdt87xx_do_update_firmware(struct i2c_client *client, +static int wdt87xx_do_update_firmware(struct wdt87xx_data *wdt, const struct firmware *fw, unsigned int chunk_id) { - struct wdt87xx_data *wdt = i2c_get_clientdata(client); + struct i2c_client *client = wdt->client; int error; - error = wdt87xx_validate_firmware(wdt, fw); - if (error) - return error; - - error = mutex_lock_interruptible(&wdt->fw_mutex); - if (error) - return error; - - disable_irq(client->irq); - error = wdt87xx_load_chunk(client, fw, chunk_id); if (error) { dev_err(&client->dev, "firmware load failed (type: %d): %d\n", chunk_id, error); - goto out; + return error; } error = wdt87xx_sw_reset(client); if (error) { dev_err(&client->dev, "soft reset failed: %d\n", error); - goto out; + return error; } /* Refresh the parameters */ error = wdt87xx_get_sysparam(client, &wdt->param); - if (error) + if (error) { dev_err(&client->dev, "failed to refresh system parameters: %d\n", error); -out: - enable_irq(client->irq); - mutex_unlock(&wdt->fw_mutex); + return error; + } - return error ? error : 0; + return 0; } static int wdt87xx_update_firmware(struct device *dev, const char *fw_name, unsigned int chunk_id) { struct i2c_client *client = to_i2c_client(dev); - const struct firmware *fw; + struct wdt87xx_data *wdt = i2c_get_clientdata(client); int error; + const struct firmware *fw __free(firmware) = NULL; error = request_firmware(&fw, fw_name, dev); if (error) { dev_err(&client->dev, "unable to retrieve firmware %s: %d\n", @@ -870,11 +860,19 @@ static int wdt87xx_update_firmware(struct device *dev, return error; } - error = wdt87xx_do_update_firmware(client, fw, chunk_id); + error = wdt87xx_validate_firmware(wdt, fw); + if (error) + return error; + + scoped_cond_guard(mutex_intr, return -EINTR, &wdt->fw_mutex) { + guard(disable_irq)(&client->irq); - release_firmware(fw); + error = wdt87xx_do_update_firmware(wdt, fw, chunk_id); + if (error) + return error; + } - return error ? error : 0; + return 0; } static ssize_t config_csum_show(struct device *dev, -- cgit v1.2.3 From 35ee82990df219f6f42962c0306c00231b85f238 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 16 Aug 2024 15:18:44 -0700 Subject: Input: wm97xx - use guard notation when acquiring mutex Guard notation simplifies code. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wm97xx-core.c | 57 +++++++++++++-------------------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index 96354c44af87..c51822563f3f 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -126,7 +126,7 @@ int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel) int timeout = 0; /* get codec */ - mutex_lock(&wm->codec_mutex); + guard(mutex)(&wm->codec_mutex); /* When the touchscreen is not in use, we may have to power up * the AUX ADC before we can use sample the AUX inputs-> @@ -160,7 +160,6 @@ int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel) wm->codec->dig_enable(wm, false); } - mutex_unlock(&wm->codec_mutex); return (rc == RC_VALID ? auxval & 0xfff : -EBUSY); } EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc); @@ -176,18 +175,11 @@ EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc); enum wm97xx_gpio_status wm97xx_get_gpio(struct wm97xx *wm, u32 gpio) { u16 status; - enum wm97xx_gpio_status ret; - mutex_lock(&wm->codec_mutex); - status = wm97xx_reg_read(wm, AC97_GPIO_STATUS); - - if (status & gpio) - ret = WM97XX_GPIO_HIGH; - else - ret = WM97XX_GPIO_LOW; + guard(mutex)(&wm->codec_mutex); - mutex_unlock(&wm->codec_mutex); - return ret; + status = wm97xx_reg_read(wm, AC97_GPIO_STATUS); + return (status & gpio) ? WM97XX_GPIO_HIGH : WM97XX_GPIO_LOW; } EXPORT_SYMBOL_GPL(wm97xx_get_gpio); @@ -205,7 +197,8 @@ void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio, { u16 reg; - mutex_lock(&wm->codec_mutex); + guard(mutex)(&wm->codec_mutex); + reg = wm97xx_reg_read(wm, AC97_GPIO_STATUS); if (status == WM97XX_GPIO_HIGH) @@ -217,7 +210,6 @@ void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio, wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg << 1); else wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg); - mutex_unlock(&wm->codec_mutex); } EXPORT_SYMBOL_GPL(wm97xx_set_gpio); @@ -231,7 +223,8 @@ void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, enum wm97xx_gpio_dir dir, { u16 reg; - mutex_lock(&wm->codec_mutex); + guard(mutex)(&wm->codec_mutex); + reg = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); if (pol == WM97XX_GPIO_POL_HIGH) @@ -264,7 +257,6 @@ void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, enum wm97xx_gpio_dir dir, reg &= ~gpio; wm97xx_reg_write(wm, AC97_GPIO_CFG, reg); - mutex_unlock(&wm->codec_mutex); } EXPORT_SYMBOL_GPL(wm97xx_config_gpio); @@ -303,7 +295,9 @@ static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id) wm->pen_is_down = 0; } else { u16 status, pol; - mutex_lock(&wm->codec_mutex); + + guard(mutex)(&wm->codec_mutex); + status = wm97xx_reg_read(wm, AC97_GPIO_STATUS); pol = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); @@ -323,7 +317,6 @@ static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id) else wm97xx_reg_write(wm, AC97_GPIO_STATUS, status & ~WM97XX_GPIO_13); - mutex_unlock(&wm->codec_mutex); } /* If the system is not using continuous mode or it provides a @@ -382,7 +375,7 @@ static int wm97xx_read_samples(struct wm97xx *wm) struct wm97xx_data data; int rc; - mutex_lock(&wm->codec_mutex); + guard(mutex)(&wm->codec_mutex); if (wm->mach_ops && wm->mach_ops->acc_enabled) rc = wm->mach_ops->acc_pen_down(wm); @@ -422,8 +415,7 @@ static int wm97xx_read_samples(struct wm97xx *wm) abs_y[0] > (data.y & 0xfff) || abs_y[1] < (data.y & 0xfff)) { dev_dbg(wm->dev, "Measurement out of range, dropping it\n"); - rc = RC_AGAIN; - goto out; + return RC_AGAIN; } input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff); @@ -439,8 +431,6 @@ static int wm97xx_read_samples(struct wm97xx *wm) wm->ts_reader_interval = wm->ts_reader_min_interval; } -out: - mutex_unlock(&wm->codec_mutex); return rc; } @@ -773,7 +763,8 @@ static int wm97xx_suspend(struct device *dev) else suspend_mode = 0; - mutex_lock(&wm->input_dev->mutex); + guard(mutex)(&wm->input_dev->mutex); + if (input_device_enabled(wm->input_dev)) cancel_delayed_work_sync(&wm->ts_reader); @@ -791,7 +782,6 @@ static int wm97xx_suspend(struct device *dev) reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) | 0x8000; wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg); } - mutex_unlock(&wm->input_dev->mutex); return 0; } @@ -800,7 +790,8 @@ static int wm97xx_resume(struct device *dev) { struct wm97xx *wm = dev_get_drvdata(dev); - mutex_lock(&wm->input_dev->mutex); + guard(mutex)(&wm->input_dev->mutex); + /* restore digitiser and gpios */ if (wm->id == WM9713_ID2) { wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig[0]); @@ -827,7 +818,6 @@ static int wm97xx_resume(struct device *dev) queue_delayed_work(wm->ts_workq, &wm->ts_reader, wm->ts_reader_interval); } - mutex_unlock(&wm->input_dev->mutex); return 0; } @@ -840,13 +830,12 @@ static DEFINE_SIMPLE_DEV_PM_OPS(wm97xx_pm_ops, wm97xx_suspend, wm97xx_resume); int wm97xx_register_mach_ops(struct wm97xx *wm, struct wm97xx_mach_ops *mach_ops) { - mutex_lock(&wm->codec_mutex); - if (wm->mach_ops) { - mutex_unlock(&wm->codec_mutex); + guard(mutex)(&wm->codec_mutex); + + if (wm->mach_ops) return -EINVAL; - } + wm->mach_ops = mach_ops; - mutex_unlock(&wm->codec_mutex); return 0; } @@ -854,9 +843,9 @@ EXPORT_SYMBOL_GPL(wm97xx_register_mach_ops); void wm97xx_unregister_mach_ops(struct wm97xx *wm) { - mutex_lock(&wm->codec_mutex); + guard(mutex)(&wm->codec_mutex); + wm->mach_ops = NULL; - mutex_unlock(&wm->codec_mutex); } EXPORT_SYMBOL_GPL(wm97xx_unregister_mach_ops); -- cgit v1.2.3 From 79df764dbecd5c4bf1b1431b865a361ce7bebb2d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 17 Aug 2024 19:08:21 -0700 Subject: Input: zinitix - use guard notation when acquiring mutex Guard notation simplifies code. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/zinitix.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/input/touchscreen/zinitix.c b/drivers/input/touchscreen/zinitix.c index 716d6fa60f86..0c36765bd79f 100644 --- a/drivers/input/touchscreen/zinitix.c +++ b/drivers/input/touchscreen/zinitix.c @@ -703,13 +703,11 @@ static int zinitix_suspend(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct bt541_ts_data *bt541 = i2c_get_clientdata(client); - mutex_lock(&bt541->input_dev->mutex); + guard(mutex)(&bt541->input_dev->mutex); if (input_device_enabled(bt541->input_dev)) zinitix_stop(bt541); - mutex_unlock(&bt541->input_dev->mutex); - return 0; } @@ -717,16 +715,17 @@ static int zinitix_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct bt541_ts_data *bt541 = i2c_get_clientdata(client); - int ret = 0; - - mutex_lock(&bt541->input_dev->mutex); + int error; - if (input_device_enabled(bt541->input_dev)) - ret = zinitix_start(bt541); + guard(mutex)(&bt541->input_dev->mutex); - mutex_unlock(&bt541->input_dev->mutex); + if (input_device_enabled(bt541->input_dev)) { + error = zinitix_start(bt541); + if (error) + return error; + } - return ret; + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(zinitix_pm_ops, zinitix_suspend, zinitix_resume); -- cgit v1.2.3 From 653f3100f551cf01974a18cce66e368f248ee48a Mon Sep 17 00:00:00 2001 From: Val Packett Date: Sat, 21 Mar 2026 04:30:07 -0300 Subject: Input: goodix-berlin - report a resolution of 10 units/mm Without a reported resolution, userspace was assuming 1 unit/mm which is wildly wrong: a regular smartphone is clearly not 2.4 meters tall. Most applications do not care much for this kind of raw mm value, but Phosh's on-screen keyboard would accidentally trigger swipe-to-close gestures due to misinterpreting small movements as huge ones. Do what the older goodix.c driver does and set the resolution to 10 units/mm to make sure the numbers calculated by userspace are reasonable. Signed-off-by: Val Packett Link: https://patch.msgid.link/20260321073242.556253-1-val@packett.cool Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/goodix_berlin_core.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/input/touchscreen/goodix_berlin_core.c b/drivers/input/touchscreen/goodix_berlin_core.c index 83f28b870531..b0938a4f3fec 100644 --- a/drivers/input/touchscreen/goodix_berlin_core.c +++ b/drivers/input/touchscreen/goodix_berlin_core.c @@ -628,6 +628,14 @@ static int goodix_berlin_input_dev_config(struct goodix_berlin_core *cd, touchscreen_parse_properties(cd->input_dev, true, &cd->props); + /* + * The resolution of these touchscreens is about 10 units/mm, the actual + * resolution does not matter much since we set INPUT_PROP_DIRECT. + * Set it to 10 to ensure userspace isn't off by an order of magnitude. + */ + input_abs_set_res(cd->input_dev, ABS_MT_POSITION_X, 10); + input_abs_set_res(cd->input_dev, ABS_MT_POSITION_Y, 10); + error = input_mt_init_slots(cd->input_dev, GOODIX_BERLIN_MAX_TOUCH, INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); if (error) -- cgit v1.2.3 From ffd01c3bcc1af4d8c3e7949152af0d9fe3d1fda5 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 25 Mar 2026 15:32:46 +0100 Subject: Input: aiptek - use HID headers The driver uses its own definitions for HID requests. This leads to duplication and obfuscation. Use HID's definitions. Signed-off-by: Oliver Neukum Link: https://patch.msgid.link/20260325143256.371854-1-oneukum@suse.com Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/aiptek.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/input/tablet/aiptek.c b/drivers/input/tablet/aiptek.c index 6df24cee3c9d..1ad3c19aa155 100644 --- a/drivers/input/tablet/aiptek.c +++ b/drivers/input/tablet/aiptek.c @@ -57,6 +57,7 @@ * http://aiptektablet.sourceforge.net. */ +#include #include #include #include @@ -164,8 +165,6 @@ #define USB_VENDOR_ID_AIPTEK 0x08ca #define USB_VENDOR_ID_KYE 0x0458 -#define USB_REQ_GET_REPORT 0x01 -#define USB_REQ_SET_REPORT 0x09 /* PointerMode codes */ @@ -856,7 +855,7 @@ aiptek_set_report(struct aiptek *aiptek, return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - USB_REQ_SET_REPORT, + HID_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, (report_type << 8) + report_id, aiptek->ifnum, buffer, size, 5000); @@ -871,7 +870,7 @@ aiptek_get_report(struct aiptek *aiptek, return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - USB_REQ_GET_REPORT, + HID_REQ_GET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, (report_type << 8) + report_id, aiptek->ifnum, buffer, size, 5000); -- cgit v1.2.3 From 734fd5ba78cb079ba1873336387da8cb4df60403 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 25 Mar 2026 15:32:47 +0100 Subject: Input: pegasus_notetaker - use HID defines The driver uses its own definitions for HID requests. This leads to duplication and obfuscation. Use HID's definitions. Signed-off-by: Oliver Neukum Link: https://patch.msgid.link/20260325143256.371854-2-oneukum@suse.com Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/pegasus_notetaker.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/input/tablet/pegasus_notetaker.c b/drivers/input/tablet/pegasus_notetaker.c index 4ce20befc657..85390ae42307 100644 --- a/drivers/input/tablet/pegasus_notetaker.c +++ b/drivers/input/tablet/pegasus_notetaker.c @@ -36,6 +36,7 @@ * T Tip */ +#include #include #include #include @@ -44,10 +45,6 @@ #include #include -/* USB HID defines */ -#define USB_REQ_GET_REPORT 0x01 -#define USB_REQ_SET_REPORT 0x09 - #define USB_VENDOR_ID_PEGASUSTECH 0x0e20 #define USB_DEVICE_ID_PEGASUS_NOTETAKER_EN100 0x0101 @@ -108,7 +105,7 @@ static int pegasus_control_msg(struct pegasus *pegasus, u8 *data, int len) result = usb_control_msg(pegasus->usbdev, usb_sndctrlpipe(pegasus->usbdev, 0), - USB_REQ_SET_REPORT, + HID_REQ_SET_REPORT, USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, cmd_buf, sizeof_buf, USB_CTRL_SET_TIMEOUT); -- cgit v1.2.3 From a7675c1f1fa38c559f267b0452ae9d7a736fa743 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 30 Mar 2026 11:59:45 +0200 Subject: Input: keyspan-remote - refactor endpoint lookup Use the common USB helper for looking up interrupt-in endpoints instead of open coding. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260330095948.1663141-2-johan@kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/misc/keyspan_remote.c | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/drivers/input/misc/keyspan_remote.c b/drivers/input/misc/keyspan_remote.c index 152633bd2266..70cd6586459e 100644 --- a/drivers/input/misc/keyspan_remote.c +++ b/drivers/input/misc/keyspan_remote.c @@ -420,24 +420,6 @@ static void keyspan_close(struct input_dev *dev) usb_kill_urb(remote->irq_urb); } -static struct usb_endpoint_descriptor *keyspan_get_in_endpoint(struct usb_host_interface *iface) -{ - - struct usb_endpoint_descriptor *endpoint; - int i; - - for (i = 0; i < iface->desc.bNumEndpoints; ++i) { - endpoint = &iface->endpoint[i].desc; - - if (usb_endpoint_is_int_in(endpoint)) { - /* we found our interrupt in endpoint */ - return endpoint; - } - } - - return NULL; -} - /* * Routine that sets up the driver to handle a specific USB device detected on the bus. */ @@ -449,8 +431,8 @@ static int keyspan_probe(struct usb_interface *interface, const struct usb_devic struct input_dev *input_dev; int i, error; - endpoint = keyspan_get_in_endpoint(interface->cur_altsetting); - if (!endpoint) + error = usb_find_int_in_endpoint(interface->cur_altsetting, &endpoint); + if (error) return -ENODEV; remote = kzalloc_obj(*remote); -- cgit v1.2.3 From 5bb3ab0daaabedb67b142835b6905bce126df6ec Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 30 Mar 2026 11:59:46 +0200 Subject: Input: appletouch - refactor endpoint lookup Use the common USB helpers for looking up interrupt-in endpoints (and determining endpoint numbers) instead of open coding. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260330095948.1663141-3-johan@kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/appletouch.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c index 87d8f5afdfd9..eebeb57515e1 100644 --- a/drivers/input/mouse/appletouch.c +++ b/drivers/input/mouse/appletouch.c @@ -829,29 +829,20 @@ static int atp_probe(struct usb_interface *iface, struct atp *dev; struct input_dev *input_dev; struct usb_device *udev = interface_to_usbdev(iface); - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - int int_in_endpointAddr = 0; - int i, error = -ENOMEM; + struct usb_endpoint_descriptor *ep; + int error; const struct atp_info *info = (const struct atp_info *)id->driver_info; /* set up the endpoint information */ /* use only the first interrupt-in endpoint */ - iface_desc = iface->cur_altsetting; - for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { - endpoint = &iface_desc->endpoint[i].desc; - if (!int_in_endpointAddr && usb_endpoint_is_int_in(endpoint)) { - /* we found an interrupt in endpoint */ - int_in_endpointAddr = endpoint->bEndpointAddress; - break; - } - } - if (!int_in_endpointAddr) { + error = usb_find_int_in_endpoint(iface->cur_altsetting, &ep); + if (error) { dev_err(&iface->dev, "Could not find int-in endpoint\n"); return -EIO; } /* allocate memory for our device state and initialize it */ + error = -ENOMEM; dev = kzalloc_obj(*dev); input_dev = input_allocate_device(); if (!dev || !input_dev) { @@ -875,7 +866,7 @@ static int atp_probe(struct usb_interface *iface, goto err_free_urb; usb_fill_int_urb(dev->urb, udev, - usb_rcvintpipe(udev, int_in_endpointAddr), + usb_rcvintpipe(udev, usb_endpoint_num(ep)), dev->data, dev->info->datalen, dev->info->callback, dev, 1); -- cgit v1.2.3 From 4decd8f4ae06a6d82079186b6ad3fe51d4654a1d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 30 Mar 2026 11:59:47 +0200 Subject: Input: synaptics_usb - refactor endpoint lookup Use the common USB helper for looking up interrupt-in endpoints instead of open coding. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260330095948.1663141-4-johan@kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics_usb.c | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/drivers/input/mouse/synaptics_usb.c b/drivers/input/mouse/synaptics_usb.c index 5a86f6f387d8..880a0c79148c 100644 --- a/drivers/input/mouse/synaptics_usb.c +++ b/drivers/input/mouse/synaptics_usb.c @@ -220,25 +220,6 @@ resubmit: __func__, error); } -static struct usb_endpoint_descriptor * -synusb_get_in_endpoint(struct usb_host_interface *iface) -{ - - struct usb_endpoint_descriptor *endpoint; - int i; - - for (i = 0; i < iface->desc.bNumEndpoints; ++i) { - endpoint = &iface->endpoint[i].desc; - - if (usb_endpoint_is_int_in(endpoint)) { - /* we found our interrupt in endpoint */ - return endpoint; - } - } - - return NULL; -} - static int synusb_open(struct input_dev *dev) { struct synusb *synusb = input_get_drvdata(dev); @@ -307,8 +288,8 @@ static int synusb_probe(struct usb_interface *intf, return error; } - ep = synusb_get_in_endpoint(intf->cur_altsetting); - if (!ep) + error = usb_find_int_in_endpoint(intf->cur_altsetting, &ep); + if (error) return -ENODEV; synusb = kzalloc_obj(*synusb); -- cgit v1.2.3 From 53ba7a47d3783192e6846cbb42f246f0fb9f9488 Mon Sep 17 00:00:00 2001 From: Griffin Kroah-Hartman Date: Tue, 3 Mar 2026 17:37:18 -0800 Subject: Input: aw86927 - respect vibration magnitude levels Previously the gain value was hardcoded. Take the magnitude passed via the input API and configure the gain register accordingly. Signed-off-by: Griffin Kroah-Hartman Link: https://patch.msgid.link/20260302-aw86938-driver-v4-1-92c865df9cca@fairphone.com Signed-off-by: Dmitry Torokhov --- drivers/input/misc/aw86927.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/input/misc/aw86927.c b/drivers/input/misc/aw86927.c index 8ad361239cfe..454e1af23df0 100644 --- a/drivers/input/misc/aw86927.c +++ b/drivers/input/misc/aw86927.c @@ -180,7 +180,7 @@ struct aw86927_data { struct i2c_client *client; struct regmap *regmap; struct gpio_desc *reset_gpio; - bool running; + u16 level; }; static const struct regmap_config aw86927_regmap_config = { @@ -325,11 +325,12 @@ static int aw86927_haptics_play(struct input_dev *dev, void *data, struct ff_eff if (!level) level = effect->u.rumble.weak_magnitude; - /* If already running, don't restart playback */ - if (haptics->running && level) + /* If level does not change, don't restart playback */ + if (haptics->level == level) return 0; - haptics->running = level; + haptics->level = level; + schedule_work(&haptics->play_work); return 0; @@ -376,8 +377,7 @@ static int aw86927_play_sine(struct aw86927_data *haptics) if (err) return err; - /* set gain to value lower than 0x80 to avoid distorted playback */ - err = regmap_write(haptics->regmap, AW86927_PLAYCFG2_REG, 0x7c); + err = regmap_write(haptics->regmap, AW86927_PLAYCFG2_REG, haptics->level * 0x80 / 0xffff); if (err) return err; @@ -409,7 +409,7 @@ static void aw86927_haptics_play_work(struct work_struct *work) struct device *dev = &haptics->client->dev; int err; - if (haptics->running) + if (haptics->level) err = aw86927_play_sine(haptics); else err = aw86927_stop(haptics); -- cgit v1.2.3 From b73724b1defe253fa9d76be4bcb585ef37ef4d68 Mon Sep 17 00:00:00 2001 From: Griffin Kroah-Hartman Date: Tue, 3 Mar 2026 17:40:10 -0800 Subject: dt-bindings: input: awinic,aw86927: Add Awinic AW86938 Add bindings for the Awinic AW86938 haptic chip which can be found in smartphones. These two chips require a similar devicetree configuration, but have a register layout that's not 100% compatible. Still, because chip model is fully detectable via ID register, these chips can be documnented in the same file. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Griffin Kroah-Hartman Link: https://patch.msgid.link/20260302-aw86938-driver-v4-2-92c865df9cca@fairphone.com Signed-off-by: Dmitry Torokhov --- Documentation/devicetree/bindings/input/awinic,aw86927.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/input/awinic,aw86927.yaml b/Documentation/devicetree/bindings/input/awinic,aw86927.yaml index b7252916bd72..bd74b81488f6 100644 --- a/Documentation/devicetree/bindings/input/awinic,aw86927.yaml +++ b/Documentation/devicetree/bindings/input/awinic,aw86927.yaml @@ -11,7 +11,12 @@ maintainers: properties: compatible: - const: awinic,aw86927 + oneOf: + - const: awinic,aw86927 + - items: + - enum: + - awinic,aw86938 + - const: awinic,aw86927 reg: maxItems: 1 -- cgit v1.2.3 From df53055c540fe8850f587a6ef0d6944bdd909483 Mon Sep 17 00:00:00 2001 From: Griffin Kroah-Hartman Date: Tue, 3 Mar 2026 17:40:41 -0800 Subject: Input: aw86927 - add support for Awinic AW86938 Add support for the I2C-connected Awinic AW86938 LRA haptic controller. The AW86938 has a similar but slightly different register layout. In particular, the boost mode register values. The AW86938 also has some extra features that aren't implemented in this driver yet. Signed-off-by: Griffin Kroah-Hartman Link: https://patch.msgid.link/20260302-aw86938-driver-v4-3-92c865df9cca@fairphone.com Signed-off-by: Dmitry Torokhov --- drivers/input/misc/aw86927.c | 52 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/drivers/input/misc/aw86927.c b/drivers/input/misc/aw86927.c index 454e1af23df0..f53b8f004cb3 100644 --- a/drivers/input/misc/aw86927.c +++ b/drivers/input/misc/aw86927.c @@ -43,6 +43,12 @@ #define AW86927_PLAYCFG1_BST_VOUT_VREFSET_MASK GENMASK(6, 0) #define AW86927_PLAYCFG1_BST_8500MV 0x50 +#define AW86938_PLAYCFG1_REG 0x06 +#define AW86938_PLAYCFG1_BST_MODE_MASK GENMASK(5, 5) +#define AW86938_PLAYCFG1_BST_MODE_BYPASS 0 +#define AW86938_PLAYCFG1_BST_VOUT_VREFSET_MASK GENMASK(4, 0) +#define AW86938_PLAYCFG1_BST_7000MV 0x11 + #define AW86927_PLAYCFG2_REG 0x07 #define AW86927_PLAYCFG3_REG 0x08 @@ -140,6 +146,7 @@ #define AW86927_CHIPIDH_REG 0x57 #define AW86927_CHIPIDL_REG 0x58 #define AW86927_CHIPID 0x9270 +#define AW86938_CHIPID 0x9380 #define AW86927_TMCFG_REG 0x5b #define AW86927_TMCFG_UNLOCK 0x7d @@ -173,7 +180,13 @@ enum aw86927_work_mode { AW86927_RAM_MODE, }; +enum aw86927_model { + AW86927, + AW86938, +}; + struct aw86927_data { + enum aw86927_model model; struct work_struct play_work; struct device *dev; struct input_dev *input_dev; @@ -565,13 +578,26 @@ static int aw86927_haptic_init(struct aw86927_data *haptics) if (err) return err; - err = regmap_update_bits(haptics->regmap, - AW86927_PLAYCFG1_REG, - AW86927_PLAYCFG1_BST_VOUT_VREFSET_MASK, - FIELD_PREP(AW86927_PLAYCFG1_BST_VOUT_VREFSET_MASK, - AW86927_PLAYCFG1_BST_8500MV)); - if (err) - return err; + switch (haptics->model) { + case AW86927: + err = regmap_update_bits(haptics->regmap, + AW86927_PLAYCFG1_REG, + AW86927_PLAYCFG1_BST_VOUT_VREFSET_MASK, + FIELD_PREP(AW86927_PLAYCFG1_BST_VOUT_VREFSET_MASK, + AW86927_PLAYCFG1_BST_8500MV)); + if (err) + return err; + break; + case AW86938: + err = regmap_update_bits(haptics->regmap, + AW86938_PLAYCFG1_REG, + AW86938_PLAYCFG1_BST_VOUT_VREFSET_MASK, + FIELD_PREP(AW86938_PLAYCFG1_BST_VOUT_VREFSET_MASK, + AW86938_PLAYCFG1_BST_7000MV)); + if (err) + return err; + break; + } err = regmap_update_bits(haptics->regmap, AW86927_PLAYCFG3_REG, @@ -599,6 +625,9 @@ static int aw86927_ram_init(struct aw86927_data *haptics) FIELD_PREP(AW86927_SYSCTRL3_EN_RAMINIT_MASK, AW86927_SYSCTRL3_EN_RAMINIT_ON)); + /* AW86938 wants a 1ms delay here */ + usleep_range(1000, 1500); + /* Set base address for the start of the SRAM waveforms */ err = regmap_write(haptics->regmap, AW86927_BASEADDRH_REG, AW86927_BASEADDRH_VAL); @@ -717,7 +746,14 @@ static int aw86927_detect(struct aw86927_data *haptics) chip_id = be16_to_cpu(read_buf); - if (chip_id != AW86927_CHIPID) { + switch (chip_id) { + case AW86927_CHIPID: + haptics->model = AW86927; + break; + case AW86938_CHIPID: + haptics->model = AW86938; + break; + default: dev_err(haptics->dev, "Unexpected CHIPID value 0x%x\n", chip_id); return -ENODEV; } -- cgit v1.2.3 From f13b7800929df0df0ba2407226ba1b627b482c92 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 1 Apr 2026 10:22:12 +0200 Subject: Input: usbtouchscreen - refactor endpoint lookup Use the common USB helpers for looking up bulk and interrupt endpoints (and determining endpoint numbers) instead of open coding. Note that the NEXIO data interface has two bulk endpoints (see commit 5197424cdccc ("Input: usbtouchscreen - add NEXIO (or iNexio) support") for the descriptors). The lookup in probe handles both bulk-in and interrupt-in endpoints and was added to handle NEXIO devices. Replace the open coded lookup with a lookup for the common interrupt endpoint and an explicit fallback accepting a bulk endpoint. This iterates over the (two) endpoints twice for NEXIO devices but makes it more clear what is going on. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260401082212.2180434-1-johan@kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/usbtouchscreen.c | 43 +++++++++++------------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index 657555c8796c..daa28135f887 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -969,24 +969,21 @@ static int nexio_init(struct usbtouch_usb *usbtouch) { struct usb_device *dev = interface_to_usbdev(usbtouch->interface); struct usb_host_interface *interface = usbtouch->interface->cur_altsetting; + struct usb_endpoint_descriptor *ep_in, *ep_out; struct nexio_priv *priv = usbtouch->priv; - int ret = -ENOMEM; int actual_len, i; char *firmware_ver = NULL, *device_name = NULL; - int input_ep = 0, output_ep = 0; + int input_ep, output_ep; + int ret; /* find first input and output endpoint */ - for (i = 0; i < interface->desc.bNumEndpoints; i++) { - if (!input_ep && - usb_endpoint_dir_in(&interface->endpoint[i].desc)) - input_ep = interface->endpoint[i].desc.bEndpointAddress; - if (!output_ep && - usb_endpoint_dir_out(&interface->endpoint[i].desc)) - output_ep = interface->endpoint[i].desc.bEndpointAddress; - } - if (!input_ep || !output_ep) + ret = usb_find_common_endpoints(interface, &ep_in, &ep_out, NULL, NULL); + if (ret) return -ENXIO; + input_ep = usb_endpoint_num(ep_in); + output_ep = usb_endpoint_num(ep_out); + u8 *buf __free(kfree) = kmalloc(NEXIO_BUFSIZE, GFP_NOIO); if (!buf) return -ENOMEM; @@ -1427,18 +1424,6 @@ static void usbtouch_free_buffers(struct usb_device *udev, kfree(usbtouch->buffer); } -static struct usb_endpoint_descriptor * -usbtouch_get_input_endpoint(struct usb_host_interface *interface) -{ - int i; - - for (i = 0; i < interface->desc.bNumEndpoints; i++) - if (usb_endpoint_dir_in(&interface->endpoint[i].desc)) - return &interface->endpoint[i].desc; - - return NULL; -} - static int usbtouch_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1447,17 +1432,21 @@ static int usbtouch_probe(struct usb_interface *intf, struct usb_endpoint_descriptor *endpoint; struct usb_device *udev = interface_to_usbdev(intf); const struct usbtouch_device_info *type; - int err = -ENOMEM; + int err; /* some devices are ignored */ type = (const struct usbtouch_device_info *)id->driver_info; if (!type) return -ENODEV; - endpoint = usbtouch_get_input_endpoint(intf->cur_altsetting); - if (!endpoint) - return -ENXIO; + err = usb_find_int_in_endpoint(intf->cur_altsetting, &endpoint); + if (err) { + err = usb_find_bulk_in_endpoint(intf->cur_altsetting, &endpoint); + if (err) + return -ENXIO; + } + err = -ENOMEM; usbtouch = kzalloc_obj(*usbtouch); input_dev = input_allocate_device(); if (!usbtouch || !input_dev) -- cgit v1.2.3 From 802b4c158cf57d615dae50000be4c6063a7db7ba Mon Sep 17 00:00:00 2001 From: Elliot Tester Date: Wed, 25 Mar 2026 23:16:17 +0100 Subject: Input: xpad - remove stale TODO and changelog header All items in the TODO block have since been addressed: axis tuning, analog button handling, rumble support, and dance pad USB IDs are all implemented. The manual changelog is also removed as history is tracked in git. Signed-off-by: Elliot Tester Link: https://patch.msgid.link/20260325221618.135833-1-elliotctester1@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index bf4accf3f581..ce0f0b7e8fa0 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -25,40 +25,6 @@ * - Greg Kroah-Hartman - usb-skeleton driver * - Xbox Linux project - extra USB IDs * - Pekka Pöyry (quantus) - Xbox One controller reverse-engineering - * - * TODO: - * - fine tune axes (especially trigger axes) - * - fix "analog" buttons (reported as digital now) - * - get rumble working - * - need USB IDs for other dance pads - * - * History: - * - * 2002-06-27 - 0.0.1 : first version, just said "XBOX HID controller" - * - * 2002-07-02 - 0.0.2 : basic working version - * - all axes and 9 of the 10 buttons work (german InterAct device) - * - the black button does not work - * - * 2002-07-14 - 0.0.3 : rework by Vojtech Pavlik - * - indentation fixes - * - usb + input init sequence fixes - * - * 2002-07-16 - 0.0.4 : minor changes, merge with Vojtech's v0.0.3 - * - verified the lack of HID and report descriptors - * - verified that ALL buttons WORK - * - fixed d-pad to axes mapping - * - * 2002-07-17 - 0.0.5 : simplified d-pad handling - * - * 2004-10-02 - 0.0.6 : DDR pad support - * - borrowed from the Xbox Linux kernel - * - USB id's for commonly used dance pads are present - * - dance pads will map D-PAD to buttons, not axes - * - pass the module paramater 'dpad_to_buttons' to force - * the D-PAD to map to buttons if your pad is not detected - * - * Later changes can be tracked in SCM. */ #include -- cgit v1.2.3 From 84e7a17d1813394b48b0641fce8217fc0bba1960 Mon Sep 17 00:00:00 2001 From: Sanjay Govind Date: Fri, 3 Apr 2026 22:35:50 -0700 Subject: Input: xpad - add RedOctane Games vendor id Add vendor ID for RedOctane Games to xpad driver. Signed-off-by: Sanjay Govind Link: https://patch.msgid.link/20260311213106.271577-2-sanjay.govind9@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index ce0f0b7e8fa0..e2fa69af2ce1 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -551,6 +551,7 @@ static const struct usb_device_id xpad_table[] = { XPAD_XBOX360_VENDOR(0x3651), /* CRKD Controllers */ XPAD_XBOXONE_VENDOR(0x366c), /* ByoWave controllers */ XPAD_XBOX360_VENDOR(0x37d7), /* Flydigi Controllers */ + XPAD_XBOX360_VENDOR(0x3958), /* RedOctane Games Controllers */ XPAD_XBOX360_VENDOR(0x413d), /* Black Shark Green Ghost Controller */ { } }; -- cgit v1.2.3 From bc561dc8ba5b9fe56ed1757bdad218c9a0f992f1 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 7 Apr 2026 09:56:52 +0800 Subject: Input: gf2k - skip invalid hat lookup values gf2k_read() decodes the hat position from a 4-bit field and uses it directly to index gf2k_hat_to_axis[]. The lookup table only has nine entries, so malformed packets can read past the end of the fixed table. Skip hat reporting when the decoded value falls outside the lookup table instead of forcing it to the neutral position. This keeps the fix local and avoids reporting a made-up axis state for malformed packets. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260407120001.1-gf2k-v2-pengpeng@iscas.ac.cn Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/gf2k.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/input/joystick/gf2k.c b/drivers/input/joystick/gf2k.c index 5a1cdce0bc48..1d843115d674 100644 --- a/drivers/input/joystick/gf2k.c +++ b/drivers/input/joystick/gf2k.c @@ -165,8 +165,10 @@ static void gf2k_read(struct gf2k *gf2k, unsigned char *data) t = GB(40,4,0); - for (i = 0; i < gf2k_hats[gf2k->id]; i++) - input_report_abs(dev, ABS_HAT0X + i, gf2k_hat_to_axis[t][i]); + if (t < ARRAY_SIZE(gf2k_hat_to_axis)) + for (i = 0; i < gf2k_hats[gf2k->id]; i++) + input_report_abs(dev, ABS_HAT0X + i, + gf2k_hat_to_axis[t][i]); t = GB(44,2,0) | GB(32,8,2) | GB(78,2,10); -- cgit v1.2.3 From 95dffe32a66cbed07fbfa7afed39d56d5014e04f Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 6 Apr 2026 21:52:34 -0700 Subject: Input: aiptek - validate raw macro indices before updating state aiptek_irq() derives macro key indices directly from tablet reports and then uses them to index macroKeyEvents[]. Report types 4 and 5 also save the derived value in aiptek->lastMacro and later use that state to release the previous key. Validate the raw macro index once before it enters that state machine, so lastMacro only ever stores an in-range macro key. Keep direct bounds checks for report type 6, which reads the macro number from the packet body and uses it immediately. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260329001711.88076-1-pengpeng@iscas.ac.cn [dtor: fix macro fallback in report 5s to use -1] Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/aiptek.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/input/tablet/aiptek.c b/drivers/input/tablet/aiptek.c index 1ad3c19aa155..c850b5890070 100644 --- a/drivers/input/tablet/aiptek.c +++ b/drivers/input/tablet/aiptek.c @@ -657,6 +657,8 @@ static void aiptek_irq(struct urb *urb) pck = (data[1] & aiptek->curSetting.stylusButtonUpper) != 0 ? 1 : 0; macro = dv && p && tip && !(data[3] & 1) ? (data[3] >> 1) : -1; + if (macro >= ARRAY_SIZE(macroKeyEvents)) + macro = -1; z = get_unaligned_le16(data + 4); if (dv) { @@ -698,7 +700,9 @@ static void aiptek_irq(struct urb *urb) left = (data[1]& aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0; right = (data[1] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0; middle = (data[1] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0; - macro = dv && p && left && !(data[3] & 1) ? (data[3] >> 1) : 0; + macro = dv && p && left && !(data[3] & 1) ? (data[3] >> 1) : -1; + if (macro >= ARRAY_SIZE(macroKeyEvents)) + macro = -1; if (dv) { /* If the selected tool changed, reset the old @@ -736,11 +740,11 @@ static void aiptek_irq(struct urb *urb) */ else if (data[0] == 6) { macro = get_unaligned_le16(data + 1); - if (macro > 0) { + if (macro > 0 && macro - 1 < ARRAY_SIZE(macroKeyEvents)) { input_report_key(inputdev, macroKeyEvents[macro - 1], 0); } - if (macro < 25) { + if (macro + 1 < ARRAY_SIZE(macroKeyEvents)) { input_report_key(inputdev, macroKeyEvents[macro + 1], 0); } @@ -759,7 +763,8 @@ static void aiptek_irq(struct urb *urb) aiptek->curSetting.toolMode; } - input_report_key(inputdev, macroKeyEvents[macro], 1); + if (macro < ARRAY_SIZE(macroKeyEvents)) + input_report_key(inputdev, macroKeyEvents[macro], 1); input_report_abs(inputdev, ABS_MISC, 1 | AIPTEK_REPORT_TOOL_UNKNOWN); input_sync(inputdev); -- cgit v1.2.3 From 512b0f41aab28733fff9fb78f0162224ba581cad Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Wed, 8 Apr 2026 16:19:25 +0200 Subject: Input: qt1050 - inline i2c_check_functionality check Inline the i2c_check_functionality() check, since the function returns a boolean status rather than an error code. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260408141926.1181389-3-thorsten.blum@linux.dev Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/qt1050.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/input/keyboard/qt1050.c b/drivers/input/keyboard/qt1050.c index bce8157d1871..f9f480c91032 100644 --- a/drivers/input/keyboard/qt1050.c +++ b/drivers/input/keyboard/qt1050.c @@ -435,8 +435,7 @@ static int qt1050_probe(struct i2c_client *client) int err; /* Check basic functionality */ - err = i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE); - if (!err) { + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { dev_err(&client->dev, "%s adapter not supported\n", dev_driver_string(&client->adapter->dev)); return -ENODEV; -- cgit v1.2.3 From 16bbb5912742ffba347828ddf5b1a297de5bcd58 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Wed, 8 Apr 2026 16:19:26 +0200 Subject: Input: qt1070 - inline i2c_check_functionality check Inline the i2c_check_functionality() check, since the function returns a boolean status rather than an error code. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260408141926.1181389-4-thorsten.blum@linux.dev Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/qt1070.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/input/keyboard/qt1070.c b/drivers/input/keyboard/qt1070.c index b3db2c7d0957..b255b997e279 100644 --- a/drivers/input/keyboard/qt1070.c +++ b/drivers/input/keyboard/qt1070.c @@ -133,8 +133,7 @@ static int qt1070_probe(struct i2c_client *client) int i; int err; - err = i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE); - if (!err) { + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { dev_err(&client->dev, "%s adapter not supported\n", dev_driver_string(&client->adapter->dev)); return -ENODEV; -- cgit v1.2.3 From 8291ffa3e51d6a280b9348fcf5ac6cf45abd2fb8 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 8 Aug 2024 10:27:27 -0700 Subject: Input: inport - remove driver Inport (ATI XL and Microsoft) mice use specialized bus interface implemented via an ISA add-in card. Have been superseded by PS/2 and then USB, and are historical curiosity by now. Remove the driver. Link: https://patch.msgid.link/20240808172733.1194442-2-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/Kconfig | 16 ---- drivers/input/mouse/Makefile | 1 - drivers/input/mouse/inport.c | 177 ------------------------------------------- 3 files changed, 194 deletions(-) delete mode 100644 drivers/input/mouse/inport.c diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 30d119d4634b..932df58c86dd 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -290,22 +290,6 @@ config MOUSE_ELAN_I2C_SMBUS If unsure, say Y. -config MOUSE_INPORT - tristate "InPort/MS/ATIXL busmouse" - depends on ISA - help - Say Y here if you have an InPort, Microsoft or ATI XL busmouse. - They are rather rare these days. - - To compile this driver as a module, choose M here: the - module will be called inport. - -config MOUSE_ATIXL - bool "ATI XL variant" - depends on MOUSE_INPORT - help - Say Y here if your mouse is of the ATI XL variety. - config MOUSE_LOGIBM tristate "Logitech busmouse" depends on ISA diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index 137ed0c32d90..b88a1b2cae14 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -12,7 +12,6 @@ obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o obj-$(CONFIG_MOUSE_CYAPA) += cyapatp.o obj-$(CONFIG_MOUSE_ELAN_I2C) += elan_i2c.o obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o -obj-$(CONFIG_MOUSE_INPORT) += inport.o obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o diff --git a/drivers/input/mouse/inport.c b/drivers/input/mouse/inport.c deleted file mode 100644 index 401d8bff8e84..000000000000 --- a/drivers/input/mouse/inport.c +++ /dev/null @@ -1,177 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) 1999-2001 Vojtech Pavlik - * - * Based on the work of: - * Teemu Rantanen Derrick Cole - * Peter Cervasio Christoph Niemann - * Philip Blundell Russell King - * Bob Harris - */ - -/* - * Inport (ATI XL and Microsoft) busmouse driver for Linux - */ - -#include -#include -#include -#include -#include - -#include -#include - -MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("Inport (ATI XL and Microsoft) busmouse driver"); -MODULE_LICENSE("GPL"); - -#define INPORT_BASE 0x23c -#define INPORT_EXTENT 4 - -#define INPORT_CONTROL_PORT INPORT_BASE + 0 -#define INPORT_DATA_PORT INPORT_BASE + 1 -#define INPORT_SIGNATURE_PORT INPORT_BASE + 2 - -#define INPORT_REG_BTNS 0x00 -#define INPORT_REG_X 0x01 -#define INPORT_REG_Y 0x02 -#define INPORT_REG_MODE 0x07 -#define INPORT_RESET 0x80 - -#ifdef CONFIG_MOUSE_ATIXL -#define INPORT_NAME "ATI XL Mouse" -#define INPORT_VENDOR 0x0002 -#define INPORT_SPEED_30HZ 0x01 -#define INPORT_SPEED_50HZ 0x02 -#define INPORT_SPEED_100HZ 0x03 -#define INPORT_SPEED_200HZ 0x04 -#define INPORT_MODE_BASE INPORT_SPEED_100HZ -#define INPORT_MODE_IRQ 0x08 -#else -#define INPORT_NAME "Microsoft InPort Mouse" -#define INPORT_VENDOR 0x0001 -#define INPORT_MODE_BASE 0x10 -#define INPORT_MODE_IRQ 0x01 -#endif -#define INPORT_MODE_HOLD 0x20 - -#define INPORT_IRQ 5 - -static int inport_irq = INPORT_IRQ; -module_param_hw_named(irq, inport_irq, uint, irq, 0); -MODULE_PARM_DESC(irq, "IRQ number (5=default)"); - -static struct input_dev *inport_dev; - -static irqreturn_t inport_interrupt(int irq, void *dev_id) -{ - unsigned char buttons; - - outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); - outb(INPORT_MODE_HOLD | INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT); - - outb(INPORT_REG_X, INPORT_CONTROL_PORT); - input_report_rel(inport_dev, REL_X, inb(INPORT_DATA_PORT)); - - outb(INPORT_REG_Y, INPORT_CONTROL_PORT); - input_report_rel(inport_dev, REL_Y, inb(INPORT_DATA_PORT)); - - outb(INPORT_REG_BTNS, INPORT_CONTROL_PORT); - buttons = inb(INPORT_DATA_PORT); - - input_report_key(inport_dev, BTN_MIDDLE, buttons & 1); - input_report_key(inport_dev, BTN_LEFT, buttons & 2); - input_report_key(inport_dev, BTN_RIGHT, buttons & 4); - - outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); - outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT); - - input_sync(inport_dev); - return IRQ_HANDLED; -} - -static int inport_open(struct input_dev *dev) -{ - if (request_irq(inport_irq, inport_interrupt, 0, "inport", NULL)) - return -EBUSY; - outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); - outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT); - - return 0; -} - -static void inport_close(struct input_dev *dev) -{ - outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); - outb(INPORT_MODE_BASE, INPORT_DATA_PORT); - free_irq(inport_irq, NULL); -} - -static int __init inport_init(void) -{ - unsigned char a, b, c; - int err; - - if (!request_region(INPORT_BASE, INPORT_EXTENT, "inport")) { - printk(KERN_ERR "inport.c: Can't allocate ports at %#x\n", INPORT_BASE); - return -EBUSY; - } - - a = inb(INPORT_SIGNATURE_PORT); - b = inb(INPORT_SIGNATURE_PORT); - c = inb(INPORT_SIGNATURE_PORT); - if (a == b || a != c) { - printk(KERN_INFO "inport.c: Didn't find InPort mouse at %#x\n", INPORT_BASE); - err = -ENODEV; - goto err_release_region; - } - - inport_dev = input_allocate_device(); - if (!inport_dev) { - printk(KERN_ERR "inport.c: Not enough memory for input device\n"); - err = -ENOMEM; - goto err_release_region; - } - - inport_dev->name = INPORT_NAME; - inport_dev->phys = "isa023c/input0"; - inport_dev->id.bustype = BUS_ISA; - inport_dev->id.vendor = INPORT_VENDOR; - inport_dev->id.product = 0x0001; - inport_dev->id.version = 0x0100; - - inport_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); - inport_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) | - BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); - inport_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); - - inport_dev->open = inport_open; - inport_dev->close = inport_close; - - outb(INPORT_RESET, INPORT_CONTROL_PORT); - outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); - outb(INPORT_MODE_BASE, INPORT_DATA_PORT); - - err = input_register_device(inport_dev); - if (err) - goto err_free_dev; - - return 0; - - err_free_dev: - input_free_device(inport_dev); - err_release_region: - release_region(INPORT_BASE, INPORT_EXTENT); - - return err; -} - -static void __exit inport_exit(void) -{ - input_unregister_device(inport_dev); - release_region(INPORT_BASE, INPORT_EXTENT); -} - -module_init(inport_init); -module_exit(inport_exit); -- cgit v1.2.3 From 931e3151dba74786f36a948a8d08490f3657c1f3 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 8 Aug 2024 10:27:28 -0700 Subject: Input: logibm - remove driver Bus mice use specialized bus interface implemented via an ISA add-in cards. They were superseded by PS/2 and later USB. Kconfig entry for the Logitech bus mice states that they "are rather rare these days". This statement was true in 2002 and is no less true in 2024. Remove the driver. Link: https://patch.msgid.link/20240808172733.1194442-3-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/Kconfig | 10 --- drivers/input/mouse/Makefile | 1 - drivers/input/mouse/logibm.c | 166 ------------------------------------------- 3 files changed, 177 deletions(-) delete mode 100644 drivers/input/mouse/logibm.c diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 932df58c86dd..04d56f40ecb6 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -290,16 +290,6 @@ config MOUSE_ELAN_I2C_SMBUS If unsure, say Y. -config MOUSE_LOGIBM - tristate "Logitech busmouse" - depends on ISA - help - Say Y here if you have a Logitech busmouse. - They are rather rare these days. - - To compile this driver as a module, choose M here: the - module will be called logibm. - config MOUSE_PC110PAD tristate "IBM PC110 touchpad" depends on ISA diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index b88a1b2cae14..16d1cdf3182f 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -12,7 +12,6 @@ obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o obj-$(CONFIG_MOUSE_CYAPA) += cyapatp.o obj-$(CONFIG_MOUSE_ELAN_I2C) += elan_i2c.o obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o -obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o obj-$(CONFIG_MOUSE_PS2) += psmouse.o diff --git a/drivers/input/mouse/logibm.c b/drivers/input/mouse/logibm.c deleted file mode 100644 index 0aab63dbc30a..000000000000 --- a/drivers/input/mouse/logibm.c +++ /dev/null @@ -1,166 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) 1999-2001 Vojtech Pavlik - * - * Based on the work of: - * James Banks Matthew Dillon - * David Giller Nathan Laredo - * Linus Torvalds Johan Myreen - * Cliff Matthews Philip Blundell - * Russell King - */ - -/* - * Logitech Bus Mouse Driver for Linux - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("Logitech busmouse driver"); -MODULE_LICENSE("GPL"); - -#define LOGIBM_BASE 0x23c -#define LOGIBM_EXTENT 4 - -#define LOGIBM_DATA_PORT LOGIBM_BASE + 0 -#define LOGIBM_SIGNATURE_PORT LOGIBM_BASE + 1 -#define LOGIBM_CONTROL_PORT LOGIBM_BASE + 2 -#define LOGIBM_CONFIG_PORT LOGIBM_BASE + 3 - -#define LOGIBM_ENABLE_IRQ 0x00 -#define LOGIBM_DISABLE_IRQ 0x10 -#define LOGIBM_READ_X_LOW 0x80 -#define LOGIBM_READ_X_HIGH 0xa0 -#define LOGIBM_READ_Y_LOW 0xc0 -#define LOGIBM_READ_Y_HIGH 0xe0 - -#define LOGIBM_DEFAULT_MODE 0x90 -#define LOGIBM_CONFIG_BYTE 0x91 -#define LOGIBM_SIGNATURE_BYTE 0xa5 - -#define LOGIBM_IRQ 5 - -static int logibm_irq = LOGIBM_IRQ; -module_param_hw_named(irq, logibm_irq, uint, irq, 0); -MODULE_PARM_DESC(irq, "IRQ number (5=default)"); - -static struct input_dev *logibm_dev; - -static irqreturn_t logibm_interrupt(int irq, void *dev_id) -{ - char dx, dy; - unsigned char buttons; - - outb(LOGIBM_READ_X_LOW, LOGIBM_CONTROL_PORT); - dx = (inb(LOGIBM_DATA_PORT) & 0xf); - outb(LOGIBM_READ_X_HIGH, LOGIBM_CONTROL_PORT); - dx |= (inb(LOGIBM_DATA_PORT) & 0xf) << 4; - outb(LOGIBM_READ_Y_LOW, LOGIBM_CONTROL_PORT); - dy = (inb(LOGIBM_DATA_PORT) & 0xf); - outb(LOGIBM_READ_Y_HIGH, LOGIBM_CONTROL_PORT); - buttons = inb(LOGIBM_DATA_PORT); - dy |= (buttons & 0xf) << 4; - buttons = ~buttons >> 5; - - input_report_rel(logibm_dev, REL_X, dx); - input_report_rel(logibm_dev, REL_Y, dy); - input_report_key(logibm_dev, BTN_RIGHT, buttons & 1); - input_report_key(logibm_dev, BTN_MIDDLE, buttons & 2); - input_report_key(logibm_dev, BTN_LEFT, buttons & 4); - input_sync(logibm_dev); - - outb(LOGIBM_ENABLE_IRQ, LOGIBM_CONTROL_PORT); - return IRQ_HANDLED; -} - -static int logibm_open(struct input_dev *dev) -{ - if (request_irq(logibm_irq, logibm_interrupt, 0, "logibm", NULL)) { - printk(KERN_ERR "logibm.c: Can't allocate irq %d\n", logibm_irq); - return -EBUSY; - } - outb(LOGIBM_ENABLE_IRQ, LOGIBM_CONTROL_PORT); - return 0; -} - -static void logibm_close(struct input_dev *dev) -{ - outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT); - free_irq(logibm_irq, NULL); -} - -static int __init logibm_init(void) -{ - int err; - - if (!request_region(LOGIBM_BASE, LOGIBM_EXTENT, "logibm")) { - printk(KERN_ERR "logibm.c: Can't allocate ports at %#x\n", LOGIBM_BASE); - return -EBUSY; - } - - outb(LOGIBM_CONFIG_BYTE, LOGIBM_CONFIG_PORT); - outb(LOGIBM_SIGNATURE_BYTE, LOGIBM_SIGNATURE_PORT); - udelay(100); - - if (inb(LOGIBM_SIGNATURE_PORT) != LOGIBM_SIGNATURE_BYTE) { - printk(KERN_INFO "logibm.c: Didn't find Logitech busmouse at %#x\n", LOGIBM_BASE); - err = -ENODEV; - goto err_release_region; - } - - outb(LOGIBM_DEFAULT_MODE, LOGIBM_CONFIG_PORT); - outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT); - - logibm_dev = input_allocate_device(); - if (!logibm_dev) { - printk(KERN_ERR "logibm.c: Not enough memory for input device\n"); - err = -ENOMEM; - goto err_release_region; - } - - logibm_dev->name = "Logitech bus mouse"; - logibm_dev->phys = "isa023c/input0"; - logibm_dev->id.bustype = BUS_ISA; - logibm_dev->id.vendor = 0x0003; - logibm_dev->id.product = 0x0001; - logibm_dev->id.version = 0x0100; - - logibm_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); - logibm_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) | - BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); - logibm_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); - - logibm_dev->open = logibm_open; - logibm_dev->close = logibm_close; - - err = input_register_device(logibm_dev); - if (err) - goto err_free_dev; - - return 0; - - err_free_dev: - input_free_device(logibm_dev); - err_release_region: - release_region(LOGIBM_BASE, LOGIBM_EXTENT); - - return err; -} - -static void __exit logibm_exit(void) -{ - input_unregister_device(logibm_dev); - release_region(LOGIBM_BASE, LOGIBM_EXTENT); -} - -module_init(logibm_init); -module_exit(logibm_exit); -- cgit v1.2.3 From 86a9e4f4efc0a8dc4490023c6e2bf57fd8080ea3 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 8 Aug 2024 10:27:30 -0700 Subject: Input: mk712 - remove driver This touchscreen controller was used om Gateway AOL Connected Touchpad released in 2000 and, according to Wikipedia, removed from the market in October 2001 due to slow sales. It looks like it can still be bought on eBay for $1000 but I really doubt anyone will actually use it. Remove the driver. Link: https://patch.msgid.link/20240808172733.1194442-5-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 12 --- drivers/input/touchscreen/Makefile | 1 - drivers/input/touchscreen/mk712.c | 207 ------------------------------------- 3 files changed, 220 deletions(-) delete mode 100644 drivers/input/touchscreen/mk712.c diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 7d5b72ee07fa..aeaf9a9cbb41 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -723,18 +723,6 @@ config TOUCHSCREEN_INEXIO To compile this driver as a module, choose M here: the module will be called inexio. -config TOUCHSCREEN_MK712 - tristate "ICS MicroClock MK712 touchscreen" - depends on ISA - help - Say Y here if you have the ICS MicroClock MK712 touchscreen - controller chip in your system. - - If unsure, say N. - - To compile this driver as a module, choose M here: the - module will be called mk712. - config TOUCHSCREEN_HP600 tristate "HP Jornada 6xx touchscreen" depends on SH_HP6XX && SH_ADC diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index ab9abd151078..f2b002abebe8 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -68,7 +68,6 @@ obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o obj-$(CONFIG_TOUCHSCREEN_MMS114) += mms114.o obj-$(CONFIG_TOUCHSCREEN_MSG2638) += msg2638.o obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o -obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o obj-$(CONFIG_TOUCHSCREEN_NOVATEK_NVT_TS) += novatek-nvt-ts.o obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o diff --git a/drivers/input/touchscreen/mk712.c b/drivers/input/touchscreen/mk712.c deleted file mode 100644 index a36fea2d5a4e..000000000000 --- a/drivers/input/touchscreen/mk712.c +++ /dev/null @@ -1,207 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * ICS MK712 touchscreen controller driver - * - * Copyright (c) 1999-2002 Transmeta Corporation - * Copyright (c) 2005 Rick Koch - * Copyright (c) 2005 Vojtech Pavlik - */ - - -/* - * This driver supports the ICS MicroClock MK712 TouchScreen controller, - * found in Gateway AOL Connected Touchpad computers. - * - * Documentation for ICS MK712 can be found at: - * https://www.idt.com/general-parts/mk712-touch-screen-controller - */ - -/* - * 1999-12-18: original version, Daniel Quinlan - * 1999-12-19: added anti-jitter code, report pen-up events, fixed mk712_poll - * to use queue_empty, Nathan Laredo - * 1999-12-20: improved random point rejection, Nathan Laredo - * 2000-01-05: checked in new anti-jitter code, changed mouse protocol, fixed - * queue code, added module options, other fixes, Daniel Quinlan - * 2002-03-15: Clean up for kernel merge - * Fixed multi open race, fixed memory checks, fixed resource - * allocation, fixed close/powerdown bug, switched to new init - * 2005-01-18: Ported to 2.6 from 2.4.28, Rick Koch - * 2005-02-05: Rewritten for the input layer, Vojtech Pavlik - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Daniel Quinlan , Vojtech Pavlik "); -MODULE_DESCRIPTION("ICS MicroClock MK712 TouchScreen driver"); -MODULE_LICENSE("GPL"); - -static unsigned int mk712_io = 0x260; /* Also 0x200, 0x208, 0x300 */ -module_param_hw_named(io, mk712_io, uint, ioport, 0); -MODULE_PARM_DESC(io, "I/O base address of MK712 touchscreen controller"); - -static unsigned int mk712_irq = 10; /* Also 12, 14, 15 */ -module_param_hw_named(irq, mk712_irq, uint, irq, 0); -MODULE_PARM_DESC(irq, "IRQ of MK712 touchscreen controller"); - -/* eight 8-bit registers */ -#define MK712_STATUS 0 -#define MK712_X 2 -#define MK712_Y 4 -#define MK712_CONTROL 6 -#define MK712_RATE 7 - -/* status */ -#define MK712_STATUS_TOUCH 0x10 -#define MK712_CONVERSION_COMPLETE 0x80 - -/* control */ -#define MK712_ENABLE_INT 0x01 -#define MK712_INT_ON_CONVERSION_COMPLETE 0x02 -#define MK712_INT_ON_CHANGE_IN_TOUCH_STATUS 0x04 -#define MK712_ENABLE_PERIODIC_CONVERSIONS 0x10 -#define MK712_READ_ONE_POINT 0x20 -#define MK712_POWERUP 0x40 - -static struct input_dev *mk712_dev; -static DEFINE_SPINLOCK(mk712_lock); - -static irqreturn_t mk712_interrupt(int irq, void *dev_id) -{ - unsigned char status; - static int debounce = 1; - static unsigned short last_x; - static unsigned short last_y; - - guard(spinlock)(&mk712_lock); - - status = inb(mk712_io + MK712_STATUS); - - if (~status & MK712_CONVERSION_COMPLETE) { - debounce = 1; - goto end; - } - - if (~status & MK712_STATUS_TOUCH) { - debounce = 1; - input_report_key(mk712_dev, BTN_TOUCH, 0); - goto end; - } - - if (debounce) { - debounce = 0; - goto end; - } - - input_report_key(mk712_dev, BTN_TOUCH, 1); - input_report_abs(mk712_dev, ABS_X, last_x); - input_report_abs(mk712_dev, ABS_Y, last_y); - - end: - last_x = inw(mk712_io + MK712_X) & 0x0fff; - last_y = inw(mk712_io + MK712_Y) & 0x0fff; - input_sync(mk712_dev); - - return IRQ_HANDLED; -} - -static int mk712_open(struct input_dev *dev) -{ - guard(spinlock_irqsave)(&mk712_lock); - - outb(0, mk712_io + MK712_CONTROL); /* Reset */ - - outb(MK712_ENABLE_INT | MK712_INT_ON_CONVERSION_COMPLETE | - MK712_INT_ON_CHANGE_IN_TOUCH_STATUS | - MK712_ENABLE_PERIODIC_CONVERSIONS | - MK712_POWERUP, mk712_io + MK712_CONTROL); - - outb(10, mk712_io + MK712_RATE); /* 187 points per second */ - - return 0; -} - -static void mk712_close(struct input_dev *dev) -{ - guard(spinlock_irqsave)(&mk712_lock); - - outb(0, mk712_io + MK712_CONTROL); -} - -static int __init mk712_init(void) -{ - int err; - - if (!request_region(mk712_io, 8, "mk712")) { - printk(KERN_WARNING "mk712: unable to get IO region\n"); - return -ENODEV; - } - - outb(0, mk712_io + MK712_CONTROL); - - if ((inw(mk712_io + MK712_X) & 0xf000) || /* Sanity check */ - (inw(mk712_io + MK712_Y) & 0xf000) || - (inw(mk712_io + MK712_STATUS) & 0xf333)) { - printk(KERN_WARNING "mk712: device not present\n"); - err = -ENODEV; - goto fail1; - } - - mk712_dev = input_allocate_device(); - if (!mk712_dev) { - printk(KERN_ERR "mk712: not enough memory\n"); - err = -ENOMEM; - goto fail1; - } - - mk712_dev->name = "ICS MicroClock MK712 TouchScreen"; - mk712_dev->phys = "isa0260/input0"; - mk712_dev->id.bustype = BUS_ISA; - mk712_dev->id.vendor = 0x0005; - mk712_dev->id.product = 0x0001; - mk712_dev->id.version = 0x0100; - - mk712_dev->open = mk712_open; - mk712_dev->close = mk712_close; - - mk712_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - mk712_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); - input_set_abs_params(mk712_dev, ABS_X, 0, 0xfff, 88, 0); - input_set_abs_params(mk712_dev, ABS_Y, 0, 0xfff, 88, 0); - - if (request_irq(mk712_irq, mk712_interrupt, 0, "mk712", mk712_dev)) { - printk(KERN_WARNING "mk712: unable to get IRQ\n"); - err = -EBUSY; - goto fail1; - } - - err = input_register_device(mk712_dev); - if (err) - goto fail2; - - return 0; - - fail2: free_irq(mk712_irq, mk712_dev); - fail1: input_free_device(mk712_dev); - release_region(mk712_io, 8); - return err; -} - -static void __exit mk712_exit(void) -{ - input_unregister_device(mk712_dev); - free_irq(mk712_irq, mk712_dev); - release_region(mk712_io, 8); -} - -module_init(mk712_init); -module_exit(mk712_exit); -- cgit v1.2.3 From f7a78e84446e19c9de9adda85a064f947aefa336 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 8 Aug 2024 10:27:31 -0700 Subject: Input: ct82c710 - remove driver This is a PS/2 mouse interface chip from Chips & Technologies that was used in TI TravelMate and Gateway Nomad laptops, which used 386 and 486 CPUs. With 486 support being removed from the kernel (and 386 support is long gone) it is time to retire this driver as well. Remove the driver. Link: https://patch.msgid.link/20240808172733.1194442-6-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/serio/Kconfig | 13 --- drivers/input/serio/Makefile | 1 - drivers/input/serio/ct82c710.c | 239 ----------------------------------------- 3 files changed, 253 deletions(-) delete mode 100644 drivers/input/serio/ct82c710.c diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index c7ef347a4dff..5f15a6462056 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -55,19 +55,6 @@ config SERIO_SERPORT To compile this driver as a module, choose M here: the module will be called serport. -config SERIO_CT82C710 - tristate "ct82c710 Aux port controller" - depends on X86 - help - Say Y here if you have a Texas Instruments TravelMate notebook - equipped with the ct82c710 chip and want to use a mouse connected - to the "QuickPort". - - If unsure, say N. - - To compile this driver as a module, choose M here: the - module will be called ct82c710. - config SERIO_Q40KBD tristate "Q40 keyboard controller" depends on Q40 diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile index 6d97bad7b844..8ab98f4aa28d 100644 --- a/drivers/input/serio/Makefile +++ b/drivers/input/serio/Makefile @@ -9,7 +9,6 @@ obj-$(CONFIG_SERIO) += serio.o obj-$(CONFIG_SERIO_I8042) += i8042.o obj-$(CONFIG_SERIO_PARKBD) += parkbd.o obj-$(CONFIG_SERIO_SERPORT) += serport.o -obj-$(CONFIG_SERIO_CT82C710) += ct82c710.o obj-$(CONFIG_SERIO_RPCKBD) += rpckbd.o obj-$(CONFIG_SERIO_SA1111) += sa1111ps2.o obj-$(CONFIG_SERIO_AMBAKMI) += ambakmi.o diff --git a/drivers/input/serio/ct82c710.c b/drivers/input/serio/ct82c710.c deleted file mode 100644 index fe5f9eda4b77..000000000000 --- a/drivers/input/serio/ct82c710.c +++ /dev/null @@ -1,239 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) 1999-2001 Vojtech Pavlik - */ - -/* - * 82C710 C&T mouse port chip driver for Linux - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("82C710 C&T mouse port chip driver"); -MODULE_LICENSE("GPL"); - -/* - * ct82c710 interface - */ - -#define CT82C710_DEV_IDLE 0x01 /* Device Idle */ -#define CT82C710_RX_FULL 0x02 /* Device Char received */ -#define CT82C710_TX_IDLE 0x04 /* Device XMIT Idle */ -#define CT82C710_RESET 0x08 /* Device Reset */ -#define CT82C710_INTS_ON 0x10 /* Device Interrupt On */ -#define CT82C710_ERROR_FLAG 0x20 /* Device Error */ -#define CT82C710_CLEAR 0x40 /* Device Clear */ -#define CT82C710_ENABLE 0x80 /* Device Enable */ - -#define CT82C710_IRQ 12 - -#define CT82C710_DATA ct82c710_iores.start -#define CT82C710_STATUS (ct82c710_iores.start + 1) - -static struct serio *ct82c710_port; -static struct platform_device *ct82c710_device; -static struct resource ct82c710_iores; - -/* - * Interrupt handler for the 82C710 mouse port. A character - * is waiting in the 82C710. - */ - -static irqreturn_t ct82c710_interrupt(int cpl, void *dev_id) -{ - return serio_interrupt(ct82c710_port, inb(CT82C710_DATA), 0); -} - -/* - * Wait for device to send output char and flush any input char. - */ - -static int ct82c170_wait(void) -{ - int timeout = 60000; - - while ((inb(CT82C710_STATUS) & (CT82C710_RX_FULL | CT82C710_TX_IDLE | CT82C710_DEV_IDLE)) - != (CT82C710_DEV_IDLE | CT82C710_TX_IDLE) && timeout) { - - if (inb_p(CT82C710_STATUS) & CT82C710_RX_FULL) inb_p(CT82C710_DATA); - - udelay(1); - timeout--; - } - - return !timeout; -} - -static void ct82c710_close(struct serio *serio) -{ - if (ct82c170_wait()) - printk(KERN_WARNING "ct82c710.c: Device busy in close()\n"); - - outb_p(inb_p(CT82C710_STATUS) & ~(CT82C710_ENABLE | CT82C710_INTS_ON), CT82C710_STATUS); - - if (ct82c170_wait()) - printk(KERN_WARNING "ct82c710.c: Device busy in close()\n"); - - free_irq(CT82C710_IRQ, NULL); -} - -static int ct82c710_open(struct serio *serio) -{ - unsigned char status; - int err; - - err = request_irq(CT82C710_IRQ, ct82c710_interrupt, 0, "ct82c710", NULL); - if (err) - return err; - - status = inb_p(CT82C710_STATUS); - - status |= (CT82C710_ENABLE | CT82C710_RESET); - outb_p(status, CT82C710_STATUS); - - status &= ~(CT82C710_RESET); - outb_p(status, CT82C710_STATUS); - - status |= CT82C710_INTS_ON; - outb_p(status, CT82C710_STATUS); /* Enable interrupts */ - - while (ct82c170_wait()) { - printk(KERN_ERR "ct82c710: Device busy in open()\n"); - status &= ~(CT82C710_ENABLE | CT82C710_INTS_ON); - outb_p(status, CT82C710_STATUS); - free_irq(CT82C710_IRQ, NULL); - return -EBUSY; - } - - return 0; -} - -/* - * Write to the 82C710 mouse device. - */ - -static int ct82c710_write(struct serio *port, unsigned char c) -{ - if (ct82c170_wait()) return -1; - outb_p(c, CT82C710_DATA); - return 0; -} - -/* - * See if we can find a 82C710 device. Read mouse address. - */ - -static int __init ct82c710_detect(void) -{ - outb_p(0x55, 0x2fa); /* Any value except 9, ff or 36 */ - outb_p(0xaa, 0x3fa); /* Inverse of 55 */ - outb_p(0x36, 0x3fa); /* Address the chip */ - outb_p(0xe4, 0x3fa); /* 390/4; 390 = config address */ - outb_p(0x1b, 0x2fa); /* Inverse of e4 */ - outb_p(0x0f, 0x390); /* Write index */ - if (inb_p(0x391) != 0xe4) /* Config address found? */ - return -ENODEV; /* No: no 82C710 here */ - - outb_p(0x0d, 0x390); /* Write index */ - ct82c710_iores.start = inb_p(0x391) << 2; /* Get mouse I/O address */ - ct82c710_iores.end = ct82c710_iores.start + 1; - ct82c710_iores.flags = IORESOURCE_IO; - outb_p(0x0f, 0x390); - outb_p(0x0f, 0x391); /* Close config mode */ - - return 0; -} - -static int ct82c710_probe(struct platform_device *dev) -{ - ct82c710_port = kzalloc_obj(*ct82c710_port); - if (!ct82c710_port) - return -ENOMEM; - - ct82c710_port->id.type = SERIO_8042; - ct82c710_port->dev.parent = &dev->dev; - ct82c710_port->open = ct82c710_open; - ct82c710_port->close = ct82c710_close; - ct82c710_port->write = ct82c710_write; - strscpy(ct82c710_port->name, "C&T 82c710 mouse port", - sizeof(ct82c710_port->name)); - snprintf(ct82c710_port->phys, sizeof(ct82c710_port->phys), - "isa%16llx/serio0", (unsigned long long)CT82C710_DATA); - - serio_register_port(ct82c710_port); - - printk(KERN_INFO "serio: C&T 82c710 mouse port at %#llx irq %d\n", - (unsigned long long)CT82C710_DATA, CT82C710_IRQ); - - return 0; -} - -static void ct82c710_remove(struct platform_device *dev) -{ - serio_unregister_port(ct82c710_port); -} - -static struct platform_driver ct82c710_driver = { - .driver = { - .name = "ct82c710", - }, - .probe = ct82c710_probe, - .remove = ct82c710_remove, -}; - - -static int __init ct82c710_init(void) -{ - int error; - - error = ct82c710_detect(); - if (error) - return error; - - error = platform_driver_register(&ct82c710_driver); - if (error) - return error; - - ct82c710_device = platform_device_alloc("ct82c710", -1); - if (!ct82c710_device) { - error = -ENOMEM; - goto err_unregister_driver; - } - - error = platform_device_add_resources(ct82c710_device, &ct82c710_iores, 1); - if (error) - goto err_free_device; - - error = platform_device_add(ct82c710_device); - if (error) - goto err_free_device; - - return 0; - - err_free_device: - platform_device_put(ct82c710_device); - err_unregister_driver: - platform_driver_unregister(&ct82c710_driver); - return error; -} - -static void __exit ct82c710_exit(void) -{ - platform_device_unregister(ct82c710_device); - platform_driver_unregister(&ct82c710_driver); -} - -module_init(ct82c710_init); -module_exit(ct82c710_exit); -- cgit v1.2.3 From 875115b82c295277b81b6dfee7debc725f44e854 Mon Sep 17 00:00:00 2001 From: Seungjin Bae Date: Wed, 8 Apr 2026 09:03:59 -0700 Subject: Input: ims-pcu - fix heap-buffer-overflow in ims_pcu_process_data() The `ims_pcu_process_data()` processes incoming URB data byte by byte. However, it fails to check if the `read_pos` index exceeds IMS_PCU_BUF_SIZE. If a malicious USB device sends a packet larger than IMS_PCU_BUF_SIZE, `read_pos` will increment indefinitely. Moreover, since `read_pos` is located immediately after `read_buf`, the attacker can overwrite `read_pos` itself to arbitrarily control the index. This manipulated `read_pos` is subsequently used in `ims_pcu_handle_response()` to copy data into `cmd_buf`, leading to a heap buffer overflow. Specifically, an attacker can overwrite the `cmd_done.wait.head` located at offset 136 relative to `cmd_buf` in the `ims_pcu_handle_response()`. Consequently, when the driver calls `complete(&pcu->cmd_done)`, it triggers a control flow hijack by using the manipulated pointer. Fix this by adding a bounds check for `read_pos` before writing to `read_buf`. If the packet is too long, discard it, log a warning, and reset the parser state. Fixes: 628329d524743 ("Input: add IMS Passenger Control Unit driver") Co-developed-by: Sanghoon Choi Signed-off-by: Sanghoon Choi Signed-off-by: Seungjin Bae Link: https://patch.msgid.link/20251221211442.841549-2-eeodqql09@gmail.com [dtor: factor out resetting packet state, reset checksum as well] Signed-off-by: Dmitry Torokhov --- drivers/input/misc/ims-pcu.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index f69de9762c4e..4c022a36dbe8 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -438,6 +438,14 @@ static void ims_pcu_handle_response(struct ims_pcu *pcu) } } +static void ims_pcu_reset_packet(struct ims_pcu *pcu) +{ + pcu->have_stx = true; + pcu->have_dle = false; + pcu->read_pos = 0; + pcu->check_sum = 0; +} + static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb) { int i; @@ -450,6 +458,14 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb) continue; if (pcu->have_dle) { + if (pcu->read_pos >= IMS_PCU_BUF_SIZE) { + dev_warn(pcu->dev, + "Packet too long (%d bytes), discarding\n", + pcu->read_pos); + ims_pcu_reset_packet(pcu); + continue; + } + pcu->have_dle = false; pcu->read_buf[pcu->read_pos++] = data; pcu->check_sum += data; @@ -462,10 +478,8 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb) dev_warn(pcu->dev, "Unexpected STX at byte %d, discarding old data\n", pcu->read_pos); + ims_pcu_reset_packet(pcu); pcu->have_stx = true; - pcu->have_dle = false; - pcu->read_pos = 0; - pcu->check_sum = 0; break; case IMS_PCU_PROTOCOL_DLE: @@ -485,12 +499,18 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb) ims_pcu_handle_response(pcu); } - pcu->have_stx = false; - pcu->have_dle = false; - pcu->read_pos = 0; + ims_pcu_reset_packet(pcu); break; default: + if (pcu->read_pos >= IMS_PCU_BUF_SIZE) { + dev_warn(pcu->dev, + "Packet too long (%d bytes), discarding\n", + pcu->read_pos); + ims_pcu_reset_packet(pcu); + continue; + } + pcu->read_buf[pcu->read_pos++] = data; pcu->check_sum += data; break; -- cgit v1.2.3 From f5f9e07060519e2287e99019a6de1eb3ebb65c37 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 10 Apr 2026 21:13:43 -0700 Subject: Input: edt-ft5x06 - fix use-after-free in debugfs teardown The commit 68743c500c6e ("Input: edt-ft5x06 - use per-client debugfs directory") removed the manual debugfs teardown, relying on the I2C core to handle it. However, this creates a window where debugfs files are still accessible after edt_ft5x06_ts_teardown_debugfs() frees tsdata->raw_buffer. To prevent a use-after-free, protect the freeing of raw_buffer with the device mutex and set raw_buffer to NULL. The debugfs read function already checks if raw_buffer is NULL under the same mutex, so this safely avoids the use-after-free. Fixes: 68743c500c6e ("Input: edt-ft5x06 - use per-client debugfs directory") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/adnJicDh-bTUaWXP@google.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/edt-ft5x06.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index ba8ff65f7ea6..d3b1177185a3 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -804,7 +804,10 @@ static void edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata) static void edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) { + guard(mutex)(&tsdata->mutex); + kfree(tsdata->raw_buffer); + tsdata->raw_buffer = NULL; } #else -- cgit v1.2.3 From 2e32d2ba1797578115bf0b91071791abaf302649 Mon Sep 17 00:00:00 2001 From: Ethan Carter Edwards Date: Sat, 18 Apr 2026 20:58:32 -0400 Subject: Input: imx_keypad - fix spelling mistake "Colums" -> "Columns" There is a spelling mistake in two comments. Fix them. Signed-off-by: Ethan Carter Edwards Link: https://patch.msgid.link/20260418-imx-typo-v1-1-2a15e54ad4e7@ethancedwards.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/imx_keypad.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c index 069c1d6376e1..ccde60cd6bb3 100644 --- a/drivers/input/keyboard/imx_keypad.c +++ b/drivers/input/keyboard/imx_keypad.c @@ -324,7 +324,7 @@ static void imx_keypad_config(struct imx_keypad *keypad) reg_val |= (keypad->cols_en_mask & 0xff) << 8; /* cols */ writew(reg_val, keypad->mmio_base + KPCR); - /* Write 0's to KPDR[15:8] (Colums) */ + /* Write 0's to KPDR[15:8] (Columns) */ reg_val = readw(keypad->mmio_base + KPDR); reg_val &= 0x00ff; writew(reg_val, keypad->mmio_base + KPDR); @@ -357,7 +357,7 @@ static void imx_keypad_inhibit(struct imx_keypad *keypad) reg_val |= KBD_STAT_KPKR | KBD_STAT_KPKD; writew(reg_val, keypad->mmio_base + KPSR); - /* Colums as open drain and disable all rows */ + /* Columns as open drain and disable all rows */ reg_val = (keypad->cols_en_mask & 0xff) << 8; writew(reg_val, keypad->mmio_base + KPCR); } -- cgit v1.2.3 From cf1f976aee444af0143c3a2fa6cf0f8bf9bd938e Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Sat, 18 Apr 2026 22:16:38 -0700 Subject: dt-bindings: input: add debounce-delay-ms common property A few bindings are already defining a debounce-delay-ms property, so add it to the input binding to reduce redundant redefines. Reviewed-by: Rob Herring (Arm) Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260312180304.3865850-2-hugo@hugovil.com Signed-off-by: Dmitry Torokhov --- Documentation/devicetree/bindings/auxdisplay/holtek,ht16k33.yaml | 5 ++--- Documentation/devicetree/bindings/input/cirrus,ep9307-keypad.yaml | 7 +++---- Documentation/devicetree/bindings/input/gpio-matrix-keypad.yaml | 5 ++--- Documentation/devicetree/bindings/input/input.yaml | 8 ++++++++ .../devicetree/bindings/input/mediatek,mt6779-keypad.yaml | 1 + Documentation/devicetree/bindings/mfd/fsl,mc13xxx.yaml | 2 -- 6 files changed, 16 insertions(+), 12 deletions(-) diff --git a/Documentation/devicetree/bindings/auxdisplay/holtek,ht16k33.yaml b/Documentation/devicetree/bindings/auxdisplay/holtek,ht16k33.yaml index b90eec2077b4..c46a2471f8b1 100644 --- a/Documentation/devicetree/bindings/auxdisplay/holtek,ht16k33.yaml +++ b/Documentation/devicetree/bindings/auxdisplay/holtek,ht16k33.yaml @@ -10,6 +10,7 @@ maintainers: - Robin van der Gracht allOf: + - $ref: /schemas/input/input.yaml# - $ref: /schemas/input/matrix-keymap.yaml# properties: @@ -33,9 +34,7 @@ properties: interrupts: maxItems: 1 - debounce-delay-ms: - maxItems: 1 - description: Debouncing interval time in milliseconds + debounce-delay-ms: true linux,keymap: true diff --git a/Documentation/devicetree/bindings/input/cirrus,ep9307-keypad.yaml b/Documentation/devicetree/bindings/input/cirrus,ep9307-keypad.yaml index a0d2460c55ab..25b8b29c87d7 100644 --- a/Documentation/devicetree/bindings/input/cirrus,ep9307-keypad.yaml +++ b/Documentation/devicetree/bindings/input/cirrus,ep9307-keypad.yaml @@ -10,6 +10,7 @@ maintainers: - Alexander Sverdlin allOf: + - $ref: input.yaml# - $ref: /schemas/input/matrix-keymap.yaml# description: @@ -37,10 +38,8 @@ properties: clocks: maxItems: 1 - debounce-delay-ms: - description: | - Time in microseconds that key must be pressed or - released for state change interrupt to trigger. + # Time for state change interrupt to trigger + debounce-delay-ms: true cirrus,prescale: description: row/column counter pre-scaler load value diff --git a/Documentation/devicetree/bindings/input/gpio-matrix-keypad.yaml b/Documentation/devicetree/bindings/input/gpio-matrix-keypad.yaml index ebfff9e42a36..69df24a5ae70 100644 --- a/Documentation/devicetree/bindings/input/gpio-matrix-keypad.yaml +++ b/Documentation/devicetree/bindings/input/gpio-matrix-keypad.yaml @@ -18,6 +18,7 @@ description: report the event using GPIO interrupts to the cpu. allOf: + - $ref: input.yaml# - $ref: /schemas/input/matrix-keymap.yaml# properties: @@ -46,9 +47,7 @@ properties: Force GPIO polarity to active low. In the absence of this property GPIOs are treated as active high. - debounce-delay-ms: - description: Debounce interval in milliseconds. - default: 0 + debounce-delay-ms: true col-scan-delay-us: description: diff --git a/Documentation/devicetree/bindings/input/input.yaml b/Documentation/devicetree/bindings/input/input.yaml index 94f7942189e8..502e0b7eb500 100644 --- a/Documentation/devicetree/bindings/input/input.yaml +++ b/Documentation/devicetree/bindings/input/input.yaml @@ -14,6 +14,14 @@ properties: description: Enable autorepeat when key is pressed and held down. type: boolean + debounce-delay-ms: + description: + Debounce delay in milliseconds. This is the time during which the key + press or release signal must remain stable before it is considered valid. + minimum: 0 + maximum: 999 + default: 0 + linux,keycodes: description: Specifies an array of numeric keycode values to be used for reporting diff --git a/Documentation/devicetree/bindings/input/mediatek,mt6779-keypad.yaml b/Documentation/devicetree/bindings/input/mediatek,mt6779-keypad.yaml index e365413732e7..914dd3283df3 100644 --- a/Documentation/devicetree/bindings/input/mediatek,mt6779-keypad.yaml +++ b/Documentation/devicetree/bindings/input/mediatek,mt6779-keypad.yaml @@ -10,6 +10,7 @@ maintainers: - Mattijs Korpershoek allOf: + - $ref: input.yaml# - $ref: /schemas/input/matrix-keymap.yaml# description: | diff --git a/Documentation/devicetree/bindings/mfd/fsl,mc13xxx.yaml b/Documentation/devicetree/bindings/mfd/fsl,mc13xxx.yaml index cfa69f1f380a..5cdb25be2731 100644 --- a/Documentation/devicetree/bindings/mfd/fsl,mc13xxx.yaml +++ b/Documentation/devicetree/bindings/mfd/fsl,mc13xxx.yaml @@ -76,8 +76,6 @@ properties: debounce-delay-ms: enum: [0, 30, 150, 750] default: 30 - description: - Sets the debouncing delay in milliseconds. active-low: description: Set active when pin is pulled low. -- cgit v1.2.3 From 906a37ba5481ac1b6f6a51c25eba88e43749d428 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Sat, 18 Apr 2026 22:17:43 -0700 Subject: dt-bindings: input: add settling-time-us common property Add common property that can be reused by other bindings. Reviewed-by: Rob Herring (Arm) Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260312180304.3865850-3-hugo@hugovil.com Signed-off-by: Dmitry Torokhov --- Documentation/devicetree/bindings/input/input.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/input/input.yaml b/Documentation/devicetree/bindings/input/input.yaml index 502e0b7eb500..64d1c46cb2f2 100644 --- a/Documentation/devicetree/bindings/input/input.yaml +++ b/Documentation/devicetree/bindings/input/input.yaml @@ -66,6 +66,14 @@ properties: reset automatically. Device with key pressed reset feature can specify this property. + settling-time-us: + description: + Delay, in microseconds, when activating an output line/col/row before + we can reliably read other input lines that maybe affected by this + output. This can be the case for an output with a RC circuit that affects + ramp-up/down times. + default: 0 + dependencies: linux,input-type: [ "linux,code" ] -- cgit v1.2.3 From 0d64bee764847a488ac33be8ec61b4ae7828f8f1 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Sat, 18 Apr 2026 22:18:30 -0700 Subject: dt-bindings: input: add GPIO charlieplex keypad Add DT bindings for GPIO charlieplex keypad. Reviewed-by: Rob Herring (Arm) Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260312180304.3865850-4-hugo@hugovil.com Signed-off-by: Dmitry Torokhov --- .../bindings/input/gpio-charlieplex-keypad.yaml | 108 +++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/gpio-charlieplex-keypad.yaml diff --git a/Documentation/devicetree/bindings/input/gpio-charlieplex-keypad.yaml b/Documentation/devicetree/bindings/input/gpio-charlieplex-keypad.yaml new file mode 100644 index 000000000000..c085de6dab85 --- /dev/null +++ b/Documentation/devicetree/bindings/input/gpio-charlieplex-keypad.yaml @@ -0,0 +1,108 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- + +$id: http://devicetree.org/schemas/input/gpio-charlieplex-keypad.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: GPIO charlieplex keypad + +maintainers: + - Hugo Villeneuve + +description: | + The charlieplex keypad supports N^2)-N different key combinations (where N is + the number of I/O lines). Key presses and releases are detected by configuring + only one line as output at a time, and reading other line states. This process + is repeated for each line. Diodes are required to ensure current flows in only + one direction between any pair of pins, as well as pull-up or pull-down + resistors on all I/O lines. + This mechanism doesn't allow to detect simultaneous key presses. + + Wiring example for 3 lines keyboard with 6 switches and 3 diodes (pull-up/down + resistors not shown but needed on L0, L1 and L2): + + L0 --+---------------------+----------------------+ + | | | + L1 -------+-----------+---------------------+ | + | | | | | | + L2 -------------+----------------+-----+ | | + | | | | | | | | | + | | | | | | | | | + | S1 \ S2 \ | S3 \ S4 \ | S5 \ S6 \ + | | | | | | | | | + | +--+--+ | +--+--+ | +--+--+ + | | | | | | + | D1 v | D2 v | D3 v + | - (k) | - (k) | - (k) + | | | | | | + +-------+ +-------+ +-------+ + + L: GPIO line + S: switch + D: diode (k indicates cathode) + +allOf: + - $ref: input.yaml# + - $ref: /schemas/input/matrix-keymap.yaml# + +properties: + compatible: + const: gpio-charlieplex-keypad + + autorepeat: true + + debounce-delay-ms: + default: 5 + + line-gpios: + description: + List of GPIOs used as lines. The gpio specifier for this property + depends on the gpio controller to which these lines are connected. + + linux,keymap: true + + poll-interval: true + + settling-time-us: true + + wakeup-source: true + +required: + - compatible + - line-gpios + - linux,keymap + - poll-interval + +additionalProperties: false + +examples: + - | + #include + #include + + keyboard { + compatible = "gpio-charlieplex-keypad"; + debounce-delay-ms = <20>; + poll-interval = <5>; + settling-time-us = <2>; + + line-gpios = <&gpio2 25 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN) + &gpio2 26 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN) + &gpio2 27 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>; + + /* MATRIX_KEY(output, input, key-code) */ + linux,keymap = < + /* + * According to wiring diagram above, if L1 is configured as + * output and HIGH, and we detect a HIGH level on input L0, + * then it means S1 is pressed: MATRIX_KEY(L1, L0, KEY...) + */ + MATRIX_KEY(1, 0, KEY_F1) /* S1 */ + MATRIX_KEY(2, 0, KEY_F2) /* S2 */ + MATRIX_KEY(0, 1, KEY_F3) /* S3 */ + MATRIX_KEY(2, 1, KEY_F4) /* S4 */ + MATRIX_KEY(1, 2, KEY_F5) /* S5 */ + MATRIX_KEY(0, 2, KEY_F6) /* S6 */ + >; + }; -- cgit v1.2.3 From 2ca45e57ea027fffe3350ae5e21ad9cecb0dce74 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Sat, 18 Apr 2026 22:18:54 -0700 Subject: Input: charlieplex_keypad - add GPIO charlieplex keypad Add support for GPIO-based charlieplex keypad, allowing to control N^2-N keys using N GPIO lines. Reuse matrix keypad keymap to simplify, even if there is no concept of rows and columns in this type of keyboard. Signed-off-by: Hugo Villeneuve Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260312180304.3865850-5-hugo@hugovil.com Signed-off-by: Dmitry Torokhov --- MAINTAINERS | 7 + drivers/input/keyboard/Kconfig | 14 ++ drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/charlieplex_keypad.c | 232 ++++++++++++++++++++++++++++ 4 files changed, 254 insertions(+) create mode 100644 drivers/input/keyboard/charlieplex_keypad.c diff --git a/MAINTAINERS b/MAINTAINERS index 7d10988cbc62..7cf5b55c5973 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5962,6 +5962,13 @@ S: Maintained F: Documentation/hwmon/powerz.rst F: drivers/hwmon/powerz.c +CHARLIEPLEX KEYPAD DRIVER +M: Hugo Villeneuve +S: Supported +W: http://www.mosaic-industries.com/embedded-systems/microcontroller-projects/electronic-circuits/matrix-keypad-scan-decode +F: Documentation/devicetree/bindings/input/gpio-charlieplex-keypad.yaml +F: drivers/input/keyboard/charlieplex_keypad.c + CHECKPATCH M: Andy Whitcroft M: Joe Perches diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 2ff4fef322c2..9d1019ba0245 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -289,6 +289,20 @@ config KEYBOARD_MATRIX To compile this driver as a module, choose M here: the module will be called matrix_keypad. +config KEYBOARD_CHARLIEPLEX + tristate "GPIO driven charlieplex keypad support" + depends on GPIOLIB || COMPILE_TEST + select INPUT_MATRIXKMAP + help + Enable support for GPIO driven charlieplex keypad. A charlieplex + keypad allows to use fewer GPIO lines to interface to key switches. + For example, an N lines charlieplex keypad can be used to interface + to N^2-N different key switches. However, this type of keypad + cannot detect more than one key press at a time. + + To compile this driver as a module, choose M here: the + module will be called charlieplex_keypad. + config KEYBOARD_HIL_OLD tristate "HP HIL keyboard support (simple driver)" depends on GSC || HP300 diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 2d906e14f3e2..60bb7baf802f 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o obj-$(CONFIG_KEYBOARD_BCM) += bcm-keypad.o obj-$(CONFIG_KEYBOARD_CAP11XX) += cap11xx.o +obj-$(CONFIG_KEYBOARD_CHARLIEPLEX) += charlieplex_keypad.o obj-$(CONFIG_KEYBOARD_CLPS711X) += clps711x-keypad.o obj-$(CONFIG_KEYBOARD_CROS_EC) += cros_ec_keyb.o obj-$(CONFIG_KEYBOARD_CYPRESS_SF) += cypress-sf.o diff --git a/drivers/input/keyboard/charlieplex_keypad.c b/drivers/input/keyboard/charlieplex_keypad.c new file mode 100644 index 000000000000..6dbb5c183f02 --- /dev/null +++ b/drivers/input/keyboard/charlieplex_keypad.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * GPIO driven charlieplex keypad driver + * + * Copyright (c) 2026 Hugo Villeneuve + * + * Based on matrix_keyboard.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct charlieplex_keypad { + struct input_dev *input_dev; + struct gpio_descs *line_gpios; + unsigned int nlines; + unsigned int settling_time_us; + unsigned int debounce_threshold; + unsigned int debounce_count; + int debounce_code; + int current_code; +}; + +static void charlieplex_keypad_report_key(struct input_dev *input) +{ + struct charlieplex_keypad *keypad = input_get_drvdata(input); + const unsigned short *keycodes = input->keycode; + + if (keypad->current_code > 0) { + input_event(input, EV_MSC, MSC_SCAN, keypad->current_code); + input_report_key(input, keycodes[keypad->current_code], 0); + input_sync(input); + } + + if (keypad->debounce_code) { + input_event(input, EV_MSC, MSC_SCAN, keypad->debounce_code); + input_report_key(input, keycodes[keypad->debounce_code], 1); + input_sync(input); + } + + keypad->current_code = keypad->debounce_code; +} + +static void charlieplex_keypad_check_switch_change(struct input_dev *input, + unsigned int code) +{ + struct charlieplex_keypad *keypad = input_get_drvdata(input); + + if (code != keypad->debounce_code) { + keypad->debounce_count = 0; + keypad->debounce_code = code; + } + + if (keypad->debounce_code != keypad->current_code) { + if (keypad->debounce_count++ >= keypad->debounce_threshold) + charlieplex_keypad_report_key(input); + } +} + +static int charlieplex_keypad_scan_line(struct charlieplex_keypad *keypad, + unsigned int oline) +{ + struct gpio_descs *line_gpios = keypad->line_gpios; + DECLARE_BITMAP(values, MATRIX_MAX_ROWS); + int err; + + /* Activate only one line as output at a time. */ + gpiod_direction_output(line_gpios->desc[oline], 1); + + if (keypad->settling_time_us) + fsleep(keypad->settling_time_us); + + /* Read input on all other lines. */ + err = gpiod_get_array_value_cansleep(line_gpios->ndescs, line_gpios->desc, + line_gpios->info, values); + + gpiod_direction_input(line_gpios->desc[oline]); + + if (err) + return err; + + for (unsigned int iline = 0; iline < keypad->nlines; iline++) { + if (iline == oline) + continue; /* Do not read active output line. */ + + /* Check if GPIO is asserted. */ + if (test_bit(iline, values)) + return MATRIX_SCAN_CODE(oline, iline, + get_count_order(keypad->nlines)); + } + + return 0; +} + +static void charlieplex_keypad_poll(struct input_dev *input) +{ + struct charlieplex_keypad *keypad = input_get_drvdata(input); + int code = 0; + + for (unsigned int oline = 0; oline < keypad->nlines; oline++) { + code = charlieplex_keypad_scan_line(keypad, oline); + if (code != 0) + break; + } + + if (code >= 0) + charlieplex_keypad_check_switch_change(input, code); +} + +static int charlieplex_keypad_init_gpio(struct platform_device *pdev, + struct charlieplex_keypad *keypad) +{ + char **pin_names; + char label[32]; + + snprintf(label, sizeof(label), "%s-pin", pdev->name); + + keypad->line_gpios = devm_gpiod_get_array(&pdev->dev, "line", GPIOD_IN); + if (IS_ERR(keypad->line_gpios)) + return PTR_ERR(keypad->line_gpios); + + keypad->nlines = keypad->line_gpios->ndescs; + + if (keypad->nlines > MATRIX_MAX_ROWS) + return -EINVAL; + + pin_names = devm_kasprintf_strarray(&pdev->dev, label, keypad->nlines); + if (IS_ERR(pin_names)) + return PTR_ERR(pin_names); + + for (unsigned int i = 0; i < keypad->line_gpios->ndescs; i++) + gpiod_set_consumer_name(keypad->line_gpios->desc[i], pin_names[i]); + + return 0; +} + +static int charlieplex_keypad_probe(struct platform_device *pdev) +{ + struct charlieplex_keypad *keypad; + struct input_dev *input_dev; + unsigned int debounce_interval_ms = 5; + unsigned int poll_interval_ms; + int err; + + keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL); + if (!keypad) + return -ENOMEM; + + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) + return -ENOMEM; + + keypad->input_dev = input_dev; + + err = device_property_read_u32(&pdev->dev, "poll-interval", &poll_interval_ms); + if (err) + return dev_err_probe(&pdev->dev, err, + "failed to parse 'poll-interval' property\n"); + + if (poll_interval_ms == 0) + return dev_err_probe(&pdev->dev, -EINVAL, "invalid 'poll-interval' value\n"); + + device_property_read_u32(&pdev->dev, "debounce-delay-ms", &debounce_interval_ms); + device_property_read_u32(&pdev->dev, "settling-time-us", &keypad->settling_time_us); + + keypad->current_code = -1; + keypad->debounce_code = -1; + keypad->debounce_threshold = DIV_ROUND_UP(debounce_interval_ms, poll_interval_ms); + + err = charlieplex_keypad_init_gpio(pdev, keypad); + if (err) + return err; + + input_dev->name = pdev->name; + input_dev->id.bustype = BUS_HOST; + + err = matrix_keypad_build_keymap(NULL, NULL, keypad->nlines, + keypad->nlines, NULL, input_dev); + if (err) + return dev_err_probe(&pdev->dev, err, "failed to build keymap\n"); + + if (device_property_read_bool(&pdev->dev, "autorepeat")) + __set_bit(EV_REP, input_dev->evbit); + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + + err = input_setup_polling(input_dev, charlieplex_keypad_poll); + if (err) + return dev_err_probe(&pdev->dev, err, "unable to set up polling\n"); + + input_set_poll_interval(input_dev, poll_interval_ms); + + input_set_drvdata(input_dev, keypad); + + err = input_register_device(keypad->input_dev); + if (err) + return err; + + return 0; +} + +static const struct of_device_id charlieplex_keypad_dt_match[] = { + { .compatible = "gpio-charlieplex-keypad" }, + { } +}; +MODULE_DEVICE_TABLE(of, charlieplex_keypad_dt_match); + +static struct platform_driver charlieplex_keypad_driver = { + .probe = charlieplex_keypad_probe, + .driver = { + .name = "charlieplex-keypad", + .of_match_table = charlieplex_keypad_dt_match, + }, +}; +module_platform_driver(charlieplex_keypad_driver); + +MODULE_AUTHOR("Hugo Villeneuve "); +MODULE_DESCRIPTION("GPIO driven charlieplex keypad driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3