* Re: [RFC v3] Add ff-memless-next
From: Michal Malý @ 2013-12-24 2:11 UTC (permalink / raw)
To: linux-input; +Cc: linux-kernel, elias.vds
In-Reply-To: <1387844508.22671.73.camel@joe-AO722>
On Monday 23 of December 2013 16:21:48 you wrote:
> On Mon, 2013-12-23 at 23:46 +0100, Michal Malý wrote:
> > One case where uncombinable effects were mishandled was corrected. Rest of
> > the changes are just coding style fixes.
>
> trivia:
> > diff --git a/drivers/input/ff-memless-next.c
> > b/drivers/input/ff-memless-next.c
> []
>
> > +static __always_inline s32 mlnx_calculate_x_force(const s32 level,
> > + const u16 direction)
>
> __always_inline is almost never warranted.
> gcc generally does the right thing.
I did some reading about __always_inline vs. plain inline and I ran into
some contradictory information about what is considered broken. Thanks
for letting me know...
Is there anything else that needs some reviewing?
--
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: [Dell Inspiron 17R SE 7720] Synaptics touchpad recognized as PS/2 Generic Mouse
From: Tommy Will @ 2013-12-24 1:04 UTC (permalink / raw)
To: Robert van Geerenstein; +Cc: Kevin Cernekee, linux-input, Justin Clift
In-Reply-To: <52B8875F.9060705@r15n.nl>
Hi Kevin,
Thanks for you following this issue.
About the trackstick issue, although I still have not got the
feedback, I'd start preparing the new patch since it
does not have problem as far as my self testing.
Hi Robert,
Please wait and try my new patch later. Hopes it could fix your problem.
--
Best Regards,
Tommy
2013/12/24 Robert van Geerenstein <info@r15n.nl>:
> On 23-12-2013 19:47, Kevin Cernekee wrote:
>>
>> On Mon, Dec 23, 2013 at 10:20 AM, Robert van Geerenstein <info@r15n.nl>
>> wrote:
>>>
>>> My touchpad is detected as a PS/2 Generic Mouse on my Dell Inspiron 7720
>>> (17R SE).
>>> It only has the functionality of a generic mouse (movement, left clik,
>>> right
>>> click) and no scrolling or multitouch.
>>
>> Hmm, doesn't this laptop have an ALPS Dolphin V2 touchpad (not Synaptics)?
>>
>> Tommy and Justin were working on this a while back. There are a few
>> patches in the list archives that you could try out. I believe
>> they're mostly ready for merging except for one remaining issue with
>> the trackstick.
>> --
>> 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
>
>
> Hmm, looks like you're right. :/
> Didn't even notice after all this time, guess back when I made the bugreport
> on launchpad I was all over the place trying to get it to work.
>
> I'll go ahead and see if I can find anything in the archives that might get
> it to work.
>
> Good to hear they're mostly ready for merging though.
^ permalink raw reply
* Re: [PATCH] Input: gpio_keys: support wakeup system from freeze mode
From: Dmitry Torokhov @ 2013-12-24 0:30 UTC (permalink / raw)
To: Anson Huang; +Cc: sachin.kamat, linux-input
In-Reply-To: <1387822880-30382-1-git-send-email-b20788@freescale.com>
Hi Anson,
On Mon, Dec 23, 2013 at 01:21:20PM -0500, Anson Huang wrote:
> For "freeze" mode of suspend, cpu will go into idle and
> those wakeup sources' irq should NOT be disabled during
> devices suspend, so we need to add IRQF_NO_SUSPEND flag
> for those wakeup sources.
>
> Steps to test this patch:
>
> 1. echo freeze > /sys/power/state;
> 2. press gpio key which has wakeup function, then system
> will resume.
I do no think this is correct approach, otheriwse every driver that can
be a wakeup source would have to use IRQF_NO_SUSPEND flag. The driver
does use enable_irq_wake() in its suspend path, and platform code should
perform all necessary work for this IRQ to be usable as a wakeup source.
Thanks.
>
> Signed-off-by: Anson Huang <b20788@freescale.com>
> ---
> drivers/input/keyboard/gpio_keys.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
> index 2db1324..aadb1db 100644
> --- a/drivers/input/keyboard/gpio_keys.c
> +++ b/drivers/input/keyboard/gpio_keys.c
> @@ -473,6 +473,8 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
>
> isr = gpio_keys_gpio_isr;
> irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
> + if (bdata->button->wakeup)
> + irqflags |= IRQF_NO_SUSPEND;
>
> } else {
> if (!button->irq) {
> --
> 1.7.9.5
>
>
--
Dmitry
^ permalink raw reply
* Re: [RFC v3] Add ff-memless-next
From: Joe Perches @ 2013-12-24 0:21 UTC (permalink / raw)
To: Michal Malý; +Cc: linux-input, linux-kernel, elias.vds
In-Reply-To: <1494187.y8mYFDm1lA@geidi-prime>
On Mon, 2013-12-23 at 23:46 +0100, Michal Malý wrote:
> One case where uncombinable effects were mishandled was corrected. Rest of
> the changes are just coding style fixes.
trivia:
> diff --git a/drivers/input/ff-memless-next.c b/drivers/input/ff-memless-next.c
[]
> +static __always_inline s32 mlnx_calculate_x_force(const s32 level,
> + const u16 direction)
__always_inline is almost never warranted.
gcc generally does the right thing.
--
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
* [RFC v3] Add ff-memless-next
From: Michal Malý @ 2013-12-23 22:46 UTC (permalink / raw)
To: linux-input; +Cc: linux-kernel, elias.vds, joe
One case where uncombinable effects were mishandled was corrected. Rest of
the changes are just coding style fixes.
Tested-by: Elias Vanderstuyft <elias.vds@gmail.com>
Signed-off-by: Michal Malý <madcatxster@prifuk.cz>
---
From 2de27604adeefa73e30ee049fae4c38265beb85d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michal=20Mal=C3=BD?= <madcatxster@prifuk.cz>
Date: Mon, 23 Dec 2013 23:42:11 +0100
Subject: [PATCH] Add ff-memless-next
---
Documentation/input/ff-memless-next.txt | 145 ++++++
drivers/input/Kconfig | 12 +
drivers/input/Makefile | 2 +
drivers/input/ff-memless-next.c | 786 ++++++++++++++++++++++++++++++++
include/linux/input/ff-memless-next.h | 31 ++
5 files changed, 976 insertions(+)
create mode 100644 Documentation/input/ff-memless-next.txt
create mode 100644 drivers/input/ff-memless-next.c
create mode 100644 include/linux/input/ff-memless-next.h
diff --git a/Documentation/input/ff-memless-next.txt b/Documentation/input/ff-memless-next.txt
new file mode 100644
index 0000000..00f893d
--- /dev/null
+++ b/Documentation/input/ff-memless-next.txt
@@ -0,0 +1,145 @@
+"ff-memless-next" force feedback module for memoryless devices.
+By Michal Malý <madcatxster@prifuk.cz> on 2013/12/21
+----------------------------------------------------------------------------
+
+1. Introduction
+~~~~~~~~~~~~~~~
+This document describes basic working principles of the "ff-memless-next"
+and its purpose. This document also contains summary of "ff-memless-next" API
+and brief instructions on how to use it to write a hardware-specific backend
+with "ff-memless-next". Some specifics of ff-memless-next that might be of
+interest for userspace developers are also discussed.
+
+2. Basic principles of ff-memless-next
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+A lot of commonly used force feedback devices do not have a memory of their
+own. This means that it is not possible to upload a force feedback effect
+to such a device and let the device's CPU handle the playback. Instead,
+the device relies solely on its driver to tell it what force to generate.
+"ff-memless-next" was written to serve in this capacity. Its purpose is to
+calculate the overall force the device should apply and pass the result to
+a hardware-specific backend which then submits the appropriate command to
+the device.
+
+"ff-memless-next" differentiates between two types of force feedback effects,
+"combinable" and "uncombinable".
+"Combinable" effects are effects that generate a force of a given
+magnitude and direction. The magnitude can change in time according to the
+envelope of the effect and its waveform; the latter applies only to periodic
+effects. Force generated by "combinable" effect does not depend on the position
+of the device. Forces generated by each "combinable" effect that is active
+are periodically recalculated as needed and superposed into one overall force.
+"Combinable" effects are FF_CONSTANT, FF_PERIODIC and FF_RAMP.
+
+"Uncombinable" effects generate a force that depends on the position of
+the device. "ff-memless-next" assumes that the device takes care of processing
+such an effect. However, a device might have a limit on how many "uncombinable"
+effects can be active at once and this limit might be lower than the maximum
+effect count set in "ff-memless-next". "ff-memless-next" allows a
+hardware-specific driver to check whether the device is able to play
+an "uncombinable" effect. Error is reported back to userspace if the device
+cannot play the effect. The hardware-specific driver may choose not to
+perform this check in which case the userspace will have no way of knowing
+whether an "uncombinable" is really being played or not.
+
+3. API provided by "ff-memless-next"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"ff-memless-next" provides an API for developers of hardware-specific
+drivers. In order to use the API, the hardware-specific driver should
+include <linux/input/ff-memless-next.h>
+Functions and structs defined by this API are:
+
+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)
+- Any hardware-specific driver that wants to use "ff-memless-next" must call
+this function. The function takes care of creating and registering a force
+feedback device within the kernel. It also initializes resources needed by
+"ff-memless-next" to handle a new device. No other initialization steps are necessary.
+ Parameters:
+ * dev - pointer to valid struct input_dev
+ * data - pointer to custom data the hardware-specific backend
+ might need to pass to the control_effect() callback function
+ (discussed later). * data will be freed automatically by
+ "ff-memless-next" upon device's destruction.
+ * control_effect - A callback function. "ff-memless-next" will call
+ this function when it is done processing effects.
+ Implementation of control_effect() in the
+ hardware-specific driver should create an appropriate
+ command and submit it to the device.
+ This function is called with dev->event_lock
+ spinlock held.
+ update_rate - Rate in milliseconds at which envelopes and periodic
+ effects are recalculated. Minimum value is limited by the
+ kernel timer resolution and changes with value of
+ CONFIG_HZ.
+
+struct mlnx_effect_command
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+- This struct is passed to the hardware-specific backend in
+the control_effect() function. See "ff-memless-next.h" for details.
+
+enum mlnx_commands
+^^^^^^^^^^^^^^^^^^
+- Types of commands generated by ff-memless-next
+ MLNX_START_COMBINED - Start or update "combinable" effect
+ MLNX_STOP_COMBINED - Stop "combinable" effect. In most cases this means
+ to set the force to zero.
+ MLNX_UPLOAD_UNCOMB - Check if the device can accept and play
+ "uncombinable" effect.
+ Hardware-specific driver should return 0
+ on success.
+ MLNX_START_UNCOMB - Start playback of "uncombinable" effect.
+ MLNX_STOP_UNCOMB - Stop playback of "uncombinable" effect.
+
+struct mlnx_simple_force
+^^^^^^^^^^^^^^^^^^^^^^^^
+ x - overall force along X axis
+ y - overall force along Y axis
+
+struct mlnx_uncomb_effect
+^^^^^^^^^^^^^^^^^^^^^^^^^
+- Information about "uncombinable" effect.
+ id - internal ID of the effect
+ * effect - pointer to the internal copy of the effect kept by
+ "ff-memless-next". This pointer will remain valid until
+ the effect is removed.
+
+Sample implementation of a dummy driver that uses "ff-memless-next" is
+available at "git://prifuk.cz/ff-dummy-device". Link to the source will
+be kept up to date.
+
+Direction of the effect's force translates to Cartesian coordinates system
+as follows:
+ Direction = 0: Down
+ Direction (0; 16384): 3rd quadrant
+ Direction = 16384: Left
+ Direction (16385; 32768): 2nd quadrant
+ Direction = 32768: Up
+ Direction (32769; 49152): 1st quadrant
+ Direction = 49152: Right
+ Direction (49153; 65535) :4th quadrant
+ Direction = 65565: Down
+
+4. Specifics of "ff-memless-next" for userspace developers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+None of the persons involved in development or testing of "ff-memless-next"
+is aware of any reference force feedback specifications. "ff-memless-next"
+tries to adhere to Microsoft's DirectInput specifications because we
+believe that is what most developers have experience with.
+
+- Waveforms of periodic effects do not start at the origin, but as follows:
+ SAW_UP: At minimum
+ SAW_DOWN: At maximum
+ SQUARE: At maximum
+ TRIANGLE: At maximum
+ SINE: At origin
+
+- Updating periodic effects:
+ - All periodic effects are restarted when their parameters are updated.
+
+- Updating effects of finite duration:
+ - Once an effect of finite length finishes playing, it is considered
+ stopped. Only effects that are playing can be updated on the fly.
+ Therefore effects of finite duration can by updated only until
+ they finish playing.
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..269a22d 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -16,6 +16,8 @@ obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o
obj-$(CONFIG_INPUT_JOYDEV) += joydev.o
obj-$(CONFIG_INPUT_EVDEV) += evdev.o
obj-$(CONFIG_INPUT_EVBUG) += evbug.o
+obj-$(CONFIG_INPUT_MATRIXKMAP) += matrix-keymap.o
+obj-$(CONFIG_INPUT_FF_MEMLESS_NEXT) += ff-memless-next.o
obj-$(CONFIG_INPUT_KEYBOARD) += keyboard/
obj-$(CONFIG_INPUT_MOUSE) += mouse/
diff --git a/drivers/input/ff-memless-next.c b/drivers/input/ff-memless-next.c
new file mode 100644
index 0000000..979b823
--- /dev/null
+++ b/drivers/input/ff-memless-next.c
@@ -0,0 +1,786 @@
+/*
+ * Force feedback support for memoryless devices
+ *
+ * This module is based on "ff-memless" orignally written by Anssi Hannula.
+ * It is extended to support all force feedback effects currently supported
+ * by the Linux input stack.
+ *
+ * Copyright(c) 2013 Michal Maly <madcatxster@prifuk.cz>
+ *
+ */
+
+#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_EFFECT_LENGTH ((1000 / CONFIG_HZ) + 1)
+#define FF_EFFECT_STARTED 1
+#define FF_EFFECT_PLAYING 2
+
+
+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 __always_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 __always_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 bool mlnx_is_combinable(const struct ff_effect *effect)
+{
+ switch (effect->type) {
+ case FF_CONSTANT:
+ case FF_PERIODIC:
+ case FF_RAMP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static inline bool mlnx_is_conditional(const struct ff_effect *effect)
+{
+ switch (effect->type) {
+ case FF_DAMPER:
+ case FF_FRICTION:
+ case FF_INERTIA:
+ case FF_SPRING:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static __always_inline bool mlnx_is_square(const struct ff_effect *effect)
+{
+ if (effect->type == FF_PERIODIC)
+ return effect->u.periodic.waveform == FF_SQUARE;
+ return false;
+}
+
+static __always_inline void mlnx_clr_playing(struct mlnx_effect *mlnxeff)
+{
+ __clear_bit(FF_EFFECT_PLAYING, &mlnxeff->flags);
+}
+
+static __always_inline void mlnx_clr_started(struct mlnx_effect *mlnxeff)
+{
+ __clear_bit(FF_EFFECT_STARTED, &mlnxeff->flags);
+}
+
+static __always_inline bool mlnx_is_playing(const struct mlnx_effect *mlnxeff)
+{
+ return test_bit(FF_EFFECT_PLAYING, &mlnxeff->flags);
+}
+
+static __always_inline bool mlnx_is_started(const struct mlnx_effect *mlnxeff)
+{
+ return test_bit(FF_EFFECT_STARTED, &mlnxeff->flags);
+}
+
+static __always_inline bool mlnx_test_set_playing(struct mlnx_effect *mlnxeff)
+{
+ return test_and_set_bit(FF_EFFECT_PLAYING, &mlnxeff->flags);
+}
+
+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 ∅
+ }
+}
+
+/* Some devices might have a limit on how many uncombinable effects
+ * can be played at once */
+static int mlnx_upload_conditional(struct mlnx_device *mlnxdev,
+ const struct ff_effect *effect)
+{
+ struct mlnx_effect_command ecmd = { .cmd = MLNX_UPLOAD_UNCOMB,
+ .u.uncomb.id = effect->id,
+ .u.uncomb.effect = effect };
+ return mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &ecmd);
+}
+
+static void mlnx_set_envelope_times(struct mlnx_effect *mlnxeff)
+{
+ const struct ff_effect *effect = &mlnxeff->effect;
+ const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+
+ if (envelope->attack_length) {
+ unsigned long j = msecs_to_jiffies(envelope->attack_length);
+ mlnxeff->attack_stop = mlnxeff->begin_at + j;
+ }
+ if (effect->replay.length && envelope->fade_length) {
+ unsigned long j = msecs_to_jiffies(envelope->fade_length);
+ mlnxeff->fade_begin = mlnxeff->stop_at - j;
+ }
+}
+
+static void mlnx_set_trip_times(struct mlnx_effect *mlnxeff,
+ const unsigned long now)
+{
+ const struct ff_effect *effect = &mlnxeff->effect;
+ const u16 replay_delay = effect->replay.delay;
+ const u16 replay_length = effect->replay.length;
+
+ mlnxeff->begin_at = now + msecs_to_jiffies(replay_delay);
+ mlnxeff->stop_at = mlnxeff->begin_at + msecs_to_jiffies(replay_length);
+ mlnxeff->updated_at = mlnxeff->begin_at;
+ if (effect->type == FF_PERIODIC)
+ mlnxeff->playback_time = effect->u.periodic.phase;
+}
+
+static void mlnx_start_effect(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,
+ const struct mlnx_effect *mlnxeff)
+{
+ switch (mlnxeff->effect.type) {
+ case FF_CONSTANT:
+ case FF_PERIODIC:
+ case FF_RAMP:
+ 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 = mlnxeff->effect.id,
+ .u.uncomb.effect = &mlnxeff->effect };
+ mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &c);
+ return;
+ }
+ default:
+ return;
+ }
+}
+
+static int mlnx_restart_effect(struct mlnx_device *mlnxdev,
+ struct mlnx_effect *mlnxeff)
+{
+ const struct ff_effect *effect = &mlnxeff->effect;
+ const unsigned long now = jiffies;
+
+ if (mlnx_is_combinable(effect)) {
+ if (effect->replay.delay)
+ mlnx_stop_effect(mlnxdev, mlnxeff);
+ else
+ mlnxdev->combinable_playing--;
+ } else if (mlnx_is_conditional(effect)) {
+ int ret;
+ if (effect->replay.delay)
+ mlnx_stop_effect(mlnxdev, mlnxeff);
+
+ ret = mlnx_upload_conditional(mlnxdev, &mlnxeff->effect);
+ if (ret)
+ return ret;
+ }
+
+ mlnx_set_trip_times(mlnxeff, now);
+ mlnx_set_envelope_times(mlnxeff);
+
+ return 0;
+}
+
+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 clength = jiffies_to_msecs(now - mlnxeff->begin_at);
+ const s32 alength = envelope->attack_length;
+ const s32 dlevel = (alevel - envelope->attack_level)
+ * clength / alength;
+ 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 clength = jiffies_to_msecs(now - mlnxeff->fade_begin);
+ const s32 flength = envelope->fade_length;
+ const s32 dlevel = (envelope->fade_level - alevel)
+ * clength / flength;
+ 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);
+ const s16 offset = effect->u.periodic.offset;
+ s32 new = level;
+ u16 t;
+
+ mlnxeff->playback_time += dt;
+ t = mlnxeff->playback_time % period;
+
+ switch (effect->u.periodic.waveform) {
+ case FF_SINE:
+ {
+ u16 degrees = (360 * t) / period;
+ new = ((level * fixp_sin(degrees)) >> FRAC_N) + offset;
+ break;
+ }
+ case FF_SQUARE:
+ {
+ u16 degrees = (360 * t) / period;
+ new = level * (degrees < 180 ? 1 : -1) + offset;
+ break;
+ }
+ case FF_SAW_UP:
+ new = 2 * level * t / period - level + offset;
+ break;
+ case FF_SAW_DOWN:
+ new = level - 2 * level * t / period + offset;
+ break;
+ case FF_TRIANGLE:
+ {
+ new = (2 * abs(level - (2 * level * t) / period));
+ new = new - level + offset;
+ break;
+ }
+ case FF_CUSTOM:
+ pr_debug("Custom waveform is not handled by this driver.\n");
+ return level;
+ default:
+ pr_err("Invalid waveform.\n");
+ return level;
+ }
+
+ mlnxeff->playback_time = t;
+ /* Ensure that the offset did not make the value exceed s16 range */
+ new = clamp(new, -0x7fff, 0x7fff);
+ 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;
+ const s16 mean = (effect->u.ramp.start_level + effect->u.ramp.end_level) / 2;
+ const u16 t = jiffies_to_msecs(now - mlnxeff->begin_at);
+ s32 start = effect->u.ramp.start_level;
+ s32 end = effect->u.ramp.end_level;
+ s32 new;
+
+ if (envelope->attack_length && time_before(now, mlnxeff->attack_stop)) {
+ const s32 clength = jiffies_to_msecs(now - mlnxeff->begin_at);
+ const s32 alength = envelope->attack_length;
+ s32 attack_level;
+ if (end > start)
+ attack_level = mean - envelope->attack_level;
+ else
+ attack_level = mean + envelope->attack_level;
+ start = (start - attack_level) * clength / alength
+ + attack_level;
+ } else if (envelope->fade_length && time_before_eq(mlnxeff->fade_begin, now)) {
+ const s32 clength = jiffies_to_msecs(now - mlnxeff->fade_begin);
+ const s32 flength = envelope->fade_length;
+ s32 fade_level;
+ if (end > start)
+ fade_level = mean + envelope->fade_level;
+ else
+ fade_level = mean - envelope->fade_level;
+ end = (fade_level - end) * clength / flength + end;
+ }
+
+ new = ((end - start) * t) / length + start;
+ new = clamp(new, -0x7fff, 0x7fff);
+ 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)) {
+ 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 {
+ fade_next = mlnxeff->updated_at
+ + update_rate_jiffies;
+
+ /* Schedule update when the effect stops */
+ if (time_after(fade_next, mlnxeff->stop_at))
+ return mlnxeff->stop_at;
+ /* Schedule update at the next checkpoint */
+ else
+ return fade_next;
+ }
+ } else
+ return mlnxeff->stop_at;
+ }
+
+ /* There is no envelope */
+
+ /* Prevent the effect from being started twice */
+ if (mlnxeff->begin_at == now && mlnx_is_playing(mlnxeff))
+ return now - 1;
+ 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 (mlnx_is_square(&mlnxeff->effect)) {
+ const u16 half_T = mlnxeff->effect.u.periodic.period/2;
+ update_periodic = msecs_to_jiffies(half_T)
+ + 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 (!mlnx_is_started(mlnxeff))
+ continue; /* Effect is not started, skip it */
+
+ if (mlnx_is_playing(mlnxeff))
+ 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)
+{
+ const struct ff_effect *effect = &mlnxeff->effect;
+ 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, effect->u.constant.level);
+ break;
+ case FF_PERIODIC:
+ level = mlnx_apply_envelope(mlnxeff,
+ effect->u.periodic.magnitude);
+ level = mlnx_calculate_periodic(mlnxeff, level);
+ break;
+ case FF_RAMP:
+ level = mlnx_calculate_ramp(mlnxeff);
+ break;
+ default:
+ pr_err("Effect %d not handled by mlnx_add_force!\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 (!mlnx_is_started(mlnxeff)) {
+ 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);
+
+ mlnx_clr_playing(mlnxeff);
+ if (--mlnxeff->repeat > 0)
+ mlnx_restart_effect(mlnxdev, mlnxeff);
+ else {
+ mlnx_clr_started(mlnxeff);
+ mlnx_stop_effect(mlnxdev, mlnxeff);
+ }
+
+ continue;
+ }
+
+ switch (mlnxeff->effect.type) {
+ case FF_CONSTANT:
+ case FF_PERIODIC:
+ case FF_RAMP:
+ if (!mlnx_test_set_playing(mlnxeff)) {
+ 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 (!mlnx_test_set_playing(mlnxeff)) {
+ 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 = clamp(cfx, -0x7fff, 0x7fff),
+ .y = clamp(cfy, -0x7fff, 0x7fff) } };
+ 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++)
+ mlnx_clr_playing(&mlnxdev->effects[i]);
+
+ 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;
+ if (!mlnx_is_started(mlnxeff))
+ mlnx_start_effect(mlnxeff);
+ } else {
+ pr_debug("Stopping effect ID %d\n", effect_id);
+ if (mlnx_is_started(mlnxeff)) {
+ if (mlnx_is_playing(mlnxeff)) {
+ mlnx_clr_playing(mlnxeff);
+ mlnx_stop_effect(mlnxdev, mlnxeff);
+ }
+ mlnx_clr_started(mlnxeff);
+ } else {
+ pr_debug("Effect ID %d already stopped.\n", effect_id);
+ return 0;
+ }
+ }
+ 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 u16 length = effect->replay.length;
+ const u16 delay = effect->replay.delay;
+ int ret, fade_from;
+
+ /* Effect's timing is below kernel timer resolution */
+ if (length && length < FF_MIN_EFFECT_LENGTH)
+ effect->replay.length = FF_MIN_EFFECT_LENGTH;
+ if (delay && delay < FF_MIN_EFFECT_LENGTH)
+ effect->replay.delay = FF_MIN_EFFECT_LENGTH;
+
+ /* Periodic effects must have a non-zero period */
+ if (effect->type == FF_PERIODIC) {
+ if (!effect->u.periodic.period)
+ return -EINVAL;
+ }
+ /* Ramp effects cannot be infinite */
+ if (effect->type == FF_RAMP && !length)
+ return -EINVAL;
+
+ if (mlnx_is_combinable(effect)) {
+ const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+
+ /* Infinite effects cannot fade */
+ if (!length && envelope->fade_length > 0)
+ return -EINVAL;
+ /* Fade length cannot be greater than effect duration */
+ fade_from = (int)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 && fade_from > 0)
+ return -EINVAL;
+ }
+
+
+ spin_lock_irq(&dev->event_lock);
+ mlnxeff->effect = *effect; /* Keep internal copy of the effect */
+ /* Check if the effect being modified is playing */
+ if (mlnx_is_started(mlnxeff)) {
+ if (mlnx_is_playing(mlnxeff)) {
+ mlnx_clr_playing(mlnxeff);
+ ret = mlnx_restart_effect(mlnxdev, mlnxeff);
+
+ if (ret) {
+ /* Restore the original effect */
+ if (old)
+ mlnxeff->effect = *old;
+ spin_unlock_irq(&dev->event_lock);
+ return ret;
+ }
+ }
+
+ mlnx_schedule_playback(mlnxdev);
+ spin_unlock_irq(&dev->event_lock);
+ return 0;
+ }
+
+ if (mlnx_is_conditional(effect)) {
+ ret = mlnx_upload_conditional(mlnxdev, &mlnxeff->effect);
+ if (ret) {
+ /* Restore the original effect */
+ if (old)
+ mlnxeff->effect = *old;
+ 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;
+
+ 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 = update_rate < FF_MIN_EFFECT_LENGTH ?
+ FF_MIN_EFFECT_LENGTH : 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;
+
+ 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..f2fb080
--- /dev/null
+++ b/include/linux/input/ff-memless-next.h
@@ -0,0 +1,31 @@
+#include <linux/input.h>
+
+enum mlnx_commands {
+ MLNX_START_COMBINED,
+ MLNX_STOP_COMBINED,
+ MLNX_START_UNCOMB,
+ MLNX_STOP_UNCOMB,
+ MLNX_UPLOAD_UNCOMB
+};
+
+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.2
--
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: [Dell Inspiron 17R SE 7720] Synaptics touchpad recognized as PS/2 Generic Mouse
From: Robert van Geerenstein @ 2013-12-23 18:56 UTC (permalink / raw)
To: Kevin Cernekee; +Cc: linux-input, Tommy Will, Justin Clift
In-Reply-To: <CAJiQ=7CG-C4RR7xO_W+SKai-fLKr6xutAn5L0omuLgDPhBtFHg@mail.gmail.com>
On 23-12-2013 19:47, Kevin Cernekee wrote:
> On Mon, Dec 23, 2013 at 10:20 AM, Robert van Geerenstein <info@r15n.nl> wrote:
>> My touchpad is detected as a PS/2 Generic Mouse on my Dell Inspiron 7720
>> (17R SE).
>> It only has the functionality of a generic mouse (movement, left clik, right
>> click) and no scrolling or multitouch.
> Hmm, doesn't this laptop have an ALPS Dolphin V2 touchpad (not Synaptics)?
>
> Tommy and Justin were working on this a while back. There are a few
> patches in the list archives that you could try out. I believe
> they're mostly ready for merging except for one remaining issue with
> the trackstick.
> --
> 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
Hmm, looks like you're right. :/
Didn't even notice after all this time, guess back when I made the
bugreport on launchpad I was all over the place trying to get it to work.
I'll go ahead and see if I can find anything in the archives that might
get it to work.
Good to hear they're mostly ready for merging though.
^ permalink raw reply
* Re: [Dell Inspiron 17R SE 7720] Synaptics touchpad recognized as PS/2 Generic Mouse
From: Kevin Cernekee @ 2013-12-23 18:47 UTC (permalink / raw)
To: Robert van Geerenstein; +Cc: linux-input, Tommy Will, Justin Clift
In-Reply-To: <52B87EE0.5030104@r15n.nl>
On Mon, Dec 23, 2013 at 10:20 AM, Robert van Geerenstein <info@r15n.nl> wrote:
> My touchpad is detected as a PS/2 Generic Mouse on my Dell Inspiron 7720
> (17R SE).
> It only has the functionality of a generic mouse (movement, left clik, right
> click) and no scrolling or multitouch.
Hmm, doesn't this laptop have an ALPS Dolphin V2 touchpad (not Synaptics)?
Tommy and Justin were working on this a while back. There are a few
patches in the list archives that you could try out. I believe
they're mostly ready for merging except for one remaining issue with
the trackstick.
^ permalink raw reply
* [Dell Inspiron 17R SE 7720] Synaptics touchpad recognized as PS/2 Generic Mouse
From: Robert van Geerenstein @ 2013-12-23 18:20 UTC (permalink / raw)
To: linux-input
My touchpad is detected as a PS/2 Generic Mouse on my Dell Inspiron 7720
(17R SE).
It only has the functionality of a generic mouse (movement, left clik,
right click) and no scrolling or multitouch.
Launchpad bugreport:
https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1144000
========================================================
There was a duplicate bugreport in this mailinglist posted by Jason O.
back in August (http://www.spinics.net/lists/linux-input/msg27046.html),
but this was incomplete.
Bug reports by Jason O.
Launchpad
https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1093237
kernel.org
https://bugzilla.kernel.org/show_bug.cgi?id=60739
========================================================
Info:
# cat /proc/version
Linux version 3.13.0-031300rc5-generic (apw@gomeisa) (gcc version 4.6.3
(Ubuntu/Linaro 4.6.3-1ubuntu5) ) #201312221635 SMP Sun Dec 22 21:37:23
UTC 2013
# lsb_release -rd
Description: Ubuntu 13.10
Release: 13.10
# sh /usr/src/linux-headers-3.13.0-031300rc5/scripts/ver_linux
If some fields are empty or look unusual you may have an old version.
Compare to the current minimal requirements in Documentation/Changes.
Linux lapmans 3.13.0-031300rc5-generic #201312221635 SMP Sun Dec 22
21:37:23 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
Gnu C 4.8
Gnu make 3.81
binutils 2.23.52.20130913
util-linux 2.20.1
mount support
module-init-tools 9
e2fsprogs 1.42.8
pcmciautils 018
PPP 2.4.5
Linux C Library 2.17
Dynamic linker (ldd) 2.17
Procps 3.3.3
Net-tools 1.60
Kbd 1.15.5
Sh-utils 8.20
wireless-tools 30
Modules Loaded ctr ccm snd_hda_codec_hdmi snd_hda_codec_idt parport_pc
ppdev x86_pkg_temp_thermal intel_powerclamp coretemp kvm_intel kvm
crct10dif_pclmul crc32_pclmul ghash_clmulni_intel aesni_intel aes_x86_64
lrw gf128mul glue_helper ablk_helper cryptd dell_laptop dcdbas microcode
psmouse serio_raw rfcomm bnep snd_hda_intel snd_hda_codec snd_hwdep
snd_pcm dm_multipath binfmt_misc arc4 scsi_dh snd_page_alloc iwldvm
snd_seq_midi snd_seq_midi_event mac80211 i915 uvcvideo snd_rawmidi
snd_seq videobuf2_vmalloc videobuf2_memops snd_seq_device videobuf2_core
snd_timer rts5139 videodev iwlwifi ext2 snd btusb drm_kms_helper
bluetooth drm dell_wmi cfg80211 sparse_keymap soundcore mei_me mei
i2c_algo_bit lpc_ich wmi lp mac_hid video parport raid10 raid456
async_raid6_recov async_memcpy async_pq async_xor async_tx xor raid6_pq
raid1 raid0 multipath linear ahci r8169 libahci mii
# cat /proc/cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 58
model name : Intel(R) Core(TM) i7-3630QM CPU @ 2.40GHz
stepping : 9
microcode : 0x15
cpu MHz : 1751.906
cache size : 6144 KB
physical id : 0
siblings : 8
core id : 0
cpu cores : 4
apicid : 0
initial apicid : 0
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov
pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx
rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology
nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx
est tm2 ssse3 cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic popcnt
tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm ida arat epb
xsaveopt pln pts dtherm tpr_shadow vnmi flexpriority ept vpid fsgsbase
smep erms
bogomips : 4788.86
clflush size : 64
cache_alignment : 64
address sizes : 36 bits physical, 48 bits virtual
power management:
processor : 1
vendor_id : GenuineIntel
cpu family : 6
model : 58
model name : Intel(R) Core(TM) i7-3630QM CPU @ 2.40GHz
stepping : 9
microcode : 0x15
cpu MHz : 1908.000
cache size : 6144 KB
physical id : 0
siblings : 8
core id : 0
cpu cores : 4
apicid : 1
initial apicid : 1
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov
pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx
rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology
nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx
est tm2 ssse3 cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic popcnt
tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm ida arat epb
xsaveopt pln pts dtherm tpr_shadow vnmi flexpriority ept vpid fsgsbase
smep erms
bogomips : 4788.86
clflush size : 64
cache_alignment : 64
address sizes : 36 bits physical, 48 bits virtual
power management:
processor : 2
vendor_id : GenuineIntel
cpu family : 6
model : 58
model name : Intel(R) Core(TM) i7-3630QM CPU @ 2.40GHz
stepping : 9
microcode : 0x15
cpu MHz : 1737.281
cache size : 6144 KB
physical id : 0
siblings : 8
core id : 1
cpu cores : 4
apicid : 2
initial apicid : 2
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov
pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx
rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology
nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx
est tm2 ssse3 cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic popcnt
tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm ida arat epb
xsaveopt pln pts dtherm tpr_shadow vnmi flexpriority ept vpid fsgsbase
smep erms
bogomips : 4788.86
clflush size : 64
cache_alignment : 64
address sizes : 36 bits physical, 48 bits virtual
power management:
processor : 3
vendor_id : GenuineIntel
cpu family : 6
model : 58
model name : Intel(R) Core(TM) i7-3630QM CPU @ 2.40GHz
stepping : 9
microcode : 0x15
cpu MHz : 1979.812
cache size : 6144 KB
physical id : 0
siblings : 8
core id : 1
cpu cores : 4
apicid : 3
initial apicid : 3
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov
pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx
rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology
nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx
est tm2 ssse3 cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic popcnt
tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm ida arat epb
xsaveopt pln pts dtherm tpr_shadow vnmi flexpriority ept vpid fsgsbase
smep erms
bogomips : 4788.86
clflush size : 64
cache_alignment : 64
address sizes : 36 bits physical, 48 bits virtual
power management:
processor : 4
vendor_id : GenuineIntel
cpu family : 6
model : 58
model name : Intel(R) Core(TM) i7-3630QM CPU @ 2.40GHz
stepping : 9
microcode : 0x15
cpu MHz : 1488.093
cache size : 6144 KB
physical id : 0
siblings : 8
core id : 2
cpu cores : 4
apicid : 4
initial apicid : 4
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov
pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx
rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology
nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx
est tm2 ssse3 cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic popcnt
tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm ida arat epb
xsaveopt pln pts dtherm tpr_shadow vnmi flexpriority ept vpid fsgsbase
smep erms
bogomips : 4788.86
clflush size : 64
cache_alignment : 64
address sizes : 36 bits physical, 48 bits virtual
power management:
processor : 5
vendor_id : GenuineIntel
cpu family : 6
model : 58
model name : Intel(R) Core(TM) i7-3630QM CPU @ 2.40GHz
stepping : 9
microcode : 0x15
cpu MHz : 1960.781
cache size : 6144 KB
physical id : 0
siblings : 8
core id : 2
cpu cores : 4
apicid : 5
initial apicid : 5
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov
pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx
rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology
nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx
est tm2 ssse3 cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic popcnt
tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm ida arat epb
xsaveopt pln pts dtherm tpr_shadow vnmi flexpriority ept vpid fsgsbase
smep erms
bogomips : 4788.86
clflush size : 64
cache_alignment : 64
address sizes : 36 bits physical, 48 bits virtual
power management:
processor : 6
vendor_id : GenuineIntel
cpu family : 6
model : 58
model name : Intel(R) Core(TM) i7-3630QM CPU @ 2.40GHz
stepping : 9
microcode : 0x15
cpu MHz : 1885.312
cache size : 6144 KB
physical id : 0
siblings : 8
core id : 3
cpu cores : 4
apicid : 6
initial apicid : 6
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov
pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx
rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology
nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx
est tm2 ssse3 cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic popcnt
tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm ida arat epb
xsaveopt pln pts dtherm tpr_shadow vnmi flexpriority ept vpid fsgsbase
smep erms
bogomips : 4788.86
clflush size : 64
cache_alignment : 64
address sizes : 36 bits physical, 48 bits virtual
power management:
processor : 7
vendor_id : GenuineIntel
cpu family : 6
model : 58
model name : Intel(R) Core(TM) i7-3630QM CPU @ 2.40GHz
stepping : 9
microcode : 0x15
cpu MHz : 1815.843
cache size : 6144 KB
physical id : 0
siblings : 8
core id : 3
cpu cores : 4
apicid : 7
initial apicid : 7
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov
pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx
rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology
nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx
est tm2 ssse3 cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic popcnt
tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm ida arat epb
xsaveopt pln pts dtherm tpr_shadow vnmi flexpriority ept vpid fsgsbase
smep erms
bogomips : 4788.86
clflush size : 64
cache_alignment : 64
address sizes : 36 bits physical, 48 bits virtual
power management:
# cat /proc/modules
ctr 13193 1 - Live 0xffffffffa0484000
ccm 17856 1 - Live 0xffffffffa047e000
snd_hda_codec_hdmi 46898 1 - Live 0xffffffffa0471000
snd_hda_codec_idt 59236 1 - Live 0xffffffffa072b000
parport_pc 36962 0 - Live 0xffffffffa0720000
ppdev 17711 0 - Live 0xffffffffa0747000
x86_pkg_temp_thermal 14269 0 - Live 0xffffffffa0685000
intel_powerclamp 19031 0 - Live 0xffffffffa064e000
coretemp 17728 0 - Live 0xffffffffa0673000
kvm_intel 144426 0 - Live 0xffffffffa0b64000
kvm 468147 1 kvm_intel, Live 0xffffffffa06ac000
crct10dif_pclmul 14250 0 - Live 0xffffffffa067a000
crc32_pclmul 13160 0 - Live 0xffffffffa0666000
ghash_clmulni_intel 13259 0 - Live 0xffffffffa05ea000
aesni_intel 55720 2 - Live 0xffffffffa069d000
aes_x86_64 17131 1 aesni_intel, Live 0xffffffffa0648000
lrw 13323 1 aesni_intel, Live 0xffffffffa0661000
gf128mul 14951 1 lrw, Live 0xffffffffa065c000
glue_helper 14095 1 aesni_intel, Live 0xffffffffa0657000
ablk_helper 13597 1 aesni_intel, Live 0xffffffffa0680000
cryptd 20530 3 ghash_clmulni_intel,aesni_intel,ablk_helper, Live
0xffffffffa0696000
dell_laptop 18315 0 - Live 0xffffffffa0690000
dcdbas 15017 1 dell_laptop, Live 0xffffffffa057e000
microcode 23788 0 - Live 0xffffffffa0637000
psmouse 108513 0 - Live 0xffffffffa061b000
serio_raw 13462 0 - Live 0xffffffffa01bc000
rfcomm 74748 12 - Live 0xffffffffa0607000
bnep 19884 2 - Live 0xffffffffa05e4000
snd_hda_intel 57222 3 - Live 0xffffffffa05f8000
snd_hda_codec 199156 3
snd_hda_codec_hdmi,snd_hda_codec_idt,snd_hda_intel, Live 0xffffffffa05b2000
snd_hwdep 13613 1 snd_hda_codec, Live 0xffffffffa0386000
snd_pcm 107140 3 snd_hda_codec_hdmi,snd_hda_intel,snd_hda_codec, Live
0xffffffffa0596000
dm_multipath 27401 0 - Live 0xffffffffa058e000
binfmt_misc 17508 1 - Live 0xffffffffa03a1000
arc4 12573 2 - Live 0xffffffffa0589000
scsi_dh 14873 1 dm_multipath, Live 0xffffffffa02f3000
snd_page_alloc 18798 2 snd_hda_intel,snd_pcm, Live 0xffffffffa0583000
iwldvm 243417 0 - Live 0xffffffffa053a000
snd_seq_midi 13324 0 - Live 0xffffffffa02a1000
snd_seq_midi_event 14899 1 snd_seq_midi, Live 0xffffffffa029c000
mac80211 654078 1 iwldvm, Live 0xffffffffa0499000
i915 816826 3 - Live 0xffffffffa03a8000
uvcvideo 82247 0 - Live 0xffffffffa038b000
snd_rawmidi 30465 1 snd_seq_midi, Live 0xffffffffa037d000
snd_seq 66061 2 snd_seq_midi,snd_seq_midi_event, Live 0xffffffffa036b000
videobuf2_vmalloc 13216 1 uvcvideo, Live 0xffffffffa01a9000
videobuf2_memops 13362 1 videobuf2_vmalloc, Live 0xffffffffa022e000
snd_seq_device 14497 3 snd_seq_midi,snd_rawmidi,snd_seq, Live
0xffffffffa01a4000
videobuf2_core 40972 1 uvcvideo, Live 0xffffffffa02e7000
snd_timer 30038 2 snd_pcm,snd_seq, Live 0xffffffffa019b000
rts5139 318332 0 - Live 0xffffffffa031c000 (C)
videodev 139761 2 uvcvideo,videobuf2_core, Live 0xffffffffa02f8000
iwlwifi 179720 1 iwldvm, Live 0xffffffffa02ba000
ext2 73828 1 - Live 0xffffffffa02a6000
snd 73850 17
snd_hda_codec_hdmi,snd_hda_codec_idt,snd_hda_intel,snd_hda_codec,snd_hwdep,snd_pcm,snd_seq_midi,snd_rawmidi,snd_seq,snd_seq_device,snd_timer,
Live 0xffffffffa021a000
btusb 28326 0 - Live 0xffffffffa0193000
drm_kms_helper 53224 1 i915, Live 0xffffffffa01ae000
bluetooth 411140 22 rfcomm,bnep,btusb, Live 0xffffffffa0236000
drm 308397 4 i915,drm_kms_helper, Live 0xffffffffa01cd000
dell_wmi 12681 0 - Live 0xffffffffa01c4000
cfg80211 509407 3 iwldvm,mac80211,iwlwifi, Live 0xffffffffa0115000
sparse_keymap 13890 1 dell_wmi, Live 0xffffffffa0110000
soundcore 12680 1 snd, Live 0xffffffffa00fe000
mei_me 18578 0 - Live 0xffffffffa0106000
mei 82671 1 mei_me, Live 0xffffffffa00e8000
i2c_algo_bit 13564 1 i915, Live 0xffffffffa00e3000
lpc_ich 21163 0 - Live 0xffffffffa00dc000
wmi 19363 1 dell_wmi, Live 0xffffffffa00cd000
lp 17799 0 - Live 0xffffffffa00a9000
mac_hid 13253 0 - Live 0xffffffffa00d3000
video 19859 1 i915, Live 0xffffffffa0038000
parport 42481 3 parport_pc,ppdev,lp, Live 0xffffffffa00bd000
raid10 48667 0 - Live 0xffffffffa00b0000
raid456 87551 0 - Live 0xffffffffa0092000
async_raid6_recov 12984 1 raid456, Live 0xffffffffa008d000
async_memcpy 12689 1 raid456, Live 0xffffffffa0088000
async_pq 13456 1 raid456, Live 0xffffffffa0080000
async_xor 13233 2 raid456,async_pq, Live 0xffffffffa003e000
async_tx 13509 5
raid456,async_raid6_recov,async_memcpy,async_pq,async_xor, Live
0xffffffffa007b000
xor 21411 1 async_xor, Live 0xffffffffa0074000
raid6_pq 97812 2 async_raid6_recov,async_pq, Live 0xffffffffa0057000
raid1 35921 0 - Live 0xffffffffa004d000
raid0 17969 0 - Live 0xffffffffa0044000
multipath 13145 0 - Live 0xffffffffa0028000
linear 12894 0 - Live 0xffffffffa0010000
ahci 30063 2 - Live 0xffffffffa002f000
r8169 73299 0 - Live 0xffffffffa0015000
libahci 32277 1 ahci, Live 0xffffffffa0007000
mii 13981 1 r8169, Live 0xffffffffa0000000
# cat /proc/ioports
0000-0cf7 : PCI Bus 0000:00
0000-001f : dma1
0020-0021 : pic1
0040-0043 : timer0
0050-0053 : timer1
0060-0060 : keyboard
0062-0062 : EC data
0064-0064 : keyboard
0066-0066 : EC cmd
0070-0077 : rtc0
0080-008f : dma page reg
00a0-00a1 : pic2
00c0-00df : dma2
00f0-00ff : fpu
0400-0403 : ACPI PM1a_EVT_BLK
0404-0405 : ACPI PM1a_CNT_BLK
0408-040b : ACPI PM_TMR
0410-0415 : ACPI CPU throttle
0420-042f : ACPI GPE0_BLK
0430-0433 : iTCO_wdt
0450-0450 : ACPI PM2_CNT_BLK
0454-0457 : pnp 00:05
0458-047f : pnp 00:03
0460-047f : iTCO_wdt
0500-057f : pnp 00:03
0680-069f : pnp 00:03
0cf8-0cff : PCI conf1
0d00-ffff : PCI Bus 0000:00
1000-1003 : pnp 00:03
1004-1013 : pnp 00:03
164e-164f : pnp 00:03
2000-2fff : PCI Bus 0000:03
2000-20ff : 0000:03:00.0
2000-20ff : r8169
3000-3fff : PCI Bus 0000:01
3000-307f : 0000:01:00.0
4000-403f : 0000:00:02.0
4060-407f : 0000:00:1f.2
4060-407f : ahci
4090-4097 : 0000:00:1f.2
4090-4097 : ahci
4098-409f : 0000:00:1f.2
4098-409f : ahci
40b8-40bb : 0000:00:1f.2
40b8-40bb : ahci
40bc-40bf : 0000:00:1f.2
40bc-40bf : ahci
efa0-efbf : 0000:00:1f.3
ffff-ffff : pnp 00:03
# cat /proc/iomem
00000000-00000fff : reserved
00001000-0009d7ff : System RAM
0009d800-0009ffff : reserved
000a0000-000bffff : PCI Bus 0000:00
000c0000-000cedff : Video ROM
000e0000-000fffff : reserved
000f0000-000fffff : System ROM
00100000-1fffffff : System RAM
01000000-01770d1d : Kernel code
01770d1e-01d1bf3f : Kernel data
01e78000-01fdcfff : Kernel bss
20000000-201fffff : reserved
20200000-40003fff : System RAM
40004000-40004fff : reserved
40005000-a7268fff : System RAM
a7269000-a746afff : reserved
a746b000-b7f54fff : System RAM
b7f55000-baeeefff : reserved
baeef000-baf9efff : ACPI Non-volatile Storage
baf9f000-baffefff : ACPI Tables
bafff000-baffffff : System RAM
bb000000-bf9fffff : reserved
bba00000-bf9fffff : Graphics Stolen Memory
bfa00000-feafffff : PCI Bus 0000:00
c0000000-d1ffffff : PCI Bus 0000:01
c0000000-cfffffff : 0000:01:00.0
d0000000-d1ffffff : 0000:01:00.0
e0000000-efffffff : 0000:00:02.0
e0000000-e07e8fff : BOOTFB
f0000000-f0ffffff : PCI Bus 0000:01
f0000000-f0ffffff : 0000:01:00.0
f1000000-f13fffff : 0000:00:02.0
f1400000-f14fffff : PCI Bus 0000:03
f1400000-f1403fff : 0000:03:00.0
f1400000-f1403fff : r8169
f1404000-f1404fff : 0000:03:00.0
f1404000-f1404fff : r8169
f1500000-f15fffff : PCI Bus 0000:02
f1500000-f1501fff : 0000:02:00.0
f1500000-f1501fff : iwlwifi
f1600000-f160ffff : 0000:00:14.0
f1600000-f160ffff : xhci_hcd
f1610000-f1613fff : 0000:00:1b.0
f1610000-f1613fff : ICH HD audio
f1614000-f16140ff : 0000:00:1f.3
f1615000-f161500f : 0000:00:16.0
f1615000-f161500f : mei_me
f1618000-f16187ff : 0000:00:1f.2
f1618000-f16187ff : ahci
f1619000-f16193ff : 0000:00:1d.0
f1619000-f16193ff : ehci_hcd
f161a000-f161a3ff : 0000:00:1a.0
f161a000-f161a3ff : ehci_hcd
f8000000-fbffffff : PCI MMCONFIG 0000 [bus 00-3f]
f8000000-fbffffff : reserved
f8000000-fbffffff : pnp 00:08
fec00000-fec00fff : reserved
fec00000-fec003ff : IOAPIC 0
fed00000-fed003ff : HPET 0
fed08000-fed08fff : reserved
fed10000-fed19fff : reserved
fed10000-fed17fff : pnp 00:08
fed18000-fed18fff : pnp 00:08
fed19000-fed19fff : pnp 00:08
fed1c000-fed1ffff : reserved
fed1c000-fed1ffff : pnp 00:08
fed1f410-fed1f414 : iTCO_wdt
fed20000-fed3ffff : pnp 00:08
fed45000-fed8ffff : pnp 00:08
fed90000-fed93fff : pnp 00:08
fee00000-fee00fff : Local APIC
fee00000-fee00fff : reserved
ff980000-ffffffff : reserved
100000000-23f5fffff : System RAM
23f600000-23fffffff : RAM buffer
# lspci -vvv
00:00.0 Host bridge: Intel Corporation 3rd Gen Core processor DRAM
Controller (rev 09)
Subsystem: Dell Device 0578
Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR- FastB2B- DisINTx-
Status: Cap+ 66MHz- UDF- FastB2B+ ParErr- DEVSEL=fast >TAbort- <TAbort-
<MAbort+ >SERR- <PERR- INTx-
Latency: 0
Capabilities: [e0] Vendor Specific Information: Len=0c <?>
00:01.0 PCI bridge: Intel Corporation Xeon E3-1200 v2/3rd Gen Core
processor PCI Express Root Port (rev 09) (prog-if 00 [Normal decode])
Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR- FastB2B- DisINTx+
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort-
<MAbort- >SERR- <PERR- INTx-
Latency: 0, Cache Line Size: 64 bytes
Bus: primary=00, secondary=01, subordinate=01, sec-latency=0
I/O behind bridge: 00003000-00003fff
Memory behind bridge: f0000000-f0ffffff
Prefetchable memory behind bridge: 00000000c0000000-00000000d1ffffff
Secondary status: 66MHz- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort-
<MAbort- <SERR- <PERR-
BridgeCtl: Parity- SERR- NoISA- VGA- MAbort- >Reset- FastB2B-
PriDiscTmr- SecDiscTmr- DiscTmrStat- DiscTmrSERREn-
Capabilities: [88] Subsystem: Dell Device 0578
Capabilities: [80] Power Management version 3
Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA PME(D0+,D1-,D2-,D3hot+,D3cold+)
Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=0 PME-
Capabilities: [90] MSI: Enable+ Count=1/1 Maskable- 64bit-
Address: feeff00c Data: 4171
Capabilities: [a0] Express (v2) Root Port (Slot+), MSI 00
DevCap: MaxPayload 256 bytes, PhantFunc 0, Latency L0s <64ns, L1 <1us
ExtTag- RBE+ FLReset-
DevCtl: Report errors: Correctable- Non-Fatal- Fatal- Unsupported-
RlxdOrd- ExtTag- PhantFunc- AuxPwr- NoSnoop-
MaxPayload 256 bytes, MaxReadReq 128 bytes
DevSta: CorrErr- UncorrErr- FatalErr- UnsuppReq- AuxPwr- TransPend-
LnkCap: Port #2, Speed 8GT/s, Width x16, ASPM L0s L1, Latency L0 <256ns,
L1 <8us
ClockPM- Surprise- LLActRep- BwNot+
LnkCtl: ASPM L0s L1 Enabled; RCB 64 bytes Disabled- Retrain- CommClk+
ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
LnkSta: Speed 8GT/s, Width x16, TrErr- Train- SlotClk+ DLActive- BWMgmt+
ABWMgmt+
SltCap: AttnBtn- PwrCtrl- MRL- AttnInd- PwrInd- HotPlug- Surprise-
Slot #1, PowerLimit 75.000W; Interlock- NoCompl+
SltCtl: Enable: AttnBtn- PwrFlt- MRL- PresDet- CmdCplt- HPIrq- LinkChg-
Control: AttnInd Unknown, PwrInd Unknown, Power- Interlock-
SltSta: Status: AttnBtn- PowerFlt- MRL- CmdCplt- PresDet+ Interlock-
Changed: MRL- PresDet+ LinkState-
RootCtl: ErrCorrectable- ErrNon-Fatal- ErrFatal- PMEIntEna- CRSVisible-
RootCap: CRSVisible-
RootSta: PME ReqID 0000, PMEStatus- PMEPending-
DevCap2: Completion Timeout: Not Supported, TimeoutDis- ARIFwd-
DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis- ARIFwd-
LnkCtl2: Target Link Speed: 8GT/s, EnterCompliance- SpeedDis-,
Selectable De-emphasis: -3.5dB
Transmit Margin: Normal Operating Range, EnterModifiedCompliance-
ComplianceSOS-
Compliance De-emphasis: -6dB
LnkSta2: Current De-emphasis Level: -6dB, EqualizationComplete+,
EqualizationPhase1+
EqualizationPhase2+, EqualizationPhase3+, LinkEqualizationRequest-
Capabilities: [100 v1] Virtual Channel
Caps: LPEVC=0 RefClk=100ns PATEntryBits=1
Arb: Fixed- WRR32- WRR64- WRR128-
Ctrl: ArbSelect=Fixed
Status: InProgress-
VC0: Caps: PATOffset=00 MaxTimeSlots=1 RejSnoopTrans-
Arb: Fixed+ WRR32- WRR64- WRR128- TWRR128- WRR256-
Ctrl: Enable+ ID=0 ArbSelect=Fixed TC/VC=ff
Status: NegoPending- InProgress-
Capabilities: [140 v1] Root Complex Link
Desc: PortNumber=02 ComponentID=01 EltType=Config
Link0: Desc: TargetPort=00 TargetComponent=01 AssocRCRB-
LinkType=MemMapped LinkValid+
Addr: 00000000fed19000
Capabilities: [d94 v1] #19
Kernel driver in use: pcieport
00:02.0 VGA compatible controller: Intel Corporation 3rd Gen Core
processor Graphics Controller (rev 09) (prog-if 00 [VGA controller])
Subsystem: Dell Device 0578
Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR- FastB2B- DisINTx+
Status: Cap+ 66MHz- UDF- FastB2B+ ParErr- DEVSEL=fast >TAbort- <TAbort-
<MAbort- >SERR- <PERR- INTx-
Latency: 0
Interrupt: pin A routed to IRQ 46
Region 0: Memory at f1000000 (64-bit, non-prefetchable) [size=4M]
Region 2: Memory at e0000000 (64-bit, prefetchable) [size=256M]
Region 4: I/O ports at 4000 [size=64]
Expansion ROM at <unassigned> [disabled]
Capabilities: [90] MSI: Enable+ Count=1/1 Maskable- 64bit-
Address: feeff00c Data: 41e1
Capabilities: [d0] Power Management version 2
Flags: PMEClk- DSI+ D1- D2- AuxCurrent=0mA PME(D0-,D1-,D2-,D3hot-,D3cold-)
Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=0 PME-
Capabilities: [a4] PCI Advanced Features
AFCap: TP+ FLR+
AFCtrl: FLR-
AFStatus: TP-
Kernel driver in use: i915
00:14.0 USB controller: Intel Corporation 7 Series/C210 Series Chipset
Family USB xHCI Host Controller (rev 04) (prog-if 30 [XHCI])
Subsystem: Dell Device 0578
Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR- FastB2B- DisINTx+
Status: Cap+ 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort-
<TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0
Interrupt: pin A routed to IRQ 41
Region 0: Memory at f1600000 (64-bit, non-prefetchable) [size=64K]
Capabilities: [70] Power Management version 2
Flags: PMEClk- DSI- D1- D2- AuxCurrent=375mA PME(D0-,D1-,D2-,D3hot+,D3cold+)
Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=0 PME-
Capabilities: [80] MSI: Enable+ Count=1/8 Maskable- 64bit+
Address: 00000000feeff00c Data: 4181
Kernel driver in use: xhci_hcd
00:16.0 Communication controller: Intel Corporation 7 Series/C210 Series
Chipset Family MEI Controller #1 (rev 04)
Subsystem: Dell Device 0578
Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR- FastB2B- DisINTx+
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort-
<MAbort- >SERR- <PERR- INTx-
Latency: 0
Interrupt: pin A routed to IRQ 44
Region 0: Memory at f1615000 (64-bit, non-prefetchable) [size=16]
Capabilities: [50] Power Management version 3
Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA PME(D0+,D1-,D2-,D3hot+,D3cold+)
Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=0 PME-
Capabilities: [8c] MSI: Enable+ Count=1/1 Maskable- 64bit+
Address: 00000000feeff00c Data: 41c1
Kernel driver in use: mei_me
00:1a.0 USB controller: Intel Corporation 7 Series/C210 Series Chipset
Family USB Enhanced Host Controller #2 (rev 04) (prog-if 20 [EHCI])
Subsystem: Dell Device 0578
Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR- FastB2B- DisINTx-
Status: Cap+ 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort-
<TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0
Interrupt: pin A routed to IRQ 16
Region 0: Memory at f161a000 (32-bit, non-prefetchable) [size=1K]
Capabilities: [50] Power Management version 2
Flags: PMEClk- DSI- D1- D2- AuxCurrent=375mA PME(D0+,D1-,D2-,D3hot+,D3cold+)
Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=0 PME-
Capabilities: [58] Debug port: BAR=1 offset=00a0
Capabilities: [98] PCI Advanced Features
AFCap: TP+ FLR+
AFCtrl: FLR-
AFStatus: TP-
Kernel driver in use: ehci-pci
00:1b.0 Audio device: Intel Corporation 7 Series/C210 Series Chipset
Family High Definition Audio Controller (rev 04)
Subsystem: Dell Device 0578
Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR- FastB2B- DisINTx+
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort-
<MAbort- >SERR- <PERR- INTx-
Latency: 0, Cache Line Size: 64 bytes
Interrupt: pin A routed to IRQ 47
Region 0: Memory at f1610000 (64-bit, non-prefetchable) [size=16K]
Capabilities: [50] Power Management version 2
Flags: PMEClk- DSI- D1- D2- AuxCurrent=55mA PME(D0+,D1-,D2-,D3hot+,D3cold+)
Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=0 PME-
Capabilities: [60] MSI: Enable+ Count=1/1 Maskable- 64bit+
Address: 00000000feeff00c Data: 4142
Capabilities: [70] Express (v1) Root Complex Integrated Endpoint, MSI 00
DevCap: MaxPayload 128 bytes, PhantFunc 0, Latency L0s <64ns, L1 <1us
ExtTag- RBE- FLReset+
DevCtl: Report errors: Correctable- Non-Fatal- Fatal- Unsupported-
RlxdOrd- ExtTag- PhantFunc- AuxPwr- NoSnoop-
MaxPayload 128 bytes, MaxReadReq 128 bytes
DevSta: CorrErr- UncorrErr- FatalErr- UnsuppReq- AuxPwr+ TransPend-
LnkCap: Port #0, Speed unknown, Width x0, ASPM unknown, Latency L0
<64ns, L1 <1us
ClockPM- Surprise- LLActRep- BwNot-
LnkCtl: ASPM Disabled; Disabled- Retrain- CommClk-
ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
LnkSta: Speed unknown, Width x0, TrErr- Train- SlotClk- DLActive-
BWMgmt- ABWMgmt-
Capabilities: [100 v1] Virtual Channel
Caps: LPEVC=0 RefClk=100ns PATEntryBits=1
Arb: Fixed- WRR32- WRR64- WRR128-
Ctrl: ArbSelect=Fixed
Status: InProgress-
VC0: Caps: PATOffset=00 MaxTimeSlots=1 RejSnoopTrans-
Arb: Fixed- WRR32- WRR64- WRR128- TWRR128- WRR256-
Ctrl: Enable+ ID=0 ArbSelect=Fixed TC/VC=01
Status: NegoPending- InProgress-
VC1: Caps: PATOffset=00 MaxTimeSlots=1 RejSnoopTrans-
Arb: Fixed- WRR32- WRR64- WRR128- TWRR128- WRR256-
Ctrl: Enable+ ID=1 ArbSelect=Fixed TC/VC=22
Status: NegoPending- InProgress-
Capabilities: [130 v1] Root Complex Link
Desc: PortNumber=0f ComponentID=00 EltType=Config
Link0: Desc: TargetPort=00 TargetComponent=00 AssocRCRB-
LinkType=MemMapped LinkValid+
Addr: 00000000fed1c000
Kernel driver in use: snd_hda_intel
00:1c.0 PCI bridge: Intel Corporation 7 Series/C210 Series Chipset
Family PCI Express Root Port 1 (rev c4) (prog-if 00 [Normal decode])
Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR- FastB2B- DisINTx-
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort-
<MAbort- >SERR- <PERR- INTx-
Latency: 0, Cache Line Size: 64 bytes
Bus: primary=00, secondary=02, subordinate=02, sec-latency=0
I/O behind bridge: 0000f000-00000fff
Memory behind bridge: f1500000-f15fffff
Prefetchable memory behind bridge: 00000000fff00000-00000000000fffff
Secondary status: 66MHz- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort-
<MAbort+ <SERR- <PERR-
BridgeCtl: Parity- SERR- NoISA- VGA- MAbort- >Reset- FastB2B-
PriDiscTmr- SecDiscTmr- DiscTmrStat- DiscTmrSERREn-
Capabilities: [40] Express (v2) Root Port (Slot+), MSI 00
DevCap: MaxPayload 128 bytes, PhantFunc 0, Latency L0s <64ns, L1 <1us
ExtTag- RBE+ FLReset-
DevCtl: Report errors: Correctable- Non-Fatal- Fatal- Unsupported-
RlxdOrd- ExtTag- PhantFunc- AuxPwr- NoSnoop-
MaxPayload 128 bytes, MaxReadReq 128 bytes
DevSta: CorrErr- UncorrErr- FatalErr- UnsuppReq- AuxPwr+ TransPend-
LnkCap: Port #1, Speed 5GT/s, Width x1, ASPM L0s L1, Latency L0 <512ns,
L1 <16us
ClockPM- Surprise- LLActRep+ BwNot-
LnkCtl: ASPM L1 Enabled; RCB 64 bytes Disabled- Retrain- CommClk+
ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
LnkSta: Speed 2.5GT/s, Width x1, TrErr- Train- SlotClk+ DLActive+
BWMgmt+ ABWMgmt-
SltCap: AttnBtn- PwrCtrl- MRL- AttnInd- PwrInd- HotPlug- Surprise-
Slot #0, PowerLimit 10.000W; Interlock- NoCompl+
SltCtl: Enable: AttnBtn- PwrFlt- MRL- PresDet- CmdCplt- HPIrq- LinkChg-
Control: AttnInd Unknown, PwrInd Unknown, Power- Interlock-
SltSta: Status: AttnBtn- PowerFlt- MRL- CmdCplt- PresDet+ Interlock-
Changed: MRL- PresDet- LinkState+
RootCtl: ErrCorrectable- ErrNon-Fatal- ErrFatal- PMEIntEna- CRSVisible-
RootCap: CRSVisible-
RootSta: PME ReqID 0000, PMEStatus- PMEPending-
DevCap2: Completion Timeout: Range BC, TimeoutDis+ ARIFwd-
DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis- ARIFwd-
LnkCtl2: Target Link Speed: 5GT/s, EnterCompliance- SpeedDis-,
Selectable De-emphasis: -6dB
Transmit Margin: Normal Operating Range, EnterModifiedCompliance-
ComplianceSOS-
Compliance De-emphasis: -6dB
LnkSta2: Current De-emphasis Level: -3.5dB, EqualizationComplete-,
EqualizationPhase1-
EqualizationPhase2-, EqualizationPhase3-, LinkEqualizationRequest-
Capabilities: [80] MSI: Enable- Count=1/1 Maskable- 64bit-
Address: 00000000 Data: 0000
Capabilities: [90] Subsystem: Dell Device 0578
Capabilities: [a0] Power Management version 2
Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA PME(D0+,D1-,D2-,D3hot+,D3cold+)
Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=0 PME-
Kernel driver in use: pcieport
00:1c.4 PCI bridge: Intel Corporation 7 Series/C210 Series Chipset
Family PCI Express Root Port 5 (rev c4) (prog-if 00 [Normal decode])
Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR- FastB2B- DisINTx-
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort-
<MAbort- >SERR- <PERR- INTx-
Latency: 0, Cache Line Size: 64 bytes
Bus: primary=00, secondary=03, subordinate=03, sec-latency=0
I/O behind bridge: 00002000-00002fff
Memory behind bridge: fff00000-000fffff
Prefetchable memory behind bridge: 00000000f1400000-00000000f14fffff
Secondary status: 66MHz- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort-
<MAbort- <SERR- <PERR-
BridgeCtl: Parity- SERR- NoISA- VGA- MAbort- >Reset- FastB2B-
PriDiscTmr- SecDiscTmr- DiscTmrStat- DiscTmrSERREn-
Capabilities: [40] Express (v2) Root Port (Slot+), MSI 00
DevCap: MaxPayload 128 bytes, PhantFunc 0, Latency L0s <64ns, L1 <1us
ExtTag- RBE+ FLReset-
DevCtl: Report errors: Correctable- Non-Fatal- Fatal- Unsupported-
RlxdOrd- ExtTag- PhantFunc- AuxPwr- NoSnoop-
MaxPayload 128 bytes, MaxReadReq 128 bytes
DevSta: CorrErr- UncorrErr- FatalErr- UnsuppReq- AuxPwr+ TransPend-
LnkCap: Port #5, Speed 5GT/s, Width x1, ASPM L0s L1, Latency L0 <512ns,
L1 <16us
ClockPM- Surprise- LLActRep+ BwNot-
LnkCtl: ASPM L1 Enabled; RCB 64 bytes Disabled- Retrain- CommClk+
ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
LnkSta: Speed 2.5GT/s, Width x1, TrErr- Train- SlotClk+ DLActive+
BWMgmt+ ABWMgmt-
SltCap: AttnBtn- PwrCtrl- MRL- AttnInd- PwrInd- HotPlug- Surprise-
Slot #4, PowerLimit 10.000W; Interlock- NoCompl+
SltCtl: Enable: AttnBtn- PwrFlt- MRL- PresDet- CmdCplt- HPIrq- LinkChg-
Control: AttnInd Unknown, PwrInd Unknown, Power- Interlock-
SltSta: Status: AttnBtn- PowerFlt- MRL- CmdCplt- PresDet+ Interlock-
Changed: MRL- PresDet- LinkState+
RootCtl: ErrCorrectable- ErrNon-Fatal- ErrFatal- PMEIntEna- CRSVisible-
RootCap: CRSVisible-
RootSta: PME ReqID 0000, PMEStatus- PMEPending-
DevCap2: Completion Timeout: Range BC, TimeoutDis+ ARIFwd-
DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis- ARIFwd-
LnkCtl2: Target Link Speed: 5GT/s, EnterCompliance- SpeedDis-,
Selectable De-emphasis: -6dB
Transmit Margin: Normal Operating Range, EnterModifiedCompliance-
ComplianceSOS-
Compliance De-emphasis: -6dB
LnkSta2: Current De-emphasis Level: -3.5dB, EqualizationComplete-,
EqualizationPhase1-
EqualizationPhase2-, EqualizationPhase3-, LinkEqualizationRequest-
Capabilities: [80] MSI: Enable- Count=1/1 Maskable- 64bit-
Address: 00000000 Data: 0000
Capabilities: [90] Subsystem: Dell Device 0578
Capabilities: [a0] Power Management version 2
Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA PME(D0+,D1-,D2-,D3hot+,D3cold+)
Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=0 PME-
Kernel driver in use: pcieport
00:1d.0 USB controller: Intel Corporation 7 Series/C210 Series Chipset
Family USB Enhanced Host Controller #1 (rev 04) (prog-if 20 [EHCI])
Subsystem: Dell Device 0578
Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR- FastB2B- DisINTx-
Status: Cap+ 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort-
<TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0
Interrupt: pin A routed to IRQ 23
Region 0: Memory at f1619000 (32-bit, non-prefetchable) [size=1K]
Capabilities: [50] Power Management version 2
Flags: PMEClk- DSI- D1- D2- AuxCurrent=375mA PME(D0+,D1-,D2-,D3hot+,D3cold+)
Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=0 PME-
Capabilities: [58] Debug port: BAR=1 offset=00a0
Capabilities: [98] PCI Advanced Features
AFCap: TP+ FLR+
AFCtrl: FLR-
AFStatus: TP-
Kernel driver in use: ehci-pci
00:1f.0 ISA bridge: Intel Corporation HM77 Express Chipset LPC
Controller (rev 04)
Subsystem: Dell Device 0578
Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR- FastB2B- DisINTx-
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=medium >TAbort-
<TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0
Capabilities: [e0] Vendor Specific Information: Len=0c <?>
Kernel driver in use: lpc_ich
00:1f.2 SATA controller: Intel Corporation 7 Series Chipset Family
6-port SATA Controller [AHCI mode] (rev 04) (prog-if 01 [AHCI 1.0])
Subsystem: Dell Device 0578
Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR- FastB2B- DisINTx+
Status: Cap+ 66MHz+ UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort-
<TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0
Interrupt: pin B routed to IRQ 43
Region 0: I/O ports at 4098 [size=8]
Region 1: I/O ports at 40bc [size=4]
Region 2: I/O ports at 4090 [size=8]
Region 3: I/O ports at 40b8 [size=4]
Region 4: I/O ports at 4060 [size=32]
Region 5: Memory at f1618000 (32-bit, non-prefetchable) [size=2K]
Capabilities: [80] MSI: Enable+ Count=1/1 Maskable- 64bit-
Address: fee3000c Data: 41b1
Capabilities: [70] Power Management version 3
Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA PME(D0-,D1-,D2-,D3hot+,D3cold-)
Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=0 PME-
Capabilities: [a8] SATA HBA v1.0 BAR4 Offset=00000004
Capabilities: [b0] PCI Advanced Features
AFCap: TP+ FLR+
AFCtrl: FLR-
AFStatus: TP-
Kernel driver in use: ahci
00:1f.3 SMBus: Intel Corporation 7 Series/C210 Series Chipset Family
SMBus Controller (rev 04)
Subsystem: Dell Device 0578
Control: I/O+ Mem+ BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR- FastB2B- DisINTx-
Status: Cap- 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort-
<TAbort- <MAbort- >SERR- <PERR- INTx-
Interrupt: pin C routed to IRQ 10
Region 0: Memory at f1614000 (64-bit, non-prefetchable) [size=256]
Region 4: I/O ports at efa0 [size=32]
01:00.0 VGA compatible controller: NVIDIA Corporation GK107M [GeForce GT
650M] (rev a1) (prog-if 00 [VGA controller])
Subsystem: Dell Device 0578
Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR- FastB2B- DisINTx-
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort-
<MAbort- >SERR- <PERR- INTx-
Latency: 0, Cache Line Size: 64 bytes
Interrupt: pin A routed to IRQ 11
Region 0: Memory at f0000000 (32-bit, non-prefetchable) [size=16M]
Region 1: Memory at c0000000 (64-bit, prefetchable) [size=256M]
Region 3: Memory at d0000000 (64-bit, prefetchable) [size=32M]
Region 5: I/O ports at 3000 [disabled] [size=128]
Expansion ROM at <ignored> [disabled]
Capabilities: [60] Power Management version 3
Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA PME(D0-,D1-,D2-,D3hot-,D3cold-)
Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=0 PME-
Capabilities: [68] MSI: Enable- Count=1/1 Maskable- 64bit+
Address: 0000000000000000 Data: 0000
Capabilities: [78] Express (v2) Endpoint, MSI 00
DevCap: MaxPayload 256 bytes, PhantFunc 0, Latency L0s unlimited, L1 <64us
ExtTag+ AttnBtn- AttnInd- PwrInd- RBE+ FLReset-
DevCtl: Report errors: Correctable- Non-Fatal- Fatal- Unsupported-
RlxdOrd+ ExtTag+ PhantFunc- AuxPwr- NoSnoop+
MaxPayload 256 bytes, MaxReadReq 512 bytes
DevSta: CorrErr- UncorrErr- FatalErr- UnsuppReq- AuxPwr- TransPend-
LnkCap: Port #0, Speed 8GT/s, Width x16, ASPM L0s L1, Latency L0 <512ns,
L1 <4us
ClockPM+ Surprise- LLActRep- BwNot-
LnkCtl: ASPM L0s L1 Enabled; RCB 64 bytes Disabled- Retrain- CommClk+
ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
LnkSta: Speed 8GT/s, Width x16, TrErr- Train- SlotClk+ DLActive- BWMgmt-
ABWMgmt-
DevCap2: Completion Timeout: Range AB, TimeoutDis+
DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis-
LnkCtl2: Target Link Speed: 8GT/s, EnterCompliance- SpeedDis-,
Selectable De-emphasis: -6dB
Transmit Margin: Normal Operating Range, EnterModifiedCompliance-
ComplianceSOS-
Compliance De-emphasis: -6dB
LnkSta2: Current De-emphasis Level: -3.5dB, EqualizationComplete+,
EqualizationPhase1+
EqualizationPhase2+, EqualizationPhase3+, LinkEqualizationRequest+
Capabilities: [b4] Vendor Specific Information: Len=14 <?>
Capabilities: [100 v1] Virtual Channel
Caps: LPEVC=0 RefClk=100ns PATEntryBits=1
Arb: Fixed- WRR32- WRR64- WRR128-
Ctrl: ArbSelect=Fixed
Status: InProgress-
VC0: Caps: PATOffset=00 MaxTimeSlots=1 RejSnoopTrans-
Arb: Fixed- WRR32- WRR64- WRR128- TWRR128- WRR256-
Ctrl: Enable+ ID=0 ArbSelect=Fixed TC/VC=ff
Status: NegoPending- InProgress-
Capabilities: [128 v1] Power Budgeting <?>
Capabilities: [600 v1] Vendor Specific Information: ID=0001 Rev=1
Len=024 <?>
Capabilities: [900 v1] #19
02:00.0 Network controller: Intel Corporation Centrino Wireless-N 2230
(rev c4)
Subsystem: Intel Corporation Centrino Wireless-N 2230 BGN
Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR- FastB2B- DisINTx+
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort-
<MAbort- >SERR- <PERR- INTx-
Latency: 0, Cache Line Size: 64 bytes
Interrupt: pin A routed to IRQ 45
Region 0: Memory at f1500000 (64-bit, non-prefetchable) [size=8K]
Capabilities: [c8] Power Management version 3
Flags: PMEClk- DSI+ D1- D2- AuxCurrent=0mA PME(D0+,D1-,D2-,D3hot+,D3cold+)
Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=0 PME-
Capabilities: [d0] MSI: Enable+ Count=1/1 Maskable- 64bit+
Address: 00000000fee1000c Data: 41d1
Capabilities: [e0] Express (v1) Endpoint, MSI 00
DevCap: MaxPayload 128 bytes, PhantFunc 0, Latency L0s <512ns, L1 unlimited
ExtTag- AttnBtn- AttnInd- PwrInd- RBE+ FLReset+
DevCtl: Report errors: Correctable- Non-Fatal- Fatal- Unsupported-
RlxdOrd+ ExtTag- PhantFunc- AuxPwr- NoSnoop+ FLReset-
MaxPayload 128 bytes, MaxReadReq 128 bytes
DevSta: CorrErr+ UncorrErr- FatalErr- UnsuppReq+ AuxPwr+ TransPend-
LnkCap: Port #0, Speed 2.5GT/s, Width x1, ASPM L0s L1, Latency L0 <4us,
L1 <32us
ClockPM+ Surprise- LLActRep- BwNot-
LnkCtl: ASPM L1 Enabled; RCB 64 bytes Disabled- Retrain- CommClk+
ExtSynch- ClockPM+ AutWidDis- BWInt- AutBWInt-
LnkSta: Speed 2.5GT/s, Width x1, TrErr- Train- SlotClk+ DLActive-
BWMgmt- ABWMgmt-
Capabilities: [100 v1] Advanced Error Reporting
UESta: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP-
ECRC- UnsupReq- ACSViol-
UEMsk: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP-
ECRC- UnsupReq- ACSViol-
UESvrt: DLP+ SDES- TLP- FCP+ CmpltTO- CmpltAbrt- UnxCmplt- RxOF+
MalfTLP+ ECRC- UnsupReq- ACSViol-
CESta: RxErr- BadTLP+ BadDLLP- Rollover- Timeout- NonFatalErr+
CEMsk: RxErr- BadTLP- BadDLLP- Rollover- Timeout- NonFatalErr+
AERCap: First Error Pointer: 00, GenCap- CGenEn- ChkCap- ChkEn-
Capabilities: [140 v1] Device Serial Number 60-36-dd-ff-ff-bc-5e-30
Kernel driver in use: iwlwifi
03:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd.
RTL8101E/RTL8102E PCI Express Fast Ethernet controller (rev 05)
Subsystem: Dell Device 0578
Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR- FastB2B- DisINTx+
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort-
<MAbort- >SERR- <PERR- INTx-
Latency: 0, Cache Line Size: 64 bytes
Interrupt: pin A routed to IRQ 42
Region 0: I/O ports at 2000 [size=256]
Region 2: Memory at f1404000 (64-bit, prefetchable) [size=4K]
Region 4: Memory at f1400000 (64-bit, prefetchable) [size=16K]
Capabilities: [40] Power Management version 3
Flags: PMEClk- DSI- D1+ D2+ AuxCurrent=375mA PME(D0+,D1+,D2+,D3hot+,D3cold+)
Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=0 PME-
Capabilities: [50] MSI: Enable+ Count=1/1 Maskable- 64bit+
Address: 00000000feeff00c Data: 4191
Capabilities: [70] Express (v2) Endpoint, MSI 01
DevCap: MaxPayload 128 bytes, PhantFunc 0, Latency L0s <512ns, L1 <64us
ExtTag- AttnBtn- AttnInd- PwrInd- RBE+ FLReset-
DevCtl: Report errors: Correctable- Non-Fatal- Fatal- Unsupported-
RlxdOrd+ ExtTag- PhantFunc- AuxPwr- NoSnoop-
MaxPayload 128 bytes, MaxReadReq 512 bytes
DevSta: CorrErr+ UncorrErr- FatalErr- UnsuppReq+ AuxPwr+ TransPend-
LnkCap: Port #0, Speed 2.5GT/s, Width x1, ASPM L0s L1, Latency L0
unlimited, L1 <64us
ClockPM+ Surprise- LLActRep- BwNot-
LnkCtl: ASPM L1 Enabled; RCB 64 bytes Disabled- Retrain- CommClk+
ExtSynch- ClockPM+ AutWidDis- BWInt- AutBWInt-
LnkSta: Speed 2.5GT/s, Width x1, TrErr- Train- SlotClk+ DLActive-
BWMgmt- ABWMgmt-
DevCap2: Completion Timeout: Not Supported, TimeoutDis+
DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis-
LnkCtl2: Target Link Speed: 2.5GT/s, EnterCompliance- SpeedDis-,
Selectable De-emphasis: -6dB
Transmit Margin: Normal Operating Range, EnterModifiedCompliance-
ComplianceSOS-
Compliance De-emphasis: -6dB
LnkSta2: Current De-emphasis Level: -6dB, EqualizationComplete-,
EqualizationPhase1-
EqualizationPhase2-, EqualizationPhase3-, LinkEqualizationRequest-
Capabilities: [b0] MSI-X: Enable- Count=4 Masked-
Vector table: BAR=4 offset=00000000
PBA: BAR=4 offset=00000800
Capabilities: [d0] Vital Product Data
Unknown small resource type 00, will not decode more.
Capabilities: [100 v1] Advanced Error Reporting
UESta: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP-
ECRC- UnsupReq- ACSViol-
UEMsk: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP-
ECRC- UnsupReq- ACSViol-
UESvrt: DLP+ SDES+ TLP- FCP+ CmpltTO- CmpltAbrt- UnxCmplt- RxOF+
MalfTLP+ ECRC- UnsupReq- ACSViol-
CESta: RxErr- BadTLP- BadDLLP- Rollover- Timeout- NonFatalErr+
CEMsk: RxErr- BadTLP- BadDLLP- Rollover- Timeout- NonFatalErr+
AERCap: First Error Pointer: 00, GenCap+ CGenEn- ChkCap+ ChkEn-
Capabilities: [140 v1] Virtual Channel
Caps: LPEVC=0 RefClk=100ns PATEntryBits=1
Arb: Fixed- WRR32- WRR64- WRR128-
Ctrl: ArbSelect=Fixed
Status: InProgress-
VC0: Caps: PATOffset=00 MaxTimeSlots=1 RejSnoopTrans-
Arb: Fixed- WRR32- WRR64- WRR128- TWRR128- WRR256-
Ctrl: Enable+ ID=0 ArbSelect=Fixed TC/VC=ff
Status: NegoPending- InProgress-
Capabilities: [160 v1] Device Serial Number 7c-03-00-00-36-4c-e0-00
Kernel driver in use: r8169
# cat /proc/scsi/scsi
Attached devices:
Host: scsi0 Channel: 00 Id: 00 Lun: 00
Vendor: ATA Model: Samsung SSD 840 Rev: DXM0
Type: Direct-Access ANSI SCSI revision: 05
Host: scsi1 Channel: 00 Id: 00 Lun: 00
Vendor: ATA Model: WDC WD10JPVT-75A Rev: 01.0
Type: Direct-Access ANSI SCSI revision: 05
Host: scsi3 Channel: 00 Id: 00 Lun: 00
Vendor: PLDS Model: DVDRWBD DS-6E2SH Rev: CD13
Type: CD-ROM ANSI SCSI revision: 05
Host: scsi6 Channel: 00 Id: 00 Lun: 00
Vendor: Generic- Model: xD/SD/M.S. Rev: 1.00
Type: Direct-Access ANSI SCSI revision: 02
# xinput --list
⎡ Virtual core pointer id=2 [master pointer (3)]
⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)]
⎜ ↳ PS/2 Generic Mouse id=13 [slave pointer (2)]
⎣ Virtual core keyboard id=3 [master keyboard (2)]
↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)]
↳ Power Button id=6 [slave keyboard (3)]
↳ Video Bus id=7 [slave keyboard (3)]
↳ Video Bus id=8 [slave keyboard (3)]
↳ Power Button id=9 [slave keyboard (3)]
↳ Sleep Button id=10 [slave keyboard (3)]
↳ Laptop_Integrated_Webcam_HD id=11 [slave keyboard (3)]
↳ AT Translated Set 2 keyboard id=12 [slave keyboard (3)]
↳ Dell WMI hotkeys id=14 [slave keyboard (3)]
--
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
* [PATCH] Input: gamepad - use independent axes for analog D-Pad buttons
From: Antonio Ospite @ 2013-12-23 16:17 UTC (permalink / raw)
To: linux-input; +Cc: Antonio Ospite, David Herrmann, Dmitry Torokhov, Jiri Kosina
Model this part of the API after the Sony PlayStation 3 Controller which
exposes independent analog values for each one of the D-Pad buttons.
The PS3 programming API psl1ght also maps the analog D-Pad buttons
individually.
Cc: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: Antonio Ospite <ospite@studenti.unina.it>
---
Hi,
as David mentioned no gamepad defines analog D-Pad buttons as mentioned in the
gamepad API, so the API change should be OK.
And is it OK too to fill the holes in the ABS space or should I just use
higher codes?
Thanks,
Antonio
Documentation/input/gamepad.txt | 4 ++--
drivers/staging/et131x/Module.symvers | 0
include/uapi/linux/input.h | 5 +++++
3 files changed, 7 insertions(+), 2 deletions(-)
delete mode 100644 drivers/staging/et131x/Module.symvers
diff --git a/Documentation/input/gamepad.txt b/Documentation/input/gamepad.txt
index ed13782..aab000d 100644
--- a/Documentation/input/gamepad.txt
+++ b/Documentation/input/gamepad.txt
@@ -124,8 +124,8 @@ D-Pad:
Digital buttons are reported as:
BTN_DPAD_*
Analog buttons are reported as:
- ABS_HAT0X and ABS_HAT0Y
- (for ABS values negative is left/up, positive is right/down)
+ ABS_DPAD_*
+ (ABS values start at 0, pressure is reported as positive values)
Analog-Sticks:
The left analog-stick is reported as ABS_X, ABS_Y. The right analog stick is
diff --git a/drivers/staging/et131x/Module.symvers b/drivers/staging/et131x/Module.symvers
deleted file mode 100644
index e69de29..0000000
diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
index d3fcbff..dcc73c0 100644
--- a/include/uapi/linux/input.h
+++ b/include/uapi/linux/input.h
@@ -842,6 +842,11 @@ struct input_keymap_entry {
#define ABS_VOLUME 0x20
+#define ABS_DPAD_UP 0x21 /* Analog D-Pad Up for gamepads */
+#define ABS_DPAD_DOWN 0x22 /* Analog D-Pad Down for gamepads */
+#define ABS_DPAD_LEFT 0x23 /* Analog D-Pad Left for gamepads */
+#define ABS_DPAD_RIGHT 0x24 /* Analog D-Pad Right for gamepads */
+
#define ABS_MISC 0x28
#define ABS_MT_SLOT 0x2f /* MT slot being modified */
--
1.8.5.2
^ permalink raw reply related
* Re: T440s Synaptics clickpad: lost sync / KBC bad data
From: Heinz Wiesinger @ 2013-12-23 8:00 UTC (permalink / raw)
To: J. Domburg; +Cc: linux-input
In-Reply-To: <alpine.DEB.2.00.1312220001560.4559@icf1.fcevgrfzbqf.pbz>
[-- Attachment #1: Type: text/plain, Size: 2075 bytes --]
On Sunday 22 December 2013 00:17:06 you wrote:
> On Sat, 21 Dec 2013, Heinz Wiesinger wrote:
> > J. Domburg <jeroen <at> spritesmods.com> writes:
> > I'm experiencing exactly the same issue. I have my T440s now for a bit
> > more
> > than two weeks. No issues with the touchpad until today when it suddenly
> > started acting up and I now see messages like these in dmesg
>
> <snip>
>
> Hi Heinz, all,
>
> Always good to know mine is not an isolated case, although in this case, I
> wouldn't want to wish the issue on anyone else...
>
> FYI: I haven't resolved this issue yet, but I'm thinking this may be a
> hardware issue after all. I decided to try and see if Win7 has this issue
> too, and got myself an USB-stick to install it from. I didn't even get
> past the installer. It gave me exactly the same issues as Linux has:
> an erratic and finally hanging cursor when using the clickpad.
>
> I can understand Lenovo not wanting to support a Linux-only-issue so up
> till now I haven't bothered them yet with my issue, but I can't see my
> laptop not having mouse support in something as commonly used and
> well-supported by hardware manufacturers as the Win7 installer. This
> smells like an hardware issue to me, so I filed a warranty case today.
>
> If people here don't mind, I'll keep the list updated, if anything so
> other people running into the same issues will have a reference and know
> this isn't necessarily a kernel thing.
>
> Depending on how Lenovo reacts, I'll either have a working laptop or a
> good reason to go soldering and probing the i2c bus. In any case, I should
> end up with a solution I'd think :) although it'd be good if someone more
> versed in the i8042 and related things would look over my shoulder in the
> latter case.
I ended up doing the same and just got their reply. They seem to be pretty
adamant about it being a software issue, with the argument being if it were a
hardware issue the clickpad would either be working fine or not working at all,
but not on and off. Did you hear back from them as well?
Grs,
Heinz
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 230 bytes --]
^ permalink raw reply
* [PATCH] Input: gpio_keys: support wakeup system from freeze mode
From: Anson Huang @ 2013-12-23 18:21 UTC (permalink / raw)
To: dmitry.torokhov, sachin.kamat; +Cc: linux-input
For "freeze" mode of suspend, cpu will go into idle and
those wakeup sources' irq should NOT be disabled during
devices suspend, so we need to add IRQF_NO_SUSPEND flag
for those wakeup sources.
Steps to test this patch:
1. echo freeze > /sys/power/state;
2. press gpio key which has wakeup function, then system
will resume.
Signed-off-by: Anson Huang <b20788@freescale.com>
---
drivers/input/keyboard/gpio_keys.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index 2db1324..aadb1db 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -473,6 +473,8 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
isr = gpio_keys_gpio_isr;
irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+ if (bdata->button->wakeup)
+ irqflags |= IRQF_NO_SUSPEND;
} else {
if (!button->irq) {
--
1.7.9.5
^ permalink raw reply related
* [PATCH] ps3 controller clone
From: Andrew Haines @ 2013-12-22 23:26 UTC (permalink / raw)
To: linux-input
[-- Attachment #1: Type: text/plain, Size: 1200 bytes --]
Hi this is my first kernel patch.
I have a ps3 controller clone that dmesg calls "Gasia Co.,Ltd PS(R)
Gamepad." When I plugged it in it was recognized but did not send any
output. After some searching I found
http://www.spinics.net/lists/linux-input/msg18172.html and changed my
kernel according to the patch on that email which didn't fix the
problem. I installed wireshark and connected the controller to
Windows(VirtualBox) and came up with the solution in the attached patch.
My controller needed the additional
hdev->hid_get_raw_report(hdev, 0xf5, buf, 64, HID_FEATURE_REPORT)
and an interrupt set.
I tried to use the newer hdev->hid_output_raw_report rather than calling
usb_interrupt_msg but I think usbhid->urbout is null in
usbhid_output_raw_report since it gets sent as a SET_REPORT instead of
URB_INTERRUPT out.
I did a little testing and running testrumble just after the controller
is attached does initialize the controller so that the buttons send
output. (Only with the additional hid_get_raw_report(hdev, 0xf5...)
I'm completely new to the kernel and have no idea how to allocate
urbout. I did some searching but I'm not sure where to look.
Regards,
Andrew Haines
[-- Attachment #2: sony-hid-clone.patch --]
[-- Type: text/x-patch, Size: 1858 bytes --]
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 098af2f8..c2bab2b 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -426,16 +426,46 @@ static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf,
*/
static int sixaxis_set_operational_usb(struct hid_device *hdev)
{
+ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+ struct usb_device *dev = interface_to_usbdev(intf);
+ //struct usbhid_device *usbhid = hdev->driver_data;
int ret;
- char *buf = kmalloc(18, GFP_KERNEL);
+ int transfered;
+ char *buf = kmalloc(65, GFP_KERNEL);
+ unsigned char buf2[] = {
+ 0x01,
+ 0x00, 0xff, 0x00, 0xff, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x1e, // led flags 1e = all
+ 0xff, 0x27, 0x10, 0x00, 0x32,
+ 0xff, 0x27, 0x10, 0x00, 0x32,
+ 0xff, 0x27, 0x10, 0x00, 0x32,
+ 0xff, 0x27, 0x10, 0x00, 0x32,
+ 0x00, 0x00, 0x00, 0x00, 0x00
+ };
if (!buf)
return -ENOMEM;
ret = hdev->hid_get_raw_report(hdev, 0xf2, buf, 17, HID_FEATURE_REPORT);
-
+
if (ret < 0)
hid_err(hdev, "can't set operational mode\n");
+ else {
+ /* Some compatible controllers like the Speedlink Strike FX and
+ * Gasia need a write to the Interrupt EP to get operational */
+ hdev->hid_get_raw_report(hdev, 0xf5, buf, 64, HID_FEATURE_REPORT);
+
+ // doesn't work. gets sent as a SET_REPORT Request intstead of a
+ // URB_INTERRUPT out. I guess usbhid->urbout is null
+ //if ( hdev->hid_output_raw_report(hdev, buf2, sizeof(buf2),
+ // HID_OUTPUT_REPORT) < 0 )
+ // hid_err(hdev, "can't set initial interrupt. Cloned controllers may not operate\n");
+
+ if ( usb_interrupt_msg(dev, usb_sndintpipe(dev, 0x02),
+ buf2, sizeof(buf2),
+ &transfered, USB_CTRL_SET_TIMEOUT))
+ hid_err(hdev, "can't set initial interrupt. Cloned controllers may not operate\n");
+ }
kfree(buf);
^ permalink raw reply related
* Re: [PATCH 5/5] mfd: input: iio: ti_amm335x: Rework TSC/ADC synchronization
From: Jonathan Cameron @ 2013-12-22 18:03 UTC (permalink / raw)
To: Sebastian Andrzej Siewior, Lee Jones, Samuel Ortiz
Cc: Dmitry Torokhov, Zubair Lutfullah, Felipe Balbi, linux-iio,
linux-input, linux-kernel
In-Reply-To: <1387466911-3732-6-git-send-email-bigeasy@linutronix.de>
On 12/19/13 15:28, Sebastian Andrzej Siewior wrote:
> The ADC driver always programs all possible ADC values and discards
> them except for the value IIO asked for. On the am335x-evm the driver
> programs four values and it takes 500us to gather them. Reducing the number
> of conversations down to the (required) one also reduces the busy loop down
> to 125us.
>
> This leads to another error, namely the FIFOCOUNT register is sometimes
> (like one out of 10 attempts) not updated in time leading to EBUSY.
> The next read has the FIFOCOUNT register updated.
> Checking for the ADCSTAT register for being idle isn't a good choice either.
> The problem is that if TSC is used at the same time, the HW completes the
> conversation for ADC *and* before the driver noticed it, the HW begins to
> perform a TSC conversation and so the driver never seen the HW idle. The
> next time we would have two values in the FIFO but since the driver reads
> everything we always see the current one.
> So instead of polling for the IDLE bit in ADCStatus register, we should
> check the FIFOCOUNT register. It should be one instead of zero because we
> request one value.
>
> This change in turn leads to another error. Sometimes if TSC & ADC are
> used together the TSC starts becoming interrupts even if nobody
becoming -> generating?
> actually touched the touchscreen. The interrupts seem valid because TSC's
> FIFO is filled with values for each channel of the TSC. This condition stops
> after a few ADC reads but will occur again. Not good.
>
> On top of this (even without the changes I just mentioned) there is a ADC
> & TSC lockup condition which was reported to me by Jeff Lance including the
> following test case:
> A busy loop of "cat /sys/bus/iio/devices/iio\:device0/in_voltage4_raw"
> and a mug on touch screen. With this setup, the hardware will lockup after
> something between 20 minutes and it could take up to a couple of hours.
> During that lockup, the ADCSTAT register says 0x30 (or 0x70) which means
> STEP_ID = IDLE and FSM_BUSY = yes. That means the hardware says that it is
> idle and busy at the same time which is an invalid condition.
>
yikes.
> For all this reasons I decided to rework this TSC/ADC part and add a
> handshake / synchronization here:
> First the ADC signals that it needs the HW and writes a 0 mask into the
> SE register. The HW (if active) will complete the current conversation
> and become idle. The TSC driver will gather the values from the FIFO
> (woken up by an interrupt) and won't "enable" another conversation.
> Instead it will wake up the ADC driver which is already waiting. The ADC
> driver will start "its" conversation and once it is done, it will
> enable the TSC steps so the TSC will work again.
>
> After this rework I haven't observed the lockup so far. Plus the busy
> loop has been reduced from 500us to 125us.
>
> The continues-read mode remains unchanged.
>
Thanks for the detailed explanation.
> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Hmm. I'm not really an expert in this complex driver, but your explanation is clear
and the code appears to implement what you describe. Hopefully we'll see a fix for
the continuous reads as well.
Fiddly little device!
Acked-by: Jonathan Cameron <jic23@kernel.org>
> ---
> drivers/iio/adc/ti_am335x_adc.c | 64 ++++++++++++++++++++++++++----------
> drivers/mfd/ti_am335x_tscadc.c | 63 +++++++++++++++++++++++++++++------
> include/linux/mfd/ti_am335x_tscadc.h | 4 +++
> 3 files changed, 103 insertions(+), 28 deletions(-)
>
> diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
> index 6822b9f..31e786e 100644
> --- a/drivers/iio/adc/ti_am335x_adc.c
> +++ b/drivers/iio/adc/ti_am335x_adc.c
> @@ -60,6 +60,24 @@ static u32 get_adc_step_mask(struct tiadc_device *adc_dev)
> return step_en;
> }
>
> +static u32 get_adc_chan_step_mask(struct tiadc_device *adc_dev,
> + struct iio_chan_spec const *chan)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(adc_dev->channel_step); i++) {
> + if (chan->channel == adc_dev->channel_line[i]) {
> + u32 step;
> +
> + step = adc_dev->channel_step[i];
> + /* +1 for the charger */
> + return 1 << (step + 1);
> + }
> + }
> + WARN_ON(1);
> + return 0;
> +}
> +
> static u32 get_adc_step_bit(struct tiadc_device *adc_dev, int chan)
> {
> return 1 << adc_dev->channel_step[chan];
> @@ -329,34 +347,43 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,
> unsigned int fifo1count, read, stepid;
> bool found = false;
> u32 step_en;
> - unsigned long timeout = jiffies + usecs_to_jiffies
> - (IDLE_TIMEOUT * adc_dev->channels);
> + unsigned long timeout;
>
> if (iio_buffer_enabled(indio_dev))
> return -EBUSY;
>
> - step_en = get_adc_step_mask(adc_dev);
> + step_en = get_adc_chan_step_mask(adc_dev, chan);
> + if (!step_en)
> + return -EINVAL;
> +
> + fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
> + while (fifo1count--)
> + tiadc_readl(adc_dev, REG_FIFO1);
> +
> am335x_tsc_se_set_once(adc_dev->mfd_tscadc, step_en);
>
> - /* Wait for ADC sequencer to complete sampling */
> - while (tiadc_readl(adc_dev, REG_ADCFSM) & SEQ_STATUS) {
> - if (time_after(jiffies, timeout))
> + timeout = jiffies + usecs_to_jiffies
> + (IDLE_TIMEOUT * adc_dev->channels);
> + /* Wait for Fifo threshold interrupt */
> + while (1) {
> + fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
> + if (fifo1count)
> + break;
> +
> + if (time_after(jiffies, timeout)) {
> + am335x_tsc_se_adc_done(adc_dev->mfd_tscadc);
> return -EAGAIN;
> + }
> }
> map_val = chan->channel + TOTAL_CHANNELS;
>
> /*
> - * When the sub-system is first enabled,
> - * the sequencer will always start with the
> - * lowest step (1) and continue until step (16).
> - * For ex: If we have enabled 4 ADC channels and
> - * currently use only 1 out of them, the
> - * sequencer still configures all the 4 steps,
> - * leading to 3 unwanted data.
> - * Hence we need to flush out this data.
> + * We check the complete FIFO. We programmed just one entry but in case
> + * something went wrong we left empty handed (-EAGAIN previously) and
> + * then the value apeared somehow in the FIFO we would have two entries.
> + * Therefore we read every item and keep only the latest version of the
> + * requested channel.
> */
> -
> - fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
> for (i = 0; i < fifo1count; i++) {
> read = tiadc_readl(adc_dev, REG_FIFO1);
> stepid = read & FIFOREAD_CHNLID_MASK;
> @@ -368,6 +395,7 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,
> *val = (u16) read;
> }
> }
> + am335x_tsc_se_adc_done(adc_dev->mfd_tscadc);
>
> if (found == false)
> return -EBUSY;
> @@ -495,8 +523,8 @@ static int tiadc_resume(struct device *dev)
> tiadc_writel(adc_dev, REG_CTRL, restore);
>
> tiadc_step_config(indio_dev);
> - am335x_tsc_se_set(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
> -
> + am335x_tsc_se_set_cache(adc_dev->mfd_tscadc,
> + adc_dev->buffer_en_ch_steps);
> return 0;
> }
>
> diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c
> index 157f569..d4e8604 100644
> --- a/drivers/mfd/ti_am335x_tscadc.c
> +++ b/drivers/mfd/ti_am335x_tscadc.c
> @@ -24,6 +24,7 @@
> #include <linux/pm_runtime.h>
> #include <linux/of.h>
> #include <linux/of_device.h>
> +#include <linux/sched.h>
>
> #include <linux/mfd/ti_am335x_tscadc.h>
>
> @@ -48,31 +49,71 @@ static const struct regmap_config tscadc_regmap_config = {
> .val_bits = 32,
> };
>
> -static void am335x_tsc_se_update(struct ti_tscadc_dev *tsadc)
> -{
> - tscadc_writel(tsadc, REG_SE, tsadc->reg_se_cache);
> -}
> -
> void am335x_tsc_se_set_cache(struct ti_tscadc_dev *tsadc, u32 val)
> {
> unsigned long flags;
>
> spin_lock_irqsave(&tsadc->reg_lock, flags);
> - tsadc->reg_se_cache |= val;
> - am335x_tsc_se_update(tsadc);
> + tsadc->reg_se_cache = val;
> + if (tsadc->adc_waiting)
> + wake_up(&tsadc->reg_se_wait);
> + else if (!tsadc->adc_in_use)
> + tscadc_writel(tsadc, REG_SE, val);
> +
> spin_unlock_irqrestore(&tsadc->reg_lock, flags);
> }
> EXPORT_SYMBOL_GPL(am335x_tsc_se_set_cache);
>
> +static void am335x_tscadc_need_adc(struct ti_tscadc_dev *tsadc)
> +{
> + DEFINE_WAIT(wait);
> + u32 reg;
> +
> + /*
> + * disable TSC steps so it does not run while the ADC is using it. If
> + * write 0 while it is running (it just started or was already running)
> + * then it completes all steps that were enabled and stops then.
> + */
> + tscadc_writel(tsadc, REG_SE, 0);
> + reg = tscadc_readl(tsadc, REG_ADCFSM);
> + if (reg & SEQ_STATUS) {
> + tsadc->adc_waiting = true;
> + prepare_to_wait(&tsadc->reg_se_wait, &wait,
> + TASK_UNINTERRUPTIBLE);
> + spin_unlock_irq(&tsadc->reg_lock);
> +
> + schedule();
> +
> + spin_lock_irq(&tsadc->reg_lock);
> + finish_wait(&tsadc->reg_se_wait, &wait);
> +
> + reg = tscadc_readl(tsadc, REG_ADCFSM);
> + WARN_ON(reg & SEQ_STATUS);
> + tsadc->adc_waiting = false;
> + }
> + tsadc->adc_in_use = true;
> +}
> +
> void am335x_tsc_se_set_once(struct ti_tscadc_dev *tsadc, u32 val)
> {
> + spin_lock_irq(&tsadc->reg_lock);
> + am335x_tscadc_need_adc(tsadc);
> +
> + tscadc_writel(tsadc, REG_SE, val);
> + spin_unlock_irq(&tsadc->reg_lock);
> +}
> +EXPORT_SYMBOL_GPL(am335x_tsc_se_set_once);
> +
> +void am335x_tsc_se_adc_done(struct ti_tscadc_dev *tsadc)
> +{
> unsigned long flags;
>
> spin_lock_irqsave(&tsadc->reg_lock, flags);
> - tscadc_writel(tsadc, REG_SE, tsadc->reg_se_cache | val);
> + tsadc->adc_in_use = false;
> + tscadc_writel(tsadc, REG_SE, tsadc->reg_se_cache);
> spin_unlock_irqrestore(&tsadc->reg_lock, flags);
> }
> -EXPORT_SYMBOL_GPL(am335x_tsc_se_set_once);
> +EXPORT_SYMBOL_GPL(am335x_tsc_se_adc_done);
>
> void am335x_tsc_se_clr(struct ti_tscadc_dev *tsadc, u32 val)
> {
> @@ -80,7 +121,7 @@ void am335x_tsc_se_clr(struct ti_tscadc_dev *tsadc, u32 val)
>
> spin_lock_irqsave(&tsadc->reg_lock, flags);
> tsadc->reg_se_cache &= ~val;
> - am335x_tsc_se_update(tsadc);
> + tscadc_writel(tsadc, REG_SE, tsadc->reg_se_cache);
> spin_unlock_irqrestore(&tsadc->reg_lock, flags);
> }
> EXPORT_SYMBOL_GPL(am335x_tsc_se_clr);
> @@ -188,6 +229,8 @@ static int ti_tscadc_probe(struct platform_device *pdev)
> }
>
> spin_lock_init(&tscadc->reg_lock);
> + init_waitqueue_head(&tscadc->reg_se_wait);
> +
> pm_runtime_enable(&pdev->dev);
> pm_runtime_get_sync(&pdev->dev);
>
> diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
> index 2fa9c06..fb96c84 100644
> --- a/include/linux/mfd/ti_am335x_tscadc.h
> +++ b/include/linux/mfd/ti_am335x_tscadc.h
> @@ -159,6 +159,9 @@ struct ti_tscadc_dev {
> int adc_cell; /* -1 if not used */
> struct mfd_cell cells[TSCADC_CELLS];
> u32 reg_se_cache;
> + bool adc_waiting;
> + bool adc_in_use;
> + wait_queue_head_t reg_se_wait;
> spinlock_t reg_lock;
> unsigned int clk_div;
>
> @@ -179,5 +182,6 @@ static inline struct ti_tscadc_dev *ti_tscadc_dev_get(struct platform_device *p)
> void am335x_tsc_se_set_cache(struct ti_tscadc_dev *tsadc, u32 val);
> void am335x_tsc_se_set_once(struct ti_tscadc_dev *tsadc, u32 val);
> void am335x_tsc_se_clr(struct ti_tscadc_dev *tsadc, u32 val);
> +void am335x_tsc_se_adc_done(struct ti_tscadc_dev *tsadc);
>
> #endif
>
^ permalink raw reply
* Re: [PATCH 4/5] mfd: ti_am335x: Drop am335x_tsc_se_update() from resume path
From: Jonathan Cameron @ 2013-12-22 17:48 UTC (permalink / raw)
To: Sebastian Andrzej Siewior, Lee Jones, Samuel Ortiz
Cc: Dmitry Torokhov, Zubair Lutfullah, Felipe Balbi, linux-iio,
linux-input, linux-kernel
In-Reply-To: <1387466911-3732-5-git-send-email-bigeasy@linutronix.de>
On 12/19/13 15:28, Sebastian Andrzej Siewior wrote:
> The update of the SE register in MFD doesn't look right as it has
> nothing to do with it. The better place to do it is in TSC driver (which
> is already doing it) and in the ADC driver which needs this only in the
> continues mode.
>
> Acked-by: Lee Jones <lee.jones@linaro.org> [MFD part]
> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Acked-by: Jonathan Cameron <jic23@kernel.org>
> ---
> drivers/iio/adc/ti_am335x_adc.c | 2 ++
> drivers/mfd/ti_am335x_tscadc.c | 1 -
> 2 files changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
> index b5197a0..6822b9f 100644
> --- a/drivers/iio/adc/ti_am335x_adc.c
> +++ b/drivers/iio/adc/ti_am335x_adc.c
> @@ -199,6 +199,7 @@ static int tiadc_buffer_predisable(struct iio_dev *indio_dev)
> tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
> IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW));
> am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
> + adc_dev->buffer_en_ch_steps = 0;
>
> /* Flush FIFO of leftover data in the time it takes to disable adc */
> fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
> @@ -494,6 +495,7 @@ static int tiadc_resume(struct device *dev)
> tiadc_writel(adc_dev, REG_CTRL, restore);
>
> tiadc_step_config(indio_dev);
> + am335x_tsc_se_set(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
>
> return 0;
> }
> diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c
> index cb0c211..157f569 100644
> --- a/drivers/mfd/ti_am335x_tscadc.c
> +++ b/drivers/mfd/ti_am335x_tscadc.c
> @@ -309,7 +309,6 @@ static int tscadc_resume(struct device *dev)
>
> if (tscadc_dev->tsc_cell != -1)
> tscadc_idle_config(tscadc_dev);
> - am335x_tsc_se_update(tscadc_dev);
> restore = tscadc_readl(tscadc_dev, REG_CTRL);
> tscadc_writel(tscadc_dev, REG_CTRL,
> (restore | CNTRLREG_TSCSSENB));
>
^ permalink raw reply
* Re: [PATCH 3/5] mfd: ti_am335x_tscadc: Don't read back REG_SE
From: Jonathan Cameron @ 2013-12-22 17:46 UTC (permalink / raw)
To: Sebastian Andrzej Siewior, Lee Jones, Samuel Ortiz
Cc: Dmitry Torokhov, Zubair Lutfullah, Felipe Balbi,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-input-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1387466911-3732-4-git-send-email-bigeasy-hfZtesqFncYOwBW4kG4KsQ@public.gmane.org>
On 12/19/13 15:28, Sebastian Andrzej Siewior wrote:
> The purpose of reg_se_cache has been defeated. It should avoid the
> read-back of the register to avoid the latency and the fact that the
> bits are reset to 0 after the individual conversation took place.
>
> The reason why this is required like this to work, is that read-back of
> the register removes the bits of the ADC so they do not start another
> conversation after the register is re-written from the TSC side for the
> update.
> To avoid the not required read-back I introduce a "set once" variant which
> does not update the cache mask. After the conversation completes, the
> bit is removed from the SE register anyway and we don't plan a new
> conversation "any time soon". The current set function is renamed to
> set_cache to distinguish the two operations.
> This is a small preparation for a larger sync-rework.
>
> Acked-by: Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> Signed-off-by: Sebastian Andrzej Siewior <bigeasy-hfZtesqFncYOwBW4kG4KsQ@public.gmane.org>
Acked-by: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> ---
> drivers/iio/adc/ti_am335x_adc.c | 4 ++--
> drivers/input/touchscreen/ti_am335x_tsc.c | 4 ++--
> drivers/mfd/ti_am335x_tscadc.c | 16 ++++++++++++----
> include/linux/mfd/ti_am335x_tscadc.h | 3 ++-
> 4 files changed, 18 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
> index e948454..b5197a0 100644
> --- a/drivers/iio/adc/ti_am335x_adc.c
> +++ b/drivers/iio/adc/ti_am335x_adc.c
> @@ -181,7 +181,7 @@ static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
> enb |= (get_adc_step_bit(adc_dev, bit) << 1);
> adc_dev->buffer_en_ch_steps = enb;
>
> - am335x_tsc_se_set(adc_dev->mfd_tscadc, enb);
> + am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, enb);
>
> tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES
> | IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW);
> @@ -335,7 +335,7 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,
> return -EBUSY;
>
> step_en = get_adc_step_mask(adc_dev);
> - am335x_tsc_se_set(adc_dev->mfd_tscadc, step_en);
> + am335x_tsc_se_set_once(adc_dev->mfd_tscadc, step_en);
>
> /* Wait for ADC sequencer to complete sampling */
> while (tiadc_readl(adc_dev, REG_ADCFSM) & SEQ_STATUS) {
> diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
> index 68beada..2ca5a7b 100644
> --- a/drivers/input/touchscreen/ti_am335x_tsc.c
> +++ b/drivers/input/touchscreen/ti_am335x_tsc.c
> @@ -198,7 +198,7 @@ static void titsc_step_config(struct titsc *ts_dev)
> /* The steps1 … end and bit 0 for TS_Charge */
> stepenable = (1 << (end_step + 2)) - 1;
> ts_dev->step_mask = stepenable;
> - am335x_tsc_se_set(ts_dev->mfd_tscadc, ts_dev->step_mask);
> + am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask);
> }
>
> static void titsc_read_coordinates(struct titsc *ts_dev,
> @@ -322,7 +322,7 @@ static irqreturn_t titsc_irq(int irq, void *dev)
>
> if (irqclr) {
> titsc_writel(ts_dev, REG_IRQSTATUS, irqclr);
> - am335x_tsc_se_set(ts_dev->mfd_tscadc, ts_dev->step_mask);
> + am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask);
> return IRQ_HANDLED;
> }
> return IRQ_NONE;
> diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c
> index 67d0eb4..cb0c211 100644
> --- a/drivers/mfd/ti_am335x_tscadc.c
> +++ b/drivers/mfd/ti_am335x_tscadc.c
> @@ -53,24 +53,32 @@ static void am335x_tsc_se_update(struct ti_tscadc_dev *tsadc)
> tscadc_writel(tsadc, REG_SE, tsadc->reg_se_cache);
> }
>
> -void am335x_tsc_se_set(struct ti_tscadc_dev *tsadc, u32 val)
> +void am335x_tsc_se_set_cache(struct ti_tscadc_dev *tsadc, u32 val)
> {
> unsigned long flags;
>
> spin_lock_irqsave(&tsadc->reg_lock, flags);
> - tsadc->reg_se_cache = tscadc_readl(tsadc, REG_SE);
> tsadc->reg_se_cache |= val;
> am335x_tsc_se_update(tsadc);
> spin_unlock_irqrestore(&tsadc->reg_lock, flags);
> }
> -EXPORT_SYMBOL_GPL(am335x_tsc_se_set);
> +EXPORT_SYMBOL_GPL(am335x_tsc_se_set_cache);
> +
> +void am335x_tsc_se_set_once(struct ti_tscadc_dev *tsadc, u32 val)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&tsadc->reg_lock, flags);
> + tscadc_writel(tsadc, REG_SE, tsadc->reg_se_cache | val);
> + spin_unlock_irqrestore(&tsadc->reg_lock, flags);
> +}
> +EXPORT_SYMBOL_GPL(am335x_tsc_se_set_once);
>
> void am335x_tsc_se_clr(struct ti_tscadc_dev *tsadc, u32 val)
> {
> unsigned long flags;
>
> spin_lock_irqsave(&tsadc->reg_lock, flags);
> - tsadc->reg_se_cache = tscadc_readl(tsadc, REG_SE);
> tsadc->reg_se_cache &= ~val;
> am335x_tsc_se_update(tsadc);
> spin_unlock_irqrestore(&tsadc->reg_lock, flags);
> diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
> index 1fe7219..2fa9c06 100644
> --- a/include/linux/mfd/ti_am335x_tscadc.h
> +++ b/include/linux/mfd/ti_am335x_tscadc.h
> @@ -176,7 +176,8 @@ static inline struct ti_tscadc_dev *ti_tscadc_dev_get(struct platform_device *p)
> return *tscadc_dev;
> }
>
> -void am335x_tsc_se_set(struct ti_tscadc_dev *tsadc, u32 val);
> +void am335x_tsc_se_set_cache(struct ti_tscadc_dev *tsadc, u32 val);
> +void am335x_tsc_se_set_once(struct ti_tscadc_dev *tsadc, u32 val);
> void am335x_tsc_se_clr(struct ti_tscadc_dev *tsadc, u32 val);
>
> #endif
>
^ permalink raw reply
* Re: [PATCH 1/5] iio: ti_am335x_adc: Adjust the closing bracket in tiadc_read_raw()
From: Jonathan Cameron @ 2013-12-22 16:55 UTC (permalink / raw)
To: Sebastian Andrzej Siewior, Lee Jones, Samuel Ortiz
Cc: Dmitry Torokhov, Zubair Lutfullah, Felipe Balbi, linux-iio,
linux-input, linux-kernel
In-Reply-To: <1387466911-3732-2-git-send-email-bigeasy@linutronix.de>
On 12/19/13 15:28, Sebastian Andrzej Siewior wrote:
> It somehow looks like the ending bracket belongs to the if statement but
> it does belong to the while loop. This patch moves the bracket where it
> belongs.
>
> Reviewed-by: Lee Jones <lee.jones@linaro.org>
> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Acked-by: Jonathan Cameron <jic23@kernel.org>
Lee, I'm assuming you are fine taking the whole series!
> ---
> drivers/iio/adc/ti_am335x_adc.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
> index d4d7482..e948454 100644
> --- a/drivers/iio/adc/ti_am335x_adc.c
> +++ b/drivers/iio/adc/ti_am335x_adc.c
> @@ -341,7 +341,7 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,
> while (tiadc_readl(adc_dev, REG_ADCFSM) & SEQ_STATUS) {
> if (time_after(jiffies, timeout))
> return -EAGAIN;
> - }
> + }
> map_val = chan->channel + TOTAL_CHANNELS;
>
> /*
>
^ permalink raw reply
* Re: T440s Synaptics clickpad: lost sync / KBC bad data
From: J. Domburg @ 2013-12-21 23:17 UTC (permalink / raw)
To: Heinz Wiesinger; +Cc: linux-input
In-Reply-To: <loom.20131221T224832-537@post.gmane.org>
On Sat, 21 Dec 2013, Heinz Wiesinger wrote:
> J. Domburg <jeroen <at> spritesmods.com> writes:
> I'm experiencing exactly the same issue. I have my T440s now for a bit more
> than two weeks. No issues with the touchpad until today when it suddenly
> started acting up and I now see messages like these in dmesg
<snip>
Hi Heinz, all,
Always good to know mine is not an isolated case, although in this case, I
wouldn't want to wish the issue on anyone else...
FYI: I haven't resolved this issue yet, but I'm thinking this may be a
hardware issue after all. I decided to try and see if Win7 has this issue
too, and got myself an USB-stick to install it from. I didn't even get
past the installer. It gave me exactly the same issues as Linux has:
an erratic and finally hanging cursor when using the clickpad.
I can understand Lenovo not wanting to support a Linux-only-issue so up
till now I haven't bothered them yet with my issue, but I can't see my
laptop not having mouse support in something as commonly used and
well-supported by hardware manufacturers as the Win7 installer. This
smells like an hardware issue to me, so I filed a warranty case today.
If people here don't mind, I'll keep the list updated, if anything so
other people running into the same issues will have a reference and know
this isn't necessarily a kernel thing.
Depending on how Lenovo reacts, I'll either have a working laptop or a
good reason to go soldering and probing the i2c bus. In any case, I should
end up with a solution I'd think :) although it'd be good if someone more
versed in the i8042 and related things would look over my shoulder in the
latter case.
Cheers,
Jeroen
^ permalink raw reply
* Re: [RFC v2] ff-memless-next driver
From: Joe Perches @ 2013-12-21 23:05 UTC (permalink / raw)
To: Michal Malý
Cc: dmitry.torokhov, linux-input, linux-kernel, anssi.hannula,
elias.vds
In-Reply-To: <2287724.uNI0tVeDgp@geidi-prime>
Refactoring can help reduce the line lengths.
For example:
Move the get_envelope function.
Use get_envelope in set_envelope_times.
---
drivers/input/ff-memless-next.c | 58 +++++++++++++++++++----------------------
1 file changed, 27 insertions(+), 31 deletions(-)
diff --git a/drivers/input/ff-memless-next.c b/drivers/input/ff-memless-next.c
index 722601a..73e1127 100644
--- a/drivers/input/ff-memless-next.c
+++ b/drivers/input/ff-memless-next.c
@@ -104,34 +104,46 @@ static int mlnx_upload_conditional(struct mlnx_device *mlnxdev,
return mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &ecmd);
}
+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 void mlnx_set_envelope_times(struct mlnx_effect *mlnxeff)
{
const struct ff_effect *effect = &mlnxeff->effect;
+ const struct ff_envelope *env;
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_CONSTANT:
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;
+ return;
}
+
+ env = mlnx_get_envelope(effect);
+ if (env->attack_length)
+ mlnxeff->attack_stop = mlnxeff->begin_at +
+ msecs_to_jiffies(env->attack_length);
+ if (effect->replay.length && env->fade_length)
+ mlnxeff->fade_begin = mlnxeff->stop_at -
+ msecs_to_jiffies(env->fade_length);
}
static void mlnx_set_trip_times(struct mlnx_effect *mlnxeff,
@@ -185,22 +197,6 @@ static void mlnx_stop_effect(struct mlnx_device *mlnxdev,
}
}
-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)
{
^ permalink raw reply related
* [RFC v2] ff-memless-next driver
From: Michal Malý @ 2013-12-21 22:10 UTC (permalink / raw)
To: dmitry.torokhov; +Cc: linux-input, linux-kernel, anssi.hannula, elias.vds
Revised patch with "ff-memless-next" driver. Changes to the previous version
include:
- Fixes for a few bugs and corner cases
- More robust checking whether ff effect parameters are sane
- (Hopefully) fully in accordance to MS DirectInput specs
- Reworked triangle wave calculation
- Added documentation file
I tried to cut the lines to keep the 80 chars limit, but in some cases it was
not possible without compromising readability...
Tested-by: Elias Vanderstuyft <elias.vds@gmail.com>
Signed-off-by: Michal Malý <madcatxster@prifuk.cz>
---
From 15a760ddca461448c95c2845008adc74bade2977 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michal=20Mal=C3=BD?= <madcatxster@prifuk.cz>
Date: Sat, 21 Dec 2013 22:45:59 +0100
Subject: [PATCH] Add ff-memless-next
---
Documentation/input/ff-memless-next.txt | 143 +++++++
drivers/input/Kconfig | 12 +
drivers/input/Makefile | 2 +
drivers/input/ff-memless-next.c | 733 ++++++++++++++++++++++++++++++++
include/linux/input/ff-memless-next.h | 29 ++
5 files changed, 919 insertions(+)
create mode 100644 Documentation/input/ff-memless-next.txt
create mode 100644 drivers/input/ff-memless-next.c
create mode 100644 include/linux/input/ff-memless-next.h
diff --git a/Documentation/input/ff-memless-next.txt b/Documentation/input/ff-memless-next.txt
new file mode 100644
index 0000000..972bbb5
--- /dev/null
+++ b/Documentation/input/ff-memless-next.txt
@@ -0,0 +1,143 @@
+"ff-memless-next" force feedback module for memoryless devices.
+By Michal Malý <madcatxster@prifuk.cz> on 2013/12/21
+----------------------------------------------------------------------------
+
+1. Introduction
+~~~~~~~~~~~~~~~
+This document describes basic working principles of the "ff-memless-next"
+and its purpose. This document also contains summary of "ff-memless-next" API
+and brief instructions on how to use it to write a hardware-specific backend
+with "ff-memless-next". Some specifics of ff-memless-next that might be of
+interest for userspace developers are also discussed.
+
+2. Basic principles of ff-memless-next
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+A lot of commonly used force feedback devices do not have a memory of their
+own. This means that it is not possible to upload a force feedback effect
+to such a device and let the device's CPU handle the playback. Instead,
+the device relies solely on its driver to tell it what force to generate.
+"ff-memless-next" was written to serve in this capacity. Its purpose is to
+calculate the overall force the device should apply and pass the result to
+a hardware-specific backend which then submits the appropriate command to
+the device.
+
+"ff-memless-next" differentiates between two types of force feedback effects,
+"combinable" and "uncombinable".
+"Combinable" effects are effects that generate a force of a given
+magnitude and direction. The magnitude can change in time according to the
+envelope of the effect and its waveform; the latter applies only to periodic
+effects. Force generated by "combinable" effect does not depend on the position
+of the device. Forces generated by each "combinable" effect that is active
+are periodically recalculated as needed and superposed into one overall force.
+"Combinable" effects are FF_CONSTANT, FF_PERIODIC and FF_RAMP.
+
+"Uncombinable" effects generate a force that depends on the position of
+the device. "ff-memless-next" assumes that the device takes care of processing
+such an effect. However, a device might have a limit on how many "uncombinable"
+effects can be active at once and this limit might be lower than the maximum
+effect count set in "ff-memless-next". "ff-memless-next" allows a
+hardware-specific driver to check whether the device is able to play
+an "uncombinable" effect. Error is reported back to userspace if the device
+cannot play the effect. The hardware-specific driver may choose not to
+perform this check in which case the userspace will have no way of knowing
+whether an "uncombinable" is really being played or not.
+
+3. API provided by "ff-memless-next"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"ff-memless-next" provides an API for developers of hardware-specific
+drivers. In order to use the API, the hardware-specific driver should
+include <linux/input/ff-memless-next.h>
+Functions and structs defined by this API are:
+
+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)
+- Any hardware-specific driver that wants to use "ff-memless-next" must call
+this function. The function takes care of creating and registering a force
+feedback device within the kernel. It also initializes resources needed by
+"ff-memless-next" to handle a new device. No other initialization steps are necessary.
+ Parameters:
+ * dev - pointer to valid struct input_dev
+ * data - pointer to custom data the hardware-specific backend
+ might need to pass to the control_effect() callback function
+ (discussed later). * data will be freed automatically by
+ "ff-memless-next" upon device's destruction.
+ * control_effect - A callback function. "ff-memless-next" will call
+ this function when it is done processing effects.
+ Implementation of control_effect() in the
+ hardware-specific driver should create an appropriate
+ command and submit it to the device.
+ This function is called with dev->event_lock
+ spinlock held.
+ update_rate - Rate in milliseconds at which envelopes and periodic
+ effects are recalculated. Minimum value is 5 msecs.
+
+struct mlnx_effect_command
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+- This struct is passed to the hardware-specific backend in
+the control_effect() function. See "ff-memless-next.h" for details.
+
+enum mlnx_commands
+^^^^^^^^^^^^^^^^^^
+- Types of commands generated by ff-memless-next
+ MLNX_START_COMBINED - Start or update "combinable" effect
+ MLNX_STOP_COMBINED - Stop "combinable" effect. In most cases this means
+ to set the force to zero.
+ MLNX_UPLOAD_UNCOMB - Check if the device can accept and play
+ "uncombinable" effect.
+ Hardware-specific driver should return 0
+ on success.
+ MLNX_START_UNCOMB - Start playback of "uncombinable" effect.
+ MLNX_STOP_UNCOMB - Stop playback of "uncombinable" effect.
+
+struct mlnx_simple_force
+^^^^^^^^^^^^^^^^^^^^^^^^
+ x - overall force along X axis
+ y - overall force along Y axis
+
+struct mlnx_uncomb_effect
+^^^^^^^^^^^^^^^^^^^^^^^^^
+- Information about "uncombinable" effect.
+ id - internal ID of the effect
+ * effect - pointer to the internal copy of the effect kept by
+ "ff-memless-next". This pointer will remain valid until
+ the effect is removed.
+
+Sample implementation of a dummy driver that uses "ff-memless-next" is
+available at "git://prifuk.cz/ff-dummy-device". Link to the source will
+be kept up to date.
+
+Direction of the effect's force translates to Cartesian coordinates system
+as follows:
+ Direction = 0: Down
+ Direction (0; 16384): 3rd quadrant
+ Direction = 16384: Left
+ Direction (16385; 32768): 2nd quadrant
+ Direction = 32768: Up
+ Direction (32769; 49152): 1st quadrant
+ Direction = 49152: Right
+ Direction (49153; 65535) :4th quadrant
+ Direction = 65565: Down
+
+4. Specifics of "ff-memless-next" for userspace developers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+None of the persons involved in development or testing of "ff-memless-next"
+is aware of any reference force feedback specifications. "ff-memless-next"
+tries to adhere to Microsoft's DirectInput specifications because we
+believe that is what most developers have experience with.
+
+- Waveforms of periodic effects do not start at the origin, but as follows:
+ SAW_UP: At minimum
+ SAW_DOWN: At maximum
+ SQUARE: At maximum
+ TRIANGLE: At maximum
+ SINE: At origin
+
+- Updating periodic effects:
+ - All periodic effects are restarted when their parameters are updated.
+
+- Updating effects of finite duration:
+ - Once an effect of finite length finishes playing, it is considered
+ stopped. Only effects that are playing can be updated on the fly.
+ Therefore effects of finite duration can by updated only until
+ they finish playing.
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..269a22d 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -16,6 +16,8 @@ obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o
obj-$(CONFIG_INPUT_JOYDEV) += joydev.o
obj-$(CONFIG_INPUT_EVDEV) += evdev.o
obj-$(CONFIG_INPUT_EVBUG) += evbug.o
+obj-$(CONFIG_INPUT_MATRIXKMAP) += matrix-keymap.o
+obj-$(CONFIG_INPUT_FF_MEMLESS_NEXT) += ff-memless-next.o
obj-$(CONFIG_INPUT_KEYBOARD) += keyboard/
obj-$(CONFIG_INPUT_MOUSE) += mouse/
diff --git a/drivers/input/ff-memless-next.c b/drivers/input/ff-memless-next.c
new file mode 100644
index 0000000..722601a
--- /dev/null
+++ b/drivers/input/ff-memless-next.c
@@ -0,0 +1,733 @@
+/*
+ * Force feedback support for memoryless devices
+ *
+ * This module is based on "ff-memless" orignally written by Anssi Hannula.
+ * It is extended to support all force feedback effects currently supported
+ * by the Linux input stack.
+ *
+ * Copyright(c) 2013 Michal Maly <madcatxster@prifuk.cz>
+ *
+ */
+
+#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_EFFECT_LENGTH ((1000 / CONFIG_HZ) + 1)
+#define FF_EFFECT_STARTED 1
+#define FF_EFFECT_PLAYING 2
+
+
+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 bool mlnx_is_combinable(const struct ff_effect *effect)
+{
+ switch (effect->type) {
+ case FF_CONSTANT:
+ case FF_PERIODIC:
+ case FF_RAMP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static inline bool mlnx_is_conditional(const struct ff_effect *effect)
+{
+ switch (effect->type) {
+ case FF_DAMPER:
+ case FF_FRICTION:
+ case FF_INERTIA:
+ case FF_SPRING:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* Some devices might have a limit on how many uncombinable effects
+ * can be played at once */
+static int mlnx_upload_conditional(struct mlnx_device *mlnxdev,
+ const struct ff_effect *effect)
+{
+ struct mlnx_effect_command ecmd = { .cmd = MLNX_UPLOAD_UNCOMB,
+ .u.uncomb.id = effect->id,
+ .u.uncomb.effect = effect };
+ return mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &ecmd);
+}
+
+static void mlnx_set_envelope_times(struct mlnx_effect *mlnxeff)
+{
+ const 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)
+{
+ const struct ff_effect *effect = &mlnxeff->effect;
+ const u16 replay_delay = effect->replay.delay;
+ const u16 replay_length = effect->replay.length;
+
+ mlnxeff->begin_at = now + msecs_to_jiffies(replay_delay);
+ mlnxeff->stop_at = mlnxeff->begin_at + msecs_to_jiffies(replay_length);
+ mlnxeff->updated_at = mlnxeff->begin_at;
+ if (effect->type == FF_PERIODIC)
+ mlnxeff->playback_time = effect->u.periodic.phase;
+}
+
+static void mlnx_start_effect(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,
+ const struct mlnx_effect *mlnxeff)
+{
+ switch (mlnxeff->effect.type) {
+ case FF_CONSTANT:
+ case FF_PERIODIC:
+ case FF_RAMP:
+ 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 = mlnxeff->effect.id,
+ .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;
+ 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;
+ 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);
+ const s16 offset = effect->u.periodic.offset;
+ s32 new = level;
+ u16 t;
+
+ mlnxeff->playback_time += dt;
+ t = mlnxeff->playback_time % period;
+
+ switch (effect->u.periodic.waveform) {
+ case FF_SINE:
+ {
+ u16 degrees = (360 * t) / period;
+ new = ((level * fixp_sin(degrees)) >> FRAC_N) + offset;
+ break;
+ }
+ case FF_SQUARE:
+ {
+ u16 degrees = (360 * t) / period;
+ new = level * (degrees < 180 ? 1 : -1) + offset;
+ break;
+ }
+ case FF_SAW_UP:
+ new = 2 * level * t / period - level + offset;
+ break;
+ case FF_SAW_DOWN:
+ new = level - 2 * level * t / period + offset;
+ break;
+ case FF_TRIANGLE:
+ {
+ new = (2 * abs(level - (2 * level * t) / period)) - level + offset;
+ break;
+ }
+ case FF_CUSTOM:
+ pr_debug("Custom waveform is not handled by this driver.\n");
+ return level;
+ default:
+ pr_err("Invalid waveform.\n");
+ return level;
+ }
+
+ mlnxeff->playback_time = t;
+ /* Ensure that the offset did not make the value exceed s16 range */
+ new = clamp(new, -0x7fff, 0x7fff);
+ 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;
+ const s16 mean = (effect->u.ramp.start_level + effect->u.ramp.end_level) / 2;
+ const u16 t = jiffies_to_msecs(now - mlnxeff->begin_at);
+ s32 start = effect->u.ramp.start_level;
+ s32 end = effect->u.ramp.end_level;
+ s32 new;
+
+ if (envelope->attack_length && time_before(now, mlnxeff->attack_stop)) {
+ const s32 into_msecs = jiffies_to_msecs(now - mlnxeff->begin_at);
+ const s32 alength = envelope->attack_length;
+ s32 attack_level;
+ if (end > start)
+ attack_level = mean - envelope->attack_level;
+ else
+ attack_level = mean + envelope->attack_level;
+ start = (start - attack_level) * into_msecs / alength + attack_level;
+ } else if (envelope->fade_length && time_before_eq(mlnxeff->fade_begin, now)) {
+ const s32 into_msecs = jiffies_to_msecs(now - mlnxeff->fade_begin);
+ const s32 flength = envelope->fade_length;
+ s32 fade_level;
+ if (end > start)
+ fade_level = mean + envelope->fade_level;
+ else
+ fade_level = mean - envelope->fade_level;
+ end = (fade_level - end) * into_msecs / flength + end;
+ }
+
+ new = ((end - start) * t) / length + start;
+ new = clamp(new, -0x7fff, 0x7fff);
+ 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)) {
+ 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 {
+ 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_err("Effect %d not handled by mlnx_add_force, this is 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);
+
+ __clear_bit(FF_EFFECT_PLAYING, &mlnxeff->flags);
+ mlnx_stop_effect(mlnxdev, mlnxeff);
+ /* If the effect should be repeated, reset it */
+ if (--mlnxeff->repeat > 0)
+ mlnx_start_effect(mlnxeff);
+ else
+ __clear_bit(FF_EFFECT_STARTED, &mlnxeff->flags);
+
+ 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 = clamp(cfx, -0x7fff, 0x7fff),
+ .y = clamp(cfy, -0x7fff, 0x7fff) } };
+ 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;
+ if (!test_bit(FF_EFFECT_STARTED, &mlnxeff->flags))
+ mlnx_start_effect(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)) {
+ __clear_bit(FF_EFFECT_PLAYING, &mlnxdev->effects[effect_id].flags);
+ mlnx_stop_effect(mlnxdev, mlnxeff);
+ }
+ __clear_bit(FF_EFFECT_STARTED, &mlnxdev->effects[effect_id].flags);
+ } else {
+ pr_debug("Effect ID %d already stopped.\n", effect_id);
+ return 0;
+ }
+ }
+ 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;
+ const u16 length = effect->replay.length;
+ const u16 delay = effect->replay.delay;
+ int ret, fade_from;
+
+ /* Effect's timing is below kernel timer resolution */
+ if (length && length < FF_MIN_EFFECT_LENGTH)
+ effect->replay.length = FF_MIN_EFFECT_LENGTH;
+ if (delay && delay < FF_MIN_EFFECT_LENGTH)
+ effect->replay.delay = FF_MIN_EFFECT_LENGTH;
+
+ /* Periodic effects must have a non-zero period */
+ if (effect->type == FF_PERIODIC) {
+ if (!effect->u.periodic.period)
+ return -EINVAL;
+ }
+ /* Ramp effects cannot be infinite */
+ if (effect->type == FF_RAMP && !length)
+ return -EINVAL;
+
+ if (mlnx_is_combinable(effect)) {
+ const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+
+ /* Infinite effects cannot fade */
+ if (!length && envelope->fade_length > 0)
+ return -EINVAL;
+ /* Fade length cannot be greater than effect duration */
+ fade_from = (int)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 && fade_from > 0)
+ return -EINVAL;
+ }
+
+
+ spin_lock_irq(&dev->event_lock);
+ mlnxeff->effect = *effect; /* Keep internal copy of the effect */
+ /* Check if the effect being modified is playing */
+ if (test_bit(FF_EFFECT_STARTED, &mlnxeff->flags)) {
+ /* Set the effect to stopped state */
+ if (test_bit(FF_EFFECT_PLAYING, &mlnxeff->flags)) {
+ __clear_bit(FF_EFFECT_PLAYING, &mlnxeff->flags);
+ if (mlnx_is_combinable(effect)) {
+ if (effect->replay.delay)
+ mlnx_stop_effect(mlnxdev, mlnxeff);
+ else
+ mlnxdev->combinable_playing--;
+ } else if (mlnx_is_conditional(effect)) {
+ if (effect->replay.delay)
+ mlnx_stop_effect(mlnxdev, mlnxeff);
+ ret = mlnx_upload_conditional(mlnxdev,
+ &mlnxeff->effect);
+ if (ret) {
+ /* Restore the original effect */
+ if (old)
+ mlnxeff->effect = *old;
+ spin_unlock_irq(&dev->event_lock);
+ return ret;
+ }
+ }
+ }
+ mlnx_set_trip_times(mlnxeff, now);
+ mlnx_set_envelope_times(mlnxeff);
+ mlnx_schedule_playback(mlnxdev);
+
+ spin_unlock_irq(&dev->event_lock);
+ return 0;
+ }
+
+ if (mlnx_is_conditional(effect)) {
+ ret = mlnx_upload_conditional(mlnxdev, &mlnxeff->effect);
+ if (ret) {
+ /* Restore the original effect */
+ if (old)
+ mlnxeff->effect = *old;
+ 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;
+
+ if (update_rate < FF_MIN_EFFECT_LENGTH)
+ 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;
+
+ 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..86f83bd3
--- /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_UPLOAD_UNCOMB
+};
+
+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.2
--
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: T440s Synaptics clickpad: lost sync / KBC bad data
From: Heinz Wiesinger @ 2013-12-21 21:59 UTC (permalink / raw)
To: linux-input
In-Reply-To: <alpine.DEB.2.00.1312151507560.6449@icf1.fcevgrfzbqf.pbz>
J. Domburg <jeroen <at> spritesmods.com> writes:
>
> Hello everyone,
>
> I hope I'm at the right address here with my problems. If not, please do
> point me in the correct direction.
>
> I have a Lenovo Thinkpad T440s with a Synaptics clickpad and an IBM
> trackpoint in it:
>
> input: PS/2 Synaptics TouchPad as
/devices/platform/i8042/serio1/input/input120
> psmouse serio1: synaptics: Touchpad model: 1, fw: 8.1, id: 0x1e2b1, caps:
0xd001a3/0x940300/0x127c00,
> board id: 2668, fw id: 1293989
> psmouse serio1: synaptics: serio: Synaptics pass-through port at
isa0060/serio1/input0
> input: SynPS/2 Synaptics TouchPad as
/devices/platform/i8042/serio1/input/input123
> psmouse serio34: alps: Unknown ALPS touchpad: E7=10 00 64, EC=10 00 64
> psmouse serio34: trackpoint: IBM TrackPoint firmware: 0x0e,
> buttons: 3/3 input: TPPS/2 IBM TrackPoint as
/devices/platform/i8042/serio1/serio34/input/input124
>
> The problem is that the trackpoint behaves incredibly erratic, sometimes
> working correctly for minutes at a time, sometimes just jumping around and
> many times just hanging. I need to remove and re-insert the psmouse module
> to solve that. Alongside this behaviour, my kernel log also is getting
> spammed with messages like:
>
> psmouse serio1: TouchPad at isa0060/serio1/input0 lost sync at byte 1
> psmouse serio1: TouchPad at isa0060/serio1/input0 lost sync at byte 1
> psmouse serio1: TouchPad at isa0060/serio1/input0 lost sync at byte 1
> psmouse serio1: TouchPad at isa0060/serio1/input0 - driver resynced.
> and
> psmouse serio1: bad data from KBC - timeout
>
> What I've tried:
> - Tried the kernel options i8042.nomux=1 and i8042.reset=1
> - Disable acpi by passing acpi=off to the kernel
> - Disabled CPU frequency scaling
> - Trying other protocols, e.g. by doing modprobe psmouse proto=imps or
> proto=bare
> - Switching from vanilla 3.12 kernel to both the latest&greatest kernel
> from Linus' Git and an antique 3.1.0 kernel
> - Turning on i8042.debug. To my untrained eye, it looks like bytes of PS/2
> data are just disappearing when the 'lost sync' messages kick in. I can
> give an example of that if needed.
>
> Strangely, the symptoms have been getting worse in the 10 days I've had
> this machine: from the first few days with no trouble to today with the
> mouse crapping out almost once every ten seconds or so. I would say it's a
> DOA and claim my warranty, if not frustratingly the machine works
> perfectly fine in Windows 8, with no touchpad trouble at all.
>
> I'm willing to try things on this machine, run patches or take any hint in
> what I can change to get this working; if it helps I can even poke an
> oscilloscope at the PS2 lines. Not having a working touchpad or
> touchpoint is getting a bit frustrating...
I'm experiencing exactly the same issue. I have my T440s now for a bit more
than two weeks. No issues with the touchpad until today when it suddenly
started acting up and I now see messages like these in dmesg:
[ 2244.631822] psmouse serio1: synaptics: Touchpad model: 1, fw: 8.1, id:
0x1e2b1, caps: 0xd001a3/0x940300/0x127c00, board id: 2668, fw id: 1293989
[ 2244.631830] psmouse serio1: synaptics: serio: Synaptics pass-through port
at isa0060/serio1/input0
[ 2244.668037] input: SynPS/2 Synaptics TouchPad as
/devices/platform/i8042/serio1/input/input34
[ 2247.518110] psmouse serio8: alps: Unknown ALPS touchpad: E7=10 00 64,
EC=10 00 64
[ 2248.807888] psmouse serio8: trackpoint: IBM TrackPoint firmware: 0x0e,
buttons: 3/3
[ 2249.016659] input: TPPS/2 IBM TrackPoint as
/devices/platform/i8042/serio1/serio8/input/input35
[ 2254.649940] psmouse serio1: TouchPad at isa0060/serio1/input0 lost sync
at byte 1
[ 2254.651179] psmouse serio1: TouchPad at isa0060/serio1/input0 lost sync
at byte 1
[ 2254.652358] psmouse serio1: TouchPad at isa0060/serio1/input0 lost sync
at byte 1
[ 2254.653594] psmouse serio1: TouchPad at isa0060/serio1/input0 lost sync
at byte 1
[ 2254.663943] psmouse serio1: TouchPad at isa0060/serio1/input0 - driver
resynced.
[ 2254.997536] psmouse serio1: TouchPad at isa0060/serio1/input0 lost sync
at byte 4
[ 2254.998663] psmouse serio1: TouchPad at isa0060/serio1/input0 lost sync
at byte 1
[ 2254.999790] psmouse serio1: TouchPad at isa0060/serio1/input0 lost sync
at byte 1
[ 2255.000973] psmouse serio1: TouchPad at isa0060/serio1/input0 lost sync
at byte 1
[ 2255.002107] psmouse serio1: TouchPad at isa0060/serio1/input0 lost sync
at byte 1
[ 2255.002111] psmouse serio1: issuing reconnect request
[ 2255.131535] psmouse serio1: synaptics: Unable to query device.
[ 2255.598324] psmouse serio1: synaptics: device claims to have extended
capability 0x0c, but I'm not able to read it.
[ 2255.643293] psmouse serio1: synaptics: Unable to initialize device.
[ 2255.767364] input: PS/2 Synaptics TouchPad as
/devices/platform/i8042/serio1/input/input37
[ 2255.953314] psmouse serio1: synaptics: Unable to query device.
Same as Jeroen, I'm willing to try anything that might help resolve this
situation.
Grs,
Heinz
^ permalink raw reply
* Re: [patch 3/3] HID: multitouch: add support of other generic collections in hid-mt
From: Henrik Rydberg @ 2013-12-21 20:26 UTC (permalink / raw)
To: Benjamin Tissoires, Benjamin Tissoires, Jiri Kosina, Edel Maks,
linux-input, linux-kernel
In-Reply-To: <1387470191-9725-4-git-send-email-benjamin.tissoires@redhat.com>
Hi Benjamin,
> The ANTEC Touch Pad is a device which can switch from a multitouch
> touchpad to a mouse. It thus presents several generic collections which
> are currently ignored by hid-multitouch. Enable them by using the generic
> protocol. Adding also a suffix for them depending on their application.
In what way does this and the preceeding patches differ from "else if (is_pen ||
export_all_inputs)"? Adding a functional pattern which then is converted to a
generic case, such that the usual branching is bound to occur anyways, seems
unnecessary.
Thanks,
Henrik
^ permalink raw reply
* Re: [PATCH 7/9] Input: pixcir_i2c_ts: Implement Type B Multi Touch reporting
From: Henrik Rydberg @ 2013-12-21 20:02 UTC (permalink / raw)
To: Roger Quadros, Dmitry Torokhov
Cc: jcbian, linux-input, linux-kernel, devicetree
In-Reply-To: <52B288ED.80904@ti.com>
Hi Roger,
> It seems the controller the original driver was written for does not report
> touch ID and just reports 2 touch co-ordinates. I'm not sure how this fits into
> Type B reporting model.
Look in drivers/input/mouse/cypress_ps2.c for an example of how to deal with
anonymous touch coordinates.
Thanks,
Henrik
^ permalink raw reply
* [PATCH] ims-pcu: Add commands supported by the new version of the FW
From: Andrey Smirnov @ 2013-12-21 19:16 UTC (permalink / raw)
To: linux-input; +Cc: andrew.smirnov, dmitry.torokhov, linux-kernel
New version of the PCU firmware supports two new commands:
- IMS_PCU_CMD_OFN_SET_CONFIG which allows to write data to the
registers of one finger navigation(OFN) chip present on the device
- IMS_PCU_CMD_OFN_GET_CONFIG which allows to read data form the
registers of said chip.
This commit adds two helper functions to use those commands and sysfs
attributes to use them. It also exposes some OFN configuration
parameters via sysfs.
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
drivers/input/misc/ims-pcu.c | 274 +++++++++++++++++++++++++++++++++++++++++--
1 file changed, 263 insertions(+), 11 deletions(-)
diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
index e204f26..050c960 100644
--- a/drivers/input/misc/ims-pcu.c
+++ b/drivers/input/misc/ims-pcu.c
@@ -68,6 +68,9 @@ struct ims_pcu {
char bl_version[IMS_PCU_BL_VERSION_LEN];
char reset_reason[IMS_PCU_BL_RESET_REASON_LEN];
int update_firmware_status;
+ u8 device_id;
+
+ u8 ofn_reg_addr;
struct usb_interface *ctrl_intf;
@@ -371,6 +374,8 @@ static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu)
#define IMS_PCU_CMD_GET_DEVICE_ID 0xae
#define IMS_PCU_CMD_SPECIAL_INFO 0xb0
#define IMS_PCU_CMD_BOOTLOADER 0xb1 /* Pass data to bootloader */
+#define IMS_PCU_CMD_OFN_SET_CONFIG 0xb3
+#define IMS_PCU_CMD_OFN_GET_CONFIG 0xb4
/* PCU responses */
#define IMS_PCU_RSP_STATUS 0xc0
@@ -389,6 +394,9 @@ static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu)
#define IMS_PCU_RSP_GET_DEVICE_ID 0xce
#define IMS_PCU_RSP_SPECIAL_INFO 0xd0
#define IMS_PCU_RSP_BOOTLOADER 0xd1 /* Bootloader response */
+#define IMS_PCU_RSP_OFN_SET_CONFIG 0xd2
+#define IMS_PCU_RSP_OFN_GET_CONFIG 0xd3
+
#define IMS_PCU_RSP_EVNT_BUTTONS 0xe0 /* Unsolicited, button state */
#define IMS_PCU_GAMEPAD_MASK 0x0001ff80UL /* Bits 7 through 16 */
@@ -1216,6 +1224,226 @@ ims_pcu_update_firmware_status_show(struct device *dev,
static DEVICE_ATTR(update_firmware_status, S_IRUGO,
ims_pcu_update_firmware_status_show, NULL);
+enum pcu_ofn_offsets {
+ OFN_REG_RESULT_LSB_OFFSET = 2,
+ OFN_REG_RESULT_MSB_OFFSET = 3,
+};
+
+static ssize_t ims_pcu_ofn_reg_data_show(struct device *dev,
+ struct device_attribute *dattr,
+ char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct ims_pcu *pcu = usb_get_intfdata(intf);
+ int error;
+
+ mutex_lock(&pcu->cmd_mutex);
+
+ error = ims_pcu_execute_command(pcu, OFN_GET_CONFIG,
+ &pcu->ofn_reg_addr,
+ sizeof(pcu->ofn_reg_addr));
+ if (error >= 0) {
+ const s16 result = pcu->cmd_buf[OFN_REG_RESULT_MSB_OFFSET] << 8 |
+ pcu->cmd_buf[OFN_REG_RESULT_LSB_OFFSET];
+ if (result < 0)
+ error = result;
+ else
+ error = scnprintf(buf, PAGE_SIZE, "%x\n", (u8)result);
+ }
+
+ mutex_unlock(&pcu->cmd_mutex);
+
+ return error;
+}
+
+static ssize_t ims_pcu_ofn_reg_data_store(struct device *dev,
+ struct device_attribute *dattr,
+ const char *buf, size_t count)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct ims_pcu *pcu = usb_get_intfdata(intf);
+ int error;
+ int value;
+ u8 buffer[2];
+
+ error = kstrtoint(buf, 0, &value);
+ if (error)
+ return error;
+
+ buffer[0] = pcu->ofn_reg_addr;
+ buffer[1] = (u8) value;
+
+ mutex_lock(&pcu->cmd_mutex);
+
+ error = ims_pcu_execute_command(pcu, OFN_SET_CONFIG,
+ &buffer, sizeof(buffer));
+
+ mutex_unlock(&pcu->cmd_mutex);
+
+ if (!error) {
+ const s16 result = pcu->cmd_buf[OFN_REG_RESULT_MSB_OFFSET] << 8 |
+ pcu->cmd_buf[OFN_REG_RESULT_LSB_OFFSET];
+ error = result;
+ }
+
+ return error ?: count;
+}
+
+static DEVICE_ATTR(ofn_reg_data, S_IRUGO | S_IWUSR,
+ ims_pcu_ofn_reg_data_show, ims_pcu_ofn_reg_data_store);
+
+static ssize_t ims_pcu_ofn_reg_addr_show(struct device *dev,
+ struct device_attribute *dattr,
+ char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct ims_pcu *pcu = usb_get_intfdata(intf);
+ int error;
+
+ mutex_lock(&pcu->cmd_mutex);
+ error = scnprintf(buf, PAGE_SIZE, "%x\n", pcu->ofn_reg_addr);
+ mutex_unlock(&pcu->cmd_mutex);
+
+ return error;
+}
+
+static ssize_t ims_pcu_ofn_reg_addr_store(struct device *dev,
+ struct device_attribute *dattr,
+ const char *buf, size_t count)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct ims_pcu *pcu = usb_get_intfdata(intf);
+ int error;
+ int value;
+
+ error = kstrtoint(buf, 0, &value);
+ if (error)
+ return error;
+
+ mutex_lock(&pcu->cmd_mutex);
+ pcu->ofn_reg_addr = (u8) value;
+ mutex_unlock(&pcu->cmd_mutex);
+
+ return error ?: count;
+}
+
+static DEVICE_ATTR(ofn_reg_addr, S_IRUGO | S_IWUSR,
+ ims_pcu_ofn_reg_addr_show, ims_pcu_ofn_reg_addr_store);
+
+static ssize_t ims_pcu_ofn_bit_show(u8 addr, u8 nr,
+ struct device *dev,
+ struct device_attribute *dattr,
+ char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct ims_pcu *pcu = usb_get_intfdata(intf);
+ int error;
+
+ mutex_lock(&pcu->cmd_mutex);
+
+ error = ims_pcu_execute_command(pcu, OFN_GET_CONFIG,
+ &addr, sizeof(addr));
+ if (error >= 0) {
+ const s16 result = pcu->cmd_buf[OFN_REG_RESULT_MSB_OFFSET] << 8 |
+ pcu->cmd_buf[OFN_REG_RESULT_LSB_OFFSET];
+ if (result < 0)
+ error = result;
+ else
+ error = scnprintf(buf, PAGE_SIZE, "%d\n",
+ !!(result & (1 << nr)));
+ }
+
+ mutex_unlock(&pcu->cmd_mutex);
+ return error;
+}
+
+static ssize_t ims_pcu_ofn_bit_store(u8 addr, u8 nr,
+ struct device *dev,
+ struct device_attribute *dattr,
+ const char *buf, size_t count)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct ims_pcu *pcu = usb_get_intfdata(intf);
+ int error;
+ int value;
+ u8 contents;
+
+
+ error = kstrtoint(buf, 0, &value);
+ if (error)
+ return error;
+
+ if (value > 1)
+ return -EINVAL;
+
+ mutex_lock(&pcu->cmd_mutex);
+
+ error = ims_pcu_execute_command(pcu, OFN_GET_CONFIG,
+ &addr, sizeof(addr));
+ if (error < 0)
+ goto exit;
+
+ {
+ const s16 result = pcu->cmd_buf[OFN_REG_RESULT_MSB_OFFSET] << 8 |
+ pcu->cmd_buf[OFN_REG_RESULT_LSB_OFFSET];
+ if (result < 0) {
+ error = result;
+ goto exit;
+ }
+ contents = (u8) result;
+ }
+
+ if (value)
+ contents |= 1 << nr;
+ else
+ contents &= ~(1 << nr);
+
+ {
+ const u8 buffer[] = { addr, contents };
+ error = ims_pcu_execute_command(pcu, OFN_SET_CONFIG,
+ &buffer, sizeof(buffer));
+ }
+
+exit:
+ mutex_unlock(&pcu->cmd_mutex);
+
+ if (!error) {
+ const s16 result = pcu->cmd_buf[OFN_REG_RESULT_MSB_OFFSET] << 8 |
+ pcu->cmd_buf[OFN_REG_RESULT_LSB_OFFSET];
+ error = result;
+ }
+
+ return error ?: count;
+}
+
+
+#define IMS_PCU_BIT_ATTR(name, addr, nr) \
+ static ssize_t ims_pcu_##name##_show(struct device *dev, \
+ struct device_attribute *dattr, \
+ char *buf) \
+ { \
+ return ims_pcu_ofn_bit_show(addr, nr, dev, dattr, buf); \
+ } \
+ \
+ static ssize_t ims_pcu_##name##_store(struct device *dev, \
+ struct device_attribute *dattr, \
+ const char *buf, size_t count) \
+ { \
+ return ims_pcu_ofn_bit_store(addr, nr, dev, dattr, buf, count); \
+ } \
+ \
+ static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, \
+ ims_pcu_##name##_show, ims_pcu_##name##_store)
+
+IMS_PCU_BIT_ATTR(ofn_engine_enable, 0x60, 7);
+IMS_PCU_BIT_ATTR(ofn_speed_enable, 0x60, 6);
+IMS_PCU_BIT_ATTR(ofn_assert_enable, 0x60, 5);
+IMS_PCU_BIT_ATTR(ofn_xyquant_enable, 0x60, 4);
+IMS_PCU_BIT_ATTR(ofn_xyscale_enable, 0x60, 1);
+
+IMS_PCU_BIT_ATTR(ofn_scale_x2, 0x63, 6);
+IMS_PCU_BIT_ATTR(ofn_scale_y2, 0x63, 7);
+
static struct attribute *ims_pcu_attrs[] = {
&ims_pcu_attr_part_number.dattr.attr,
&ims_pcu_attr_serial_number.dattr.attr,
@@ -1226,6 +1454,18 @@ static struct attribute *ims_pcu_attrs[] = {
&dev_attr_reset_device.attr,
&dev_attr_update_firmware.attr,
&dev_attr_update_firmware_status.attr,
+
+#define IMS_PCU_ATTRS_OFN_START_OFFSET (9)
+
+ &dev_attr_ofn_reg_data.attr,
+ &dev_attr_ofn_reg_addr.attr,
+ &dev_attr_ofn_engine_enable.attr,
+ &dev_attr_ofn_speed_enable.attr,
+ &dev_attr_ofn_assert_enable.attr,
+ &dev_attr_ofn_xyquant_enable.attr,
+ &dev_attr_ofn_xyscale_enable.attr,
+ &dev_attr_ofn_scale_x2.attr,
+ &dev_attr_ofn_scale_y2.attr,
NULL
};
@@ -1244,8 +1484,21 @@ static umode_t ims_pcu_is_attr_visible(struct kobject *kobj,
mode = 0;
}
} else {
- if (attr == &dev_attr_update_firmware_status.attr)
+ if (attr == &dev_attr_update_firmware_status.attr) {
mode = 0;
+ } else if (pcu->device_id == 5) {
+ /*
+ PCU-B devices, both GEN_1 and GEN_2(device_id == 5),
+ have no OFN sensor so exposing those attributes
+ for them does not make any sense
+ */
+ int i;
+ for (i = IMS_PCU_ATTRS_OFN_START_OFFSET; ims_pcu_attrs[i]; i++)
+ if (attr == ims_pcu_attrs[i]) {
+ mode = 0;
+ break;
+ }
+ }
}
return mode;
@@ -1624,7 +1877,6 @@ static int ims_pcu_init_application_mode(struct ims_pcu *pcu)
static atomic_t device_no = ATOMIC_INIT(0);
const struct ims_pcu_device_info *info;
- u8 device_id;
int error;
error = ims_pcu_get_device_info(pcu);
@@ -1633,7 +1885,7 @@ static int ims_pcu_init_application_mode(struct ims_pcu *pcu)
return error;
}
- error = ims_pcu_identify_type(pcu, &device_id);
+ error = ims_pcu_identify_type(pcu, &pcu->device_id);
if (error) {
dev_err(pcu->dev,
"Failed to identify device, error: %d\n", error);
@@ -1645,9 +1897,9 @@ static int ims_pcu_init_application_mode(struct ims_pcu *pcu)
return 0;
}
- if (device_id >= ARRAY_SIZE(ims_pcu_device_info) ||
- !ims_pcu_device_info[device_id].keymap) {
- dev_err(pcu->dev, "Device ID %d is not valid\n", device_id);
+ if (pcu->device_id >= ARRAY_SIZE(ims_pcu_device_info) ||
+ !ims_pcu_device_info[pcu->device_id].keymap) {
+ dev_err(pcu->dev, "Device ID %d is not valid\n", pcu->device_id);
/* Same as above, punt to userspace */
return 0;
}
@@ -1659,7 +1911,7 @@ static int ims_pcu_init_application_mode(struct ims_pcu *pcu)
if (error)
return error;
- info = &ims_pcu_device_info[device_id];
+ info = &ims_pcu_device_info[pcu->device_id];
error = ims_pcu_setup_buttons(pcu, info->keymap, info->keymap_len);
if (error)
goto err_destroy_backlight;
@@ -1783,14 +2035,14 @@ static int ims_pcu_probe(struct usb_interface *intf,
if (error)
goto err_stop_io;
- error = sysfs_create_group(&intf->dev.kobj, &ims_pcu_attr_group);
- if (error)
- goto err_stop_io;
-
error = pcu->bootloader_mode ?
ims_pcu_init_bootloader_mode(pcu) :
ims_pcu_init_application_mode(pcu);
if (error)
+ goto err_stop_io;
+
+ error = sysfs_create_group(&intf->dev.kobj, &ims_pcu_attr_group);
+ if (error)
goto err_remove_sysfs;
return 0;
--
1.8.3.2
^ permalink raw reply related
* Re: [PATCH] HID: input: fix input sysfs path for hid devices
From: Jiri Kosina @ 2013-12-20 22:36 UTC (permalink / raw)
To: David Herrmann
Cc: Benjamin Tissoires, Benjamin Tissoires, open list:HID CORE LAYER,
linux-kernel
In-Reply-To: <CANq1E4QrPAJ7zNdN51PGQD5tkD_gTYfRoHOg7=zwVvnu-xOR0g@mail.gmail.com>
On Thu, 19 Dec 2013, David Herrmann wrote:
> <benjamin.tissoires@redhat.com> wrote:
> > we used to set the parent of the input device as the parent of
> > the hid bus. This was introduced when we created hid as a real bus, and
> > to keep backward compatibility. Now, it's time to proper set the parent
> > so that sysfs has an idea of which input device is attached to
> > which hid device.
> >
> > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> > ---
> >
> > Hi Jiri,
> >
> > well, the regression test did not showed anything bad, and a look at the hid
> > drivers also showed that no drivers should be armed by that.
> >
> > I think this is valuable because it will give a better understanding of the
> > actual mapping hardware/inputs. Like a touchscreen with pen + touch will have
> > the same hid parent, and I expect X/Wayland to be able to detect this at some
> > point to keep the mapping correctly between the two input devices and the screen.
> >
> > I also need that for hid-replay, so that I can be sure which input is attached
> > to which uhid device, and give up the heuristics I currently use.
>
> I was just wondering where we have multiple HID devices on a single
> parent, but yeah, uhid is a good example. I actually have no
> objections to this patch and it looks fine. But I cannot tell whether
> anyone relies on this.
>
> I'd say we should give it a try.
Agreed, let's take it for 3.14, and if anyone complains about breakage
caused by this (which I don't expect), we'll revert to the previous state.
--
Jiri Kosina
SUSE Labs
^ permalink raw reply
* Re: [PATCH] Bluetooth: hidp: make sure input buffers are big enough
From: Marcel Holtmann @ 2013-12-20 13:36 UTC (permalink / raw)
To: David Herrmann
Cc: linux-input, Jiri Kosina,
linux-bluetooth@vger.kernel.org development, Gustavo F. Padovan
In-Reply-To: <1387451372-6881-1-git-send-email-dh.herrmann@gmail.com>
Hi David,
> HID core expects the input buffers to be at least of size 4096
> (HID_MAX_BUFFER_SIZE). Other sizes will result in buffer-overflows if an
> input-report is smaller than advertised. We could, like i2c, compute the
> biggest report-size instead of using HID_MAX_BUFFER_SIZE, but this will
> blow up if report-descriptors are changed after ->start() has been called.
> So lets be safe and just use the biggest buffer we have.
>
> Note that this adds an additional copy to the HIDP input path. If there is
> a way to make sure the skb-buf is big enough, we should use that instead.
>
> The best way would be to make hid-core honor the @size argument, though,
> that sounds easier than it is. So lets just fix the buffer-overflows for
> now and afterwards look for a faster way for all transport drivers.
>
> Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
> ---
> Hi
>
> Any ideas how to improve this patch? I'd like to avoid the extra copy but I have
> no clue how the skb stuff works exactly.
the buffers are coming straight from L2CAP. They might come in fragments. We have to reassemble them and while there are large packets for sure, we rather not want to allocate 4096 for every reassembled packet to make HID happy.
I am not super familiar with the underlying memory management of SKBs, but I assume they use slices of some sort internally. So uses large SKBs for 20 byte HID report will cause an issue with all other protocols running on top of L2CAP>
> I also haven't figured out a nice way to make HID-core honor the "size"
> parameter. hid-input depends on getting the whole input-report.
I think this needs clearly fixing.
> Comments welcome!
> David
>
> net/bluetooth/hidp/core.c | 16 ++++++++++++++--
> net/bluetooth/hidp/hidp.h | 4 ++++
> 2 files changed, 18 insertions(+), 2 deletions(-)
>
> diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
> index 292e619..d9fb934 100644
> --- a/net/bluetooth/hidp/core.c
> +++ b/net/bluetooth/hidp/core.c
> @@ -430,6 +430,16 @@ static void hidp_del_timer(struct hidp_session *session)
> del_timer(&session->timer);
> }
>
> +static void hidp_process_report(struct hidp_session *session,
> + int type, const u8 *data, int len, int intr)
> +{
> + if (len > HID_MAX_BUFFER_SIZE)
> + len = HID_MAX_BUFFER_SIZE;
> +
> + memcpy(session->input_buf, data, len);
> + hid_input_report(session->hid, type, session->input_buf, len, intr);
> +}
> +
> static void hidp_process_handshake(struct hidp_session *session,
> unsigned char param)
> {
> @@ -502,7 +512,8 @@ static int hidp_process_data(struct hidp_session *session, struct sk_buff *skb,
> hidp_input_report(session, skb);
>
> if (session->hid)
> - hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 0);
> + hidp_process_report(session, HID_INPUT_REPORT,
> + skb->data, skb->len, 0);
> break;
>
> case HIDP_DATA_RTYPE_OTHER:
> @@ -584,7 +595,8 @@ static void hidp_recv_intr_frame(struct hidp_session *session,
> hidp_input_report(session, skb);
>
> if (session->hid) {
> - hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 1);
> + hidp_process_report(session, HID_INPUT_REPORT,
> + skb->data, skb->len, 1);
> BT_DBG("report len %d", skb->len);
> }
> } else {
> diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h
> index ab52414..8798492 100644
> --- a/net/bluetooth/hidp/hidp.h
> +++ b/net/bluetooth/hidp/hidp.h
> @@ -24,6 +24,7 @@
> #define __HIDP_H
>
> #include <linux/types.h>
> +#include <linux/hid.h>
> #include <linux/kref.h>
> #include <net/bluetooth/bluetooth.h>
> #include <net/bluetooth/l2cap.h>
> @@ -179,6 +180,9 @@ struct hidp_session {
>
> /* Used in hidp_output_raw_report() */
> int output_report_success; /* boolean */
> +
> + /* temporary input buffer */
> + u8 input_buf[HID_MAX_BUFFER_SIZE];
> };
The report does not need any specific alignment?
Regards
Marcel
^ 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