Linux Input/HID development
 help / color / mirror / Atom feed
* Re: Xpad Driver Replacement
From: Ken Phillis Jr @ 2013-12-14  5:16 UTC (permalink / raw)
  Cc: linux-input
In-Reply-To: <52A68A23.7050707@computerquip.com>

On Mon, Dec 9, 2013 at 9:27 PM, Zachary Lund <admin@computerquip.com> wrote:
>
>
> On 12/08/13 00:18, Zachary Lund wrote:
>>
>> Secondly, the Xbox 360 controllers claim to be HID compliant... this is not an HID driver. That's because the report descriptor is missing and I, unfortunately, do not know what to do about that. Some drivers like XBCD and the driver found at tattiebogle.net both provide their own report descriptor and work from there. While I'd like to do the same eventually, it will take me longer than a week to do that as I'd have to educate myself on HID and figure out what to do about the missing descriptors.
>
> I've run into an instant road block. The controller claims bInterfaceClass to be 0xFF (Vendor-specific) so usbhid won't probe it. I didn't think it would be so difficult to work around that but I've spent the better part of today trying to figure out just that. usbhid is a usb_driver that has only one requirement to be probed: bInterfaceClass be 0x03. Unfortunately, the device fails this requirement.
>
> Does anyone know of a way around this mechanism? Or perhaps I should take a different approach?
>

I agree that the XBOX 360 controller support should appear as a
separate driver. However the approach for detecting this should
instead rely on what microsoft has described as the "Xbox 360 Common
Controller class" (XUSB) [1]. The documentation surround the XINPUT
covers Audio and Joystick input. However I believe it is possible to
just cover the joystick input types ( and sub-controller types ) [2]
without overdoing the driver.

[1] MSDN Reference - DirectInput and XUSB
 http://msdn.microsoft.com/en-us/library/windows/desktop/hh405052%28v=vs.85%29.aspx

[2] MSDN Reference - XINPUT and sub-controller types.
http://msdn.microsoft.com/en-us/library/windows/desktop/hh405050%28v=vs.85%29.aspx

^ permalink raw reply

* Re: [PATCH 1/2] joydev: Map ABS_{THROTTLE,GAS,BREAK} to positive values.
From: Benjamin Franzke @ 2013-12-14  6:22 UTC (permalink / raw)
  To: Ken Phillis Jr; +Cc: linux-input
In-Reply-To: <CACiKS=QHY6Zq69+zRamZu6xPDUQs0DzNXB8TO_y977fq+qY2pw@mail.gmail.com>

I didnt test with games on steam.
I've tested with supertuxkart 0.8.1 and jstest.

Could you give me a hint, why you think that it breaks(?) input detection?

I think SDL2 used evdev, so how should a changed joydev break the
correction? (or do you mean in joydev fallback mode?)
Searching through the SDL code I guess you refer to this:
 http://hg.libsdl.org/SDL/file/a5f8137ccf01/src/joystick/SDL_gamecontroller.c#l133
This really looks like a workaround for this brokeness of the joydev
correction for triggers,
thats why this should be fixed in kernel.

It cant really be inteded to map triggers to -32767-32767, and
consider this API to not brake application that have workarounds for
this.
Or is it?

2013/12/14 Ken Phillis Jr <kphillisjr@gmail.com>:
> On Wed, Dec 11, 2013 at 2:15 AM, Benjamin Franzke
> <benjaminfranzke@googlemail.com> wrote:
>> Gamepads like XInput devices have triggers with a range from
>> e.g. 0 to 255.
>> When the conventional joydev AXES correction/mapping method is used,
>> the default state 0 (i.e. when the user does not press a button or trigger)
>> is mapped to -32767 (and 255 to 32767).
>> You'll get 0 only when pressing the trigger half-way down.
>> This has several drawbacks:
>>  - A trigger is not usable at all when configured to have 2 directions
>>    when there is physically only one.
>>  - Applications that let the user configure joystick input assignments
>>    may register the mapped non-zero default state as a press,
>>    preventing the user from configuring any sensible value.
>>
>> Traditonal calibration e.g. with jscal does not help to get a usable
>> experience either, it'll map the default state to -32767 as well.
>> Only manually editing the calibration data does work currently.
>>
>> This patch tries to fix this issue by calculating a positive-only range for
>> the throttle break and gas ABS bits.
>> Maybe other ABS types need to be added to this scheme as well(?)
>>
>> Note:
>> Although joydev is considered obsolete and people are encouraged to just
>> use evdev, joydev should still be fixed, because
>>  1. many applications still use it.
>>  2. userspace already copies the joydev correction code to evdev (e.g. libSDL).
>>
>> This bug should be fixed here in joydev first, and when we have a good
>> sensible state, this should be propagated to the application/libraries
>> that copied the joydev correction code.
>>
>> Gamepad drivers will need to advertise triggers as THROTTLE,GAS,BREAK instead
>> of e.g. ABS_{RZ,Z} where phyisically appropriate, to make use of this.
>>
>> I've considered changing the current joydev correction calculation for all ABS
>> types, to have only a scaling functionality by default, no translation
>> (all positive values remain positive, negatives negative and zero remains zero),
>> so that drivers would need to expose zero-centered ABS values.
>> It turned out to many drivers would need to be adjusted, which i cant
>> test.
>>
>> Signed-off-by: Benjamin Franzke <benjaminfranzke@googlemail.com>
>
>
> I have serious concerns that this Patch and the Next patch will the
> input detection of the triggers on various games powered by SDL2. Have
> you tested this against games listed on steam that have full
> controller support?

^ permalink raw reply

* Re: [PATCH 1/2] joydev: Map ABS_{THROTTLE,GAS,BREAK} to positive values.
From: Ken Phillis Jr @ 2013-12-14  6:56 UTC (permalink / raw)
  To: linux-input
In-Reply-To: <CAFc8KeTxX7Wdm5nWGBctwLKew+kmgrJmR0Hdd1cSYStADPFQXQ@mail.gmail.com>

On Sat, Dec 14, 2013 at 12:22 AM, Benjamin Franzke
<benjaminfranzke@googlemail.com> wrote:
>
> I didnt test with games on steam.
> I've tested with supertuxkart 0.8.1 and jstest.
>
> Could you give me a hint, why you think that it breaks(?) input detection?
>
> I think SDL2 used evdev, so how should a changed joydev break the
> correction? (or do you mean in joydev fallback mode?)
> Searching through the SDL code I guess you refer to this:
>  http://hg.libsdl.org/SDL/file/a5f8137ccf01/src/joystick/SDL_gamecontroller.c#l133
> This really looks like a workaround for this brokeness of the joydev
> correction for triggers,
> thats why this should be fixed in kernel.
>
> It cant really be inteded to map triggers to -32767-32767, and
> consider this API to not brake application that have workarounds for
> this.
> Or is it?
>
>



I believe the more appropriate work around would be to have the
appropriate mappings setting that are dependent on the device type.
This is to help improve overall interoperability. I know that most
xbox 360 devices can directly define the device type with the
descriptors. That said, Two of the mappings that can work are
MAP_FLIGHT_STICK and  MAP_RACING_WHEEL.

In the case of the MAP_RACING_WHEEL the following look reasonable enough...
ABS_WHEEL ( X axis on Left stick )
ABS_GAS  ( Left trigger )
ABS_BRAKE ( Right Trigger )

in the case of MAP_FLIGHT_STICK, the following is needed...
PITCH/ROLL - Axis of Left stick
POV Hat - Right stick
Rudder - Left Trigger
Throttle - Right Trigger.

^ permalink raw reply

* Why does adding HID USB ID allow a keyboard to work?
From: Reyad Attiyat @ 2013-12-14  7:14 UTC (permalink / raw)
  To: linux-input

I have a basic question about the way the linux kernel handles human
input devices. I bought a Surface Pro 2 with a Type cover keyboard.
This did not work out of the box with stock linux kernel 3.11. When I
added the hardware id of the device to the hid-microsoft.c driver it
worked.

Does this work simply because when I add the hardware id to the
driver, the kernel knows to use usb-hid generic driver with the
device? I'm trying to understand the hid-microsoft driver and it seems
to apply quirks/hacks that enable Microsoft devices to work with the
hid driver. Such as remapping keys. I don't need any of these quirks
as all the media keys on the keyboard work all by adding the hardware
id.

Why doesn't the device work out of the box? Is the device not
advertising itself as a hid device over usb? I'm little confused as to
how this should be fixed. Does the kernel fail in noticing this hid
device or does the device fail in telling the kernel it is a
keyboard/mouse?

^ permalink raw reply

* Re: [PATCH 1/2] joydev: Map ABS_{THROTTLE,GAS,BREAK} to positive values.
From: Benjamin Franzke @ 2013-12-14  8:16 UTC (permalink / raw)
  To: Ken Phillis Jr; +Cc: linux-input
In-Reply-To: <CACiKS=TRkEcEXHrB2P9wOfxrYWb8LtJ3S7F+h-OvZeNXYsoxpg@mail.gmail.com>

2013/12/14 Ken Phillis Jr <kphillisjr@gmail.com>:
> On Sat, Dec 14, 2013 at 12:22 AM, Benjamin Franzke
> <benjaminfranzke@googlemail.com> wrote:
>>
>> I didnt test with games on steam.
>> I've tested with supertuxkart 0.8.1 and jstest.
>>
>> Could you give me a hint, why you think that it breaks(?) input detection?
>>
>> I think SDL2 used evdev, so how should a changed joydev break the
>> correction? (or do you mean in joydev fallback mode?)
>> Searching through the SDL code I guess you refer to this:
>>  http://hg.libsdl.org/SDL/file/a5f8137ccf01/src/joystick/SDL_gamecontroller.c#l133
>> This really looks like a workaround for this brokeness of the joydev
>> correction for triggers,
>> thats why this should be fixed in kernel.
>>
>> It cant really be inteded to map triggers to -32767-32767, and
>> consider this API to not brake application that have workarounds for
>> this.
>> Or is it?
>>
>>
>
>
>
> I believe the more appropriate work around would be to have the
> appropriate mappings setting that are dependent on the device type.
> This is to help improve overall interoperability. I know that most
> xbox 360 devices can directly define the device type with the
> descriptors. That said, Two of the mappings that can work are
> MAP_FLIGHT_STICK and  MAP_RACING_WHEEL.
>
> In the case of the MAP_RACING_WHEEL the following look reasonable enough...
> ABS_WHEEL ( X axis on Left stick )
> ABS_GAS  ( Left trigger )
> ABS_BRAKE ( Right Trigger )

I guess you mean left->brake and right->gas, thats how it is usually used.
I agree that GAS is better suited in combination with BRAKE.
>
> in the case of MAP_FLIGHT_STICK, the following is needed...
> PITCH/ROLL - Axis of Left stick
> POV Hat - Right stick
> Rudder - Left Trigger
> Throttle - Right Trigger.

I think this profiles can be another optimization, but they'll still
need an adjusted default-correction in joydev.
An alternative to these map types would probably be just generic bits
like ABS_TRIGGER_*?

> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: kernel panic on gpio-keys
From: Dmitry Torokhov @ 2013-12-14  9:39 UTC (permalink / raw)
  To: Paul Cercueil; +Cc: linux-input
In-Reply-To: <52A8BA49.7080507@gmail.com>

On Wed, Dec 11, 2013 at 08:17:29PM +0100, Paul Cercueil wrote:
> Hi there,
> 
> I am trying to use the gpio-keys driver to inject joystick events.
> There seems to be some basic support of it, looking at <linux/gpio_keys.h>.
> 
> However, registering the following will trigger a kernel panic in
> the kernel:
> 
> static struct gpio_keys_button my_buttons[] {
> 	{
> 		.gpio = GPIO_FOO,
> 		.type = EV_ABS,
> 		.code = ABS_HAT0X,
> 		.value = 1,
> 	},
> };
> 
> (tested on kernel 3.12).
> 
> I don't know well the input subsystem, so I have no idea of what is
> going wrong. Could anybody try to at least reproduce the issue?

It woudl be helpful if you poster the stack trace from panic so we'd
have an idea where the fault happens.

Thanks.

-- 
Dmitry

^ permalink raw reply

* This is an Email Service Alert from Helpdesk
From: Veronica Coxsome @ 2013-12-14 12:04 UTC (permalink / raw)






This is an Email Service Alert from Helpdesk. This is to inform you that your mailbox has exceeds its storage limit, you will be unable to receive and send emails. To

re-set your Account Space on our database, prior to maintain your INBOX from 20G to 20.9G. CLICK HERE http://www.flynet.pp.ua/-4./ to Activate.

Warm Regards,
Helpdesk Administrator.

^ permalink raw reply

* [PATCH] drivers: input: Include appropriate header file in cyttsp_i2c_common.c
From: Rashika Kheria @ 2013-12-14 13:38 UTC (permalink / raw)
  To: linux-kernel; +Cc: Ferruh Yigit, Dmitry Torokhov, linux-input, josh

This patch includes appropriate header file cyttsp4_core.h in
touchscreen/cyttsp_i2c_common.c because functions
cyttsp_i2c_read_block_data() and cyttsp_i2c_write_block_data()
have their prototype declaration in cyttsp4_core.h.

Thus, it also eliminates the following warning in cyttsp_i2c_common.c:
drivers/input/touchscreen/cyttsp_i2c_common.c:34:5: warning: no previous prototype for ‘cyttsp_i2c_read_block_data’ [-Wmissing-prototypes]
drivers/input/touchscreen/cyttsp_i2c_common.c:64:5: warning: no previous prototype for ‘cyttsp_i2c_write_block_data’ [-Wmissing-prototypes]


Signed-off-by: Rashika Kheria <rashika.kheria@gmail.com>
---
 drivers/input/touchscreen/cyttsp_i2c_common.c |    2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/input/touchscreen/cyttsp_i2c_common.c b/drivers/input/touchscreen/cyttsp_i2c_common.c
index 1d7b6f1..ccefa56 100644
--- a/drivers/input/touchscreen/cyttsp_i2c_common.c
+++ b/drivers/input/touchscreen/cyttsp_i2c_common.c
@@ -31,6 +31,8 @@
 #include <linux/module.h>
 #include <linux/types.h>
 
+#include "cyttsp4_core.h"
+
 int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf,
 				      u16 addr, u8 length, void *values)
 {
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* Re: [PATCH] drivers: input: Include appropriate header file in cyttsp_i2c_common.c
From: Josh Triplett @ 2013-12-14 20:57 UTC (permalink / raw)
  To: Rashika Kheria; +Cc: linux-kernel, Ferruh Yigit, Dmitry Torokhov, linux-input
In-Reply-To: <20131214133822.GA13124@rashika>

On Sat, Dec 14, 2013 at 07:08:22PM +0530, Rashika Kheria wrote:
> This patch includes appropriate header file cyttsp4_core.h in
> touchscreen/cyttsp_i2c_common.c because functions
> cyttsp_i2c_read_block_data() and cyttsp_i2c_write_block_data()
> have their prototype declaration in cyttsp4_core.h.
> 
> Thus, it also eliminates the following warning in cyttsp_i2c_common.c:
> drivers/input/touchscreen/cyttsp_i2c_common.c:34:5: warning: no previous prototype for ‘cyttsp_i2c_read_block_data’ [-Wmissing-prototypes]
> drivers/input/touchscreen/cyttsp_i2c_common.c:64:5: warning: no previous prototype for ‘cyttsp_i2c_write_block_data’ [-Wmissing-prototypes]
> 
> 
> Signed-off-by: Rashika Kheria <rashika.kheria@gmail.com>

Reviewed-by: Josh Triplett <josh@joshtriplett.org>

>  drivers/input/touchscreen/cyttsp_i2c_common.c |    2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/drivers/input/touchscreen/cyttsp_i2c_common.c b/drivers/input/touchscreen/cyttsp_i2c_common.c
> index 1d7b6f1..ccefa56 100644
> --- a/drivers/input/touchscreen/cyttsp_i2c_common.c
> +++ b/drivers/input/touchscreen/cyttsp_i2c_common.c
> @@ -31,6 +31,8 @@
>  #include <linux/module.h>
>  #include <linux/types.h>
>  
> +#include "cyttsp4_core.h"
> +
>  int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf,
>  				      u16 addr, u8 length, void *values)
>  {
> -- 
> 1.7.9.5
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH] Add new USB_DEVICE_ID to holtek workaround module (hid-holtek-mouse.c)
From: Jiri Kosina @ 2013-12-14 23:40 UTC (permalink / raw)
  To: Kharlamov Alexey; +Cc: linux-input, linux-kernel
In-Reply-To: <120781387058135@web10j.yandex.ru>


[ usual CCs added ]

On Sun, 15 Dec 2013, Kharlamov Alexey wrote:

> New USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070 constant was added to existing 
> workaround module, so RITMIX ROM-316 mouse can now work.
> 
> Signed-off-by: Alexey Kharlamov <derlafff@ya.ru>
> 
> ---
> 
> Tested and patched on linux-3.13-rc3

Hi Alexey,

I am wondering how this patch was tested, please? See comments below.

> diff -uprN a/drivers/hid/hid-holtek-mouse.c b/drivers/hid/hid-holtek-mouse.c
> --- a/drivers/hid/hid-holtek-mouse.c	2013-12-06 21:34:04.000000000 +0400
> +++ b/drivers/hid/hid-holtek-mouse.c	2013-12-15 01:24:30.277447386 +0400
> @@ -28,6 +28,7 @@
>   * - USB ID 04d9:a04a, sold as Tracer Sniper TRM-503, NOVA Gaming Slider X200
>   *   and Zalman ZM-GM1
>   * - USB ID 04d9:a081, sold as SHARKOON DarkGlider Gaming mouse
> + * - USB ID 04d9:a070, sold as RITMIX ROM-316 Gaming Mouse
>   * - USB ID 04d9:a072, sold as LEETGION Hellion Gaming Mouse
>   */

You are not adding a new switch case to holtek_mouse_report_fixup(), hence 
your patch seems to be a no-op (as the function does absolutely nothing 
and returns the original descriptor back)?

>  
> @@ -68,6 +69,8 @@ static const struct hid_device_id holtek
>  	{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT,
>  			USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A) },
>  	{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT,
> +			USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070) },
> +	{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT,
>  			USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072) },
>  	{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT,
>  			USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) },
> diff -uprN a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> --- a/drivers/hid/hid-ids.h	2013-12-06 21:34:04.000000000 +0400
> +++ b/drivers/hid/hid-ids.h	2013-12-15 01:22:37.661390627 +0400
> @@ -455,6 +455,7 @@
>  #define USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD	0xa055
>  #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A	0xa04a
>  #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067	0xa067
> +#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070	0xa070
>  #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072	0xa072
>  #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081	0xa081

You also need to add the device to hid_have_special_driver[] so that the 
binding is guaranteed to be correct.

Could you please work in fixing up and resending the patch?

Thanks,

-- 
Jiri Kosina
SUSE Labs

^ permalink raw reply

* [RFC] Add ff-memless-next driver
From: Michal Malý @ 2013-12-15  0:19 UTC (permalink / raw)
  To: dmitry.torokhov; +Cc: linux-kernel, linux-input, elias.vds, anssi.hannula

Hi,

"ff-memless-next" driver is an extended modification of the original ff-memless driver. Unlike ff-memless, ff-memless-next targets only "serious" FFB devices such as racing wheels and hi(ish)-end joysticks with FFB actuators instead of simple rumble motors. Modifications in ff-memless-next include:
- Support of periodic and ramp effects
  These are treated as "combinable" effects and forces created by each of these effects is superposed into one total force
- Support for conditional effects
  As these effects cannot be effectively combined together, they are handled separately depending on the capabilities of the underlying HW-specific driver
- Removed emulation of rumble effect
- Adjustable update rate
- Differentiation between "set effect's force to zero" and "stop effect"
- Checks whether the effect's parameters are valid upon upload - this should better be handled by ff-core though IMHO.

At the moment there is no HW-specific backend that would make use of ff-memless-next, the plan is to update hid-lg4ff once ff-memless-next gets accepted by upstream. ff-memless-next can be tested with dummy FFB device module which is available here (git://prifuk.cz/ff-dummy-device).

My primary motivation to write ff-memless-next were limitations of the current ff-memless driver I came across during work on the driver for Logitech gaming wheels. However, ff-memless-next contains no code that would specifically target Logitech devices.

I must give a huge thanks to Elias Vanderstuyft (CC'd) for his extensive testing of the driver and numerous helpful suggestions.

Tested-by: Elias Vanderstuyft <elias.vds@gmail.com>
Signed-off-by: Michal Malý <madcatxster@prifuk.cz>
---
From 12c7feb547ff16d91f6d04986862eaf5f266ddeb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michal=20Mal=C3=BD?= <madcatxster@prifuk.cz>
Date: Sat, 14 Dec 2013 21:53:26 +0100
Subject: [PATCH 1/1] Add ff-memless-next driver

---
 drivers/input/Kconfig                 |  12 +
 drivers/input/Makefile                |   1 +
 drivers/input/ff-memless-next.c       | 675 ++++++++++++++++++++++++++++++++++
 include/linux/input/ff-memless-next.h |  29 ++
 4 files changed, 717 insertions(+)
 create mode 100644 drivers/input/ff-memless-next.c
 create mode 100644 include/linux/input/ff-memless-next.h

diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index a11ff74..893ab00 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -77,6 +77,18 @@ config INPUT_MATRIXKMAP
 	  To compile this driver as a module, choose M here: the
 	  module will be called matrix-keymap.
 
+config INPUT_FF_MEMLESS_NEXT
+	tristate "New version of support for memoryless force feedback devices"
+	help
+	  Say Y here if you want to enable support for various memoryless
+	  force feedback devices (as of now there is no hardware-specific
+	  driver that supports this)
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ff-memless-next.
+
 comment "Userland interfaces"
 
 config INPUT_MOUSEDEV
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 5ca3f63..169e99c 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_INPUT_FF_MEMLESS)	+= ff-memless.o
 obj-$(CONFIG_INPUT_POLLDEV)	+= input-polldev.o
 obj-$(CONFIG_INPUT_SPARSEKMAP)	+= sparse-keymap.o
 obj-$(CONFIG_INPUT_MATRIXKMAP)	+= matrix-keymap.o
+obj-$(CONFIG_INPUT_FF_MEMLESS_NEXT)	+= ff-memless-next.o
 
 obj-$(CONFIG_INPUT_MOUSEDEV)	+= mousedev.o
 obj-$(CONFIG_INPUT_JOYDEV)	+= joydev.o
diff --git a/drivers/input/ff-memless-next.c b/drivers/input/ff-memless-next.c
new file mode 100644
index 0000000..23727b2
--- /dev/null
+++ b/drivers/input/ff-memless-next.c
@@ -0,0 +1,675 @@
+/*
+ *
+ * Force feedback support for memory-less devices
+ *
+ * This module is based on "ff-memless" orignally written by Anssi Hannula.
+ * It is extented to support all force feedback effects currently supported
+ * by the Linux input stack. All effects except FF_RAMP are handled in accordance with
+ * Microsoft DirectInput specification valid at the time the module was written.
+ *
+ * Copyright(c) 2013 Michal Maly <madcatxster@prifuk.cz>
+ *
+ */
+
+/*
+ * 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 the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/jiffies.h>
+#include <linux/fixp-arith.h>
+#include <linux/input/ff-memless-next.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michal \"MadCatX\" Maly");
+MODULE_DESCRIPTION("Force feedback support for memoryless force feedback devices");
+
+#define FF_MAX_EFFECTS 16
+#define FF_MIN_UPDATE_RATE_MSECS 5
+
+#define FF_EFFECT_STARTED 0x0
+#define FF_EFFECT_PLAYING 0x1
+
+
+struct mlnx_effect {
+	struct ff_effect *effect;
+	unsigned long flags;
+	unsigned long begin_at;
+	unsigned long stop_at;
+	unsigned long updated_at;
+	unsigned long attack_stop;
+	unsigned long fade_begin;
+	int repeat;
+	u16 playback_time;
+};
+
+struct mlnx_device {
+	u8 combinable_playing;
+	unsigned long update_rate_jiffies;
+	void *private;
+	struct mlnx_effect effects[FF_MAX_EFFECTS];
+	int gain;
+	struct timer_list timer;
+	struct input_dev *dev;
+
+	int (*control_effect)(struct input_dev *, void *, const struct mlnx_effect_command *);
+};
+
+static inline s32 mlnx_calculate_x_force(const s32 level, const u16 direction)
+{
+	s32 new = (level * -fixp_sin(direction)) >> FRAC_N;
+	pr_debug("x force: %d\n", new);
+	return new;
+}
+
+static inline s32 mlnx_calculate_y_force(const s32 level, const u16 direction)
+{
+	s32 new = (level * -fixp_cos(direction)) >> FRAC_N;
+	pr_debug("y force: %d\n", new);
+	return new;
+}
+
+static inline s32 mlnx_clamp_level(const s32 level)
+{
+	return (level > 0x7fff) ? 0x7fff : ((level < -0x7fff) ? -0x7fff : level);
+}
+
+static inline int mlnx_is_conditional(const struct ff_effect *effect)
+{
+	return (effect->type == FF_DAMPER) || (effect->type == FF_FRICTION) || (effect->type == FF_INERTIA) || (effect->type == FF_SPRING);
+}
+
+static void mlnx_set_envelope_times(struct mlnx_effect *mlnxeff)
+{
+	struct ff_effect *effect = mlnxeff->effect;
+	switch (effect->type) {
+	case FF_CONSTANT:
+		if (effect->u.constant.envelope.attack_length)
+			mlnxeff->attack_stop = mlnxeff->begin_at + msecs_to_jiffies(effect->u.constant.envelope.attack_length);
+		if (effect->replay.length && effect->u.constant.envelope.fade_length)
+			mlnxeff->fade_begin = mlnxeff->stop_at - msecs_to_jiffies(effect->u.constant.envelope.fade_length);
+		break;
+	case FF_PERIODIC:
+		pr_debug("Phase: %u, Offset: %d\n", effect->u.periodic.phase, effect->u.periodic.offset);
+		if (effect->u.periodic.envelope.attack_length)
+			mlnxeff->attack_stop = mlnxeff->begin_at + msecs_to_jiffies(effect->u.periodic.envelope.attack_length);
+		if (effect->replay.length && effect->u.periodic.envelope.fade_length)
+			mlnxeff->fade_begin = mlnxeff->stop_at - msecs_to_jiffies(effect->u.periodic.envelope.fade_length);
+		break;
+	case FF_RAMP:
+		if (effect->u.ramp.envelope.attack_length)
+			mlnxeff->attack_stop = mlnxeff->begin_at + msecs_to_jiffies(effect->u.ramp.envelope.attack_length);
+		if (effect->replay.length && effect->u.ramp.envelope.fade_length)
+			mlnxeff->fade_begin = mlnxeff->stop_at - msecs_to_jiffies(effect->u.ramp.envelope.fade_length);
+		break;
+	default:
+		break;
+	}
+}
+
+static void mlnx_set_trip_times(struct mlnx_effect *mlnxeff, const unsigned long now)
+{
+	mlnxeff->begin_at = now + msecs_to_jiffies(mlnxeff->effect->replay.delay);
+	mlnxeff->stop_at = mlnxeff->begin_at + msecs_to_jiffies(mlnxeff->effect->replay.length);
+	mlnxeff->updated_at = mlnxeff->begin_at;
+	if (mlnxeff->effect->type == FF_PERIODIC) {
+		mlnxeff->playback_time = mlnxeff->effect->u.periodic.phase;
+		/* Adjust periodic effects phase accordingly to Microsoft DirectInput specification */
+		switch (mlnxeff->effect->u.periodic.waveform) {
+		case FF_TRIANGLE:
+			mlnxeff->playback_time += mlnxeff->effect->u.periodic.period / 4;
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+static void mlnx_start_effect(struct mlnx_device *mlnxdev, struct mlnx_effect *mlnxeff)
+{
+	const unsigned long now = jiffies;
+
+	mlnx_set_trip_times(mlnxeff, now);
+	mlnx_set_envelope_times(mlnxeff);
+	__set_bit(FF_EFFECT_STARTED, &mlnxeff->flags);
+}
+
+static void mlnx_stop_effect(struct mlnx_device *mlnxdev, struct mlnx_effect *mlnxeff, const int idx)
+{
+	switch (mlnxeff->effect->type) {
+	case FF_CONSTANT:
+	case FF_PERIODIC:
+		if (--mlnxdev->combinable_playing == 0) {
+			const struct mlnx_effect_command c = { .cmd = MLNX_STOP_COMBINED };
+			mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &c);
+		}
+		return;
+	case FF_DAMPER:
+	case FF_FRICTION:
+	case FF_INERTIA:
+	case FF_SPRING:
+	{
+		const struct mlnx_effect_command c = { .cmd = MLNX_STOP_UNCOMB,
+							 .u.uncomb.id = idx,
+							 .u.uncomb.effect = mlnxeff->effect };
+		mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &c);
+		return;
+	}
+	default:
+		return;
+	}
+}
+
+static const struct ff_envelope *mlnx_get_envelope(const struct ff_effect *effect)
+{
+	static const struct ff_envelope empty;
+
+	switch (effect->type) {
+	case FF_CONSTANT:
+		return &effect->u.constant.envelope;
+	case FF_PERIODIC:
+		return &effect->u.periodic.envelope;
+	case FF_RAMP:
+		return &effect->u.ramp.envelope;
+	default:
+		return &empty;
+	}
+}
+
+static s32 mlnx_apply_envelope(const struct mlnx_effect *mlnxeff, const s32 level)
+{
+	const struct ff_effect *effect = mlnxeff->effect;
+	const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+	const unsigned long now = jiffies;
+	const s32 alevel = abs(level);
+
+	/* Effect has an envelope with nonzero attack time */
+	if (envelope->attack_length && time_before(now, mlnxeff->attack_stop)) {
+		const s32 into_trans_msecs = jiffies_to_msecs(now - mlnxeff->begin_at);
+		const s32 trans_length = envelope->attack_length;
+		const s32 dlevel = (alevel - envelope->attack_level) * into_trans_msecs / trans_length;
+		pr_debug("Effect is attacking\n");
+		pr_debug("Envelope orig: %d, dlevel: %d, into: %d, length: %d\n", level, dlevel, into_trans_msecs, trans_length);
+		return level < 0 ? -(dlevel + envelope->attack_level) : (dlevel + envelope->attack_level);
+	} else if (envelope->fade_length && time_before_eq(mlnxeff->fade_begin, now)) {
+		const s32 into_trans_msecs = jiffies_to_msecs(now - mlnxeff->fade_begin);
+		const s32 trans_length = envelope->fade_length;
+		const s32 dlevel = (envelope->fade_level - alevel) * into_trans_msecs / trans_length;
+		pr_debug("Effect is fading\n");
+		pr_debug("Envelope orig: %d, dlevel: %d, into: %d, length: %d\n", level, dlevel, into_trans_msecs, trans_length);
+		return level < 0 ? -(dlevel + alevel) : (dlevel + alevel);
+	}
+
+	return level;
+}
+
+static s32 mlnx_calculate_periodic(struct mlnx_effect *mlnxeff, const s32 level)
+{
+	const struct ff_effect *effect = mlnxeff->effect;
+	const unsigned long now = jiffies;
+	const u16 period = effect->u.periodic.period;
+	const unsigned long dt = jiffies_to_msecs(now - mlnxeff->updated_at);
+	s32 new = level;
+	unsigned long n_periods;
+	u16 t;
+
+	mlnxeff->playback_time += dt;
+	mlnxeff->playback_time &= 0x7fff; /* Make sure we don't exceed the max allowed period */
+	n_periods = mlnxeff->playback_time / period;
+	t = mlnxeff->playback_time - (n_periods * period);
+
+	switch (effect->u.periodic.waveform) {
+	case FF_SINE:
+	{
+		u16 degrees = (360 * t) / period;
+		new = ((level * fixp_sin(degrees)) >> FRAC_N) + effect->u.periodic.offset;
+		break;
+	}
+	case FF_SQUARE:
+	{
+		u16 degrees = (360 * t) / period;
+		new = level * (degrees < 180 ? 1 : -1);
+		break;
+	}
+	case FF_SAW_UP:
+		new = 2 * level * t / period - level + effect->u.periodic.offset;
+		break;
+	case FF_SAW_DOWN:
+		new = level - 2 * level * t / period + effect->u.periodic.offset;
+		break;
+	case FF_TRIANGLE:
+	{
+		/* Fixed-point implementation of A = 1 - 4 * ABS(0.5 - FRAC(0.5x + 0.25)) */
+		s32 a = abs(0x4000 - (((0x8000 * t / period) + 0x2000) & 0x7fff));
+		new = ((level * (0x8000 - 4 * a)) >> 15) + effect->u.periodic.offset;
+		break;
+	}
+	case FF_CUSTOM:
+		pr_debug("Custom waveform is not handled by this driver.\n");
+		return level;
+	default:
+		pr_debug("Invalid waveform.\n");
+		return level;
+	}
+
+	new = mlnx_clamp_level(new); /* Make sure that the offset did not make the value exceed the s16 range */
+	pr_debug("level: %d, playback: %u, t: %u, dt: %lu\n", new, mlnxeff->playback_time, t, dt);
+	return new;
+}
+
+static s32 mlnx_calculate_ramp(const struct mlnx_effect *mlnxeff)
+{
+	const struct ff_effect *effect = mlnxeff->effect;
+	const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+	const unsigned long now = jiffies;
+	const u16 length = effect->replay.length;
+	s16 start = effect->u.ramp.start_level;
+	s16 end = effect->u.ramp.end_level;
+	u16 t = jiffies_to_msecs(now - mlnxeff->begin_at);
+	s32 new;
+
+	/* Effect has an envelope with nonzero attack time
+	 * If the envelope is attacking, adjust "start", if the
+	 * effect is fading, adjust "end". */
+	if (envelope->attack_length && time_before(now, mlnxeff->attack_stop)) {
+		const s32 into_trans_msecs = jiffies_to_msecs(now - mlnxeff->begin_at);
+		const s32 trans_length = envelope->attack_length;
+		const s32 dlevel = (abs(start) - envelope->attack_level) * into_trans_msecs / trans_length;
+		start = start < 0 ? -(dlevel + envelope->attack_level) : (dlevel + envelope->attack_level);
+		pr_debug("RA Envelope orig: %d, dlevel: %d, into: %d, length: %d\n", start, dlevel, into_trans_msecs, trans_length);
+	} else if (envelope->fade_length && time_before_eq(mlnxeff->fade_begin, now)) {
+		const s32 into_trans_msecs = jiffies_to_msecs(now - mlnxeff->fade_begin);
+		const s32 trans_length = envelope->fade_length;
+		const s32 dlevel = (envelope->fade_level - abs(end)) * into_trans_msecs / trans_length;
+		end = end < 0 ? -(dlevel + abs(end)) : (dlevel + abs(end));
+		pr_debug("RA Envelope orig: %d, dlevel: %d, into: %d, length: %d\n", end, dlevel, into_trans_msecs, trans_length);
+	}
+
+	new = ((end - start) * t) / (length) + start;
+	pr_debug("RAMP level: %d, t: %u\n", new, t);
+	return new;
+}
+
+static void mlnx_destroy(struct ff_device *dev)
+{
+	struct mlnx_device *mlnxdev = dev->private;
+	del_timer_sync(&mlnxdev->timer);
+
+	kfree(mlnxdev->private);
+}
+
+static unsigned long mlnx_get_envelope_update_time(const struct mlnx_effect *mlnxeff, const unsigned long update_rate_jiffies)
+{
+	const struct ff_effect *effect = mlnxeff->effect;
+	const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+	const unsigned long now = jiffies;
+	unsigned long fade_next;
+
+	/* Effect has an envelope with nonzero attack time */
+	if (envelope->attack_length && time_before(now, mlnxeff->attack_stop)) {
+		pr_debug("Attack stop: %lu\n", mlnxeff->attack_stop);
+		if (time_before(mlnxeff->updated_at + update_rate_jiffies, mlnxeff->attack_stop))
+			return mlnxeff->updated_at + update_rate_jiffies;
+		else
+			return mlnxeff->attack_stop;
+	}
+
+	/* Effect has an envelope with nonzero fade time */
+	if (mlnxeff->effect->replay.length) {
+		if (envelope->fade_length) {
+
+			/* Schedule the next update when the fade begins */
+			if (time_before(mlnxeff->updated_at, mlnxeff->fade_begin))
+				return mlnxeff->fade_begin;
+
+			/* Already fading */
+			else if (time_before(mlnxeff->fade_begin, now)) {
+				fade_next = mlnxeff->updated_at + update_rate_jiffies;
+				if (time_after(fade_next, mlnxeff->stop_at))
+					return mlnxeff->stop_at; /* Schedule update when the effect stops */
+				else
+					return fade_next;      /* Schedule update at the next checkpoint */
+			}
+		} else
+			return mlnxeff->stop_at;
+	}
+
+	/* There is no envelope */
+	if (mlnxeff->begin_at == now && test_bit(FF_EFFECT_PLAYING, &mlnxeff->flags))
+		return now - 1; /* Prevent the effect from being started twice */
+	else
+		return mlnxeff->begin_at;
+}
+
+static unsigned long mlnx_get_update_time(const struct mlnx_effect *mlnxeff, const unsigned long update_rate_jiffies)
+{
+	const unsigned long now = jiffies;
+	unsigned long time, update_periodic;
+
+	switch (mlnxeff->effect->type) {
+	/* Constant effect does not change with time, but it can have an envelope and a duration */
+	case FF_CONSTANT:
+		return mlnx_get_envelope_update_time(mlnxeff, update_rate_jiffies);
+	/* Periodic and ramp effects have to be periodically updated */
+	case FF_PERIODIC:
+	case FF_RAMP:
+		time = mlnx_get_envelope_update_time(mlnxeff, update_rate_jiffies);
+
+		if (mlnxeff->effect->type == FF_PERIODIC && mlnxeff->effect->u.periodic.waveform == FF_SQUARE)
+			update_periodic = msecs_to_jiffies(mlnxeff->effect->u.periodic.period / 2) + mlnxeff->updated_at;
+		else
+			update_periodic = mlnxeff->updated_at + update_rate_jiffies;
+
+		/* Periodic effect has to be updated earlier than envelope or envelope update time is in the past */
+		if (time_before(update_periodic, time) || time_before(time, now))
+			return update_periodic;
+		else /* Envelope needs to be updated */
+			return time;
+	case FF_DAMPER:
+	case FF_FRICTION:
+	case FF_INERTIA:
+	case FF_SPRING:
+	default:
+		if (time_after_eq(mlnxeff->begin_at, now))
+			return mlnxeff->begin_at;
+		else
+			return mlnxeff->stop_at;
+	}
+}
+
+static void mlnx_schedule_playback(struct mlnx_device *mlnxdev)
+{
+	struct mlnx_effect *mlnxeff;
+	const unsigned long now = jiffies;
+	int events = 0;
+	int i;
+	unsigned long earliest = 0;
+	unsigned long time;
+
+	/* Iterate over all effects and determine the earliest time when we have to attend to any */
+	for (i = 0; i < FF_MAX_EFFECTS; i++) {
+		mlnxeff = &mlnxdev->effects[i];
+
+		if (!test_bit(FF_EFFECT_STARTED, &mlnxeff->flags))
+			continue; /* Effect is not started, skip it */
+
+		if (test_bit(FF_EFFECT_PLAYING, &mlnxeff->flags))
+			time = mlnx_get_update_time(mlnxeff, mlnxdev->update_rate_jiffies);
+		else
+			time = mlnxeff->begin_at;
+
+		pr_debug("Update time for effect %d: %lu\n", i, time);
+
+		/* Scheduled time is in the future and is either before the current earliest time
+		 * or it is the first valid time value in this pass */
+		if (time_before_eq(now, time) && (++events == 1 || time_before(time, earliest)))
+			earliest = time;
+	}
+
+	if (events) {
+		pr_debug("Events: %d, earliest: %lu\n", events, earliest);
+		mod_timer(&mlnxdev->timer, earliest);
+	}
+}
+
+static void mlnx_add_force(struct mlnx_effect *mlnxeff, s32 *cfx, s32 *cfy, const u16 gain)
+{
+	u16 direction;
+	s32 level;
+
+	pr_debug("Processing effect type %d, ID %d\n", mlnxeff->effect->type, mlnxeff->effect->id);
+
+	direction = mlnxeff->effect->direction * 360 / 0xffff;
+	pr_debug("Direction deg: %u\n", direction);
+
+	switch (mlnxeff->effect->type) {
+	case FF_CONSTANT:
+		level = mlnx_apply_envelope(mlnxeff, mlnxeff->effect->u.constant.level);
+		break;
+	case FF_PERIODIC:
+		level = mlnx_apply_envelope(mlnxeff, mlnxeff->effect->u.periodic.magnitude);
+		level = mlnx_calculate_periodic(mlnxeff, level);
+		break;
+	case FF_RAMP:
+		level = mlnx_calculate_ramp(mlnxeff);
+		break;
+	default:
+		pr_debug("Effect %d is not handled by mlnx_add_force, this is probably a bug!\n", mlnxeff->effect->type);
+		return;
+	}
+
+	*cfx += mlnx_calculate_x_force(level, direction) * gain / 0xffff;
+	*cfy += mlnx_calculate_y_force(level, direction) * gain / 0xffff;
+}
+
+static void mlnx_play_effects(struct mlnx_device *mlnxdev)
+{
+	const u16 gain = mlnxdev->gain;
+	const unsigned long now = jiffies;
+	int i;
+	int cfx = 0;
+	int cfy = 0;
+
+	for (i = 0; i < FF_MAX_EFFECTS; i++) {
+		struct mlnx_effect *mlnxeff = &mlnxdev->effects[i];
+
+		if (!test_bit(FF_EFFECT_STARTED, &mlnxeff->flags)) {
+			pr_debug("Effect %hd/%d not started\n", mlnxeff->effect->id, i);
+			continue;
+		}
+
+		if (time_before(now, mlnxeff->begin_at)) {
+			pr_debug("Effect %hd/%d begins at a later time\n", mlnxeff->effect->id, i);
+			continue;
+		}
+
+		if (time_before_eq(mlnxeff->stop_at, now) && mlnxeff->effect->replay.length) {
+			pr_debug("Effect %hd/%d has to be stopped\n", mlnxeff->effect->id, i);
+
+			/* If the effect should be repeated, reset it */
+			if (--mlnxeff->repeat > 0) {
+				__clear_bit(FF_EFFECT_PLAYING, &mlnxeff->flags);
+				mlnxeff->begin_at = mlnxeff->stop_at + msecs_to_jiffies(mlnxeff->effect->replay.delay);
+				mlnxeff->stop_at = mlnxeff->begin_at + msecs_to_jiffies(mlnxeff->effect->replay.length);
+			} else /* The effect has to be stopped */
+				mlnx_stop_effect(mlnxdev, mlnxeff, i);
+
+			continue;
+		}
+
+		switch (mlnxeff->effect->type) {
+		case FF_CONSTANT:
+		case FF_PERIODIC:
+		case FF_RAMP:
+			if (!test_and_set_bit(FF_EFFECT_PLAYING, &mlnxeff->flags)) {
+				mlnxdev->combinable_playing++;
+				pr_debug("Starting combinable effect, total %u\n", mlnxdev->combinable_playing);
+			}
+			mlnx_add_force(mlnxeff, &cfx, &cfy, gain);
+			break;
+		case FF_DAMPER:
+		case FF_FRICTION:
+		case FF_INERTIA:
+		case FF_SPRING:
+			if (!test_and_set_bit(FF_EFFECT_PLAYING, &mlnxeff->flags)) {
+				const struct mlnx_effect_command ecmd = { .cmd = MLNX_START_UNCOMB,
+									  .u.uncomb.id = i,
+									  .u.uncomb.effect = mlnxeff->effect };
+				mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &ecmd);
+			}
+			break;
+		default:
+			pr_debug("Unhandled type of effect.\n");
+		}
+		mlnxeff->updated_at = now;
+	}
+
+	if (mlnxdev->combinable_playing) {
+		const struct mlnx_effect_command ecmd = { .cmd = MLNX_START_COMBINED,
+							  .u.simple_force = { .x = mlnx_clamp_level(cfx), .y = mlnx_clamp_level(cfy) } };
+		mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &ecmd);
+	}
+
+	mlnx_schedule_playback(mlnxdev);
+}
+
+static void mlnx_set_gain(struct input_dev *dev, u16 gain)
+{
+	struct mlnx_device *mlnxdev = dev->ff->private;
+	int i;
+
+	mlnxdev->gain = gain;
+
+	for (i = 0; i < FF_MAX_EFFECTS; i++)
+		__clear_bit(FF_EFFECT_PLAYING, &mlnxdev->effects[i].flags);
+
+	mlnx_play_effects(mlnxdev);
+}
+
+static int mlnx_startstop(struct input_dev *dev, int effect_id, int repeat)
+{
+	struct mlnx_device *mlnxdev = dev->ff->private;
+	struct mlnx_effect *mlnxeff = &mlnxdev->effects[effect_id];
+
+	if (repeat > 0) {
+		pr_debug("Starting effect ID %d\n", effect_id);
+		mlnxeff->repeat = repeat;
+		mlnx_start_effect(mlnxdev, mlnxeff);
+	} else {
+		pr_debug("Stopping effect ID %d\n", effect_id);
+		if (test_bit(FF_EFFECT_STARTED, &mlnxdev->effects[effect_id].flags)) {
+			if (test_bit(FF_EFFECT_PLAYING, &mlnxdev->effects[effect_id].flags)) {
+				mlnx_stop_effect(mlnxdev, mlnxeff, effect_id);
+				__clear_bit(FF_EFFECT_PLAYING, &mlnxdev->effects[effect_id].flags);
+			}
+			__clear_bit(FF_EFFECT_STARTED, &mlnxdev->effects[effect_id].flags);
+		} else
+			pr_debug("Effect ID %d already stopped.\n", effect_id);
+	}
+
+	mlnx_play_effects(mlnxdev);
+
+	return 0;
+}
+
+static void mlnx_timer_fired(unsigned long data)
+{
+	struct input_dev *dev = (struct input_dev *)data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	mlnx_play_effects(dev->ff->private);
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static int mlnx_upload(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old)
+{
+	struct mlnx_device *mlnxdev = dev->ff->private;
+	struct mlnx_effect *mlnxeff = &mlnxdev->effects[effect->id];
+	const unsigned long now = jiffies;
+	int ret, fade_from;
+
+	pr_debug("Uploading effect type %d, ID %d\n", effect->type, effect->id);
+
+	if (effect->type == FF_PERIODIC) {
+		if (!effect->u.periodic.period)
+			return -EINVAL;
+	}
+
+	if (effect->type == FF_CONSTANT || effect->type == FF_PERIODIC || effect->type == FF_RAMP) {
+		const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+
+		/* Infinite effects cannot fade */
+		if (effect->replay.length == 0 && envelope->fade_length > 0)
+			return -EINVAL;
+		/* Fade length cannot be greater than effect duration */
+		fade_from = (int)effect->replay.length - (int)envelope->fade_length;
+		if (fade_from < 0)
+			return -EINVAL;
+		/* Envelope cannot start fading before it finishes attacking */
+		if (fade_from < envelope->attack_length)
+			return -EINVAL;
+	}
+
+	spin_lock_irq(&dev->event_lock);
+	/* Check if the effect being modified is playing */
+	if (test_bit(FF_EFFECT_STARTED, &mlnxeff->flags)) {
+		/* Set the effect to stopped state */
+		__clear_bit(FF_EFFECT_STARTED, &mlnxeff->flags);
+		mlnx_set_trip_times(mlnxeff, now);
+		mlnx_set_envelope_times(mlnxeff);
+		mlnx_schedule_playback(mlnxdev);
+	}
+
+	/* Some devices might have a limit on how many uncombinable effect can be played at once */
+	if (mlnx_is_conditional(effect)) {
+		struct mlnx_effect_command ecmd = { .cmd = MLNX_CAN_PLAY,
+						    .u.uncomb.id = effect->id,
+						    .u.uncomb.effect = effect };
+		ret = mlnxdev->control_effect(dev, mlnxdev->private, &ecmd);
+		if (ret) {
+			pr_debug("Device rejected effect\n");
+			spin_unlock_irq(&dev->event_lock);
+			return ret;
+		}
+	}
+
+	spin_unlock_irq(&dev->event_lock);
+
+	return 0;
+}
+
+int input_ff_create_mlnx(struct input_dev *dev, void *data, int(*control_effect)(struct input_dev *, void *, const struct mlnx_effect_command *),
+			 const u16 update_rate)
+{
+	struct mlnx_device *mlnxdev;
+	int ret;
+	int i;
+
+	if (update_rate < FF_MIN_UPDATE_RATE_MSECS)
+		return -EINVAL;
+
+	mlnxdev = kzalloc(sizeof(struct mlnx_device), GFP_KERNEL);
+	if (!mlnxdev)
+		return -ENOMEM;
+
+	mlnxdev->dev = dev;
+	mlnxdev->private = data;
+	mlnxdev->control_effect = control_effect;
+	mlnxdev->gain = 0xffff;
+	mlnxdev->update_rate_jiffies = msecs_to_jiffies(update_rate);
+	setup_timer(&mlnxdev->timer, mlnx_timer_fired, (unsigned long)dev);
+
+	ret = input_ff_create(dev, FF_MAX_EFFECTS);
+	if (ret) {
+		kfree(mlnxdev);
+		return ret;
+	}
+
+	dev->ff->private = mlnxdev;
+	dev->ff->upload = mlnx_upload;
+	dev->ff->set_gain = mlnx_set_gain;
+	dev->ff->destroy = mlnx_destroy;
+	dev->ff->playback = mlnx_startstop;
+
+	/* Link mlnxdev->effects to dev->ff->effects */
+	for (i = 0; i < FF_MAX_EFFECTS; i++)
+		mlnxdev->effects[i].effect = &dev->ff->effects[i];
+
+	pr_debug("MLNX: Device successfully registered.\n");
+	return 0;
+}
+EXPORT_SYMBOL_GPL(input_ff_create_mlnx);
diff --git a/include/linux/input/ff-memless-next.h b/include/linux/input/ff-memless-next.h
new file mode 100644
index 0000000..0711c1c
--- /dev/null
+++ b/include/linux/input/ff-memless-next.h
@@ -0,0 +1,29 @@
+#include <linux/input.h>
+
+enum mlnx_commands {
+	MLNX_START_COMBINED,
+	MLNX_STOP_COMBINED,
+	MLNX_START_UNCOMB,
+	MLNX_STOP_UNCOMB,
+	MLNX_CAN_PLAY
+};
+
+struct mlnx_simple_force {
+	const s32 x;
+	const s32 y;
+};
+
+struct mlnx_uncomb_effect {
+	const int id;
+	const struct ff_effect *effect;
+};
+
+struct mlnx_effect_command {
+	const enum mlnx_commands cmd;
+	union {
+		const struct mlnx_simple_force simple_force;
+		const struct mlnx_uncomb_effect uncomb;
+	} u;
+};
+
+int input_ff_create_mlnx(struct input_dev *dev, void *data, int(*control_effect)(struct input_dev *, void *, const struct mlnx_effect_command *), const u16 update_rate);
-- 
1.8.5.1


--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [RFC] Add ff-memless-next driver
From: Michal Malý @ 2013-12-14 20:58 UTC (permalink / raw)
  To: dmitry.torokhov; +Cc: linux-kernel, linux-input, elias.vds, anssi.hannula

Hi,

"ff-memless-next" driver is an extended modification of the original ff-memless driver. Unlike ff-memless, ff-memless-next targets only "serious" FFB devices such as racing wheels and hi(ish)-end joysticks with FFB actuators instead of simple rumble motors. Modifications in ff-memless-next include:
- Support of periodic and ramp effects
  These are treated as "combinable" effects and forces created by each of these effects is superposed into one total force
- Support for conditional effects
  As these effects cannot be effectively combined together, they are handled separately depending on the capabilities of the underlying HW-specific driver
- Removed emulation of rumble effect
- Adjustable update rate
- Differentiation between "set effect's force to zero" and "stop effect"
- Checks whether the effect's parameters are valid upon upload - this should better be handled by ff-core though IMHO.

At the moment there is no HW-specific backend that would make use of ff-memless-next, the plan is to update hid-lg4ff once ff-memless-next gets accepted by upstream. ff-memless-next can be tested with dummy FFB device module which is available here (git://prifuk.cz/ff-dummy-device).

My primary motivation to write ff-memless-next were limitations of the current ff-memless driver I came across during work on the driver for Logitech gaming wheels. However, ff-memless-next contains no code that would specifically target Logitech devices.

I must give a huge thanks to Elias Vanderstuyft (CC'd) for his extensive testing of the driver and numerous helpful suggestions.

Tested-by: Elias Vanderstuyft <elias.vds@gmail.com>
Signed-off-by: Michal Malý <madcatxster@prifuk.cz>
---
From 12c7feb547ff16d91f6d04986862eaf5f266ddeb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michal=20Mal=C3=BD?= <madcatxster@prifuk.cz>
Date: Sat, 14 Dec 2013 21:53:26 +0100
Subject: [PATCH 1/1] Add ff-memless-next driver

---
 drivers/input/Kconfig                 |  12 +
 drivers/input/Makefile                |   1 +
 drivers/input/ff-memless-next.c       | 675 ++++++++++++++++++++++++++++++++++
 include/linux/input/ff-memless-next.h |  29 ++
 4 files changed, 717 insertions(+)
 create mode 100644 drivers/input/ff-memless-next.c
 create mode 100644 include/linux/input/ff-memless-next.h

diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index a11ff74..893ab00 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -77,6 +77,18 @@ config INPUT_MATRIXKMAP
 	  To compile this driver as a module, choose M here: the
 	  module will be called matrix-keymap.
 
+config INPUT_FF_MEMLESS_NEXT
+	tristate "New version of support for memoryless force feedback devices"
+	help
+	  Say Y here if you want to enable support for various memoryless
+	  force feedback devices (as of now there is no hardware-specific
+	  driver that supports this)
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ff-memless-next.
+
 comment "Userland interfaces"
 
 config INPUT_MOUSEDEV
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 5ca3f63..169e99c 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_INPUT_FF_MEMLESS)	+= ff-memless.o
 obj-$(CONFIG_INPUT_POLLDEV)	+= input-polldev.o
 obj-$(CONFIG_INPUT_SPARSEKMAP)	+= sparse-keymap.o
 obj-$(CONFIG_INPUT_MATRIXKMAP)	+= matrix-keymap.o
+obj-$(CONFIG_INPUT_FF_MEMLESS_NEXT)	+= ff-memless-next.o
 
 obj-$(CONFIG_INPUT_MOUSEDEV)	+= mousedev.o
 obj-$(CONFIG_INPUT_JOYDEV)	+= joydev.o
diff --git a/drivers/input/ff-memless-next.c b/drivers/input/ff-memless-next.c
new file mode 100644
index 0000000..23727b2
--- /dev/null
+++ b/drivers/input/ff-memless-next.c
@@ -0,0 +1,675 @@
+/*
+ *
+ * Force feedback support for memory-less devices
+ *
+ * This module is based on "ff-memless" orignally written by Anssi Hannula.
+ * It is extented to support all force feedback effects currently supported
+ * by the Linux input stack. All effects except FF_RAMP are handled in accordance with
+ * Microsoft DirectInput specification valid at the time the module was written.
+ *
+ * Copyright(c) 2013 Michal Maly <madcatxster@prifuk.cz>
+ *
+ */
+
+/*
+ * 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 the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/jiffies.h>
+#include <linux/fixp-arith.h>
+#include <linux/input/ff-memless-next.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michal \"MadCatX\" Maly");
+MODULE_DESCRIPTION("Force feedback support for memoryless force feedback devices");
+
+#define FF_MAX_EFFECTS 16
+#define FF_MIN_UPDATE_RATE_MSECS 5
+
+#define FF_EFFECT_STARTED 0x0
+#define FF_EFFECT_PLAYING 0x1
+
+
+struct mlnx_effect {
+	struct ff_effect *effect;
+	unsigned long flags;
+	unsigned long begin_at;
+	unsigned long stop_at;
+	unsigned long updated_at;
+	unsigned long attack_stop;
+	unsigned long fade_begin;
+	int repeat;
+	u16 playback_time;
+};
+
+struct mlnx_device {
+	u8 combinable_playing;
+	unsigned long update_rate_jiffies;
+	void *private;
+	struct mlnx_effect effects[FF_MAX_EFFECTS];
+	int gain;
+	struct timer_list timer;
+	struct input_dev *dev;
+
+	int (*control_effect)(struct input_dev *, void *, const struct mlnx_effect_command *);
+};
+
+static inline s32 mlnx_calculate_x_force(const s32 level, const u16 direction)
+{
+	s32 new = (level * -fixp_sin(direction)) >> FRAC_N;
+	pr_debug("x force: %d\n", new);
+	return new;
+}
+
+static inline s32 mlnx_calculate_y_force(const s32 level, const u16 direction)
+{
+	s32 new = (level * -fixp_cos(direction)) >> FRAC_N;
+	pr_debug("y force: %d\n", new);
+	return new;
+}
+
+static inline s32 mlnx_clamp_level(const s32 level)
+{
+	return (level > 0x7fff) ? 0x7fff : ((level < -0x7fff) ? -0x7fff : level);
+}
+
+static inline int mlnx_is_conditional(const struct ff_effect *effect)
+{
+	return (effect->type == FF_DAMPER) || (effect->type == FF_FRICTION) || (effect->type == FF_INERTIA) || (effect->type == FF_SPRING);
+}
+
+static void mlnx_set_envelope_times(struct mlnx_effect *mlnxeff)
+{
+	struct ff_effect *effect = mlnxeff->effect;
+	switch (effect->type) {
+	case FF_CONSTANT:
+		if (effect->u.constant.envelope.attack_length)
+			mlnxeff->attack_stop = mlnxeff->begin_at + msecs_to_jiffies(effect->u.constant.envelope.attack_length);
+		if (effect->replay.length && effect->u.constant.envelope.fade_length)
+			mlnxeff->fade_begin = mlnxeff->stop_at - msecs_to_jiffies(effect->u.constant.envelope.fade_length);
+		break;
+	case FF_PERIODIC:
+		pr_debug("Phase: %u, Offset: %d\n", effect->u.periodic.phase, effect->u.periodic.offset);
+		if (effect->u.periodic.envelope.attack_length)
+			mlnxeff->attack_stop = mlnxeff->begin_at + msecs_to_jiffies(effect->u.periodic.envelope.attack_length);
+		if (effect->replay.length && effect->u.periodic.envelope.fade_length)
+			mlnxeff->fade_begin = mlnxeff->stop_at - msecs_to_jiffies(effect->u.periodic.envelope.fade_length);
+		break;
+	case FF_RAMP:
+		if (effect->u.ramp.envelope.attack_length)
+			mlnxeff->attack_stop = mlnxeff->begin_at + msecs_to_jiffies(effect->u.ramp.envelope.attack_length);
+		if (effect->replay.length && effect->u.ramp.envelope.fade_length)
+			mlnxeff->fade_begin = mlnxeff->stop_at - msecs_to_jiffies(effect->u.ramp.envelope.fade_length);
+		break;
+	default:
+		break;
+	}
+}
+
+static void mlnx_set_trip_times(struct mlnx_effect *mlnxeff, const unsigned long now)
+{
+	mlnxeff->begin_at = now + msecs_to_jiffies(mlnxeff->effect->replay.delay);
+	mlnxeff->stop_at = mlnxeff->begin_at + msecs_to_jiffies(mlnxeff->effect->replay.length);
+	mlnxeff->updated_at = mlnxeff->begin_at;
+	if (mlnxeff->effect->type == FF_PERIODIC) {
+		mlnxeff->playback_time = mlnxeff->effect->u.periodic.phase;
+		/* Adjust periodic effects phase accordingly to Microsoft DirectInput specification */
+		switch (mlnxeff->effect->u.periodic.waveform) {
+		case FF_TRIANGLE:
+			mlnxeff->playback_time += mlnxeff->effect->u.periodic.period / 4;
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+static void mlnx_start_effect(struct mlnx_device *mlnxdev, struct mlnx_effect *mlnxeff)
+{
+	const unsigned long now = jiffies;
+
+	mlnx_set_trip_times(mlnxeff, now);
+	mlnx_set_envelope_times(mlnxeff);
+	__set_bit(FF_EFFECT_STARTED, &mlnxeff->flags);
+}
+
+static void mlnx_stop_effect(struct mlnx_device *mlnxdev, struct mlnx_effect *mlnxeff, const int idx)
+{
+	switch (mlnxeff->effect->type) {
+	case FF_CONSTANT:
+	case FF_PERIODIC:
+		if (--mlnxdev->combinable_playing == 0) {
+			const struct mlnx_effect_command c = { .cmd = MLNX_STOP_COMBINED };
+			mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &c);
+		}
+		return;
+	case FF_DAMPER:
+	case FF_FRICTION:
+	case FF_INERTIA:
+	case FF_SPRING:
+	{
+		const struct mlnx_effect_command c = { .cmd = MLNX_STOP_UNCOMB,
+							 .u.uncomb.id = idx,
+							 .u.uncomb.effect = mlnxeff->effect };
+		mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &c);
+		return;
+	}
+	default:
+		return;
+	}
+}
+
+static const struct ff_envelope *mlnx_get_envelope(const struct ff_effect *effect)
+{
+	static const struct ff_envelope empty;
+
+	switch (effect->type) {
+	case FF_CONSTANT:
+		return &effect->u.constant.envelope;
+	case FF_PERIODIC:
+		return &effect->u.periodic.envelope;
+	case FF_RAMP:
+		return &effect->u.ramp.envelope;
+	default:
+		return &empty;
+	}
+}
+
+static s32 mlnx_apply_envelope(const struct mlnx_effect *mlnxeff, const s32 level)
+{
+	const struct ff_effect *effect = mlnxeff->effect;
+	const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+	const unsigned long now = jiffies;
+	const s32 alevel = abs(level);
+
+	/* Effect has an envelope with nonzero attack time */
+	if (envelope->attack_length && time_before(now, mlnxeff->attack_stop)) {
+		const s32 into_trans_msecs = jiffies_to_msecs(now - mlnxeff->begin_at);
+		const s32 trans_length = envelope->attack_length;
+		const s32 dlevel = (alevel - envelope->attack_level) * into_trans_msecs / trans_length;
+		pr_debug("Effect is attacking\n");
+		pr_debug("Envelope orig: %d, dlevel: %d, into: %d, length: %d\n", level, dlevel, into_trans_msecs, trans_length);
+		return level < 0 ? -(dlevel + envelope->attack_level) : (dlevel + envelope->attack_level);
+	} else if (envelope->fade_length && time_before_eq(mlnxeff->fade_begin, now)) {
+		const s32 into_trans_msecs = jiffies_to_msecs(now - mlnxeff->fade_begin);
+		const s32 trans_length = envelope->fade_length;
+		const s32 dlevel = (envelope->fade_level - alevel) * into_trans_msecs / trans_length;
+		pr_debug("Effect is fading\n");
+		pr_debug("Envelope orig: %d, dlevel: %d, into: %d, length: %d\n", level, dlevel, into_trans_msecs, trans_length);
+		return level < 0 ? -(dlevel + alevel) : (dlevel + alevel);
+	}
+
+	return level;
+}
+
+static s32 mlnx_calculate_periodic(struct mlnx_effect *mlnxeff, const s32 level)
+{
+	const struct ff_effect *effect = mlnxeff->effect;
+	const unsigned long now = jiffies;
+	const u16 period = effect->u.periodic.period;
+	const unsigned long dt = jiffies_to_msecs(now - mlnxeff->updated_at);
+	s32 new = level;
+	unsigned long n_periods;
+	u16 t;
+
+	mlnxeff->playback_time += dt;
+	mlnxeff->playback_time &= 0x7fff; /* Make sure we don't exceed the max allowed period */
+	n_periods = mlnxeff->playback_time / period;
+	t = mlnxeff->playback_time - (n_periods * period);
+
+	switch (effect->u.periodic.waveform) {
+	case FF_SINE:
+	{
+		u16 degrees = (360 * t) / period;
+		new = ((level * fixp_sin(degrees)) >> FRAC_N) + effect->u.periodic.offset;
+		break;
+	}
+	case FF_SQUARE:
+	{
+		u16 degrees = (360 * t) / period;
+		new = level * (degrees < 180 ? 1 : -1);
+		break;
+	}
+	case FF_SAW_UP:
+		new = 2 * level * t / period - level + effect->u.periodic.offset;
+		break;
+	case FF_SAW_DOWN:
+		new = level - 2 * level * t / period + effect->u.periodic.offset;
+		break;
+	case FF_TRIANGLE:
+	{
+		/* Fixed-point implementation of A = 1 - 4 * ABS(0.5 - FRAC(0.5x + 0.25)) */
+		s32 a = abs(0x4000 - (((0x8000 * t / period) + 0x2000) & 0x7fff));
+		new = ((level * (0x8000 - 4 * a)) >> 15) + effect->u.periodic.offset;
+		break;
+	}
+	case FF_CUSTOM:
+		pr_debug("Custom waveform is not handled by this driver.\n");
+		return level;
+	default:
+		pr_debug("Invalid waveform.\n");
+		return level;
+	}
+
+	new = mlnx_clamp_level(new); /* Make sure that the offset did not make the value exceed the s16 range */
+	pr_debug("level: %d, playback: %u, t: %u, dt: %lu\n", new, mlnxeff->playback_time, t, dt);
+	return new;
+}
+
+static s32 mlnx_calculate_ramp(const struct mlnx_effect *mlnxeff)
+{
+	const struct ff_effect *effect = mlnxeff->effect;
+	const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+	const unsigned long now = jiffies;
+	const u16 length = effect->replay.length;
+	s16 start = effect->u.ramp.start_level;
+	s16 end = effect->u.ramp.end_level;
+	u16 t = jiffies_to_msecs(now - mlnxeff->begin_at);
+	s32 new;
+
+	/* Effect has an envelope with nonzero attack time
+	 * If the envelope is attacking, adjust "start", if the
+	 * effect is fading, adjust "end". */
+	if (envelope->attack_length && time_before(now, mlnxeff->attack_stop)) {
+		const s32 into_trans_msecs = jiffies_to_msecs(now - mlnxeff->begin_at);
+		const s32 trans_length = envelope->attack_length;
+		const s32 dlevel = (abs(start) - envelope->attack_level) * into_trans_msecs / trans_length;
+		start = start < 0 ? -(dlevel + envelope->attack_level) : (dlevel + envelope->attack_level);
+		pr_debug("RA Envelope orig: %d, dlevel: %d, into: %d, length: %d\n", start, dlevel, into_trans_msecs, trans_length);
+	} else if (envelope->fade_length && time_before_eq(mlnxeff->fade_begin, now)) {
+		const s32 into_trans_msecs = jiffies_to_msecs(now - mlnxeff->fade_begin);
+		const s32 trans_length = envelope->fade_length;
+		const s32 dlevel = (envelope->fade_level - abs(end)) * into_trans_msecs / trans_length;
+		end = end < 0 ? -(dlevel + abs(end)) : (dlevel + abs(end));
+		pr_debug("RA Envelope orig: %d, dlevel: %d, into: %d, length: %d\n", end, dlevel, into_trans_msecs, trans_length);
+	}
+
+	new = ((end - start) * t) / (length) + start;
+	pr_debug("RAMP level: %d, t: %u\n", new, t);
+	return new;
+}
+
+static void mlnx_destroy(struct ff_device *dev)
+{
+	struct mlnx_device *mlnxdev = dev->private;
+	del_timer_sync(&mlnxdev->timer);
+
+	kfree(mlnxdev->private);
+}
+
+static unsigned long mlnx_get_envelope_update_time(const struct mlnx_effect *mlnxeff, const unsigned long update_rate_jiffies)
+{
+	const struct ff_effect *effect = mlnxeff->effect;
+	const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+	const unsigned long now = jiffies;
+	unsigned long fade_next;
+
+	/* Effect has an envelope with nonzero attack time */
+	if (envelope->attack_length && time_before(now, mlnxeff->attack_stop)) {
+		pr_debug("Attack stop: %lu\n", mlnxeff->attack_stop);
+		if (time_before(mlnxeff->updated_at + update_rate_jiffies, mlnxeff->attack_stop))
+			return mlnxeff->updated_at + update_rate_jiffies;
+		else
+			return mlnxeff->attack_stop;
+	}
+
+	/* Effect has an envelope with nonzero fade time */
+	if (mlnxeff->effect->replay.length) {
+		if (envelope->fade_length) {
+
+			/* Schedule the next update when the fade begins */
+			if (time_before(mlnxeff->updated_at, mlnxeff->fade_begin))
+				return mlnxeff->fade_begin;
+
+			/* Already fading */
+			else if (time_before(mlnxeff->fade_begin, now)) {
+				fade_next = mlnxeff->updated_at + update_rate_jiffies;
+				if (time_after(fade_next, mlnxeff->stop_at))
+					return mlnxeff->stop_at; /* Schedule update when the effect stops */
+				else
+					return fade_next;      /* Schedule update at the next checkpoint */
+			}
+		} else
+			return mlnxeff->stop_at;
+	}
+
+	/* There is no envelope */
+	if (mlnxeff->begin_at == now && test_bit(FF_EFFECT_PLAYING, &mlnxeff->flags))
+		return now - 1; /* Prevent the effect from being started twice */
+	else
+		return mlnxeff->begin_at;
+}
+
+static unsigned long mlnx_get_update_time(const struct mlnx_effect *mlnxeff, const unsigned long update_rate_jiffies)
+{
+	const unsigned long now = jiffies;
+	unsigned long time, update_periodic;
+
+	switch (mlnxeff->effect->type) {
+	/* Constant effect does not change with time, but it can have an envelope and a duration */
+	case FF_CONSTANT:
+		return mlnx_get_envelope_update_time(mlnxeff, update_rate_jiffies);
+	/* Periodic and ramp effects have to be periodically updated */
+	case FF_PERIODIC:
+	case FF_RAMP:
+		time = mlnx_get_envelope_update_time(mlnxeff, update_rate_jiffies);
+
+		if (mlnxeff->effect->type == FF_PERIODIC && mlnxeff->effect->u.periodic.waveform == FF_SQUARE)
+			update_periodic = msecs_to_jiffies(mlnxeff->effect->u.periodic.period / 2) + mlnxeff->updated_at;
+		else
+			update_periodic = mlnxeff->updated_at + update_rate_jiffies;
+
+		/* Periodic effect has to be updated earlier than envelope or envelope update time is in the past */
+		if (time_before(update_periodic, time) || time_before(time, now))
+			return update_periodic;
+		else /* Envelope needs to be updated */
+			return time;
+	case FF_DAMPER:
+	case FF_FRICTION:
+	case FF_INERTIA:
+	case FF_SPRING:
+	default:
+		if (time_after_eq(mlnxeff->begin_at, now))
+			return mlnxeff->begin_at;
+		else
+			return mlnxeff->stop_at;
+	}
+}
+
+static void mlnx_schedule_playback(struct mlnx_device *mlnxdev)
+{
+	struct mlnx_effect *mlnxeff;
+	const unsigned long now = jiffies;
+	int events = 0;
+	int i;
+	unsigned long earliest = 0;
+	unsigned long time;
+
+	/* Iterate over all effects and determine the earliest time when we have to attend to any */
+	for (i = 0; i < FF_MAX_EFFECTS; i++) {
+		mlnxeff = &mlnxdev->effects[i];
+
+		if (!test_bit(FF_EFFECT_STARTED, &mlnxeff->flags))
+			continue; /* Effect is not started, skip it */
+
+		if (test_bit(FF_EFFECT_PLAYING, &mlnxeff->flags))
+			time = mlnx_get_update_time(mlnxeff, mlnxdev->update_rate_jiffies);
+		else
+			time = mlnxeff->begin_at;
+
+		pr_debug("Update time for effect %d: %lu\n", i, time);
+
+		/* Scheduled time is in the future and is either before the current earliest time
+		 * or it is the first valid time value in this pass */
+		if (time_before_eq(now, time) && (++events == 1 || time_before(time, earliest)))
+			earliest = time;
+	}
+
+	if (events) {
+		pr_debug("Events: %d, earliest: %lu\n", events, earliest);
+		mod_timer(&mlnxdev->timer, earliest);
+	}
+}
+
+static void mlnx_add_force(struct mlnx_effect *mlnxeff, s32 *cfx, s32 *cfy, const u16 gain)
+{
+	u16 direction;
+	s32 level;
+
+	pr_debug("Processing effect type %d, ID %d\n", mlnxeff->effect->type, mlnxeff->effect->id);
+
+	direction = mlnxeff->effect->direction * 360 / 0xffff;
+	pr_debug("Direction deg: %u\n", direction);
+
+	switch (mlnxeff->effect->type) {
+	case FF_CONSTANT:
+		level = mlnx_apply_envelope(mlnxeff, mlnxeff->effect->u.constant.level);
+		break;
+	case FF_PERIODIC:
+		level = mlnx_apply_envelope(mlnxeff, mlnxeff->effect->u.periodic.magnitude);
+		level = mlnx_calculate_periodic(mlnxeff, level);
+		break;
+	case FF_RAMP:
+		level = mlnx_calculate_ramp(mlnxeff);
+		break;
+	default:
+		pr_debug("Effect %d is not handled by mlnx_add_force, this is probably a bug!\n", mlnxeff->effect->type);
+		return;
+	}
+
+	*cfx += mlnx_calculate_x_force(level, direction) * gain / 0xffff;
+	*cfy += mlnx_calculate_y_force(level, direction) * gain / 0xffff;
+}
+
+static void mlnx_play_effects(struct mlnx_device *mlnxdev)
+{
+	const u16 gain = mlnxdev->gain;
+	const unsigned long now = jiffies;
+	int i;
+	int cfx = 0;
+	int cfy = 0;
+
+	for (i = 0; i < FF_MAX_EFFECTS; i++) {
+		struct mlnx_effect *mlnxeff = &mlnxdev->effects[i];
+
+		if (!test_bit(FF_EFFECT_STARTED, &mlnxeff->flags)) {
+			pr_debug("Effect %hd/%d not started\n", mlnxeff->effect->id, i);
+			continue;
+		}
+
+		if (time_before(now, mlnxeff->begin_at)) {
+			pr_debug("Effect %hd/%d begins at a later time\n", mlnxeff->effect->id, i);
+			continue;
+		}
+
+		if (time_before_eq(mlnxeff->stop_at, now) && mlnxeff->effect->replay.length) {
+			pr_debug("Effect %hd/%d has to be stopped\n", mlnxeff->effect->id, i);
+
+			/* If the effect should be repeated, reset it */
+			if (--mlnxeff->repeat > 0) {
+				__clear_bit(FF_EFFECT_PLAYING, &mlnxeff->flags);
+				mlnxeff->begin_at = mlnxeff->stop_at + msecs_to_jiffies(mlnxeff->effect->replay.delay);
+				mlnxeff->stop_at = mlnxeff->begin_at + msecs_to_jiffies(mlnxeff->effect->replay.length);
+			} else /* The effect has to be stopped */
+				mlnx_stop_effect(mlnxdev, mlnxeff, i);
+
+			continue;
+		}
+
+		switch (mlnxeff->effect->type) {
+		case FF_CONSTANT:
+		case FF_PERIODIC:
+		case FF_RAMP:
+			if (!test_and_set_bit(FF_EFFECT_PLAYING, &mlnxeff->flags)) {
+				mlnxdev->combinable_playing++;
+				pr_debug("Starting combinable effect, total %u\n", mlnxdev->combinable_playing);
+			}
+			mlnx_add_force(mlnxeff, &cfx, &cfy, gain);
+			break;
+		case FF_DAMPER:
+		case FF_FRICTION:
+		case FF_INERTIA:
+		case FF_SPRING:
+			if (!test_and_set_bit(FF_EFFECT_PLAYING, &mlnxeff->flags)) {
+				const struct mlnx_effect_command ecmd = { .cmd = MLNX_START_UNCOMB,
+									  .u.uncomb.id = i,
+									  .u.uncomb.effect = mlnxeff->effect };
+				mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &ecmd);
+			}
+			break;
+		default:
+			pr_debug("Unhandled type of effect.\n");
+		}
+		mlnxeff->updated_at = now;
+	}
+
+	if (mlnxdev->combinable_playing) {
+		const struct mlnx_effect_command ecmd = { .cmd = MLNX_START_COMBINED,
+							  .u.simple_force = { .x = mlnx_clamp_level(cfx), .y = mlnx_clamp_level(cfy) } };
+		mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &ecmd);
+	}
+
+	mlnx_schedule_playback(mlnxdev);
+}
+
+static void mlnx_set_gain(struct input_dev *dev, u16 gain)
+{
+	struct mlnx_device *mlnxdev = dev->ff->private;
+	int i;
+
+	mlnxdev->gain = gain;
+
+	for (i = 0; i < FF_MAX_EFFECTS; i++)
+		__clear_bit(FF_EFFECT_PLAYING, &mlnxdev->effects[i].flags);
+
+	mlnx_play_effects(mlnxdev);
+}
+
+static int mlnx_startstop(struct input_dev *dev, int effect_id, int repeat)
+{
+	struct mlnx_device *mlnxdev = dev->ff->private;
+	struct mlnx_effect *mlnxeff = &mlnxdev->effects[effect_id];
+
+	if (repeat > 0) {
+		pr_debug("Starting effect ID %d\n", effect_id);
+		mlnxeff->repeat = repeat;
+		mlnx_start_effect(mlnxdev, mlnxeff);
+	} else {
+		pr_debug("Stopping effect ID %d\n", effect_id);
+		if (test_bit(FF_EFFECT_STARTED, &mlnxdev->effects[effect_id].flags)) {
+			if (test_bit(FF_EFFECT_PLAYING, &mlnxdev->effects[effect_id].flags)) {
+				mlnx_stop_effect(mlnxdev, mlnxeff, effect_id);
+				__clear_bit(FF_EFFECT_PLAYING, &mlnxdev->effects[effect_id].flags);
+			}
+			__clear_bit(FF_EFFECT_STARTED, &mlnxdev->effects[effect_id].flags);
+		} else
+			pr_debug("Effect ID %d already stopped.\n", effect_id);
+	}
+
+	mlnx_play_effects(mlnxdev);
+
+	return 0;
+}
+
+static void mlnx_timer_fired(unsigned long data)
+{
+	struct input_dev *dev = (struct input_dev *)data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	mlnx_play_effects(dev->ff->private);
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static int mlnx_upload(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old)
+{
+	struct mlnx_device *mlnxdev = dev->ff->private;
+	struct mlnx_effect *mlnxeff = &mlnxdev->effects[effect->id];
+	const unsigned long now = jiffies;
+	int ret, fade_from;
+
+	pr_debug("Uploading effect type %d, ID %d\n", effect->type, effect->id);
+
+	if (effect->type == FF_PERIODIC) {
+		if (!effect->u.periodic.period)
+			return -EINVAL;
+	}
+
+	if (effect->type == FF_CONSTANT || effect->type == FF_PERIODIC || effect->type == FF_RAMP) {
+		const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+
+		/* Infinite effects cannot fade */
+		if (effect->replay.length == 0 && envelope->fade_length > 0)
+			return -EINVAL;
+		/* Fade length cannot be greater than effect duration */
+		fade_from = (int)effect->replay.length - (int)envelope->fade_length;
+		if (fade_from < 0)
+			return -EINVAL;
+		/* Envelope cannot start fading before it finishes attacking */
+		if (fade_from < envelope->attack_length)
+			return -EINVAL;
+	}
+
+	spin_lock_irq(&dev->event_lock);
+	/* Check if the effect being modified is playing */
+	if (test_bit(FF_EFFECT_STARTED, &mlnxeff->flags)) {
+		/* Set the effect to stopped state */
+		__clear_bit(FF_EFFECT_STARTED, &mlnxeff->flags);
+		mlnx_set_trip_times(mlnxeff, now);
+		mlnx_set_envelope_times(mlnxeff);
+		mlnx_schedule_playback(mlnxdev);
+	}
+
+	/* Some devices might have a limit on how many uncombinable effect can be played at once */
+	if (mlnx_is_conditional(effect)) {
+		struct mlnx_effect_command ecmd = { .cmd = MLNX_CAN_PLAY,
+						    .u.uncomb.id = effect->id,
+						    .u.uncomb.effect = effect };
+		ret = mlnxdev->control_effect(dev, mlnxdev->private, &ecmd);
+		if (ret) {
+			pr_debug("Device rejected effect\n");
+			spin_unlock_irq(&dev->event_lock);
+			return ret;
+		}
+	}
+
+	spin_unlock_irq(&dev->event_lock);
+
+	return 0;
+}
+
+int input_ff_create_mlnx(struct input_dev *dev, void *data, int(*control_effect)(struct input_dev *, void *, const struct mlnx_effect_command *),
+			 const u16 update_rate)
+{
+	struct mlnx_device *mlnxdev;
+	int ret;
+	int i;
+
+	if (update_rate < FF_MIN_UPDATE_RATE_MSECS)
+		return -EINVAL;
+
+	mlnxdev = kzalloc(sizeof(struct mlnx_device), GFP_KERNEL);
+	if (!mlnxdev)
+		return -ENOMEM;
+
+	mlnxdev->dev = dev;
+	mlnxdev->private = data;
+	mlnxdev->control_effect = control_effect;
+	mlnxdev->gain = 0xffff;
+	mlnxdev->update_rate_jiffies = msecs_to_jiffies(update_rate);
+	setup_timer(&mlnxdev->timer, mlnx_timer_fired, (unsigned long)dev);
+
+	ret = input_ff_create(dev, FF_MAX_EFFECTS);
+	if (ret) {
+		kfree(mlnxdev);
+		return ret;
+	}
+
+	dev->ff->private = mlnxdev;
+	dev->ff->upload = mlnx_upload;
+	dev->ff->set_gain = mlnx_set_gain;
+	dev->ff->destroy = mlnx_destroy;
+	dev->ff->playback = mlnx_startstop;
+
+	/* Link mlnxdev->effects to dev->ff->effects */
+	for (i = 0; i < FF_MAX_EFFECTS; i++)
+		mlnxdev->effects[i].effect = &dev->ff->effects[i];
+
+	pr_debug("MLNX: Device successfully registered.\n");
+	return 0;
+}
+EXPORT_SYMBOL_GPL(input_ff_create_mlnx);
diff --git a/include/linux/input/ff-memless-next.h b/include/linux/input/ff-memless-next.h
new file mode 100644
index 0000000..0711c1c
--- /dev/null
+++ b/include/linux/input/ff-memless-next.h
@@ -0,0 +1,29 @@
+#include <linux/input.h>
+
+enum mlnx_commands {
+	MLNX_START_COMBINED,
+	MLNX_STOP_COMBINED,
+	MLNX_START_UNCOMB,
+	MLNX_STOP_UNCOMB,
+	MLNX_CAN_PLAY
+};
+
+struct mlnx_simple_force {
+	const s32 x;
+	const s32 y;
+};
+
+struct mlnx_uncomb_effect {
+	const int id;
+	const struct ff_effect *effect;
+};
+
+struct mlnx_effect_command {
+	const enum mlnx_commands cmd;
+	union {
+		const struct mlnx_simple_force simple_force;
+		const struct mlnx_uncomb_effect uncomb;
+	} u;
+};
+
+int input_ff_create_mlnx(struct input_dev *dev, void *data, int(*control_effect)(struct input_dev *, void *, const struct mlnx_effect_command *), const u16 update_rate);
-- 
1.8.5.1



--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* Re: [RFC] Add ff-memless-next driver
From: Joe Perches @ 2013-12-15  2:04 UTC (permalink / raw)
  To: Michal Malý
  Cc: dmitry.torokhov, linux-kernel, linux-input, elias.vds,
	anssi.hannula
In-Reply-To: <20107437.IkYg87uJMY@geidi-prime>

On Sun, 2013-12-15 at 01:19 +0100, Michal Malý wrote:
> diff --git a/drivers/input/ff-memless-next.c b/drivers/input/ff-memless-next.c
[]
> +static inline s32 mlnx_clamp_level(const s32 level)
> +{
> +	return (level > 0x7fff) ? 0x7fff : ((level < -0x7fff) ? -0x7fff : level);

	clamp(level, -0x7fff, 0x7fff);
[]
> +static inline int mlnx_is_conditional(const struct ff_effect *effect)
> +{
> +	return (effect->type == FF_DAMPER) || (effect->type == FF_FRICTION) || (effect->type == FF_INERTIA) || (effect->type == FF_SPRING);
> +}

Maybe try to run your patch through scripts/checkpatch.pl

bool?
80 char line max? (true for the file, not just here)
switch?
	switch (effect->type) {
	case FF_DAMPER:
	case FF_FRICTION:
	case FF_INERTIA:
	case FF_SPRING:
		return true;
	}
	return false;

^ permalink raw reply

* Recent change to hid-core.c
From: Mark Lord @ 2013-12-15  5:26 UTC (permalink / raw)
  To: jiri Kosina, linux-input

Jiri,

The recent update 08ec2dcc3527a20c619aca2fb36f800908256bac
 "Merge branches 'for-3.11/multitouch', 'for-3.11/sony' and 'for-3.11/upstream' into for-linus"
included an unexpected change to the return code handing for ->raw_event() calls.

A HID driver's raw_event() method previously could return these values:
   0 --> keep processing.
   1 --> no further processing required.
   <0 --> error.

Now, "1" and "0" are both treated as "keep processing",
so a lower level HID driver has to return a negative error code
to achieve the "no further processing required" state.

Was this intentional?  Doesn't that have side-effects for some drivers?

Thanks
-- 
Mark Lord
Real-Time Remedies Inc.
mlord@pobox.com

^ permalink raw reply

* Re: [PATCH] drivers: input: Include appropriate header file in cyttsp_i2c_common.c
From: Dmitry Torokhov @ 2013-12-15 10:27 UTC (permalink / raw)
  To: Josh Triplett; +Cc: Rashika Kheria, linux-kernel, Ferruh Yigit, linux-input
In-Reply-To: <20131214205752.GS17601@leaf>

On Sat, Dec 14, 2013 at 12:57:52PM -0800, Josh Triplett wrote:
> On Sat, Dec 14, 2013 at 07:08:22PM +0530, Rashika Kheria wrote:
> > This patch includes appropriate header file cyttsp4_core.h in
> > touchscreen/cyttsp_i2c_common.c because functions
> > cyttsp_i2c_read_block_data() and cyttsp_i2c_write_block_data()
> > have their prototype declaration in cyttsp4_core.h.
> > 
> > Thus, it also eliminates the following warning in cyttsp_i2c_common.c:
> > drivers/input/touchscreen/cyttsp_i2c_common.c:34:5: warning: no previous prototype for ‘cyttsp_i2c_read_block_data’ [-Wmissing-prototypes]
> > drivers/input/touchscreen/cyttsp_i2c_common.c:64:5: warning: no previous prototype for ‘cyttsp_i2c_write_block_data’ [-Wmissing-prototypes]
> > 
> > 
> > Signed-off-by: Rashika Kheria <rashika.kheria@gmail.com>
> 
> Reviewed-by: Josh Triplett <josh@joshtriplett.org>

Applied, thank you.

> 
> >  drivers/input/touchscreen/cyttsp_i2c_common.c |    2 ++
> >  1 file changed, 2 insertions(+)
> > 
> > diff --git a/drivers/input/touchscreen/cyttsp_i2c_common.c b/drivers/input/touchscreen/cyttsp_i2c_common.c
> > index 1d7b6f1..ccefa56 100644
> > --- a/drivers/input/touchscreen/cyttsp_i2c_common.c
> > +++ b/drivers/input/touchscreen/cyttsp_i2c_common.c
> > @@ -31,6 +31,8 @@
> >  #include <linux/module.h>
> >  #include <linux/types.h>
> >  
> > +#include "cyttsp4_core.h"
> > +
> >  int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf,
> >  				      u16 addr, u8 length, void *values)
> >  {
> > -- 
> > 1.7.9.5
> > 

-- 
Dmitry
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH] input: egalax: Send touch end event when is about to suspend
From: Dmitry Torokhov @ 2013-12-15 10:40 UTC (permalink / raw)
  To: Felipe F. Tonello
  Cc: linux-kernel, linux-input, Andy Shevchenko, Sachin Kamat,
	Heiko Abraham
In-Reply-To: <1386720196-5821-1-git-send-email-eu@felipetonello.com>

Hi Felipe,

On Tue, Dec 10, 2013 at 04:03:16PM -0800, Felipe F. Tonello wrote:
> From: "Felipe F. Tonello" <eu@felipetonello.com>
> 
> This is useful to report for users of this device that don't know anything
> about the suspension of the device. So users will receive a touch end event
> when the device is about to suspend, making it more user friendly.
> 
> One example of users is the X Server with the evdev input driver. This patch
> make sure that the X server will propagate a touch end event to its windows.
> 

Hmm, I would argue we need to do this in input core, similarly to what
we do for the keys, so that all drivers would benefit from the change.

Thanks.

> Signed-off-by: Felipe F. Tonello <eu@felipetonello.com>
> ---
>  drivers/input/touchscreen/egalax_ts.c | 18 +++++++++++++++++-
>  1 file changed, 17 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c
> index 054d225..1f21f0d 100644
> --- a/drivers/input/touchscreen/egalax_ts.c
> +++ b/drivers/input/touchscreen/egalax_ts.c
> @@ -63,6 +63,7 @@
>  struct egalax_ts {
>  	struct i2c_client		*client;
>  	struct input_dev		*input_dev;
> +	bool					ids_in_use[MAX_SUPPORT_POINTS];
>  };
>  
>  static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id)
> @@ -117,6 +118,8 @@ static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id)
>  	input_mt_report_pointer_emulation(input_dev, true);
>  	input_sync(input_dev);
>  
> +	ts->ids_in_use[id] = down;
> +
>  	return IRQ_HANDLED;
>  }
>  
> @@ -247,9 +250,22 @@ static int egalax_ts_suspend(struct device *dev)
>  		0x3, 0x6, 0xa, 0x3, 0x36, 0x3f, 0x2, 0, 0, 0
>  	};
>  	struct i2c_client *client = to_i2c_client(dev);
> -	int ret;
> +	struct egalax_ts *ts = i2c_get_clientdata(client);
> +	int ret, id;
>  
>  	ret = i2c_master_send(client, suspend_cmd, MAX_I2C_DATA_LEN);
> +
> +	/* We send a touch end event (for the ids in use) if the egalax module is
> +	   about to be turned off. */
> +	for (id = 0; id < MAX_SUPPORT_POINTS; ++id) {
> +		if (ts->ids_in_use[id]) {
> +			input_mt_slot(ts->input_dev, id);
> +			input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false);
> +			input_mt_report_pointer_emulation(ts->input_dev, true);
> +			input_sync(ts->input_dev);
> +		}
> +	}
> +
>  	return ret > 0 ? 0 : ret;
>  }
>  
> -- 
> 1.8.3.1
> 

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH 5/7] genirq: Add devm_request_any_context_irq()
From: Dmitry Torokhov @ 2013-12-15 10:42 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: linux-kernel, linux-arm-msm, linux-arm-kernel, linux-input,
	Stephen Boyd
In-Reply-To: <1386718996-3733-6-git-send-email-sboyd@codeaurora.org>

On Tue, Dec 10, 2013 at 03:43:14PM -0800, Stephen Boyd wrote:
> Some drivers use request_any_context_irq() but there isn't a
> devm_* function for it. Add one so that these drivers don't need
> to explicitly free the irq on driver detach.
> 
> Cc: Thomas Gleixner <tglx@linutronix.de>

Thomas, would it be OK with me picking this patch through my tree if you
do not have issues with it?

Thanks.

> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
> ---
>  include/linux/interrupt.h |  5 +++++
>  kernel/irq/devres.c       | 45 +++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 50 insertions(+)
> 
> diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
> index c9e831d..8334f9b 100644
> --- a/include/linux/interrupt.h
> +++ b/include/linux/interrupt.h
> @@ -160,6 +160,11 @@ devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler,
>  					 devname, dev_id);
>  }
>  
> +extern int __must_check
> +devm_request_any_context_irq(struct device *dev, unsigned int irq,
> +		 irq_handler_t handler, unsigned long irqflags,
> +		 const char *devname, void *dev_id);
> +
>  extern void devm_free_irq(struct device *dev, unsigned int irq, void *dev_id);
>  
>  /*
> diff --git a/kernel/irq/devres.c b/kernel/irq/devres.c
> index bd8e788..f71e9ff 100644
> --- a/kernel/irq/devres.c
> +++ b/kernel/irq/devres.c
> @@ -73,6 +73,51 @@ int devm_request_threaded_irq(struct device *dev, unsigned int irq,
>  EXPORT_SYMBOL(devm_request_threaded_irq);
>  
>  /**
> + *	devm_request_any_context_irq - allocate an interrupt line for a managed device
> + *	@dev: device to request interrupt for
> + *	@irq: Interrupt line to allocate
> + *	@handler: Function to be called when the IRQ occurs
> + *	@thread_fn: function to be called in a threaded interrupt context. NULL
> + *		    for devices which handle everything in @handler
> + *	@irqflags: Interrupt type flags
> + *	@devname: An ascii name for the claiming device
> + *	@dev_id: A cookie passed back to the handler function
> + *
> + *	Except for the extra @dev argument, this function takes the
> + *	same arguments and performs the same function as
> + *	request_any_context_irq().  IRQs requested with this function will be
> + *	automatically freed on driver detach.
> + *
> + *	If an IRQ allocated with this function needs to be freed
> + *	separately, devm_free_irq() must be used.
> + */
> +int devm_request_any_context_irq(struct device *dev, unsigned int irq,
> +			      irq_handler_t handler, unsigned long irqflags,
> +			      const char *devname, void *dev_id)
> +{
> +	struct irq_devres *dr;
> +	int rc;
> +
> +	dr = devres_alloc(devm_irq_release, sizeof(struct irq_devres),
> +			  GFP_KERNEL);
> +	if (!dr)
> +		return -ENOMEM;
> +
> +	rc = request_any_context_irq(irq, handler, irqflags, devname, dev_id);
> +	if (rc) {
> +		devres_free(dr);
> +		return rc;
> +	}
> +
> +	dr->irq = irq;
> +	dr->dev_id = dev_id;
> +	devres_add(dev, dr);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(devm_request_any_context_irq);
> +
> +/**
>   *	devm_free_irq - free an interrupt
>   *	@dev: device to free interrupt for
>   *	@irq: Interrupt line to free
> -- 
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> hosted by The Linux Foundation
> 

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH] Input: zforce: fix possible driver hang during suspend
From: Dmitry Torokhov @ 2013-12-15 10:43 UTC (permalink / raw)
  To: Heiko Stübner; +Cc: Henrik Rydberg, linux-input
In-Reply-To: <201312101650.04799.heiko@sntech.de>

On Tue, Dec 10, 2013 at 04:50:04PM +0100, Heiko Stübner wrote:
> handle_level_irq masks the interrupt before handling it, and only
> unmasks it after the handler is finished. So when a touch event
> happens after threads are suspended, but before the system is fully asleep
> the irq handler tries to wakeup the thread which will only happen on the
> next resume, resulting in the wakeup event never being sent and the driver
> not being able to wake the system from sleep due to the masked irq.
> 
> Therefore move the wakeup_event to a small non-threaded handler.
> 
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>

Applied, thank you.

> ---
>  drivers/input/touchscreen/zforce_ts.c |   21 +++++++++++++++------
>  1 file changed, 15 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c
> index 75762d6..aa127ba 100644
> --- a/drivers/input/touchscreen/zforce_ts.c
> +++ b/drivers/input/touchscreen/zforce_ts.c
> @@ -455,7 +455,18 @@ static void zforce_complete(struct zforce_ts *ts, int cmd, int result)
>  	}
>  }
>  
> -static irqreturn_t zforce_interrupt(int irq, void *dev_id)
> +static irqreturn_t zforce_irq(int irq, void *dev_id)
> +{
> +	struct zforce_ts *ts = dev_id;
> +	struct i2c_client *client = ts->client;
> +
> +	if (ts->suspended && device_may_wakeup(&client->dev))
> +		pm_wakeup_event(&client->dev, 500);
> +
> +	return IRQ_WAKE_THREAD;
> +}
> +
> +static irqreturn_t zforce_irq_thread(int irq, void *dev_id)
>  {
>  	struct zforce_ts *ts = dev_id;
>  	struct i2c_client *client = ts->client;
> @@ -465,12 +476,10 @@ static irqreturn_t zforce_interrupt(int irq, void *dev_id)
>  	u8 *payload;
>  
>  	/*
> -	 * When suspended, emit a wakeup signal if necessary and return.
> +	 * When still suspended, return.
>  	 * Due to the level-interrupt we will get re-triggered later.
>  	 */
>  	if (ts->suspended) {
> -		if (device_may_wakeup(&client->dev))
> -			pm_wakeup_event(&client->dev, 500);
>  		msleep(20);
>  		return IRQ_HANDLED;
>  	}
> @@ -763,8 +772,8 @@ static int zforce_probe(struct i2c_client *client,
>  	 * Therefore we can trigger the interrupt anytime it is low and do
>  	 * not need to limit it to the interrupt edge.
>  	 */
> -	ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
> -					zforce_interrupt,
> +	ret = devm_request_threaded_irq(&client->dev, client->irq,
> +					zforce_irq, zforce_irq_thread,
>  					IRQF_TRIGGER_LOW | IRQF_ONESHOT,
>  					input_dev->name, ts);
>  	if (ret) {
> -- 
> 1.7.10.4
> 

-- 
Dmitry
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 1/7] Input: pmic8xxx-pwrkey - Pass input device directly to interrupt
From: Dmitry Torokhov @ 2013-12-15 11:21 UTC (permalink / raw)
  To: Stephen Boyd; +Cc: linux-kernel, linux-arm-msm, linux-arm-kernel, linux-input
In-Reply-To: <1386718996-3733-2-git-send-email-sboyd@codeaurora.org>

Hi Stephen,

On Tue, Dec 10, 2013 at 03:43:10PM -0800, Stephen Boyd wrote:
> Instead of passing the pointer to the container structure just
> pass the input device here. This saves a dereference in the fast
> path.
> 
> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
> ---
>  drivers/input/misc/pmic8xxx-pwrkey.c | 22 ++++++++--------------
>  1 file changed, 8 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c
> index ce3c426..233b216 100644
> --- a/drivers/input/misc/pmic8xxx-pwrkey.c
> +++ b/drivers/input/misc/pmic8xxx-pwrkey.c
> @@ -32,26 +32,21 @@
>   * @key_press_irq: key press irq number
>   */
>  struct pmic8xxx_pwrkey {
> -	struct input_dev *pwr;

This causes compile errors as you need pwrkey->pwr in remove() to
unregister input device. I'll fix it up.

>  	int key_press_irq;
>  };
>  
> -static irqreturn_t pwrkey_press_irq(int irq, void *_pwrkey)
> +static irqreturn_t pwrkey_press_irq(int irq, void *pwr)
>  {
> -	struct pmic8xxx_pwrkey *pwrkey = _pwrkey;
> -
> -	input_report_key(pwrkey->pwr, KEY_POWER, 1);
> -	input_sync(pwrkey->pwr);
> +	input_report_key(pwr, KEY_POWER, 1);
> +	input_sync(pwr);
>  
>  	return IRQ_HANDLED;
>  }
>  
> -static irqreturn_t pwrkey_release_irq(int irq, void *_pwrkey)
> +static irqreturn_t pwrkey_release_irq(int irq, void *pwr)
>  {
> -	struct pmic8xxx_pwrkey *pwrkey = _pwrkey;
> -
> -	input_report_key(pwrkey->pwr, KEY_POWER, 0);
> -	input_sync(pwrkey->pwr);
> +	input_report_key(pwr, KEY_POWER, 0);
> +	input_sync(pwr);
>  
>  	return IRQ_HANDLED;
>  }
> @@ -147,12 +142,11 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
>  	}
>  
>  	pwrkey->key_press_irq = key_press_irq;
> -	pwrkey->pwr = pwr;
>  
>  	platform_set_drvdata(pdev, pwrkey);
>  
>  	err = devm_request_irq(&pdev->dev, key_press_irq, pwrkey_press_irq,
> -		IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_press", pwrkey);
> +		IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_press", pwr);
>  	if (err < 0) {
>  		dev_dbg(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n",
>  				 key_press_irq, err);
> @@ -160,7 +154,7 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
>  	}
>  
>  	err = devm_request_irq(&pdev->dev, key_release_irq, pwrkey_release_irq,
> -		 IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_release", pwrkey);
> +		 IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_release", pwr);
>  	if (err < 0) {
>  		dev_dbg(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n",
>  				 key_release_irq, err);
> -- 
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> hosted by The Linux Foundation
> 

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH 2/7] Input: pmic8xxx-pwrkey - Migrate to regmap APIs
From: Dmitry Torokhov @ 2013-12-15 11:33 UTC (permalink / raw)
  To: Stephen Boyd; +Cc: linux-kernel, linux-arm-msm, linux-arm-kernel, linux-input
In-Reply-To: <1386718996-3733-3-git-send-email-sboyd@codeaurora.org>

On Tue, Dec 10, 2013 at 03:43:11PM -0800, Stephen Boyd wrote:
> Use the regmap APIs for this driver instead of custom pm8xxx
> APIs. This breaks this driver's dependency on the pm8xxx APIs and
> allows us to easily port it to other bus protocols in the future.
> 
> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
> ---
>  drivers/input/misc/pmic8xxx-pwrkey.c | 13 +++++++++----
>  1 file changed, 9 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c
> index 233b216..a4de105 100644
> --- a/drivers/input/misc/pmic8xxx-pwrkey.c
> +++ b/drivers/input/misc/pmic8xxx-pwrkey.c
> @@ -18,9 +18,9 @@
>  #include <linux/input.h>
>  #include <linux/interrupt.h>
>  #include <linux/platform_device.h>
> +#include <linux/regmap.h>
>  #include <linux/log2.h>
>  
> -#include <linux/mfd/pm8xxx/core.h>
>  #include <linux/input/pmic8xxx-pwrkey.h>
>  
>  #define PON_CNTL_1 0x1C
> @@ -83,7 +83,8 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
>  	int key_press_irq = platform_get_irq(pdev, 1);
>  	int err;
>  	unsigned int delay;
> -	u8 pon_cntl;
> +	unsigned int pon_cntl;
> +	struct regmap *regmap;
>  	struct pmic8xxx_pwrkey *pwrkey;
>  	const struct pm8xxx_pwrkey_platform_data *pdata =
>  					dev_get_platdata(&pdev->dev);
> @@ -108,6 +109,10 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
>  		err = -ENOMEM;
>  	}
>  
> +	regmap = dev_get_regmap(pdev->dev.parent, NULL);
> +	if (!regmap)
> +		return -ENODEV;

And you are leaking memory here...

> +
>  	input_set_capability(pwr, EV_KEY, KEY_POWER);
>  
>  	pwr->name = "pmic8xxx_pwrkey";
> @@ -116,7 +121,7 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
>  	delay = (pdata->kpd_trigger_delay_us << 10) / USEC_PER_SEC;
>  	delay = 1 + ilog2(delay);
>  
> -	err = pm8xxx_readb(pdev->dev.parent, PON_CNTL_1, &pon_cntl);
> +	err = regmap_read(regmap, PON_CNTL_1, &pon_cntl);
>  	if (err < 0) {
>  		dev_err(&pdev->dev, "failed reading PON_CNTL_1 err=%d\n", err);
>  		return err;
> @@ -129,7 +134,7 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
>  	else
>  		pon_cntl &= ~PON_CNTL_PULL_UP;
>  
> -	err = pm8xxx_writeb(pdev->dev.parent, PON_CNTL_1, pon_cntl);
> +	err = regmap_write(regmap, PON_CNTL_1, pon_cntl);
>  	if (err < 0) {
>  		dev_err(&pdev->dev, "failed writing PON_CNTL_1 err=%d\n", err);
>  		return err;
> -- 
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> hosted by The Linux Foundation
> 

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH] input synaptics-rmi4: Debugfs initialization fixes
From: Dmitry Torokhov @ 2013-12-15 11:56 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Linux Input, Andrew Duggan, Vincent Huang, Vivian Ly,
	Daniel Rosenberg, Jean Delvare, Joerie de Gram, Linus Walleij,
	Benjamin Tissoires
In-Reply-To: <1386727924-11522-1-git-send-email-cheiny@synaptics.com>

On Tue, Dec 10, 2013 at 06:12:04PM -0800, Christopher Heiny wrote:
> This fixes the broken #ifdefs on CONFIG_RMI4_DEBUG.  Per device debugfs
> setup is reordered to ensure that the debugfs tree for a given device is
> created before it is registered (since the action of registering a device
> may cause other things to happen that depend on debugfs being initialized
> already).
> 
> Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> Cc: Jean Delvare <khali@linux-fr.org>
> Cc: Linus Walleij <linus.walleij@stericsson.com>
> Cc: Joerie de Gram <j.de.gram@gmail.com>
> Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> 

Applied, thank you.

> ---
> 
> This patch implements changes to the synaptics-rmi4 branch of
> Dmitry's input tree.  The base for the patch is commit
> f154022b208a59b048f52b7b5d58a5ec1949b96e.
> 
>  drivers/input/rmi4/rmi_bus.c | 24 +++++++++++++++---------
>  1 file changed, 15 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c
> index cb14cf6..173f6be 100644
> --- a/drivers/input/rmi4/rmi_bus.c
> +++ b/drivers/input/rmi4/rmi_bus.c
> @@ -47,7 +47,7 @@ bool rmi_is_physical_device(struct device *dev)
>  	return dev->type == &rmi_device_type;
>  }
>  
> -#if CONFIG_RMI4_DEBUG
> +#ifdef CONFIG_RMI4_DEBUG
>  
>  static void rmi_physical_setup_debugfs(struct rmi_device *rmi_dev)
>  {
> @@ -109,12 +109,12 @@ int rmi_register_physical_device(struct rmi_phys_device *phys)
>  
>  	phys->rmi_dev = rmi_dev;
>  
> +	rmi_physical_setup_debugfs(rmi_dev);
> +
>  	error = device_register(&rmi_dev->dev);
>  	if (error)
>  		return error;
>  
> -	rmi_physical_setup_debugfs(rmi_dev);
> -
>  	dev_dbg(phys->dev, "%s: Registered %s as %s.\n", __func__,
>  		pdata->sensor_name, dev_name(&rmi_dev->dev));
>  
> @@ -155,7 +155,7 @@ bool rmi_is_function_device(struct device *dev)
>  	return dev->type == &rmi_function_type;
>  }
>  
> -#if CONFIG_RMI4_DEBUG
> +#ifdef CONFIG_RMI4_DEBUG
>  
>  static void rmi_function_setup_debugfs(struct rmi_function *fn)
>  {
> @@ -233,18 +233,23 @@ int rmi_register_function(struct rmi_function *fn)
>  	fn->dev.type = &rmi_function_type;
>  	fn->dev.bus = &rmi_bus_type;
>  
> +	rmi_function_setup_debugfs(fn);
> +
>  	error = device_register(&fn->dev);
>  	if (error) {
>  		dev_err(&rmi_dev->dev,
>  			"Failed device_register function device %s\n",
>  			dev_name(&fn->dev));
> -		return error;
> +		goto error_exit;
>  	}
>  
>  	dev_dbg(&rmi_dev->dev, "Registered F%02X.\n", fn->fd.function_number);
>  
> -	rmi_function_setup_debugfs(fn);
>  	return 0;
> +
> +error_exit:
> +	rmi_function_teardown_debugfs(fn);
> +	return error;
>  }
>  
>  void rmi_unregister_function(struct rmi_function *fn)
> @@ -357,6 +362,8 @@ static int __init rmi_bus_init(void)
>  		return error;
>  	}
>  
> +	rmi_bus_setup_debugfs();
> +
>  	error = rmi_register_f01_handler();
>  	if (error) {
>  		pr_err("%s: error registering the RMI F01 handler: %d\n",
> @@ -371,13 +378,12 @@ static int __init rmi_bus_init(void)
>  		goto err_unregister_f01;
>  	}
>  
> -	rmi_bus_setup_debugfs();
> -
>  	return 0;
>  
>  err_unregister_f01:
>  	rmi_unregister_f01_handler();
>  err_unregister_bus:
> +	rmi_bus_teardown_debugfs();
>  	bus_unregister(&rmi_bus_type);
>  	return error;
>  }
> @@ -390,9 +396,9 @@ static void __exit rmi_bus_exit(void)
>  	 * all we have to do at this point is unregister ourselves.
>  	 */
>  
> -	rmi_bus_teardown_debugfs();
>  	rmi_unregister_physical_driver();
>  	rmi_unregister_f01_handler();
> +	rmi_bus_teardown_debugfs();
>  	bus_unregister(&rmi_bus_type);
>  }
>  module_exit(rmi_bus_exit);

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH] input synaptics-rmi4: Eliminate packed structs in rmi_f11.c
From: Dmitry Torokhov @ 2013-12-15 11:57 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Linux Input, Andrew Duggan, Vincent Huang, Vivian Ly,
	Daniel Rosenberg, Jean Delvare, Joerie de Gram, Linus Walleij,
	Benjamin Tissoires
In-Reply-To: <1386729594-5471-1-git-send-email-cheiny@synaptics.com>

On Tue, Dec 10, 2013 at 06:39:54PM -0800, Christopher Heiny wrote:
> This converts rmi_f11.c from using bitfields in packed structs, converting
> to bitmasks and shifts to parse out bitfields, nibbles, and so on.
> 
> Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> Cc: Jean Delvare <khali@linux-fr.org>
> Cc: Linus Walleij <linus.walleij@stericsson.com>
> Cc: Joerie de Gram <j.de.gram@gmail.com>
> Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> 

Applied, thank you.

> 
> 
> > ---
> > 
> > This patch implements changes to the synaptics-rmi4 branch of
> > Dmitry's input tree.  The base for the patch is commit
> > f154022b208a59b048f52b7b5d58a5ec1949b96e.
> > 
> >  drivers/input/rmi4/rmi_f11.c | 1526 +++++++++++++++---------------------------
> >  1 file changed, 553 insertions(+), 973 deletions(-)
> > 
> > diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c
> > index d3a9ba8..d63ef31 100644
> > --- a/drivers/input/rmi4/rmi_f11.c
> > +++ b/drivers/input/rmi4/rmi_f11.c
> > @@ -69,33 +69,124 @@
> >   */
> >  
> >  /**
> > - * @rezero - writing 1 to this will cause the sensor to calibrate to the
> > - * current capacitive state.
> > + * @rezero - writing this to the F11 command register will cause the sensor to
> > + * calibrate to the current capacitive state.
> >   */
> > -struct f11_2d_commands {
> > -	u8 rezero:1;
> > -	u8 reserved:7;
> > -} __attribute__((__packed__));
> > +#define RMI_F11_REZERO  0x01
> > +
> > +#define RMI_F11_HAS_QUERY9              (1 << 3)
> > +#define RMI_F11_HAS_QUERY11             (1 << 4)
> > +#define RMI_F11_HAS_QUERY12             (1 << 5)
> > +#define RMI_F11_HAS_QUERY27             (1 << 6)
> > +#define RMI_F11_HAS_QUERY28             (1 << 7)
> > +
> > +/** Defs for Query 1 */
> > +
> > +#define RMI_F11_NR_FINGERS_MASK 0x07
> > +#define RMI_F11_HAS_REL                 (1 << 3)
> > +#define RMI_F11_HAS_ABS                 (1 << 4)
> > +#define RMI_F11_HAS_GESTURES            (1 << 5)
> > +#define RMI_F11_HAS_SENSITIVITY_ADJ     (1 << 6)
> > +#define RMI_F11_CONFIGURABLE            (1 << 7)
> > +
> > +/** Defs for Query 2, 3, and 4. */
> > +#define RMI_F11_NR_ELECTRODES_MASK      0x7F
> > +
> > +/** Defs for Query 5 */
> > +
> > +#define RMI_F11_ABS_DATA_SIZE_MASK      0x03
> > +#define RMI_F11_HAS_ANCHORED_FINGER     (1 << 2)
> > +#define RMI_F11_HAS_ADJ_HYST            (1 << 3)
> > +#define RMI_F11_HAS_DRIBBLE             (1 << 4)
> > +#define RMI_F11_HAS_BENDING_CORRECTION  (1 << 5)
> > +#define RMI_F11_HAS_LARGE_OBJECT_SUPPRESSION    (1 << 6)
> > +#define RMI_F11_HAS_JITTER_FILTER       (1 << 7)
> > +
> > +/** Defs for Query 7 */
> > +#define RMI_F11_HAS_SINGLE_TAP                  (1 << 0)
> > +#define RMI_F11_HAS_TAP_AND_HOLD                (1 << 1)
> > +#define RMI_F11_HAS_DOUBLE_TAP                  (1 << 2)
> > +#define RMI_F11_HAS_EARLY_TAP                   (1 << 3)
> > +#define RMI_F11_HAS_FLICK                       (1 << 4)
> > +#define RMI_F11_HAS_PRESS                       (1 << 5)
> > +#define RMI_F11_HAS_PINCH                       (1 << 6)
> > +#define RMI_F11_HAS_CHIRAL                      (1 << 7)
> > +
> > +/** Defs for Query 8 */
> > +#define RMI_F11_HAS_PALM_DET                    (1 << 0)
> > +#define RMI_F11_HAS_ROTATE                      (1 << 1)
> > +#define RMI_F11_HAS_TOUCH_SHAPES                (1 << 2)
> > +#define RMI_F11_HAS_SCROLL_ZONES                (1 << 3)
> > +#define RMI_F11_HAS_INDIVIDUAL_SCROLL_ZONES     (1 << 4)
> > +#define RMI_F11_HAS_MF_SCROLL                   (1 << 5)
> > +#define RMI_F11_HAS_MF_EDGE_MOTION              (1 << 6)
> > +#define RMI_F11_HAS_MF_SCROLL_INERTIA           (1 << 7)
> > +
> > +/** Defs for Query 9. */
> > +#define RMI_F11_HAS_PEN                         (1 << 0)
> > +#define RMI_F11_HAS_PROXIMITY                   (1 << 1)
> > +#define RMI_F11_HAS_PALM_DET_SENSITIVITY        (1 << 2)
> > +#define RMI_F11_HAS_SUPPRESS_ON_PALM_DETECT     (1 << 3)
> > +#define RMI_F11_HAS_TWO_PEN_THRESHOLDS          (1 << 4)
> > +#define RMI_F11_HAS_CONTACT_GEOMETRY            (1 << 5)
> > +#define RMI_F11_HAS_PEN_HOVER_DISCRIMINATION    (1 << 6)
> > +#define RMI_F11_HAS_PEN_FILTERS                 (1 << 7)
> > +
> > +/** Defs for Query 10. */
> > +#define RMI_F11_NR_TOUCH_SHAPES_MASK            0x1F
> > +
> > +/** Defs for Query 11 */
> > +
> > +#define RMI_F11_HAS_Z_TUNING                    (1 << 0)
> > +#define RMI_F11_HAS_ALGORITHM_SELECTION         (1 << 1)
> > +#define RMI_F11_HAS_W_TUNING                    (1 << 2)
> > +#define RMI_F11_HAS_PITCH_INFO                  (1 << 3)
> > +#define RMI_F11_HAS_FINGER_SIZE                 (1 << 4)
> > +#define RMI_F11_HAS_SEGMENTATION_AGGRESSIVENESS (1 << 5)
> > +#define RMI_F11_HAS_XY_CLIP                     (1 << 6)
> > +#define RMI_F11_HAS_DRUMMING_FILTER             (1 << 7)
> > +
> > +/** Defs for Query 12. */
> > +
> > +#define RMI_F11_HAS_GAPLESS_FINGER              (1 << 0)
> > +#define RMI_F11_HAS_GAPLESS_FINGER_TUNING       (1 << 1)
> > +#define RMI_F11_HAS_8BIT_W                      (1 << 2)
> > +#define RMI_F11_HAS_ADJUSTABLE_MAPPING          (1 << 3)
> > +#define RMI_F11_HAS_INFO2                       (1 << 4)
> > +#define RMI_F11_HAS_PHYSICAL_PROPS              (1 << 5)
> > +#define RMI_F11_HAS_FINGER_LIMIT                (1 << 6)
> > +#define RMI_F11_HAS_LINEAR_COEFF                (1 << 7)
> > +
> > +/** Defs for Query 13. */
> > +
> > +#define RMI_F11_JITTER_WINDOW_MASK              0x1F
> > +#define RMI_F11_JITTER_FILTER_MASK              0x60
> > +#define RMI_F11_JITTER_FILTER_SHIFT             5
> > +
> > +/** Defs for Query 14. */
> > +#define RMI_F11_LIGHT_CONTROL_MASK              0x03
> > +#define RMI_F11_IS_CLEAR                        (1 << 2)
> > +#define RMI_F11_CLICKPAD_PROPS_MASK             0x18
> > +#define RMI_F11_CLICKPAD_PROPS_SHIFT            3
> > +#define RMI_F11_MOUSE_BUTTONS_MASK              0x60
> > +#define RMI_F11_MOUSE_BUTTONS_SHIFT             5
> > +#define RMI_F11_HAS_ADVANCED_GESTURES           (1 << 7)
> > +
> > +#define RMI_F11_QUERY_SIZE                      4
> > +#define RMI_F11_QUERY_GESTURE_SIZE              2
> >  
> > -/** This query is always present, and is on a per device basis.  All other
> > - * queries are on a per-sensor basis.
> > +#define F11_LIGHT_CTL_NONE 0x00
> > +#define F11_LUXPAD	   0x01
> > +#define F11_DUAL_MODE      0x02
> > +
> > +#define F11_NOT_CLICKPAD     0x00
> > +#define F11_HINGED_CLICKPAD  0x01
> > +#define F11_UNIFORM_CLICKPAD 0x02
> > +
> > +/**
> > + * Query registers 1 through 4 are always present.
> >   *
> > - * @nbr_of_sensors - the number of 2D sensors on the touch device.
> > - * @has_query9 - indicates the F11_2D_Query9 register exists.
> > - * @has_query11 - indicates the F11_2D_Query11 register exists.
> > - * @has_query12 - indicates the F11_2D_Query12 register exists.
> > - */
> > -struct f11_2d_device_query {
> > -	u8 nbr_of_sensors:3;
> > -	u8 has_query9:1;
> > -	u8 has_query11:1;
> > -	u8 has_query12:1;
> > -	u8 has_query27:1;
> > -	u8 has_query28:1;
> > -} __attribute__((__packed__));
> > -
> > -/** Query registers 1 through 4 are always present.
> > - * @number_of_fingers - describes the maximum number of fingers the 2-D sensor
> > + * @nr_fingers - describes the maximum number of fingers the 2-D sensor
> >   * supports.
> >   * @has_rel - the sensor supports relative motion reporting.
> >   * @has_abs - the sensor supports absolute poition reporting.
> > @@ -109,27 +200,8 @@ struct f11_2d_device_query {
> >   * supports on the Y axis.
> >   * @max_electrodes - the total number of X and Y electrodes that may be
> >   * configured.
> > - */
> > -struct f11_2d_sensor_info {
> > -	/* query1 */
> > -	u8 number_of_fingers:3;
> > -	u8 has_rel:1;
> > -	u8 has_abs:1;
> > -	u8 has_gestures:1;
> > -	u8 has_sensitivity_adjust:1;
> > -	u8 configurable:1;
> > -	/* query2 */
> > -	u8 num_of_x_electrodes:7;
> > -	u8 reserved_1:1;
> > -	/* query3 */
> > -	u8 num_of_y_electrodes:7;
> > -	u8 reserved_2:1;
> > -	/* query4 */
> > -	u8 max_electrodes:7;
> > -	u8 reserved_3:1;
> > -} __attribute__((__packed__));
> > -
> > -/** Query 5 - this is present if the has_abs bit is set.
> > + *
> > + * Query 5 is present if the has_abs bit is set.
> >   *
> >   * @abs_data_size - describes the format of data reported by the absolute
> >   * data source.  Only one format (the kind used here) is supported at this
> > @@ -146,18 +218,8 @@ struct f11_2d_sensor_info {
> >   * @has_large_object_suppression - control register 58 and data register 28
> >   * exist.
> >   * @has_jitter_filter - query 13 and control 73..76 exist.
> > - */
> > -struct f11_2d_abs_info {
> > -	u8 abs_data_size:2;
> > -	u8 has_anchored_finger:1;
> > -	u8 has_adj_hyst:1;
> > -	u8 has_dribble:1;
> > -	u8 has_bending_correction:1;
> > -	u8 has_large_object_suppression:1;
> > -	u8 has_jitter_filter:1;
> > -} __attribute__((__packed__));
> > -
> > -/** Gesture information queries 7 and 8 are present if has_gestures bit is set.
> > + *
> > + * Gesture information queries 7 and 8 are present if has_gestures bit is set.
> >   *
> >   * @has_single_tap - a basic single-tap gesture is supported.
> >   * @has_tap_n_hold - tap-and-hold gesture is supported.
> > @@ -176,38 +238,17 @@ struct f11_2d_abs_info {
> >   * @has_scroll_zones - scrolling areas near the sensor edges are supported.
> >   * @has_individual_scroll_zones - if 1, then 4 scroll zones are supported;
> >   * if 0, then only two are supported.
> > - * @has_multi_finger_scroll - the multifinger_scrolling bit will be set when
> > + * @has_mf_scroll - the multifinger_scrolling bit will be set when
> >   * more than one finger is involved in a scrolling action.
> > - */
> > -struct f11_2d_gesture_info {
> > -	u8 has_single_tap:1;
> > -	u8 has_tap_n_hold:1;
> > -	u8 has_double_tap:1;
> > -	u8 has_early_tap:1;
> > -	u8 has_flick:1;
> > -	u8 has_press:1;
> > -	u8 has_pinch:1;
> > -	u8 has_chiral:1;
> > -
> > -	u8 has_palm_det:1;
> > -	u8 has_rotate:1;
> > -	u8 has_touch_shapes:1;
> > -	u8 has_scroll_zones:1;
> > -	u8 has_individual_scroll_zones:1;
> > -	u8 has_multi_finger_scroll:1;
> > -	u8 has_mf_edge_motion:1;
> > -	u8 has_mf_scroll_inertia:1;
> > -} __attribute__((__packed__));
> > -
> > -/** Utility for checking bytes in the gesture info registers.  This is done
> > - * often enough that we put it here to declutter the conditionals.
> > - */
> > -static bool has_gesture_bits(const struct f11_2d_gesture_info *info,
> > -			     const u8 byte) {
> > -	return ((u8 *) info)[byte] != 0;
> > -}
> > -
> > -/**
> > + *
> > + * Convenience for checking bytes in the gesture info registers.  This is done
> > + * often enough that we put it here to declutter the conditionals
> > + *
> > + * @query7_nonzero - true if none of the query 7 bits are set
> > + * @query8_nonzero - true if none of the query 8 bits are set
> > + *
> > + * Query 9 is present if the has_query9 is set.
> > + *
> >   * @has_pen - detection of a stylus is supported and registers F11_2D_Ctrl20
> >   * and F11_2D_Ctrl21 exist.
> >   * @has_proximity - detection of fingers near the sensor is supported and
> > @@ -216,30 +257,14 @@ static bool has_gesture_bits(const struct f11_2d_gesture_info *info,
> >   * feature and register F11_2D_Ctrl27 exists.
> >   * @has_two_pen_thresholds - is has_pen is also set, then F11_2D_Ctrl35 exists.
> >   * @has_contact_geometry - the sensor supports the use of contact geometry to
> > - * map absolute X and Y target positions and registers F11_2D_Data18.* through
> > - * F11_2D_Data27 exist.
> > - */
> > -struct f11_2d_query9 {
> > -	u8 has_pen:1;
> > -	u8 has_proximity:1;
> > -	u8 has_palm_det_sensitivity:1;
> > -	u8 has_suppress_on_palm_detect:1;
> > -	u8 has_two_pen_thresholds:1;
> > -	u8 has_contact_geometry:1;
> > -	u8 has_pen_hover_discrimination:1;
> > -	u8 has_pen_filters:1;
> > -} __attribute__((__packed__));
> > -
> > -/** Touch shape info (query 10) is present if has_touch_shapes is set.
> > + * map absolute X and Y target positions and registers F11_2D_Data18
> > + * through F11_2D_Data27 exist.
> >   *
> > - * @nbr_touch_shapes - the total number of touch shapes supported.
> > - */
> > -struct f11_2d_ts_info {
> > -	u8 nbr_touch_shapes:5;
> > -	u8 reserved:3;
> > -} __attribute__((__packed__));
> > -
> > -/** Query 11 is present if the has_query11 bit is set in query 0.
> > + * Touch shape info (query 10) is present if has_touch_shapes is set.
> > + *
> > + * @nr_touch_shapes - the total number of touch shapes supported.
> > + *
> > + * Query 11 is present if the has_query11 bit is set in query 0.
> >   *
> >   * @has_z_tuning - if set, the sensor supports Z tuning and registers
> >   * F11_2D_Ctrl29 through F11_2D_Ctrl33 exist.
> > @@ -259,19 +284,9 @@ struct f11_2d_ts_info {
> >   * @has_drumming_filter - the sensor can be configured to distinguish
> >   * between a fast flick and a quick drumming movement and registers
> >   * F11_2D_Ctrl50 and F11_2D_Ctrl51 exist.
> > - */
> > -struct f11_2d_query11 {
> > -	u8 has_z_tuning:1;
> > -	u8 has_algorithm_selection:1;
> > -	u8 has_w_tuning:1;
> > -	u8 has_pitch_info:1;
> > -	u8 has_finger_size:1;
> > -	u8 has_segmentation_aggressiveness:1;
> > -	u8 has_XY_clip:1;
> > -	u8 has_drumming_filter:1;
> > -} __attribute__((__packed__));
> > -
> > -/**
> > + *
> > + * Query 12 is present if hasQuery12 bit is set.
> > + *
> >   * @has_gapless_finger - control registers relating to gapless finger are
> >   * present.
> >   * @has_gapless_finger_tuning - additional control and data registers relating
> > @@ -283,29 +298,12 @@ struct f11_2d_query11 {
> >   * of the sensor are present.
> >   * @has_finger_limit - indicates that F11 Ctrl 80 exists.
> >   * @has_linear_coeff - indicates that F11 Ctrl 81 exists.
> > - */
> > -struct f11_2d_query12 {
> > -	u8 has_gapless_finger:1;
> > -	u8 has_gapless_finger_tuning:1;
> > -	u8 has_8bit_w:1;
> > -	u8 has_adjustable_mapping:1;
> > -	u8 has_info2:1;
> > -	u8 has_physical_props:1;
> > -	u8 has_finger_limit:1;
> > -	u8 has_linear_coeff_2:1;
> > -} __attribute__((__packed__));
> > -
> > -/** This register is present if Query 5's has_jitter_filter bit is set.
> > + *
> > + * Query 13 is present if Query 5's has_jitter_filter bit is set.
> >   * @jitter_window_size - used by Design Studio 4.
> >   * @jitter_filter_type - used by Design Studio 4.
> > - */
> > -struct f11_2d_query13 {
> > -	u8 jtter_window_size:5;
> > -	u8 jitter_filter_type:2;
> > -	u8 reserved:1;
> > -} __attribute__((__packed__));
> > -
> > -/** This register is present if query 12's has_general_info2 flag is set.
> > + *
> > + * Query 14 is present if query 12's has_general_info2 flag is set.
> >   *
> >   * @light_control - Indicates what light/led control features are present, if
> >   * any.
> > @@ -316,422 +314,174 @@ struct f11_2d_query13 {
> >   * @mouse_buttons - specifies the number of mouse buttons present (if any).
> >   * @has_advanced_gestures - advanced driver gestures are supported.
> >   */
> > -struct f11_2d_query14 {
> > -	u8 light_control:2;
> > -	u8 is_clear:1;
> > -	u8 clickpad_props:2;
> > -	u8 mouse_buttons:2;
> > -	u8 has_advanced_gestures:1;
> > -} __attribute__((__packed__));
> > -
> > -#define F11_LIGHT_CTL_NONE 0x00
> > -#define F11_LUXPAD	   0x01
> > -#define F11_DUAL_MODE      0x02
> > -
> > -#define F11_NOT_CLICKPAD     0x00
> > -#define F11_HINGED_CLICKPAD  0x01
> > -#define F11_UNIFORM_CLICKPAD 0x02
> > -
> > -/** See notes above for information about specific query register sets.
> > - */
> >  struct f11_2d_sensor_queries {
> > -	struct f11_2d_sensor_info info;
> > -	struct f11_2d_abs_info abs_info;
> > -	u8 f11_2d_query6;
> > -	struct f11_2d_gesture_info gesture_info;
> > -	struct f11_2d_query9 query9;
> > -	struct f11_2d_ts_info ts_info;
> > -	struct f11_2d_query11 features_1;
> > -	struct f11_2d_query12 features_2;
> > -	struct f11_2d_query13 jitter_filter;
> > -	struct f11_2d_query14 info_2;
> > -};
> > -
> > -/**
> > - * @reporting_mode - controls how often finger position data is reported.
> > - * @abs_pos_filt - when set, enables various noise and jitter filtering
> > - * algorithms for absolute reports.
> > - * @rel_pos_filt - when set, enables various noise and jitter filtering
> > - * algorithms for relative reports.
> > - * @rel_ballistics - enables ballistics processing for the relative finger
> > - * motion on the 2-D sensor.
> > - * @dribble - enables the dribbling feature.
> > - * @report_beyond_clip - when this is set, fingers outside the active area
> > - * specified by the x_clip and y_clip registers will be reported, but with
> > - * reported finger position clipped to the edge of the active area.
> > - * @palm_detect_thresh - the threshold at which a wide finger is considered a
> > - * palm. A value of 0 inhibits palm detection.
> > - * @motion_sensitivity - specifies the threshold an anchored finger must move
> > - * before it is considered no longer anchored.  High values mean more
> > - * sensitivity.
> > - * @man_track_en - for anchored finger tracking, whether the host (1) or the
> > - * device (0) determines which finger is the tracked finger.
> > - * @man_tracked_finger - when man_track_en is 1, specifies whether finger 0 or
> > - * finger 1 is the tracked finger.
> > - * @delta_x_threshold - 2-D position update interrupts are inhibited unless
> > - * the finger moves more than a certain threshold distance along the X axis.
> > - * @delta_y_threshold - 2-D position update interrupts are inhibited unless
> > - * the finger moves more than a certain threshold distance along the Y axis.
> > - * @velocity - When rel_ballistics is set, this register defines the
> > - * velocity ballistic parameter applied to all relative motion events.
> > - * @acceleration - When rel_ballistics is set, this register defines the
> > - * acceleration ballistic parameter applied to all relative motion events.
> > - * @sensor_max_x_pos - the maximum X coordinate reported by the sensor.
> > - * @sensor_max_y_pos - the maximum Y coordinate reported by the sensor.
> > - */
> > -struct f11_2d_ctrl0_9 {
> > -	/* F11_2D_Ctrl0 */
> > -	u8 reporting_mode:3;
> > -	u8 abs_pos_filt:1;
> > -	u8 rel_pos_filt:1;
> > -	u8 rel_ballistics:1;
> > -	u8 dribble:1;
> > -	u8 report_beyond_clip:1;
> > -	/* F11_2D_Ctrl1 */
> > -	u8 palm_detect_thres:4;
> > -	u8 motion_sensitivity:2;
> > -	u8 man_track_en:1;
> > -	u8 man_tracked_finger:1;
> > -	/* F11_2D_Ctrl2 and 3 */
> > -	u8 delta_x_threshold:8;
> > -	u8 delta_y_threshold:8;
> > -	/* F11_2D_Ctrl4 and 5 */
> > -	u8 velocity:8;
> > -	u8 acceleration:8;
> > -	/* F11_2D_Ctrl6 thru 9 */
> > -	u16 sensor_max_x_pos:12;
> > -	u8 ctrl7_reserved:4;
> > -	u16 sensor_max_y_pos:12;
> > -	u8 ctrl9_reserved:4;
> > -} __attribute__((__packed__));
> > -
> > -/**
> > - * @single_tap_int_enable - enable tap gesture recognition.
> > - * @tap_n_hold_int_enable - enable tap-and-hold gesture recognition.
> > - * @double_tap_int_enable - enable double-tap gesture recognition.
> > - * @early_tap_int_enable - enable early tap notification.
> > - * @flick_int_enable - enable flick detection.
> > - * @press_int_enable - enable press gesture recognition.
> > - * @pinch_int_enable - enable pinch detection.
> > - */
> > -struct f11_2d_ctrl10 {
> > -	u8 single_tap_int_enable:1;
> > -	u8 tap_n_hold_int_enable:1;
> > -	u8 double_tap_int_enable:1;
> > -	u8 early_tap_int_enable:1;
> > -	u8 flick_int_enable:1;
> > -	u8 press_int_enable:1;
> > -	u8 pinch_int_enable:1;
> > -	u8 reserved:1;
> > -} __attribute__((__packed__));
> > -
> > -/**
> > - * @palm_detect_int_enable - enable palm detection feature.
> > - * @rotate_int_enable - enable rotate gesture detection.
> > - * @touch_shape_int_enable - enable the TouchShape feature.
> > - * @scroll_zone_int_enable - enable scroll zone reporting.
> > - * @multi_finger_scroll_int_enable - enable the multfinger scroll feature.
> > - */
> > -struct f11_2d_ctrl11 {
> > -	u8 palm_detect_int_enable:1;
> > -	u8 rotate_int_enable:1;
> > -	u8 touch_shape_int_enable:1;
> > -	u8 scroll_zone_int_enable:1;
> > -	u8 multi_finger_scroll_int_enable:1;
> > -	u8 reserved:3;
> > -} __attribute__((__packed__));
> > -
> > -/**
> > - * @sens_adjustment - allows a host to alter the overall sensitivity of a
> > - * 2-D sensor. A positive value in this register will make the sensor more
> > - * sensitive than the factory defaults, and a negative value will make it
> > - * less sensitive.
> > - * @hyst_adjustment - increase the touch/no-touch hysteresis by 2 Z-units for
> > - * each one unit increment in this setting.
> > - */
> > -struct f11_2d_ctrl14 {
> > -	s8 sens_adjustment:5;
> > -	u8 hyst_adjustment:3;
> > -} __attribute__((__packed__));
> > -
> > -/**
> > - * @max_tap_time - the maximum duration of a tap, in 10-millisecond units.
> > - */
> > -struct f11_2d_ctrl15 {
> > -	u8 max_tap_time:8;
> > -} __attribute__((__packed__));
> > -
> > -/**
> > - * @min_press_time - The minimum duration required for stationary finger(s) to
> > - * generate a press gesture, in 10-millisecond units.
> > - */
> > -struct f11_2d_ctrl16 {
> > -	u8 min_press_time:8;
> > -} __attribute__((__packed__));
> > +	/* query1 */
> > +	u8 nr_fingers;
> > +	bool has_rel;
> > +	bool has_abs;
> > +	bool has_gestures;
> > +	bool has_sensitivity_adjust;
> > +	bool configurable;
> >  
> > -/**
> > - * @max_tap_distance - Determines the maximum finger movement allowed during
> > - * a tap, in 0.1-millimeter units.
> > - */
> > -struct f11_2d_ctrl17 {
> > -	u8 max_tap_distance:8;
> > -} __attribute__((__packed__));
> > +	/* query2 */
> > +	u8 nr_x_electrodes;
> >  
> > -/**
> > - * @min_flick_distance - the minimum finger movement for a flick gesture,
> > - * in 1-millimeter units.
> > - * @min_flick_speed - the minimum finger speed for a flick gesture, in
> > - * 10-millimeter/second units.
> > - */
> > -struct f11_2d_ctrl18_19 {
> > -	u8 min_flick_distance:8;
> > -	u8 min_flick_speed:8;
> > -} __attribute__((__packed__));
> > +	/* query3 */
> > +	u8 nr_y_electrodes;
> >  
> > -/**
> > - * @pen_detect_enable - enable reporting of stylus activity.
> > - * @pen_jitter_filter_enable - Setting this enables the stylus anti-jitter
> > - * filter.
> > - * @pen_z_threshold - This is the stylus-detection lower threshold. Smaller
> > - * values result in higher sensitivity.
> > - */
> > -struct f11_2d_ctrl20_21 {
> > -	u8 pen_detect_enable:1;
> > -	u8 pen_jitter_filter_enable:1;
> > -	u8 ctrl20_reserved:6;
> > -	u8 pen_z_threshold:8;
> > -} __attribute__((__packed__));
> > +	/* query4 */
> > +	u8 max_electrodes;
> >  
> > -/**
> > - * These are not accessible through sysfs yet.
> > - *
> > - * @proximity_detect_int_en - enable proximity detection feature.
> > - * @proximity_jitter_filter_en - enables an anti-jitter filter on proximity
> > - * data.
> > - * @proximity_detection_z_threshold - the threshold for finger-proximity
> > - * detection.
> > - * @proximity_delta_x_threshold - In reduced-reporting modes, this is the
> > - * threshold for proximate-finger movement in the direction parallel to the
> > - * X-axis.
> > - * @proximity_delta_y_threshold - In reduced-reporting modes, this is the
> > - * threshold for proximate-finger movement in the direction parallel to the
> > - * Y-axis.
> > - * * @proximity_delta_Z_threshold - In reduced-reporting modes, this is the
> > - * threshold for proximate-finger movement in the direction parallel to the
> > - * Z-axis.
> > - */
> > -struct f11_2d_ctrl22_26 {
> > -	/* control 22 */
> > -	u8 proximity_detect_int_en:1;
> > -	u8 proximity_jitter_filter_en:1;
> > -	u8 f11_2d_ctrl6_b3__7:6;
> > +	/* query5 */
> > +	u8 abs_data_size;
> > +	bool has_anchored_finger;
> > +	bool has_adj_hyst;
> > +	bool has_dribble;
> > +	bool has_bending_correction;
> > +	bool has_large_object_suppression;
> > +	bool has_jitter_filter;
> >  
> > -	/* control 23 */
> > -	u8 proximity_detection_z_threshold;
> > +	u8 f11_2d_query6;
> >  
> > -	/* control 24 */
> > -	u8 proximity_delta_x_threshold;
> > +	/* query 7 */
> > +	bool has_single_tap;
> > +	bool has_tap_n_hold;
> > +	bool has_double_tap;
> > +	bool has_early_tap;
> > +	bool has_flick;
> > +	bool has_press;
> > +	bool has_pinch;
> > +	bool has_chiral;
> > +
> > +	bool query7_nonzero;
> > +
> > +	/* query 8 */
> > +	bool has_palm_det;
> > +	bool has_rotate;
> > +	bool has_touch_shapes;
> > +	bool has_scroll_zones;
> > +	bool has_individual_scroll_zones;
> > +	bool has_mf_scroll;
> > +	bool has_mf_edge_motion;
> > +	bool has_mf_scroll_inertia;
> > +
> > +	bool query8_nonzero;
> > +
> > +	/* Query 9 */
> > +	bool has_pen;
> > +	bool has_proximity;
> > +	bool has_palm_det_sensitivity;
> > +	bool has_suppress_on_palm_detect;
> > +	bool has_two_pen_thresholds;
> > +	bool has_contact_geometry;
> > +	bool has_pen_hover_discrimination;
> > +	bool has_pen_filters;
> > +
> > +	/* Query 10 */
> > +	u8 nr_touch_shapes;
> > +
> > +	/* Query 11. */
> > +	bool has_z_tuning;
> > +	bool has_algorithm_selection;
> > +	bool has_w_tuning;
> > +	bool has_pitch_info;
> > +	bool has_finger_size;
> > +	bool has_segmentation_aggressiveness;
> > +	bool has_XY_clip;
> > +	bool has_drumming_filter;
> > +
> > +	/* Query 12 */
> > +	bool has_gapless_finger;
> > +	bool has_gapless_finger_tuning;
> > +	bool has_8bit_w;
> > +	bool has_adjustable_mapping;
> > +	bool has_info2;
> > +	bool has_physical_props;
> > +	bool has_finger_limit;
> > +	bool has_linear_coeff_2;
> > +
> > +	/* Query 13 */
> > +	u8 jitter_window_size;
> > +	u8 jitter_filter_type;
> > +
> > +	/* Query 14 */
> > +	u8 light_control;
> > +	bool is_clear;
> > +	u8 clickpad_props;
> > +	u8 mouse_buttons;
> > +	bool has_advanced_gestures;
> > +};
> >  
> > -	/* control 25 */
> > -	u8 proximity_delta_y_threshold;
> > +/* Defs for Ctrl0. */
> > +#define RMI_F11_REPORT_MODE_MASK        0x07
> > +#define RMI_F11_ABS_POS_FILT            (1 << 3)
> > +#define RMI_F11_REL_POS_FILT            (1 << 4)
> > +#define RMI_F11_REL_BALLISTICS          (1 << 5)
> > +#define RMI_F11_DRIBBLE                 (1 << 6)
> > +#define RMI_F11_REPORT_BEYOND_CLIP      (1 << 7)
> >  
> > -	/* control 26 */
> > -	u8 proximity_delta_z_threshold;
> > -} __attribute__((__packed__));
> > +/* Defs for Ctrl1. */
> > +#define RMI_F11_PALM_DETECT_THRESH_MASK 0x0F
> > +#define RMI_F11_MOTION_SENSITIVITY_MASK 0x30
> > +#define RMI_F11_MANUAL_TRACKING         (1 << 6)
> > +#define RMI_F11_MANUAL_TRACKED_FINGER   (1 << 7)
> >  
> > -/**
> > - * @palm_detecy_sensitivity - When this value is small, smaller objects will
> > - * be identified as palms; when this value is large, only larger objects will
> > - * be identified as palms. 0 represents the factory default.
> > - * @suppress_on_palm_detect - when set, all F11 interrupts except palm_detect
> > - * are suppressed while a palm is detected.
> > - */
> > -struct f11_2d_ctrl27 {
> > -	s8 palm_detect_sensitivity:4;
> > -	u8 suppress_on_palm_detect:1;
> > -	u8 f11_2d_ctrl27_b5__7:3;
> > -} __attribute__((__packed__));
> > -
> > -/**
> > - * @multi_finger_scroll_mode - allows choice of multi-finger scroll mode and
> > - * determines whether and how X or Y displacements are reported.
> > - * @edge_motion_en - enables the edge_motion feature.
> > - * @multi_finger_scroll_momentum - controls the length of time that scrolling
> > - * continues after fingers have been lifted.
> > - */
> > -struct f11_2d_ctrl28 {
> > -	u8 multi_finger_scroll_mode:2;
> > -	u8 edge_motion_en:1;
> > -	u8 f11_2d_ctrl28b_3:1;
> > -	u8 multi_finger_scroll_momentum:4;
> > -} __attribute__((__packed__));
> > -
> > -/**
> > - * @z_touch_threshold - Specifies the finger-arrival Z threshold. Large values
> > - * may cause smaller fingers to be rejected.
> > - * @z_touch_hysteresis - Specifies the difference between the finger-arrival
> > - * Z threshold and the finger-departure Z threshold.
> > - */
> > -struct f11_2d_ctrl29_30 {
> > -	u8 z_touch_threshold;
> > -	u8 z_touch_hysteresis;
> > -} __attribute__((__packed__));
> > +#define RMI_F11_DELTA_X_THRESHOLD       2
> > +#define RMI_F11_DELTA_Y_THRESHOLD       3
> >  
> > +#define RMI_F11_CTRL_REG_COUNT          10
> >  
> >  struct f11_2d_ctrl {
> > -	struct f11_2d_ctrl0_9		 *ctrl0_9;
> > -	u16				ctrl0_9_address;
> > -	struct f11_2d_ctrl10		*ctrl10;
> > -	struct f11_2d_ctrl11		*ctrl11;
> > -	u8				ctrl12_size;
> > -	struct f11_2d_ctrl14		*ctrl14;
> > -	struct f11_2d_ctrl15		*ctrl15;
> > -	struct f11_2d_ctrl16		*ctrl16;
> > -	struct f11_2d_ctrl17		*ctrl17;
> > -	struct f11_2d_ctrl18_19		*ctrl18_19;
> > -	struct f11_2d_ctrl20_21		*ctrl20_21;
> > -	struct f11_2d_ctrl22_26		*ctrl22_26;
> > -	struct f11_2d_ctrl27		*ctrl27;
> > -	struct f11_2d_ctrl28		*ctrl28;
> > -	struct f11_2d_ctrl29_30		*ctrl29_30;
> > +	u8              ctrl0_9[RMI_F11_CTRL_REG_COUNT];
> > +	u16             ctrl0_9_address;
> >  };
> >  
> > -/**
> > - * @x_msb - top 8 bits of X finger position.
> > - * @y_msb - top 8 bits of Y finger position.
> > - * @x_lsb - bottom 4 bits of X finger position.
> > - * @y_lsb - bottom 4 bits of Y finger position.
> > - * @w_y - contact patch width along Y axis.
> > - * @w_x - contact patch width along X axis.
> > - * @z - finger Z value (proxy for pressure).
> > - */
> > -struct f11_2d_data_1_5 {
> > -	u8 x_msb;
> > -	u8 y_msb;
> > -	u8 x_lsb:4;
> > -	u8 y_lsb:4;
> > -	u8 w_y:4;
> > -	u8 w_x:4;
> > -	u8 z;
> > -} __attribute__((__packed__));
> > -
> > -/**
> > - * @delta_x - relative motion along X axis.
> > - * @delta_y - relative motion along Y axis.
> > - */
> > -struct f11_2d_data_6_7 {
> > -	s8 delta_x;
> > -	s8 delta_y;
> > -} __attribute__((__packed__));
> > -
> > -/**
> > - * @single_tap - a single tap was recognized.
> > - * @tap_and_hold - a tap-and-hold gesture was recognized.
> > - * @double_tap - a double tap gesture was recognized.
> > - * @early_tap - a tap gesture might be happening.
> > - * @flick - a flick gesture was detected.
> > - * @press - a press gesture was recognized.
> > - * @pinch - a pinch gesture was detected.
> > - */
> > -struct f11_2d_data_8 {
> > -	bool single_tap:1;
> > -	bool tap_and_hold:1;
> > -	bool double_tap:1;
> > -	bool early_tap:1;
> > -	bool flick:1;
> > -	bool press:1;
> > -	bool pinch:1;
> > -} __attribute__((__packed__));
> > -
> > -/**
> > - * @palm_detect - a palm or other large object is in contact with the sensor.
> > - * @rotate - a rotate gesture was detected.
> > - * @shape - a TouchShape has been activated.
> > - * @scrollzone - scrolling data is available.
> > - * @finger_count - number of fingers involved in the reported gesture.
> > - */
> > -struct f11_2d_data_9 {
> > -	bool palm_detect:1;
> > -	bool rotate:1;
> > -	bool shape:1;
> > -	bool scrollzone:1;
> > -	u8 finger_count:3;
> > -} __attribute__((__packed__));
> > -
> > -/**
> > - * @pinch_motion - when a pinch gesture is detected, this is the change in
> > - * distance between the two fingers since this register was last read.
> > - */
> > -struct f11_2d_data_10 {
> > -	s8 pinch_motion;
> > -} __attribute__((__packed__));
> > +#define RMI_F11_ABS_BYTES 5
> > +#define RMI_F11_REL_BYTES 2
> >  
> > -/**
> > - * @x_flick_dist - when a flick gesture is detected,  the distance of flick
> > - * gesture in X direction.
> > - * @y_flick_dist - when a flick gesture is detected,  the distance of flick
> > - * gesture in Y direction.
> > - * @flick_time - the total time of the flick gesture, in 10ms units.
> > - */
> > -struct f11_2d_data_10_12 {
> > -	s8 x_flick_dist;
> > -	s8 y_flick_dist;
> > -	u8 flick_time;
> > -} __attribute__((__packed__));
> > +/* Defs for Data 8 */
> >  
> > -/**
> > - * @motion - when a rotate gesture is detected, the accumulated distance
> > - * of the rotate motion. Clockwise motion is positive and counterclockwise
> > - * motion is negative.
> > - * @finger_separation - when a rotate gesture is detected, the distance
> > - * between the fingers.
> > - */
> > -struct f11_2d_data_11_12 {
> > -	s8 motion;
> > -	u8 finger_separation;
> > -} __attribute__((__packed__));
> > +#define RMI_F11_SINGLE_TAP              (1 << 0)
> > +#define RMI_F11_TAP_AND_HOLD            (1 << 1)
> > +#define RMI_F11_DOUBLE_TAP              (1 << 2)
> > +#define RMI_F11_EARLY_TAP               (1 << 3)
> > +#define RMI_F11_FLICK                   (1 << 4)
> > +#define RMI_F11_PRESS                   (1 << 5)
> > +#define RMI_F11_PINCH                   (1 << 6)
> >  
> > -/**
> > - * @shape_n - a bitmask of the currently activate TouchShapes (if any).
> > - */
> > -struct f11_2d_data_13 {
> > -	u8 shape_n;
> > -} __attribute__((__packed__));
> > +/* Defs for Data 9 */
> >  
> > -/**
> > - * @horizontal - chiral scrolling distance in the X direction.
> > - * @vertical - chiral scrolling distance in the Y direction.
> > - */
> > -struct f11_2d_data_14_15 {
> > -	s8 horizontal;
> > -	s8 vertical;
> > -} __attribute__((__packed__));
> > +#define RMI_F11_PALM_DETECT                     (1 << 0)
> > +#define RMI_F11_ROTATE                          (1 << 1)
> > +#define RMI_F11_SHAPE                           (1 << 2)
> > +#define RMI_F11_SCROLLZONE                      (1 << 3)
> > +#define RMI_F11_GESTURE_FINGER_COUNT_MASK       0x70
> >  
> > -/**
> > - * @x_low - scroll zone motion along the lower edge of the sensor.
> > - * @y_right - scroll zone motion along the right edge of the sensor.
> > - * @x_upper - scroll zone motion along the upper edge of the sensor.
> > - * @y_left - scroll zone motion along the left edge of the sensor.
> > +/** Handy pointers into our data buffer.
> > + *
> > + * @f_state - start of finger state registers.
> > + * @abs_pos - start of absolute position registers (if present).
> > + * @rel_pos - start of relative data registers (if present).
> > + * @gest_1  - gesture flags (if present).
> > + * @gest_2  - gesture flags & finger count (if present).
> > + * @pinch   - pinch motion register (if present).
> > + * @flick   - flick distance X & Y, flick time (if present).
> > + * @rotate  - rotate motion and finger separation.
> > + * @multi_scroll - chiral deltas for X and Y (if present).
> > + * @scroll_zones - scroll deltas for 4 regions (if present).
> >   */
> > -struct f11_2d_data_14_17 {
> > -	s8 x_low;
> > -	s8 y_right;
> > -	s8 x_upper;
> > -	s8 y_left;
> > -} __attribute__((__packed__));
> > -
> >  struct f11_2d_data {
> > -	u8				*f_state;
> > -	const struct f11_2d_data_1_5	*abs_pos;
> > -	const struct f11_2d_data_6_7	*rel_pos;
> > -	const struct f11_2d_data_8	*gest_1;
> > -	const struct f11_2d_data_9	*gest_2;
> > -	const struct f11_2d_data_10	*pinch;
> > -	const struct f11_2d_data_10_12	*flick;
> > -	const struct f11_2d_data_11_12	*rotate;
> > -	const struct f11_2d_data_13	*shapes;
> > -	const struct f11_2d_data_14_15	*multi_scroll;
> > -	const struct f11_2d_data_14_17	*scroll_zones;
> > +	u8	*f_state;
> > +	u8	*abs_pos;
> > +	s8	*rel_pos;
> > +	u8	*gest_1;
> > +	u8	*gest_2;
> > +	s8	*pinch;
> > +	u8	*flick;
> > +	u8	*rotate;
> > +	u8	*shapes;
> > +	s8	*multi_scroll;
> > +	s8	*scroll_zones;
> >  };
> >  
> >  /**
> > @@ -785,11 +535,13 @@ struct f11_2d_sensor {
> >   * poor electrical behavior on resume, where the initial calibration of the
> >   * sensor(s) coming out of sleep state may be bogus.
> >   * @sensors - per sensor data structures.
> > - * @debugfs_rezero_wait - allows control of the rezero_wait value.  Useful
> > - * during system prototyping.
> >   */
> >  struct f11_data {
> > -	struct f11_2d_device_query dev_query;
> > +	bool has_query9;
> > +	bool has_query11;
> > +	bool has_query12;
> > +	bool has_query27;
> > +	bool has_query28;
> >  	struct f11_2d_ctrl dev_controls;
> >  	struct mutex dev_controls_mutex;
> >  	u16 rezero_wait_ms;
> > @@ -809,7 +561,7 @@ enum finger_state_values {
> >  static int get_tool_type(struct f11_2d_sensor *sensor, u8 finger_state)
> >  {
> >  	if (IS_ENABLED(CONFIG_RMI4_F11_PEN) &&
> > -			sensor->sens_query.query9.has_pen &&
> > +			sensor->sens_query.has_pen &&
> >  			finger_state == F11_PEN)
> >  		return MT_TOOL_PEN;
> >  	return MT_TOOL_FINGER;
> > @@ -822,8 +574,8 @@ static void rmi_f11_rel_pos_report(struct f11_2d_sensor *sensor, u8 n_finger)
> >  	s8 x, y;
> >  	s8 temp;
> >  
> > -	x = data->rel_pos[n_finger].delta_x;
> > -	y = data->rel_pos[n_finger].delta_y;
> > +	x = data->rel_pos[n_finger * 2];
> > +	y = data->rel_pos[n_finger * 2 + 1];
> >  
> >  	x = min(F11_REL_POS_MAX, max(F11_REL_POS_MIN, (int)x));
> >  	y = min(F11_REL_POS_MAX, max(F11_REL_POS_MIN, (int)y));
> > @@ -856,17 +608,18 @@ static void rmi_f11_abs_pos_report(struct f11_data *f11,
> >  	u16 x, y, z;
> >  	int w_x, w_y, w_max, w_min, orient;
> >  	int temp;
> > +	u8 abs_base = n_finger * RMI_F11_ABS_BYTES;
> >  
> >  	if (finger_state) {
> > -		x = ((data->abs_pos[n_finger].x_msb << 4) |
> > -			data->abs_pos[n_finger].x_lsb);
> > -		y = ((data->abs_pos[n_finger].y_msb << 4) |
> > -			data->abs_pos[n_finger].y_lsb);
> > -		z = data->abs_pos[n_finger].z;
> > -		w_x = data->abs_pos[n_finger].w_x;
> > -		w_y = data->abs_pos[n_finger].w_y;
> > +		x = (data->abs_pos[abs_base] << 4) |
> > +			(data->abs_pos[abs_base + 2] & 0x0F);
> > +		y = (data->abs_pos[abs_base + 1] << 4) |
> > +			(data->abs_pos[abs_base + 2] >> 4);
> > +		w_x = data->abs_pos[abs_base + 3] & 0x0F;
> > +		w_y = data->abs_pos[abs_base + 3] >> 4;
> >  		w_max = max(w_x, w_y);
> >  		w_min = min(w_x, w_y);
> > +		z = data->abs_pos[abs_base + 4];
> >  
> >  		if (axis_align->swap_axes) {
> >  			temp = x;
> > @@ -976,38 +729,36 @@ static int f11_2d_construct_data(struct f11_2d_sensor *sensor)
> >  	struct f11_2d_data *data = &sensor->data;
> >  	int i;
> >  
> > -	sensor->nbr_fingers = (query->info.number_of_fingers == 5 ? 10 :
> > -				query->info.number_of_fingers + 1);
> > +	sensor->nbr_fingers = (query->nr_fingers == 5 ? 10 :
> > +				query->nr_fingers + 1);
> >  
> >  	sensor->pkt_size = DIV_ROUND_UP(sensor->nbr_fingers, 4);
> >  
> > -	if (query->info.has_abs)
> > +	if (query->has_abs)
> >  		sensor->pkt_size += (sensor->nbr_fingers * 5);
> >  
> > -	if (query->info.has_rel)
> > +	if (query->has_rel)
> >  		sensor->pkt_size +=  (sensor->nbr_fingers * 2);
> >  
> >  	/* Check if F11_2D_Query7 is non-zero */
> > -	if (has_gesture_bits(&query->gesture_info, 0))
> > +	if (query->query7_nonzero)
> >  		sensor->pkt_size += sizeof(u8);
> >  
> >  	/* Check if F11_2D_Query7 or F11_2D_Query8 is non-zero */
> > -	if (has_gesture_bits(&query->gesture_info, 0) ||
> > -				has_gesture_bits(&query->gesture_info, 1))
> > +	if (query->query7_nonzero || query->query8_nonzero)
> >  		sensor->pkt_size += sizeof(u8);
> >  
> > -	if (query->gesture_info.has_pinch || query->gesture_info.has_flick
> > -			|| query->gesture_info.has_rotate) {
> > +	if (query->has_pinch || query->has_flick || query->has_rotate) {
> >  		sensor->pkt_size += 3;
> > -		if (!query->gesture_info.has_flick)
> > +		if (!query->has_flick)
> >  			sensor->pkt_size--;
> > -		if (!query->gesture_info.has_rotate)
> > +		if (!query->has_rotate)
> >  			sensor->pkt_size--;
> >  	}
> >  
> > -	if (query->gesture_info.has_touch_shapes)
> > +	if (query->has_touch_shapes)
> >  		sensor->pkt_size +=
> > -			DIV_ROUND_UP(query->ts_info.nbr_touch_shapes + 1, 8);
> > +			DIV_ROUND_UP(query->nr_touch_shapes + 1, 8);
> >  
> >  	sensor->data_pkt = kzalloc(sensor->pkt_size, GFP_KERNEL);
> >  	if (!sensor->data_pkt)
> > @@ -1016,58 +767,52 @@ static int f11_2d_construct_data(struct f11_2d_sensor *sensor)
> >  	data->f_state = sensor->data_pkt;
> >  	i = DIV_ROUND_UP(sensor->nbr_fingers, 4);
> >  
> > -	if (query->info.has_abs) {
> > -		data->abs_pos = (struct f11_2d_data_1_5 *)
> > -				&sensor->data_pkt[i];
> > -		i += (sensor->nbr_fingers * 5);
> > +	if (query->has_abs) {
> > +		data->abs_pos = &sensor->data_pkt[i];
> > +		i += (sensor->nbr_fingers * RMI_F11_ABS_BYTES);
> >  	}
> >  
> > -	if (query->info.has_rel) {
> > -		data->rel_pos = (struct f11_2d_data_6_7 *)
> > -				&sensor->data_pkt[i];
> > -		i += (sensor->nbr_fingers * 2);
> > +	if (query->has_rel) {
> > +		data->rel_pos = &sensor->data_pkt[i];
> > +		i += (sensor->nbr_fingers * RMI_F11_REL_BYTES);
> >  	}
> >  
> > -	if (has_gesture_bits(&query->gesture_info, 0)) {
> > -		data->gest_1 = (struct f11_2d_data_8 *)&sensor->data_pkt[i];
> > +	if (query->query7_nonzero) {
> > +		data->gest_1 = &sensor->data_pkt[i];
> >  		i++;
> >  	}
> >  
> > -	if (has_gesture_bits(&query->gesture_info, 0) ||
> > -				has_gesture_bits(&query->gesture_info, 1)) {
> > -		data->gest_2 = (struct f11_2d_data_9 *)&sensor->data_pkt[i];
> > +	if (query->query7_nonzero || query->query8_nonzero) {
> > +		data->gest_2 = &sensor->data_pkt[i];
> >  		i++;
> >  	}
> >  
> > -	if (query->gesture_info.has_pinch) {
> > -		data->pinch = (struct f11_2d_data_10 *)&sensor->data_pkt[i];
> > +	if (query->has_pinch) {
> > +		data->pinch = &sensor->data_pkt[i];
> >  		i++;
> >  	}
> >  
> > -	if (query->gesture_info.has_flick) {
> > -		if (query->gesture_info.has_pinch) {
> > -			data->flick = (struct f11_2d_data_10_12 *)data->pinch;
> > +	if (query->has_flick) {
> > +		if (query->has_pinch) {
> > +			data->flick = data->pinch;
> >  			i += 2;
> >  		} else {
> > -			data->flick = (struct f11_2d_data_10_12 *)
> > -					&sensor->data_pkt[i];
> > +			data->flick = &sensor->data_pkt[i];
> >  			i += 3;
> >  		}
> >  	}
> >  
> > -	if (query->gesture_info.has_rotate) {
> > -		if (query->gesture_info.has_flick) {
> > -			data->rotate = (struct f11_2d_data_11_12 *)
> > -					(data->flick + 1);
> > +	if (query->has_rotate) {
> > +		if (query->has_flick) {
> > +			data->rotate = data->flick + 1;
> >  		} else {
> > -			data->rotate = (struct f11_2d_data_11_12 *)
> > -					&sensor->data_pkt[i];
> > +			data->rotate = &sensor->data_pkt[i];
> >  			i += 2;
> >  		}
> >  	}
> >  
> > -	if (query->gesture_info.has_touch_shapes)
> > -		data->shapes = (struct f11_2d_data_13 *)&sensor->data_pkt[i];
> > +	if (query->has_touch_shapes)
> > +		data->shapes = &sensor->data_pkt[i];
> >  
> >  	return 0;
> >  }
> > @@ -1075,213 +820,16 @@ static int f11_2d_construct_data(struct f11_2d_sensor *sensor)
> >  static int f11_read_control_regs(struct rmi_function *fn,
> >  				struct f11_2d_ctrl *ctrl, u16 ctrl_base_addr) {
> >  	struct rmi_device *rmi_dev = fn->rmi_dev;
> > -	u16 read_address = ctrl_base_addr;
> >  	int error = 0;
> >  
> > -	ctrl->ctrl0_9_address = read_address;
> > -	error = rmi_read_block(rmi_dev, read_address, ctrl->ctrl0_9,
> > -		sizeof(*ctrl->ctrl0_9));
> > +	ctrl->ctrl0_9_address = ctrl_base_addr;
> > +	error = rmi_read_block(rmi_dev, ctrl_base_addr, ctrl->ctrl0_9,
> > +				RMI_F11_CTRL_REG_COUNT);
> >  	if (error < 0) {
> >  		dev_err(&fn->dev, "Failed to read ctrl0, code: %d.\n", error);
> >  		return error;
> >  	}
> > -	read_address += sizeof(*ctrl->ctrl0_9);
> >  
> > -	if (ctrl->ctrl10) {
> > -		error = rmi_read_block(rmi_dev, read_address,
> > -			ctrl->ctrl10, sizeof(*ctrl->ctrl10));
> > -		if (error < 0) {
> > -			dev_err(&fn->dev,
> > -				"Failed to read ctrl10, code: %d.\n", error);
> > -			return error;
> > -		}
> > -		read_address += sizeof(*ctrl->ctrl10);
> > -	}
> > -
> > -	if (ctrl->ctrl11) {
> > -		error = rmi_read_block(rmi_dev, read_address,
> > -			ctrl->ctrl11, sizeof(*ctrl->ctrl11));
> > -		if (error < 0) {
> > -			dev_err(&fn->dev,
> > -				"Failed to read ctrl11, code: %d.\n", error);
> > -			return error;
> > -		}
> > -		read_address += sizeof(*ctrl->ctrl11);
> > -	}
> > -
> > -	if (ctrl->ctrl14) {
> > -		error = rmi_read_block(rmi_dev, read_address,
> > -			ctrl->ctrl14, sizeof(*ctrl->ctrl14));
> > -		if (error < 0) {
> > -			dev_err(&fn->dev,
> > -				"Failed to read ctrl14, code: %d.\n", error);
> > -			return error;
> > -		}
> > -		read_address += sizeof(*ctrl->ctrl14);
> > -	}
> > -
> > -	if (ctrl->ctrl15) {
> > -		error = rmi_read_block(rmi_dev, read_address,
> > -			ctrl->ctrl15, sizeof(*ctrl->ctrl15));
> > -		if (error < 0) {
> > -			dev_err(&fn->dev,
> > -				"Failed to read ctrl15, code: %d.\n", error);
> > -			return error;
> > -		}
> > -		read_address += sizeof(*ctrl->ctrl15);
> > -	}
> > -
> > -	if (ctrl->ctrl16) {
> > -		error = rmi_read_block(rmi_dev, read_address,
> > -			ctrl->ctrl16, sizeof(*ctrl->ctrl16));
> > -		if (error < 0) {
> > -			dev_err(&fn->dev,
> > -				"Failed to read ctrl16, code: %d.\n", error);
> > -			return error;
> > -		}
> > -		read_address += sizeof(*ctrl->ctrl16);
> > -	}
> > -
> > -	if (ctrl->ctrl17) {
> > -		error = rmi_read_block(rmi_dev, read_address,
> > -			ctrl->ctrl17, sizeof(*ctrl->ctrl17));
> > -		if (error < 0) {
> > -			dev_err(&fn->dev,
> > -				"Failed to read ctrl17, code: %d.\n", error);
> > -			return error;
> > -		}
> > -		read_address += sizeof(*ctrl->ctrl17);
> > -	}
> > -
> > -	if (ctrl->ctrl18_19) {
> > -		error = rmi_read_block(rmi_dev, read_address,
> > -			ctrl->ctrl18_19, sizeof(*ctrl->ctrl18_19));
> > -		if (error < 0) {
> > -			dev_err(&fn->dev,
> > -				"Failed to read ctrl18_19, code: %d.\n", error);
> > -			return error;
> > -		}
> > -		read_address += sizeof(*ctrl->ctrl18_19);
> > -	}
> > -
> > -	if (ctrl->ctrl20_21) {
> > -		error = rmi_read_block(rmi_dev, read_address,
> > -			ctrl->ctrl20_21, sizeof(*ctrl->ctrl20_21));
> > -		if (error < 0) {
> > -			dev_err(&fn->dev,
> > -				"Failed to read ctrl20_21, code: %d.\n", error);
> > -			return error;
> > -		}
> > -		read_address += sizeof(*ctrl->ctrl20_21);
> > -	}
> > -
> > -	if (ctrl->ctrl22_26) {
> > -		error = rmi_read_block(rmi_dev, read_address,
> > -			ctrl->ctrl22_26, sizeof(*ctrl->ctrl22_26));
> > -		if (error < 0) {
> > -			dev_err(&fn->dev,
> > -				"Failed to read ctrl22_26, code: %d.\n", error);
> > -			return error;
> > -		}
> > -		read_address += sizeof(*ctrl->ctrl22_26);
> > -	}
> > -
> > -	if (ctrl->ctrl27) {
> > -		error = rmi_read_block(rmi_dev, read_address,
> > -			ctrl->ctrl27, sizeof(*ctrl->ctrl27));
> > -		if (error < 0) {
> > -			dev_err(&fn->dev,
> > -				"Failed to read ctrl27, code: %d.\n", error);
> > -			return error;
> > -		}
> > -		read_address += sizeof(*ctrl->ctrl27);
> > -	}
> > -
> > -	if (ctrl->ctrl28) {
> > -		error = rmi_read_block(rmi_dev, read_address,
> > -			ctrl->ctrl28, sizeof(*ctrl->ctrl28));
> > -		if (error < 0) {
> > -			dev_err(&fn->dev,
> > -				"Failed to read ctrl28, code: %d.\n", error);
> > -			return error;
> > -		}
> > -		read_address += sizeof(*ctrl->ctrl28);
> > -	}
> > -
> > -	if (ctrl->ctrl29_30) {
> > -		error = rmi_read_block(rmi_dev, read_address,
> > -			ctrl->ctrl29_30, sizeof(*ctrl->ctrl29_30));
> > -		if (error < 0) {
> > -			dev_err(&fn->dev,
> > -				"Failed to read ctrl29_30, code: %d.\n", error);
> > -			return error;
> > -		}
> > -		read_address += sizeof(*ctrl->ctrl29_30);
> > -	}
> > -	return 0;
> > -}
> > -
> > -static int f11_allocate_control_regs(struct rmi_function *fn,
> > -				struct f11_2d_device_query *device_query,
> > -				struct f11_2d_sensor_queries *sensor_query,
> > -				struct f11_2d_ctrl *ctrl,
> > -				u16 ctrl_base_addr) {
> > -
> > -	ctrl->ctrl0_9 = devm_kzalloc(&fn->dev, sizeof(struct f11_2d_ctrl0_9),
> > -				       GFP_KERNEL);
> > -	if (!ctrl->ctrl0_9)
> > -		return -ENOMEM;
> > -	if (has_gesture_bits(&sensor_query->gesture_info, 0)) {
> > -		ctrl->ctrl10 = devm_kzalloc(&fn->dev,
> > -			sizeof(struct f11_2d_ctrl10), GFP_KERNEL);
> > -		if (!ctrl->ctrl10)
> > -			return -ENOMEM;
> > -	}
> > -
> > -	if (has_gesture_bits(&sensor_query->gesture_info, 1)) {
> > -		ctrl->ctrl11 = devm_kzalloc(&fn->dev,
> > -			sizeof(struct f11_2d_ctrl11), GFP_KERNEL);
> > -		if (!ctrl->ctrl11)
> > -			return -ENOMEM;
> > -	}
> > -
> > -	if (device_query->has_query9 && sensor_query->query9.has_pen) {
> > -		ctrl->ctrl20_21 = devm_kzalloc(&fn->dev,
> > -			sizeof(struct f11_2d_ctrl20_21), GFP_KERNEL);
> > -		if (!ctrl->ctrl20_21)
> > -			return -ENOMEM;
> > -	}
> > -
> > -	if (device_query->has_query9 && sensor_query->query9.has_proximity) {
> > -		ctrl->ctrl22_26 = devm_kzalloc(&fn->dev,
> > -			sizeof(struct f11_2d_ctrl22_26), GFP_KERNEL);
> > -		if (!ctrl->ctrl22_26)
> > -			return -ENOMEM;
> > -	}
> > -
> > -	if (device_query->has_query9 &&
> > -		(sensor_query->query9.has_palm_det_sensitivity ||
> > -		sensor_query->query9.has_suppress_on_palm_detect)) {
> > -		ctrl->ctrl27 = devm_kzalloc(&fn->dev,
> > -			sizeof(struct f11_2d_ctrl27), GFP_KERNEL);
> > -		if (!ctrl->ctrl27)
> > -			return -ENOMEM;
> > -	}
> > -
> > -	if (sensor_query->gesture_info.has_multi_finger_scroll) {
> > -		ctrl->ctrl28 = devm_kzalloc(&fn->dev,
> > -			sizeof(struct f11_2d_ctrl28), GFP_KERNEL);
> > -		if (!ctrl->ctrl28)
> > -			return -ENOMEM;
> > -	}
> > -
> > -	if (device_query->has_query11 &&
> > -			sensor_query->features_1.has_z_tuning) {
> > -		ctrl->ctrl29_30 = devm_kzalloc(&fn->dev,
> > -			sizeof(struct f11_2d_ctrl29_30), GFP_KERNEL);
> > -		if (!ctrl->ctrl29_30)
> > -			return -ENOMEM;
> > -	}
> >  
> >  	return 0;
> >  }
> > @@ -1292,139 +840,71 @@ static int f11_write_control_regs(struct rmi_function *fn,
> >  					u16 ctrl_base_addr)
> >  {
> >  	struct rmi_device *rmi_dev = fn->rmi_dev;
> > -	u16 write_address = ctrl_base_addr;
> >  	int error;
> >  
> > -	error = rmi_write_block(rmi_dev, write_address,
> > -				ctrl->ctrl0_9,
> > -				 sizeof(*ctrl->ctrl0_9));
> > +	error = rmi_write_block(rmi_dev, ctrl_base_addr, ctrl->ctrl0_9,
> > +				RMI_F11_CTRL_REG_COUNT);
> >  	if (error < 0)
> >  		return error;
> > -	write_address += sizeof(ctrl->ctrl0_9);
> > -
> > -	if (ctrl->ctrl10) {
> > -		error = rmi_write_block(rmi_dev, write_address,
> > -					ctrl->ctrl10, sizeof(*ctrl->ctrl10));
> > -		if (error < 0)
> > -			return error;
> > -		write_address++;
> > -	}
> > -
> > -	if (ctrl->ctrl11) {
> > -		error = rmi_write_block(rmi_dev, write_address,
> > -					ctrl->ctrl11, sizeof(*ctrl->ctrl11));
> > -		if (error < 0)
> > -			return error;
> > -		write_address++;
> > -	}
> > -
> > -	if (ctrl->ctrl14) {
> > -		error = rmi_write_block(rmi_dev, write_address,
> > -				ctrl->ctrl14, sizeof(ctrl->ctrl14));
> > -		if (error < 0)
> > -			return error;
> > -		write_address += sizeof(*ctrl->ctrl15);
> > -	}
> >  
> > -	if (ctrl->ctrl15) {
> > -		error = rmi_write_block(rmi_dev, write_address,
> > -				ctrl->ctrl15, sizeof(*ctrl->ctrl15));
> > -		if (error < 0)
> > -			return error;
> > -		write_address += sizeof(*ctrl->ctrl15);
> > -	}
> > -
> > -	if (ctrl->ctrl16) {
> > -		error = rmi_write_block(rmi_dev, write_address,
> > -				ctrl->ctrl16, sizeof(*ctrl->ctrl16));
> > -		if (error < 0)
> > -			return error;
> > -		write_address += sizeof(*ctrl->ctrl16);
> > -	}
> > -
> > -	if (ctrl->ctrl17) {
> > -		error = rmi_write_block(rmi_dev, write_address,
> > -				ctrl->ctrl17, sizeof(*ctrl->ctrl17));
> > -		if (error < 0)
> > -			return error;
> > -		write_address += sizeof(*ctrl->ctrl17);
> > -	}
> > -
> > -	if (ctrl->ctrl18_19) {
> > -		error = rmi_write_block(rmi_dev, write_address,
> > -			ctrl->ctrl18_19, sizeof(*ctrl->ctrl18_19));
> > -		if (error < 0)
> > -			return error;
> > -		write_address += sizeof(*ctrl->ctrl18_19);
> > -	}
> > -
> > -	if (ctrl->ctrl20_21) {
> > -		error = rmi_write_block(rmi_dev, write_address,
> > -			ctrl->ctrl20_21, sizeof(*ctrl->ctrl20_21));
> > -		if (error < 0)
> > -			return error;
> > -		write_address += sizeof(*ctrl->ctrl20_21);
> > -	}
> > -
> > -	if (ctrl->ctrl22_26) {
> > -		error = rmi_write_block(rmi_dev, write_address,
> > -			ctrl->ctrl22_26, sizeof(*ctrl->ctrl22_26));
> > -		if (error < 0)
> > -			return error;
> > -		write_address += sizeof(*ctrl->ctrl22_26);
> > -	}
> > -
> > -	if (ctrl->ctrl27) {
> > -		error = rmi_write_block(rmi_dev, write_address,
> > -			ctrl->ctrl27, sizeof(*ctrl->ctrl27));
> > -		if (error < 0)
> > -			return error;
> > -		write_address += sizeof(*ctrl->ctrl27);
> > -	}
> > -
> > -	if (ctrl->ctrl28) {
> > -		error = rmi_write_block(rmi_dev, write_address,
> > -			ctrl->ctrl28, sizeof(*ctrl->ctrl28));
> > -		if (error < 0)
> > -			return error;
> > -		write_address += sizeof(*ctrl->ctrl28);
> > -	}
> > -
> > -	if (ctrl->ctrl29_30) {
> > -		error = rmi_write_block(rmi_dev, write_address,
> > -					ctrl->ctrl29_30,
> > -					sizeof(struct f11_2d_ctrl29_30));
> > -		if (error < 0)
> > -			return error;
> > -		write_address += sizeof(struct f11_2d_ctrl29_30);
> > -	}
> >  
> >  	return 0;
> >  }
> >  
> >  static int rmi_f11_get_query_parameters(struct rmi_device *rmi_dev,
> > -			struct f11_2d_device_query *dev_query,
> > +			struct f11_data *f11,
> >  			struct f11_2d_sensor_queries *sensor_query,
> >  			u16 query_base_addr)
> >  {
> >  	int query_size;
> >  	int rc;
> > +	u8 query_buf[RMI_F11_QUERY_SIZE];
> >  
> > -	rc = rmi_read_block(rmi_dev, query_base_addr,
> > -			    &sensor_query->info, sizeof(sensor_query->info));
> > +	rc = rmi_read_block(rmi_dev, query_base_addr, query_buf,
> > +				RMI_F11_QUERY_SIZE);
> >  	if (rc < 0)
> >  		return rc;
> > -	query_size = sizeof(sensor_query->info);
> >  
> > -	if (sensor_query->info.has_abs) {
> > -		rc = rmi_read(rmi_dev, query_base_addr + query_size,
> > -					&sensor_query->abs_info);
> > +	sensor_query->nr_fingers = query_buf[0] & RMI_F11_NR_FINGERS_MASK;
> > +	sensor_query->has_rel = !!(query_buf[0] & RMI_F11_HAS_REL);
> > +	sensor_query->has_abs = !!(query_buf[0] & RMI_F11_HAS_ABS);
> > +	sensor_query->has_gestures = !!(query_buf[0] & RMI_F11_HAS_GESTURES);
> > +	sensor_query->has_sensitivity_adjust =
> > +		!!(query_buf[0] && RMI_F11_HAS_SENSITIVITY_ADJ);
> > +	sensor_query->configurable = !!(query_buf[0] & RMI_F11_CONFIGURABLE);
> > +
> > +	sensor_query->nr_x_electrodes =
> > +				query_buf[1] & RMI_F11_NR_ELECTRODES_MASK;
> > +	sensor_query->nr_y_electrodes =
> > +				query_buf[2] & RMI_F11_NR_ELECTRODES_MASK;
> > +	sensor_query->max_electrodes =
> > +				query_buf[3] & RMI_F11_NR_ELECTRODES_MASK;
> > +
> > +	query_size = RMI_F11_QUERY_SIZE;
> > +
> > +	if (sensor_query->has_abs) {
> > +		rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf);
> >  		if (rc < 0)
> >  			return rc;
> > +
> > +		sensor_query->abs_data_size =
> > +			query_buf[0] & RMI_F11_ABS_DATA_SIZE_MASK;
> > +		sensor_query->has_anchored_finger =
> > +			!!(query_buf[0] & RMI_F11_HAS_ANCHORED_FINGER);
> > +		sensor_query->has_adj_hyst =
> > +			!!(query_buf[0] & RMI_F11_HAS_ADJ_HYST);
> > +		sensor_query->has_dribble =
> > +			!!(query_buf[0] & RMI_F11_HAS_DRIBBLE);
> > +		sensor_query->has_bending_correction =
> > +			!!(query_buf[0] & RMI_F11_HAS_BENDING_CORRECTION);
> > +		sensor_query->has_large_object_suppression =
> > +		!!(query_buf[0] && RMI_F11_HAS_LARGE_OBJECT_SUPPRESSION);
> > +		sensor_query->has_jitter_filter =
> > +			!!(query_buf[0] & RMI_F11_HAS_JITTER_FILTER);
> >  		query_size++;
> >  	}
> >  
> > -	if (sensor_query->info.has_rel) {
> > +	if (sensor_query->has_rel) {
> >  		rc = rmi_read(rmi_dev, query_base_addr + query_size,
> >  					&sensor_query->f11_2d_query6);
> >  		if (rc < 0)
> > @@ -1432,67 +912,173 @@ static int rmi_f11_get_query_parameters(struct rmi_device *rmi_dev,
> >  		query_size++;
> >  	}
> >  
> > -	if (sensor_query->info.has_gestures) {
> > +	if (sensor_query->has_gestures) {
> >  		rc = rmi_read_block(rmi_dev, query_base_addr + query_size,
> > -					&sensor_query->gesture_info,
> > -					sizeof(sensor_query->gesture_info));
> > +					query_buf, RMI_F11_QUERY_GESTURE_SIZE);
> >  		if (rc < 0)
> >  			return rc;
> > -		query_size += sizeof(sensor_query->gesture_info);
> > -	}
> >  
> > -	if (dev_query->has_query9) {
> > -		rc = rmi_read_block(rmi_dev, query_base_addr + query_size,
> > -					&sensor_query->query9,
> > -					sizeof(sensor_query->query9));
> > +		sensor_query->has_single_tap =
> > +			!!(query_buf[0] & RMI_F11_HAS_SINGLE_TAP);
> > +		sensor_query->has_tap_n_hold =
> > +			!!(query_buf[0] & RMI_F11_HAS_TAP_AND_HOLD);
> > +		sensor_query->has_double_tap =
> > +			!!(query_buf[0] & RMI_F11_HAS_DOUBLE_TAP);
> > +		sensor_query->has_early_tap =
> > +			!!(query_buf[0] & RMI_F11_HAS_EARLY_TAP);
> > +		sensor_query->has_flick =
> > +			!!(query_buf[0] & RMI_F11_HAS_FLICK);
> > +		sensor_query->has_press =
> > +			!!(query_buf[0] & RMI_F11_HAS_PRESS);
> > +		sensor_query->has_pinch =
> > +			!!(query_buf[0] & RMI_F11_HAS_PINCH);
> > +		sensor_query->has_chiral =
> > +			!!(query_buf[0] & RMI_F11_HAS_CHIRAL);
> > +
> > +		/* query 8 */
> > +		sensor_query->has_palm_det =
> > +			!!(query_buf[1] & RMI_F11_HAS_PALM_DET);
> > +		sensor_query->has_rotate =
> > +			!!(query_buf[1] & RMI_F11_HAS_ROTATE);
> > +		sensor_query->has_touch_shapes =
> > +			!!(query_buf[1] & RMI_F11_HAS_TOUCH_SHAPES);
> > +		sensor_query->has_scroll_zones =
> > +			!!(query_buf[1] & RMI_F11_HAS_SCROLL_ZONES);
> > +		sensor_query->has_individual_scroll_zones =
> > +			!!(query_buf[1] & RMI_F11_HAS_INDIVIDUAL_SCROLL_ZONES);
> > +		sensor_query->has_mf_scroll =
> > +			!!(query_buf[1] & RMI_F11_HAS_MF_SCROLL);
> > +		sensor_query->has_mf_edge_motion =
> > +			!!(query_buf[1] & RMI_F11_HAS_MF_EDGE_MOTION);
> > +		sensor_query->has_mf_scroll_inertia =
> > +			!!(query_buf[1] & RMI_F11_HAS_MF_SCROLL_INERTIA);
> > +
> > +		sensor_query->query7_nonzero = !!(query_buf[0]);
> > +		sensor_query->query8_nonzero = !!(query_buf[1]);
> > +
> > +		query_size += 2;
> > +	}
> > +
> > +	if (f11->has_query9) {
> > +		rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf);
> >  		if (rc < 0)
> >  			return rc;
> > -		query_size += sizeof(sensor_query->query9);
> > +
> > +		sensor_query->has_pen =
> > +			!!(query_buf[0] & RMI_F11_HAS_PEN);
> > +		sensor_query->has_proximity =
> > +			!!(query_buf[0] & RMI_F11_HAS_PROXIMITY);
> > +		sensor_query->has_palm_det_sensitivity =
> > +			!!(query_buf[0] & RMI_F11_HAS_PALM_DET_SENSITIVITY);
> > +		sensor_query->has_suppress_on_palm_detect =
> > +			!!(query_buf[0] & RMI_F11_HAS_SUPPRESS_ON_PALM_DETECT);
> > +		sensor_query->has_two_pen_thresholds =
> > +			!!(query_buf[0] & RMI_F11_HAS_TWO_PEN_THRESHOLDS);
> > +		sensor_query->has_contact_geometry =
> > +			!!(query_buf[0] & RMI_F11_HAS_CONTACT_GEOMETRY);
> > +		sensor_query->has_pen_hover_discrimination =
> > +			!!(query_buf[0] & RMI_F11_HAS_PEN_HOVER_DISCRIMINATION);
> > +		sensor_query->has_pen_filters =
> > +			!!(query_buf[0] & RMI_F11_HAS_PEN_FILTERS);
> > +
> > +		query_size++;
> >  	}
> >  
> > -	if (sensor_query->gesture_info.has_touch_shapes) {
> > -		rc = rmi_read_block(rmi_dev, query_base_addr + query_size,
> > -					&sensor_query->ts_info,
> > -					sizeof(sensor_query->ts_info));
> > +	if (sensor_query->has_touch_shapes) {
> > +		rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf);
> >  		if (rc < 0)
> >  			return rc;
> > -		query_size += sizeof(sensor_query->ts_info);
> > +
> > +		sensor_query->nr_touch_shapes = query_buf[0] &
> > +				RMI_F11_NR_TOUCH_SHAPES_MASK;
> > +
> > +		query_size++;
> >  	}
> >  
> > -	if (dev_query->has_query11) {
> > -		rc = rmi_read_block(rmi_dev, query_base_addr + query_size,
> > -					&sensor_query->features_1,
> > -					sizeof(sensor_query->features_1));
> > +	if (f11->has_query11) {
> > +		rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf);
> >  		if (rc < 0)
> >  			return rc;
> > -		query_size += sizeof(sensor_query->features_1);
> > +
> > +		sensor_query->has_z_tuning =
> > +			!!(query_buf[0] & RMI_F11_HAS_Z_TUNING);
> > +		sensor_query->has_algorithm_selection =
> > +			!!(query_buf[0] & RMI_F11_HAS_ALGORITHM_SELECTION);
> > +		sensor_query->has_w_tuning =
> > +			!!(query_buf[0] & RMI_F11_HAS_W_TUNING);
> > +		sensor_query->has_pitch_info =
> > +			!!(query_buf[0] & RMI_F11_HAS_PITCH_INFO);
> > +		sensor_query->has_finger_size =
> > +			!!(query_buf[0] & RMI_F11_HAS_FINGER_SIZE);
> > +		sensor_query->has_segmentation_aggressiveness =
> > +			!!(query_buf[0] &
> > +				RMI_F11_HAS_SEGMENTATION_AGGRESSIVENESS);
> > +		sensor_query->has_XY_clip =
> > +			!!(query_buf[0] & RMI_F11_HAS_XY_CLIP);
> > +		sensor_query->has_drumming_filter =
> > +			!!(query_buf[0] & RMI_F11_HAS_DRUMMING_FILTER);
> > +
> > +		query_size++;
> >  	}
> >  
> > -	if (dev_query->has_query12) {
> > -		rc = rmi_read_block(rmi_dev, query_base_addr + query_size,
> > -					&sensor_query->features_2,
> > -					sizeof(sensor_query->features_2));
> > +	if (f11->has_query12) {
> > +		rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf);
> >  		if (rc < 0)
> >  			return rc;
> > -		query_size += sizeof(sensor_query->features_2);
> > +
> > +		sensor_query->has_gapless_finger =
> > +			!!(query_buf[0] & RMI_F11_HAS_GAPLESS_FINGER);
> > +		sensor_query->has_gapless_finger_tuning =
> > +			!!(query_buf[0] & RMI_F11_HAS_GAPLESS_FINGER_TUNING);
> > +		sensor_query->has_8bit_w =
> > +			!!(query_buf[0] & RMI_F11_HAS_8BIT_W);
> > +		sensor_query->has_adjustable_mapping =
> > +			!!(query_buf[0] & RMI_F11_HAS_ADJUSTABLE_MAPPING);
> > +		sensor_query->has_info2 =
> > +			!!(query_buf[0] & RMI_F11_HAS_INFO2);
> > +		sensor_query->has_physical_props =
> > +			!!(query_buf[0] & RMI_F11_HAS_PHYSICAL_PROPS);
> > +		sensor_query->has_finger_limit =
> > +			!!(query_buf[0] & RMI_F11_HAS_FINGER_LIMIT);
> > +		sensor_query->has_linear_coeff_2 =
> > +			!!(query_buf[0] & RMI_F11_HAS_LINEAR_COEFF);
> > +
> > +		query_size++;
> >  	}
> >  
> > -	if (sensor_query->abs_info.has_jitter_filter) {
> > -		rc = rmi_read_block(rmi_dev, query_base_addr + query_size,
> > -					&sensor_query->jitter_filter,
> > -					sizeof(sensor_query->jitter_filter));
> > +	if (sensor_query->has_jitter_filter) {
> > +		rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf);
> >  		if (rc < 0)
> >  			return rc;
> > -		query_size += sizeof(sensor_query->jitter_filter);
> > +
> > +		sensor_query->jitter_window_size = query_buf[0] &
> > +			RMI_F11_JITTER_WINDOW_MASK;
> > +		sensor_query->jitter_filter_type = (query_buf[0] &
> > +			RMI_F11_JITTER_FILTER_MASK) >>
> > +			RMI_F11_JITTER_FILTER_SHIFT;
> > +
> > +		query_size++;
> >  	}
> >  
> > -	if (dev_query->has_query12 && sensor_query->features_2.has_info2) {
> > -		rc = rmi_read_block(rmi_dev, query_base_addr + query_size,
> > -				    &sensor_query->info_2,
> > -					sizeof(sensor_query->info_2));
> > +	if (f11->has_query12 && sensor_query->has_info2) {
> > +		rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf);
> >  		if (rc < 0)
> >  			return rc;
> > -		query_size += sizeof(sensor_query->info_2);
> > +
> > +		sensor_query->light_control =
> > +			query_buf[0] & RMI_F11_LIGHT_CONTROL_MASK;
> > +		sensor_query->is_clear =
> > +			!!(query_buf[0] & RMI_F11_IS_CLEAR);
> > +		sensor_query->clickpad_props =
> > +			(query_buf[0] & RMI_F11_CLICKPAD_PROPS_MASK) >>
> > +			RMI_F11_CLICKPAD_PROPS_SHIFT;
> > +		sensor_query->mouse_buttons =
> > +			(query_buf[0] & RMI_F11_MOUSE_BUTTONS_MASK) >>
> > +			RMI_F11_MOUSE_BUTTONS_SHIFT;
> > +		sensor_query->has_advanced_gestures =
> > +			!!(query_buf[0] & RMI_F11_HAS_ADVANCED_GESTURES);
> > +
> > +		query_size++;
> >  	}
> >  
> >  	return query_size;
> > @@ -1506,10 +1092,15 @@ static void f11_set_abs_params(struct rmi_function *fn)
> >  	struct f11_data *f11 = fn->data;
> >  	struct f11_2d_sensor *sensor = &f11->sensor;
> >  	struct input_dev *input = sensor->input;
> > -	u16 device_x_max =
> > -		f11->dev_controls.ctrl0_9->sensor_max_x_pos;
> > -	u16 device_y_max =
> > -		f11->dev_controls.ctrl0_9->sensor_max_y_pos;
> > +	/* These two lines are not doing what we want them to.  So we use
> > +	 * some shifts instead.
> > +	int device_x_max = le16_to_cpu(*(f11->dev_controls.ctrl0_9 + 6));
> > +	int device_y_max = le16_to_cpu(*(f11->dev_controls.ctrl0_9 + 8));
> > +	 */
> > +	u16 device_x_max = f11->dev_controls.ctrl0_9[6] |
> > +			((f11->dev_controls.ctrl0_9[7] & 0x0F) << 8);
> > +	u16 device_y_max = f11->dev_controls.ctrl0_9[8] |
> > +			((f11->dev_controls.ctrl0_9[9] & 0x0F) << 8);
> >  	u16 x_min, x_max, y_min, y_max;
> >  	unsigned int input_flags;
> >  
> > @@ -1517,8 +1108,8 @@ static void f11_set_abs_params(struct rmi_function *fn)
> >  	 * as a touchpad in the platform data
> >  	 */
> >  	if (sensor->sensor_type == rmi_f11_sensor_touchpad ||
> > -			(sensor->sens_query.features_2.has_info2 &&
> > -				!sensor->sens_query.info_2.is_clear))
> > +			(sensor->sens_query.has_info2 &&
> > +				!sensor->sens_query.is_clear))
> >  		input_flags = INPUT_PROP_POINTER;
> >  	else
> >  		input_flags = INPUT_PROP_DIRECT;
> > @@ -1567,8 +1158,7 @@ static void f11_set_abs_params(struct rmi_function *fn)
> >  			y_min, y_max, 0, 0);
> >  	if (!sensor->type_a)
> >  		input_mt_init_slots(input, sensor->nbr_fingers, input_flags);
> > -	if (IS_ENABLED(CONFIG_RMI4_F11_PEN) &&
> > -			sensor->sens_query.query9.has_pen)
> > +	if (IS_ENABLED(CONFIG_RMI4_F11_PEN) && sensor->sens_query.has_pen)
> >  		input_set_abs_params(input, ABS_MT_TOOL_TYPE,
> >  				     0, MT_TOOL_MAX, 0, 0);
> >  	else
> > @@ -1588,6 +1178,7 @@ static int rmi_f11_initialize(struct rmi_function *fn)
> >  	int rc;
> >  	struct rmi_device_platform_data *pdata = to_rmi_platform_data(rmi_dev);
> >  	struct f11_2d_sensor *sensor;
> > +	u8 buf;
> >  
> >  	dev_dbg(&fn->dev, "Initializing F11 values for %s.\n",
> >  		 pdata->sensor_name);
> > @@ -1605,29 +1196,26 @@ static int rmi_f11_initialize(struct rmi_function *fn)
> >  	query_base_addr = fn->fd.query_base_addr;
> >  	control_base_addr = fn->fd.control_base_addr;
> >  
> > -	rc = rmi_read(rmi_dev, query_base_addr, &f11->dev_query);
> > +	rc = rmi_read(rmi_dev, query_base_addr, &buf);
> >  	if (rc < 0)
> >  		return rc;
> >  
> > +	f11->has_query9 = !!(buf & RMI_F11_HAS_QUERY9);
> > +	f11->has_query11 = !!(buf & RMI_F11_HAS_QUERY11);
> > +	f11->has_query12 = !!(buf & RMI_F11_HAS_QUERY12);
> > +	f11->has_query27 = !!(buf & RMI_F11_HAS_QUERY27);
> > +	f11->has_query28 = !!(buf & RMI_F11_HAS_QUERY28);
> > +
> >  	query_offset = (query_base_addr + 1);
> >  	sensor = &f11->sensor;
> >  	sensor->fn = fn;
> >  
> > -	rc = rmi_f11_get_query_parameters(rmi_dev, &f11->dev_query,
> > +	rc = rmi_f11_get_query_parameters(rmi_dev, f11,
> >  			&sensor->sens_query, query_offset);
> >  	if (rc < 0)
> >  		return rc;
> >  	query_offset += rc;
> >  
> > -	rc = f11_allocate_control_regs(fn,
> > -			&f11->dev_query, &sensor->sens_query,
> > -			&f11->dev_controls, control_base_addr);
> > -	if (rc < 0) {
> > -		dev_err(&fn->dev,
> > -			"Failed to allocate F11 control params.\n");
> > -		return rc;
> > -	}
> > -
> >  	rc = f11_read_control_regs(fn, &f11->dev_controls,
> >  			control_base_addr);
> >  	if (rc < 0) {
> > @@ -1670,12 +1258,11 @@ static int rmi_f11_initialize(struct rmi_function *fn)
> >  
> >  	ctrl = &f11->dev_controls;
> >  	if (sensor->axis_align.delta_x_threshold) {
> > -		ctrl->ctrl0_9->delta_x_threshold =
> > +		ctrl->ctrl0_9[RMI_F11_DELTA_X_THRESHOLD] =
> >  			sensor->axis_align.delta_x_threshold;
> > -		rc = rmi_write_block(rmi_dev,
> > -				ctrl->ctrl0_9_address,
> > +		rc = rmi_write_block(rmi_dev, ctrl->ctrl0_9_address,
> >  				ctrl->ctrl0_9,
> > -				sizeof(*ctrl->ctrl0_9));
> > +				RMI_F11_CTRL_REG_COUNT);
> >  		if (rc < 0)
> >  			dev_warn(&fn->dev, "Failed to write to delta_x_threshold. Code: %d.\n",
> >  				rc);
> > @@ -1683,12 +1270,10 @@ static int rmi_f11_initialize(struct rmi_function *fn)
> >  	}
> >  
> >  	if (sensor->axis_align.delta_y_threshold) {
> > -		ctrl->ctrl0_9->delta_y_threshold =
> > +		ctrl->ctrl0_9[RMI_F11_DELTA_Y_THRESHOLD] =
> >  			sensor->axis_align.delta_y_threshold;
> > -		rc = rmi_write_block(rmi_dev,
> > -				ctrl->ctrl0_9_address,
> > -				ctrl->ctrl0_9,
> > -				sizeof(*ctrl->ctrl0_9));
> > +		rc = rmi_write_block(rmi_dev, ctrl->ctrl0_9_address,
> > +				ctrl->ctrl0_9, RMI_F11_CTRL_REG_COUNT);
> >  		if (rc < 0)
> >  			dev_warn(&fn->dev, "Failed to write to delta_y_threshold. Code: %d.\n",
> >  				rc);
> > @@ -1737,7 +1322,7 @@ static int rmi_f11_register_devices(struct rmi_function *fn)
> >  
> >  	f11_set_abs_params(fn);
> >  
> > -	if (sensor->sens_query.info.has_rel) {
> > +	if (sensor->sens_query.has_rel) {
> >  		set_bit(EV_REL, input_dev->evbit);
> >  		set_bit(REL_X, input_dev->relbit);
> >  		set_bit(REL_Y, input_dev->relbit);
> > @@ -1749,7 +1334,7 @@ static int rmi_f11_register_devices(struct rmi_function *fn)
> >  		goto error_unregister;
> >  	}
> >  
> > -	if (sensor->sens_query.info.has_rel) {
> > +	if (sensor->sens_query.has_rel) {
> >  		/*create input device for mouse events  */
> >  		input_dev_mouse = input_allocate_device();
> >  		if (!input_dev_mouse) {
> > @@ -1857,10 +1442,6 @@ static int rmi_f11_resume(struct device *dev)
> >  	struct rmi_function *fn = to_rmi_function(dev);
> >  	struct rmi_device *rmi_dev = fn->rmi_dev;
> >  	struct f11_data *data = fn->data;
> > -	/* Command register always reads as 0, so we can just use a local. */
> > -	struct f11_2d_commands commands = {
> > -		.rezero = true,
> > -	};
> >  	int error;
> >  
> >  	dev_dbg(&fn->dev, "Resuming...\n");
> > @@ -1869,8 +1450,7 @@ static int rmi_f11_resume(struct device *dev)
> >  
> >  	mdelay(data->rezero_wait_ms);
> >  
> > -	error = rmi_write_block(rmi_dev, fn->fd.command_base_addr,
> > -				&commands, sizeof(commands));
> > +	error = rmi_write(rmi_dev, fn->fd.command_base_addr, RMI_F11_REZERO);
> >  	if (error < 0) {
> >  		dev_err(&fn->dev,
> >  			"%s: failed to issue rezero command, error = %d.",
> 
> -- 
> Dmitry

^ permalink raw reply

* Re: [PATCH] input synaptics-rmi4: Eliminate packed structs in PDT handling
From: Dmitry Torokhov @ 2013-12-15 11:57 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Linux Input, Andrew Duggan, Vincent Huang, Vivian Ly,
	Daniel Rosenberg, Jean Delvare, Joerie de Gram, Linus Walleij,
	Benjamin Tissoires
In-Reply-To: <1386731442-31146-1-git-send-email-cheiny@synaptics.com>

On Tue, Dec 10, 2013 at 07:10:42PM -0800, Christopher Heiny wrote:
> This converts the PDT handling routines from using bitfields in packed structs,
> converting to bitmasks and shifts to parse out bitfields, nibbles, and so on.
> 
> Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> Cc: Jean Delvare <khali@linux-fr.org>
> Cc: Linus Walleij <linus.walleij@stericsson.com>
> Cc: Joerie de Gram <j.de.gram@gmail.com>
> Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> 

Applied, thank you.

> ---
> 
> This patch implements changes to the synaptics-rmi4 branch of
> Dmitry's input tree.  The base for the patch is commit
> f154022b208a59b048f52b7b5d58a5ec1949b96e.
> 
>  drivers/input/rmi4/rmi_driver.c | 45 +++++++++++++++++++++++++++-------------
>  drivers/input/rmi4/rmi_driver.h | 46 +++++++++++++++--------------------------
>  2 files changed, 48 insertions(+), 43 deletions(-)
> 
> diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
> index dffbfa0..a30c7d3 100644
> --- a/drivers/input/rmi4/rmi_driver.c
> +++ b/drivers/input/rmi4/rmi_driver.c
> @@ -483,6 +483,31 @@ int rmi_driver_irq_get_mask(struct rmi_device *rmi_dev,
>  		return -ENOMEM;
>  }
>  
> +int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry,
> +			u16 pdt_address)
> +{
> +	u8 buf[RMI_PDT_ENTRY_SIZE];
> +	int error;
> +
> +	error = rmi_read_block(rmi_dev, pdt_address, buf, RMI_PDT_ENTRY_SIZE);
> +	if (error < 0) {
> +		dev_err(&rmi_dev->dev, "Read PDT entry at %#06x failed, code: %d.\n",
> +				pdt_address, error);
> +		return error;
> +	}
> +
> +	entry->query_base_addr = buf[0];
> +	entry->command_base_addr = buf[1];
> +	entry->control_base_addr = buf[2];
> +	entry->data_base_addr = buf[3];
> +	entry->interrupt_source_count = buf[4] & RMI_PDT_INT_SOURCE_COUNT_MASK;
> +	entry->function_version = (buf[4] & RMI_PDT_FUNCTION_VERSION_MASK) >> 5;
> +	entry->function_number = buf[5];
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(rmi_read_pdt_entry);
> +
>  static void rmi_driver_copy_pdt_to_fd(struct pdt_entry *pdt,
>  				      struct rmi_function_descriptor *fd,
>  				      u16 page_start)
> @@ -572,14 +597,10 @@ static int reset_and_reflash(struct rmi_device *rmi_dev)
>  		u16 pdt_end = page_start + PDT_END_SCAN_LOCATION;
>  
>  		done = true;
> -		for (i = pdt_start; i >= pdt_end; i -= sizeof(pdt_entry)) {
> -			retval = rmi_read_block(rmi_dev, i, &pdt_entry,
> -					       sizeof(pdt_entry));
> -			if (retval != sizeof(pdt_entry)) {
> -				dev_err(dev, "Read PDT entry at %#06x failed, code = %d.\n",
> -						i, retval);
> +		for (i = pdt_start; i >= pdt_end; i -= RMI_PDT_ENTRY_SIZE) {
> +			retval = rmi_read_pdt_entry(rmi_dev, &pdt_entry, i);
> +			if (retval < 0)
>  				return retval;
> -			}
>  
>  			if (RMI4_END_OF_PDT(pdt_entry.function_number))
>  				break;
> @@ -634,14 +655,10 @@ static int rmi_scan_pdt(struct rmi_device *rmi_dev)
>  		u16 pdt_end = page_start + PDT_END_SCAN_LOCATION;
>  
>  		done = true;
> -		for (i = pdt_start; i >= pdt_end; i -= sizeof(pdt_entry)) {
> -			retval = rmi_read_block(rmi_dev, i, &pdt_entry,
> -					       sizeof(pdt_entry));
> -			if (retval != sizeof(pdt_entry)) {
> -				dev_err(dev, "Read of PDT entry at %#06x failed.\n",
> -					i);
> +		for (i = pdt_start; i >= pdt_end; i -= RMI_PDT_ENTRY_SIZE) {
> +			retval = rmi_read_pdt_entry(rmi_dev, &pdt_entry, i);
> +			if (retval < 0)
>  				goto error_exit;
> -			}
>  
>  			if (RMI4_END_OF_PDT(pdt_entry.function_number))
>  				break;
> diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
> index f8d87e9..5e3c4d4 100644
> --- a/drivers/input/rmi4/rmi_driver.h
> +++ b/drivers/input/rmi4/rmi_driver.h
> @@ -29,11 +29,7 @@
>  #define PDT_PROPERTIES_LOCATION 0x00EF
>  #define BSR_LOCATION 0x00FE
>  
> -struct pdt_properties {
> -	u8 reserved_1:6;
> -	u8 has_bsr:1;
> -	u8 reserved_2:1;
> -} __attribute__((__packed__));
> +#define RMI_PDT_PROPS_HAS_BSR 0x02
>  
>  struct rmi_driver_data {
>  	struct list_head function_list;
> @@ -61,7 +57,7 @@ struct rmi_driver_data {
>  	ktime_t poll_interval;
>  
>  	struct mutex pdt_mutex;
> -	struct pdt_properties pdt_props;
> +	u8 pdt_props;
>  	u8 bsr;
>  
>  	bool enabled;
> @@ -90,34 +86,26 @@ struct rmi_driver_data {
>  	void *data;
>  };
>  
> +#define RMI_PDT_ENTRY_SIZE 6
> +#define RMI_PDT_FUNCTION_VERSION_MASK   0x60
> +#define RMI_PDT_INT_SOURCE_COUNT_MASK   0x07
> +
>  #define PDT_START_SCAN_LOCATION 0x00e9
>  #define PDT_END_SCAN_LOCATION	0x0005
>  #define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff)
>  
>  struct pdt_entry {
> -	u8 query_base_addr:8;
> -	u8 command_base_addr:8;
> -	u8 control_base_addr:8;
> -	u8 data_base_addr:8;
> -	u8 interrupt_source_count:3;
> -	u8 bits3and4:2;
> -	u8 function_version:2;
> -	u8 bit7:1;
> -	u8 function_number:8;
> -} __attribute__((__packed__));
> -
> -static inline void copy_pdt_entry_to_fd(struct pdt_entry *pdt,
> -				 struct rmi_function_descriptor *fd,
> -				 u16 page_start)
> -{
> -	fd->query_base_addr = pdt->query_base_addr + page_start;
> -	fd->command_base_addr = pdt->command_base_addr + page_start;
> -	fd->control_base_addr = pdt->control_base_addr + page_start;
> -	fd->data_base_addr = pdt->data_base_addr + page_start;
> -	fd->function_number = pdt->function_number;
> -	fd->interrupt_source_count = pdt->interrupt_source_count;
> -	fd->function_version = pdt->function_version;
> -}
> +	u8 query_base_addr;
> +	u8 command_base_addr;
> +	u8 control_base_addr;
> +	u8 data_base_addr;
> +	u8 interrupt_source_count;
> +	u8 function_version;
> +	u8 function_number;
> +};
> +
> +int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry,
> +			u16 pdt_address);
>  
>  bool rmi_is_physical_driver(struct device_driver *);
>  int rmi_register_physical_driver(void);

-- 
Dmitry

^ permalink raw reply

* [PATCH] Input: pmic8xxx-pwrkey - switch to using managed resources
From: Dmitry Torokhov @ 2013-12-15 12:07 UTC (permalink / raw)
  To: linux-input; +Cc: Stephen Boyd, Lars-Peter Clausen, Sachin Kamat, linux-kernel

This simplifies error handling and device removal paths.

Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
 drivers/input/misc/pmic8xxx-pwrkey.c | 74 ++++++++++++------------------------
 1 file changed, 25 insertions(+), 49 deletions(-)

diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c
index ef93840..aaf3325 100644
--- a/drivers/input/misc/pmic8xxx-pwrkey.c
+++ b/drivers/input/misc/pmic8xxx-pwrkey.c
@@ -32,7 +32,6 @@
  * @key_press_irq: key press irq number
  */
 struct pmic8xxx_pwrkey {
-	struct input_dev *pwr;
 	int key_press_irq;
 };
 
@@ -110,22 +109,22 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	pwrkey = kzalloc(sizeof(*pwrkey), GFP_KERNEL);
+	pwrkey = devm_kzalloc(&pdev->dev, sizeof(*pwrkey), GFP_KERNEL);
 	if (!pwrkey)
 		return -ENOMEM;
 
-	pwr = input_allocate_device();
+	pwrkey->key_press_irq = key_press_irq;
+
+	pwr = devm_input_allocate_device(&pdev->dev);
 	if (!pwr) {
 		dev_dbg(&pdev->dev, "Can't allocate power button\n");
-		err = -ENOMEM;
-		goto free_pwrkey;
+		return -ENOMEM;
 	}
 
 	input_set_capability(pwr, EV_KEY, KEY_POWER);
 
 	pwr->name = "pmic8xxx_pwrkey";
 	pwr->phys = "pmic8xxx_pwrkey/input0";
-	pwr->dev.parent = &pdev->dev;
 
 	delay = (pdata->kpd_trigger_delay_us << 10) / USEC_PER_SEC;
 	delay = 1 + ilog2(delay);
@@ -133,7 +132,7 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
 	err = regmap_read(regmap, PON_CNTL_1, &pon_cntl);
 	if (err < 0) {
 		dev_err(&pdev->dev, "failed reading PON_CNTL_1 err=%d\n", err);
-		goto free_input_dev;
+		return err;
 	}
 
 	pon_cntl &= ~PON_CNTL_TRIG_DELAY_MASK;
@@ -146,66 +145,43 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
 	err = regmap_write(regmap, PON_CNTL_1, pon_cntl);
 	if (err < 0) {
 		dev_err(&pdev->dev, "failed writing PON_CNTL_1 err=%d\n", err);
-		goto free_input_dev;
+		return err;
 	}
 
-	err = input_register_device(pwr);
+	err = devm_request_irq(&pdev->dev, key_press_irq, pwrkey_press_irq,
+			       IRQF_TRIGGER_RISING,
+			       "pmic8xxx_pwrkey_press", pwr);
 	if (err) {
-		dev_dbg(&pdev->dev, "Can't register power key: %d\n", err);
-		goto free_input_dev;
+		dev_err(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n",
+			key_press_irq, err);
+		return err;
 	}
 
-	pwrkey->key_press_irq = key_press_irq;
-	pwrkey->pwr = pwr;
-
-	platform_set_drvdata(pdev, pwrkey);
-
-	err = request_irq(key_press_irq, pwrkey_press_irq,
-		IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_press", pwr);
-	if (err < 0) {
-		dev_dbg(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n",
-				 key_press_irq, err);
-		goto unreg_input_dev;
+	err = devm_request_irq(&pdev->dev, key_release_irq, pwrkey_release_irq,
+			       IRQF_TRIGGER_RISING,
+			       "pmic8xxx_pwrkey_release", pwr);
+	if (err) {
+		dev_err(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n",
+			key_release_irq, err);
+		return err;
 	}
 
-	err = request_irq(key_release_irq, pwrkey_release_irq,
-		 IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_release", pwr);
-	if (err < 0) {
-		dev_dbg(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n",
-				 key_release_irq, err);
-
-		goto free_press_irq;
+	err = input_register_device(pwr);
+	if (err) {
+		dev_err(&pdev->dev, "Can't register power key: %d\n", err);
+		return err;
 	}
 
+	platform_set_drvdata(pdev, pwrkey);
 	device_init_wakeup(&pdev->dev, pdata->wakeup);
 
 	return 0;
-
-free_press_irq:
-	free_irq(key_press_irq, pwrkey);
-unreg_input_dev:
-	input_unregister_device(pwr);
-	pwr = NULL;
-free_input_dev:
-	input_free_device(pwr);
-free_pwrkey:
-	kfree(pwrkey);
-	return err;
 }
 
 static int pmic8xxx_pwrkey_remove(struct platform_device *pdev)
 {
-	struct pmic8xxx_pwrkey *pwrkey = platform_get_drvdata(pdev);
-	int key_release_irq = platform_get_irq(pdev, 0);
-	int key_press_irq = platform_get_irq(pdev, 1);
-
 	device_init_wakeup(&pdev->dev, 0);
 
-	free_irq(key_press_irq, pwrkey->pwr);
-	free_irq(key_release_irq, pwrkey->pwr);
-	input_unregister_device(pwrkey->pwr);
-	kfree(pwrkey);
-
 	return 0;
 }
 
-- 
1.8.3.1


-- 
Dmitry

^ permalink raw reply related

* Re: [RFC] Add ff-memless-next driver
From: Michal Malý @ 2013-12-15 13:07 UTC (permalink / raw)
  To: Joe Perches
  Cc: dmitry.torokhov, linux-kernel, linux-input, elias.vds,
	anssi.hannula
In-Reply-To: <1387073042.2276.21.camel@joe-AO722>

On Saturday 14 of December 2013 18:04:02 Joe Perches wrote:
> On Sun, 2013-12-15 at 01:19 +0100, Michal Malý wrote:
> > diff --git a/drivers/input/ff-memless-next.c
> > b/drivers/input/ff-memless-next.c
> []
> 
> > +static inline s32 mlnx_clamp_level(const s32 level)
> > +{
> > +	return (level > 0x7fff) ? 0x7fff : ((level < -0x7fff) ? -0x7fff :
> > level);
> 
> 	clamp(level, -0x7fff, 0x7fff);
> []
> 
> > +static inline int mlnx_is_conditional(const struct ff_effect *effect)
> > +{
> > +	return (effect->type == FF_DAMPER) || (effect->type == FF_FRICTION) ||
> > (effect->type == FF_INERTIA) || (effect->type == FF_SPRING); +}
> 
> Maybe try to run your patch through scripts/checkpatch.pl
> 
> bool?
> 80 char line max? (true for the file, not just here)
> switch?
> 	switch (effect->type) {
> 	case FF_DAMPER:
> 	case FF_FRICTION:
> 	case FF_INERTIA:
> 	case FF_SPRING:
> 		return true;
> 	}
> 	return false;

Okay, thanks for the input. I found a problem regarding effect updating in the code anyway. I'll submit a corrected version of the patch once I'm sure it's fixed.

Michal

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox