From 1b84f2a4cd4a6f517a313261f6f7c8caae5696c6 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 2 Feb 2015 12:26:22 +0100 Subject: mfd: cros_ec: Use fixed size arrays to transfer data with the EC The struct cros_ec_command will be used as an ioctl() argument for the API to control the ChromeOS EC from user-space. So the data structure has to be 64-bit safe to make it compatible between 32 and 64 avoiding the need for a compat ioctl interface. Since pointers are self-aligned to different byte boundaries, use fixed size arrays instead of pointers for transferring ingoing and outgoing data with the Embedded Controller. Also, re-arrange struct members by decreasing alignment requirements to reduce the needing padding size. Signed-off-by: Javier Martinez Canillas Acked-by: Lee Jones Tested-by: Gwendal Grignou Reviewed-by: Gwendal Grignou Signed-off-by: Olof Johansson --- drivers/input/keyboard/cros_ec_keyb.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index ffa989f2c785..769f8f7f62b7 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -148,16 +148,19 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state) { + int ret; struct cros_ec_command msg = { - .version = 0, .command = EC_CMD_MKBP_STATE, - .outdata = NULL, - .outsize = 0, - .indata = kb_state, .insize = ckdev->cols, }; - return cros_ec_cmd_xfer(ckdev->ec, &msg); + ret = cros_ec_cmd_xfer(ckdev->ec, &msg); + if (ret < 0) + return ret; + + memcpy(kb_state, msg.indata, ckdev->cols); + + return 0; } static irqreturn_t cros_ec_keyb_irq(int irq, void *data) -- cgit v1.2.3-59-g8ed1b From 559addc25b00ff3a40eff03a0b3873c2b6d726f8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 4 Feb 2015 06:07:30 -0300 Subject: [media] fixp-arith: replace sin/cos table by a better precision one The cos table used at fixp-arith.h has only 8 bits of precision. That causes problems if it is reused on other drivers. As some media drivers require a higher precision sin/cos implementation, replace the current implementation by one that will provide 32 bits precision. The values generated by the new implementation matches the 32 bit precision of glibc's sin for an angle measured in integer degrees. It also provides support for fractional angles via linear interpolation. On experimental calculus, when used a table with a 0.001 degree angle, the maximum error for sin is 0.000038, which is likely good enough for practical purposes. There are some logic there that seems to be specific to the usage inside ff-memless.c. Move those logic to there, as they're not needed elsewhere. Cc: Hans de Goede Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Prashant Laddha Signed-off-by: Hans Verkuil Acked-by: Dmitry Torokhov Signed-off-by: Mauro Carvalho Chehab --- drivers/input/ff-memless.c | 18 ++++- drivers/media/usb/gspca/ov534.c | 11 +-- include/linux/fixp-arith.h | 145 +++++++++++++++++++++++++++++----------- 3 files changed, 125 insertions(+), 49 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c index 74c0d8c6002a..fcc6c3368182 100644 --- a/drivers/input/ff-memless.c +++ b/drivers/input/ff-memless.c @@ -237,6 +237,18 @@ static u16 ml_calculate_direction(u16 direction, u16 force, (force + new_force)) << 1; } +#define FRAC_N 8 +static inline s16 fixp_new16(s16 a) +{ + return ((s32)a) >> (16 - FRAC_N); +} + +static inline s16 fixp_mult(s16 a, s16 b) +{ + a = ((s32)a * 0x100) / 0x7fff; + return ((s32)(a * b)) >> FRAC_N; +} + /* * Combine two effects and apply gain. */ @@ -247,7 +259,7 @@ static void ml_combine_effects(struct ff_effect *effect, struct ff_effect *new = state->effect; unsigned int strong, weak, i; int x, y; - fixp_t level; + s16 level; switch (new->type) { case FF_CONSTANT: @@ -255,8 +267,8 @@ static void ml_combine_effects(struct ff_effect *effect, level = fixp_new16(apply_envelope(state, new->u.constant.level, &new->u.constant.envelope)); - x = fixp_mult(fixp_sin(i), level) * gain / 0xffff; - y = fixp_mult(-fixp_cos(i), level) * gain / 0xffff; + x = fixp_mult(fixp_sin16(i), level) * gain / 0xffff; + y = fixp_mult(-fixp_cos16(i), level) * gain / 0xffff; /* * here we abuse ff_ramp to hold x and y of constant force * If in future any driver wants something else than x and y diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c index a9c866d6d82d..146071b8e116 100644 --- a/drivers/media/usb/gspca/ov534.c +++ b/drivers/media/usb/gspca/ov534.c @@ -816,21 +816,16 @@ static void sethue(struct gspca_dev *gspca_dev, s32 val) s16 huesin; s16 huecos; - /* fixp_sin and fixp_cos accept only positive values, while - * our val is between -90 and 90 - */ - val += 360; - /* According to the datasheet the registers expect HUESIN and * HUECOS to be the result of the trigonometric functions, * scaled by 0x80. * - * The 0x100 here represents the maximun absolute value + * The 0x7fff here represents the maximum absolute value * returned byt fixp_sin and fixp_cos, so the scaling will * consider the result like in the interval [-1.0, 1.0]. */ - huesin = fixp_sin(val) * 0x80 / 0x100; - huecos = fixp_cos(val) * 0x80 / 0x100; + huesin = fixp_sin16(val) * 0x80 / 0x7fff; + huecos = fixp_cos16(val) * 0x80 / 0x7fff; if (huesin < 0) { sccb_reg_write(gspca_dev, 0xab, diff --git a/include/linux/fixp-arith.h b/include/linux/fixp-arith.h index 3089d7382325..d4686fe1cac7 100644 --- a/include/linux/fixp-arith.h +++ b/include/linux/fixp-arith.h @@ -1,6 +1,8 @@ #ifndef _FIXP_ARITH_H #define _FIXP_ARITH_H +#include + /* * Simplistic fixed-point arithmetics. * Hmm, I'm probably duplicating some code :( @@ -29,59 +31,126 @@ #include -/* The type representing fixed-point values */ -typedef s16 fixp_t; +static const s32 sin_table[] = { + 0x00000000, 0x023be165, 0x04779632, 0x06b2f1d2, 0x08edc7b6, 0x0b27eb5c, + 0x0d61304d, 0x0f996a26, 0x11d06c96, 0x14060b67, 0x163a1a7d, 0x186c6ddd, + 0x1a9cd9ac, 0x1ccb3236, 0x1ef74bf2, 0x2120fb82, 0x234815ba, 0x256c6f9e, + 0x278dde6e, 0x29ac379f, 0x2bc750e8, 0x2ddf003f, 0x2ff31bdd, 0x32037a44, + 0x340ff241, 0x36185aee, 0x381c8bb5, 0x3a1c5c56, 0x3c17a4e7, 0x3e0e3ddb, + 0x3fffffff, 0x41ecc483, 0x43d464fa, 0x45b6bb5d, 0x4793a20f, 0x496af3e1, + 0x4b3c8c11, 0x4d084650, 0x4ecdfec6, 0x508d9210, 0x5246dd48, 0x53f9be04, + 0x55a6125a, 0x574bb8e5, 0x58ea90c2, 0x5a827999, 0x5c135399, 0x5d9cff82, + 0x5f1f5ea0, 0x609a52d1, 0x620dbe8a, 0x637984d3, 0x64dd894f, 0x6639b039, + 0x678dde6d, 0x68d9f963, 0x6a1de735, 0x6b598ea1, 0x6c8cd70a, 0x6db7a879, + 0x6ed9eba0, 0x6ff389de, 0x71046d3c, 0x720c8074, 0x730baeec, 0x7401e4bf, + 0x74ef0ebb, 0x75d31a5f, 0x76adf5e5, 0x777f903b, 0x7847d908, 0x7906c0af, + 0x79bc384c, 0x7a6831b8, 0x7b0a9f8c, 0x7ba3751c, 0x7c32a67c, 0x7cb82884, + 0x7d33f0c8, 0x7da5f5a3, 0x7e0e2e31, 0x7e6c924f, 0x7ec11aa3, 0x7f0bc095, + 0x7f4c7e52, 0x7f834ecf, 0x7fb02dc4, 0x7fd317b3, 0x7fec09e1, 0x7ffb025e, + 0x7fffffff +}; -#define FRAC_N 8 -#define FRAC_MASK ((1< 180) { + negative = true; + degrees -= 180; + } + if (degrees > 90) + degrees = 180 - degrees; + ret = sin_table[degrees]; -/* a: 123 -> 123.0 */ -static inline fixp_t fixp_new(s16 a) -{ - return a< -1.0 - 0x8000 -> 1.0 - 0x0000 -> 0.0 -*/ -static inline fixp_t fixp_new16(s16 a) +/** + * fixp_sin32() returns the sin of an angle in degrees + * + * @degrees: angle, in degrees. The angle can be positive or negative + * + * The returned value ranges from -0x7fffffff to +0x7fffffff. + */ +static inline s32 fixp_sin32(int degrees) { - return ((s32)a)>>(16-FRAC_N); + degrees = (degrees % 360 + 360) % 360; + + return __fixp_sin32(degrees); } -static inline fixp_t fixp_cos(unsigned int degrees) +/* cos(x) = sin(x + 90 degrees) */ +#define fixp_cos32(v) fixp_sin32((v) + 90) + +/* + * 16 bits variants + * + * The returned value ranges from -0x7fff to 0x7fff + */ + +#define fixp_sin16(v) (fixp_sin32(v) >> 16) +#define fixp_cos16(v) (fixp_cos32(v) >> 16) + +/** + * fixp_sin32_rad() - calculates the sin of an angle in radians + * + * @radians: angle, in radians + * @twopi: value to be used for 2*pi + * + * Provides a variant for the cases where just 360 + * values is not enough. This function uses linear + * interpolation to a wider range of values given by + * twopi var. + * + * Experimental tests gave a maximum difference of + * 0.000038 between the value calculated by sin() and + * the one produced by this function, when twopi is + * equal to 360000. That seems to be enough precision + * for practical purposes. + * + * Please notice that two high numbers for twopi could cause + * overflows, so the routine will not allow values of twopi + * bigger than 1^18. + */ +static inline s32 fixp_sin32_rad(u32 radians, u32 twopi) { - int quadrant = (degrees / 90) & 3; - unsigned int i = degrees % 90; + int degrees; + s32 v1, v2, dx, dy; + s64 tmp; - if (quadrant == 1 || quadrant == 3) - i = 90 - i; + /* + * Avoid too large values for twopi, as we don't want overflows. + */ + BUG_ON(twopi > 1 << 18); - i >>= 1; + degrees = (radians * 360) / twopi; + tmp = radians - (degrees * twopi) / 360; - return (quadrant == 1 || quadrant == 2)? -cos_table[i] : cos_table[i]; -} + degrees = (degrees % 360 + 360) % 360; + v1 = __fixp_sin32(degrees); -static inline fixp_t fixp_sin(unsigned int degrees) -{ - return -fixp_cos(degrees + 90); -} + v2 = fixp_sin32(degrees + 1); -static inline fixp_t fixp_mult(fixp_t a, fixp_t b) -{ - return ((s32)(a*b))>>FRAC_N; + dx = twopi / 360; + dy = v2 - v1; + + tmp *= dy; + + return v1 + div_s64(tmp, dx); } +/* cos(x) = sin(x + pi/2 radians) */ + +#define fixp_cos32_rad(rad, twopi) \ + fixp_sin32_rad(rad + twopi / 4, twopi) + #endif -- cgit v1.2.3-59-g8ed1b From 1feb57a245a4910b03202a814ffc51a900bd4aca Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Wed, 21 Jan 2015 22:33:46 +0100 Subject: gpio: add parameter to allow the use named gpios The gpio binding document says that new code should always use named gpios. Patch 40b73183 added support to parse a list of gpios from child nodes, but does not make it possible to use named gpios. This patch adds the con_id property and implements it is done in gpiolib.c, where the old-style of using unnamed gpios still works. Signed-off-by: Olliver Schinagl Acked-by: Bryan Wu Acked-by: Dmitry Torokhov Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/devres.c | 18 +++++++++++++++++- drivers/input/keyboard/gpio_keys_polled.c | 2 +- drivers/leds/leds-gpio.c | 2 +- include/linux/gpio/consumer.h | 1 + 4 files changed, 20 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/gpio/devres.c b/drivers/gpio/devres.c index 13dbd3dfc33a..12c2082f968e 100644 --- a/drivers/gpio/devres.c +++ b/drivers/gpio/devres.c @@ -111,23 +111,39 @@ EXPORT_SYMBOL(__devm_gpiod_get_index); /** * devm_get_gpiod_from_child - get a GPIO descriptor from a device's child node * @dev: GPIO consumer + * @con_id: function within the GPIO consumer * @child: firmware node (child of @dev) * * GPIO descriptors returned from this function are automatically disposed on * driver detach. */ struct gpio_desc *devm_get_gpiod_from_child(struct device *dev, + const char *con_id, struct fwnode_handle *child) { + static const char const *suffixes[] = { "gpios", "gpio" }; + char prop_name[32]; /* 32 is max size of property name */ struct gpio_desc **dr; struct gpio_desc *desc; + unsigned int i; dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *), GFP_KERNEL); if (!dr) return ERR_PTR(-ENOMEM); - desc = fwnode_get_named_gpiod(child, "gpios"); + for (i = 0; i < ARRAY_SIZE(suffixes); i++) { + if (con_id) + snprintf(prop_name, sizeof(prop_name), "%s-%s", + con_id, suffixes[i]); + else + snprintf(prop_name, sizeof(prop_name), "%s", + suffixes[i]); + + desc = fwnode_get_named_gpiod(child, prop_name); + if (!IS_ERR(desc) || (PTR_ERR(desc) == -EPROBE_DEFER)) + break; + } if (IS_ERR(desc)) { devres_free(dr); return desc; diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c index 90df4df58b07..097d7216d98e 100644 --- a/drivers/input/keyboard/gpio_keys_polled.c +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -125,7 +125,7 @@ static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct device_for_each_child_node(dev, child) { struct gpio_desc *desc; - desc = devm_get_gpiod_from_child(dev, child); + desc = devm_get_gpiod_from_child(dev, NULL, child); if (IS_ERR(desc)) { error = PTR_ERR(desc); if (error != -EPROBE_DEFER) diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index d26af0a79a90..15eb3f86f670 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -184,7 +184,7 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) struct gpio_led led = {}; const char *state = NULL; - led.gpiod = devm_get_gpiod_from_child(dev, child); + led.gpiod = devm_get_gpiod_from_child(dev, NULL, child); if (IS_ERR(led.gpiod)) { fwnode_handle_put(child); ret = PTR_ERR(led.gpiod); diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 45afc2dee560..ed20759229eb 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -110,6 +110,7 @@ struct fwnode_handle; struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, const char *propname); struct gpio_desc *devm_get_gpiod_from_child(struct device *dev, + const char *con_id, struct fwnode_handle *child); #else /* CONFIG_GPIOLIB */ -- cgit v1.2.3-59-g8ed1b From 1422731dbc14063fc7083cb090108c0d95267361 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 19 Mar 2015 15:52:45 +0100 Subject: mfd: tc3589x: Enforce device-tree only mode All systems using the TC3589x multifunction expander uses devicetree, so don't clutter the place with a lot of and assume it is there. Signed-off-by: Linus Walleij Signed-off-by: Lee Jones --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-tc3589x.c | 3 --- drivers/input/keyboard/tc3589x-keypad.c | 9 --------- drivers/mfd/Kconfig | 1 + drivers/mfd/tc3589x.c | 9 --------- 5 files changed, 2 insertions(+), 21 deletions(-) (limited to 'drivers/input') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index c1e2ca3d9a51..dc1aaa83a347 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -669,6 +669,7 @@ config GPIO_STP_XWAY config GPIO_TC3589X bool "TC3589X GPIOs" depends on MFD_TC3589X + depends on OF_GPIO select GPIOLIB_IRQCHIP help This enables support for the GPIOs found on the TC3589X diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c index 11aed2671065..31b244cffabb 100644 --- a/drivers/gpio/gpio-tc3589x.c +++ b/drivers/gpio/gpio-tc3589x.c @@ -260,10 +260,7 @@ static int tc3589x_gpio_probe(struct platform_device *pdev) tc3589x_gpio->chip.ngpio = tc3589x->num_gpio; tc3589x_gpio->chip.dev = &pdev->dev; tc3589x_gpio->chip.base = -1; - -#ifdef CONFIG_OF_GPIO tc3589x_gpio->chip.of_node = np; -#endif /* Bring the GPIO module out of reset */ ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, diff --git a/drivers/input/keyboard/tc3589x-keypad.c b/drivers/input/keyboard/tc3589x-keypad.c index 8ff612d160b0..aa695a80ec32 100644 --- a/drivers/input/keyboard/tc3589x-keypad.c +++ b/drivers/input/keyboard/tc3589x-keypad.c @@ -296,7 +296,6 @@ static void tc3589x_keypad_close(struct input_dev *input) tc3589x_keypad_disable(keypad); } -#ifdef CONFIG_OF static const struct tc3589x_keypad_platform_data * tc3589x_keypad_of_probe(struct device *dev) { @@ -346,14 +345,6 @@ tc3589x_keypad_of_probe(struct device *dev) return plat; } -#else -static inline const struct tc3589x_keypad_platform_data * -tc3589x_keypad_of_probe(struct device *dev) -{ - return ERR_PTR(-ENODEV); -} -#endif - static int tc3589x_keypad_probe(struct platform_device *pdev) { diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 5e68fdef64e7..ba097f7ce26f 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1260,6 +1260,7 @@ config MFD_TIMBERDALE config MFD_TC3589X bool "Toshiba TC35892 and variants" depends on I2C=y + depends on OF select MFD_CORE help Support for the Toshiba TC35892 and variants I/O Expander. diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c index aacb3720065c..cf356395c9e9 100644 --- a/drivers/mfd/tc3589x.c +++ b/drivers/mfd/tc3589x.c @@ -318,7 +318,6 @@ static int tc3589x_device_init(struct tc3589x *tc3589x) return ret; } -#ifdef CONFIG_OF static const struct of_device_id tc3589x_match[] = { /* Legacy compatible string */ { .compatible = "tc3589x", .data = (void *) TC3589X_UNKNOWN }, @@ -359,14 +358,6 @@ tc3589x_of_probe(struct device *dev, enum tc3589x_version *version) return pdata; } -#else -static inline struct tc3589x_platform_data * -tc3589x_of_probe(struct device *dev, enum tc3589x_version *version) -{ - dev_err(dev, "no device tree support\n"); - return ERR_PTR(-ENODEV); -} -#endif static int tc3589x_probe(struct i2c_client *i2c, const struct i2c_device_id *id) -- cgit v1.2.3-59-g8ed1b From e831cd251fb91d6c25352d322743db0d17ea11dd Mon Sep 17 00:00:00 2001 From: Florian Echtler Date: Mon, 16 Mar 2015 06:48:23 -0300 Subject: [media] add raw video stream support for Samsung SUR40 This patch adds raw video support for the Samsung SUR40 using vbuf2-dma-sg. All tests from v4l2-compliance pass. Support for VB2_USERPTR is currently disabled due to unexpected interference with dma-sg buffer sizes. Signed-off-by: Florian Echtler Signed-off-by: Hans Verkuil [hans.verkuil@cisco.com: fix compile warning: %ld -> %zd] Signed-off-by: Mauro Carvalho Chehab --- drivers/input/touchscreen/Kconfig | 2 + drivers/input/touchscreen/sur40.c | 429 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 419 insertions(+), 12 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 58917525126e..f8d16f15c1d1 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -953,7 +953,9 @@ config TOUCHSCREEN_SUN4I config TOUCHSCREEN_SUR40 tristate "Samsung SUR40 (Surface 2.0/PixelSense) touchscreen" depends on USB + depends on MEDIA_USB_SUPPORT select INPUT_POLLDEV + select VIDEOBUF2_DMA_SG help Say Y here if you want support for the Samsung SUR40 touchscreen (also known as Microsoft Surface 2.0 or Microsoft PixelSense). diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index f1cb05148b46..b295e1744ea1 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -1,7 +1,7 @@ /* * Surface2.0/SUR40/PixelSense input driver * - * Copyright (c) 2013 by Florian 'floe' Echtler + * Copyright (c) 2014 by Florian 'floe' Echtler * * Derived from the USB Skeleton driver 1.1, * Copyright (c) 2003 Greg Kroah-Hartman (greg@kroah.com) @@ -12,6 +12,9 @@ * and from the generic hid-multitouch driver, * Copyright (c) 2010-2012 Stephane Chatty * + * and from the v4l2-pci-skeleton driver, + * Copyright (c) Copyright 2014 Cisco Systems, Inc. + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of @@ -31,6 +34,11 @@ #include #include #include +#include +#include +#include +#include +#include /* read 512 bytes from endpoint 0x86 -> get header + blobs */ struct sur40_header { @@ -82,9 +90,19 @@ struct sur40_data { struct sur40_blob blobs[]; } __packed; +/* read 512 bytes from endpoint 0x82 -> get header below + * continue reading 16k blocks until header.size bytes read */ +struct sur40_image_header { + __le32 magic; /* "SUBF" */ + __le32 packet_id; + __le32 size; /* always 0x0007e900 = 960x540 */ + __le32 timestamp; /* milliseconds (increases by 16 or 17 each frame) */ + __le32 unknown; /* "epoch?" always 02/03 00 00 00 */ +} __packed; /* version information */ #define DRIVER_SHORT "sur40" +#define DRIVER_LONG "Samsung SUR40" #define DRIVER_AUTHOR "Florian 'floe' Echtler " #define DRIVER_DESC "Surface2.0/SUR40/PixelSense input driver" @@ -99,6 +117,13 @@ struct sur40_data { /* touch data endpoint */ #define TOUCH_ENDPOINT 0x86 +/* video data endpoint */ +#define VIDEO_ENDPOINT 0x82 + +/* video header fields */ +#define VIDEO_HEADER_MAGIC 0x46425553 +#define VIDEO_PACKET_SIZE 16384 + /* polling interval (ms) */ #define POLL_INTERVAL 10 @@ -113,21 +138,23 @@ struct sur40_data { #define SUR40_GET_STATE 0xc5 /* 4 bytes state (?) */ #define SUR40_GET_SENSORS 0xb1 /* 8 bytes sensors */ -/* - * Note: an earlier, non-public version of this driver used USB_RECIP_ENDPOINT - * here by mistake which is very likely to have corrupted the firmware EEPROM - * on two separate SUR40 devices. Thanks to Alan Stern who spotted this bug. - * Should you ever run into a similar problem, the background story to this - * incident and instructions on how to fix the corrupted EEPROM are available - * at https://floe.butterbrot.org/matrix/hacking/surface/brick.html -*/ - +/* master device state */ struct sur40_state { struct usb_device *usbdev; struct device *dev; struct input_polled_dev *input; + struct v4l2_device v4l2; + struct video_device vdev; + struct mutex lock; + + struct vb2_queue queue; + struct vb2_alloc_ctx *alloc_ctx; + struct list_head buf_list; + spinlock_t qlock; + int sequence; + struct sur40_data *bulk_in_buffer; size_t bulk_in_size; u8 bulk_in_epaddr; @@ -135,6 +162,27 @@ struct sur40_state { char phys[64]; }; +struct sur40_buffer { + struct vb2_buffer vb; + struct list_head list; +}; + +/* forward declarations */ +static const struct video_device sur40_video_device; +static const struct v4l2_pix_format sur40_video_format; +static const struct vb2_queue sur40_queue; +static void sur40_process_video(struct sur40_state *sur40); + +/* + * Note: an earlier, non-public version of this driver used USB_RECIP_ENDPOINT + * here by mistake which is very likely to have corrupted the firmware EEPROM + * on two separate SUR40 devices. Thanks to Alan Stern who spotted this bug. + * Should you ever run into a similar problem, the background story to this + * incident and instructions on how to fix the corrupted EEPROM are available + * at https://floe.butterbrot.org/matrix/hacking/surface/brick.html +*/ + +/* command wrapper */ static int sur40_command(struct sur40_state *dev, u8 command, u16 index, void *buffer, u16 size) { @@ -247,7 +295,6 @@ static void sur40_report_blob(struct sur40_blob *blob, struct input_dev *input) /* core function: poll for new input data */ static void sur40_poll(struct input_polled_dev *polldev) { - struct sur40_state *sur40 = polldev->private; struct input_dev *input = polldev->input; int result, bulk_read, need_blobs, packet_blobs, i; @@ -314,6 +361,81 @@ static void sur40_poll(struct input_polled_dev *polldev) input_mt_sync_frame(input); input_sync(input); + + sur40_process_video(sur40); +} + +/* deal with video data */ +static void sur40_process_video(struct sur40_state *sur40) +{ + + struct sur40_image_header *img = (void *)(sur40->bulk_in_buffer); + struct sur40_buffer *new_buf; + struct usb_sg_request sgr; + struct sg_table *sgt; + int result, bulk_read; + + if (!vb2_start_streaming_called(&sur40->queue)) + return; + + /* get a new buffer from the list */ + spin_lock(&sur40->qlock); + new_buf = list_entry(sur40->buf_list.next, struct sur40_buffer, list); + list_del(&new_buf->list); + spin_unlock(&sur40->qlock); + + /* retrieve data via bulk read */ + result = usb_bulk_msg(sur40->usbdev, + usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT), + sur40->bulk_in_buffer, sur40->bulk_in_size, + &bulk_read, 1000); + + if (result < 0) { + dev_err(sur40->dev, "error in usb_bulk_read\n"); + goto err_poll; + } + + if (bulk_read != sizeof(struct sur40_image_header)) { + dev_err(sur40->dev, "received %d bytes (%zd expected)\n", + bulk_read, sizeof(struct sur40_image_header)); + goto err_poll; + } + + if (le32_to_cpu(img->magic) != VIDEO_HEADER_MAGIC) { + dev_err(sur40->dev, "image magic mismatch\n"); + goto err_poll; + } + + if (le32_to_cpu(img->size) != sur40_video_format.sizeimage) { + dev_err(sur40->dev, "image size mismatch\n"); + goto err_poll; + } + + sgt = vb2_dma_sg_plane_desc(&new_buf->vb, 0); + + result = usb_sg_init(&sgr, sur40->usbdev, + usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT), 0, + sgt->sgl, sgt->nents, sur40_video_format.sizeimage, 0); + if (result < 0) { + dev_err(sur40->dev, "error %d in usb_sg_init\n", result); + goto err_poll; + } + + usb_sg_wait(&sgr); + if (sgr.status < 0) { + dev_err(sur40->dev, "error %d in usb_sg_wait\n", sgr.status); + goto err_poll; + } + + /* mark as finished */ + v4l2_get_timestamp(&new_buf->vb.v4l2_buf.timestamp); + new_buf->vb.v4l2_buf.sequence = sur40->sequence++; + new_buf->vb.v4l2_buf.field = V4L2_FIELD_NONE; + vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_DONE); + return; + +err_poll: + vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_ERROR); } /* Initialize input device parameters. */ @@ -377,6 +499,11 @@ static int sur40_probe(struct usb_interface *interface, goto err_free_dev; } + /* initialize locks/lists */ + INIT_LIST_HEAD(&sur40->buf_list); + spin_lock_init(&sur40->qlock); + mutex_init(&sur40->lock); + /* Set up polled input device control structure */ poll_dev->private = sur40; poll_dev->poll_interval = POLL_INTERVAL; @@ -387,7 +514,7 @@ static int sur40_probe(struct usb_interface *interface, /* Set up regular input device structure */ sur40_input_setup(poll_dev->input); - poll_dev->input->name = "Samsung SUR40"; + poll_dev->input->name = DRIVER_LONG; usb_to_input_id(usbdev, &poll_dev->input->id); usb_make_path(usbdev, sur40->phys, sizeof(sur40->phys)); strlcat(sur40->phys, "/input0", sizeof(sur40->phys)); @@ -408,6 +535,7 @@ static int sur40_probe(struct usb_interface *interface, goto err_free_polldev; } + /* register the polled input device */ error = input_register_polled_device(poll_dev); if (error) { dev_err(&interface->dev, @@ -415,12 +543,54 @@ static int sur40_probe(struct usb_interface *interface, goto err_free_buffer; } + /* register the video master device */ + snprintf(sur40->v4l2.name, sizeof(sur40->v4l2.name), "%s", DRIVER_LONG); + error = v4l2_device_register(sur40->dev, &sur40->v4l2); + if (error) { + dev_err(&interface->dev, + "Unable to register video master device."); + goto err_unreg_v4l2; + } + + /* initialize the lock and subdevice */ + sur40->queue = sur40_queue; + sur40->queue.drv_priv = sur40; + sur40->queue.lock = &sur40->lock; + + /* initialize the queue */ + error = vb2_queue_init(&sur40->queue); + if (error) + goto err_unreg_v4l2; + + sur40->alloc_ctx = vb2_dma_sg_init_ctx(sur40->dev); + if (IS_ERR(sur40->alloc_ctx)) { + dev_err(sur40->dev, "Can't allocate buffer context"); + goto err_unreg_v4l2; + } + + sur40->vdev = sur40_video_device; + sur40->vdev.v4l2_dev = &sur40->v4l2; + sur40->vdev.lock = &sur40->lock; + sur40->vdev.queue = &sur40->queue; + video_set_drvdata(&sur40->vdev, sur40); + + error = video_register_device(&sur40->vdev, VFL_TYPE_GRABBER, -1); + if (error) { + dev_err(&interface->dev, + "Unable to register video subdevice."); + goto err_unreg_video; + } + /* we can register the device now, as it is ready */ usb_set_intfdata(interface, sur40); dev_dbg(&interface->dev, "%s is now attached\n", DRIVER_DESC); return 0; +err_unreg_video: + video_unregister_device(&sur40->vdev); +err_unreg_v4l2: + v4l2_device_unregister(&sur40->v4l2); err_free_buffer: kfree(sur40->bulk_in_buffer); err_free_polldev: @@ -436,6 +606,10 @@ static void sur40_disconnect(struct usb_interface *interface) { struct sur40_state *sur40 = usb_get_intfdata(interface); + video_unregister_device(&sur40->vdev); + v4l2_device_unregister(&sur40->v4l2); + vb2_dma_sg_cleanup_ctx(sur40->alloc_ctx); + input_unregister_polled_device(sur40->input); input_free_polled_device(sur40->input); kfree(sur40->bulk_in_buffer); @@ -445,12 +619,243 @@ static void sur40_disconnect(struct usb_interface *interface) dev_dbg(&interface->dev, "%s is now disconnected\n", DRIVER_DESC); } +/* + * Setup the constraints of the queue: besides setting the number of planes + * per buffer and the size and allocation context of each plane, it also + * checks if sufficient buffers have been allocated. Usually 3 is a good + * minimum number: many DMA engines need a minimum of 2 buffers in the + * queue and you need to have another available for userspace processing. + */ +static int sur40_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct sur40_state *sur40 = vb2_get_drv_priv(q); + + if (q->num_buffers + *nbuffers < 3) + *nbuffers = 3 - q->num_buffers; + + if (fmt && fmt->fmt.pix.sizeimage < sur40_video_format.sizeimage) + return -EINVAL; + + *nplanes = 1; + sizes[0] = fmt ? fmt->fmt.pix.sizeimage : sur40_video_format.sizeimage; + alloc_ctxs[0] = sur40->alloc_ctx; + + return 0; +} + +/* + * Prepare the buffer for queueing to the DMA engine: check and set the + * payload size. + */ +static int sur40_buffer_prepare(struct vb2_buffer *vb) +{ + struct sur40_state *sur40 = vb2_get_drv_priv(vb->vb2_queue); + unsigned long size = sur40_video_format.sizeimage; + + if (vb2_plane_size(vb, 0) < size) { + dev_err(&sur40->usbdev->dev, "buffer too small (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, size); + return 0; +} + +/* + * Queue this buffer to the DMA engine. + */ +static void sur40_buffer_queue(struct vb2_buffer *vb) +{ + struct sur40_state *sur40 = vb2_get_drv_priv(vb->vb2_queue); + struct sur40_buffer *buf = (struct sur40_buffer *)vb; + + spin_lock(&sur40->qlock); + list_add_tail(&buf->list, &sur40->buf_list); + spin_unlock(&sur40->qlock); +} + +static void return_all_buffers(struct sur40_state *sur40, + enum vb2_buffer_state state) +{ + struct sur40_buffer *buf, *node; + + spin_lock(&sur40->qlock); + list_for_each_entry_safe(buf, node, &sur40->buf_list, list) { + vb2_buffer_done(&buf->vb, state); + list_del(&buf->list); + } + spin_unlock(&sur40->qlock); +} + +/* + * Start streaming. First check if the minimum number of buffers have been + * queued. If not, then return -ENOBUFS and the vb2 framework will call + * this function again the next time a buffer has been queued until enough + * buffers are available to actually start the DMA engine. + */ +static int sur40_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct sur40_state *sur40 = vb2_get_drv_priv(vq); + + sur40->sequence = 0; + return 0; +} + +/* + * Stop the DMA engine. Any remaining buffers in the DMA queue are dequeued + * and passed on to the vb2 framework marked as STATE_ERROR. + */ +static void sur40_stop_streaming(struct vb2_queue *vq) +{ + struct sur40_state *sur40 = vb2_get_drv_priv(vq); + + /* Release all active buffers */ + return_all_buffers(sur40, VB2_BUF_STATE_ERROR); +} + +/* V4L ioctl */ +static int sur40_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct sur40_state *sur40 = video_drvdata(file); + + strlcpy(cap->driver, DRIVER_SHORT, sizeof(cap->driver)); + strlcpy(cap->card, DRIVER_LONG, sizeof(cap->card)); + usb_make_path(sur40->usbdev, cap->bus_info, sizeof(cap->bus_info)); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int sur40_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + i->type = V4L2_INPUT_TYPE_CAMERA; + i->std = V4L2_STD_UNKNOWN; + strlcpy(i->name, "In-Cell Sensor", sizeof(i->name)); + i->capabilities = 0; + return 0; +} + +static int sur40_vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + return (i == 0) ? 0 : -EINVAL; +} + +static int sur40_vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int sur40_vidioc_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + f->fmt.pix = sur40_video_format; + return 0; +} + +static int sur40_vidioc_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index != 0) + return -EINVAL; + strlcpy(f->description, "8-bit greyscale", sizeof(f->description)); + f->pixelformat = V4L2_PIX_FMT_GREY; + f->flags = 0; + return 0; +} + static const struct usb_device_id sur40_table[] = { { USB_DEVICE(ID_MICROSOFT, ID_SUR40) }, /* Samsung SUR40 */ { } /* terminating null entry */ }; MODULE_DEVICE_TABLE(usb, sur40_table); +/* V4L2 structures */ +static const struct vb2_ops sur40_queue_ops = { + .queue_setup = sur40_queue_setup, + .buf_prepare = sur40_buffer_prepare, + .buf_queue = sur40_buffer_queue, + .start_streaming = sur40_start_streaming, + .stop_streaming = sur40_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static const struct vb2_queue sur40_queue = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + /* + * VB2_USERPTR in currently not enabled: passing a user pointer to + * dma-sg will result in segment sizes that are not a multiple of + * 512 bytes, which is required by the host controller. + */ + .io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF, + .buf_struct_size = sizeof(struct sur40_buffer), + .ops = &sur40_queue_ops, + .mem_ops = &vb2_dma_sg_memops, + .timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC, + .min_buffers_needed = 3, +}; + +static const struct v4l2_file_operations sur40_video_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .unlocked_ioctl = video_ioctl2, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, +}; + +static const struct v4l2_ioctl_ops sur40_video_ioctl_ops = { + + .vidioc_querycap = sur40_vidioc_querycap, + + .vidioc_enum_fmt_vid_cap = sur40_vidioc_enum_fmt, + .vidioc_try_fmt_vid_cap = sur40_vidioc_fmt, + .vidioc_s_fmt_vid_cap = sur40_vidioc_fmt, + .vidioc_g_fmt_vid_cap = sur40_vidioc_fmt, + + .vidioc_enum_input = sur40_vidioc_enum_input, + .vidioc_g_input = sur40_vidioc_g_input, + .vidioc_s_input = sur40_vidioc_s_input, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +static const struct video_device sur40_video_device = { + .name = DRIVER_LONG, + .fops = &sur40_video_fops, + .ioctl_ops = &sur40_video_ioctl_ops, + .release = video_device_release_empty, +}; + +static const struct v4l2_pix_format sur40_video_format = { + .pixelformat = V4L2_PIX_FMT_GREY, + .width = SENSOR_RES_X / 2, + .height = SENSOR_RES_Y / 2, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .bytesperline = SENSOR_RES_X / 2, + .sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2), +}; + /* USB-specific object needed to register this driver with the USB subsystem. */ static struct usb_driver sur40_driver = { .name = DRIVER_SHORT, -- cgit v1.2.3-59-g8ed1b From bd884149aca61de269fd9bad83fe2a4232ffab21 Mon Sep 17 00:00:00 2001 From: Ulrik De Bie Date: Mon, 6 Apr 2015 15:35:38 -0700 Subject: Input: elantech - fix absolute mode setting on some ASUS laptops On ASUS TP500LN and X750JN, the touchpad absolute mode is reset each time set_rate is done. In order to fix this, we will verify the firmware version, and if it matches the one in those laptops, the set_rate function is overloaded with a function elantech_set_rate_restore_reg_07 that performs the set_rate with the original function, followed by a restore of reg_07 (the register that sets the absolute mode on elantech v4 hardware). Also the ASUS TP500LN and X750JN firmware version, capabilities, and button constellation is added to elantech.c Cc: stable@vger.kernel.org Reported-and-tested-by: George Moutsopoulos Signed-off-by: Ulrik De Bie Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elantech.c | 22 ++++++++++++++++++++++ drivers/input/mouse/elantech.h | 1 + 2 files changed, 23 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 6e22682c8255..991dc6b20a58 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -892,6 +892,21 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse) return PSMOUSE_FULL_PACKET; } +/* + * This writes the reg_07 value again to the hardware at the end of every + * set_rate call because the register loses its value. reg_07 allows setting + * absolute mode on v4 hardware + */ +static void elantech_set_rate_restore_reg_07(struct psmouse *psmouse, + unsigned int rate) +{ + struct elantech_data *etd = psmouse->private; + + etd->original_set_rate(psmouse, rate); + if (elantech_write_reg(psmouse, 0x07, etd->reg_07)) + psmouse_err(psmouse, "restoring reg_07 failed\n"); +} + /* * Put the touchpad into absolute mode */ @@ -1094,6 +1109,8 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse, * Asus K53SV 0x450f01 78, 15, 0c 2 hw buttons * Asus G46VW 0x460f02 00, 18, 0c 2 hw buttons * Asus G750JX 0x360f00 00, 16, 0c 2 hw buttons + * Asus TP500LN 0x381f17 10, 14, 0e clickpad + * Asus X750JN 0x381f17 10, 14, 0e clickpad * Asus UX31 0x361f00 20, 15, 0e clickpad * Asus UX32VD 0x361f02 00, 15, 0e clickpad * Avatar AVIU-145A2 0x361f00 ? clickpad @@ -1635,6 +1652,11 @@ int elantech_init(struct psmouse *psmouse) goto init_fail; } + if (etd->fw_version == 0x381f17) { + etd->original_set_rate = psmouse->set_rate; + psmouse->set_rate = elantech_set_rate_restore_reg_07; + } + if (elantech_set_input_params(psmouse)) { psmouse_err(psmouse, "failed to query touchpad range.\n"); goto init_fail; diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h index 6f3afec02f03..f965d1569cc3 100644 --- a/drivers/input/mouse/elantech.h +++ b/drivers/input/mouse/elantech.h @@ -142,6 +142,7 @@ struct elantech_data { struct finger_pos mt[ETP_MAX_FINGERS]; unsigned char parity[256]; int (*send_cmd)(struct psmouse *psmouse, unsigned char c, unsigned char *param); + void (*original_set_rate)(struct psmouse *psmouse, unsigned int rate); }; #ifdef CONFIG_MOUSE_PS2_ELANTECH -- cgit v1.2.3-59-g8ed1b From c627589282213bbef12a482c0b87f23526ceb3f0 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 30 Mar 2015 14:54:15 -0400 Subject: Input - mt: Fix input_mt_get_slot_by_key The case occurred recently with a touchscreen using twice a slot during a single EV_SYN event: E: 0.288415 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- E: 0.296207 0003 002f 0000 # EV_ABS / ABS_MT_SLOT 0 E: 0.296207 0003 0039 -001 # EV_ABS / ABS_MT_TRACKING_ID -1 E: 0.296207 0003 002f 0001 # EV_ABS / ABS_MT_SLOT 1 E: 0.296207 0003 0035 0908 # EV_ABS / ABS_MT_POSITION_X 908 E: 0.296207 0003 0036 1062 # EV_ABS / ABS_MT_POSITION_Y 1062 E: 0.296207 0003 002f 0000 # EV_ABS / ABS_MT_SLOT 0 E: 0.296207 0003 0039 8787 # EV_ABS / ABS_MT_TRACKING_ID 8787 E: 0.296207 0003 0035 1566 # EV_ABS / ABS_MT_POSITION_X 1566 E: 0.296207 0003 0036 0861 # EV_ABS / ABS_MT_POSITION_Y 861 E: 0.296207 0003 0000 0908 # EV_ABS / ABS_X 908 E: 0.296207 0003 0001 1062 # EV_ABS / ABS_Y 1062 E: 0.296207 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- This occurred because while having already slots 0 and 1 assigned, the touchscreen sent: 0.293377 Tip Switch: 0 | Contact Id: 0 | X: 539 | Y: 1960 | Contact Count: 3 0.294783 Tip Switch: 1 | Contact Id: 1 | X: 908 | Y: 1062 | Contact Count: 0 0.296187 Tip Switch: 1 | Contact Id: 2 | X: 1566 | Y: 861 | Contact Count: 0 Slot 0 is released correclty, but when we look for Contact ID 2, the slot 0 is then picked up again because it is marked as inactive (trackingID < 0). This is wrong, and we should not reuse a slot in the same frame. The test should also check for input_mt_is_used(). In addition, we need to initialize mt->frame to an other value than 0. With mt->frame being 0, all slots are tags as currently used, and so input_mt_get_slot_by_key() would return -1 for all requests. Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=88903 Signed-off-by: Benjamin Tissoires Acked-by: Dmitry Torokhov Signed-off-by: Jiri Kosina --- drivers/input/input-mt.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index fbe29fcb15c5..f4b58f77c524 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -88,10 +88,13 @@ int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots, goto err_mem; } - /* Mark slots as 'unused' */ + /* Mark slots as 'inactive' */ for (i = 0; i < num_slots; i++) input_mt_set_value(&mt->slots[i], ABS_MT_TRACKING_ID, -1); + /* Mark slots as 'unused' */ + mt->frame = 1; + dev->mt = mt; return 0; err_mem: @@ -430,6 +433,8 @@ EXPORT_SYMBOL(input_mt_assign_slots); * set the key on the first unused slot and return. * * If no available slot can be found, -1 is returned. + * Note that for this function to work properly, input_mt_sync_frame() has + * to be called at each frame. */ int input_mt_get_slot_by_key(struct input_dev *dev, int key) { @@ -444,7 +449,7 @@ int input_mt_get_slot_by_key(struct input_dev *dev, int key) return s - mt->slots; for (s = mt->slots; s != mt->slots + mt->num_slots; s++) - if (!input_mt_is_active(s)) { + if (!input_mt_is_active(s) && !input_mt_is_used(mt, s)) { s->key = key; return s - mt->slots; } -- cgit v1.2.3-59-g8ed1b From c2529908a1905cd8b76bed0a6039975226cb24eb Mon Sep 17 00:00:00 2001 From: Florian Echtler Date: Tue, 31 Mar 2015 06:43:28 -0300 Subject: [media] sur40: fix occasional hard freeze due to buffer queue underrun This patch fixes a kernel panic which occurs when buf_list is empty. This can happen occasionally when user space is under heavy load (e.g. due to image processing on the CPU) and new buffers aren't re-queued fast enough. In that case, vb2_start_streaming_called can return true, but when the spinlock is taken and sur40_poll attempts to fetch the next buffer from buf_list, the list is in fact empty. This patch needs to be applied on top of the queued one adding V4L2 support to the sur40 driver. Signed-off-by: Florian Echtler Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/input/touchscreen/sur40.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index b295e1744ea1..a24eba5ea843 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -380,6 +380,11 @@ static void sur40_process_video(struct sur40_state *sur40) /* get a new buffer from the list */ spin_lock(&sur40->qlock); + if (list_empty(&sur40->buf_list)) { + dev_dbg(sur40->dev, "buffer queue empty\n"); + spin_unlock(&sur40->qlock); + return; + } new_buf = list_entry(sur40->buf_list.next, struct sur40_buffer, list); list_del(&new_buf->list); spin_unlock(&sur40->qlock); -- cgit v1.2.3-59-g8ed1b From 6bcca19f5dcedc3a006ca0bcc3699a437cadee74 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 8 Apr 2015 09:26:42 -0700 Subject: Input: alps - fix touchpad buttons getting stuck when used with trackpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the left touchpad button gets pressed, and then the trackpoint is moved, and then the button is released, the following happens: 1) touchpad packet is received, touchpad evdev node reports BTN_LEFT 1 2) pointing stick packet is received, the hw will report a BTN_LEFT 1 in this packet because when the trackstick is active it communicates the combined touchpad + pointing stick buttons in the trackstick packet, since alps_report_bare_ps2_packet passes NULL (*) for the dev2 parameter to alps_report_buttons the combining is not detected and the pointing stick evdev node will also report BTN_LEFT 1 3) on release of the button a pointing stick packet with BTN_LEFT 0 is received and the pointing stick evdev node will report BTN_LEFT 0 Note how because of the passing as NULL for dev2 the touchpad evdev node will never send BTN_LEFT 0 in this scenario leading to a stuck mouse button. This is a regression in 4.0 introduced by commit 04aae283ba6a8 ("Input: ALPS - do not mix trackstick and external PS/2 mouse data") This commit fixes this by passing in the touchpad evdev as dev2 parameter when calling alps_report_buttons for the pointingstick on alps v2 devices, so that alps_report_buttons correctly detect that we're already reporting the button as pressed via the touchpad evdev node, and will also send the release event there. Cc: stable@vger.kernel.org # 4.0 Reported-by: Hans de Bruin Signed-off-by: Hans de Goede Acked-by: Pali Rohár Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 27bcdbc950c9..ea6cb64dfb28 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -1159,13 +1159,14 @@ static void alps_report_bare_ps2_packet(struct psmouse *psmouse, bool report_buttons) { struct alps_data *priv = psmouse->private; - struct input_dev *dev; + struct input_dev *dev, *dev2 = NULL; /* Figure out which device to use to report the bare packet */ if (priv->proto_version == ALPS_PROTO_V2 && (priv->flags & ALPS_DUALPOINT)) { /* On V2 devices the DualPoint Stick reports bare packets */ dev = priv->dev2; + dev2 = psmouse->dev; } else if (unlikely(IS_ERR_OR_NULL(priv->dev3))) { /* Register dev3 mouse if we received PS/2 packet first time */ if (!IS_ERR(priv->dev3)) @@ -1177,7 +1178,7 @@ static void alps_report_bare_ps2_packet(struct psmouse *psmouse, } if (report_buttons) - alps_report_buttons(dev, NULL, + alps_report_buttons(dev, dev2, packet[0] & 1, packet[0] & 2, packet[0] & 4); input_report_rel(dev, REL_X, -- cgit v1.2.3-59-g8ed1b From 92bac83dd79e60e65c475222e41a992a70434beb Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 12 Apr 2015 15:42:35 -0700 Subject: Input: alps - non interleaved V2 dualpoint has separate stick button bits Non interleaved V2 dualpoint touchpad / stick combos have separate stick button bits in the touchpad packets, if we do not check these then the trackpoint buttons will not work when using the touchpad, and when pressed when the user starts using the touchpad will report a release event even though the button is still pressed. This commit fixes this by checking the separate bits, note that we simply combine the buttons, since the hardware does the same when using the touchpad buttons with the trackpoint, so we do not have enough information to properly separate them. Reported-by: Hans de Bruin Signed-off-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index ea6cb64dfb28..da3af8db697c 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -243,6 +243,14 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse) return; } + /* Non interleaved V2 dualpoint has separate stick button bits */ + if (priv->proto_version == ALPS_PROTO_V2 && + priv->flags == (ALPS_PASS | ALPS_DUALPOINT)) { + left |= packet[0] & 1; + right |= packet[0] & 2; + middle |= packet[0] & 4; + } + alps_report_buttons(dev, dev2, left, right, middle); /* Convert hardware tap to a reasonable Z value */ -- cgit v1.2.3-59-g8ed1b From ef403bcaf16b704b772cf33cc2871fb8693d3bb3 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 23 Apr 2015 15:09:07 -0300 Subject: [media] Input: TOUCHSCREEN_SUR40 should depend on HAS_DMA If NO_DMA=y: warning: (TOUCHSCREEN_SUR40 && VIDEO_TW68 && VIDEO_CX23885 && VIDEO_CX25821 && VIDEO_CX88 && VIDEO_SAA7134) selects VIDEOBUF2_DMA_SG which has unmet direct dependencies (MEDIA_SUPPORT && HAS_DMA) ERROR: "dma_unmap_sg" [drivers/media/v4l2-core/videobuf2-dma-sg.ko] undefined! ERROR: "dma_map_sg" [drivers/media/v4l2-core/videobuf2-dma-sg.ko] undefined! ERROR: "dma_sync_sg_for_cpu" [drivers/media/v4l2-core/videobuf2-dma-sg.ko] undefined! TOUCHSCREEN_SUR40 selects VIDEOBUF2_DMA_SG, which bypasses its dependency on HAS_DMA. Make TOUCHSCREEN_SUR40 depend on HAS_DMA to fix this. Signed-off-by: Geert Uytterhoeven Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/input/touchscreen/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 80f6386709bf..05e4c6ffbe36 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -979,8 +979,7 @@ config TOUCHSCREEN_SUN4I config TOUCHSCREEN_SUR40 tristate "Samsung SUR40 (Surface 2.0/PixelSense) touchscreen" - depends on USB - depends on MEDIA_USB_SUPPORT + depends on USB && MEDIA_USB_SUPPORT && HAS_DMA select INPUT_POLLDEV select VIDEOBUF2_DMA_SG help -- cgit v1.2.3-59-g8ed1b From 429b474990cb4e5e8cfe2352daf649d0599cccb6 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 31 Mar 2015 15:55:59 +0200 Subject: tty: remove buf parameter from tty_name() tty_name no longer uses the buf parameter, so remove it along with all the 64 byte stack buffers that used to be passed in. Mostly generated by the coccinelle script @depends on patch@ identifier buf; constant C; expression tty; @@ - char buf[C]; <+... - tty_name(tty, buf) + tty_name(tty) ...+> allmodconfig compiles, so I'm fairly confident the stack buffers weren't used for other purposes as well. Signed-off-by: Rasmus Villemoes Reviewed-by: Peter Hurley Acked-by: Jesper Nilsson Acked-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/serio/serport.c | 5 ++--- drivers/tty/amiserial.c | 8 ++------ drivers/tty/cyclades.c | 8 ++------ drivers/tty/n_gsm.c | 3 +-- drivers/tty/n_tty.c | 7 ++----- drivers/tty/serial/crisv10.c | 8 ++------ drivers/tty/serial/serial_core.c | 4 +--- drivers/tty/tty_io.c | 28 +++++++++++----------------- drivers/tty/tty_ioctl.c | 4 +--- drivers/tty/tty_ldisc.c | 8 +++----- include/linux/tty.h | 2 +- 11 files changed, 28 insertions(+), 57 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index 69175b825346..9c927d35c1f5 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -167,7 +167,6 @@ static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, u { struct serport *serport = (struct serport*) tty->disc_data; struct serio *serio; - char name[64]; if (test_and_set_bit(SERPORT_BUSY, &serport->flags)) return -EBUSY; @@ -177,7 +176,7 @@ static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, u return -ENOMEM; strlcpy(serio->name, "Serial port", sizeof(serio->name)); - snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", tty_name(tty, name)); + snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", tty_name(tty)); serio->id = serport->id; serio->id.type = SERIO_RS232; serio->write = serport_serio_write; @@ -187,7 +186,7 @@ static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, u serio->dev.parent = tty->dev; serio_register_port(serport->serio); - printk(KERN_INFO "serio: Serial port %s\n", tty_name(tty, name)); + printk(KERN_INFO "serio: Serial port %s\n", tty_name(tty)); wait_event_interruptible(serport->wait, test_bit(SERPORT_DEAD, &serport->flags)); serio_unregister_port(serport->serio); diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index b2d760055952..894d3a84e285 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -966,9 +966,7 @@ static void rs_throttle(struct tty_struct * tty) struct serial_state *info = tty->driver_data; unsigned long flags; #ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("throttle %s: %d....\n", tty_name(tty, buf), + printk("throttle %s: %d....\n", tty_name(tty), tty->ldisc.chars_in_buffer(tty)); #endif @@ -991,9 +989,7 @@ static void rs_unthrottle(struct tty_struct * tty) struct serial_state *info = tty->driver_data; unsigned long flags; #ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("unthrottle %s: %d....\n", tty_name(tty, buf), + printk("unthrottle %s: %d....\n", tty_name(tty), tty->ldisc.chars_in_buffer(tty)); #endif diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index fd66f57390d0..87f6578c6f4a 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -2861,9 +2861,7 @@ static void cy_throttle(struct tty_struct *tty) unsigned long flags; #ifdef CY_DEBUG_THROTTLE - char buf[64]; - - printk(KERN_DEBUG "cyc:throttle %s: %ld...ttyC%d\n", tty_name(tty, buf), + printk(KERN_DEBUG "cyc:throttle %s: %ld...ttyC%d\n", tty_name(tty), tty->ldisc.chars_in_buffer(tty), info->line); #endif @@ -2902,10 +2900,8 @@ static void cy_unthrottle(struct tty_struct *tty) unsigned long flags; #ifdef CY_DEBUG_THROTTLE - char buf[64]; - printk(KERN_DEBUG "cyc:unthrottle %s: %ld...ttyC%d\n", - tty_name(tty, buf), tty_chars_in_buffer(tty), info->line); + tty_name(tty), tty_chars_in_buffer(tty), info->line); #endif if (serial_paranoia_check(info, tty->name, "cy_unthrottle")) diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 91abc00aa833..7e039669fc82 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -2274,7 +2274,6 @@ static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, const unsigned char *dp; char *f; int i; - char buf[64]; char flags = TTY_NORMAL; if (debug & 4) @@ -2296,7 +2295,7 @@ static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, break; default: WARN_ONCE(1, "%s: unknown flag %d\n", - tty_name(tty, buf), flags); + tty_name(tty), flags); break; } } diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index cf6e0f2e1331..54da8f49394d 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1179,13 +1179,12 @@ static void n_tty_receive_break(struct tty_struct *tty) static void n_tty_receive_overrun(struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; - char buf[64]; ldata->num_overrun++; if (time_after(jiffies, ldata->overrun_time + HZ) || time_after(ldata->overrun_time, jiffies)) { printk(KERN_WARNING "%s: %d input overrun(s)\n", - tty_name(tty, buf), + tty_name(tty), ldata->num_overrun); ldata->overrun_time = jiffies; ldata->num_overrun = 0; @@ -1460,8 +1459,6 @@ static void n_tty_receive_char_closing(struct tty_struct *tty, unsigned char c) static void n_tty_receive_char_flagged(struct tty_struct *tty, unsigned char c, char flag) { - char buf[64]; - switch (flag) { case TTY_BREAK: n_tty_receive_break(tty); @@ -1475,7 +1472,7 @@ n_tty_receive_char_flagged(struct tty_struct *tty, unsigned char c, char flag) break; default: printk(KERN_ERR "%s: unknown flag %d\n", - tty_name(tty, buf), flag); + tty_name(tty), flag); break; } } diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 0c1825b0b41d..568ea0d2d699 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -3216,9 +3216,7 @@ rs_throttle(struct tty_struct * tty) { struct e100_serial *info = (struct e100_serial *)tty->driver_data; #ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("throttle %s: %lu....\n", tty_name(tty, buf), + printk("throttle %s: %lu....\n", tty_name(tty), (unsigned long)tty->ldisc.chars_in_buffer(tty)); #endif DFLOW(DEBUG_LOG(info->line,"rs_throttle %lu\n", tty->ldisc.chars_in_buffer(tty))); @@ -3238,9 +3236,7 @@ rs_unthrottle(struct tty_struct * tty) { struct e100_serial *info = (struct e100_serial *)tty->driver_data; #ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("unthrottle %s: %lu....\n", tty_name(tty, buf), + printk("unthrottle %s: %lu....\n", tty_name(tty), (unsigned long)tty->ldisc.chars_in_buffer(tty)); #endif DFLOW(DEBUG_LOG(info->line,"rs_unthrottle ldisc %d\n", tty->ldisc.chars_in_buffer(tty))); diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 0b7bb12dfc68..eec067d8eedb 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -894,12 +894,10 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, * need to rate-limit; it's CAP_SYS_ADMIN only. */ if (uport->flags & UPF_SPD_MASK) { - char buf[64]; - dev_notice(uport->dev, "%s sets custom speed on %s. This is deprecated.\n", current->comm, - tty_name(port->tty, buf)); + tty_name(port->tty)); } uart_change_speed(tty, state, NULL); } diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 1eaf0fbd99e4..57fc6ee12332 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -235,7 +235,6 @@ static void tty_del_file(struct file *file) /** * tty_name - return tty naming * @tty: tty structure - * @buf: unused * * Convert a tty structure into a name. The name reflects the kernel * naming policy and if udev is in use may not reflect user space @@ -243,7 +242,7 @@ static void tty_del_file(struct file *file) * Locking: none */ -const char *tty_name(const struct tty_struct *tty, char *buf) +const char *tty_name(const struct tty_struct *tty) { if (!tty) /* Hmm. NULL pointer. That's fun. */ return "NULL tty"; @@ -768,8 +767,7 @@ static void do_tty_hangup(struct work_struct *work) void tty_hangup(struct tty_struct *tty) { #ifdef TTY_DEBUG_HANGUP - char buf[64]; - printk(KERN_DEBUG "%s hangup...\n", tty_name(tty, buf)); + printk(KERN_DEBUG "%s hangup...\n", tty_name(tty)); #endif schedule_work(&tty->hangup_work); } @@ -788,9 +786,7 @@ EXPORT_SYMBOL(tty_hangup); void tty_vhangup(struct tty_struct *tty) { #ifdef TTY_DEBUG_HANGUP - char buf[64]; - - printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf)); + printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty)); #endif __tty_hangup(tty, 0); } @@ -829,9 +825,7 @@ void tty_vhangup_self(void) static void tty_vhangup_session(struct tty_struct *tty) { #ifdef TTY_DEBUG_HANGUP - char buf[64]; - - printk(KERN_DEBUG "%s vhangup session...\n", tty_name(tty, buf)); + printk(KERN_DEBUG "%s vhangup session...\n", tty_name(tty)); #endif __tty_hangup(tty, 1); } @@ -1767,7 +1761,6 @@ int tty_release(struct inode *inode, struct file *filp) struct tty_struct *o_tty = NULL; int do_sleep, final; int idx; - char buf[64]; long timeout = 0; int once = 1; @@ -1791,7 +1784,7 @@ int tty_release(struct inode *inode, struct file *filp) #ifdef TTY_DEBUG_HANGUP printk(KERN_DEBUG "%s: %s (tty count=%d)...\n", __func__, - tty_name(tty, buf), tty->count); + tty_name(tty), tty->count); #endif if (tty->ops->close) @@ -1842,7 +1835,7 @@ int tty_release(struct inode *inode, struct file *filp) if (once) { once = 0; printk(KERN_WARNING "%s: %s: read/write wait queue active!\n", - __func__, tty_name(tty, buf)); + __func__, tty_name(tty)); } schedule_timeout_killable(timeout); if (timeout < 120 * HZ) @@ -1854,13 +1847,13 @@ int tty_release(struct inode *inode, struct file *filp) if (o_tty) { if (--o_tty->count < 0) { printk(KERN_WARNING "%s: bad pty slave count (%d) for %s\n", - __func__, o_tty->count, tty_name(o_tty, buf)); + __func__, o_tty->count, tty_name(o_tty)); o_tty->count = 0; } } if (--tty->count < 0) { printk(KERN_WARNING "%s: bad tty->count (%d) for %s\n", - __func__, tty->count, tty_name(tty, buf)); + __func__, tty->count, tty_name(tty)); tty->count = 0; } @@ -1903,7 +1896,7 @@ int tty_release(struct inode *inode, struct file *filp) return 0; #ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "%s: %s: final close\n", __func__, tty_name(tty, buf)); + printk(KERN_DEBUG "%s: %s: final close\n", __func__, tty_name(tty)); #endif /* * Ask the line discipline code to release its structures @@ -1914,7 +1907,8 @@ int tty_release(struct inode *inode, struct file *filp) tty_flush_works(tty); #ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "%s: %s: freeing structure...\n", __func__, tty_name(tty, buf)); + printk(KERN_DEBUG "%s: %s: freeing structure...\n", __func__, + tty_name(tty)); #endif /* * The release_tty function takes care of the details of clearing diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 8e53fe469664..5232fb60b0b1 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -211,9 +211,7 @@ int tty_unthrottle_safe(struct tty_struct *tty) void tty_wait_until_sent(struct tty_struct *tty, long timeout) { #ifdef TTY_DEBUG_WAIT_UNTIL_SENT - char buf[64]; - - printk(KERN_DEBUG "%s wait until sent...\n", tty_name(tty, buf)); + printk(KERN_DEBUG "%s wait until sent...\n", tty_name(tty)); #endif if (!timeout) timeout = MAX_SCHEDULE_TIMEOUT; diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 3737f55272d2..c07fb5d9bcf9 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -22,9 +22,8 @@ #undef LDISC_DEBUG_HANGUP #ifdef LDISC_DEBUG_HANGUP -#define tty_ldisc_debug(tty, f, args...) ({ \ - char __b[64]; \ - printk(KERN_DEBUG "%s: %s: " f, __func__, tty_name(tty, __b), ##args); \ +#define tty_ldisc_debug(tty, f, args...) ({ \ + printk(KERN_DEBUG "%s: %s: " f, __func__, tty_name(tty), ##args); \ }) #else #define tty_ldisc_debug(tty, f, args...) @@ -483,7 +482,6 @@ static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld) static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) { - char buf[64]; struct tty_ldisc *new_ldisc; int r; @@ -504,7 +502,7 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) if (r < 0) panic("Couldn't open N_TTY ldisc for " "%s --- error %d.", - tty_name(tty, buf), r); + tty_name(tty), r); } } diff --git a/include/linux/tty.h b/include/linux/tty.h index 4cbecfc7b3c9..9a72c9144d8a 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -421,7 +421,7 @@ static inline struct tty_struct *tty_kref_get(struct tty_struct *tty) extern int tty_paranoia_check(struct tty_struct *tty, struct inode *inode, const char *routine); -extern const char *tty_name(const struct tty_struct *tty, char *buf); +extern const char *tty_name(const struct tty_struct *tty); extern void tty_wait_until_sent(struct tty_struct *tty, long timeout); extern int tty_check_change(struct tty_struct *tty); extern void __stop_tty(struct tty_struct *tty); -- cgit v1.2.3-59-g8ed1b From 22ffc3e42aa6a656192a45c7364fbb2de3214d93 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2015 12:45:38 -0700 Subject: Input: sx8654 - fix memory allocation check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have been testing wrong variable when trying to make sure that input allocation succeeded. Reported by Coverity (CID 1295918). Acked-by: Sébastien Szymanski Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/sx8654.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c index aecb9ad2e701..642f4a53de50 100644 --- a/drivers/input/touchscreen/sx8654.c +++ b/drivers/input/touchscreen/sx8654.c @@ -187,7 +187,7 @@ static int sx8654_probe(struct i2c_client *client, return -ENOMEM; input = devm_input_allocate_device(&client->dev); - if (!sx8654) + if (!input) return -ENOMEM; input->name = "SX8654 I2C Touchscreen"; -- cgit v1.2.3-59-g8ed1b From 3c0213d17a09601e0c6c0ae0e27caf70d988290f Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 23 Apr 2015 09:08:43 -0700 Subject: Input: elantech - fix semi-mt protocol for v3 HW When the v3 hardware sees more than one finger, it uses the semi-mt protocol to report the touches. However, it currently works when num_fingers is 0, 1 or 2, but when it is 3 and above, it sends only 1 finger as if num_fingers was 1. This confuses userspace which knows how to deal with extra fingers when all the slots are used, but not when some are missing. Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=90101 Cc: stable@vger.kernel.org Signed-off-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elantech.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 991dc6b20a58..79363b687195 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -315,7 +315,7 @@ static void elantech_report_semi_mt_data(struct input_dev *dev, unsigned int x2, unsigned int y2) { elantech_set_slot(dev, 0, num_fingers != 0, x1, y1); - elantech_set_slot(dev, 1, num_fingers == 2, x2, y2); + elantech_set_slot(dev, 1, num_fingers >= 2, x2, y2); } /* -- cgit v1.2.3-59-g8ed1b From 72eceab743cf61082357068e0686ffac66fe47e5 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 20 May 2015 14:37:41 -0700 Subject: Input: alps - fix finger jumps on lifting 2 fingers on v7 touchpad On v7 touchpads sometimes when 2 fingers are moved down on the touchpad until they "fall of" the touchpad, the second touch will report 0 for y (max y really since the y axis is inverted) and max x as coordinates, rather then reporting 0, 0 as is expected for a non touching finger. This commit detects this and treats these touches as non touching. See the evemu-recording here: https://bugzilla.redhat.com/attachment.cgi?id=1025058 BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1221200 Signed-off-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index e6708f6efb4d..7752bd59d4b7 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -941,6 +941,11 @@ static void alps_get_finger_coordinate_v7(struct input_mt_pos *mt, case V7_PACKET_ID_TWO: mt[1].x &= ~0x000F; mt[1].y |= 0x000F; + /* Detect false-postive touches where x & y report max value */ + if (mt[1].y == 0x7ff && mt[1].x == 0xff0) { + mt[1].x = 0; + /* y gets set to 0 at the end of this function */ + } break; case V7_PACKET_ID_MULTI: -- cgit v1.2.3-59-g8ed1b From 412dbad2c7e273b48e8477247c74b2ad65c452d2 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Wed, 20 May 2015 14:46:14 -0700 Subject: Input: vmmouse - do not reference non-existing version of X driver The vmmouse Kconfig help text was referring to an incorrect user-space driver version. Fix this. Signed-off-by: Thomas Hellstrom Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 7462d2fc8cfe..d7820d1152d2 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -156,7 +156,7 @@ config MOUSE_PS2_VMMOUSE Say Y here if you are running under control of VMware hypervisor (ESXi, Workstation or Fusion). Also make sure that when you enable this option, you remove the xf86-input-vmmouse user-space driver - or upgrade it to at least xf86-input-vmmouse 13.0.1, which doesn't + or upgrade it to at least xf86-input-vmmouse 13.1.0, which doesn't load in the presence of an in-kernel vmmouse driver. If unsure, say N. -- cgit v1.2.3-59-g8ed1b From 15397f153cfd69c2164c1fa593e26707ed1a3e72 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Wed, 20 May 2015 14:50:30 -0700 Subject: Input: joydev - don't classify the vmmouse as a joystick Joydev is currently thinking some absolute mice are joystick, and that messes up games in VMware guests, as the cursor typically gets stuck in the top left corner. Try to detect the event signature of a VMmouse input device and back off for such devices. We're still incorrectly detecting, for example, the VMware absolute USB mouse as a joystick, but adding an event signature matching also that device would be considerably more risky, so defer that to a later merge window. Signed-off-by: Thomas Hellstrom Signed-off-by: Dmitry Torokhov --- drivers/input/joydev.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index f362883c94e3..1d247bcf2ae2 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -747,6 +747,63 @@ static void joydev_cleanup(struct joydev *joydev) input_close_device(handle); } +static bool joydev_dev_is_absolute_mouse(struct input_dev *dev) +{ + DECLARE_BITMAP(jd_scratch, KEY_CNT); + + BUILD_BUG_ON(ABS_CNT > KEY_CNT || EV_CNT > KEY_CNT); + + /* + * Virtualization (VMware, etc) and remote management (HP + * ILO2) solutions use absolute coordinates for their virtual + * pointing devices so that there is one-to-one relationship + * between pointer position on the host screen and virtual + * guest screen, and so their mice use ABS_X, ABS_Y and 3 + * primary button events. This clashes with what joydev + * considers to be joysticks (a device with at minimum ABS_X + * axis). + * + * Here we are trying to separate absolute mice from + * joysticks. A device is, for joystick detection purposes, + * considered to be an absolute mouse if the following is + * true: + * + * 1) Event types are exactly EV_ABS, EV_KEY and EV_SYN. + * 2) Absolute events are exactly ABS_X and ABS_Y. + * 3) Keys are exactly BTN_LEFT, BTN_RIGHT and BTN_MIDDLE. + * 4) Device is not on "Amiga" bus. + */ + + bitmap_zero(jd_scratch, EV_CNT); + __set_bit(EV_ABS, jd_scratch); + __set_bit(EV_KEY, jd_scratch); + __set_bit(EV_SYN, jd_scratch); + if (!bitmap_equal(jd_scratch, dev->evbit, EV_CNT)) + return false; + + bitmap_zero(jd_scratch, ABS_CNT); + __set_bit(ABS_X, jd_scratch); + __set_bit(ABS_Y, jd_scratch); + if (!bitmap_equal(dev->absbit, jd_scratch, ABS_CNT)) + return false; + + bitmap_zero(jd_scratch, KEY_CNT); + __set_bit(BTN_LEFT, jd_scratch); + __set_bit(BTN_RIGHT, jd_scratch); + __set_bit(BTN_MIDDLE, jd_scratch); + + if (!bitmap_equal(dev->keybit, jd_scratch, KEY_CNT)) + return false; + + /* + * Amiga joystick (amijoy) historically uses left/middle/right + * button events. + */ + if (dev->id.bustype == BUS_AMIGA) + return false; + + return true; +} static bool joydev_match(struct input_handler *handler, struct input_dev *dev) { @@ -758,6 +815,10 @@ static bool joydev_match(struct input_handler *handler, struct input_dev *dev) if (test_bit(EV_KEY, dev->evbit) && test_bit(BTN_DIGI, dev->keybit)) return false; + /* Avoid absolute mice */ + if (joydev_dev_is_absolute_mouse(dev)) + return false; + return true; } -- cgit v1.2.3-59-g8ed1b From e686e9e156109cd2475196689a3144d91cf354b3 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 7 May 2015 22:24:58 -0700 Subject: Input: smtpe-ts - use msecs_to_jiffies() instead of HZ Use msecs_to_jiffies(20) instead of plain (HZ / 50), as the former is much more explicit about it's behavior. We want to schedule the task 20 mS from now, so make it explicit in the code. Signed-off-by: Marek Vasut Reviewed-by: Viresh Kumar Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/stmpe-ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c index 2d5ff86b343f..702ad200d916 100644 --- a/drivers/input/touchscreen/stmpe-ts.c +++ b/drivers/input/touchscreen/stmpe-ts.c @@ -164,7 +164,7 @@ static irqreturn_t stmpe_ts_handler(int irq, void *data) STMPE_TSC_CTRL_TSC_EN, STMPE_TSC_CTRL_TSC_EN); /* start polling for touch_det to detect release */ - schedule_delayed_work(&ts->work, HZ / 50); + schedule_delayed_work(&ts->work, msecs_to_jiffies(20)); return IRQ_HANDLED; } -- cgit v1.2.3-59-g8ed1b From 77b071e7931dd762563ac74e3e448b2aef23ad2f Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 7 May 2015 22:25:49 -0700 Subject: Input: smtpe-ts - wait 50mS until polling for pen-up Wait a little bit longer, 50mS instead of 20mS, until the driver starts polling for pen-up. The problematic behavior before this patch is applied is as follows. The behavior was observed on the STMPE610QTR controller. Upon a physical pen-down event, the touchscreen reports one set of x-y-p coordinates and a pen-down event. After that, the pen-up polling is triggered and since the controller is not ready yet, the polling mistakenly detects a pen-up event while the physical state is still such that the pen is down on the touch surface. The pen-up handling flushes the controller FIFO, so after that, all the samples in the controller are discarded. The controller becomes ready shortly after this bogus pen-up handling and does generate again a pen-down interrupt. This time, the controller contains x-y-p samples which all read as zero. Since pressure value is zero, this set of samples is effectively ignored by userland. In the end, the driver just bounces between pen-down and bogus pen-up handling, generating no useful results. Fix this by giving the controller a bit more time before polling it for pen-up. Signed-off-by: Marek Vasut Reviewed-by: Viresh Kumar Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/stmpe-ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c index 702ad200d916..e4c31256a74d 100644 --- a/drivers/input/touchscreen/stmpe-ts.c +++ b/drivers/input/touchscreen/stmpe-ts.c @@ -164,7 +164,7 @@ static irqreturn_t stmpe_ts_handler(int irq, void *data) STMPE_TSC_CTRL_TSC_EN, STMPE_TSC_CTRL_TSC_EN); /* start polling for touch_det to detect release */ - schedule_delayed_work(&ts->work, msecs_to_jiffies(20)); + schedule_delayed_work(&ts->work, msecs_to_jiffies(50)); return IRQ_HANDLED; } -- cgit v1.2.3-59-g8ed1b From 9c27847dda9cfae7c273cde62becf364f9fa9ea3 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 27 May 2015 11:09:38 +0930 Subject: kernel/params: constify struct kernel_param_ops uses Most code already uses consts for the struct kernel_param_ops, sweep the kernel for the last offending stragglers. Other than include/linux/moduleparam.h and kernel/params.c all other changes were generated with the following Coccinelle SmPL patch. Merge conflicts between trees can be handled with Coccinelle. In the future git could get Coccinelle merge support to deal with patch --> fail --> grammar --> Coccinelle --> new patch conflicts automatically for us on patches where the grammar is available and the patch is of high confidence. Consider this a feature request. Test compiled on x86_64 against: * allnoconfig * allmodconfig * allyesconfig @ const_found @ identifier ops; @@ const struct kernel_param_ops ops = { }; @ const_not_found depends on !const_found @ identifier ops; @@ -struct kernel_param_ops ops = { +const struct kernel_param_ops ops = { }; Generated-by: Coccinelle SmPL Cc: Rusty Russell Cc: Junio C Hamano Cc: Andrew Morton Cc: Kees Cook Cc: Tejun Heo Cc: Ingo Molnar Cc: cocci@systeme.lip6.fr Cc: linux-kernel@vger.kernel.org Signed-off-by: Luis R. Rodriguez Signed-off-by: Rusty Russell --- arch/s390/kernel/perf_cpum_sf.c | 2 +- arch/x86/kvm/mmu_audit.c | 2 +- arch/x86/platform/uv/uv_nmi.c | 2 +- drivers/block/null_blk.c | 4 ++-- drivers/char/ipmi/ipmi_watchdog.c | 6 +++--- drivers/dma/dmatest.c | 4 ++-- drivers/ide/ide.c | 2 +- drivers/infiniband/ulp/srp/ib_srp.c | 4 ++-- drivers/input/misc/ati_remote2.c | 4 ++-- drivers/input/mouse/psmouse-base.c | 2 +- drivers/misc/lis3lv02d/lis3lv02d.c | 2 +- drivers/mtd/ubi/block.c | 2 +- drivers/net/wireless/ath/wil6210/main.c | 4 ++-- drivers/power/test_power.c | 16 ++++++++-------- drivers/thermal/intel_powerclamp.c | 4 ++-- drivers/tty/hvc/hvc_iucv.c | 2 +- drivers/tty/sysrq.c | 2 +- drivers/video/fbdev/uvesafb.c | 2 +- drivers/virtio/virtio_mmio.c | 2 +- fs/nfs/super.c | 2 +- include/linux/moduleparam.h | 30 +++++++++++++++--------------- kernel/params.c | 14 +++++++------- net/sunrpc/auth.c | 2 +- net/sunrpc/xprtsock.c | 6 +++--- security/apparmor/lsm.c | 6 +++--- security/integrity/ima/ima_crypto.c | 2 +- sound/pci/hda/hda_intel.c | 2 +- 27 files changed, 66 insertions(+), 66 deletions(-) (limited to 'drivers/input') diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c index e6a1578fc000..afe05bfb7e00 100644 --- a/arch/s390/kernel/perf_cpum_sf.c +++ b/arch/s390/kernel/perf_cpum_sf.c @@ -1572,7 +1572,7 @@ static int param_set_sfb_size(const char *val, const struct kernel_param *kp) } #define param_check_sfb_size(name, p) __param_check(name, p, void) -static struct kernel_param_ops param_ops_sfb_size = { +static const struct kernel_param_ops param_ops_sfb_size = { .set = param_set_sfb_size, .get = param_get_sfb_size, }; diff --git a/arch/x86/kvm/mmu_audit.c b/arch/x86/kvm/mmu_audit.c index 9ade5cfb5a4c..87393e3ae087 100644 --- a/arch/x86/kvm/mmu_audit.c +++ b/arch/x86/kvm/mmu_audit.c @@ -291,7 +291,7 @@ static int mmu_audit_set(const char *val, const struct kernel_param *kp) return 0; } -static struct kernel_param_ops audit_param_ops = { +static const struct kernel_param_ops audit_param_ops = { .set = mmu_audit_set, .get = param_get_bool, }; diff --git a/arch/x86/platform/uv/uv_nmi.c b/arch/x86/platform/uv/uv_nmi.c index 7488cafab955..020c101c255f 100644 --- a/arch/x86/platform/uv/uv_nmi.c +++ b/arch/x86/platform/uv/uv_nmi.c @@ -104,7 +104,7 @@ static int param_set_local64(const char *val, const struct kernel_param *kp) return 0; } -static struct kernel_param_ops param_ops_local64 = { +static const struct kernel_param_ops param_ops_local64 = { .get = param_get_local64, .set = param_set_local64, }; diff --git a/drivers/block/null_blk.c b/drivers/block/null_blk.c index 65cd61a4145e..0b4b25617bb7 100644 --- a/drivers/block/null_blk.c +++ b/drivers/block/null_blk.c @@ -99,7 +99,7 @@ static int null_set_queue_mode(const char *str, const struct kernel_param *kp) return null_param_store_val(str, &queue_mode, NULL_Q_BIO, NULL_Q_MQ); } -static struct kernel_param_ops null_queue_mode_param_ops = { +static const struct kernel_param_ops null_queue_mode_param_ops = { .set = null_set_queue_mode, .get = param_get_int, }; @@ -127,7 +127,7 @@ static int null_set_irqmode(const char *str, const struct kernel_param *kp) NULL_IRQ_TIMER); } -static struct kernel_param_ops null_irqmode_param_ops = { +static const struct kernel_param_ops null_irqmode_param_ops = { .set = null_set_irqmode, .get = param_get_int, }; diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 37b8be7cba95..0ac3bd1a5497 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -208,7 +208,7 @@ static int set_param_timeout(const char *val, const struct kernel_param *kp) return rv; } -static struct kernel_param_ops param_ops_timeout = { +static const struct kernel_param_ops param_ops_timeout = { .set = set_param_timeout, .get = param_get_int, }; @@ -270,14 +270,14 @@ static int set_param_wdog_ifnum(const char *val, const struct kernel_param *kp) return 0; } -static struct kernel_param_ops param_ops_wdog_ifnum = { +static const struct kernel_param_ops param_ops_wdog_ifnum = { .set = set_param_wdog_ifnum, .get = param_get_int, }; #define param_check_wdog_ifnum param_check_int -static struct kernel_param_ops param_ops_str = { +static const struct kernel_param_ops param_ops_str = { .set = set_param_str, .get = get_param_str, }; diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index 220ee49633e4..b8576fd6bd0e 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -120,7 +120,7 @@ static struct dmatest_info { static int dmatest_run_set(const char *val, const struct kernel_param *kp); static int dmatest_run_get(char *val, const struct kernel_param *kp); -static struct kernel_param_ops run_ops = { +static const struct kernel_param_ops run_ops = { .set = dmatest_run_set, .get = dmatest_run_get, }; @@ -195,7 +195,7 @@ static int dmatest_wait_get(char *val, const struct kernel_param *kp) return param_get_bool(val, kp); } -static struct kernel_param_ops wait_ops = { +static const struct kernel_param_ops wait_ops = { .get = dmatest_wait_get, .set = param_set_bool, }; diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index e29b02ca9e91..f086ef387475 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -199,7 +199,7 @@ static int ide_set_dev_param_mask(const char *s, const struct kernel_param *kp) return 0; } -static struct kernel_param_ops param_ops_ide_dev_mask = { +static const struct kernel_param_ops param_ops_ide_dev_mask = { .set = ide_set_dev_param_mask }; diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index 918814cd0f80..e4eec9da5ea9 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -98,7 +98,7 @@ module_param(register_always, bool, 0444); MODULE_PARM_DESC(register_always, "Use memory registration even for contiguous memory regions"); -static struct kernel_param_ops srp_tmo_ops; +static const struct kernel_param_ops srp_tmo_ops; static int srp_reconnect_delay = 10; module_param_cb(reconnect_delay, &srp_tmo_ops, &srp_reconnect_delay, @@ -183,7 +183,7 @@ out: return res; } -static struct kernel_param_ops srp_tmo_ops = { +static const struct kernel_param_ops srp_tmo_ops = { .get = srp_tmo_get, .set = srp_tmo_set, }; diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c index f63341f20b91..cfd58e87da26 100644 --- a/drivers/input/misc/ati_remote2.c +++ b/drivers/input/misc/ati_remote2.c @@ -94,7 +94,7 @@ static int ati_remote2_get_mode_mask(char *buffer, static unsigned int channel_mask = ATI_REMOTE2_MAX_CHANNEL_MASK; #define param_check_channel_mask(name, p) __param_check(name, p, unsigned int) -static struct kernel_param_ops param_ops_channel_mask = { +static const struct kernel_param_ops param_ops_channel_mask = { .set = ati_remote2_set_channel_mask, .get = ati_remote2_get_channel_mask, }; @@ -103,7 +103,7 @@ MODULE_PARM_DESC(channel_mask, "Bitmask of channels to accept <15:Channel16>...< static unsigned int mode_mask = ATI_REMOTE2_MAX_MODE_MASK; #define param_check_mode_mask(name, p) __param_check(name, p, unsigned int) -static struct kernel_param_ops param_ops_mode_mask = { +static const struct kernel_param_ops param_ops_mode_mask = { .set = ati_remote2_set_mode_mask, .get = ati_remote2_get_mode_mask, }; diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 5bb1658f60c7..f8286b625ec4 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -47,7 +47,7 @@ MODULE_LICENSE("GPL"); static unsigned int psmouse_max_proto = PSMOUSE_AUTO; static int psmouse_set_maxproto(const char *val, const struct kernel_param *); static int psmouse_get_maxproto(char *buffer, const struct kernel_param *kp); -static struct kernel_param_ops param_ops_proto_abbrev = { +static const struct kernel_param_ops param_ops_proto_abbrev = { .set = psmouse_set_maxproto, .get = psmouse_get_maxproto, }; diff --git a/drivers/misc/lis3lv02d/lis3lv02d.c b/drivers/misc/lis3lv02d/lis3lv02d.c index 4739689d23ad..fb8705fc3aca 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d.c +++ b/drivers/misc/lis3lv02d/lis3lv02d.c @@ -115,7 +115,7 @@ static int param_set_axis(const char *val, const struct kernel_param *kp) return ret; } -static struct kernel_param_ops param_ops_axis = { +static const struct kernel_param_ops param_ops_axis = { .set = param_set_axis, .get = param_get_int, }; diff --git a/drivers/mtd/ubi/block.c b/drivers/mtd/ubi/block.c index c9eb78f10a0d..4968c071f399 100644 --- a/drivers/mtd/ubi/block.c +++ b/drivers/mtd/ubi/block.c @@ -161,7 +161,7 @@ static int __init ubiblock_set_param(const char *val, return 0; } -static struct kernel_param_ops ubiblock_param_ops = { +static const struct kernel_param_ops ubiblock_param_ops = { .set = ubiblock_set_param, }; module_param_cb(block, &ubiblock_param_ops, NULL, 0); diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index c2a238426425..790c3df0bb03 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -58,7 +58,7 @@ static int mtu_max_set(const char *val, const struct kernel_param *kp) return ret; } -static struct kernel_param_ops mtu_max_ops = { +static const struct kernel_param_ops mtu_max_ops = { .set = mtu_max_set, .get = param_get_uint, }; @@ -87,7 +87,7 @@ static int ring_order_set(const char *val, const struct kernel_param *kp) return 0; } -static struct kernel_param_ops ring_order_ops = { +static const struct kernel_param_ops ring_order_ops = { .set = ring_order_set, .get = param_get_uint, }; diff --git a/drivers/power/test_power.c b/drivers/power/test_power.c index f986e0cca7ac..83c42ea88f2b 100644 --- a/drivers/power/test_power.c +++ b/drivers/power/test_power.c @@ -448,42 +448,42 @@ static int param_set_battery_voltage(const char *key, #define param_get_battery_voltage param_get_int -static struct kernel_param_ops param_ops_ac_online = { +static const struct kernel_param_ops param_ops_ac_online = { .set = param_set_ac_online, .get = param_get_ac_online, }; -static struct kernel_param_ops param_ops_usb_online = { +static const struct kernel_param_ops param_ops_usb_online = { .set = param_set_usb_online, .get = param_get_usb_online, }; -static struct kernel_param_ops param_ops_battery_status = { +static const struct kernel_param_ops param_ops_battery_status = { .set = param_set_battery_status, .get = param_get_battery_status, }; -static struct kernel_param_ops param_ops_battery_present = { +static const struct kernel_param_ops param_ops_battery_present = { .set = param_set_battery_present, .get = param_get_battery_present, }; -static struct kernel_param_ops param_ops_battery_technology = { +static const struct kernel_param_ops param_ops_battery_technology = { .set = param_set_battery_technology, .get = param_get_battery_technology, }; -static struct kernel_param_ops param_ops_battery_health = { +static const struct kernel_param_ops param_ops_battery_health = { .set = param_set_battery_health, .get = param_get_battery_health, }; -static struct kernel_param_ops param_ops_battery_capacity = { +static const struct kernel_param_ops param_ops_battery_capacity = { .set = param_set_battery_capacity, .get = param_get_battery_capacity, }; -static struct kernel_param_ops param_ops_battery_voltage = { +static const struct kernel_param_ops param_ops_battery_voltage = { .set = param_set_battery_voltage, .get = param_get_battery_voltage, }; diff --git a/drivers/thermal/intel_powerclamp.c b/drivers/thermal/intel_powerclamp.c index 725718e97a0b..500f8f551817 100644 --- a/drivers/thermal/intel_powerclamp.c +++ b/drivers/thermal/intel_powerclamp.c @@ -119,7 +119,7 @@ exit: return ret; } -static struct kernel_param_ops duration_ops = { +static const struct kernel_param_ops duration_ops = { .set = duration_set, .get = param_get_int, }; @@ -167,7 +167,7 @@ exit_win: return ret; } -static struct kernel_param_ops window_size_ops = { +static const struct kernel_param_ops window_size_ops = { .set = window_size_set, .get = param_get_int, }; diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c index f78a87b07872..bb809cf36617 100644 --- a/drivers/tty/hvc/hvc_iucv.c +++ b/drivers/tty/hvc/hvc_iucv.c @@ -1345,7 +1345,7 @@ static int param_get_vmidfilter(char *buffer, const struct kernel_param *kp) #define param_check_vmidfilter(name, p) __param_check(name, p, void) -static struct kernel_param_ops param_ops_vmidfilter = { +static const struct kernel_param_ops param_ops_vmidfilter = { .set = param_set_vmidfilter, .get = param_get_vmidfilter, }; diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 843f2cdc280b..03b02c37d247 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -1002,7 +1002,7 @@ static int sysrq_reset_seq_param_set(const char *buffer, return 0; } -static struct kernel_param_ops param_ops_sysrq_reset_seq = { +static const struct kernel_param_ops param_ops_sysrq_reset_seq = { .get = param_get_ushort, .set = sysrq_reset_seq_param_set, }; diff --git a/drivers/video/fbdev/uvesafb.c b/drivers/video/fbdev/uvesafb.c index d32d1c4d1b99..178ae93b7ebd 100644 --- a/drivers/video/fbdev/uvesafb.c +++ b/drivers/video/fbdev/uvesafb.c @@ -1977,7 +1977,7 @@ static int param_set_scroll(const char *val, const struct kernel_param *kp) return 0; } -static struct kernel_param_ops param_ops_scroll = { +static const struct kernel_param_ops param_ops_scroll = { .set = param_set_scroll, }; #define param_check_scroll(name, p) __param_check(name, p, void) diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index 7a5e60dea6c5..10189b5b627f 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -691,7 +691,7 @@ static int vm_cmdline_get(char *buffer, const struct kernel_param *kp) return strlen(buffer) + 1; } -static struct kernel_param_ops vm_cmdline_param_ops = { +static const struct kernel_param_ops vm_cmdline_param_ops = { .set = vm_cmdline_set, .get = vm_cmdline_get, }; diff --git a/fs/nfs/super.c b/fs/nfs/super.c index f175b833b6ba..aa62004f1706 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -2847,7 +2847,7 @@ static int param_set_portnr(const char *val, const struct kernel_param *kp) *((unsigned int *)kp->arg) = num; return 0; } -static struct kernel_param_ops param_ops_portnr = { +static const struct kernel_param_ops param_ops_portnr = { .set = param_set_portnr, .get = param_get_uint, }; diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h index 1c9effa25e26..5d0f4d97997f 100644 --- a/include/linux/moduleparam.h +++ b/include/linux/moduleparam.h @@ -225,7 +225,7 @@ struct kparam_array /* Obsolete - use module_param_cb() */ #define module_param_call(name, set, get, arg, perm) \ - static struct kernel_param_ops __param_ops_##name = \ + static const struct kernel_param_ops __param_ops_##name = \ { .flags = 0, (void *)set, (void *)get }; \ __module_param_call(MODULE_PARAM_PREFIX, \ name, &__param_ops_##name, arg, \ @@ -376,64 +376,64 @@ static inline void destroy_params(const struct kernel_param *params, #define __param_check(name, p, type) \ static inline type __always_unused *__check_##name(void) { return(p); } -extern struct kernel_param_ops param_ops_byte; +extern const struct kernel_param_ops param_ops_byte; extern int param_set_byte(const char *val, const struct kernel_param *kp); extern int param_get_byte(char *buffer, const struct kernel_param *kp); #define param_check_byte(name, p) __param_check(name, p, unsigned char) -extern struct kernel_param_ops param_ops_short; +extern const struct kernel_param_ops param_ops_short; extern int param_set_short(const char *val, const struct kernel_param *kp); extern int param_get_short(char *buffer, const struct kernel_param *kp); #define param_check_short(name, p) __param_check(name, p, short) -extern struct kernel_param_ops param_ops_ushort; +extern const struct kernel_param_ops param_ops_ushort; extern int param_set_ushort(const char *val, const struct kernel_param *kp); extern int param_get_ushort(char *buffer, const struct kernel_param *kp); #define param_check_ushort(name, p) __param_check(name, p, unsigned short) -extern struct kernel_param_ops param_ops_int; +extern const struct kernel_param_ops param_ops_int; extern int param_set_int(const char *val, const struct kernel_param *kp); extern int param_get_int(char *buffer, const struct kernel_param *kp); #define param_check_int(name, p) __param_check(name, p, int) -extern struct kernel_param_ops param_ops_uint; +extern const struct kernel_param_ops param_ops_uint; extern int param_set_uint(const char *val, const struct kernel_param *kp); extern int param_get_uint(char *buffer, const struct kernel_param *kp); #define param_check_uint(name, p) __param_check(name, p, unsigned int) -extern struct kernel_param_ops param_ops_long; +extern const struct kernel_param_ops param_ops_long; extern int param_set_long(const char *val, const struct kernel_param *kp); extern int param_get_long(char *buffer, const struct kernel_param *kp); #define param_check_long(name, p) __param_check(name, p, long) -extern struct kernel_param_ops param_ops_ulong; +extern const struct kernel_param_ops param_ops_ulong; extern int param_set_ulong(const char *val, const struct kernel_param *kp); extern int param_get_ulong(char *buffer, const struct kernel_param *kp); #define param_check_ulong(name, p) __param_check(name, p, unsigned long) -extern struct kernel_param_ops param_ops_ullong; +extern const struct kernel_param_ops param_ops_ullong; extern int param_set_ullong(const char *val, const struct kernel_param *kp); extern int param_get_ullong(char *buffer, const struct kernel_param *kp); #define param_check_ullong(name, p) __param_check(name, p, unsigned long long) -extern struct kernel_param_ops param_ops_charp; +extern const struct kernel_param_ops param_ops_charp; extern int param_set_charp(const char *val, const struct kernel_param *kp); extern int param_get_charp(char *buffer, const struct kernel_param *kp); #define param_check_charp(name, p) __param_check(name, p, char *) /* We used to allow int as well as bool. We're taking that away! */ -extern struct kernel_param_ops param_ops_bool; +extern const struct kernel_param_ops param_ops_bool; extern int param_set_bool(const char *val, const struct kernel_param *kp); extern int param_get_bool(char *buffer, const struct kernel_param *kp); #define param_check_bool(name, p) __param_check(name, p, bool) -extern struct kernel_param_ops param_ops_invbool; +extern const struct kernel_param_ops param_ops_invbool; extern int param_set_invbool(const char *val, const struct kernel_param *kp); extern int param_get_invbool(char *buffer, const struct kernel_param *kp); #define param_check_invbool(name, p) __param_check(name, p, bool) /* An int, which can only be set like a bool (though it shows as an int). */ -extern struct kernel_param_ops param_ops_bint; +extern const struct kernel_param_ops param_ops_bint; extern int param_set_bint(const char *val, const struct kernel_param *kp); #define param_get_bint param_get_int #define param_check_bint param_check_int @@ -477,9 +477,9 @@ extern int param_set_bint(const char *val, const struct kernel_param *kp); perm, -1, 0); \ __MODULE_PARM_TYPE(name, "array of " #type) -extern struct kernel_param_ops param_array_ops; +extern const struct kernel_param_ops param_array_ops; -extern struct kernel_param_ops param_ops_string; +extern const struct kernel_param_ops param_ops_string; extern int param_set_copystring(const char *val, const struct kernel_param *); extern int param_get_string(char *buffer, const struct kernel_param *kp); diff --git a/kernel/params.c b/kernel/params.c index a22d6a759b1a..b7635c025e9b 100644 --- a/kernel/params.c +++ b/kernel/params.c @@ -251,7 +251,7 @@ char *parse_args(const char *doing, return scnprintf(buffer, PAGE_SIZE, format, \ *((type *)kp->arg)); \ } \ - struct kernel_param_ops param_ops_##name = { \ + const struct kernel_param_ops param_ops_##name = { \ .set = param_set_##name, \ .get = param_get_##name, \ }; \ @@ -303,7 +303,7 @@ static void param_free_charp(void *arg) maybe_kfree_parameter(*((char **)arg)); } -struct kernel_param_ops param_ops_charp = { +const struct kernel_param_ops param_ops_charp = { .set = param_set_charp, .get = param_get_charp, .free = param_free_charp, @@ -328,7 +328,7 @@ int param_get_bool(char *buffer, const struct kernel_param *kp) } EXPORT_SYMBOL(param_get_bool); -struct kernel_param_ops param_ops_bool = { +const struct kernel_param_ops param_ops_bool = { .flags = KERNEL_PARAM_OPS_FL_NOARG, .set = param_set_bool, .get = param_get_bool, @@ -356,7 +356,7 @@ int param_get_invbool(char *buffer, const struct kernel_param *kp) } EXPORT_SYMBOL(param_get_invbool); -struct kernel_param_ops param_ops_invbool = { +const struct kernel_param_ops param_ops_invbool = { .set = param_set_invbool, .get = param_get_invbool, }; @@ -379,7 +379,7 @@ int param_set_bint(const char *val, const struct kernel_param *kp) } EXPORT_SYMBOL(param_set_bint); -struct kernel_param_ops param_ops_bint = { +const struct kernel_param_ops param_ops_bint = { .flags = KERNEL_PARAM_OPS_FL_NOARG, .set = param_set_bint, .get = param_get_int, @@ -476,7 +476,7 @@ static void param_array_free(void *arg) arr->ops->free(arr->elem + arr->elemsize * i); } -struct kernel_param_ops param_array_ops = { +const struct kernel_param_ops param_array_ops = { .set = param_array_set, .get = param_array_get, .free = param_array_free, @@ -504,7 +504,7 @@ int param_get_string(char *buffer, const struct kernel_param *kp) } EXPORT_SYMBOL(param_get_string); -struct kernel_param_ops param_ops_string = { +const struct kernel_param_ops param_ops_string = { .set = param_set_copystring, .get = param_get_string, }; diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 47f38be4155f..02f53674dc39 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -72,7 +72,7 @@ static int param_get_hashtbl_sz(char *buffer, const struct kernel_param *kp) #define param_check_hashtbl_sz(name, p) __param_check(name, p, unsigned int); -static struct kernel_param_ops param_ops_hashtbl_sz = { +static const struct kernel_param_ops param_ops_hashtbl_sz = { .set = param_set_hashtbl_sz, .get = param_get_hashtbl_sz, }; diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 66891e32c5e3..b0517287075b 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -2982,7 +2982,7 @@ static int param_set_portnr(const char *val, const struct kernel_param *kp) RPC_MAX_RESVPORT); } -static struct kernel_param_ops param_ops_portnr = { +static const struct kernel_param_ops param_ops_portnr = { .set = param_set_portnr, .get = param_get_uint, }; @@ -3001,7 +3001,7 @@ static int param_set_slot_table_size(const char *val, RPC_MAX_SLOT_TABLE); } -static struct kernel_param_ops param_ops_slot_table_size = { +static const struct kernel_param_ops param_ops_slot_table_size = { .set = param_set_slot_table_size, .get = param_get_uint, }; @@ -3017,7 +3017,7 @@ static int param_set_max_slot_table_size(const char *val, RPC_MAX_SLOT_TABLE_LIMIT); } -static struct kernel_param_ops param_ops_max_slot_table_size = { +static const struct kernel_param_ops param_ops_max_slot_table_size = { .set = param_set_max_slot_table_size, .get = param_get_uint, }; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index e5f1561439db..45eb96d1e1d9 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -667,7 +667,7 @@ static struct security_operations apparmor_ops = { static int param_set_aabool(const char *val, const struct kernel_param *kp); static int param_get_aabool(char *buffer, const struct kernel_param *kp); #define param_check_aabool param_check_bool -static struct kernel_param_ops param_ops_aabool = { +static const struct kernel_param_ops param_ops_aabool = { .flags = KERNEL_PARAM_OPS_FL_NOARG, .set = param_set_aabool, .get = param_get_aabool @@ -676,7 +676,7 @@ static struct kernel_param_ops param_ops_aabool = { static int param_set_aauint(const char *val, const struct kernel_param *kp); static int param_get_aauint(char *buffer, const struct kernel_param *kp); #define param_check_aauint param_check_uint -static struct kernel_param_ops param_ops_aauint = { +static const struct kernel_param_ops param_ops_aauint = { .set = param_set_aauint, .get = param_get_aauint }; @@ -684,7 +684,7 @@ static struct kernel_param_ops param_ops_aauint = { static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp); static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp); #define param_check_aalockpolicy param_check_bool -static struct kernel_param_ops param_ops_aalockpolicy = { +static const struct kernel_param_ops param_ops_aalockpolicy = { .flags = KERNEL_PARAM_OPS_FL_NOARG, .set = param_set_aalockpolicy, .get = param_get_aalockpolicy diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index 686355fea7fd..e24121afb2f2 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -55,7 +55,7 @@ static int param_set_bufsize(const char *val, const struct kernel_param *kp) return 0; } -static struct kernel_param_ops param_ops_bufsize = { +static const struct kernel_param_ops param_ops_bufsize = { .set = param_set_bufsize, .get = param_get_uint, }; diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 34040d26c94f..23a1d2ffa7f3 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -166,7 +166,7 @@ MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode " #ifdef CONFIG_PM static int param_set_xint(const char *val, const struct kernel_param *kp); -static struct kernel_param_ops param_ops_xint = { +static const struct kernel_param_ops param_ops_xint = { .set = param_set_xint, .get = param_get_int, }; -- cgit v1.2.3-59-g8ed1b From ca27ab31597c9e3c75f0dd46d85087c0627d748c Mon Sep 17 00:00:00 2001 From: Florian Echtler Date: Mon, 25 May 2015 09:04:13 -0300 Subject: [media] reduce poll interval to allow full 60 FPS framerate The SUR40 hardware can deliver images at up to 60 FPS; at full USB2 bandwidth, one raw frame will take about 11 ms to transmit. If the poll interval is above 5 ms, fully handling one frame will take longer than 16 ms and the overall frame rate will drop below 60 FPS. To get the full frame rate without blocking all the time and still allowing for a bit of timing jitter, we reduce the poll interval to 4 ms. Signed-off-by: Martin Kaltenbrunner Signed-off-by: Florian Echtler Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/input/touchscreen/sur40.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index a24eba5ea843..e707b8dce8ed 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -125,7 +125,7 @@ struct sur40_image_header { #define VIDEO_PACKET_SIZE 16384 /* polling interval (ms) */ -#define POLL_INTERVAL 10 +#define POLL_INTERVAL 4 /* maximum number of contacts FIXME: this is a guess? */ #define MAX_CONTACTS 64 -- cgit v1.2.3-59-g8ed1b From da6e4674b0917d3a76ebb7938e6e2fa9532ce76f Mon Sep 17 00:00:00 2001 From: Florian Echtler Date: Mon, 25 May 2015 09:04:14 -0300 Subject: [media] add frame size/frame rate query functions Add missing functions to query the single fixed frame size (960x540) and supported frame rates. Technically, the SUR40 supports any arbitrary frame rate up to 60 FPS, as it is polled and not interrupt-driven. For now, we just report 30 and 60 FPS, which is sufficient to make most V4L2 tools work. Signed-off-by: Martin Kaltenbrunner Signed-off-by: Florian Echtler Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/input/touchscreen/sur40.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index e707b8dce8ed..d860d056cccb 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -778,6 +778,33 @@ static int sur40_vidioc_enum_fmt(struct file *file, void *priv, return 0; } +static int sur40_vidioc_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *f) +{ + if ((f->index != 0) || (f->pixel_format != V4L2_PIX_FMT_GREY)) + return -EINVAL; + + f->type = V4L2_FRMSIZE_TYPE_DISCRETE; + f->discrete.width = sur40_video_format.width; + f->discrete.height = sur40_video_format.height; + return 0; +} + +static int sur40_vidioc_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *f) +{ + if ((f->index > 1) || (f->pixel_format != V4L2_PIX_FMT_GREY) + || (f->width != sur40_video_format.width) + || (f->height != sur40_video_format.height)) + return -EINVAL; + + f->type = V4L2_FRMIVAL_TYPE_DISCRETE; + f->discrete.denominator = 60/(f->index+1); + f->discrete.numerator = 1; + return 0; +} + + static const struct usb_device_id sur40_table[] = { { USB_DEVICE(ID_MICROSOFT, ID_SUR40) }, /* Samsung SUR40 */ { } /* terminating null entry */ @@ -829,6 +856,9 @@ static const struct v4l2_ioctl_ops sur40_video_ioctl_ops = { .vidioc_s_fmt_vid_cap = sur40_vidioc_fmt, .vidioc_g_fmt_vid_cap = sur40_vidioc_fmt, + .vidioc_enum_framesizes = sur40_vidioc_enum_framesizes, + .vidioc_enum_frameintervals = sur40_vidioc_enum_frameintervals, + .vidioc_enum_input = sur40_vidioc_enum_input, .vidioc_g_input = sur40_vidioc_g_input, .vidioc_s_input = sur40_vidioc_s_input, -- cgit v1.2.3-59-g8ed1b From 0cfdfcc1db13c7b6b6f930ac0528d5b010f45220 Mon Sep 17 00:00:00 2001 From: Florian Echtler Date: Mon, 25 May 2015 09:04:15 -0300 Subject: [media] add extra debug output, remove noisy warning Add dev_dbg statements for easier future debugging; also change the warning about packet ID mismatches to debug output to avoid flooding the logs. This warning is only important in a very specific/rare use case when trying to correlate input events with video data. Signed-off-by: Martin Kaltenbrunner Signed-off-by: Florian Echtler Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/input/touchscreen/sur40.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index d860d056cccb..8add986abc82 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -342,7 +342,7 @@ static void sur40_poll(struct input_polled_dev *polldev) * instead of at the end. */ if (packet_id != header->packet_id) - dev_warn(sur40->dev, "packet ID mismatch\n"); + dev_dbg(sur40->dev, "packet ID mismatch\n"); packet_blobs = result / sizeof(struct sur40_blob); dev_dbg(sur40->dev, "received %d blobs\n", packet_blobs); @@ -389,6 +389,8 @@ static void sur40_process_video(struct sur40_state *sur40) list_del(&new_buf->list); spin_unlock(&sur40->qlock); + dev_dbg(sur40->dev, "buffer acquired\n"); + /* retrieve data via bulk read */ result = usb_bulk_msg(sur40->usbdev, usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT), @@ -416,6 +418,8 @@ static void sur40_process_video(struct sur40_state *sur40) goto err_poll; } + dev_dbg(sur40->dev, "header acquired\n"); + sgt = vb2_dma_sg_plane_desc(&new_buf->vb, 0); result = usb_sg_init(&sgr, sur40->usbdev, @@ -432,11 +436,14 @@ static void sur40_process_video(struct sur40_state *sur40) goto err_poll; } + dev_dbg(sur40->dev, "image acquired\n"); + /* mark as finished */ v4l2_get_timestamp(&new_buf->vb.v4l2_buf.timestamp); new_buf->vb.v4l2_buf.sequence = sur40->sequence++; new_buf->vb.v4l2_buf.field = V4L2_FIELD_NONE; vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_DONE); + dev_dbg(sur40->dev, "buffer marked done\n"); return; err_poll: -- cgit v1.2.3-59-g8ed1b From 2b7eea83a4e694018ddc155078bc36bf702ea466 Mon Sep 17 00:00:00 2001 From: Florian Echtler Date: Mon, 25 May 2015 09:04:16 -0300 Subject: [media] return BUF_STATE_ERROR if streaming stopped during acquisition When stop_streaming is called while a frame is currently being retrieved, the buffer being filled will still be returned with BUF_STATE_DONE. By resetting the sequence number and checking before returning the buffer, it can now correctly be returned with BUF_STATE_ERROR. Signed-off-by: Martin Kaltenbrunner Signed-off-by: Florian Echtler Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/input/touchscreen/sur40.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index 8add986abc82..8be7b9b79f20 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -438,6 +438,10 @@ static void sur40_process_video(struct sur40_state *sur40) dev_dbg(sur40->dev, "image acquired\n"); + /* return error if streaming was stopped in the meantime */ + if (sur40->sequence == -1) + goto err_poll; + /* mark as finished */ v4l2_get_timestamp(&new_buf->vb.v4l2_buf.timestamp); new_buf->vb.v4l2_buf.sequence = sur40->sequence++; @@ -723,6 +727,7 @@ static int sur40_start_streaming(struct vb2_queue *vq, unsigned int count) static void sur40_stop_streaming(struct vb2_queue *vq) { struct sur40_state *sur40 = vb2_get_drv_priv(vq); + sur40->sequence = -1; /* Release all active buffers */ return_all_buffers(sur40, VB2_BUF_STATE_ERROR); -- cgit v1.2.3-59-g8ed1b From 5f0ee9d17aae628b22be86966471db65be21f262 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 2 Jun 2015 10:40:50 -0700 Subject: Input: elantech - fix detection of touchpads where the revision matches a known rate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the check to skip the rate check more lax, so that it applies to all hw_version 4 models. This fixes the touchpad not being detected properly on Asus PU551LA laptops. Cc: stable@vger.kernel.org Reported-and-tested-by: David Zafra Gómez Signed-off-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elantech.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 79363b687195..f181d73d9629 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -1376,10 +1376,11 @@ static bool elantech_is_signature_valid(const unsigned char *param) return true; /* - * Some models have a revision higher then 20. Meaning param[2] may - * be 10 or 20, skip the rates check for these. + * Some hw_version >= 4 models have a revision higher then 20. Meaning + * that param[2] may be 10 or 20, skip the rates check for these. */ - if (param[0] == 0x46 && (param[1] & 0xef) == 0x0f && param[2] < 40) + if ((param[0] & 0x0f) >= 0x06 && (param[1] & 0xaf) == 0x0f && + param[2] < 40) return true; for (i = 0; i < ARRAY_SIZE(rates); i++) -- cgit v1.2.3-59-g8ed1b From 692dd1916436164e228608803dfb6cb768d6355a Mon Sep 17 00:00:00 2001 From: 洪一竹 Date: Thu, 4 Jun 2015 22:00:24 -0700 Subject: Input: elantech - add new icbody type This adds new icbody type to the list recognized by Elantech PS/2 driver. Cc: stable@vger.kernel.org Signed-off-by: Sam Hung Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elantech.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/input') diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index f181d73d9629..ce3d40004458 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -1556,6 +1556,7 @@ static int elantech_set_properties(struct elantech_data *etd) case 9: case 10: case 13: + case 14: etd->hw_version = 4; break; default: -- cgit v1.2.3-59-g8ed1b From 088df2ccef75754cc16a6ba31829d23bcb2b68ed Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 4 Jun 2015 22:31:43 -0700 Subject: Input: alps - do not reduce trackpoint speed by half On some v7 devices (e.g. Lenovo-E550) the deltas reported are typically only in the 0-1 range dividing this by 2 results in a range of 0-0. And even for v7 devices where this does not lead to making the trackstick entirely unusable, it makes it twice as slow as before we added v7 support and were using the ps/2 mouse emulation of the dual point setup. If some kind of generic slowdown is actually necessary for some devices, then that belongs in userspace, not in the kernel. Cc: stable@vger.kernel.org Reported-and-tested-by: Rico Moorman Signed-off-by: Hans de Goede Reviewed-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 7752bd59d4b7..a353b7de6d22 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -1063,9 +1063,8 @@ static void alps_process_trackstick_packet_v7(struct psmouse *psmouse) right = (packet[1] & 0x02) >> 1; middle = (packet[1] & 0x04) >> 2; - /* Divide 2 since trackpoint's speed is too fast */ - input_report_rel(dev2, REL_X, (char)x / 2); - input_report_rel(dev2, REL_Y, -((char)y / 2)); + input_report_rel(dev2, REL_X, (char)x); + input_report_rel(dev2, REL_Y, -((char)y)); input_report_key(dev2, BTN_LEFT, left); input_report_key(dev2, BTN_RIGHT, right); -- cgit v1.2.3-59-g8ed1b From 7f2ca8b55aeff1fe51ed3570200ef88a96060917 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 8 Jun 2015 10:17:32 -0700 Subject: Input: synaptics - add min/max quirk for Lenovo S540 https://bugzilla.redhat.com/show_bug.cgi?id=1223051#c2 Cc: stable@vger.kernel.org Tested-by: tommy.gagnes@gmail.com Signed-off-by: Peter Hutterer Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 630af73e98c4..35c8d0ceabee 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -150,6 +150,11 @@ static const struct min_max_quirk min_max_pnpid_table[] = { {ANY_BOARD_ID, 2961}, 1024, 5112, 2024, 4832 }, + { + (const char * const []){"LEN2000", NULL}, + {ANY_BOARD_ID, ANY_BOARD_ID}, + 1024, 5113, 2021, 4832 + }, { (const char * const []){"LEN2001", NULL}, {ANY_BOARD_ID, ANY_BOARD_ID}, @@ -191,7 +196,7 @@ static const char * const topbuttonpad_pnp_ids[] = { "LEN0045", "LEN0047", "LEN0049", - "LEN2000", + "LEN2000", /* S540 */ "LEN2001", /* Edge E431 */ "LEN2002", /* Edge E531 */ "LEN2003", -- cgit v1.2.3-59-g8ed1b From a841178445bb72a3d566b4e6ab9d19e9b002eb47 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 9 Jun 2015 13:04:42 +0200 Subject: mfd: cros_ec: Use a zero-length array for command data Commit 1b84f2a4cd4a ("mfd: cros_ec: Use fixed size arrays to transfer data with the EC") modified the struct cros_ec_command fields to not use pointers for the input and output buffers and use fixed length arrays instead. This change was made because the cros_ec ioctl API uses that struct cros_ec_command to allow user-space to send commands to the EC and to get data from the EC. So using pointers made the API not 64-bit safe. Unfortunately this approach was not flexible enough for all the use-cases since there may be a need to send larger commands on newer versions of the EC command protocol. So to avoid to choose a constant length that it may be too big for most commands and thus wasting memory and CPU cycles on copy from and to user-space or having a size that is too small for some big commands, use a zero-length array that is both 64-bit safe and flexible. The same buffer is used for both output and input data so the maximum of these values should be used to allocate it. Suggested-by: Gwendal Grignou Signed-off-by: Javier Martinez Canillas Tested-by: Heiko Stuebner Acked-by: Lee Jones Acked-by: Olof Johansson Signed-off-by: Lee Jones --- drivers/i2c/busses/i2c-cros-ec-tunnel.c | 45 ++++++--- drivers/input/keyboard/cros_ec_keyb.c | 29 ++++-- drivers/mfd/cros_ec.c | 28 ++++-- drivers/mfd/cros_ec_i2c.c | 4 +- drivers/mfd/cros_ec_spi.c | 2 +- drivers/platform/chrome/cros_ec_dev.c | 65 +++++++----- drivers/platform/chrome/cros_ec_lightbar.c | 152 +++++++++++++++++++--------- drivers/platform/chrome/cros_ec_lpc.c | 8 +- drivers/platform/chrome/cros_ec_sysfs.c | 154 ++++++++++++++++++----------- include/linux/mfd/cros_ec.h | 6 +- 10 files changed, 318 insertions(+), 175 deletions(-) (limited to 'drivers/input') diff --git a/drivers/i2c/busses/i2c-cros-ec-tunnel.c b/drivers/i2c/busses/i2c-cros-ec-tunnel.c index fa8dedd8c3a2..a0d95ff682ae 100644 --- a/drivers/i2c/busses/i2c-cros-ec-tunnel.c +++ b/drivers/i2c/busses/i2c-cros-ec-tunnel.c @@ -182,8 +182,9 @@ static int ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg i2c_msgs[], const u16 bus_num = bus->remote_bus; int request_len; int response_len; + int alloc_size; int result; - struct cros_ec_command msg = { }; + struct cros_ec_command *msg; request_len = ec_i2c_count_message(i2c_msgs, num); if (request_len < 0) { @@ -198,25 +199,39 @@ static int ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg i2c_msgs[], return response_len; } - result = ec_i2c_construct_message(msg.outdata, i2c_msgs, num, bus_num); - if (result) - return result; + alloc_size = max(request_len, response_len); + msg = kmalloc(sizeof(*msg) + alloc_size, GFP_KERNEL); + if (!msg) + return -ENOMEM; - msg.version = 0; - msg.command = EC_CMD_I2C_PASSTHRU; - msg.outsize = request_len; - msg.insize = response_len; + result = ec_i2c_construct_message(msg->data, i2c_msgs, num, bus_num); + if (result) { + dev_err(dev, "Error constructing EC i2c message %d\n", result); + goto exit; + } - result = cros_ec_cmd_xfer(bus->ec, &msg); - if (result < 0) - return result; + msg->version = 0; + msg->command = EC_CMD_I2C_PASSTHRU; + msg->outsize = request_len; + msg->insize = response_len; - result = ec_i2c_parse_response(msg.indata, i2c_msgs, &num); - if (result < 0) - return result; + result = cros_ec_cmd_xfer(bus->ec, msg); + if (result < 0) { + dev_err(dev, "Error transferring EC i2c message %d\n", result); + goto exit; + } + + result = ec_i2c_parse_response(msg->data, i2c_msgs, &num); + if (result < 0) { + dev_err(dev, "Error parsing EC i2c message %d\n", result); + goto exit; + } /* Indicate success by saying how many messages were sent */ - return num; + result = num; +exit: + kfree(msg); + return result; } static u32 ec_i2c_functionality(struct i2c_adapter *adap) diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index b50c5b8b8a4d..974154a74505 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -148,19 +148,28 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state) { - int ret; - struct cros_ec_command msg = { - .command = EC_CMD_MKBP_STATE, - .insize = ckdev->cols, - }; + int ret = 0; + struct cros_ec_command *msg; - ret = cros_ec_cmd_xfer(ckdev->ec, &msg); - if (ret < 0) - return ret; + msg = kmalloc(sizeof(*msg) + ckdev->cols, GFP_KERNEL); + if (!msg) + return -ENOMEM; - memcpy(kb_state, msg.indata, ckdev->cols); + msg->version = 0; + msg->command = EC_CMD_MKBP_STATE; + msg->insize = ckdev->cols; + msg->outsize = 0; - return 0; + ret = cros_ec_cmd_xfer(ckdev->ec, msg); + if (ret < 0) { + dev_err(ckdev->dev, "Error transferring EC message %d\n", ret); + goto exit; + } + + memcpy(kb_state, msg->data, ckdev->cols); +exit: + kfree(msg); + return ret; } static irqreturn_t cros_ec_keyb_irq(int irq, void *data) diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index 1574a9352a6d..4a0f6dfcd376 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -41,7 +41,7 @@ int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, out[2] = msg->outsize; csum = out[0] + out[1] + out[2]; for (i = 0; i < msg->outsize; i++) - csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->outdata[i]; + csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->data[i]; out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = (uint8_t)(csum & 0xff); return EC_MSG_TX_PROTO_BYTES + msg->outsize; @@ -75,11 +75,20 @@ int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev, ret = ec_dev->cmd_xfer(ec_dev, msg); if (msg->result == EC_RES_IN_PROGRESS) { int i; - struct cros_ec_command status_msg = { }; + struct cros_ec_command *status_msg; struct ec_response_get_comms_status *status; - status_msg.command = EC_CMD_GET_COMMS_STATUS; - status_msg.insize = sizeof(*status); + status_msg = kmalloc(sizeof(*status_msg) + sizeof(*status), + GFP_KERNEL); + if (!status_msg) { + ret = -ENOMEM; + goto exit; + } + + status_msg->version = 0; + status_msg->command = EC_CMD_GET_COMMS_STATUS; + status_msg->insize = sizeof(*status); + status_msg->outsize = 0; /* * Query the EC's status until it's no longer busy or @@ -88,20 +97,23 @@ int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev, for (i = 0; i < EC_COMMAND_RETRIES; i++) { usleep_range(10000, 11000); - ret = ec_dev->cmd_xfer(ec_dev, &status_msg); + ret = ec_dev->cmd_xfer(ec_dev, status_msg); if (ret < 0) break; - msg->result = status_msg.result; - if (status_msg.result != EC_RES_SUCCESS) + msg->result = status_msg->result; + if (status_msg->result != EC_RES_SUCCESS) break; status = (struct ec_response_get_comms_status *) - status_msg.indata; + status_msg->data; if (!(status->flags & EC_COMMS_STATUS_PROCESSING)) break; } + + kfree(status_msg); } +exit: mutex_unlock(&ec_dev->lock); return ret; diff --git a/drivers/mfd/cros_ec_i2c.c b/drivers/mfd/cros_ec_i2c.c index 82b4d6148698..fbf7819f5de5 100644 --- a/drivers/mfd/cros_ec_i2c.c +++ b/drivers/mfd/cros_ec_i2c.c @@ -76,7 +76,7 @@ static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev, /* copy message payload and compute checksum */ sum = out_buf[0] + out_buf[1] + out_buf[2]; for (i = 0; i < msg->outsize; i++) { - out_buf[3 + i] = msg->outdata[i]; + out_buf[3 + i] = msg->data[i]; sum += out_buf[3 + i]; } out_buf[3 + msg->outsize] = sum; @@ -109,7 +109,7 @@ static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev, /* copy response packet payload and compute checksum */ sum = in_buf[0] + in_buf[1]; for (i = 0; i < len; i++) { - msg->indata[i] = in_buf[2 + i]; + msg->data[i] = in_buf[2 + i]; sum += in_buf[2 + i]; } dev_dbg(ec_dev->dev, "packet: %*ph, sum = %02x\n", diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c index 27bd52e5e8b7..573730fec947 100644 --- a/drivers/mfd/cros_ec_spi.c +++ b/drivers/mfd/cros_ec_spi.c @@ -299,7 +299,7 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev, for (i = 0; i < len; i++) { sum += ptr[i + 2]; if (ec_msg->insize) - ec_msg->indata[i] = ptr[i + 2]; + ec_msg->data[i] = ptr[i + 2]; } sum &= 0xff; diff --git a/drivers/platform/chrome/cros_ec_dev.c b/drivers/platform/chrome/cros_ec_dev.c index 6090d0b2826f..e91ced1cb8ce 100644 --- a/drivers/platform/chrome/cros_ec_dev.c +++ b/drivers/platform/chrome/cros_ec_dev.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "cros_ec_dev.h" @@ -36,28 +37,31 @@ static int ec_get_version(struct cros_ec_device *ec, char *str, int maxlen) static const char * const current_image_name[] = { "unknown", "read-only", "read-write", "invalid", }; - struct cros_ec_command msg = { - .version = 0, - .command = EC_CMD_GET_VERSION, - .outdata = { 0 }, - .outsize = 0, - .indata = { 0 }, - .insize = sizeof(*resp), - }; + struct cros_ec_command *msg; int ret; - ret = cros_ec_cmd_xfer(ec, &msg); + msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->version = 0; + msg->command = EC_CMD_GET_VERSION; + msg->insize = sizeof(*resp); + msg->outsize = 0; + + ret = cros_ec_cmd_xfer(ec, msg); if (ret < 0) - return ret; + goto exit; - if (msg.result != EC_RES_SUCCESS) { + if (msg->result != EC_RES_SUCCESS) { snprintf(str, maxlen, "%s\nUnknown EC version: EC returned %d\n", - CROS_EC_DEV_VERSION, msg.result); - return 0; + CROS_EC_DEV_VERSION, msg->result); + ret = -EINVAL; + goto exit; } - resp = (struct ec_response_get_version *)msg.indata; + resp = (struct ec_response_get_version *)msg->data; if (resp->current_image >= ARRAY_SIZE(current_image_name)) resp->current_image = 3; /* invalid */ @@ -65,7 +69,10 @@ static int ec_get_version(struct cros_ec_device *ec, char *str, int maxlen) resp->version_string_ro, resp->version_string_rw, current_image_name[resp->current_image]); - return 0; + ret = 0; +exit: + kfree(msg); + return ret; } /* Device file ops */ @@ -110,20 +117,32 @@ static ssize_t ec_device_read(struct file *filp, char __user *buffer, static long ec_device_ioctl_xcmd(struct cros_ec_device *ec, void __user *arg) { long ret; - struct cros_ec_command s_cmd = { }; + struct cros_ec_command u_cmd; + struct cros_ec_command *s_cmd; - if (copy_from_user(&s_cmd, arg, sizeof(s_cmd))) + if (copy_from_user(&u_cmd, arg, sizeof(u_cmd))) return -EFAULT; - ret = cros_ec_cmd_xfer(ec, &s_cmd); + s_cmd = kmalloc(sizeof(*s_cmd) + max(u_cmd.outsize, u_cmd.insize), + GFP_KERNEL); + if (!s_cmd) + return -ENOMEM; + + if (copy_from_user(s_cmd, arg, sizeof(*s_cmd) + u_cmd.outsize)) { + ret = -EFAULT; + goto exit; + } + + ret = cros_ec_cmd_xfer(ec, s_cmd); /* Only copy data to userland if data was received. */ if (ret < 0) - return ret; + goto exit; - if (copy_to_user(arg, &s_cmd, sizeof(s_cmd))) - return -EFAULT; - - return 0; + if (copy_to_user(arg, s_cmd, sizeof(*s_cmd) + u_cmd.insize)) + ret = -EFAULT; +exit: + kfree(s_cmd); + return ret; } static long ec_device_ioctl_readmem(struct cros_ec_device *ec, void __user *arg) diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c index b4ff47a9069a..560e5d41b7ae 100644 --- a/drivers/platform/chrome/cros_ec_lightbar.c +++ b/drivers/platform/chrome/cros_ec_lightbar.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "cros_ec_dev.h" @@ -91,54 +92,79 @@ out: return ret; } -#define INIT_MSG(P, R) { \ - .command = EC_CMD_LIGHTBAR_CMD, \ - .outsize = sizeof(*P), \ - .insize = sizeof(*R), \ - } +static struct cros_ec_command *alloc_lightbar_cmd_msg(void) +{ + struct cros_ec_command *msg; + int len; + + len = max(sizeof(struct ec_params_lightbar), + sizeof(struct ec_response_lightbar)); + + msg = kmalloc(sizeof(*msg) + len, GFP_KERNEL); + if (!msg) + return NULL; + + msg->version = 0; + msg->command = EC_CMD_LIGHTBAR_CMD; + msg->outsize = sizeof(struct ec_params_lightbar); + msg->insize = sizeof(struct ec_response_lightbar); + + return msg; +} static int get_lightbar_version(struct cros_ec_device *ec, uint32_t *ver_ptr, uint32_t *flg_ptr) { struct ec_params_lightbar *param; struct ec_response_lightbar *resp; - struct cros_ec_command msg = INIT_MSG(param, resp); + struct cros_ec_command *msg; int ret; - param = (struct ec_params_lightbar *)msg.outdata; - param->cmd = LIGHTBAR_CMD_VERSION; - ret = cros_ec_cmd_xfer(ec, &msg); - if (ret < 0) + msg = alloc_lightbar_cmd_msg(); + if (!msg) return 0; - switch (msg.result) { + param = (struct ec_params_lightbar *)msg->data; + param->cmd = LIGHTBAR_CMD_VERSION; + ret = cros_ec_cmd_xfer(ec, msg); + if (ret < 0) { + ret = 0; + goto exit; + } + + switch (msg->result) { case EC_RES_INVALID_PARAM: /* Pixel had no version command. */ if (ver_ptr) *ver_ptr = 0; if (flg_ptr) *flg_ptr = 0; - return 1; + ret = 1; + goto exit; case EC_RES_SUCCESS: - resp = (struct ec_response_lightbar *)msg.indata; + resp = (struct ec_response_lightbar *)msg->data; /* Future devices w/lightbars should implement this command */ if (ver_ptr) *ver_ptr = resp->version.num; if (flg_ptr) *flg_ptr = resp->version.flags; - return 1; + ret = 1; + goto exit; } /* Anything else (ie, EC_RES_INVALID_COMMAND) - no lightbar */ - return 0; + ret = 0; +exit: + kfree(msg); + return ret; } static ssize_t version_show(struct device *dev, struct device_attribute *attr, char *buf) { - uint32_t version, flags; + uint32_t version = 0, flags = 0; struct cros_ec_device *ec = dev_get_drvdata(dev); int ret; @@ -158,8 +184,7 @@ static ssize_t brightness_store(struct device *dev, const char *buf, size_t count) { struct ec_params_lightbar *param; - struct ec_response_lightbar *resp; - struct cros_ec_command msg = INIT_MSG(param, resp); + struct cros_ec_command *msg; int ret; unsigned int val; struct cros_ec_device *ec = dev_get_drvdata(dev); @@ -167,21 +192,30 @@ static ssize_t brightness_store(struct device *dev, if (kstrtouint(buf, 0, &val)) return -EINVAL; - param = (struct ec_params_lightbar *)msg.outdata; + msg = alloc_lightbar_cmd_msg(); + if (!msg) + return -ENOMEM; + + param = (struct ec_params_lightbar *)msg->data; param->cmd = LIGHTBAR_CMD_BRIGHTNESS; param->brightness.num = val; ret = lb_throttle(); if (ret) - return ret; + goto exit; - ret = cros_ec_cmd_xfer(ec, &msg); + ret = cros_ec_cmd_xfer(ec, msg); if (ret < 0) - return ret; + goto exit; - if (msg.result != EC_RES_SUCCESS) - return -EINVAL; + if (msg->result != EC_RES_SUCCESS) { + ret = -EINVAL; + goto exit; + } - return count; + ret = count; +exit: + kfree(msg); + return ret; } @@ -196,12 +230,15 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ec_params_lightbar *param; - struct ec_response_lightbar *resp; - struct cros_ec_command msg = INIT_MSG(param, resp); + struct cros_ec_command *msg; struct cros_ec_device *ec = dev_get_drvdata(dev); unsigned int val[4]; int ret, i = 0, j = 0, ok = 0; + msg = alloc_lightbar_cmd_msg(); + if (!msg) + return -ENOMEM; + do { /* Skip any whitespace */ while (*buf && isspace(*buf)) @@ -215,7 +252,7 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr, return -EINVAL; if (i == 4) { - param = (struct ec_params_lightbar *)msg.outdata; + param = (struct ec_params_lightbar *)msg->data; param->cmd = LIGHTBAR_CMD_RGB; param->rgb.led = val[0]; param->rgb.red = val[1]; @@ -231,12 +268,14 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr, return ret; } - ret = cros_ec_cmd_xfer(ec, &msg); + ret = cros_ec_cmd_xfer(ec, msg); if (ret < 0) - return ret; + goto exit; - if (msg.result != EC_RES_SUCCESS) - return -EINVAL; + if (msg->result != EC_RES_SUCCESS) { + ret = -EINVAL; + goto exit; + } i = 0; ok = 1; @@ -248,6 +287,8 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr, } while (*buf); +exit: + kfree(msg); return (ok && i == 0) ? count : -EINVAL; } @@ -261,42 +302,55 @@ static ssize_t sequence_show(struct device *dev, { struct ec_params_lightbar *param; struct ec_response_lightbar *resp; - struct cros_ec_command msg = INIT_MSG(param, resp); + struct cros_ec_command *msg; int ret; struct cros_ec_device *ec = dev_get_drvdata(dev); - param = (struct ec_params_lightbar *)msg.outdata; + msg = alloc_lightbar_cmd_msg(); + if (!msg) + return -ENOMEM; + + param = (struct ec_params_lightbar *)msg->data; param->cmd = LIGHTBAR_CMD_GET_SEQ; ret = lb_throttle(); if (ret) - return ret; + goto exit; - ret = cros_ec_cmd_xfer(ec, &msg); + ret = cros_ec_cmd_xfer(ec, msg); if (ret < 0) - return ret; + goto exit; - if (msg.result != EC_RES_SUCCESS) - return scnprintf(buf, PAGE_SIZE, - "ERROR: EC returned %d\n", msg.result); + if (msg->result != EC_RES_SUCCESS) { + ret = scnprintf(buf, PAGE_SIZE, + "ERROR: EC returned %d\n", msg->result); + goto exit; + } - resp = (struct ec_response_lightbar *)msg.indata; + resp = (struct ec_response_lightbar *)msg->data; if (resp->get_seq.num >= ARRAY_SIZE(seqname)) - return scnprintf(buf, PAGE_SIZE, "%d\n", resp->get_seq.num); + ret = scnprintf(buf, PAGE_SIZE, "%d\n", resp->get_seq.num); else - return scnprintf(buf, PAGE_SIZE, "%s\n", - seqname[resp->get_seq.num]); + ret = scnprintf(buf, PAGE_SIZE, "%s\n", + seqname[resp->get_seq.num]); + +exit: + kfree(msg); + return ret; } static ssize_t sequence_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ec_params_lightbar *param; - struct ec_response_lightbar *resp; - struct cros_ec_command msg = INIT_MSG(param, resp); + struct cros_ec_command *msg; unsigned int num; int ret, len; struct cros_ec_device *ec = dev_get_drvdata(dev); + msg = alloc_lightbar_cmd_msg(); + if (!msg) + return -ENOMEM; + for (len = 0; len < count; len++) if (!isalnum(buf[len])) break; @@ -311,18 +365,18 @@ static ssize_t sequence_store(struct device *dev, struct device_attribute *attr, return ret; } - param = (struct ec_params_lightbar *)msg.outdata; + param = (struct ec_params_lightbar *)msg->data; param->cmd = LIGHTBAR_CMD_SEQ; param->seq.num = num; ret = lb_throttle(); if (ret) return ret; - ret = cros_ec_cmd_xfer(ec, &msg); + ret = cros_ec_cmd_xfer(ec, msg); if (ret < 0) return ret; - if (msg.result != EC_RES_SUCCESS) + if (msg->result != EC_RES_SUCCESS) return -EINVAL; return count; diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c index 860310513cf0..4c7f0df33bf8 100644 --- a/drivers/platform/chrome/cros_ec_lpc.c +++ b/drivers/platform/chrome/cros_ec_lpc.c @@ -73,8 +73,8 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, /* Copy data and update checksum */ for (i = 0; i < msg->outsize; i++) { - outb(msg->outdata[i], EC_LPC_ADDR_HOST_PARAM + i); - csum += msg->outdata[i]; + outb(msg->data[i], EC_LPC_ADDR_HOST_PARAM + i); + csum += msg->data[i]; } /* Finalize checksum and write args */ @@ -129,8 +129,8 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, /* Read response and update checksum */ for (i = 0; i < args.data_size; i++) { - msg->indata[i] = inb(EC_LPC_ADDR_HOST_PARAM + i); - csum += msg->indata[i]; + msg->data[i] = inb(EC_LPC_ADDR_HOST_PARAM + i); + csum += msg->data[i]; } /* Verify checksum */ diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c index fb62ab6cc659..78ba82d670cb 100644 --- a/drivers/platform/chrome/cros_ec_sysfs.c +++ b/drivers/platform/chrome/cros_ec_sysfs.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -66,14 +67,19 @@ static ssize_t store_ec_reboot(struct device *dev, {"hibernate", EC_REBOOT_HIBERNATE, 0}, {"at-shutdown", -1, EC_REBOOT_FLAG_ON_AP_SHUTDOWN}, }; - struct cros_ec_command msg = { 0 }; - struct ec_params_reboot_ec *param = - (struct ec_params_reboot_ec *)msg.outdata; + struct cros_ec_command *msg; + struct ec_params_reboot_ec *param; int got_cmd = 0, offset = 0; int i; int ret; struct cros_ec_device *ec = dev_get_drvdata(dev); + msg = kmalloc(sizeof(*msg) + sizeof(*param), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + param = (struct ec_params_reboot_ec *)msg->data; + param->flags = 0; while (1) { /* Find word to start scanning */ @@ -100,19 +106,26 @@ static ssize_t store_ec_reboot(struct device *dev, offset++; } - if (!got_cmd) - return -EINVAL; - - msg.command = EC_CMD_REBOOT_EC; - msg.outsize = sizeof(param); - ret = cros_ec_cmd_xfer(ec, &msg); - if (ret < 0) - return ret; - if (msg.result != EC_RES_SUCCESS) { - dev_dbg(ec->dev, "EC result %d\n", msg.result); - return -EINVAL; + if (!got_cmd) { + count = -EINVAL; + goto exit; } + msg->version = 0; + msg->command = EC_CMD_REBOOT_EC; + msg->outsize = sizeof(*param); + msg->insize = 0; + ret = cros_ec_cmd_xfer(ec, msg); + if (ret < 0) { + count = ret; + goto exit; + } + if (msg->result != EC_RES_SUCCESS) { + dev_dbg(ec->dev, "EC result %d\n", msg->result); + count = -EINVAL; + } +exit: + kfree(msg); return count; } @@ -123,22 +136,32 @@ static ssize_t show_ec_version(struct device *dev, struct ec_response_get_version *r_ver; struct ec_response_get_chip_info *r_chip; struct ec_response_board_version *r_board; - struct cros_ec_command msg = { 0 }; + struct cros_ec_command *msg; int ret; int count = 0; struct cros_ec_device *ec = dev_get_drvdata(dev); + msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + /* Get versions. RW may change. */ - msg.command = EC_CMD_GET_VERSION; - msg.insize = sizeof(*r_ver); - ret = cros_ec_cmd_xfer(ec, &msg); - if (ret < 0) - return ret; - if (msg.result != EC_RES_SUCCESS) - return scnprintf(buf, PAGE_SIZE, - "ERROR: EC returned %d\n", msg.result); + msg->version = 0; + msg->command = EC_CMD_GET_VERSION; + msg->insize = sizeof(*r_ver); + msg->outsize = 0; + ret = cros_ec_cmd_xfer(ec, msg); + if (ret < 0) { + count = ret; + goto exit; + } + if (msg->result != EC_RES_SUCCESS) { + count = scnprintf(buf, PAGE_SIZE, + "ERROR: EC returned %d\n", msg->result); + goto exit; + } - r_ver = (struct ec_response_get_version *)msg.indata; + r_ver = (struct ec_response_get_version *)msg->data; /* Strings should be null-terminated, but let's be sure. */ r_ver->version_string_ro[sizeof(r_ver->version_string_ro) - 1] = '\0'; r_ver->version_string_rw[sizeof(r_ver->version_string_rw) - 1] = '\0'; @@ -152,33 +175,33 @@ static ssize_t show_ec_version(struct device *dev, image_names[r_ver->current_image] : "?")); /* Get build info. */ - msg.command = EC_CMD_GET_BUILD_INFO; - msg.insize = sizeof(msg.indata); - ret = cros_ec_cmd_xfer(ec, &msg); + msg->command = EC_CMD_GET_BUILD_INFO; + msg->insize = EC_HOST_PARAM_SIZE; + ret = cros_ec_cmd_xfer(ec, msg); if (ret < 0) count += scnprintf(buf + count, PAGE_SIZE - count, "Build info: XFER ERROR %d\n", ret); - else if (msg.result != EC_RES_SUCCESS) + else if (msg->result != EC_RES_SUCCESS) count += scnprintf(buf + count, PAGE_SIZE - count, - "Build info: EC error %d\n", msg.result); + "Build info: EC error %d\n", msg->result); else { - msg.indata[sizeof(msg.indata) - 1] = '\0'; + msg->data[sizeof(msg->data) - 1] = '\0'; count += scnprintf(buf + count, PAGE_SIZE - count, - "Build info: %s\n", msg.indata); + "Build info: %s\n", msg->data); } /* Get chip info. */ - msg.command = EC_CMD_GET_CHIP_INFO; - msg.insize = sizeof(*r_chip); - ret = cros_ec_cmd_xfer(ec, &msg); + msg->command = EC_CMD_GET_CHIP_INFO; + msg->insize = sizeof(*r_chip); + ret = cros_ec_cmd_xfer(ec, msg); if (ret < 0) count += scnprintf(buf + count, PAGE_SIZE - count, "Chip info: XFER ERROR %d\n", ret); - else if (msg.result != EC_RES_SUCCESS) + else if (msg->result != EC_RES_SUCCESS) count += scnprintf(buf + count, PAGE_SIZE - count, - "Chip info: EC error %d\n", msg.result); + "Chip info: EC error %d\n", msg->result); else { - r_chip = (struct ec_response_get_chip_info *)msg.indata; + r_chip = (struct ec_response_get_chip_info *)msg->data; r_chip->vendor[sizeof(r_chip->vendor) - 1] = '\0'; r_chip->name[sizeof(r_chip->name) - 1] = '\0'; @@ -192,23 +215,25 @@ static ssize_t show_ec_version(struct device *dev, } /* Get board version */ - msg.command = EC_CMD_GET_BOARD_VERSION; - msg.insize = sizeof(*r_board); - ret = cros_ec_cmd_xfer(ec, &msg); + msg->command = EC_CMD_GET_BOARD_VERSION; + msg->insize = sizeof(*r_board); + ret = cros_ec_cmd_xfer(ec, msg); if (ret < 0) count += scnprintf(buf + count, PAGE_SIZE - count, "Board version: XFER ERROR %d\n", ret); - else if (msg.result != EC_RES_SUCCESS) + else if (msg->result != EC_RES_SUCCESS) count += scnprintf(buf + count, PAGE_SIZE - count, - "Board version: EC error %d\n", msg.result); + "Board version: EC error %d\n", msg->result); else { - r_board = (struct ec_response_board_version *)msg.indata; + r_board = (struct ec_response_board_version *)msg->data; count += scnprintf(buf + count, PAGE_SIZE - count, "Board version: %d\n", r_board->board_version); } +exit: + kfree(msg); return count; } @@ -216,27 +241,38 @@ static ssize_t show_ec_flashinfo(struct device *dev, struct device_attribute *attr, char *buf) { struct ec_response_flash_info *resp; - struct cros_ec_command msg = { 0 }; + struct cros_ec_command *msg; int ret; struct cros_ec_device *ec = dev_get_drvdata(dev); + msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL); + if (!msg) + return -ENOMEM; + /* The flash info shouldn't ever change, but ask each time anyway. */ - msg.command = EC_CMD_FLASH_INFO; - msg.insize = sizeof(*resp); - ret = cros_ec_cmd_xfer(ec, &msg); + msg->version = 0; + msg->command = EC_CMD_FLASH_INFO; + msg->insize = sizeof(*resp); + msg->outsize = 0; + ret = cros_ec_cmd_xfer(ec, msg); if (ret < 0) - return ret; - if (msg.result != EC_RES_SUCCESS) - return scnprintf(buf, PAGE_SIZE, - "ERROR: EC returned %d\n", msg.result); - - resp = (struct ec_response_flash_info *)msg.indata; - - return scnprintf(buf, PAGE_SIZE, - "FlashSize %d\nWriteSize %d\n" - "EraseSize %d\nProtectSize %d\n", - resp->flash_size, resp->write_block_size, - resp->erase_block_size, resp->protect_block_size); + goto exit; + if (msg->result != EC_RES_SUCCESS) { + ret = scnprintf(buf, PAGE_SIZE, + "ERROR: EC returned %d\n", msg->result); + goto exit; + } + + resp = (struct ec_response_flash_info *)msg->data; + + ret = scnprintf(buf, PAGE_SIZE, + "FlashSize %d\nWriteSize %d\n" + "EraseSize %d\nProtectSize %d\n", + resp->flash_size, resp->write_block_size, + resp->erase_block_size, resp->protect_block_size); +exit: + kfree(msg); + return ret; } /* Module initialization */ diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index 14cf522123dd..7eee38abd02a 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -42,8 +42,7 @@ enum { * @outsize: Outgoing length in bytes * @insize: Max number of bytes to accept from EC * @result: EC's response to the command (separate from communication failure) - * @outdata: Outgoing data to EC - * @indata: Where to put the incoming data from EC + * @data: Where to put the incoming data from EC and outgoing data to EC */ struct cros_ec_command { uint32_t version; @@ -51,8 +50,7 @@ struct cros_ec_command { uint32_t outsize; uint32_t insize; uint32_t result; - uint8_t outdata[EC_PROTO2_MAX_PARAM_SIZE]; - uint8_t indata[EC_PROTO2_MAX_PARAM_SIZE]; + uint8_t data[0]; }; /** -- cgit v1.2.3-59-g8ed1b From 062476f24aa7cf714169342cc50626fd9bbb93da Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 9 Jun 2015 13:04:44 +0200 Subject: mfd: cros_ec: Move protocol helpers out of the MFD driver The MFD driver should only have the logic to instantiate its child devices and setup any shared resources that will be used by the subdevices drivers. The cros_ec MFD is more complex than expected since it also has helpers to communicate with the EC. So the driver will only get more bigger as other protocols are supported in the future. So move the communication protocol helpers to its own driver as drivers/platform/chrome/cros_ec_proto.c. Suggested-by: Lee Jones Signed-off-by: Javier Martinez Canillas Tested-by: Heiko Stuebner Acked-by: Lee Jones Acked-by: Olof Johansson Signed-off-by: Lee Jones --- drivers/i2c/busses/Kconfig | 2 +- drivers/input/keyboard/Kconfig | 2 +- drivers/mfd/Kconfig | 6 +- drivers/mfd/cros_ec.c | 96 -------------------------- drivers/platform/chrome/Kconfig | 9 ++- drivers/platform/chrome/Makefile | 1 + drivers/platform/chrome/cros_ec_proto.c | 115 ++++++++++++++++++++++++++++++++ 7 files changed, 129 insertions(+), 102 deletions(-) create mode 100644 drivers/platform/chrome/cros_ec_proto.c (limited to 'drivers/input') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 2255af23b9c7..5f1c1c4f5d87 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -1103,7 +1103,7 @@ config I2C_SIBYTE config I2C_CROS_EC_TUNNEL tristate "ChromeOS EC tunnel I2C bus" - depends on MFD_CROS_EC + depends on CROS_EC_PROTO help If you say yes here you get an I2C bus that will tunnel i2c commands through to the other side of the ChromeOS EC to the i2c bus diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 106fbac7f8c5..e8eb60c6d83e 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -677,7 +677,7 @@ config KEYBOARD_W90P910 config KEYBOARD_CROS_EC tristate "ChromeOS EC keyboard" select INPUT_MATRIXKMAP - depends on MFD_CROS_EC + depends on CROS_EC_PROTO help Say Y here to enable the matrix keyboard used by ChromeOS devices and implemented on the ChromeOS EC. You must enable one bus option diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index d5ad04dad081..cf3b86d441f7 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -94,6 +94,8 @@ config MFD_AXP20X config MFD_CROS_EC tristate "ChromeOS Embedded Controller" select MFD_CORE + select CHROME_PLATFORMS + select CROS_EC_PROTO help If you say Y here you get support for the ChromeOS Embedded Controller (EC) providing keyboard, battery and power services. @@ -102,7 +104,7 @@ config MFD_CROS_EC config MFD_CROS_EC_I2C tristate "ChromeOS Embedded Controller (I2C)" - depends on MFD_CROS_EC && I2C + depends on MFD_CROS_EC && CROS_EC_PROTO && I2C help If you say Y here, you get support for talking to the ChromeOS @@ -112,7 +114,7 @@ config MFD_CROS_EC_I2C config MFD_CROS_EC_SPI tristate "ChromeOS Embedded Controller (SPI)" - depends on MFD_CROS_EC && SPI && OF + depends on MFD_CROS_EC && CROS_EC_PROTO && SPI && OF ---help--- If you say Y here, you get support for talking to the ChromeOS EC diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index 4a0f6dfcd376..d857f6a2b57b 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -23,102 +23,6 @@ #include #include #include -#include -#include - -#define EC_COMMAND_RETRIES 50 - -int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, - struct cros_ec_command *msg) -{ - uint8_t *out; - int csum, i; - - BUG_ON(msg->outsize > EC_PROTO2_MAX_PARAM_SIZE); - out = ec_dev->dout; - out[0] = EC_CMD_VERSION0 + msg->version; - out[1] = msg->command; - out[2] = msg->outsize; - csum = out[0] + out[1] + out[2]; - for (i = 0; i < msg->outsize; i++) - csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->data[i]; - out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = (uint8_t)(csum & 0xff); - - return EC_MSG_TX_PROTO_BYTES + msg->outsize; -} -EXPORT_SYMBOL(cros_ec_prepare_tx); - -int cros_ec_check_result(struct cros_ec_device *ec_dev, - struct cros_ec_command *msg) -{ - switch (msg->result) { - case EC_RES_SUCCESS: - return 0; - case EC_RES_IN_PROGRESS: - dev_dbg(ec_dev->dev, "command 0x%02x in progress\n", - msg->command); - return -EAGAIN; - default: - dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n", - msg->command, msg->result); - return 0; - } -} -EXPORT_SYMBOL(cros_ec_check_result); - -int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev, - struct cros_ec_command *msg) -{ - int ret; - - mutex_lock(&ec_dev->lock); - ret = ec_dev->cmd_xfer(ec_dev, msg); - if (msg->result == EC_RES_IN_PROGRESS) { - int i; - struct cros_ec_command *status_msg; - struct ec_response_get_comms_status *status; - - status_msg = kmalloc(sizeof(*status_msg) + sizeof(*status), - GFP_KERNEL); - if (!status_msg) { - ret = -ENOMEM; - goto exit; - } - - status_msg->version = 0; - status_msg->command = EC_CMD_GET_COMMS_STATUS; - status_msg->insize = sizeof(*status); - status_msg->outsize = 0; - - /* - * Query the EC's status until it's no longer busy or - * we encounter an error. - */ - for (i = 0; i < EC_COMMAND_RETRIES; i++) { - usleep_range(10000, 11000); - - ret = ec_dev->cmd_xfer(ec_dev, status_msg); - if (ret < 0) - break; - - msg->result = status_msg->result; - if (status_msg->result != EC_RES_SUCCESS) - break; - - status = (struct ec_response_get_comms_status *) - status_msg->data; - if (!(status->flags & EC_COMMS_STATUS_PROCESSING)) - break; - } - - kfree(status_msg); - } -exit: - mutex_unlock(&ec_dev->lock); - - return ret; -} -EXPORT_SYMBOL(cros_ec_cmd_xfer); static const struct mfd_cell cros_devs[] = { { diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index 2a6531a5fde8..cb1329919527 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -40,7 +40,7 @@ config CHROMEOS_PSTORE config CROS_EC_CHARDEV tristate "Chrome OS Embedded Controller userspace device interface" - depends on MFD_CROS_EC + depends on CROS_EC_PROTO ---help--- This driver adds support to talk with the ChromeOS EC from userspace. @@ -49,7 +49,7 @@ config CROS_EC_CHARDEV config CROS_EC_LPC tristate "ChromeOS Embedded Controller (LPC)" - depends on MFD_CROS_EC && (X86 || COMPILE_TEST) + depends on MFD_CROS_EC && CROS_EC_PROTO && (X86 || COMPILE_TEST) help If you say Y here, you get support for talking to the ChromeOS EC over an LPC bus. This uses a simple byte-level protocol with a @@ -59,4 +59,9 @@ config CROS_EC_LPC To compile this driver as a module, choose M here: the module will be called cros_ec_lpc. +config CROS_EC_PROTO + bool + help + ChromeOS EC communication protocol helpers. + endif # CHROMEOS_PLATFORMS diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index bd8d8601e875..4a11b010f5d8 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o cros_ec_devs-objs := cros_ec_dev.o cros_ec_sysfs.o cros_ec_lightbar.o obj-$(CONFIG_CROS_EC_CHARDEV) += cros_ec_devs.o obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpc.o +obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c new file mode 100644 index 000000000000..58e98a24fd08 --- /dev/null +++ b/drivers/platform/chrome/cros_ec_proto.c @@ -0,0 +1,115 @@ +/* + * ChromeOS EC communication protocol helper functions + * + * Copyright (C) 2015 Google, Inc + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +#define EC_COMMAND_RETRIES 50 + +int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, + struct cros_ec_command *msg) +{ + uint8_t *out; + int csum, i; + + BUG_ON(msg->outsize > EC_PROTO2_MAX_PARAM_SIZE); + out = ec_dev->dout; + out[0] = EC_CMD_VERSION0 + msg->version; + out[1] = msg->command; + out[2] = msg->outsize; + csum = out[0] + out[1] + out[2]; + for (i = 0; i < msg->outsize; i++) + csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->data[i]; + out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = (uint8_t)(csum & 0xff); + + return EC_MSG_TX_PROTO_BYTES + msg->outsize; +} +EXPORT_SYMBOL(cros_ec_prepare_tx); + +int cros_ec_check_result(struct cros_ec_device *ec_dev, + struct cros_ec_command *msg) +{ + switch (msg->result) { + case EC_RES_SUCCESS: + return 0; + case EC_RES_IN_PROGRESS: + dev_dbg(ec_dev->dev, "command 0x%02x in progress\n", + msg->command); + return -EAGAIN; + default: + dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n", + msg->command, msg->result); + return 0; + } +} +EXPORT_SYMBOL(cros_ec_check_result); + +int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev, + struct cros_ec_command *msg) +{ + int ret; + + mutex_lock(&ec_dev->lock); + ret = ec_dev->cmd_xfer(ec_dev, msg); + if (msg->result == EC_RES_IN_PROGRESS) { + int i; + struct cros_ec_command *status_msg; + struct ec_response_get_comms_status *status; + + status_msg = kmalloc(sizeof(*status_msg) + sizeof(*status), + GFP_KERNEL); + if (!status_msg) { + ret = -ENOMEM; + goto exit; + } + + status_msg->version = 0; + status_msg->command = EC_CMD_GET_COMMS_STATUS; + status_msg->insize = sizeof(*status); + status_msg->outsize = 0; + + /* + * Query the EC's status until it's no longer busy or + * we encounter an error. + */ + for (i = 0; i < EC_COMMAND_RETRIES; i++) { + usleep_range(10000, 11000); + + ret = ec_dev->cmd_xfer(ec_dev, status_msg); + if (ret < 0) + break; + + msg->result = status_msg->result; + if (status_msg->result != EC_RES_SUCCESS) + break; + + status = (struct ec_response_get_comms_status *) + status_msg->data; + if (!(status->flags & EC_COMMS_STATUS_PROCESSING)) + break; + } + + kfree(status_msg); + } +exit: + mutex_unlock(&ec_dev->lock); + + return ret; +} +EXPORT_SYMBOL(cros_ec_cmd_xfer); -- cgit v1.2.3-59-g8ed1b From 57b33ff077beebb68481a2b6b8e5fe58ca998169 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Tue, 9 Jun 2015 13:04:47 +0200 Subject: mfd: cros_ec: Support multiple EC in a system Chromebooks can have more than one Embedded Controller so the cros_ec device id has to be incremented for each EC registered. Add a new structure to represent multiple EC as different char devices (e.g: /dev/cros_ec, /dev/cros_pd). It connects to cros_ec_device and allows sysfs inferface for cros_pd. Also reduce number of allocated objects, make chromeos sysfs class object a static and add refcounting to prevent object deletion while command is in progress. Signed-off-by: Gwendal Grignou Reviewed-by: Dmitry Torokhov Signed-off-by: Javier Martinez Canillas Tested-by: Heiko Stuebner Acked-by: Lee Jones Acked-by: Olof Johansson Signed-off-by: Lee Jones --- drivers/input/keyboard/cros_ec_keyb.c | 2 +- drivers/mfd/cros_ec.c | 54 ++++++++++-- drivers/mfd/cros_ec_i2c.c | 1 - drivers/mfd/cros_ec_spi.c | 1 - drivers/platform/chrome/cros_ec_dev.c | 130 ++++++++++++++++++++--------- drivers/platform/chrome/cros_ec_dev.h | 7 -- drivers/platform/chrome/cros_ec_lightbar.c | 75 +++++++++-------- drivers/platform/chrome/cros_ec_lpc.c | 1 - drivers/platform/chrome/cros_ec_sysfs.c | 48 +++++------ include/linux/mfd/cros_ec.h | 44 ++++++++-- 10 files changed, 236 insertions(+), 127 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index 974154a74505..b01966dc7eb3 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -275,7 +275,7 @@ static int cros_ec_keyb_probe(struct platform_device *pdev) ckdev->dev = dev; dev_set_drvdata(&pdev->dev, ckdev); - idev->name = ec->ec_name; + idev->name = CROS_EC_DEV_NAME; idev->phys = ec->phys_name; __set_bit(EV_REP, idev->evbit); diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index 08d82bfc5268..11b1884bce62 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -24,11 +24,29 @@ #include #include -static const struct mfd_cell cros_devs[] = { - { - .name = "cros-ec-ctl", - .id = PLATFORM_DEVID_AUTO, - }, +#define CROS_EC_DEV_EC_INDEX 0 +#define CROS_EC_DEV_PD_INDEX 1 + +struct cros_ec_platform ec_p = { + .ec_name = CROS_EC_DEV_NAME, + .cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_EC_INDEX), +}; + +struct cros_ec_platform pd_p = { + .ec_name = CROS_EC_DEV_PD_NAME, + .cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX), +}; + +struct mfd_cell ec_cell = { + .name = "cros-ec-ctl", + .platform_data = &ec_p, + .pdata_size = sizeof(ec_p), +}; + +struct mfd_cell ec_pd_cell = { + .name = "cros-ec-ctl", + .platform_data = &pd_p, + .pdata_size = sizeof(pd_p), }; int cros_ec_register(struct cros_ec_device *ec_dev) @@ -52,14 +70,34 @@ int cros_ec_register(struct cros_ec_device *ec_dev) cros_ec_query_all(ec_dev); - err = mfd_add_devices(dev, 0, cros_devs, - ARRAY_SIZE(cros_devs), + err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, &ec_cell, 1, NULL, ec_dev->irq, NULL); if (err) { - dev_err(dev, "failed to add mfd devices\n"); + dev_err(dev, + "Failed to register Embedded Controller subdevice %d\n", + err); return err; } + if (ec_dev->max_passthru) { + /* + * Register a PD device as well on top of this device. + * We make the following assumptions: + * - behind an EC, we have a pd + * - only one device added. + * - the EC is responsive at init time (it is not true for a + * sensor hub. + */ + err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, + &ec_pd_cell, 1, NULL, ec_dev->irq, NULL); + if (err) { + dev_err(dev, + "Failed to register Power Delivery subdevice %d\n", + err); + return err; + } + } + if (IS_ENABLED(CONFIG_OF) && dev->of_node) { err = of_platform_populate(dev->of_node, NULL, NULL, dev); if (err) { diff --git a/drivers/mfd/cros_ec_i2c.c b/drivers/mfd/cros_ec_i2c.c index 22e8a4ae1711..b9a0963ca5c3 100644 --- a/drivers/mfd/cros_ec_i2c.c +++ b/drivers/mfd/cros_ec_i2c.c @@ -302,7 +302,6 @@ static int cros_ec_i2c_probe(struct i2c_client *client, ec_dev->irq = client->irq; ec_dev->cmd_xfer = cros_ec_cmd_xfer_i2c; ec_dev->pkt_xfer = cros_ec_pkt_xfer_i2c; - ec_dev->ec_name = client->name; ec_dev->phys_name = client->adapter->name; ec_dev->din_size = sizeof(struct ec_host_response_i2c) + sizeof(struct ec_response_get_protocol_info); diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c index 4e6f2f6b1095..faba03e2f1ef 100644 --- a/drivers/mfd/cros_ec_spi.c +++ b/drivers/mfd/cros_ec_spi.c @@ -637,7 +637,6 @@ static int cros_ec_spi_probe(struct spi_device *spi) ec_dev->irq = spi->irq; ec_dev->cmd_xfer = cros_ec_cmd_xfer_spi; ec_dev->pkt_xfer = cros_ec_pkt_xfer_spi; - ec_dev->ec_name = ec_spi->spi->modalias; ec_dev->phys_name = dev_name(&ec_spi->spi->dev); ec_dev->din_size = EC_MSG_PREAMBLE_COUNT + sizeof(struct ec_host_response) + diff --git a/drivers/platform/chrome/cros_ec_dev.c b/drivers/platform/chrome/cros_ec_dev.c index e91ced1cb8ce..e8fcdc237029 100644 --- a/drivers/platform/chrome/cros_ec_dev.c +++ b/drivers/platform/chrome/cros_ec_dev.c @@ -27,11 +27,22 @@ /* Device variables */ #define CROS_MAX_DEV 128 -static struct class *cros_class; static int ec_major; +static const struct attribute_group *cros_ec_groups[] = { + &cros_ec_attr_group, + &cros_ec_lightbar_attr_group, + NULL, +}; + +static struct class cros_class = { + .owner = THIS_MODULE, + .name = "chromeos", + .dev_groups = cros_ec_groups, +}; + /* Basic communication */ -static int ec_get_version(struct cros_ec_device *ec, char *str, int maxlen) +static int ec_get_version(struct cros_ec_dev *ec, char *str, int maxlen) { struct ec_response_get_version *resp; static const char * const current_image_name[] = { @@ -45,11 +56,11 @@ static int ec_get_version(struct cros_ec_device *ec, char *str, int maxlen) return -ENOMEM; msg->version = 0; - msg->command = EC_CMD_GET_VERSION; + msg->command = EC_CMD_GET_VERSION + ec->cmd_offset; msg->insize = sizeof(*resp); msg->outsize = 0; - ret = cros_ec_cmd_xfer(ec, msg); + ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) goto exit; @@ -78,8 +89,10 @@ exit: /* Device file ops */ static int ec_device_open(struct inode *inode, struct file *filp) { - filp->private_data = container_of(inode->i_cdev, - struct cros_ec_device, cdev); + struct cros_ec_dev *ec = container_of(inode->i_cdev, + struct cros_ec_dev, cdev); + filp->private_data = ec; + nonseekable_open(inode, filp); return 0; } @@ -91,7 +104,7 @@ static int ec_device_release(struct inode *inode, struct file *filp) static ssize_t ec_device_read(struct file *filp, char __user *buffer, size_t length, loff_t *offset) { - struct cros_ec_device *ec = filp->private_data; + struct cros_ec_dev *ec = filp->private_data; char msg[sizeof(struct ec_response_get_version) + sizeof(CROS_EC_DEV_VERSION)]; size_t count; @@ -114,7 +127,7 @@ static ssize_t ec_device_read(struct file *filp, char __user *buffer, } /* Ioctls */ -static long ec_device_ioctl_xcmd(struct cros_ec_device *ec, void __user *arg) +static long ec_device_ioctl_xcmd(struct cros_ec_dev *ec, void __user *arg) { long ret; struct cros_ec_command u_cmd; @@ -133,7 +146,8 @@ static long ec_device_ioctl_xcmd(struct cros_ec_device *ec, void __user *arg) goto exit; } - ret = cros_ec_cmd_xfer(ec, s_cmd); + s_cmd->command += ec->cmd_offset; + ret = cros_ec_cmd_xfer(ec->ec_dev, s_cmd); /* Only copy data to userland if data was received. */ if (ret < 0) goto exit; @@ -145,19 +159,21 @@ exit: return ret; } -static long ec_device_ioctl_readmem(struct cros_ec_device *ec, void __user *arg) +static long ec_device_ioctl_readmem(struct cros_ec_dev *ec, void __user *arg) { + struct cros_ec_device *ec_dev = ec->ec_dev; struct cros_ec_readmem s_mem = { }; long num; /* Not every platform supports direct reads */ - if (!ec->cmd_readmem) + if (!ec_dev->cmd_readmem) return -ENOTTY; if (copy_from_user(&s_mem, arg, sizeof(s_mem))) return -EFAULT; - num = ec->cmd_readmem(ec, s_mem.offset, s_mem.bytes, s_mem.buffer); + num = ec_dev->cmd_readmem(ec_dev, s_mem.offset, s_mem.bytes, + s_mem.buffer); if (num <= 0) return num; @@ -170,7 +186,7 @@ static long ec_device_ioctl_readmem(struct cros_ec_device *ec, void __user *arg) static long ec_device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - struct cros_ec_device *ec = filp->private_data; + struct cros_ec_dev *ec = filp->private_data; if (_IOC_TYPE(cmd) != CROS_EC_DEV_IOC) return -ENOTTY; @@ -193,45 +209,81 @@ static const struct file_operations fops = { .unlocked_ioctl = ec_device_ioctl, }; +static void __remove(struct device *dev) +{ + struct cros_ec_dev *ec = container_of(dev, struct cros_ec_dev, + class_dev); + kfree(ec); +} + static int ec_device_probe(struct platform_device *pdev) { - struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); - int retval = -ENOTTY; - dev_t devno = MKDEV(ec_major, 0); + int retval = -ENOMEM; + struct device *dev = &pdev->dev; + struct cros_ec_platform *ec_platform = dev_get_platdata(dev); + dev_t devno = MKDEV(ec_major, pdev->id); + struct cros_ec_dev *ec = kzalloc(sizeof(*ec), GFP_KERNEL); + + if (!ec) + return retval; - /* Instantiate it (and remember the EC) */ + dev_set_drvdata(dev, ec); + ec->ec_dev = dev_get_drvdata(dev->parent); + ec->dev = dev; + ec->cmd_offset = ec_platform->cmd_offset; + device_initialize(&ec->class_dev); cdev_init(&ec->cdev, &fops); + /* + * Add the character device + * Link cdev to the class device to be sure device is not used + * before unbinding it. + */ + ec->cdev.kobj.parent = &ec->class_dev.kobj; retval = cdev_add(&ec->cdev, devno, 1); if (retval) { - dev_err(&pdev->dev, ": failed to add character device\n"); - return retval; + dev_err(dev, ": failed to add character device\n"); + goto cdev_add_failed; } - ec->vdev = device_create(cros_class, NULL, devno, ec, - CROS_EC_DEV_NAME); - if (IS_ERR(ec->vdev)) { - retval = PTR_ERR(ec->vdev); - dev_err(&pdev->dev, ": failed to create device\n"); - cdev_del(&ec->cdev); - return retval; + /* + * Add the class device + * Link to the character device for creating the /dev entry + * in devtmpfs. + */ + ec->class_dev.devt = ec->cdev.dev; + ec->class_dev.class = &cros_class; + ec->class_dev.parent = dev; + ec->class_dev.release = __remove; + + retval = dev_set_name(&ec->class_dev, "%s", ec_platform->ec_name); + if (retval) { + dev_err(dev, "dev_set_name failed => %d\n", retval); + goto set_named_failed; } - /* Initialize extra interfaces */ - ec_dev_sysfs_init(ec); - ec_dev_lightbar_init(ec); + retval = device_add(&ec->class_dev); + if (retval) { + dev_err(dev, "device_register failed => %d\n", retval); + goto dev_reg_failed; + } return 0; + +dev_reg_failed: +set_named_failed: + dev_set_drvdata(dev, NULL); + cdev_del(&ec->cdev); +cdev_add_failed: + kfree(ec); + return retval; } static int ec_device_remove(struct platform_device *pdev) { - struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); - - ec_dev_lightbar_remove(ec); - ec_dev_sysfs_remove(ec); - device_destroy(cros_class, MKDEV(ec_major, 0)); + struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev); cdev_del(&ec->cdev); + device_unregister(&ec->class_dev); return 0; } @@ -248,10 +300,10 @@ static int __init cros_ec_dev_init(void) int ret; dev_t dev = 0; - cros_class = class_create(THIS_MODULE, "chromeos"); - if (IS_ERR(cros_class)) { + ret = class_register(&cros_class); + if (ret) { pr_err(CROS_EC_DEV_NAME ": failed to register device class\n"); - return PTR_ERR(cros_class); + return ret; } /* Get a range of minor numbers (starting with 0) to work with */ @@ -273,7 +325,7 @@ static int __init cros_ec_dev_init(void) failed_devreg: unregister_chrdev_region(MKDEV(ec_major, 0), CROS_MAX_DEV); failed_chrdevreg: - class_destroy(cros_class); + class_unregister(&cros_class); return ret; } @@ -281,7 +333,7 @@ static void __exit cros_ec_dev_exit(void) { platform_driver_unregister(&cros_ec_dev_driver); unregister_chrdev(ec_major, CROS_EC_DEV_NAME); - class_destroy(cros_class); + class_unregister(&cros_class); } module_init(cros_ec_dev_init); diff --git a/drivers/platform/chrome/cros_ec_dev.h b/drivers/platform/chrome/cros_ec_dev.h index 45d67f7e518c..bfd2c84c3571 100644 --- a/drivers/platform/chrome/cros_ec_dev.h +++ b/drivers/platform/chrome/cros_ec_dev.h @@ -24,7 +24,6 @@ #include #include -#define CROS_EC_DEV_NAME "cros_ec" #define CROS_EC_DEV_VERSION "1.0.0" /* @@ -44,10 +43,4 @@ struct cros_ec_readmem { #define CROS_EC_DEV_IOCXCMD _IOWR(CROS_EC_DEV_IOC, 0, struct cros_ec_command) #define CROS_EC_DEV_IOCRDMEM _IOWR(CROS_EC_DEV_IOC, 1, struct cros_ec_readmem) -void ec_dev_sysfs_init(struct cros_ec_device *); -void ec_dev_sysfs_remove(struct cros_ec_device *); - -void ec_dev_lightbar_init(struct cros_ec_device *); -void ec_dev_lightbar_remove(struct cros_ec_device *); - #endif /* _CROS_EC_DEV_H_ */ diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c index 6e1986a2dce1..144e09df9b84 100644 --- a/drivers/platform/chrome/cros_ec_lightbar.c +++ b/drivers/platform/chrome/cros_ec_lightbar.c @@ -92,7 +92,7 @@ out: return ret; } -static struct cros_ec_command *alloc_lightbar_cmd_msg(void) +static struct cros_ec_command *alloc_lightbar_cmd_msg(struct cros_ec_dev *ec) { struct cros_ec_command *msg; int len; @@ -105,14 +105,14 @@ static struct cros_ec_command *alloc_lightbar_cmd_msg(void) return NULL; msg->version = 0; - msg->command = EC_CMD_LIGHTBAR_CMD; + msg->command = EC_CMD_LIGHTBAR_CMD + ec->cmd_offset; msg->outsize = sizeof(struct ec_params_lightbar); msg->insize = sizeof(struct ec_response_lightbar); return msg; } -static int get_lightbar_version(struct cros_ec_device *ec, +static int get_lightbar_version(struct cros_ec_dev *ec, uint32_t *ver_ptr, uint32_t *flg_ptr) { struct ec_params_lightbar *param; @@ -120,13 +120,13 @@ static int get_lightbar_version(struct cros_ec_device *ec, struct cros_ec_command *msg; int ret; - msg = alloc_lightbar_cmd_msg(); + msg = alloc_lightbar_cmd_msg(ec); if (!msg) return 0; param = (struct ec_params_lightbar *)msg->data; param->cmd = LIGHTBAR_CMD_VERSION; - ret = cros_ec_cmd_xfer(ec, msg); + ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) { ret = 0; goto exit; @@ -165,7 +165,8 @@ static ssize_t version_show(struct device *dev, struct device_attribute *attr, char *buf) { uint32_t version = 0, flags = 0; - struct cros_ec_device *ec = dev_get_drvdata(dev); + struct cros_ec_dev *ec = container_of(dev, + struct cros_ec_dev, class_dev); int ret; ret = lb_throttle(); @@ -187,12 +188,13 @@ static ssize_t brightness_store(struct device *dev, struct cros_ec_command *msg; int ret; unsigned int val; - struct cros_ec_device *ec = dev_get_drvdata(dev); + struct cros_ec_dev *ec = container_of(dev, + struct cros_ec_dev, class_dev); if (kstrtouint(buf, 0, &val)) return -EINVAL; - msg = alloc_lightbar_cmd_msg(); + msg = alloc_lightbar_cmd_msg(ec); if (!msg) return -ENOMEM; @@ -203,7 +205,7 @@ static ssize_t brightness_store(struct device *dev, if (ret) goto exit; - ret = cros_ec_cmd_xfer(ec, msg); + ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) goto exit; @@ -231,11 +233,12 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr, { struct ec_params_lightbar *param; struct cros_ec_command *msg; - struct cros_ec_device *ec = dev_get_drvdata(dev); + struct cros_ec_dev *ec = container_of(dev, + struct cros_ec_dev, class_dev); unsigned int val[4]; int ret, i = 0, j = 0, ok = 0; - msg = alloc_lightbar_cmd_msg(); + msg = alloc_lightbar_cmd_msg(ec); if (!msg) return -ENOMEM; @@ -268,7 +271,7 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr, return ret; } - ret = cros_ec_cmd_xfer(ec, msg); + ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) goto exit; @@ -304,9 +307,10 @@ static ssize_t sequence_show(struct device *dev, struct ec_response_lightbar *resp; struct cros_ec_command *msg; int ret; - struct cros_ec_device *ec = dev_get_drvdata(dev); + struct cros_ec_dev *ec = container_of(dev, + struct cros_ec_dev, class_dev); - msg = alloc_lightbar_cmd_msg(); + msg = alloc_lightbar_cmd_msg(ec); if (!msg) return -ENOMEM; @@ -316,7 +320,7 @@ static ssize_t sequence_show(struct device *dev, if (ret) goto exit; - ret = cros_ec_cmd_xfer(ec, msg); + ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) goto exit; @@ -345,9 +349,10 @@ static ssize_t sequence_store(struct device *dev, struct device_attribute *attr, struct cros_ec_command *msg; unsigned int num; int ret, len; - struct cros_ec_device *ec = dev_get_drvdata(dev); + struct cros_ec_dev *ec = container_of(dev, + struct cros_ec_dev, class_dev); - msg = alloc_lightbar_cmd_msg(); + msg = alloc_lightbar_cmd_msg(ec); if (!msg) return -ENOMEM; @@ -372,7 +377,7 @@ static ssize_t sequence_store(struct device *dev, struct device_attribute *attr, if (ret) return ret; - ret = cros_ec_cmd_xfer(ec, msg); + ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) return ret; @@ -397,25 +402,27 @@ static struct attribute *__lb_cmds_attrs[] = { &dev_attr_sequence.attr, NULL, }; -static struct attribute_group lb_cmds_attr_group = { - .name = "lightbar", - .attrs = __lb_cmds_attrs, -}; -void ec_dev_lightbar_init(struct cros_ec_device *ec) +static umode_t cros_ec_lightbar_attrs_are_visible(struct kobject *kobj, + struct attribute *a, int n) { - int ret = 0; + struct device *dev = container_of(kobj, struct device, kobj); + struct cros_ec_dev *ec = container_of(dev, + struct cros_ec_dev, class_dev); + struct platform_device *pdev = container_of(ec->dev, + struct platform_device, dev); + if (pdev->id != 0) + return 0; /* Only instantiate this stuff if the EC has a lightbar */ - if (!get_lightbar_version(ec, NULL, NULL)) - return; - - ret = sysfs_create_group(&ec->vdev->kobj, &lb_cmds_attr_group); - if (ret) - pr_warn("sysfs_create_group() failed: %d\n", ret); + if (get_lightbar_version(ec, NULL, NULL)) + return a->mode; + else + return 0; } -void ec_dev_lightbar_remove(struct cros_ec_device *ec) -{ - sysfs_remove_group(&ec->vdev->kobj, &lb_cmds_attr_group); -} +struct attribute_group cros_ec_lightbar_attr_group = { + .name = "lightbar", + .attrs = __lb_cmds_attrs, + .is_visible = cros_ec_lightbar_attrs_are_visible, +}; diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c index cac24d356c91..bdd77ce45f05 100644 --- a/drivers/platform/chrome/cros_ec_lpc.c +++ b/drivers/platform/chrome/cros_ec_lpc.c @@ -283,7 +283,6 @@ static int cros_ec_lpc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ec_dev); ec_dev->dev = dev; - ec_dev->ec_name = pdev->name; ec_dev->phys_name = dev_name(dev); ec_dev->cmd_xfer = cros_ec_cmd_xfer_lpc; ec_dev->pkt_xfer = cros_ec_pkt_xfer_lpc; diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c index 78ba82d670cb..f3baf9973989 100644 --- a/drivers/platform/chrome/cros_ec_sysfs.c +++ b/drivers/platform/chrome/cros_ec_sysfs.c @@ -72,7 +72,8 @@ static ssize_t store_ec_reboot(struct device *dev, int got_cmd = 0, offset = 0; int i; int ret; - struct cros_ec_device *ec = dev_get_drvdata(dev); + struct cros_ec_dev *ec = container_of(dev, + struct cros_ec_dev, class_dev); msg = kmalloc(sizeof(*msg) + sizeof(*param), GFP_KERNEL); if (!msg) @@ -112,10 +113,10 @@ static ssize_t store_ec_reboot(struct device *dev, } msg->version = 0; - msg->command = EC_CMD_REBOOT_EC; + msg->command = EC_CMD_REBOOT_EC + ec->cmd_offset; msg->outsize = sizeof(*param); msg->insize = 0; - ret = cros_ec_cmd_xfer(ec, msg); + ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) { count = ret; goto exit; @@ -139,7 +140,8 @@ static ssize_t show_ec_version(struct device *dev, struct cros_ec_command *msg; int ret; int count = 0; - struct cros_ec_device *ec = dev_get_drvdata(dev); + struct cros_ec_dev *ec = container_of(dev, + struct cros_ec_dev, class_dev); msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL); if (!msg) @@ -147,10 +149,10 @@ static ssize_t show_ec_version(struct device *dev, /* Get versions. RW may change. */ msg->version = 0; - msg->command = EC_CMD_GET_VERSION; + msg->command = EC_CMD_GET_VERSION + ec->cmd_offset; msg->insize = sizeof(*r_ver); msg->outsize = 0; - ret = cros_ec_cmd_xfer(ec, msg); + ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) { count = ret; goto exit; @@ -175,9 +177,9 @@ static ssize_t show_ec_version(struct device *dev, image_names[r_ver->current_image] : "?")); /* Get build info. */ - msg->command = EC_CMD_GET_BUILD_INFO; + msg->command = EC_CMD_GET_BUILD_INFO + ec->cmd_offset; msg->insize = EC_HOST_PARAM_SIZE; - ret = cros_ec_cmd_xfer(ec, msg); + ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) count += scnprintf(buf + count, PAGE_SIZE - count, "Build info: XFER ERROR %d\n", ret); @@ -191,9 +193,9 @@ static ssize_t show_ec_version(struct device *dev, } /* Get chip info. */ - msg->command = EC_CMD_GET_CHIP_INFO; + msg->command = EC_CMD_GET_CHIP_INFO + ec->cmd_offset; msg->insize = sizeof(*r_chip); - ret = cros_ec_cmd_xfer(ec, msg); + ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) count += scnprintf(buf + count, PAGE_SIZE - count, "Chip info: XFER ERROR %d\n", ret); @@ -215,9 +217,9 @@ static ssize_t show_ec_version(struct device *dev, } /* Get board version */ - msg->command = EC_CMD_GET_BOARD_VERSION; + msg->command = EC_CMD_GET_BOARD_VERSION + ec->cmd_offset; msg->insize = sizeof(*r_board); - ret = cros_ec_cmd_xfer(ec, msg); + ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) count += scnprintf(buf + count, PAGE_SIZE - count, "Board version: XFER ERROR %d\n", ret); @@ -243,7 +245,8 @@ static ssize_t show_ec_flashinfo(struct device *dev, struct ec_response_flash_info *resp; struct cros_ec_command *msg; int ret; - struct cros_ec_device *ec = dev_get_drvdata(dev); + struct cros_ec_dev *ec = container_of(dev, + struct cros_ec_dev, class_dev); msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL); if (!msg) @@ -251,10 +254,10 @@ static ssize_t show_ec_flashinfo(struct device *dev, /* The flash info shouldn't ever change, but ask each time anyway. */ msg->version = 0; - msg->command = EC_CMD_FLASH_INFO; + msg->command = EC_CMD_FLASH_INFO + ec->cmd_offset; msg->insize = sizeof(*resp); msg->outsize = 0; - ret = cros_ec_cmd_xfer(ec, msg); + ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) goto exit; if (msg->result != EC_RES_SUCCESS) { @@ -288,20 +291,7 @@ static struct attribute *__ec_attrs[] = { NULL, }; -static struct attribute_group ec_attr_group = { +struct attribute_group cros_ec_attr_group = { .attrs = __ec_attrs, }; -void ec_dev_sysfs_init(struct cros_ec_device *ec) -{ - int error; - - error = sysfs_create_group(&ec->vdev->kobj, &ec_attr_group); - if (error) - pr_warn("failed to create group: %d\n", error); -} - -void ec_dev_sysfs_remove(struct cros_ec_device *ec) -{ - sysfs_remove_group(&ec->vdev->kobj, &ec_attr_group); -} diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index 92e13aaa450c..da72671a42fa 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -17,10 +17,14 @@ #define __LINUX_MFD_CROS_EC_H #include +#include #include #include #include +#define CROS_EC_DEV_NAME "cros_ec" +#define CROS_EC_DEV_PD_NAME "cros_pd" + /* * The EC is unresponsive for a time after a reboot command. Add a * simple delay to make sure that the bus stays locked. @@ -71,11 +75,8 @@ struct cros_ec_command { /** * struct cros_ec_device - Information about a ChromeOS EC device * - * @ec_name: name of EC device (e.g. 'chromeos-ec') * @phys_name: name of physical comms layer (e.g. 'i2c-4') * @dev: Device pointer for physical comms device - * @vdev: Device pointer for virtual comms device - * @cdev: Character device structure for virtual comms device * @was_wake_device: true if this device was set to wake the system from * sleep at the last suspend * @cmd_readmem: direct read of the EC memory-mapped region, if supported @@ -87,6 +88,7 @@ struct cros_ec_command { * * @priv: Private data * @irq: Interrupt to use + * @id: Device id * @din: input buffer (for data from EC) * @dout: output buffer (for data to EC) * \note @@ -109,11 +111,8 @@ struct cros_ec_command { struct cros_ec_device { /* These are used by other drivers that want to talk to the EC */ - const char *ec_name; const char *phys_name; struct device *dev; - struct device *vdev; - struct cdev cdev; bool was_wake_device; struct class *cros_class; int (*cmd_readmem)(struct cros_ec_device *ec, unsigned int offset, @@ -138,6 +137,35 @@ struct cros_ec_device { struct mutex lock; }; +/* struct cros_ec_platform - ChromeOS EC platform information + * + * @ec_name: name of EC device (e.g. 'cros-ec', 'cros-pd', ...) + * used in /dev/ and sysfs. + * @cmd_offset: offset to apply for each command. Set when + * registering a devicde behind another one. + */ +struct cros_ec_platform { + const char *ec_name; + u16 cmd_offset; +}; + +/* + * struct cros_ec_dev - ChromeOS EC device entry point + * + * @class_dev: Device structure used in sysfs + * @cdev: Character device structure in /dev + * @ec_dev: cros_ec_device structure to talk to the physical device + * @dev: pointer to the platform device + * @cmd_offset: offset to apply for each command. + */ +struct cros_ec_dev { + struct device class_dev; + struct cdev cdev; + struct cros_ec_device *ec_dev; + struct device *dev; + u16 cmd_offset; +}; + /** * cros_ec_suspend - Handle a suspend operation for the ChromeOS EC device * @@ -224,4 +252,8 @@ int cros_ec_register(struct cros_ec_device *ec_dev); */ int cros_ec_query_all(struct cros_ec_device *ec_dev); +/* sysfs stuff */ +extern struct attribute_group cros_ec_attr_group; +extern struct attribute_group cros_ec_lightbar_attr_group; + #endif /* __LINUX_MFD_CROS_EC_H */ -- cgit v1.2.3-59-g8ed1b From 469d7d22cea146e40efe8c330e5164b4d8f13934 Mon Sep 17 00:00:00 2001 From: Frodo Lai Date: Tue, 16 Jun 2015 15:03:53 -0700 Subject: Input: pixcir_i2c_ts - fix receive error The i2c_master_recv() uses readsize to receive data from i2c but compares to size of rdbuf which is always 27. This would cause problem when the max_fingers is not 5. Change the comparison value to readsize instead. Fixes: 36874c7e219 ("Input: pixcir_i2c_ts - support up to 5 fingers and hardware tracking IDs:) Cc: stable@vger.kernel.org Signed-off-by: Frodo Lai Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/pixcir_i2c_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index 2c2107147319..8f3e243a62bf 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -78,7 +78,7 @@ static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata, } ret = i2c_master_recv(tsdata->client, rdbuf, readsize); - if (ret != sizeof(rdbuf)) { + if (ret != readsize) { dev_err(&tsdata->client->dev, "%s: i2c_master_recv failed(), ret=%d\n", __func__, ret); -- cgit v1.2.3-59-g8ed1b From 539c4b88146cc320aee18b08ebb43ab57d523dcc Mon Sep 17 00:00:00 2001 From: duson Date: Tue, 7 Jul 2015 10:25:39 -0700 Subject: Input: elan_i2c - change the hover event from MT to ST The firmware only reports hover condition while the very first contact is approaching the surface; the hover is not reported for the subsequent contacts. Therefore we should not be using ABS_MT_DISTANCE to report hover but rather its single-touch counterpart ABS_DISTANCE. Signed-off-by: Duson Lin Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elan_i2c_core.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index 62641f2adaf7..5b5f403d8ce6 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -771,7 +771,7 @@ static const struct attribute_group *elan_sysfs_groups[] = { */ static void elan_report_contact(struct elan_tp_data *data, int contact_num, bool contact_valid, - bool hover_event, u8 *finger_data) + u8 *finger_data) { struct input_dev *input = data->input; unsigned int pos_x, pos_y; @@ -815,9 +815,7 @@ static void elan_report_contact(struct elan_tp_data *data, input_mt_report_slot_state(input, MT_TOOL_FINGER, true); input_report_abs(input, ABS_MT_POSITION_X, pos_x); input_report_abs(input, ABS_MT_POSITION_Y, data->max_y - pos_y); - input_report_abs(input, ABS_MT_DISTANCE, hover_event); - input_report_abs(input, ABS_MT_PRESSURE, - hover_event ? 0 : scaled_pressure); + input_report_abs(input, ABS_MT_PRESSURE, scaled_pressure); input_report_abs(input, ABS_TOOL_WIDTH, mk_x); input_report_abs(input, ABS_MT_TOUCH_MAJOR, major); input_report_abs(input, ABS_MT_TOUCH_MINOR, minor); @@ -839,14 +837,14 @@ static void elan_report_absolute(struct elan_tp_data *data, u8 *packet) hover_event = hover_info & 0x40; for (i = 0; i < ETP_MAX_FINGERS; i++) { contact_valid = tp_info & (1U << (3 + i)); - elan_report_contact(data, i, contact_valid, hover_event, - finger_data); + elan_report_contact(data, i, contact_valid, finger_data); if (contact_valid) finger_data += ETP_FINGER_DATA_LEN; } input_report_key(input, BTN_LEFT, tp_info & 0x01); + input_report_abs(input, ABS_DISTANCE, hover_event != 0); input_mt_report_pointer_emulation(input, true); input_sync(input); } @@ -922,6 +920,7 @@ static int elan_setup_input_device(struct elan_tp_data *data) input_abs_set_res(input, ABS_Y, data->y_res); input_set_abs_params(input, ABS_PRESSURE, 0, ETP_MAX_PRESSURE, 0, 0); input_set_abs_params(input, ABS_TOOL_WIDTH, 0, ETP_FINGER_WIDTH, 0, 0); + input_set_abs_params(input, ABS_DISTANCE, 0, 1, 0, 0); /* And MT parameters */ input_set_abs_params(input, ABS_MT_POSITION_X, 0, data->max_x, 0, 0); @@ -934,7 +933,6 @@ static int elan_setup_input_device(struct elan_tp_data *data) ETP_FINGER_WIDTH * max_width, 0, 0); input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, ETP_FINGER_WIDTH * min_width, 0, 0); - input_set_abs_params(input, ABS_MT_DISTANCE, 0, 1, 0, 0); data->input = input; -- cgit v1.2.3-59-g8ed1b From dbf3c370862d73fcd2c74ca55e254bb02143238d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 10 Jul 2015 10:11:07 -0700 Subject: Revert "Input: synaptics - allocate 3 slots to keep stability in image sensors" This reverts commit 63c4fda3c0bb841b1aad1298fc7fe94058fc79f8 as it causes issues with detecting 3-finger taps. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=100481 Cc: stable@vger.kernel.org Acked-by: Benjamin Tissoires --- drivers/input/mouse/synaptics.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 35c8d0ceabee..3a32caf06bf1 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -1199,7 +1199,7 @@ static void set_input_params(struct psmouse *psmouse, ABS_MT_POSITION_Y); /* Image sensors can report per-contact pressure */ input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0); - input_mt_init_slots(dev, 3, INPUT_MT_POINTER | INPUT_MT_TRACK); + input_mt_init_slots(dev, 2, INPUT_MT_POINTER | INPUT_MT_TRACK); /* Image sensors can signal 4 and 5 finger clicks */ __set_bit(BTN_TOOL_QUADTAP, dev->keybit); -- cgit v1.2.3-59-g8ed1b