From 1976d7d200c5a32e72293a2ada36b7b7c9d6dd6e Mon Sep 17 00:00:00 2001 From: Nick Black Date: Thu, 11 Jul 2019 23:42:03 -0700 Subject: Input: synaptics - whitelist Lenovo T580 SMBus intertouch Adds the Lenovo T580 to the SMBus intertouch list for Synaptics touchpads. I've tested with this for a week now, and it seems a great improvement. It's also nice to have the complaint gone from dmesg. Signed-off-by: Nick Black Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index b6da0c1267e3..774cddc3f066 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -178,6 +178,7 @@ static const char * const smbus_pnp_ids[] = { "LEN0092", /* X1 Carbon 6 */ "LEN0096", /* X280 */ "LEN0097", /* X280 -> ALPS trackpoint */ + "LEN009b", /* T580 */ "LEN200f", /* T450s */ "SYN3052", /* HP EliteBook 840 G4 */ "SYN3221", /* HP 15-ay000 */ -- cgit v1.2.3-59-g8ed1b From 071cfc43c875ee2e6ac2ae6929c2c153e1d13fe7 Mon Sep 17 00:00:00 2001 From: Nishka Dasgupta Date: Thu, 11 Jul 2019 23:49:16 -0700 Subject: Input: sun4i-lradc-keys - add of_node_put() before return Each iteration of for_each_child_of_node puts the previous node, but in the case of a return from the middle of the loop, there is no put, thus causing a memory leak. Hence add an of_node_put before the return in three places. Issue found with Coccinelle. Signed-off-by: Nishka Dasgupta Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/sun4i-lradc-keys.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/input/keyboard/sun4i-lradc-keys.c b/drivers/input/keyboard/sun4i-lradc-keys.c index df3eec72a9b2..40903a07af99 100644 --- a/drivers/input/keyboard/sun4i-lradc-keys.c +++ b/drivers/input/keyboard/sun4i-lradc-keys.c @@ -207,18 +207,21 @@ static int sun4i_lradc_load_dt_keymap(struct device *dev, error = of_property_read_u32(pp, "channel", &channel); if (error || channel != 0) { dev_err(dev, "%pOFn: Inval channel prop\n", pp); + of_node_put(pp); return -EINVAL; } error = of_property_read_u32(pp, "voltage", &map->voltage); if (error) { dev_err(dev, "%pOFn: Inval voltage prop\n", pp); + of_node_put(pp); return -EINVAL; } error = of_property_read_u32(pp, "linux,code", &map->keycode); if (error) { dev_err(dev, "%pOFn: Inval linux,code prop\n", pp); + of_node_put(pp); return -EINVAL; } -- cgit v1.2.3-59-g8ed1b From cc95527cceb3d1aaadd5cc1cf601d466af2e53f2 Mon Sep 17 00:00:00 2001 From: Nishka Dasgupta Date: Thu, 11 Jul 2019 23:49:47 -0700 Subject: Input: mtk-pmic-keys - add of_node_put() before return Each iteration of for_each_child_of_node puts the previous node, but in the case of a return from the middle of the loop, there is no put, thus causing a memory leak. Hence add an of_node_put before the return in three places. Issue found with Coccinelle. Signed-off-by: Nishka Dasgupta Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/mtk-pmic-keys.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/input/keyboard/mtk-pmic-keys.c b/drivers/input/keyboard/mtk-pmic-keys.c index 8e6ebab05ab4..ebfe119dd398 100644 --- a/drivers/input/keyboard/mtk-pmic-keys.c +++ b/drivers/input/keyboard/mtk-pmic-keys.c @@ -286,8 +286,10 @@ static int mtk_pmic_keys_probe(struct platform_device *pdev) keys->keys[index].regs = &mtk_pmic_regs->keys_regs[index]; keys->keys[index].irq = platform_get_irq(pdev, index); - if (keys->keys[index].irq < 0) + if (keys->keys[index].irq < 0) { + of_node_put(child); return keys->keys[index].irq; + } error = of_property_read_u32(child, "linux,keycodes", &keys->keys[index].keycode); @@ -295,6 +297,7 @@ static int mtk_pmic_keys_probe(struct platform_device *pdev) dev_err(keys->dev, "failed to read key:%d linux,keycode property: %d\n", index, error); + of_node_put(child); return error; } @@ -302,8 +305,10 @@ static int mtk_pmic_keys_probe(struct platform_device *pdev) keys->keys[index].wakeup = true; error = mtk_pmic_key_setup(keys, &keys->keys[index]); - if (error) + if (error) { + of_node_put(child); return error; + } index++; } -- cgit v1.2.3-59-g8ed1b From 2a017fd82c5402b3c8df5e3d6e5165d9e6147dc1 Mon Sep 17 00:00:00 2001 From: Grant Hernandez Date: Sat, 13 Jul 2019 01:00:12 -0700 Subject: Input: gtco - bounds check collection indent level The GTCO tablet input driver configures itself from an HID report sent via USB during the initial enumeration process. Some debugging messages are generated during the parsing. A debugging message indentation counter is not bounds checked, leading to the ability for a specially crafted HID report to cause '-' and null bytes be written past the end of the indentation array. As long as the kernel has CONFIG_DYNAMIC_DEBUG enabled, this code will not be optimized out. This was discovered during code review after a previous syzkaller bug was found in this driver. Signed-off-by: Grant Hernandez Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/gtco.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/input/tablet/gtco.c b/drivers/input/tablet/gtco.c index 4b8b9d7aa75e..35031228a6d0 100644 --- a/drivers/input/tablet/gtco.c +++ b/drivers/input/tablet/gtco.c @@ -78,6 +78,7 @@ Scott Hill shill@gtcocalcomp.com /* Max size of a single report */ #define REPORT_MAX_SIZE 10 +#define MAX_COLLECTION_LEVELS 10 /* Bitmask whether pen is in range */ @@ -223,8 +224,7 @@ static void parse_hid_report_descriptor(struct gtco *device, char * report, char maintype = 'x'; char globtype[12]; int indent = 0; - char indentstr[10] = ""; - + char indentstr[MAX_COLLECTION_LEVELS + 1] = { 0 }; dev_dbg(ddev, "======>>>>>>PARSE<<<<<<======\n"); @@ -350,6 +350,13 @@ static void parse_hid_report_descriptor(struct gtco *device, char * report, case TAG_MAIN_COL_START: maintype = 'S'; + if (indent == MAX_COLLECTION_LEVELS) { + dev_err(ddev, "Collection level %d would exceed limit of %d\n", + indent + 1, + MAX_COLLECTION_LEVELS); + break; + } + if (data == 0) { dev_dbg(ddev, "======>>>>>> Physical\n"); strcpy(globtype, "Physical"); @@ -369,8 +376,15 @@ static void parse_hid_report_descriptor(struct gtco *device, char * report, break; case TAG_MAIN_COL_END: - dev_dbg(ddev, "<<<<<<======\n"); maintype = 'E'; + + if (indent == 0) { + dev_err(ddev, "Collection level already at zero\n"); + break; + } + + dev_dbg(ddev, "<<<<<<======\n"); + indent--; for (x = 0; x < indent; x++) indentstr[x] = '-'; -- cgit v1.2.3-59-g8ed1b From e2c063c8970044033dac9cedc686db27008e4bb1 Mon Sep 17 00:00:00 2001 From: Fuqian Huang Date: Sat, 13 Jul 2019 01:05:19 -0700 Subject: Input: auo-pixcir-ts - switch to using devm_add_action_or_reset() devm_add_action_or_reset() is introduced as a helper function which internally calls devm_add_action(). If devm_add_action() fails then it will execute the action mentioned and return the error code. This reduce source code size (avoid writing the action twice) and reduce the likelyhood of bugs. Signed-off-by: Fuqian Huang Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/auo-pixcir-ts.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/input/touchscreen/auo-pixcir-ts.c b/drivers/input/touchscreen/auo-pixcir-ts.c index df8ca856393b..02507c0b6641 100644 --- a/drivers/input/touchscreen/auo-pixcir-ts.c +++ b/drivers/input/touchscreen/auo-pixcir-ts.c @@ -612,9 +612,8 @@ static int auo_pixcir_probe(struct i2c_client *client, return error; } - error = devm_add_action(&client->dev, auo_pixcir_reset, ts); + error = devm_add_action_or_reset(&client->dev, auo_pixcir_reset, ts); if (error) { - auo_pixcir_reset(ts); dev_err(&client->dev, "failed to register reset action, %d\n", error); return error; -- cgit v1.2.3-59-g8ed1b From d38b6cf50a6b911ee46683330f3af17fcceca509 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sun, 14 Jul 2019 11:27:51 -0700 Subject: Input: synaptics - fix misuse of strlcpy Probable cut&paste typo - use the correct field size. Signed-off-by: Joe Perches Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 774cddc3f066..12313424e97d 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -706,7 +706,7 @@ static void synaptics_pt_create(struct psmouse *psmouse) serio->id.type = SERIO_PS_PSTHRU; strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name)); - strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name)); + strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->phys)); serio->write = synaptics_pt_write; serio->start = synaptics_pt_start; serio->stop = synaptics_pt_stop; -- cgit v1.2.3-59-g8ed1b From 6d45d3e08b7460d8ae4199a71a5ad271b0d98f83 Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Sun, 14 Jul 2019 14:25:59 -0700 Subject: Input: iforce - remove empty multiline comments Those are remnants of the SPDX identifier migration, which haven't been removed properly. Signed-off-by: Tim Schumacher Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/iforce/iforce-ff.c | 3 --- drivers/input/joystick/iforce/iforce-main.c | 3 --- drivers/input/joystick/iforce/iforce-packets.c | 3 --- drivers/input/joystick/iforce/iforce-serio.c | 3 --- drivers/input/joystick/iforce/iforce-usb.c | 3 --- drivers/input/joystick/iforce/iforce.h | 3 --- 6 files changed, 18 deletions(-) diff --git a/drivers/input/joystick/iforce/iforce-ff.c b/drivers/input/joystick/iforce/iforce-ff.c index 4cadebd8b9c4..95c0348843e6 100644 --- a/drivers/input/joystick/iforce/iforce-ff.c +++ b/drivers/input/joystick/iforce/iforce-ff.c @@ -6,9 +6,6 @@ * USB/RS232 I-Force joysticks and wheels. */ -/* - */ - #include "iforce.h" /* diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c index 9a5f90da06ec..b2a68bc9f0b4 100644 --- a/drivers/input/joystick/iforce/iforce-main.c +++ b/drivers/input/joystick/iforce/iforce-main.c @@ -6,9 +6,6 @@ * USB/RS232 I-Force joysticks and wheels. */ -/* - */ - #include #include "iforce.h" diff --git a/drivers/input/joystick/iforce/iforce-packets.c b/drivers/input/joystick/iforce/iforce-packets.c index b313e38b2c3a..763642c8cee9 100644 --- a/drivers/input/joystick/iforce/iforce-packets.c +++ b/drivers/input/joystick/iforce/iforce-packets.c @@ -6,9 +6,6 @@ * USB/RS232 I-Force joysticks and wheels. */ -/* - */ - #include #include "iforce.h" diff --git a/drivers/input/joystick/iforce/iforce-serio.c b/drivers/input/joystick/iforce/iforce-serio.c index bbe31e0b759f..f95a81b9fac7 100644 --- a/drivers/input/joystick/iforce/iforce-serio.c +++ b/drivers/input/joystick/iforce/iforce-serio.c @@ -6,9 +6,6 @@ * USB/RS232 I-Force joysticks and wheels. */ -/* - */ - #include #include "iforce.h" diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c index ade376bfb79f..29abfeeef9a5 100644 --- a/drivers/input/joystick/iforce/iforce-usb.c +++ b/drivers/input/joystick/iforce/iforce-usb.c @@ -6,9 +6,6 @@ * USB/RS232 I-Force joysticks and wheels. */ -/* - */ - #include #include "iforce.h" diff --git a/drivers/input/joystick/iforce/iforce.h b/drivers/input/joystick/iforce/iforce.h index 9cfa460466aa..6aa761ebbdf7 100644 --- a/drivers/input/joystick/iforce/iforce.h +++ b/drivers/input/joystick/iforce/iforce.h @@ -6,9 +6,6 @@ * USB/RS232 I-Force joysticks and wheels. */ -/* - */ - #include #include #include -- cgit v1.2.3-59-g8ed1b From b8374b85529d9fb974be80c0809c070398aa8cb2 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 14 Jul 2019 14:30:36 -0700 Subject: Input: adp5589 - initialize GPIO controller parent device While not strictly required for normal operation setting the GPIO parent device allows the GPIO framework to generate more verbose debug output for the GPIO chip. Signed-off-by: Lars-Peter Clausen Signed-off-by: Alexandru Ardelean Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/adp5589-keys.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c index 4c05c70a8cf3..4f96a4a99e5b 100644 --- a/drivers/input/keyboard/adp5589-keys.c +++ b/drivers/input/keyboard/adp5589-keys.c @@ -505,6 +505,7 @@ static int adp5589_gpio_add(struct adp5589_kpad *kpad) if (!gpio_data) return 0; + kpad->gc.parent = dev; kpad->gc.ngpio = adp5589_build_gpiomap(kpad, pdata); if (kpad->gc.ngpio == 0) { dev_info(dev, "No unused gpios left to export\n"); -- cgit v1.2.3-59-g8ed1b From 88f28e95e72eebecce5ba6944adffdf3654f626c Mon Sep 17 00:00:00 2001 From: Maya Nakamura Date: Sun, 14 Jul 2019 16:27:18 -0700 Subject: Input: hyperv-keyboard - remove dependencies on PAGE_SIZE for ring buffer Define the ring buffer size as a constant expression because it should not depend on the guest page size. Signed-off-by: Maya Nakamura Reviewed-by: Michael Kelley Signed-off-by: Dmitry Torokhov --- drivers/input/serio/hyperv-keyboard.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/serio/hyperv-keyboard.c b/drivers/input/serio/hyperv-keyboard.c index 8e457e50f837..88ae7c2ac3c8 100644 --- a/drivers/input/serio/hyperv-keyboard.c +++ b/drivers/input/serio/hyperv-keyboard.c @@ -75,8 +75,8 @@ struct synth_kbd_keystroke { #define HK_MAXIMUM_MESSAGE_SIZE 256 -#define KBD_VSC_SEND_RING_BUFFER_SIZE (10 * PAGE_SIZE) -#define KBD_VSC_RECV_RING_BUFFER_SIZE (10 * PAGE_SIZE) +#define KBD_VSC_SEND_RING_BUFFER_SIZE (40 * 1024) +#define KBD_VSC_RECV_RING_BUFFER_SIZE (40 * 1024) #define XTKBD_EMUL0 0xe0 #define XTKBD_EMUL1 0xe1 -- cgit v1.2.3-59-g8ed1b From 7e4935ccc3236751e5fe4bd6846f86e46bb2e427 Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Mon, 15 Jul 2019 10:00:58 -0700 Subject: Input: alps - don't handle ALPS cs19 trackpoint-only device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On a latest Lenovo laptop, the trackpoint and 3 buttons below it don't work at all, when we move the trackpoint or press those 3 buttons, the kernel will print out: "Rejected trackstick packet from non DualPoint device" This device is identified as an alps touchpad but the packet has trackpoint format, so the alps.c drops the packet and prints out the message above. According to XiaoXiao's explanation, this device is named cs19 and is trackpoint-only device, its firmware is only for trackpoint, it is independent of touchpad and is a device completely different from DualPoint ones. To drive this device with mininal changes to the existing driver, we just let the alps driver not handle this device, then the trackpoint.c will be the driver of this device if the trackpoint driver is enabled. (if not, this device will fallback to a bare PS/2 device) With the trackpoint.c, this trackpoint and 3 buttons all work well, they have all features that the trackpoint should have, like scrolling-screen, drag-and-drop and frame-selection. Signed-off-by: XiaoXiao Liu Signed-off-by: Hui Wang Reviewed-by: Pali Rohár Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 8996323ce8d9..62ffea00902a 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -21,6 +21,7 @@ #include "psmouse.h" #include "alps.h" +#include "trackpoint.h" /* * Definitions for ALPS version 3 and 4 command mode protocol @@ -2861,6 +2862,23 @@ static const struct alps_protocol_info *alps_match_table(unsigned char *e7, return NULL; } +static bool alps_is_cs19_trackpoint(struct psmouse *psmouse) +{ + u8 param[2] = { 0 }; + + if (ps2_command(&psmouse->ps2dev, + param, MAKE_PS2_CMD(0, 2, TP_READ_ID))) + return false; + + /* + * param[0] contains the trackpoint device variant_id while + * param[1] contains the firmware_id. So far all alps + * trackpoint-only devices have their variant_ids equal + * TP_VARIANT_ALPS and their firmware_ids are in 0x20~0x2f range. + */ + return param[0] == TP_VARIANT_ALPS && (param[1] & 0x20); +} + static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) { const struct alps_protocol_info *protocol; @@ -3161,6 +3179,20 @@ int alps_detect(struct psmouse *psmouse, bool set_properties) if (error) return error; + /* + * ALPS cs19 is a trackpoint-only device, and uses different + * protocol than DualPoint ones, so we return -EINVAL here and let + * trackpoint.c drive this device. If the trackpoint driver is not + * enabled, the device will fall back to a bare PS/2 mouse. + * If ps2_command() fails here, we depend on the immediately + * followed psmouse_reset() to reset the device to normal state. + */ + if (alps_is_cs19_trackpoint(psmouse)) { + psmouse_dbg(psmouse, + "ALPS CS19 trackpoint-only device detected, ignoring\n"); + return -EINVAL; + } + /* * Reset the device to make sure it is fully operational: * on some laptops, like certain Dell Latitudes, we may -- cgit v1.2.3-59-g8ed1b From 038b1a05eae6666b731920e46f47d8e2332e07ff Mon Sep 17 00:00:00 2001 From: Ronald Tschalär Date: Mon, 15 Jul 2019 10:30:11 -0700 Subject: Input: add Apple SPI keyboard and trackpad driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The keyboard and trackpad on recent MacBook's (since 8,1) and MacBookPro's (13,* and 14,*) are attached to an SPI controller instead of USB, as previously. The higher level protocol is not publicly documented and hence has been reverse engineered. As a consequence there are still a number of unknown fields and commands. However, the known parts have been working well and received extensive testing and use. In order for this driver to work, the proper SPI drivers need to be loaded too; for MB8,1 these are spi_pxa2xx_platform and spi_pxa2xx_pci; for all others they are spi_pxa2xx_platform and intel_lpss_pci. Link: https://bugzilla.kernel.org/show_bug.cgi?id=99891 Link: https://bugzilla.kernel.org/show_bug.cgi?id=108331 Signed-off-by: Ronald Tschalär Reviewed-by: Andy Shevchenko Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 16 + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/applespi.c | 1978 +++++++++++++++++++++++++++++++ drivers/input/keyboard/applespi.h | 29 + drivers/input/keyboard/applespi_trace.h | 93 ++ 5 files changed, 2117 insertions(+) create mode 100644 drivers/input/keyboard/applespi.c create mode 100644 drivers/input/keyboard/applespi.h create mode 100644 drivers/input/keyboard/applespi_trace.h diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 7c4f19dab34f..8e9c3ea9d5e7 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -71,6 +71,22 @@ config KEYBOARD_AMIGA config ATARI_KBD_CORE bool +config KEYBOARD_APPLESPI + tristate "Apple SPI keyboard and trackpad" + depends on ACPI && EFI + depends on SPI + depends on X86 || COMPILE_TEST + help + Say Y here if you are running Linux on any Apple MacBook8,1 or later, + or any MacBookPro13,* or MacBookPro14,*. + + You will also need to enable appropriate SPI master controllers: + spi_pxa2xx_platform and spi_pxa2xx_pci for MacBook8,1, and + spi_pxa2xx_platform and intel_lpss_pci for the rest. + + To compile this driver as a module, choose M here: the + module will be called applespi. + config KEYBOARD_ATARI tristate "Atari keyboard" depends on ATARI diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index f0291ca39f62..06a0af6efeae 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o +obj-$(CONFIG_KEYBOARD_APPLESPI) += applespi.o obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o obj-$(CONFIG_KEYBOARD_BCM) += bcm-keypad.o diff --git a/drivers/input/keyboard/applespi.c b/drivers/input/keyboard/applespi.c new file mode 100644 index 000000000000..c1a68438245a --- /dev/null +++ b/drivers/input/keyboard/applespi.c @@ -0,0 +1,1978 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MacBook (Pro) SPI keyboard and touchpad driver + * + * Copyright (c) 2015-2018 Federico Lorenzi + * Copyright (c) 2017-2018 Ronald Tschalär + */ + +/* + * The keyboard and touchpad controller on the MacBookAir6, MacBookPro12, + * MacBook8 and newer can be driven either by USB or SPI. However the USB + * pins are only connected on the MacBookAir6 and 7 and the MacBookPro12. + * All others need this driver. The interface is selected using ACPI methods: + * + * * UIEN ("USB Interface Enable"): If invoked with argument 1, disables SPI + * and enables USB. If invoked with argument 0, disables USB. + * * UIST ("USB Interface Status"): Returns 1 if USB is enabled, 0 otherwise. + * * SIEN ("SPI Interface Enable"): If invoked with argument 1, disables USB + * and enables SPI. If invoked with argument 0, disables SPI. + * * SIST ("SPI Interface Status"): Returns 1 if SPI is enabled, 0 otherwise. + * * ISOL: Resets the four GPIO pins used for SPI. Intended to be invoked with + * argument 1, then once more with argument 0. + * + * UIEN and UIST are only provided on models where the USB pins are connected. + * + * SPI-based Protocol + * ------------------ + * + * The device and driver exchange messages (struct message); each message is + * encapsulated in one or more packets (struct spi_packet). There are two types + * of exchanges: reads, and writes. A read is signaled by a GPE, upon which one + * message can be read from the device. A write exchange consists of writing a + * command message, immediately reading a short status packet, and then, upon + * receiving a GPE, reading the response message. Write exchanges cannot be + * interleaved, i.e. a new write exchange must not be started till the previous + * write exchange is complete. Whether a received message is part of a read or + * write exchange is indicated in the encapsulating packet's flags field. + * + * A single message may be too large to fit in a single packet (which has a + * fixed, 256-byte size). In that case it will be split over multiple, + * consecutive packets. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define CREATE_TRACE_POINTS +#include "applespi.h" +#include "applespi_trace.h" + +#define APPLESPI_PACKET_SIZE 256 +#define APPLESPI_STATUS_SIZE 4 + +#define PACKET_TYPE_READ 0x20 +#define PACKET_TYPE_WRITE 0x40 +#define PACKET_DEV_KEYB 0x01 +#define PACKET_DEV_TPAD 0x02 +#define PACKET_DEV_INFO 0xd0 + +#define MAX_ROLLOVER 6 + +#define MAX_FINGERS 11 +#define MAX_FINGER_ORIENTATION 16384 +#define MAX_PKTS_PER_MSG 2 + +#define KBD_BL_LEVEL_MIN 32U +#define KBD_BL_LEVEL_MAX 255U +#define KBD_BL_LEVEL_SCALE 1000000U +#define KBD_BL_LEVEL_ADJ \ + ((KBD_BL_LEVEL_MAX - KBD_BL_LEVEL_MIN) * KBD_BL_LEVEL_SCALE / 255U) + +#define EFI_BL_LEVEL_NAME L"KeyboardBacklightLevel" +#define EFI_BL_LEVEL_GUID EFI_GUID(0xa076d2af, 0x9678, 0x4386, 0x8b, 0x58, 0x1f, 0xc8, 0xef, 0x04, 0x16, 0x19) + +#define APPLE_FLAG_FKEY 0x01 + +#define SPI_RW_CHG_DELAY_US 100 /* from experimentation, in µs */ + +#define SYNAPTICS_VENDOR_ID 0x06cb + +static unsigned int fnmode = 1; +module_param(fnmode, uint, 0644); +MODULE_PARM_DESC(fnmode, "Mode of Fn key on Apple keyboards (0 = disabled, [1] = fkeyslast, 2 = fkeysfirst)"); + +static unsigned int fnremap; +module_param(fnremap, uint, 0644); +MODULE_PARM_DESC(fnremap, "Remap Fn key ([0] = no-remap; 1 = left-ctrl, 2 = left-shift, 3 = left-alt, 4 = left-meta, 6 = right-shift, 7 = right-alt, 8 = right-meta)"); + +static bool iso_layout; +module_param(iso_layout, bool, 0644); +MODULE_PARM_DESC(iso_layout, "Enable/Disable hardcoded ISO-layout of the keyboard. ([0] = disabled, 1 = enabled)"); + +static char touchpad_dimensions[40]; +module_param_string(touchpad_dimensions, touchpad_dimensions, + sizeof(touchpad_dimensions), 0444); +MODULE_PARM_DESC(touchpad_dimensions, "The pixel dimensions of the touchpad, as XxY+W+H ."); + +/** + * struct keyboard_protocol - keyboard message. + * message.type = 0x0110, message.length = 0x000a + * + * @unknown1: unknown + * @modifiers: bit-set of modifier/control keys pressed + * @unknown2: unknown + * @keys_pressed: the (non-modifier) keys currently pressed + * @fn_pressed: whether the fn key is currently pressed + * @crc16: crc over the whole message struct (message header + + * this struct) minus this @crc16 field + */ +struct keyboard_protocol { + u8 unknown1; + u8 modifiers; + u8 unknown2; + u8 keys_pressed[MAX_ROLLOVER]; + u8 fn_pressed; + __le16 crc16; +}; + +/** + * struct tp_finger - single trackpad finger structure, le16-aligned + * + * @origin: zero when switching track finger + * @abs_x: absolute x coodinate + * @abs_y: absolute y coodinate + * @rel_x: relative x coodinate + * @rel_y: relative y coodinate + * @tool_major: tool area, major axis + * @tool_minor: tool area, minor axis + * @orientation: 16384 when point, else 15 bit angle + * @touch_major: touch area, major axis + * @touch_minor: touch area, minor axis + * @unused: zeros + * @pressure: pressure on forcetouch touchpad + * @multi: one finger: varies, more fingers: constant + * @crc16: on last finger: crc over the whole message struct + * (i.e. message header + this struct) minus the last + * @crc16 field; unknown on all other fingers. + */ +struct tp_finger { + __le16 origin; + __le16 abs_x; + __le16 abs_y; + __le16 rel_x; + __le16 rel_y; + __le16 tool_major; + __le16 tool_minor; + __le16 orientation; + __le16 touch_major; + __le16 touch_minor; + __le16 unused[2]; + __le16 pressure; + __le16 multi; + __le16 crc16; +}; + +/** + * struct touchpad_protocol - touchpad message. + * message.type = 0x0210 + * + * @unknown1: unknown + * @clicked: 1 if a button-click was detected, 0 otherwise + * @unknown2: unknown + * @number_of_fingers: the number of fingers being reported in @fingers + * @clicked2: same as @clicked + * @unknown3: unknown + * @fingers: the data for each finger + */ +struct touchpad_protocol { + u8 unknown1[1]; + u8 clicked; + u8 unknown2[28]; + u8 number_of_fingers; + u8 clicked2; + u8 unknown3[16]; + struct tp_finger fingers[0]; +}; + +/** + * struct command_protocol_tp_info - get touchpad info. + * message.type = 0x1020, message.length = 0x0000 + * + * @crc16: crc over the whole message struct (message header + + * this struct) minus this @crc16 field + */ +struct command_protocol_tp_info { + __le16 crc16; +}; + +/** + * struct touchpad_info - touchpad info response. + * message.type = 0x1020, message.length = 0x006e + * + * @unknown1: unknown + * @model_flags: flags (vary by model number, but significance otherwise + * unknown) + * @model_no: the touchpad model number + * @unknown2: unknown + * @crc16: crc over the whole message struct (message header + + * this struct) minus this @crc16 field + */ +struct touchpad_info_protocol { + u8 unknown1[105]; + u8 model_flags; + u8 model_no; + u8 unknown2[3]; + __le16 crc16; +}; + +/** + * struct command_protocol_mt_init - initialize multitouch. + * message.type = 0x0252, message.length = 0x0002 + * + * @cmd: value: 0x0102 + * @crc16: crc over the whole message struct (message header + + * this struct) minus this @crc16 field + */ +struct command_protocol_mt_init { + __le16 cmd; + __le16 crc16; +}; + +/** + * struct command_protocol_capsl - toggle caps-lock led + * message.type = 0x0151, message.length = 0x0002 + * + * @unknown: value: 0x01 (length?) + * @led: 0 off, 2 on + * @crc16: crc over the whole message struct (message header + + * this struct) minus this @crc16 field + */ +struct command_protocol_capsl { + u8 unknown; + u8 led; + __le16 crc16; +}; + +/** + * struct command_protocol_bl - set keyboard backlight brightness + * message.type = 0xB051, message.length = 0x0006 + * + * @const1: value: 0x01B0 + * @level: the brightness level to set + * @const2: value: 0x0001 (backlight off), 0x01F4 (backlight on) + * @crc16: crc over the whole message struct (message header + + * this struct) minus this @crc16 field + */ +struct command_protocol_bl { + __le16 const1; + __le16 level; + __le16 const2; + __le16 crc16; +}; + +/** + * struct message - a complete spi message. + * + * Each message begins with fixed header, followed by a message-type specific + * payload, and ends with a 16-bit crc. Because of the varying lengths of the + * payload, the crc is defined at the end of each payload struct, rather than + * in this struct. + * + * @type: the message type + * @zero: always 0 + * @counter: incremented on each message, rolls over after 255; there is a + * separate counter for each message type. + * @rsp_buf_len:response buffer length (the exact nature of this field is quite + * speculative). On a request/write this is often the same as + * @length, though in some cases it has been seen to be much larger + * (e.g. 0x400); on a response/read this the same as on the + * request; for reads that are not responses it is 0. + * @length: length of the remainder of the data in the whole message + * structure (after re-assembly in case of being split over + * multiple spi-packets), minus the trailing crc. The total size + * of the message struct is therefore @length + 10. + */ +struct message { + __le16 type; + u8 zero; + u8 counter; + __le16 rsp_buf_len; + __le16 length; + union { + struct keyboard_protocol keyboard; + struct touchpad_protocol touchpad; + struct touchpad_info_protocol tp_info; + struct command_protocol_tp_info tp_info_command; + struct command_protocol_mt_init init_mt_command; + struct command_protocol_capsl capsl_command; + struct command_protocol_bl bl_command; + u8 data[0]; + }; +}; + +/* type + zero + counter + rsp_buf_len + length */ +#define MSG_HEADER_SIZE 8 + +/** + * struct spi_packet - a complete spi packet; always 256 bytes. This carries + * the (parts of the) message in the data. But note that this does not + * necessarily contain a complete message, as in some cases (e.g. many + * fingers pressed) the message is split over multiple packets (see the + * @offset, @remaining, and @length fields). In general the data parts in + * spi_packet's are concatenated until @remaining is 0, and the result is an + * message. + * + * @flags: 0x40 = write (to device), 0x20 = read (from device); note that + * the response to a write still has 0x40. + * @device: 1 = keyboard, 2 = touchpad + * @offset: specifies the offset of this packet's data in the complete + * message; i.e. > 0 indicates this is a continuation packet (in + * the second packet for a message split over multiple packets + * this would then be the same as the @length in the first packet) + * @remaining: number of message bytes remaining in subsequents packets (in + * the first packet of a message split over two packets this would + * then be the same as the @length in the second packet) + * @length: length of the valid data in the @data in this packet + * @data: all or part of a message + * @crc16: crc over this whole structure minus this @crc16 field. This + * covers just this packet, even on multi-packet messages (in + * contrast to the crc in the message). + */ +struct spi_packet { + u8 flags; + u8 device; + __le16 offset; + __le16 remaining; + __le16 length; + u8 data[246]; + __le16 crc16; +}; + +struct spi_settings { + u64 spi_cs_delay; /* cs-to-clk delay in us */ + u64 reset_a2r_usec; /* active-to-receive delay? */ + u64 reset_rec_usec; /* ? (cur val: 10) */ +}; + +/* this mimics struct drm_rect */ +struct applespi_tp_info { + int x_min; + int y_min; + int x_max; + int y_max; +}; + +struct applespi_data { + struct spi_device *spi; + struct spi_settings spi_settings; + struct input_dev *keyboard_input_dev; + struct input_dev *touchpad_input_dev; + + u8 *tx_buffer; + u8 *tx_status; + u8 *rx_buffer; + + u8 *msg_buf; + unsigned int saved_msg_len; + + struct applespi_tp_info tp_info; + + u8 last_keys_pressed[MAX_ROLLOVER]; + u8 last_keys_fn_pressed[MAX_ROLLOVER]; + u8 last_fn_pressed; + struct input_mt_pos pos[MAX_FINGERS]; + int slots[MAX_FINGERS]; + int gpe; + acpi_handle sien; + acpi_handle sist; + + struct spi_transfer dl_t; + struct spi_transfer rd_t; + struct spi_message rd_m; + + struct spi_transfer ww_t; + struct spi_transfer wd_t; + struct spi_transfer wr_t; + struct spi_transfer st_t; + struct spi_message wr_m; + + bool want_tp_info_cmd; + bool want_mt_init_cmd; + bool want_cl_led_on; + bool have_cl_led_on; + unsigned int want_bl_level; + unsigned int have_bl_level; + unsigned int cmd_msg_cntr; + /* lock to protect the above parameters and flags below */ + spinlock_t cmd_msg_lock; + bool cmd_msg_queued; + enum applespi_evt_type cmd_evt_type; + + struct led_classdev backlight_info; + + bool suspended; + bool drain; + wait_queue_head_t drain_complete; + bool read_active; + bool write_active; + + struct work_struct work; + struct touchpad_info_protocol rcvd_tp_info; + + struct dentry *debugfs_root; + bool debug_tp_dim; + char tp_dim_val[40]; + int tp_dim_min_x; + int tp_dim_max_x; + int tp_dim_min_y; + int tp_dim_max_y; +}; + +static const unsigned char applespi_scancodes[] = { + 0, 0, 0, 0, + KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, + KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, + KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, + KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, + KEY_ENTER, KEY_ESC, KEY_BACKSPACE, KEY_TAB, KEY_SPACE, KEY_MINUS, + KEY_EQUAL, KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH, 0, + KEY_SEMICOLON, KEY_APOSTROPHE, KEY_GRAVE, KEY_COMMA, KEY_DOT, KEY_SLASH, + KEY_CAPSLOCK, + KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, + KEY_F10, KEY_F11, KEY_F12, 0, 0, 0, 0, 0, 0, 0, 0, 0, + KEY_RIGHT, KEY_LEFT, KEY_DOWN, KEY_UP, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_102ND, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_RO, 0, KEY_YEN, 0, 0, 0, 0, 0, + 0, KEY_KATAKANAHIRAGANA, KEY_MUHENKAN +}; + +/* + * This must have exactly as many entries as there are bits in + * struct keyboard_protocol.modifiers . + */ +static const unsigned char applespi_controlcodes[] = { + KEY_LEFTCTRL, + KEY_LEFTSHIFT, + KEY_LEFTALT, + KEY_LEFTMETA, + 0, + KEY_RIGHTSHIFT, + KEY_RIGHTALT, + KEY_RIGHTMETA +}; + +struct applespi_key_translation { + u16 from; + u16 to; + u8 flags; +}; + +static const struct applespi_key_translation applespi_fn_codes[] = { + { KEY_BACKSPACE, KEY_DELETE }, + { KEY_ENTER, KEY_INSERT }, + { KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY }, + { KEY_F2, KEY_BRIGHTNESSUP, APPLE_FLAG_FKEY }, + { KEY_F3, KEY_SCALE, APPLE_FLAG_FKEY }, + { KEY_F4, KEY_DASHBOARD, APPLE_FLAG_FKEY }, + { KEY_F5, KEY_KBDILLUMDOWN, APPLE_FLAG_FKEY }, + { KEY_F6, KEY_KBDILLUMUP, APPLE_FLAG_FKEY }, + { KEY_F7, KEY_PREVIOUSSONG, APPLE_FLAG_FKEY }, + { KEY_F8, KEY_PLAYPAUSE, APPLE_FLAG_FKEY }, + { KEY_F9, KEY_NEXTSONG, APPLE_FLAG_FKEY }, + { KEY_F10, KEY_MUTE, APPLE_FLAG_FKEY }, + { KEY_F11, KEY_VOLUMEDOWN, APPLE_FLAG_FKEY }, + { KEY_F12, KEY_VOLUMEUP, APPLE_FLAG_FKEY }, + { KEY_RIGHT, KEY_END }, + { KEY_LEFT, KEY_HOME }, + { KEY_DOWN, KEY_PAGEDOWN }, + { KEY_UP, KEY_PAGEUP }, + { } +}; + +static const struct applespi_key_translation apple_iso_keyboard[] = { + { KEY_GRAVE, KEY_102ND }, + { KEY_102ND, KEY_GRAVE }, + { } +}; + +struct applespi_tp_model_info { + u16 model; + struct applespi_tp_info tp_info; +}; + +static const struct applespi_tp_model_info applespi_tp_models[] = { + { + .model = 0x04, /* MB8 MB9 MB10 */ + .tp_info = { -5087, -182, 5579, 6089 }, + }, + { + .model = 0x05, /* MBP13,1 MBP13,2 MBP14,1 MBP14,2 */ + .tp_info = { -6243, -170, 6749, 7685 }, + }, + { + .model = 0x06, /* MBP13,3 MBP14,3 */ + .tp_info = { -7456, -163, 7976, 9283 }, + }, + {} +}; + +typedef void (*applespi_trace_fun)(enum applespi_evt_type, + enum applespi_pkt_type, u8 *, size_t); + +static applespi_trace_fun applespi_get_trace_fun(enum applespi_evt_type type) +{ + switch (type) { + case ET_CMD_TP_INI: + return trace_applespi_tp_ini_cmd; + case ET_CMD_BL: + return trace_applespi_backlight_cmd; + case ET_CMD_CL: + return trace_applespi_caps_lock_cmd; + case ET_RD_KEYB: + return trace_applespi_keyboard_data; + case ET_RD_TPAD: + return trace_applespi_touchpad_data; + case ET_RD_UNKN: + return trace_applespi_unknown_data; + default: + WARN_ONCE(1, "Unknown msg type %d", type); + return trace_applespi_unknown_data; + } +} + +static void applespi_setup_read_txfrs(struct applespi_data *applespi) +{ + struct spi_message *msg = &applespi->rd_m; + struct spi_transfer *dl_t = &applespi->dl_t; + struct spi_transfer *rd_t = &applespi->rd_t; + + memset(dl_t, 0, sizeof(*dl_t)); + memset(rd_t, 0, sizeof(*rd_t)); + + dl_t->delay_usecs = applespi->spi_settings.spi_cs_delay; + + rd_t->rx_buf = applespi->rx_buffer; + rd_t->len = APPLESPI_PACKET_SIZE; + + spi_message_init(msg); + spi_message_add_tail(dl_t, msg); + spi_message_add_tail(rd_t, msg); +} + +static void applespi_setup_write_txfrs(struct applespi_data *applespi) +{ + struct spi_message *msg = &applespi->wr_m; + struct spi_transfer *wt_t = &applespi->ww_t; + struct spi_transfer *dl_t = &applespi->wd_t; + struct spi_transfer *wr_t = &applespi->wr_t; + struct spi_transfer *st_t = &applespi->st_t; + + memset(wt_t, 0, sizeof(*wt_t)); + memset(dl_t, 0, sizeof(*dl_t)); + memset(wr_t, 0, sizeof(*wr_t)); + memset(st_t, 0, sizeof(*st_t)); + + /* + * All we need here is a delay at the beginning of the message before + * asserting cs. But the current spi API doesn't support this, so we + * end up with an extra unnecessary (but harmless) cs assertion and + * deassertion. + */ + wt_t->delay_usecs = SPI_RW_CHG_DELAY_US; + wt_t->cs_change = 1; + + dl_t->delay_usecs = applespi->spi_settings.spi_cs_delay; + + wr_t->tx_buf = applespi->tx_buffer; + wr_t->len = APPLESPI_PACKET_SIZE; + wr_t->delay_usecs = SPI_RW_CHG_DELAY_US; + + st_t->rx_buf = applespi->tx_status; + st_t->len = APPLESPI_STATUS_SIZE; + + spi_message_init(msg); + spi_message_add_tail(wt_t, msg); + spi_message_add_tail(dl_t, msg); + spi_message_add_tail(wr_t, msg); + spi_message_add_tail(st_t, msg); +} + +static int applespi_async(struct applespi_data *applespi, + struct spi_message *message, void (*complete)(void *)) +{ + message->complete = complete; + message->context = applespi; + + return spi_async(applespi->spi, message); +} + +static inline bool applespi_check_write_status(struct applespi_data *applespi, + int sts) +{ + static u8 status_ok[] = { 0xac, 0x27, 0x68, 0xd5 }; + + if (sts < 0) { + dev_warn(&applespi->spi->dev, "Error writing to device: %d\n", + sts); + return false; + } + + if (memcmp(applespi->tx_status, status_ok, APPLESPI_STATUS_SIZE)) { + dev_warn(&applespi->spi->dev, "Error writing to device: %*ph\n", + APPLESPI_STATUS_SIZE, applespi->tx_status); + return false; + } + + return true; +} + +static int applespi_get_spi_settings(struct applespi_data *applespi) +{ + struct acpi_device *adev = ACPI_COMPANION(&applespi->spi->dev); + const union acpi_object *o; + struct spi_settings *settings = &applespi->spi_settings; + + if (!acpi_dev_get_property(adev, "spiCSDelay", ACPI_TYPE_BUFFER, &o)) + settings->spi_cs_delay = *(u64 *)o->buffer.pointer; + else + dev_warn(&applespi->spi->dev, + "Property spiCSDelay not found\n"); + + if (!acpi_dev_get_property(adev, "resetA2RUsec", ACPI_TYPE_BUFFER, &o)) + settings->reset_a2r_usec = *(u64 *)o->buffer.pointer; + else + dev_warn(&applespi->spi->dev, + "Property resetA2RUsec not found\n"); + + if (!acpi_dev_get_property(adev, "resetRecUsec", ACPI_TYPE_BUFFER, &o)) + settings->reset_rec_usec = *(u64 *)o->buffer.pointer; + else + dev_warn(&applespi->spi->dev, + "Property resetRecUsec not found\n"); + + dev_dbg(&applespi->spi->dev, + "SPI settings: spi_cs_delay=%llu reset_a2r_usec=%llu reset_rec_usec=%llu\n", + settings->spi_cs_delay, settings->reset_a2r_usec, + settings->reset_rec_usec); + + return 0; +} + +static int applespi_setup_spi(struct applespi_data *applespi) +{ + int sts; + + sts = applespi_get_spi_settings(applespi); + if (sts) + return sts; + + spin_lock_init(&applespi->cmd_msg_lock); + init_waitqueue_head(&applespi->drain_complete); + + return 0; +} + +static int applespi_enable_spi(struct applespi_data *applespi) +{ + acpi_status acpi_sts; + unsigned long long spi_status; + + /* check if SPI is already enabled, so we can skip the delay below */ + acpi_sts = acpi_evaluate_integer(applespi->sist, NULL, NULL, + &spi_status); + if (ACPI_SUCCESS(acpi_sts) && spi_status) + return 0; + + /* SIEN(1) will enable SPI communication */ + acpi_sts = acpi_execute_simple_method(applespi->sien, NULL, 1); + if (ACPI_FAILURE(acpi_sts)) { + dev_err(&applespi->spi->dev, "SIEN failed: %s\n", + acpi_format_exception(acpi_sts)); + return -ENODEV; + } + + /* + * Allow the SPI interface to come up before returning. Without this + * delay, the SPI commands to enable multitouch mode may not reach + * the trackpad controller, causing pointer movement to break upon + * resume from sleep. + */ + msleep(50); + + return 0; +} + +static int applespi_send_cmd_msg(struct applespi_data *applespi); + +static void applespi_msg_complete(struct applespi_data *applespi, + bool is_write_msg, bool is_read_compl) +{ + unsigned long flags; + + spin_lock_irqsave(&applespi->cmd_msg_lock, flags); + + if (is_read_compl) + applespi->read_active = false; + if (is_write_msg) + applespi->write_active = false; + + if (applespi->drain && !applespi->write_active) + wake_up_all(&applespi->drain_complete); + + if (is_write_msg) { + applespi->cmd_msg_queued = false; + applespi_send_cmd_msg(applespi); + } + + spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags); +} + +static void applespi_async_write_complete(void *context) +{ + struct applespi_data *applespi = context; + enum applespi_evt_type evt_type = applespi->cmd_evt_type; + + applespi_get_trace_fun(evt_type)(evt_type, PT_WRITE, + applespi->tx_buffer, + APPLESPI_PACKET_SIZE); + applespi_get_trace_fun(evt_type)(evt_type, PT_STATUS, + applespi->tx_status, + APPLESPI_STATUS_SIZE); + + if (!applespi_check_write_status(applespi, applespi->wr_m.status)) { + /* + * If we got an error, we presumably won't get the expected + * response message either. + */ + applespi_msg_complete(applespi, true, false); + } +} + +static int applespi_send_cmd_msg(struct applespi_data *applespi) +{ + u16 crc; + int sts; + struct spi_packet *packet = (struct spi_packet *)applespi->tx_buffer; + struct message *message = (struct message *)packet->data; + u16 msg_len; + u8 device; + + /* check if draining */ + if (applespi->drain) + return 0; + + /* check whether send is in progress */ + if (applespi->cmd_msg_queued) + return 0; + + /* set up packet */ + memset(packet, 0, APPLESPI_PACKET_SIZE); + + /* are we processing init commands? */ + if (applespi->want_tp_info_cmd) { + applespi->want_tp_info_cmd = false; + applespi->want_mt_init_cmd = true; + applespi->cmd_evt_type = ET_CMD_TP_INI; + + /* build init command */ + device = PACKET_DEV_INFO; + + message->type = cpu_to_le16(0x1020); + msg_len = sizeof(message->tp_info_command); + + message->zero = 0x02; + message->rsp_buf_len = cpu_to_le16(0x0200); + + } else if (applespi->want_mt_init_cmd) { + applespi->want_mt_init_cmd = false; + applespi->cmd_evt_type = ET_CMD_TP_INI; + + /* build init command */ + device = PACKET_DEV_TPAD; + + message->type = cpu_to_le16(0x0252); + msg_len = sizeof(message->init_mt_command); + + message->init_mt_command.cmd = cpu_to_le16(0x0102); + + /* do we need caps-lock command? */ + } else if (applespi->want_cl_led_on != applespi->have_cl_led_on) { + applespi->have_cl_led_on = applespi->want_cl_led_on; + applespi->cmd_evt_type = ET_CMD_CL; + + /* build led command */ + device = PACKET_DEV_KEYB; + + message->type = cpu_to_le16(0x0151); + msg_len = sizeof(message->capsl_command); + + message->capsl_command.unknown = 0x01; + message->capsl_command.led = applespi->have_cl_led_on ? 2 : 0; + + /* do we need backlight command? */ + } else if (applespi->want_bl_level != applespi->have_bl_level) { + applespi->have_bl_level = applespi->want_bl_level; + applespi->cmd_evt_type = ET_CMD_BL; + + /* build command buffer */ + device = PACKET_DEV_KEYB; + + message->type = cpu_to_le16(0xB051); + msg_len = sizeof(message->bl_command); + + message->bl_command.const1 = cpu_to_le16(0x01B0); + message->bl_command.level = + cpu_to_le16(applespi->have_bl_level); + + if (applespi->have_bl_level > 0) + message->bl_command.const2 = cpu_to_le16(0x01F4); + else + message->bl_command.const2 = cpu_to_le16(0x0001); + + /* everything's up-to-date */ + } else { + return 0; + } + + /* finalize packet */ + packet->flags = PACKET_TYPE_WRITE; + packet->device = device; + packet->length = cpu_to_le16(MSG_HEADER_SIZE + msg_len); + + message->counter = applespi->cmd_msg_cntr++ % (U8_MAX + 1); + + message->length = cpu_to_le16(msg_len - 2); + if (!message->rsp_buf_len) + message->rsp_buf_len = message->length; + + crc = crc16(0, (u8 *)message, le16_to_cpu(packet->length) - 2); + put_unaligned_le16(crc, &message->data[msg_len - 2]); + + crc = crc16(0, (u8 *)packet, sizeof(*packet) - 2); + packet->crc16 = cpu_to_le16(crc); + + /* send command */ + sts = applespi_async(applespi, &applespi->wr_m, + applespi_async_write_complete); + if (sts) { + dev_warn(&applespi->spi->dev, + "Error queueing async write to device: %d\n", sts); + return sts; + } + + applespi->cmd_msg_queued = true; + applespi->write_active = true; + + return 0; +} + +static void applespi_init(struct applespi_data *applespi, bool is_resume) +{ + unsigned long flags; + + spin_lock_irqsave(&applespi->cmd_msg_lock, flags); + + if (is_resume) + applespi->want_mt_init_cmd = true; + else + applespi->want_tp_info_cmd = true; + applespi_send_cmd_msg(applespi); + + spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags); +} + +static int applespi_set_capsl_led(struct applespi_data *applespi, + bool capslock_on) +{ + unsigned long flags; + int sts; + + spin_lock_irqsave(&applespi->cmd_msg_lock, flags); + + applespi->want_cl_led_on = capslock_on; + sts = applespi_send_cmd_msg(applespi); + + spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags); + + return sts; +} + +static void applespi_set_bl_level(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct applespi_data *applespi = + container_of(led_cdev, struct applespi_data, backlight_info); + unsigned long flags; + int sts; + + spin_lock_irqsave(&applespi->cmd_msg_lock, flags); + + if (value == 0) { + applespi->want_bl_level = value; + } else { + /* + * The backlight does not turn on till level 32, so we scale + * the range here so that from a user's perspective it turns + * on at 1. + */ + applespi->want_bl_level = + ((value * KBD_BL_LEVEL_ADJ) / KBD_BL_LEVEL_SCALE + + KBD_BL_LEVEL_MIN); + } + + sts = applespi_send_cmd_msg(applespi); + + spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags); +} + +static int applespi_event(struct input_dev *dev, unsigned int type, + unsigned int code, int value) +{ + struct applespi_data *applespi = input_get_drvdata(dev); + + switch (type) { + case EV_LED: + applespi_set_capsl_led(applespi, !!test_bit(LED_CAPSL, dev->led)); + return 0; + } + + return -EINVAL; +} + +/* lifted from the BCM5974 driver and renamed from raw2int */ +/* convert 16-bit little endian to signed integer */ +static inline int le16_to_int(__le16 x) +{ + return (signed short)le16_to_cpu(x); +} + +static void applespi_debug_update_dimensions(struct applespi_data *applespi, + const struct tp_finger *f) +{ + applespi->tp_dim_min_x = min_t(int, applespi->tp_dim_min_x, f->abs_x); + applespi->tp_dim_max_x = max_t(int, applespi->tp_dim_max_x, f->abs_x); + applespi->tp_dim_min_y = min_t(int, applespi->tp_dim_min_y, f->abs_y); + applespi->tp_dim_max_y = max_t(int, applespi->tp_dim_max_y, f->abs_y); +} + +static int applespi_tp_dim_open(struct inode *inode, struct file *file) +{ + struct applespi_data *applespi = inode->i_private; + + file->private_data = applespi; + + snprintf(applespi->tp_dim_val, sizeof(applespi->tp_dim_val), + "0x%.4x %dx%d+%u+%u\n", + applespi->touchpad_input_dev->id.product, + applespi->tp_dim_min_x, applespi->tp_dim_min_y, + applespi->tp_dim_max_x - applespi->tp_dim_min_x, + applespi->tp_dim_max_y - applespi->tp_dim_min_y); + + return nonseekable_open(inode, file); +} + +static ssize_t applespi_tp_dim_read(struct file *file, char __user *buf, + size_t len, loff_t *off) +{ + struct applespi_data *applespi = file->private_data; + + return simple_read_from_buffer(buf, len, off, applespi->tp_dim_val, + strlen(applespi->tp_dim_val)); +} + +static const struct file_operations applespi_tp_dim_fops = { + .owner = THIS_MODULE, + .open = applespi_tp_dim_open, + .read = applespi_tp_dim_read, + .llseek = no_llseek, +}; + +static void report_finger_data(struct input_dev *input, int slot, + const struct input_mt_pos *pos, + const struct tp_finger *f) +{ + input_mt_slot(input, slot); + input_mt_report_slot_state(input, MT_TOOL_FINGER, true); + + input_report_abs(input, ABS_MT_TOUCH_MAJOR, + le16_to_int(f->touch_major) << 1); + input_report_abs(input, ABS_MT_TOUCH_MINOR, + le16_to_int(f->touch_minor) << 1); + input_report_abs(input, ABS_MT_WIDTH_MAJOR, + le16_to_int(f->tool_major) << 1); + input_report_abs(input, ABS_MT_WIDTH_MINOR, + le16_to_int(f->tool_minor) << 1); + input_report_abs(input, ABS_MT_ORIENTATION, + MAX_FINGER_ORIENTATION - le16_to_int(f->orientation)); + input_report_abs(input, ABS_MT_POSITION_X, pos->x); + input_report_abs(input, ABS_MT_POSITION_Y, pos->y); +} + +static void report_tp_state(struct applespi_data *applespi, + struct touchpad_protocol *t) +{ + const struct tp_finger *f; + struct input_dev *input; + const struct applespi_tp_info *tp_info = &applespi->tp_info; + int i, n; + + /* touchpad_input_dev is set async in worker */ + input = smp_load_acquire(&applespi->touchpad_input_dev); + if (!input) + return; /* touchpad isn't initialized yet */ + + n = 0; + + for (i = 0; i < t->number_of_fingers; i++) { + f = &t->fingers[i]; + if (le16_to_int(f->touch_major) == 0) + continue; + applespi->pos[n].x = le16_to_int(f->abs_x); + applespi->pos[n].y = tp_info->y_min + tp_info->y_max - + le16_to_int(f->abs_y); + n++; + + if (applespi->debug_tp_dim) + applespi_debug_update_dimensions(applespi, f); + } + + input_mt_assign_slots(input, applespi->slots, applespi->pos, n, 0); + + for (i = 0; i < n; i++) + report_finger_data(input, applespi->slots[i], + &applespi->pos[i], &t->fingers[i]); + + input_mt_sync_frame(input); + input_report_key(input, BTN_LEFT, t->clicked); + + input_sync(input); +} + +static const struct applespi_key_translation * +applespi_find_translation(const struct applespi_key_translation *table, u16 key) +{ + const struct applespi_key_translation *trans; + + for (trans = table; trans->from; trans++) + if (trans->from == key) + return trans; + + return NULL; +} + +static unsigned int applespi_translate_fn_key(unsigned int key, int fn_pressed) +{ + const struct applespi_key_translation *trans; + int do_translate; + + trans = applespi_find_translation(applespi_fn_codes, key); + if (trans) { + if (trans->flags & APPLE_FLAG_FKEY) + do_translate = (fnmode == 2 && fn_pressed) || + (fnmode == 1 && !fn_pressed); + else + do_translate = fn_pressed; + + if (do_translate) + key = trans->to; + } + + return key; +} + +static unsigned int applespi_translate_iso_layout(unsigned int key) +{ + const struct applespi_key_translation *trans; + + trans = applespi_find_translation(apple_iso_keyboard, key); + if (trans) + key = trans->to; + + return key; +} + +static unsigned int applespi_code_to_key(u8 code, int fn_pressed) +{ + unsigned int key = applespi_scancodes[code]; + + if (fnmode) + key = applespi_translate_fn_key(key, fn_pressed); + if (iso_layout) + key = applespi_translate_iso_layout(key); + return key; +} + +static void +applespi_remap_fn_key(struct keyboard_protocol *keyboard_protocol) +{ + unsigned char tmp; + u8 bit = BIT((fnremap - 1) & 0x07); + + if (!fnremap || fnremap > ARRAY_SIZE(applespi_controlcodes) || + !applespi_controlcodes[fnremap - 1]) + return; + + tmp = keyboard_protocol->fn_pressed; + keyboard_protocol->fn_pressed = !!(keyboard_protocol->modifiers & bit); + if (tmp) + keyboard_protocol->modifiers |= bit; + else + keyboard_protocol->modifiers &= ~bit; +} + +static void +applespi_handle_keyboard_event(struct applespi_data *applespi, + struct keyboard_protocol *keyboard_protocol) +{ + unsigned int key; + int i; + + compiletime_assert(ARRAY_SIZE(applespi_controlcodes) == + sizeof_field(struct keyboard_protocol, modifiers) * 8, + "applespi_controlcodes has wrong number of entries"); + + /* check for rollover overflow, which is signalled by all keys == 1 */ + if (!memchr_inv(keyboard_protocol->keys_pressed, 1, MAX_ROLLOVER)) + return; + + /* remap fn key if desired */ + applespi_remap_fn_key(keyboard_protocol); + + /* check released keys */ + for (i = 0; i < MAX_ROLLOVER; i++) { + if (memchr(keyboard_protocol->keys_pressed, + applespi->last_keys_pressed[i], MAX_ROLLOVER)) + continue; /* key is still pressed */ + + key = applespi_code_to_key(applespi->last_keys_pressed[i], + applespi->last_keys_fn_pressed[i]); + input_report_key(applespi->keyboard_input_dev, key, 0); + applespi->last_keys_fn_pressed[i] = 0; + } + + /* check pressed keys */ + for (i = 0; i < MAX_ROLLOVER; i++) { + if (keyboard_protocol->keys_pressed[i] < + ARRAY_SIZE(applespi_scancodes) && + keyboard_protocol->keys_pressed[i] > 0) { + key = applespi_code_to_key( + keyboard_protocol->keys_pressed[i], + keyboard_protocol->fn_pressed); + input_report_key(applespi->keyboard_input_dev, key, 1); + applespi->last_keys_fn_pressed[i] = + keyboard_protocol->fn_pressed; + } + } + + /* check control keys */ + for (i = 0; i < ARRAY_SIZE(applespi_controlcodes); i++) { + if (keyboard_protocol->modifiers & BIT(i)) + input_report_key(applespi->keyboard_input_dev, + applespi_controlcodes[i], 1); + else + input_report_key(applespi->keyboard_input_dev, + applespi_controlcodes[i], 0); + } + + /* check function key */ + if (keyboard_protocol->fn_pressed && !applespi->last_fn_pressed) + input_report_key(applespi->keyboard_input_dev, KEY_FN, 1); + else if (!keyboard_protocol->fn_pressed && applespi->last_fn_pressed) + input_report_key(applespi->keyboard_input_dev, KEY_FN, 0); + applespi->last_fn_pressed = keyboard_protocol->fn_pressed; + + /* done */ + input_sync(applespi->keyboard_input_dev); + memcpy(&applespi->last_keys_pressed, keyboard_protocol->keys_pressed, + sizeof(applespi->last_keys_pressed)); +} + +static const struct applespi_tp_info *applespi_find_touchpad_info(u8 model) +{ + const struct applespi_tp_model_info *info; + + for (info = applespi_tp_models; info->model; info++) { + if (info->model == model) + return &info->tp_info; + } + + return NULL; +} + +static int +applespi_register_touchpad_device(struct applespi_data *applespi, + struct touchpad_info_protocol *rcvd_tp_info) +{ + const struct applespi_tp_info *tp_info; + struct input_dev *touchpad_input_dev; + int sts; + + /* set up touchpad dimensions */ + tp_info = applespi_find_touchpad_info(rcvd_tp_info->model_no); + if (!tp_info) { + dev_warn(&applespi->spi->dev, + "Unknown touchpad model %x - falling back to MB8 touchpad\n", + rcvd_tp_info->model_no); + tp_info = &applespi_tp_models[0].tp_info; + } + + applespi->tp_info = *tp_info; + + if (touchpad_dimensions[0]) { + int x, y, w, h; + + sts = sscanf(touchpad_dimensions, "%dx%d+%u+%u", &x, &y, &w, &h); + if (sts == 4) { + dev_info(&applespi->spi->dev, + "Overriding touchpad dimensions from module param\n"); + applespi->tp_info.x_min = x; + applespi->tp_info.y_min = y; + applespi->tp_info.x_max = x + w; + applespi->tp_info.y_max = y + h; + } else { + dev_warn(&applespi->spi->dev, + "Invalid touchpad dimensions '%s': must be in the form XxY+W+H\n", + touchpad_dimensions); + touchpad_dimensions[0] = '\0'; + } + } + if (!touchpad_dimensions[0]) { + snprintf(touchpad_dimensions, sizeof(touchpad_dimensions), + "%dx%d+%u+%u", + applespi->tp_info.x_min, + applespi->tp_info.y_min, + applespi->tp_info.x_max - applespi->tp_info.x_min, + applespi->tp_info.y_max - applespi->tp_info.y_min); + } + + /* create touchpad input device */ + touchpad_input_dev = devm_input_allocate_device(&applespi->spi->dev); + if (!touchpad_input_dev) { + dev_err(&applespi->spi->dev, + "Failed to allocate touchpad input device\n"); + return -ENOMEM; + } + + touchpad_input_dev->name = "Apple SPI Touchpad"; + touchpad_input_dev->phys = "applespi/input1"; + touchpad_input_dev->dev.parent = &applespi->spi->dev; + touchpad_input_dev->id.bustype = BUS_SPI; + touchpad_input_dev->id.vendor = SYNAPTICS_VENDOR_ID; + touchpad_input_dev->id.product = + rcvd_tp_info->model_no << 8 | rcvd_tp_info->model_flags; + + /* basic properties */ + input_set_capability(touchpad_input_dev, EV_REL, REL_X); + input_set_capability(touchpad_input_dev, EV_REL, REL_Y); + + __set_bit(INPUT_PROP_POINTER, touchpad_input_dev->propbit); + __set_bit(INPUT_PROP_BUTTONPAD, touchpad_input_dev->propbit); + + /* finger touch area */ + input_set_abs_params(touchpad_input_dev, ABS_MT_TOUCH_MAJOR, + 0, 5000, 0, 0); + input_set_abs_params(touchpad_input_dev, ABS_MT_TOUCH_MINOR, + 0, 5000, 0, 0); + + /* finger approach area */ + input_set_abs_params(touchpad_input_dev, ABS_MT_WIDTH_MAJOR, + 0, 5000, 0, 0); + input_set_abs_params(touchpad_input_dev, ABS_MT_WIDTH_MINOR, + 0, 5000, 0, 0); + + /* finger orientation */ + input_set_abs_params(touchpad_input_dev, ABS_MT_ORIENTATION, + -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, + 0, 0); + + /* finger position */ + input_set_abs_params(touchpad_input_dev, ABS_MT_POSITION_X, + applespi->tp_info.x_min, applespi->tp_info.x_max, + 0, 0); + input_set_abs_params(touchpad_input_dev, ABS_MT_POSITION_Y, + applespi->tp_info.y_min, applespi->tp_info.y_max, + 0, 0); + + /* touchpad button */ + input_set_capability(touchpad_input_dev, EV_KEY, BTN_LEFT); + + /* multitouch */ + sts = input_mt_init_slots(touchpad_input_dev, MAX_FINGERS, + INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | + INPUT_MT_TRACK); + if (sts) { + dev_err(&applespi->spi->dev, + "failed to initialize slots: %d", sts); + return sts; + } + + /* register input device */ + sts = input_register_device(touchpad_input_dev); + if (sts) { + dev_err(&applespi->spi->dev, + "Unable to register touchpad input device (%d)\n", sts); + return sts; + } + + /* touchpad_input_dev is read async in spi callback */ + smp_store_release(&applespi->touchpad_input_dev, touchpad_input_dev); + + return 0; +} + +static void applespi_worker(struct work_struct *work) +{ + struct applespi_data *applespi = + container_of(work, struct applespi_data, work); + + applespi_register_touchpad_device(applespi, &applespi->rcvd_tp_info); +} + +static void applespi_handle_cmd_response(struct applespi_data *applespi, + struct spi_packet *packet, + struct message *message) +{ + if (packet->device == PACKET_DEV_INFO && + le16_to_cpu(message->type) == 0x1020) { + /* + * We're not allowed to sleep here, but registering an input + * device can sleep. + */ + applespi->rcvd_tp_info = message->tp_info; + schedule_work(&applespi->work); + return; + } + + if (le16_to_cpu(message->length) != 0x0000) { + dev_warn_ratelimited(&applespi->spi->dev, + "Received unexpected write response: length=%x\n", + le16_to_cpu(message->length)); + return; + } + + if (packet->device == PACKET_DEV_TPAD && + le16_to_cpu(message->type) == 0x0252 && + le16_to_cpu(message->rsp_buf_len) == 0x0002) + dev_info(&applespi->spi->dev, "modeswitch done.\n"); +} + +static bool applespi_verify_crc(struct applespi_data *applespi, u8 *buffer, + size_t buflen) +{ + u16 crc; + + crc = crc16(0, buffer, buflen); + if (crc) { + dev_warn_ratelimited(&applespi->spi->dev, + "Received corrupted packet (crc mismatch)\n"); + trace_applespi_bad_crc(ET_RD_CRC, READ, buffer, buflen); + + return false; + } + + return true; +} + +static void applespi_debug_print_read_packet(struct applespi_data *applespi, + struct spi_packet *packet) +{ + unsigned int evt_type; + + if (packet->flags == PACKET_TYPE_READ && + packet->device == PACKET_DEV_KEYB) + evt_type = ET_RD_KEYB; + else if (packet->flags == PACKET_TYPE_READ && + packet->device == PACKET_DEV_TPAD) + evt_type = ET_RD_TPAD; + else if (packet->flags == PACKET_TYPE_WRITE) + evt_type = applespi->cmd_evt_type; + else + evt_type = ET_RD_UNKN; + + applespi_get_trace_fun(evt_type)(evt_type, PT_READ, applespi->rx_buffer, + APPLESPI_PACKET_SIZE); +} + +static void applespi_got_data(struct applespi_data *applespi) +{ + struct spi_packet *packet; + struct message *message; + unsigned int msg_len; + unsigned int off; + unsigned int rem; + unsigned int len; + + /* process packet header */ + if (!applespi_verify_crc(applespi, applespi->rx_buffer, + APPLESPI_PACKET_SIZE)) { + unsigned long flags; + + spin_lock_irqsave(&applespi->cmd_msg_lock, flags); + + if (applespi->drain) { + applespi->read_active = false; + applespi->write_active = false; + + wake_up_all(&applespi->drain_complete); + } + + spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags); + + return; + } + + packet = (struct spi_packet *)applespi->rx_buffer; + + applespi_debug_print_read_packet(applespi, packet); + + off = le16_to_cpu(packet->offset); + rem = le16_to_cpu(packet->remaining); + len = le16_to_cpu(packet->length); + + if (len > sizeof(packet->data)) { + dev_warn_ratelimited(&applespi->spi->dev, + "Received corrupted packet (invalid packet length %u)\n", + len); + goto msg_complete; + } + + /* handle multi-packet messages */ + if (rem > 0 || off > 0) { + if (off != applespi->saved_msg_len) { + dev_warn_ratelimited(&applespi->spi->dev, + "Received unexpected offset (got %u, expected %u)\n", + off, applespi->saved_msg_len); + goto msg_complete; + } + + if (off + rem > MAX_PKTS_PER_MSG * APPLESPI_PACKET_SIZE) { + dev_warn_ratelimited(&applespi->spi->dev, + "Received message too large (size %u)\n", + off + rem); + goto msg_complete; + } + + if (off + len > MAX_PKTS_PER_MSG * APPLESPI_PACKET_SIZE) { + dev_warn_ratelimited(&applespi->spi->dev, + "Received message too large (size %u)\n", + off + len); + goto msg_complete; + } + + memcpy(applespi->msg_buf + off, &packet->data, len); + applespi->saved_msg_len += len; + + if (rem > 0) + return; + + message = (struct message *)applespi->msg_buf; + msg_len = applespi->saved_msg_len; + } else { + message = (struct message *)&packet->data; + msg_len = len; + } + + /* got complete message - verify */ + if (!applespi_verify_crc(applespi, (u8 *)message, msg_len)) + goto msg_complete; + + if (le16_to_cpu(message->length) != msg_len - MSG_HEADER_SIZE - 2) { + dev_warn_ratelimited(&applespi->spi->dev, + "Received corrupted packet (invalid message length %u - expected %u)\n", + le16_to_cpu(message->length), + msg_len - MSG_HEADER_SIZE - 2); + goto msg_complete; + } + + /* handle message */ + if (packet->flags == PACKET_TYPE_READ && + packet->device == PACKET_DEV_KEYB) { + applespi_handle_keyboard_event(applespi, &message->keyboard); + + } else if (packet->flags == PACKET_TYPE_READ && + packet->device == PACKET_DEV_TPAD) { + struct touchpad_protocol *tp; + size_t tp_len; + + tp = &message->touchpad; + tp_len = sizeof(*tp) + + tp->number_of_fingers * sizeof(tp->fingers[0]); + + if (le16_to_cpu(message->length) + 2 != tp_len) { + dev_warn_ratelimited(&applespi->spi->dev, + "Received corrupted packet (invalid message length %u - num-fingers %u, tp-len %zu)\n", + le16_to_cpu(message->length), + tp->number_of_fingers, tp_len); + goto msg_complete; + } + + if (tp->number_of_fingers > MAX_FINGERS) { + dev_warn_ratelimited(&applespi->spi->dev, + "Number of reported fingers (%u) exceeds max (%u))\n", + tp->number_of_fingers, + MAX_FINGERS); + tp->number_of_fingers = MAX_FINGERS; + } + + report_tp_state(applespi, tp); + + } else if (packet->flags == PACKET_TYPE_WRITE) { + applespi_handle_cmd_response(applespi, packet, message); + } + +msg_complete: + applespi->saved_msg_len = 0; + + applespi_msg_complete(applespi, packet->flags == PACKET_TYPE_WRITE, + true); +} + +static void applespi_async_read_complete(void *context) +{ + struct applespi_data *applespi = context; + + if (applespi->rd_m.status < 0) { + dev_warn(&applespi->spi->dev, "Error reading from device: %d\n", + applespi->rd_m.status); + /* + * We don't actually know if this was a pure read, or a response + * to a write. But this is a rare error condition that should + * never occur, so clearing both flags to avoid deadlock. + */ + applespi_msg_complete(applespi, true, true); + } else { + applespi_got_data(applespi); + } + + acpi_finish_gpe(NULL, applespi->gpe); +} + +static u32 applespi_notify(acpi_handle gpe_device, u32 gpe, void *context) +{ + struct applespi_data *applespi = context; + int sts; + unsigned long flags; + + trace_applespi_irq_received(ET_RD_IRQ, PT_READ); + + spin_lock_irqsave(&applespi->cmd_msg_lock, flags); + + if (!applespi->suspended) { + sts = applespi_async(applespi, &applespi->rd_m, + applespi_async_read_complete); + if (sts) + dev_warn(&applespi->spi->dev, + "Error queueing async read to device: %d\n", + sts); + else + applespi->read_active = true; + } + + spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags); + + return ACPI_INTERRUPT_HANDLED; +} + +static int applespi_get_saved_bl_level(struct applespi_data *applespi) +{ + struct efivar_entry *efivar_entry; + u16 efi_data = 0; + unsigned long efi_data_len; + int sts; + + efivar_entry = kmalloc(sizeof(*efivar_entry), GFP_KERNEL); + if (!efivar_entry) + return -ENOMEM; + + memcpy(efivar_entry->var.VariableName, EFI_BL_LEVEL_NAME, + sizeof(EFI_BL_LEVEL_NAME)); + efivar_entry->var.VendorGuid = EFI_BL_LEVEL_GUID; + efi_data_len = sizeof(efi_data); + + sts = efivar_entry_get(efivar_entry, NULL, &efi_data_len, &efi_data); + if (sts && sts != -ENOENT) + dev_warn(&applespi->spi->dev, + "Error getting backlight level from EFI vars: %d\n", + sts); + + kfree(efivar_entry); + + return sts ? sts : efi_data; +} + +static void applespi_save_bl_level(struct applespi_data *applespi, + unsigned int level) +{ + efi_guid_t efi_guid; + u32 efi_attr; + unsigned long efi_data_len; + u16 efi_data; + int sts; + + /* Save keyboard backlight level */ + efi_guid = EFI_BL_LEVEL_GUID; + efi_data = (u16)level; + efi_data_len = sizeof(efi_data); + efi_attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS; + + sts = efivar_entry_set_safe(EFI_BL_LEVEL_NAME, efi_guid, efi_attr, true, + efi_data_len, &efi_data); + if (sts) + dev_warn(&applespi->spi->dev, + "Error saving backlight level to EFI vars: %d\n", sts); +} + +static int applespi_probe(struct spi_device *spi) +{ + struct applespi_data *applespi; + acpi_handle spi_handle = ACPI_HANDLE(&spi->dev); + acpi_status acpi_sts; + int sts, i; + unsigned long long gpe, usb_status; + + /* check if the USB interface is present and enabled already */ + acpi_sts = acpi_evaluate_integer(spi_handle, "UIST", NULL, &usb_status); + if (ACPI_SUCCESS(acpi_sts) && usb_status) { + /* let the USB driver take over instead */ + dev_info(&spi->dev, "USB interface already enabled\n"); + return -ENODEV; + } + + /* allocate driver data */ + applespi = devm_kzalloc(&spi->dev, sizeof(*applespi), GFP_KERNEL); + if (!applespi) + return -ENOMEM; + + applespi->spi = spi; + + INIT_WORK(&applespi->work, applespi_worker); + + /* store the driver data */ + spi_set_drvdata(spi, applespi); + + /* create our buffers */ + applespi->tx_buffer = devm_kmalloc(&spi->dev, APPLESPI_PACKET_SIZE, + GFP_KERNEL); + applespi->tx_status = devm_kmalloc(&spi->dev, APPLESPI_STATUS_SIZE, + GFP_KERNEL); + applespi->rx_buffer = devm_kmalloc(&spi->dev, APPLESPI_PACKET_SIZE, + GFP_KERNEL); + applespi->msg_buf = devm_kmalloc_array(&spi->dev, MAX_PKTS_PER_MSG, + APPLESPI_PACKET_SIZE, + GFP_KERNEL); + + if (!applespi->tx_buffer || !applespi->tx_status || + !applespi->rx_buffer || !applespi->msg_buf) + return -ENOMEM; + + /* set up our spi messages */ + applespi_setup_read_txfrs(applespi); + applespi_setup_write_txfrs(applespi); + + /* cache ACPI method handles */ + acpi_sts = acpi_get_handle(spi_handle, "SIEN", &applespi->sien); + if (ACPI_FAILURE(acpi_sts)) { + dev_err(&applespi->spi->dev, + "Failed to get SIEN ACPI method handle: %s\n", + acpi_format_exception(acpi_sts)); + return -ENODEV; + } + + acpi_sts = acpi_get_handle(spi_handle, "SIST", &applespi->sist); + if (ACPI_FAILURE(acpi_sts)) { + dev_err(&applespi->spi->dev, + "Failed to get SIST ACPI method handle: %s\n", + acpi_format_exception(acpi_sts)); + return -ENODEV; + } + + /* switch on the SPI interface */ + sts = applespi_setup_spi(applespi); + if (sts) + return sts; + + sts = applespi_enable_spi(applespi); + if (sts) + return sts; + + /* setup the keyboard input dev */ + applespi->keyboard_input_dev = devm_input_allocate_device(&spi->dev); + + if (!applespi->keyboard_input_dev) + return -ENOMEM; + + applespi->keyboard_input_dev->name = "Apple SPI Keyboard"; + applespi->keyboard_input_dev->phys = "applespi/input0"; + applespi->keyboard_input_dev->dev.parent = &spi->dev; + applespi->keyboard_input_dev->id.bustype = BUS_SPI; + + applespi->keyboard_input_dev->evbit[0] = + BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) | BIT_MASK(EV_REP); + applespi->keyboard_input_dev->ledbit[0] = BIT_MASK(LED_CAPSL); + + input_set_drvdata(applespi->keyboard_input_dev, applespi); + applespi->keyboard_input_dev->event = applespi_event; + + for (i = 0; i < ARRAY_SIZE(applespi_scancodes); i++) + if (applespi_scancodes[i]) + input_set_capability(applespi->keyboard_input_dev, + EV_KEY, applespi_scancodes[i]); + + for (i = 0; i < ARRAY_SIZE(applespi_controlcodes); i++) + if (applespi_controlcodes[i]) + input_set_capability(applespi->keyboard_input_dev, + EV_KEY, applespi_controlcodes[i]); + + for (i = 0; i < ARRAY_SIZE(applespi_fn_codes); i++) + if (applespi_fn_codes[i].to) + input_set_capability(applespi->keyboard_input_dev, + EV_KEY, applespi_fn_codes[i].to); + + input_set_capability(applespi->keyboard_input_dev, EV_KEY, KEY_FN); + + sts = input_register_device(applespi->keyboard_input_dev); + if (sts) { + dev_err(&applespi->spi->dev, + "Unable to register keyboard input device (%d)\n", sts); + return -ENODEV; + } + + /* + * The applespi device doesn't send interrupts normally (as is described + * in its DSDT), but rather seems to use ACPI GPEs. + */ + acpi_sts = acpi_evaluate_integer(spi_handle, "_GPE", NULL, &gpe); + if (ACPI_FAILURE(acpi_sts)) { + dev_err(&applespi->spi->dev, + "Failed to obtain GPE for SPI slave device: %s\n", + acpi_format_exception(acpi_sts)); + return -ENODEV; + } + applespi->gpe = (int)gpe; + + acpi_sts = acpi_install_gpe_handler(NULL, applespi->gpe, + ACPI_GPE_LEVEL_TRIGGERED, + applespi_notify, applespi); + if (ACPI_FAILURE(acpi_sts)) { + dev_err(&applespi->spi->dev, + "Failed to install GPE handler for GPE %d: %s\n", + applespi->gpe, acpi_format_exception(acpi_sts)); + return -ENODEV; + } + + applespi->suspended = false; + + acpi_sts = acpi_enable_gpe(NULL, applespi->gpe); + if (ACPI_FAILURE(acpi_sts)) { + dev_err(&applespi->spi->dev, + "Failed to enable GPE handler for GPE %d: %s\n", + applespi->gpe, acpi_format_exception(acpi_sts)); + acpi_remove_gpe_handler(NULL, applespi->gpe, applespi_notify); + return -ENODEV; + } + + /* trigger touchpad setup */ + applespi_init(applespi, false); + + /* + * By default this device is not enabled for wakeup; but USB keyboards + * generally are, so the expectation is that by default the keyboard + * will wake the system. + */ + device_wakeup_enable(&spi->dev); + + /* set up keyboard-backlight */ + sts = applespi_get_saved_bl_level(applespi); + if (sts >= 0) + applespi_set_bl_level(&applespi->backlight_info, sts); + + applespi->backlight_info.name = "spi::kbd_backlight"; + applespi->backlight_info.default_trigger = "kbd-backlight"; + applespi->backlight_info.brightness_set = applespi_set_bl_level; + + sts = devm_led_classdev_register(&spi->dev, &applespi->backlight_info); + if (sts) + dev_warn(&applespi->spi->dev, + "Unable to register keyboard backlight class dev (%d)\n", + sts); + + /* set up debugfs entries for touchpad dimensions logging */ + applespi->debugfs_root = debugfs_create_dir("applespi", NULL); + if (IS_ERR(applespi->debugfs_root)) { + if (PTR_ERR(applespi->debugfs_root) != -ENODEV) + dev_warn(&applespi->spi->dev, + "Error creating debugfs root entry (%ld)\n", + PTR_ERR(applespi->debugfs_root)); + } else { + struct dentry *ret; + + ret = debugfs_create_bool("enable_tp_dim", 0600, + applespi->debugfs_root, + &applespi->debug_tp_dim); + if (IS_ERR(ret)) + dev_dbg(&applespi->spi->dev, + "Error creating debugfs entry enable_tp_dim (%ld)\n", + PTR_ERR(ret)); + + ret = debugfs_create_file("tp_dim", 0400, + applespi->debugfs_root, applespi, + &applespi_tp_dim_fops); + if (IS_ERR(ret)) + dev_dbg(&applespi->spi->dev, + "Error creating debugfs entry tp_dim (%ld)\n", + PTR_ERR(ret)); + } + + return 0; +} + +static void applespi_drain_writes(struct applespi_data *applespi) +{ + unsigned long flags; + + spin_lock_irqsave(&applespi->cmd_msg_lock, flags); + + applespi->drain = true; + wait_event_lock_irq(applespi->drain_complete, !applespi->write_active, + applespi->cmd_msg_lock); + + spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags); +} + +static void applespi_drain_reads(struct applespi_data *applespi) +{ + unsigned long flags; + + spin_lock_irqsave(&applespi->cmd_msg_lock, flags); + + wait_event_lock_irq(applespi->drain_complete, !applespi->read_active, + applespi->cmd_msg_lock); + + applespi->suspended = true; + + spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags); +} + +static int applespi_remove(struct spi_device *spi) +{ + struct applespi_data *applespi = spi_get_drvdata(spi); + + applespi_drain_writes(applespi); + + acpi_disable_gpe(NULL, applespi->gpe); + acpi_remove_gpe_handler(NULL, applespi->gpe, applespi_notify); + device_wakeup_disable(&spi->dev); + + applespi_drain_reads(applespi); + + debugfs_remove_recursive(applespi->debugfs_root); + + return 0; +} + +static void applespi_shutdown(struct spi_device *spi) +{ + struct applespi_data *applespi = spi_get_drvdata(spi); + + applespi_save_bl_level(applespi, applespi->have_bl_level); +} + +static int applespi_poweroff_late(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct applespi_data *applespi = spi_get_drvdata(spi); + + applespi_save_bl_level(applespi, applespi->have_bl_level); + + return 0; +} + +static int __maybe_unused applespi_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct applespi_data *applespi = spi_get_drvdata(spi); + acpi_status acpi_sts; + int sts; + + /* turn off caps-lock - it'll stay on otherwise */ + sts = applespi_set_capsl_led(applespi, false); + if (sts) + dev_warn(&applespi->spi->dev, + "Failed to turn off caps-lock led (%d)\n", sts); + + applespi_drain_writes(applespi); + + /* disable the interrupt */ + acpi_sts = acpi_disable_gpe(NULL, applespi->gpe); + if (ACPI_FAILURE(acpi_sts)) + dev_err(&applespi->spi->dev, + "Failed to disable GPE handler for GPE %d: %s\n", + applespi->gpe, acpi_format_exception(acpi_sts)); + + applespi_drain_reads(applespi); + + return 0; +} + +static int __maybe_unused applespi_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct applespi_data *applespi = spi_get_drvdata(spi); + acpi_status acpi_sts; + unsigned long flags; + + /* ensure our flags and state reflect a newly resumed device */ + spin_lock_irqsave(&applespi->cmd_msg_lock, flags); + + applespi->drain = false; + applespi->have_cl_led_on = false; + applespi->have_bl_level = 0; + applespi->cmd_msg_queued = false; + applespi->read_active = false; + applespi->write_active = false; + + applespi->suspended = false; + + spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags); + + /* switch on the SPI interface */ + applespi_enable_spi(applespi); + + /* re-enable the interrupt */ + acpi_sts = acpi_enable_gpe(NULL, applespi->gpe); + if (ACPI_FAILURE(acpi_sts)) + dev_err(&applespi->spi->dev, + "Failed to re-enable GPE handler for GPE %d: %s\n", + applespi->gpe, acpi_format_exception(acpi_sts)); + + /* switch the touchpad into multitouch mode */ + applespi_init(applespi, true); + + return 0; +} + +static const struct acpi_device_id applespi_acpi_match[] = { + { "APP000D", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, applespi_acpi_match); + +const struct dev_pm_ops applespi_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(applespi_suspend, applespi_resume) + .poweroff_late = applespi_poweroff_late, +}; + +static struct spi_driver applespi_driver = { + .driver = { + .name = "applespi", + .acpi_match_table = applespi_acpi_match, + .pm = &applespi_pm_ops, + }, + .probe = applespi_probe, + .remove = applespi_remove, + .shutdown = applespi_shutdown, +}; + +module_spi_driver(applespi_driver) + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MacBook(Pro) SPI Keyboard/Touchpad driver"); +MODULE_AUTHOR("Federico Lorenzi"); +MODULE_AUTHOR("Ronald Tschalär"); diff --git a/drivers/input/keyboard/applespi.h b/drivers/input/keyboard/applespi.h new file mode 100644 index 000000000000..7f5ab10c597a --- /dev/null +++ b/drivers/input/keyboard/applespi.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * MacBook (Pro) SPI keyboard and touchpad driver + * + * Copyright (c) 2015-2019 Federico Lorenzi + * Copyright (c) 2017-2019 Ronald Tschalär + */ + +#ifndef _APPLESPI_H_ +#define _APPLESPI_H_ + +enum applespi_evt_type { + ET_CMD_TP_INI = BIT(0), + ET_CMD_BL = BIT(1), + ET_CMD_CL = BIT(2), + ET_RD_KEYB = BIT(8), + ET_RD_TPAD = BIT(9), + ET_RD_UNKN = BIT(10), + ET_RD_IRQ = BIT(11), + ET_RD_CRC = BIT(12), +}; + +enum applespi_pkt_type { + PT_READ, + PT_WRITE, + PT_STATUS, +}; + +#endif /* _APPLESPI_H_ */ diff --git a/drivers/input/keyboard/applespi_trace.h b/drivers/input/keyboard/applespi_trace.h new file mode 100644 index 000000000000..0ad1a3d79f50 --- /dev/null +++ b/drivers/input/keyboard/applespi_trace.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * MacBook (Pro) SPI keyboard and touchpad driver + * + * Copyright (c) 2015-2019 Federico Lorenzi + * Copyright (c) 2017-2019 Ronald Tschalär + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM applespi + +#if !defined(_APPLESPI_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) +#define _APPLESPI_TRACE_H_ + +#include +#include + +#include "applespi.h" + +DECLARE_EVENT_CLASS(dump_message_template, + TP_PROTO(enum applespi_evt_type evt_type, + enum applespi_pkt_type pkt_type, + u8 *buf, + size_t len), + + TP_ARGS(evt_type, pkt_type, buf, len), + + TP_STRUCT__entry( + __field(enum applespi_evt_type, evt_type) + __field(enum applespi_pkt_type, pkt_type) + __field(size_t, len) + __dynamic_array(u8, buf, len) + ), + + TP_fast_assign( + __entry->evt_type = evt_type; + __entry->pkt_type = pkt_type; + __entry->len = len; + memcpy(__get_dynamic_array(buf), buf, len); + ), + + TP_printk("%-6s: %s", + __print_symbolic(__entry->pkt_type, + { PT_READ, "read" }, + { PT_WRITE, "write" }, + { PT_STATUS, "status" } + ), + __print_hex(__get_dynamic_array(buf), __entry->len)) +); + +#define DEFINE_DUMP_MESSAGE_EVENT(name) \ +DEFINE_EVENT(dump_message_template, name, \ + TP_PROTO(enum applespi_evt_type evt_type, \ + enum applespi_pkt_type pkt_type, \ + u8 *buf, \ + size_t len), \ + TP_ARGS(evt_type, pkt_type, buf, len) \ +) + +DEFINE_DUMP_MESSAGE_EVENT(applespi_tp_ini_cmd); +DEFINE_DUMP_MESSAGE_EVENT(applespi_backlight_cmd); +DEFINE_DUMP_MESSAGE_EVENT(applespi_caps_lock_cmd); +DEFINE_DUMP_MESSAGE_EVENT(applespi_keyboard_data); +DEFINE_DUMP_MESSAGE_EVENT(applespi_touchpad_data); +DEFINE_DUMP_MESSAGE_EVENT(applespi_unknown_data); +DEFINE_DUMP_MESSAGE_EVENT(applespi_bad_crc); + +TRACE_EVENT(applespi_irq_received, + TP_PROTO(enum applespi_evt_type evt_type, + enum applespi_pkt_type pkt_type), + + TP_ARGS(evt_type, pkt_type), + + TP_STRUCT__entry( + __field(enum applespi_evt_type, evt_type) + __field(enum applespi_pkt_type, pkt_type) + ), + + TP_fast_assign( + __entry->evt_type = evt_type; + __entry->pkt_type = pkt_type; + ), + + "\n" +); + +#endif /* _APPLESPI_TRACE_H_ */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH ../../drivers/input/keyboard +#define TRACE_INCLUDE_FILE applespi_trace +#include -- cgit v1.2.3-59-g8ed1b From d56fef0ec85b8a8bae5d7d003b93f0a0aaf82ed9 Mon Sep 17 00:00:00 2001 From: Mao Wenan Date: Tue, 16 Jul 2019 20:16:28 +0200 Subject: Input: applespi - remove set but not used variables 'sts' Fixes gcc '-Wunused-but-set-variable' warning: drivers/input/keyboard/applespi.c: In function applespi_set_bl_level: drivers/input/keyboard/applespi.c:902:6: warning: variable sts set but not used [-Wunused-but-set-variable] Fixes: b426ac0452093d ("Input: add Apple SPI keyboard and trackpad driver") Signed-off-by: Mao Wenan Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/applespi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/input/keyboard/applespi.c b/drivers/input/keyboard/applespi.c index c1a68438245a..548737e7aeda 100644 --- a/drivers/input/keyboard/applespi.c +++ b/drivers/input/keyboard/applespi.c @@ -899,7 +899,6 @@ static void applespi_set_bl_level(struct led_classdev *led_cdev, struct applespi_data *applespi = container_of(led_cdev, struct applespi_data, backlight_info); unsigned long flags; - int sts; spin_lock_irqsave(&applespi->cmd_msg_lock, flags); @@ -916,7 +915,7 @@ static void applespi_set_bl_level(struct led_classdev *led_cdev, KBD_BL_LEVEL_MIN); } - sts = applespi_send_cmd_msg(applespi); + applespi_send_cmd_msg(applespi); spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags); } -- cgit v1.2.3-59-g8ed1b From 49e6979e7e92cf496105b5636f1df0ac17c159c0 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Tue, 16 Jul 2019 20:17:20 +0200 Subject: Input: psmouse - fix build error of multiple definition trackpoint_detect() should be static inline while CONFIG_MOUSE_PS2_TRACKPOINT is not set, otherwise, we build fails: drivers/input/mouse/alps.o: In function `trackpoint_detect': alps.c:(.text+0x8e00): multiple definition of `trackpoint_detect' drivers/input/mouse/psmouse-base.o:psmouse-base.c:(.text+0x1b50): first defined here Reported-by: Hulk Robot Fixes: 55e3d9224b60 ("Input: psmouse - allow disabing certain protocol extensions") Signed-off-by: YueHaibing Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/trackpoint.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/input/mouse/trackpoint.h b/drivers/input/mouse/trackpoint.h index 0afffe8d824f..77110f3ec21d 100644 --- a/drivers/input/mouse/trackpoint.h +++ b/drivers/input/mouse/trackpoint.h @@ -158,7 +158,8 @@ struct trackpoint_data { #ifdef CONFIG_MOUSE_PS2_TRACKPOINT int trackpoint_detect(struct psmouse *psmouse, bool set_properties); #else -inline int trackpoint_detect(struct psmouse *psmouse, bool set_properties) +static inline int trackpoint_detect(struct psmouse *psmouse, + bool set_properties) { return -ENOSYS; } -- cgit v1.2.3-59-g8ed1b From 771a081e44a9baa1991ef011cc453ef425591740 Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Fri, 19 Jul 2019 12:38:58 +0300 Subject: Input: alps - fix a mismatch between a condition check and its comment In the function alps_is_cs19_trackpoint(), we check if the param[1] is in the 0x20~0x2f range, but the code we wrote for this checking is not correct: (param[1] & 0x20) does not mean param[1] is in the range of 0x20~0x2f, it also means the param[1] is in the range of 0x30~0x3f, 0x60~0x6f... Now fix it with a new condition checking ((param[1] & 0xf0) == 0x20). Fixes: 7e4935ccc323 ("Input: alps - don't handle ALPS cs19 trackpoint-only device") Cc: stable@vger.kernel.org Signed-off-by: Hui Wang Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 62ffea00902a..34700eda0429 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -2876,7 +2876,7 @@ static bool alps_is_cs19_trackpoint(struct psmouse *psmouse) * trackpoint-only devices have their variant_ids equal * TP_VARIANT_ALPS and their firmware_ids are in 0x20~0x2f range. */ - return param[0] == TP_VARIANT_ALPS && (param[1] & 0x20); + return param[0] == TP_VARIANT_ALPS && ((param[1] & 0xf0) == 0x20); } static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) -- cgit v1.2.3-59-g8ed1b