Input: i8042 - Add support for platform filter contexts

Currently the platform filter cannot access any driver-specific state
which forces drivers installing a i8042 filter to have at least some
kind of global pointer for their filter.

Allow callers of i8042_install_filter() to submit a context pointer
which is then passed to the i8042 filter. This frees drivers from the
responsibility of having to manage this global pointer themself.

Also introduce a separate type for the i8042 filter (i8042_filter_t)
so that the function definitions can stay compact.

Tested on a Dell Inspiron 3505.

Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Armin Wolf <W_Armin@gmx.de>
Link: https://lore.kernel.org/r/20250113221314.435812-1-W_Armin@gmx.de
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
This commit is contained in:
Armin Wolf 2025-01-13 23:13:14 +01:00 committed by Ilpo Järvinen
parent 1e4e3dff9e
commit cec8c359f8
No known key found for this signature in database
GPG Key ID: 59AC4F6153E5CE31
11 changed files with 48 additions and 33 deletions

View File

@ -121,7 +121,7 @@ static void slidebar_mode_set(u8 mode)
}
static bool slidebar_i8042_filter(unsigned char data, unsigned char str,
struct serio *port)
struct serio *port, void *context)
{
static bool extended = false;
@ -219,7 +219,7 @@ static int __init ideapad_probe(struct platform_device* pdev)
input_set_capability(slidebar_input_dev, EV_ABS, ABS_X);
input_set_abs_params(slidebar_input_dev, ABS_X, 0, 0xff, 0, 0);
err = i8042_install_filter(slidebar_i8042_filter);
err = i8042_install_filter(slidebar_i8042_filter, NULL);
if (err) {
dev_err(&pdev->dev,
"Failed to install i8042 filter: %d\n", err);

View File

@ -179,8 +179,8 @@ static struct platform_device *i8042_platform_device;
static struct notifier_block i8042_kbd_bind_notifier_block;
static bool i8042_handle_data(int irq);
static bool (*i8042_platform_filter)(unsigned char data, unsigned char str,
struct serio *serio);
static i8042_filter_t i8042_platform_filter;
static void *i8042_platform_filter_context;
void i8042_lock_chip(void)
{
@ -194,8 +194,7 @@ void i8042_unlock_chip(void)
}
EXPORT_SYMBOL(i8042_unlock_chip);
int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str,
struct serio *serio))
int i8042_install_filter(i8042_filter_t filter, void *context)
{
guard(spinlock_irqsave)(&i8042_lock);
@ -203,12 +202,12 @@ int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str,
return -EBUSY;
i8042_platform_filter = filter;
i8042_platform_filter_context = context;
return 0;
}
EXPORT_SYMBOL(i8042_install_filter);
int i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str,
struct serio *port))
int i8042_remove_filter(i8042_filter_t filter)
{
guard(spinlock_irqsave)(&i8042_lock);
@ -216,6 +215,7 @@ int i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str,
return -EINVAL;
i8042_platform_filter = NULL;
i8042_platform_filter_context = NULL;
return 0;
}
EXPORT_SYMBOL(i8042_remove_filter);
@ -480,7 +480,10 @@ static bool i8042_filter(unsigned char data, unsigned char str,
}
}
if (i8042_platform_filter && i8042_platform_filter(data, str, serio)) {
if (!i8042_platform_filter)
return false;
if (i8042_platform_filter(data, str, serio, i8042_platform_filter_context)) {
dbg("Filtered out by platform filter\n");
return true;
}

View File

@ -50,7 +50,8 @@ MODULE_PARM_DESC(tablet_mode_sw, "Tablet mode detect: -1:auto 0:disable 1:kbd-do
static struct quirk_entry *quirks;
static bool atkbd_reports_vol_keys;
static bool asus_i8042_filter(unsigned char data, unsigned char str, struct serio *port)
static bool asus_i8042_filter(unsigned char data, unsigned char str, struct serio *port,
void *context)
{
static bool extended_e0;
static bool extended_e1;

View File

@ -4824,7 +4824,7 @@ static int asus_wmi_add(struct platform_device *pdev)
}
if (asus->driver->i8042_filter) {
err = i8042_install_filter(asus->driver->i8042_filter);
err = i8042_install_filter(asus->driver->i8042_filter, NULL);
if (err)
pr_warn("Unable to install key filter - %d\n", err);
}

View File

@ -73,8 +73,7 @@ struct asus_wmi_driver {
void (*key_filter) (struct asus_wmi_driver *driver, int *code,
unsigned int *value, bool *autorelease);
/* Optional standard i8042 filter */
bool (*i8042_filter)(unsigned char data, unsigned char str,
struct serio *serio);
i8042_filter_t i8042_filter;
int (*probe) (struct platform_device *device);
void (*detect_quirks) (struct asus_wmi_driver *driver);

View File

@ -725,8 +725,8 @@ static void dell_update_rfkill(struct work_struct *ignored)
}
static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
struct serio *port)
static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str, struct serio *port,
void *context)
{
static bool extended;
@ -884,7 +884,7 @@ static int __init dell_setup_rfkill(void)
pr_warn("Unable to register dell rbtn notifier\n");
goto err_filter;
} else {
ret = i8042_install_filter(dell_laptop_i8042_filter);
ret = i8042_install_filter(dell_laptop_i8042_filter, NULL);
if (ret) {
pr_warn("Unable to install key filter\n");
goto err_filter;

View File

@ -267,7 +267,7 @@ static struct delayed_led_classdev hpled_led = {
};
static bool hp_accel_i8042_filter(unsigned char data, unsigned char str,
struct serio *port)
struct serio *port, void *context)
{
static bool extended;
@ -326,7 +326,7 @@ static int lis3lv02d_probe(struct platform_device *device)
/* filter to remove HPQ6000 accelerometer data
* from keyboard bus stream */
if (strstr(dev_name(&device->dev), "HPQ6000"))
i8042_install_filter(hp_accel_i8042_filter);
i8042_install_filter(hp_accel_i8042_filter, NULL);
INIT_WORK(&hpled_led.work, delayed_set_status_worker);
ret = led_classdev_register(NULL, &hpled_led.led_classdev);

View File

@ -806,8 +806,8 @@ static void msi_send_touchpad_key(struct work_struct *ignored)
}
static DECLARE_DELAYED_WORK(msi_touchpad_dwork, msi_send_touchpad_key);
static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
struct serio *port)
static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, struct serio *port,
void *context)
{
static bool extended;
@ -996,7 +996,7 @@ static int __init load_scm_model_init(struct platform_device *sdev)
if (result)
goto fail_input;
result = i8042_install_filter(msi_laptop_i8042_filter);
result = i8042_install_filter(msi_laptop_i8042_filter, NULL);
if (result) {
pr_err("Unable to install key filter\n");
goto fail_filter;

View File

@ -260,7 +260,7 @@ struct pcc_acpi {
* keypress events over the PS/2 kbd interface, filter these out.
*/
static bool panasonic_i8042_filter(unsigned char data, unsigned char str,
struct serio *port)
struct serio *port, void *context)
{
static bool extended;
@ -1100,7 +1100,7 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
pcc->platform = NULL;
}
i8042_install_filter(panasonic_i8042_filter);
i8042_install_filter(panasonic_i8042_filter, NULL);
return 0;
out_platform:

View File

@ -2755,7 +2755,7 @@ static int toshiba_acpi_enable_hotkeys(struct toshiba_acpi_dev *dev)
}
static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str,
struct serio *port)
struct serio *port, void *context)
{
if (str & I8042_STR_AUXDATA)
return false;
@ -2915,7 +2915,7 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
if (ec_handle && acpi_has_method(ec_handle, "NTFY")) {
INIT_WORK(&dev->hotkey_work, toshiba_acpi_hotkey_work);
error = i8042_install_filter(toshiba_acpi_i8042_filter);
error = i8042_install_filter(toshiba_acpi_i8042_filter, NULL);
if (error) {
pr_err("Error installing key filter\n");
goto err_free_dev;

View File

@ -54,15 +54,29 @@
struct serio;
/**
* typedef i8042_filter_t - i8042 filter callback
* @data: Data received by the i8042 controller
* @str: Status register of the i8042 controller
* @serio: Serio of the i8042 controller
* @context: Context pointer associated with this callback
*
* This represents a i8042 filter callback which can be used with i8042_install_filter()
* and i8042_remove_filter() to filter the i8042 input for platform-specific key codes.
*
* Context: Interrupt context.
* Returns: true if the data should be filtered out, false if otherwise.
*/
typedef bool (*i8042_filter_t)(unsigned char data, unsigned char str, struct serio *serio,
void *context);
#if defined(CONFIG_SERIO_I8042) || defined(CONFIG_SERIO_I8042_MODULE)
void i8042_lock_chip(void);
void i8042_unlock_chip(void);
int i8042_command(unsigned char *param, int command);
int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str,
struct serio *serio));
int i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str,
struct serio *serio));
int i8042_install_filter(i8042_filter_t filter, void *context);
int i8042_remove_filter(i8042_filter_t filter);
#else
@ -79,14 +93,12 @@ static inline int i8042_command(unsigned char *param, int command)
return -ENODEV;
}
static inline int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str,
struct serio *serio))
static inline int i8042_install_filter(i8042_filter_t filter, void *context)
{
return -ENODEV;
}
static inline int i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str,
struct serio *serio))
static inline int i8042_remove_filter(i8042_filter_t filter)
{
return -ENODEV;
}