* [git pull] Input updates for 3.13-rc3
From: Dmitry Torokhov @ 2013-12-12 16:37 UTC (permalink / raw)
To: Linus Torvalds; +Cc: linux-kernel, linux-input
[-- Attachment #1: Type: text/plain, Size: 710 bytes --]
Hi Linus,
Please pull from:
git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git for-linus
or
master.kernel.org:/pub/scm/linux/kernel/git/dtor/input.git for-linus
to receive more updates for the input subsystem. You will get a fix for
recent sysfs breakage in serio subsystem plus a fixup to adxl34x driver.
Changelog:
---------
Dmitry Torokhov (1):
Input: serio - fix sysfs layout
Michael Hennerich (1):
Input: adxl34x - Fix bug in definition of ADXL346_2D_ORIENT
Diffstat:
--------
drivers/input/misc/adxl34x.c | 2 +-
drivers/input/serio/serio.c | 24 +++++++++++++++++-------
2 files changed, 18 insertions(+), 8 deletions(-)
--
Dmitry
[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* 3.12.3: Oops while hitting keyboard
From: Ortwin Glück @ 2013-12-12 19:10 UTC (permalink / raw)
To: linux-input, linux-kernel
Hi,
My little son triggered the following Oops by hitting the Bluetooth keyboard
like a little monkey:
Linux gandalf 3.12.3 #2 SMP PREEMPT Wed Dec 4 23:04:27 CET 2013 x86_64 Intel(R)
Core(TM) i3 CPU 540 @ 3.07GHz GenuineIntel GNU/Linux
Dec 12 18:04:11 gandalf kernel: input: gandalf kbd as
/devices/pci0000:00/0000:00:1a.7/usb1/1-1/1-1.
1/1-1.1.1/1-1.1.1:1.0/bluetooth/hci0/hci0:12/input45
Dec 12 18:04:11 gandalf kernel: apple 0005:05AC:023A.0025: input,hidraw2:
BLUETOOTH HID v0.50 Keyboa
rd [gandalf kbd] on 7c:6d:62:9f:db:2d
Dec 12 18:04:28 gandalf kernel: BUG: unable to handle kernel paging request at
ffffffff82450968
Dec 12 18:04:28 gandalf kernel: IP: [<ffffffff813450c7>] kbd_event+0x1d7/0x740
Dec 12 18:04:28 gandalf kernel: PGD 1c0c067 PUD 1c0d063 PMD 0
Dec 12 18:04:28 gandalf kernel: PGD 1c0c067 PUD 1c0d063 PMD 0
Dec 12 18:04:28 gandalf kernel: Oops: 0000 [#1] PREEMPT SMP
Dec 12 18:04:28 gandalf kernel: Modules linked in: fbcon radeon cfbfillrect
cfbimgblt bitblit cfbcop
yarea softcursor font i2c_algo_bit drm_kms_helper ttm drm fb fbdev
Dec 12 18:04:28 gandalf kernel: CPU: 2 PID: 9153 Comm: khidpd_05ac023a Not
tainted 3.12.3 #2
Dec 12 18:04:28 gandalf kernel: Hardware name: Apple Inc. iMac11,2/Mac-F2238AC8,
BIOS IM112.88Z.0057.B00.1005031455 05/03/10
Dec 12 18:04:28 gandalf kernel: task: ffff8800327e8000 ti: ffff880030858000
task.ti: ffff880030858000
Dec 12 18:04:28 gandalf kernel: RIP: 0010:[<ffffffff813450c7>]
[<ffffffff813450c7>] kbd_event+0x1d7/0x740
Dec 12 18:04:28 gandalf kernel: RSP: 0018:ffff880030859ba8 EFLAGS: 00010046
Dec 12 18:04:28 gandalf kernel: RAX: 000000000010000d RBX: 0000000000000038 RCX:
ffffffff81e6cbde
Dec 12 18:04:28 gandalf kernel: RDX: ffff880030859bc0 RSI: 0000000000000001 RDI:
ffffffff81e6cba0
Dec 12 18:04:28 gandalf kernel: RBP: 0000000000000000 R08: 0000000000000001 R09:
0000000000000000
Dec 12 18:04:28 gandalf kernel: R10: 0000000000000000 R11: 0000000000000001 R12:
ffff8800ba829800
Dec 12 18:04:28 gandalf kernel: R13: 000000000010000d R14: ffff880030b10500 R15:
0000000000000038
Dec 12 18:04:28 gandalf kernel: FS: 0000000000000000(0000)
GS:ffff88013bc80000(0000) knlGS:0000000000000000
Dec 12 18:04:28 gandalf kernel: CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b
Dec 12 18:04:28 gandalf kernel: CR2: ffffffff82450968 CR3: 0000000001c0b000 CR4:
00000000000007e0
Dec 12 18:04:28 gandalf kernel: CR2: ffffffff82450968 CR3: 0000000001c0b000 CR4:
00000000000007e0
Dec 12 18:04:28 gandalf kernel: Stack:
Dec 12 18:04:28 gandalf kernel: ffffffff819db59b ffffffff8106e1b3
0000000000000081 ffff8800ba829800
Dec 12 18:04:28 gandalf kernel: 0010000d00000000 0000003800000000
0000000000000096 0000000000000003
Dec 12 18:04:28 gandalf kernel: ffff880030b103d8 ffffffff81c4e5e0
ffff880030b103d8 ffff880030b105a0
Dec 12 18:04:28 gandalf kernel: Call Trace:
Dec 12 18:04:28 gandalf kernel: [<ffffffff8106e1b3>] ? __wake_up+0x43/0x70
Dec 12 18:04:28 gandalf kernel: [<ffffffff8145f3e3>] ? input_to_handler+0xd3/0xf0
Dec 12 18:04:28 gandalf kernel: [<ffffffff81461e72>] ?
input_pass_values.part.8+0x162/0x170
Dec 12 18:04:28 gandalf kernel: [<ffffffff814625ad>] ?
input_handle_event+0x12d/0x550
Dec 12 18:04:28 gandalf kernel: [<ffffffff81462afe>] ? input_event+0x5e/0xa0
Dec 12 18:04:28 gandalf kernel: [<ffffffff814e4d07>] ?
hidinput_report_event+0x37/0x50
Dec 12 18:04:28 gandalf kernel: [<ffffffff814e3275>] ?
hid_report_raw_event+0x285/0x460
Dec 12 18:04:28 gandalf kernel: [<ffffffff814e356d>] ? hid_input_report+0x11d/0x1b0
Dec 12 18:04:28 gandalf kernel: [<ffffffff816d589b>] ?
hidp_session_thread+0x5eb/0x640
Dec 12 18:04:28 gandalf kernel: [<ffffffff810732d0>] ? try_to_wake_up+0x2a0/0x2a0
Dec 12 18:04:28 gandalf kernel: [<ffffffff810732d0>] ? try_to_wake_up+0x2a0/0x2a0
Dec 12 18:04:28 gandalf kernel: [<ffffffff816d52b0>] ?
hidp_input_report.isra.9+0x2e0/0x2e0
Dec 12 18:04:28 gandalf kernel: [<ffffffff810653b3>] ? kthread+0xb3/0xc0
Dec 12 18:04:28 gandalf kernel: [<ffffffff81070000>] ? get_group+0x20/0x80
Dec 12 18:04:28 gandalf kernel: [<ffffffff81065300>] ?
kthread_freezable_should_stop+0x60/0x60
Dec 12 18:04:28 gandalf kernel: [<ffffffff81065300>] ?
kthread_freezable_should_stop+0x60/0x60
Dec 12 18:04:28 gandalf kernel: [<ffffffff81065300>] ?
kthread_freezable_should_stop+0x60/0x60
Dec 12 18:04:28 gandalf kernel: [<ffffffff81773cfc>] ? ret_from_fork+0x7c/0xb0
Dec 12 18:04:28 gandalf kernel: [<ffffffff81773cfc>] ? ret_from_fork+0x7c/0xb0
Dec 12 18:04:28 gandalf kernel: [<ffffffff81065300>] ?
kthread_freezable_should_stop+0x60/0x60
Dec 12 18:04:28 gandalf kernel: Code: e6 81 44 0f b6 68 01 44 0b 2d b6 7c b2 00
41 31 d5 44 89 6c 24 24 48 8d 54 24 18 0f b6 40 02 d0 e8 83 e0 0f 89 44 24 28 49
63 c5 <48> 8b 0c c5 00 09 c5 81 48 89 4c 24 08 e8 c7 59 d2 ff 48 8b 4c
Dec 12 18:04:28 gandalf kernel: RIP [<ffffffff813450c7>] kbd_event+0x1d7/0x740
Dec 12 18:04:28 gandalf kernel: RSP <ffff880030859ba8>
Dec 12 18:04:28 gandalf kernel: CR2: ffffffff82450968
Dec 12 18:04:28 gandalf kernel: ---[ end trace edcf19cf8c005260 ]---
Thanks,
Ortwin
^ permalink raw reply
* [PATCH] HID: remove SIS entries from hid_have_special_driver[]
From: Jiri Kosina @ 2013-12-13 14:45 UTC (permalink / raw)
To: Emanuel Krenz, Wanlong Gao, AceLan Kao, Forest Bond; +Cc: linux-input
The entries are not needed, as hid-multitouch gets bound correctly
automatically by contact ID.
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
---
I will be queuing for 3.14 if noone objects.
drivers/hid/hid-core.c | 3 ---
1 files changed, 0 insertions(+), 3 deletions(-)
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 48296c0..b0de258 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1822,9 +1822,6 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
- { HID_USB_DEVICE(USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS9200_TOUCH) },
- { HID_USB_DEVICE(USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS817_TOUCH) },
- { HID_USB_DEVICE(USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS1030_TOUCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) },
--
Jiri Kosina
SUSE Labs
^ permalink raw reply related
* Re: [PATCHv4 0/2] twl4030-keypad DT binding
From: Sebastian Reichel @ 2013-12-13 18:47 UTC (permalink / raw)
To: Dmitry Torokhov, Dmitry Torokhov, linux-input
In-Reply-To: <1385830416-12900-1-git-send-email-sre@debian.org>
[-- Attachment #1: Type: text/plain, Size: 170 bytes --]
Hi Dmitry,
> Add device tree support for the twl4030 keypad, which is
> for example used in the Nokia N900.
Can you add this to the input queue for 3.14?
-- Sebastian
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: [PATCH 1/2] joydev: Map ABS_{THROTTLE,GAS,BREAK} to positive values.
From: Ken Phillis Jr @ 2013-12-14 5:13 UTC (permalink / raw)
To: Benjamin Franzke; +Cc: linux-input
In-Reply-To: <1386749731-6500-1-git-send-email-benjaminfranzke@googlemail.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: 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 ∅
+ }
+}
+
+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 ∅
+ }
+}
+
+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
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox