* Re: [PATCH 2/3 RESEND] mfd: tc3589x: Reform device tree probing
From: Linus Walleij @ 2014-01-23 15:04 UTC (permalink / raw)
To: Lee Jones
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Dmitry Torokhov, Linux Input, Samuel Ortiz,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
Mark Rutland
In-Reply-To: <20140123133141.GD8586@lee--X1>
On Thu, Jan 23, 2014 at 2:31 PM, Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
> Patch looks good to me. Is there any reason why we should rush this in
> for v3.14, or is it okay to go to -next?
No rush, but it's been on review like forever so unless there is
some noise from the DT people at -rc1 I'd be very happy if you
could apply patches 1 & 2 by then.
The third one can be applied out-of-order to the input tree after
that.
Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] Input: add short note about ABS_MISC+<n> axes
From: David Herrmann @ 2014-01-23 14:39 UTC (permalink / raw)
To: open list:HID CORE LAYER
Cc: Jiri Kosina, Dmitry Torokhov, Benjamin Tissoires, David Herrmann
In-Reply-To: <1387207386-4601-1-git-send-email-dh.herrmann@gmail.com>
Hi Dmitry
ping?
David
On Mon, Dec 16, 2013 at 4:23 PM, David Herrmann <dh.herrmann@gmail.com> wrote:
> We must not reassign those axes or we will break backwards-compat. Add a
> small note for that so we don't accidentally some day reuse these.
>
> Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
> ---
> include/uapi/linux/input.h | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
> index a372627..da1125e 100644
> --- a/include/uapi/linux/input.h
> +++ b/include/uapi/linux/input.h
> @@ -816,6 +816,10 @@ struct input_keymap_entry {
>
> #define ABS_MISC 0x28
>
> +/* ABS_MISC+x is used by HID to assign to unknown axes. Note that they used to
> + * flow over into the MT definitions. For backwards-compatibility, we must
> + * not reassign [0x29-0x2e] */
> +
> #define ABS_MT_SLOT 0x2f /* MT slot being modified */
> #define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */
> #define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */
> --
> 1.8.5.1
>
^ permalink raw reply
* Re: [PATCH v4 2/2] input/uinput: add UI_GET_SYSNAME ioctl to retrieve the sysfs path
From: David Herrmann @ 2014-01-23 14:37 UTC (permalink / raw)
To: Benjamin Tissoires
Cc: Benjamin Tissoires, Dmitry Torokhov, Peter Hutterer,
open list:HID CORE LAYER, linux-kernel
In-Reply-To: <1390411440-1158-2-git-send-email-benjamin.tissoires@redhat.com>
Hi Benjamin
On Wed, Jan 22, 2014 at 6:24 PM, Benjamin Tissoires
<benjamin.tissoires@redhat.com> wrote:
> uinput is used in the xorg-integration-tests suite and in the wayland
> test suite. These automated tests suites create many virtual input
> devices and then hook something to read these newly created devices.
>
> Currently, uinput does not provide the created input device, which means
> that we rely on an heuristic to guess which input node was created.
> The problem is that is heuristic is subjected to races between different
> uinput devices or even with physical devices. Having a way to retrieve
> the sysfs path allows us to find without any doubts the event node.
>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> ---
>
> Hi guys,
>
> a new version of this patch with a brand new commit message.
> Happy reviewing :)
>
> changes since v3:
> - new commit message (I hope it will be better now)
> - removed the -EAGAIN stuff thanks to the previous patch (not in v3)
> - used "put_user(0, (char *)dest + len - 1);" instead of "copy_to_user(dest + len - 1, "\0", 1);"
> - check if maxlen == 0
>
> changes since v2:
> - the ioctl returns only the device name, thus I renamed the ioctl to UI_GET_SYSNAME
> - reordered uinput_str_to_user() arguments
> - be sure to terminate the user string we send by \0
> - abort if udev->state is not UIST_CREATED
> - dropped the patch 1/2 (adding resolution to uinput) because I think David has
> already it in one of his queues (ABS2 IIRC)
>
> The corresponding libevdev patch is still here:
> http://lists.freedesktop.org/archives/input-tools/2014-January/000757.html
>
> Cheers,
> Benjamin
>
> drivers/input/misc/uinput.c | 42 ++++++++++++++++++++++++++++++++++++++++++
> include/linux/uinput.h | 2 ++
> include/uapi/linux/uinput.h | 13 ++++++++++++-
> 3 files changed, 56 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
> index d8ae08d..b51269f 100644
> --- a/drivers/input/misc/uinput.c
> +++ b/drivers/input/misc/uinput.c
> @@ -20,6 +20,8 @@
> * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
> *
> * Changes/Revisions:
> + * 0.4 01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>)
> + * - add UI_GET_SYSNAME ioctl
> * 0.3 09/04/2006 (Anssi Hannula <anssi.hannula@gmail.com>)
> * - updated ff support for the changes in kernel interface
> * - added MODULE_VERSION
> @@ -670,6 +672,30 @@ static int uinput_ff_upload_from_user(const char __user *buffer,
> __ret; \
> })
>
> +static int uinput_str_to_user(void __user *dest, const char *str,
> + unsigned int maxlen)
> +{
> + int len, ret;
> +
> + if (!str)
> + return -ENOENT;
> +
> + if (maxlen == 0)
> + return -EINVAL;
> +
> + len = strlen(str) + 1;
> + if (len > maxlen)
> + len = maxlen;
> +
> + ret = copy_to_user(dest, str, len);
Technically you could use "len - 1" here, but it's fine.
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
Thanks
David
> + if (ret)
> + return -EFAULT;
> +
> + /* force terminating '\0' */
> + ret = put_user(0, (char *)dest + len - 1);
> + return ret ? -EFAULT : len;
> +}
> +
> static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
> unsigned long arg, void __user *p)
> {
> @@ -679,6 +705,8 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
> struct uinput_ff_erase ff_erase;
> struct uinput_request *req;
> char *phys;
> + const char *name;
> + unsigned int size;
>
> retval = mutex_lock_interruptible(&udev->mutex);
> if (retval)
> @@ -831,6 +859,20 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
> goto out;
> }
>
> + size = _IOC_SIZE(cmd);
> +
> + /* Now check variable-length commands */
> + switch (cmd & ~IOCSIZE_MASK) {
> + case UI_GET_SYSNAME(0):
> + if (udev->state != UIST_CREATED) {
> + retval = -ENOENT;
> + goto out;
> + }
> + name = dev_name(&udev->dev->dev);
> + retval = uinput_str_to_user(p, name, size);
> + goto out;
> + }
> +
> retval = -EINVAL;
> out:
> mutex_unlock(&udev->mutex);
> diff --git a/include/linux/uinput.h b/include/linux/uinput.h
> index 0a4487d..0994c0d 100644
> --- a/include/linux/uinput.h
> +++ b/include/linux/uinput.h
> @@ -20,6 +20,8 @@
> * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
> *
> * Changes/Revisions:
> + * 0.4 01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>)
> + * - add UI_GET_SYSNAME ioctl
> * 0.3 24/05/2006 (Anssi Hannula <anssi.hannulagmail.com>)
> * - update ff support for the changes in kernel interface
> * - add UINPUT_VERSION
> diff --git a/include/uapi/linux/uinput.h b/include/uapi/linux/uinput.h
> index fe46431..0389b48 100644
> --- a/include/uapi/linux/uinput.h
> +++ b/include/uapi/linux/uinput.h
> @@ -20,6 +20,8 @@
> * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
> *
> * Changes/Revisions:
> + * 0.4 01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>)
> + * - add UI_GET_SYSNAME ioctl
> * 0.3 24/05/2006 (Anssi Hannula <anssi.hannulagmail.com>)
> * - update ff support for the changes in kernel interface
> * - add UINPUT_VERSION
> @@ -35,7 +37,7 @@
> #include <linux/types.h>
> #include <linux/input.h>
>
> -#define UINPUT_VERSION 3
> +#define UINPUT_VERSION 4
>
>
> struct uinput_ff_upload {
> @@ -73,6 +75,15 @@ struct uinput_ff_erase {
> #define UI_BEGIN_FF_ERASE _IOWR(UINPUT_IOCTL_BASE, 202, struct uinput_ff_erase)
> #define UI_END_FF_ERASE _IOW(UINPUT_IOCTL_BASE, 203, struct uinput_ff_erase)
>
> +/**
> + * UI_GET_SYSNAME - get the sysfs name of the created uinput device
> + *
> + * @return the sysfs name of the created virtual input device.
> + * The complete sysfs path is then /sys/devices/virtual/input/--NAME--
> + * Usually, it is in the form "inputN"
> + */
> +#define UI_GET_SYSNAME(len) _IOC(_IOC_READ, UINPUT_IOCTL_BASE, 300, len)
> +
> /*
> * To write a force-feedback-capable driver, the upload_effect
> * and erase_effect callbacks in input_dev must be implemented.
> --
> 1.8.4.2
>
^ permalink raw reply
* Re: [PATCH v4 1/2] input/uinput: replace breaks by goto out in uinput_ioctl_handler
From: David Herrmann @ 2014-01-23 14:35 UTC (permalink / raw)
To: Benjamin Tissoires
Cc: Benjamin Tissoires, Dmitry Torokhov, Peter Hutterer,
open list:HID CORE LAYER, linux-kernel
In-Reply-To: <1390411440-1158-1-git-send-email-benjamin.tissoires@redhat.com>
Hi Benjamin
On Wed, Jan 22, 2014 at 6:23 PM, Benjamin Tissoires
<benjamin.tissoires@redhat.com> wrote:
> From: Benjamin Tisssoires <benjamin.tissoires@redhat.com>
>
> The current implementation prevents us to add variable-length ioctl.
> Use a bunch of gotos instead of break to allow us to do so.
>
> No functional changes.
Looks good.
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
Thanks
David
> Signed-off-by: Benjamin Tisssoires <benjamin.tissoires@redhat.com>
> ---
>
> changes since v3:
> - brand new patch :)
>
> drivers/input/misc/uinput.c | 56 ++++++++++++++++++++++-----------------------
> 1 file changed, 27 insertions(+), 29 deletions(-)
>
> diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
> index 7728359..d8ae08d 100644
> --- a/drivers/input/misc/uinput.c
> +++ b/drivers/input/misc/uinput.c
> @@ -693,51 +693,51 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
> switch (cmd) {
> case UI_DEV_CREATE:
> retval = uinput_create_device(udev);
> - break;
> + goto out;
>
> case UI_DEV_DESTROY:
> uinput_destroy_device(udev);
> - break;
> + goto out;
>
> case UI_SET_EVBIT:
> retval = uinput_set_bit(arg, evbit, EV_MAX);
> - break;
> + goto out;
>
> case UI_SET_KEYBIT:
> retval = uinput_set_bit(arg, keybit, KEY_MAX);
> - break;
> + goto out;
>
> case UI_SET_RELBIT:
> retval = uinput_set_bit(arg, relbit, REL_MAX);
> - break;
> + goto out;
>
> case UI_SET_ABSBIT:
> retval = uinput_set_bit(arg, absbit, ABS_MAX);
> - break;
> + goto out;
>
> case UI_SET_MSCBIT:
> retval = uinput_set_bit(arg, mscbit, MSC_MAX);
> - break;
> + goto out;
>
> case UI_SET_LEDBIT:
> retval = uinput_set_bit(arg, ledbit, LED_MAX);
> - break;
> + goto out;
>
> case UI_SET_SNDBIT:
> retval = uinput_set_bit(arg, sndbit, SND_MAX);
> - break;
> + goto out;
>
> case UI_SET_FFBIT:
> retval = uinput_set_bit(arg, ffbit, FF_MAX);
> - break;
> + goto out;
>
> case UI_SET_SWBIT:
> retval = uinput_set_bit(arg, swbit, SW_MAX);
> - break;
> + goto out;
>
> case UI_SET_PROPBIT:
> retval = uinput_set_bit(arg, propbit, INPUT_PROP_MAX);
> - break;
> + goto out;
>
> case UI_SET_PHYS:
> if (udev->state == UIST_CREATED) {
> @@ -753,18 +753,18 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
>
> kfree(udev->dev->phys);
> udev->dev->phys = phys;
> - break;
> + goto out;
>
> case UI_BEGIN_FF_UPLOAD:
> retval = uinput_ff_upload_from_user(p, &ff_up);
> if (retval)
> - break;
> + goto out;
>
> req = uinput_request_find(udev, ff_up.request_id);
> if (!req || req->code != UI_FF_UPLOAD ||
> !req->u.upload.effect) {
> retval = -EINVAL;
> - break;
> + goto out;
> }
>
> ff_up.retval = 0;
> @@ -775,65 +775,63 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
> memset(&ff_up.old, 0, sizeof(struct ff_effect));
>
> retval = uinput_ff_upload_to_user(p, &ff_up);
> - break;
> + goto out;
>
> case UI_BEGIN_FF_ERASE:
> if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
> retval = -EFAULT;
> - break;
> + goto out;
> }
>
> req = uinput_request_find(udev, ff_erase.request_id);
> if (!req || req->code != UI_FF_ERASE) {
> retval = -EINVAL;
> - break;
> + goto out;
> }
>
> ff_erase.retval = 0;
> ff_erase.effect_id = req->u.effect_id;
> if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) {
> retval = -EFAULT;
> - break;
> + goto out;
> }
>
> - break;
> + goto out;
>
> case UI_END_FF_UPLOAD:
> retval = uinput_ff_upload_from_user(p, &ff_up);
> if (retval)
> - break;
> + goto out;
>
> req = uinput_request_find(udev, ff_up.request_id);
> if (!req || req->code != UI_FF_UPLOAD ||
> !req->u.upload.effect) {
> retval = -EINVAL;
> - break;
> + goto out;
> }
>
> req->retval = ff_up.retval;
> uinput_request_done(udev, req);
> - break;
> + goto out;
>
> case UI_END_FF_ERASE:
> if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
> retval = -EFAULT;
> - break;
> + goto out;
> }
>
> req = uinput_request_find(udev, ff_erase.request_id);
> if (!req || req->code != UI_FF_ERASE) {
> retval = -EINVAL;
> - break;
> + goto out;
> }
>
> req->retval = ff_erase.retval;
> uinput_request_done(udev, req);
> - break;
> -
> - default:
> - retval = -EINVAL;
> + goto out;
> }
>
> + retval = -EINVAL;
> out:
> mutex_unlock(&udev->mutex);
> return retval;
> --
> 1.8.4.2
>
^ permalink raw reply
* Re: [PATCH 2/3 RESEND] mfd: tc3589x: Reform device tree probing
From: Lee Jones @ 2014-01-23 13:31 UTC (permalink / raw)
To: Linus Walleij
Cc: devicetree, Dmitry Torokhov, linux-input, Samuel Ortiz,
linux-kernel, linux-arm-kernel, Mark Rutland
In-Reply-To: <1390481008-23900-1-git-send-email-linus.walleij@linaro.org>
> This changes the following mechanisms in the TC3589x device tree
> probing path:
>
> - Use the .of_match_table in struct device_driver to match the
> device in the device tree.
> - Add matches for the proper compatible strings "toshiba,..."
> and all sub-variants, just as is done for the .id matches.
> - Move over all the allocation of platform data etc to the
> tc3589x_of_probe() function and follow the pattern of passing
> a platform data pointer back, or an error pointer on error,
> as found in the STMPE driver.
> - Match the new (proper) compatible strings for the GPIO and
> keypad MFD cells.
> - Use of_device_is_compatible() rather than just !strcmp()
> to discover which cells to instantiate.
>
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
> ---
> drivers/mfd/tc3589x.c | 84 ++++++++++++++++++++++++++++++++++++---------------
> 1 file changed, 59 insertions(+), 25 deletions(-)
Patch looks good to me. Is there any reason why we should rush this in
for v3.14, or is it okay to go to -next?
Acked-by: Lee Jones <lee.jones@linaro.org>
--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
--
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 3/3 RESEND] input: tc3589x-keypad: support probing from device tree
From: Linus Walleij @ 2014-01-23 12:43 UTC (permalink / raw)
To: devicetree, Dmitry Torokhov, linux-input, Samuel Ortiz, Lee Jones
Cc: linux-kernel, linux-arm-kernel, Mark Rutland, Linus Walleij
Implement device tree probing for the tc3589x keypad driver.
This is modeled on the STMPE keypad driver and tested on the
Ux500 TVK1281618 UIB.
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ChangeLog v2->v3:
- Use two local u32 variables to avoid weirdness in u8 casting
of the resulting values to the pointers.
ChangeLog v1->v2:
- Fix rows/columns binding to read two u32's insead of two
u8 /bits/ as noted by Mark Rutland.
---
drivers/input/keyboard/tc3589x-keypad.c | 66 ++++++++++++++++++++++++++++++++-
1 file changed, 64 insertions(+), 2 deletions(-)
diff --git a/drivers/input/keyboard/tc3589x-keypad.c b/drivers/input/keyboard/tc3589x-keypad.c
index 208de7cbb7fa..7f36e7addb86 100644
--- a/drivers/input/keyboard/tc3589x-keypad.c
+++ b/drivers/input/keyboard/tc3589x-keypad.c
@@ -297,6 +297,65 @@ static void tc3589x_keypad_close(struct input_dev *input)
tc3589x_keypad_disable(keypad);
}
+#ifdef CONFIG_OF
+static const struct tc3589x_keypad_platform_data *
+tc3589x_keypad_of_probe(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct tc3589x_keypad_platform_data *plat;
+ u32 cols, rows;
+ u32 debounce_ms;
+ int proplen;
+
+ if (!np)
+ return ERR_PTR(-ENODEV);
+
+ plat = devm_kzalloc(dev, sizeof(*plat), GFP_KERNEL);
+ if (!plat)
+ return ERR_PTR(-ENOMEM);
+
+ of_property_read_u32(np, "keypad,num-columns", &cols);
+ of_property_read_u32(np, "keypad,num-rows", &rows);
+ plat->kcol = (u8) cols;
+ plat->krow = (u8) rows;
+ if (!plat->krow || !plat->kcol ||
+ plat->krow > TC_KPD_ROWS || plat->kcol > TC_KPD_COLUMNS) {
+ dev_err(dev,
+ "keypad columns/rows not properly specified (%ux%u)\n",
+ plat->kcol, plat->krow);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (!of_get_property(np, "linux,keymap", &proplen)) {
+ dev_err(dev, "property linux,keymap not found\n");
+ return ERR_PTR(-ENOENT);
+ }
+
+ plat->no_autorepeat = of_property_read_bool(np, "linux,no-autorepeat");
+ plat->enable_wakeup = of_property_read_bool(np, "linux,wakeup");
+
+ /* The custom delay format is ms/16 */
+ of_property_read_u32(np, "debounce-delay-ms", &debounce_ms);
+ if (debounce_ms)
+ plat->debounce_period = debounce_ms * 16;
+ else
+ plat->debounce_period = TC_KPD_DEBOUNCE_PERIOD;
+
+ plat->settle_time = TC_KPD_SETTLE_TIME;
+ /* FIXME: should be property of the IRQ resource? */
+ plat->irqtype = IRQF_TRIGGER_FALLING;
+
+ return plat;
+}
+#else
+static inline const struct tc3589x_keypad_platform_data *
+tc3589x_keypad_of_probe(struct device *dev)
+{
+ return ERR_PTR(-ENODEV);
+}
+#endif
+
+
static int tc3589x_keypad_probe(struct platform_device *pdev)
{
struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent);
@@ -307,8 +366,11 @@ static int tc3589x_keypad_probe(struct platform_device *pdev)
plat = tc3589x->pdata->keypad;
if (!plat) {
- dev_err(&pdev->dev, "invalid keypad platform data\n");
- return -EINVAL;
+ plat = tc3589x_keypad_of_probe(&pdev->dev);
+ if (IS_ERR(plat)) {
+ dev_err(&pdev->dev, "invalid keypad platform data\n");
+ return PTR_ERR(plat);
+ }
}
irq = platform_get_irq(pdev, 0);
--
1.8.4.2
^ permalink raw reply related
* [PATCH 2/3 RESEND] mfd: tc3589x: Reform device tree probing
From: Linus Walleij @ 2014-01-23 12:43 UTC (permalink / raw)
To: devicetree, Dmitry Torokhov, linux-input, Samuel Ortiz, Lee Jones
Cc: linux-kernel, linux-arm-kernel, Mark Rutland, Linus Walleij
This changes the following mechanisms in the TC3589x device tree
probing path:
- Use the .of_match_table in struct device_driver to match the
device in the device tree.
- Add matches for the proper compatible strings "toshiba,..."
and all sub-variants, just as is done for the .id matches.
- Move over all the allocation of platform data etc to the
tc3589x_of_probe() function and follow the pattern of passing
a platform data pointer back, or an error pointer on error,
as found in the STMPE driver.
- Match the new (proper) compatible strings for the GPIO and
keypad MFD cells.
- Use of_device_is_compatible() rather than just !strcmp()
to discover which cells to instantiate.
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
drivers/mfd/tc3589x.c | 84 ++++++++++++++++++++++++++++++++++++---------------
1 file changed, 59 insertions(+), 25 deletions(-)
diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c
index 87ea51dc6234..ed1ee2634809 100644
--- a/drivers/mfd/tc3589x.c
+++ b/drivers/mfd/tc3589x.c
@@ -13,8 +13,10 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tc3589x.h>
+#include <linux/err.h>
/**
* enum tc3589x_version - indicates the TC3589x version
@@ -160,7 +162,7 @@ static struct mfd_cell tc3589x_dev_gpio[] = {
.name = "tc3589x-gpio",
.num_resources = ARRAY_SIZE(gpio_resources),
.resources = &gpio_resources[0],
- .of_compatible = "tc3589x-gpio",
+ .of_compatible = "toshiba,tc3589x-gpio",
},
};
@@ -169,7 +171,7 @@ static struct mfd_cell tc3589x_dev_keypad[] = {
.name = "tc3589x-keypad",
.num_resources = ARRAY_SIZE(keypad_resources),
.resources = &keypad_resources[0],
- .of_compatible = "tc3589x-keypad",
+ .of_compatible = "toshiba,tc3589x-keypad",
},
};
@@ -318,45 +320,74 @@ static int tc3589x_device_init(struct tc3589x *tc3589x)
return ret;
}
-static int tc3589x_of_probe(struct device_node *np,
- struct tc3589x_platform_data *pdata)
+#ifdef CONFIG_OF
+static const struct of_device_id tc3589x_match[] = {
+ /* Legacy compatible string */
+ { .compatible = "tc3589x", .data = (void *) TC3589X_UNKNOWN },
+ { .compatible = "toshiba,tc35890", .data = (void *) TC3589X_TC35890 },
+ { .compatible = "toshiba,tc35892", .data = (void *) TC3589X_TC35892 },
+ { .compatible = "toshiba,tc35893", .data = (void *) TC3589X_TC35893 },
+ { .compatible = "toshiba,tc35894", .data = (void *) TC3589X_TC35894 },
+ { .compatible = "toshiba,tc35895", .data = (void *) TC3589X_TC35895 },
+ { .compatible = "toshiba,tc35896", .data = (void *) TC3589X_TC35896 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, tc3589x_match);
+
+static struct tc3589x_platform_data *
+tc3589x_of_probe(struct device *dev, enum tc3589x_version *version)
{
+ struct device_node *np = dev->of_node;
+ struct tc3589x_platform_data *pdata;
struct device_node *child;
+ const struct of_device_id *of_id;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ of_id = of_match_device(tc3589x_match, dev);
+ if (!of_id)
+ return ERR_PTR(-ENODEV);
+ *version = (enum tc3589x_version) of_id->data;
for_each_child_of_node(np, child) {
- if (!strcmp(child->name, "tc3589x_gpio")) {
+ if (of_device_is_compatible(child, "toshiba,tc3589x-gpio"))
pdata->block |= TC3589x_BLOCK_GPIO;
- }
- if (!strcmp(child->name, "tc3589x_keypad")) {
+ if (of_device_is_compatible(child, "toshiba,tc3589x-keypad"))
pdata->block |= TC3589x_BLOCK_KEYPAD;
- }
}
- return 0;
+ return pdata;
}
+#else
+static inline struct tc3589x_platform_data *
+tc3589x_of_probe(struct device *dev, enum tc3589x_version *version)
+{
+ dev_err(dev, "no device tree support\n");
+ return ERR_PTR(-ENODEV);
+}
+#endif
static int tc3589x_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
- struct tc3589x_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct device_node *np = i2c->dev.of_node;
+ struct tc3589x_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct tc3589x *tc3589x;
+ enum tc3589x_version version;
int ret;
if (!pdata) {
- if (np) {
- pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
-
- ret = tc3589x_of_probe(np, pdata);
- if (ret)
- return ret;
- }
- else {
+ pdata = tc3589x_of_probe(&i2c->dev, &version);
+ if (IS_ERR(pdata)) {
dev_err(&i2c->dev, "No platform data or DT found\n");
- return -EINVAL;
+ return PTR_ERR(pdata);
}
+ } else {
+ /* When not probing from device tree we have this ID */
+ version = id->driver_data;
}
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA
@@ -375,7 +406,7 @@ static int tc3589x_probe(struct i2c_client *i2c,
tc3589x->pdata = pdata;
tc3589x->irq_base = pdata->irq_base;
- switch (id->driver_data) {
+ switch (version) {
case TC3589X_TC35893:
case TC3589X_TC35895:
case TC3589X_TC35896:
@@ -471,9 +502,12 @@ static const struct i2c_device_id tc3589x_id[] = {
MODULE_DEVICE_TABLE(i2c, tc3589x_id);
static struct i2c_driver tc3589x_driver = {
- .driver.name = "tc3589x",
- .driver.owner = THIS_MODULE,
- .driver.pm = &tc3589x_dev_pm_ops,
+ .driver = {
+ .name = "tc3589x",
+ .owner = THIS_MODULE,
+ .pm = &tc3589x_dev_pm_ops,
+ .of_match_table = of_match_ptr(tc3589x_match),
+ },
.probe = tc3589x_probe,
.remove = tc3589x_remove,
.id_table = tc3589x_id,
--
1.8.4.2
^ permalink raw reply related
* [PATCH 1/3 RESEND] mfd: tc3589x: Add device tree bindings
From: Linus Walleij @ 2014-01-23 12:43 UTC (permalink / raw)
To: devicetree, Dmitry Torokhov, linux-input, Samuel Ortiz, Lee Jones
Cc: linux-kernel, linux-arm-kernel, Mark Rutland, Linus Walleij
This defines the device tree bindings for the Toshiba TC3589x
series of multi-purpose expanders. Only the stuff I can test
is defined: GPIO and keypad. Others may implement more
subdevices further down the road.
This is to complement
commit a435ae1d51e2f18414f2a87219fdbe068231e692
"mfd: Enable the tc3589x for Device Tree" which left off
the definition of the device tree bindings.
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ChangeLog v2->v3:
- Fix the keys/rows bindings to be u32 rather than
/bits/ 8, inconsistency noted by Mark Rutland.
ChangeLog v1->v2:
- Include a verbose example in the DT bindings.
- Explain why this is a stand-alone bindings patch.
---
Documentation/devicetree/bindings/mfd/tc3589x.txt | 107 ++++++++++++++++++++++
1 file changed, 107 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/tc3589x.txt
diff --git a/Documentation/devicetree/bindings/mfd/tc3589x.txt b/Documentation/devicetree/bindings/mfd/tc3589x.txt
new file mode 100644
index 000000000000..c6ac5bd2ce51
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/tc3589x.txt
@@ -0,0 +1,107 @@
+* Toshiba TC3589x multi-purpose expander
+
+The Toshiba TC3589x series are I2C-based MFD devices which may expose the
+following built-in devices: gpio, keypad, rotator (vibrator), PWM (for
+e.g. LEDs or vibrators) The included models are:
+
+- TC35890
+- TC35892
+- TC35893
+- TC35894
+- TC35895
+- TC35896
+
+Required properties:
+ - compatible : must be "toshiba,tc35890", "toshiba,tc35892", "toshiba,tc35893",
+ "toshiba,tc35894", "toshiba,tc35895" or "toshiba,tc35896"
+ - reg : I2C address of the device
+ - interrupt-parent : specifies which IRQ controller we're connected to
+ - interrupts : the interrupt on the parent the controller is connected to
+ - interrupt-controller : marks the device node as an interrupt controller
+ - #interrupt-cells : should be <1>, the first cell is the IRQ offset on this
+ TC3589x interrupt controller.
+
+Optional nodes:
+
+- GPIO
+ This GPIO module inside the TC3589x has 24 (TC35890, TC35892) or 20
+ (other models) GPIO lines.
+ - compatible : must be "toshiba,tc3589x-gpio"
+ - interrupts : interrupt on the parent, which must be the tc3589x MFD device
+ - interrupt-controller : marks the device node as an interrupt controller
+ - #interrupt-cells : should be <2>, the first cell is the IRQ offset on this
+ TC3589x GPIO interrupt controller, the second cell is the interrupt flags
+ in accordance with <dt-bindings/interrupt-controller/irq.h>. The following
+ flags are valid:
+ - IRQ_TYPE_LEVEL_LOW
+ - IRQ_TYPE_LEVEL_HIGH
+ - IRQ_TYPE_EDGE_RISING
+ - IRQ_TYPE_EDGE_FALLING
+ - IRQ_TYPE_EDGE_BOTH
+ - gpio-controller : marks the device node as a GPIO controller
+ - #gpio-cells : should be <2>, the first cell is the GPIO offset on this
+ GPIO controller, the second cell is the flags.
+
+- Keypad
+ This keypad is the same on all variants, supporting up to 96 different
+ keys. The linux-specific properties are modeled on those already existing
+ in other input drivers.
+ - compatible : must be "toshiba,tc3589x-keypad"
+ - debounce-delay-ms : debounce interval in milliseconds
+ - keypad,num-rows : number of rows in the matrix, see
+ bindings/input/matrix-keymap.txt
+ - keypad,num-columns : number of columns in the matrix, see
+ bindings/input/matrix-keymap.txt
+ - linux,keymap: the definition can be found in
+ bindings/input/matrix-keymap.txt
+ - linux,no-autorepeat: do no enable autorepeat feature.
+ - linux,wakeup: use any event on keypad as wakeup event.
+
+Example:
+
+tc35893@44 {
+ compatible = "toshiba,tc35893";
+ reg = <0x44>;
+ interrupt-parent = <&gpio6>;
+ interrupts = <26 IRQ_TYPE_EDGE_RISING>;
+
+ interrupt-controller;
+ #interrupt-cells = <1>;
+
+ tc3589x_gpio {
+ compatible = "toshiba,tc3589x-gpio";
+ interrupts = <0>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+ tc3589x_keypad {
+ compatible = "toshiba,tc3589x-keypad";
+ interrupts = <6>;
+ debounce-delay-ms = <4>;
+ keypad,num-columns = <8>;
+ keypad,num-rows = <8>;
+ linux,no-autorepeat;
+ linux,wakeup;
+ linux,keymap = <0x0301006b
+ 0x04010066
+ 0x06040072
+ 0x040200d7
+ 0x0303006a
+ 0x0205000e
+ 0x0607008b
+ 0x0500001c
+ 0x0403000b
+ 0x03040034
+ 0x05020067
+ 0x0305006c
+ 0x040500e7
+ 0x0005009e
+ 0x06020073
+ 0x01030039
+ 0x07060069
+ 0x050500d9>;
+ };
+};
--
1.8.4.2
^ permalink raw reply related
* [PATCH v2] input: synaptics-rmi4 - Count IRQs before creating functions; save F01 container.
From: Christopher Heiny @ 2014-01-23 1:35 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
Vivian Ly, Daniel Rosenberg, Jean Delvare, Joerie de Gram,
Linus Walleij, Benjamin Tissoires
Because creating a function can trigger events that result in the IRQ
related storage being accessed, we need to count the IRQs and allocate
their storage before the functions are created, rather than counting
them as the functions are created and allocating them afterwards. Since
we know the number of IRQs already, we can allocate the mask at
function creation time, rather than in a post-creation loop. Also, the
F01 function_container is needed elsewhere, so we need to save it here.
In order to keep the IRQ count logic sane in bootloader mode, we move
the check for bootloader mode from F01 initialization to the IRQ
counting routine.
Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
drivers/input/rmi4/rmi_driver.c | 122 ++++++++++++++++++++++++++++++----------
drivers/input/rmi4/rmi_driver.h | 1 -
drivers/input/rmi4/rmi_f01.c | 11 +---
3 files changed, 92 insertions(+), 42 deletions(-)
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index ce0520c..5d338ef 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -560,10 +560,19 @@ static int create_function(struct rmi_device *rmi_dev, void *clbk_ctx,
rmi_driver_copy_pdt_to_fd(pdt, &fn->fd, page_start);
+ error = rmi_driver_irq_get_mask(rmi_dev, fn);
+ if (error < 0) {
+ dev_err(dev, "%s: Failed to create irq_mask for F%02X.\n",
+ __func__, pdt->function_number);
+ return error;
+ }
+
error = rmi_register_function(fn);
if (error)
goto err_free_mem;
+ if (pdt->function_number == 0x01)
+ data->f01_container = fn;
list_add_tail(&fn->node, &data->function_list);
return RMI_SCAN_CONTINUE;
@@ -573,6 +582,30 @@ err_free_mem:
return RMI_SCAN_ERROR;
}
+/* Indicates that flash programming is enabled (bootloader mode). */
+#define RMI_F01_STATUS_BOOTLOADER(status) (!!((status) & 0x40))
+
+/*
+ * Given the PDT entry for F01, read the device status register to determine
+ * if we're stuck in bootloader mode or not.
+ *
+ */
+static int check_bootloader_mode(struct rmi_device *rmi_dev, struct pdt_entry *pdt,
+ u16 page_start)
+{
+ u8 device_status;
+ int retval = 0;
+
+ retval = rmi_read(rmi_dev, pdt->data_base_addr + page_start,
+ &device_status);
+ if (retval < 0) {
+ dev_err(&rmi_dev->dev, "Failed to read device status.\n");
+ return retval;
+ }
+
+ return RMI_F01_STATUS_BOOTLOADER(device_status);
+}
+
static int rmi_initial_reset(struct rmi_device *rmi_dev,
void *clbk_ctx, struct pdt_entry *pdt_entry, int page)
{
@@ -599,6 +632,23 @@ static int rmi_initial_reset(struct rmi_device *rmi_dev,
return (!page) ? RMI_SCAN_CONTINUE : RMI_SCAN_ERROR;
}
+static int rmi_count_irqs(struct rmi_device *rmi_dev,
+ void * clbk_ctx, struct pdt_entry *pdt_entry, int page)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+
+ data->irq_count += pdt_entry->interrupt_source_count;
+ if (pdt_entry->function_number == 0x01) {
+ data->f01_bootloader_mode = check_bootloader_mode(rmi_dev,
+ pdt_entry, page);
+ if (data->f01_bootloader_mode)
+ dev_warn(&rmi_dev->dev,
+ "WARNING: RMI4 device is in bootloader mode!\n");
+ }
+
+ return RMI_SCAN_CONTINUE;
+}
+
static int rmi_create_functions(struct rmi_device *rmi_dev)
{
struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
@@ -766,7 +816,6 @@ static int rmi_driver_probe(struct device *dev)
{
struct rmi_driver *rmi_driver;
struct rmi_driver_data *data = NULL;
- struct rmi_function *fn;
struct rmi_device_platform_data *pdata;
int retval = 0;
struct rmi_device *rmi_dev;
@@ -821,28 +870,6 @@ static int rmi_driver_probe(struct device *dev)
if (retval)
dev_warn(dev, "RMI initial reset failed! Continuing in spite of this.\n");
- retval = rmi_create_functions(rmi_dev);
- if (retval) {
- dev_err(dev, "PDT scan for %s failed.\n", pdata->sensor_name);
- retval = -ENODEV;
- goto err_free_data;
- }
-
- if (!data->f01_container) {
- dev_err(dev, "missing F01 container!\n");
- retval = -EINVAL;
- goto err_free_data;
- }
-
- list_for_each_entry(fn, &data->function_list, node) {
- retval = rmi_driver_irq_get_mask(rmi_dev, fn);
- if (retval < 0) {
- dev_err(dev, "%s: Failed to create irq_mask.\n",
- __func__);
- goto err_free_data;
- }
- }
-
retval = rmi_read(rmi_dev, PDT_PROPERTIES_LOCATION, &data->pdt_props);
if (retval < 0) {
/*
@@ -853,6 +880,22 @@ static int rmi_driver_probe(struct device *dev)
PDT_PROPERTIES_LOCATION);
}
+ /*
+ * We need to count the IRQs and allocate their storage before scanning
+ * the PDT and creating the function entries, because adding a new
+ * function can trigger events that result in the IRQ related storage
+ * being accessed.
+ */
+ dev_dbg(dev, "Counting IRQs.\n");
+ retval = rmi_scan_pdt(rmi_dev, NULL, rmi_count_irqs);
+ if (retval) {
+ retval = -ENODEV;
+ dev_err(dev, "IRQ counting for %s.\n",
+ pdata->sensor_name);
+ goto err_free_data;
+ }
+ data->num_of_irq_regs = (data->irq_count + 7) / 8;
+
mutex_init(&data->irq_mutex);
data->irq_status = devm_kzalloc(dev,
BITS_TO_LONGS(data->irq_count)*sizeof(unsigned long),
@@ -872,6 +915,28 @@ static int rmi_driver_probe(struct device *dev)
goto err_free_data;
}
+ data->irq_mask_store = devm_kzalloc(dev,
+ BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long),
+ GFP_KERNEL);
+ if (!data->irq_mask_store) {
+ dev_err(dev, "Failed to allocate irq_mask_store.\n");
+ retval = -ENOMEM;
+ goto err_free_data;
+ }
+
+ retval = rmi_create_functions(rmi_dev);
+ if (retval) {
+ retval = -ENODEV;
+ dev_err(dev, "Function creation failed.\n");
+ goto err_free_data;
+ }
+
+ if (!data->f01_container) {
+ dev_err(dev, "missing F01 container!\n");
+ retval = -EINVAL;
+ goto err_free_data;
+ }
+
retval = rmi_read_block(rmi_dev,
data->f01_container->fd.control_base_addr+1,
data->current_irq_mask, data->num_of_irq_regs);
@@ -881,14 +946,6 @@ static int rmi_driver_probe(struct device *dev)
goto err_free_data;
}
- data->irq_mask_store = devm_kzalloc(dev,
- BITS_TO_LONGS(data->irq_count)*sizeof(unsigned long),
- GFP_KERNEL);
- if (!data->irq_mask_store) {
- dev_err(dev, "Failed to allocate mask store.\n");
- retval = -ENOMEM;
- goto err_free_data;
- }
if (IS_ENABLED(CONFIG_PM)) {
data->pm_data = pdata->pm_data;
data->pre_suspend = pdata->pre_suspend;
@@ -954,6 +1011,9 @@ static int rmi_driver_probe(struct device *dev)
return 0;
err_free_data:
+ rmi_free_function_list(rmi_dev);
+ if (gpio_is_valid(pdata->attn_gpio))
+ gpio_free(pdata->attn_gpio);
return retval;
}
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
index 2c1c63f..e5566c4 100644
--- a/drivers/input/rmi4/rmi_driver.h
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -100,7 +100,6 @@ struct pdt_entry {
#define RMI_SCAN_ERROR -1
#define RMI_SCAN_CONTINUE 0
#define RMI_SCAN_DONE 1
-
int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
int (*rmi_pdt_scan_clbk)(struct rmi_device *rmi_dev,
void *clbk_ctx, struct pdt_entry *entry, int page));
diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c
index 628b082..c7f2360 100644
--- a/drivers/input/rmi4/rmi_f01.c
+++ b/drivers/input/rmi4/rmi_f01.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2012 Synaptics Incorporated
+ * Copyright (c) 2011-2014 Synaptics Incorporated
* Copyright (c) 2011 Unixphere
*
* This program is free software; you can redistribute it and/or modify it
@@ -59,8 +59,6 @@ struct f01_basic_properties {
/* Most recent device status event */
#define RMI_F01_STATUS_CODE(status) ((status) & 0x0f)
-/* Indicates that flash programming is enabled (bootloader mode). */
-#define RMI_F01_STATUS_BOOTLOADER(status) (!!((status) & 0x40))
/* The device has lost its configuration for some reason. */
#define RMI_F01_STATUS_UNCONFIGURED(status) (!!((status) & 0x80))
@@ -358,13 +356,6 @@ static int rmi_f01_initialize(struct rmi_function *fn)
goto error_exit;
}
- driver_data->f01_bootloader_mode =
- RMI_F01_STATUS_BOOTLOADER(data->device_status);
- if (driver_data->f01_bootloader_mode)
- dev_warn(&rmi_dev->dev,
- "WARNING: RMI4 device is in bootloader mode!\n");
-
-
if (RMI_F01_STATUS_UNCONFIGURED(data->device_status)) {
dev_err(&fn->dev,
"Device was reset during configuration process, status: %#02x!\n",
^ permalink raw reply related
* [PATCH v2] input synaptics-rmi4: PDT scan cleanup
From: Christopher Heiny @ 2014-01-23 0:56 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
Vivian Ly, Daniel Rosenberg, Jean Delvare, Joerie de Gram,
Linus Walleij, Benjamin Tissoires
Eliminates copy-paste code that handled scans of the Page Descriptor
Table, replacing it with a single PDT scan routine that invokes a
callback function. The scan routine is not static so that it can be
used by the firmware update code (under development, not yet submitted).
Symbols that no longer needed to be public were moved into rmi_driver.c.
Updated the copyright dates and eliminate C++ style comments while we
were at it.
Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
drivers/input/rmi4/rmi_driver.c | 190 ++++++++++++++++++++--------------------
drivers/input/rmi4/rmi_driver.h | 19 ++--
2 files changed, 105 insertions(+), 104 deletions(-)
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index eb790ff..ce0520c 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2013 Synaptics Incorporated
+ * Copyright (c) 2011-2014 Synaptics Incorporated
* Copyright (c) 2011 Unixphere
*
* This driver provides the core support for a single RMI4-based device.
@@ -36,6 +36,14 @@
#define RMI4_MAX_PAGE 0xff
#define RMI4_PAGE_SIZE 0x100
+#define RMI_PDT_ENTRY_SIZE 6
+#define RMI_PDT_FUNCTION_VERSION_MASK 0x60
+#define RMI_PDT_INT_SOURCE_COUNT_MASK 0x07
+
+#define PDT_START_SCAN_LOCATION 0x00e9
+#define PDT_END_SCAN_LOCATION 0x0005
+#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff)
+
#define RMI_DEVICE_RESET_CMD 0x01
#define DEFAULT_RESET_DELAY_MS 100
@@ -325,7 +333,7 @@ static int process_interrupt_requests(struct rmi_device *rmi_dev)
static int rmi_driver_set_input_params(struct rmi_device *rmi_dev,
struct input_dev *input)
{
- // FIXME: set up parent
+ /* FIXME: set up parent */
input->name = SYNAPTICS_INPUT_DEVICE_NAME;
input->id.vendor = SYNAPTICS_VENDOR_ID;
input->id.bustype = BUS_RMI;
@@ -484,7 +492,7 @@ int rmi_driver_irq_get_mask(struct rmi_device *rmi_dev,
return -ENOMEM;
}
-int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry,
+static int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry,
u16 pdt_address)
{
u8 buf[RMI_PDT_ENTRY_SIZE];
@@ -507,7 +515,6 @@ int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry,
return 0;
}
-EXPORT_SYMBOL_GPL(rmi_read_pdt_entry);
static void rmi_driver_copy_pdt_to_fd(struct pdt_entry *pdt,
struct rmi_function_descriptor *fd,
@@ -522,15 +529,15 @@ static void rmi_driver_copy_pdt_to_fd(struct pdt_entry *pdt,
fd->function_version = pdt->function_version;
}
-static int create_function(struct rmi_device *rmi_dev,
- struct pdt_entry *pdt,
- int *current_irq_count,
- u16 page_start)
+static int create_function(struct rmi_device *rmi_dev, void *clbk_ctx,
+ struct pdt_entry *pdt, int page)
{
struct device *dev = &rmi_dev->dev;
struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
struct rmi_device_platform_data *pdata = to_rmi_platform_data(rmi_dev);
struct rmi_function *fn;
+ int *current_irq_count = (int *)clbk_ctx;
+ u16 page_start = RMI4_PAGE_SIZE * page;
int error;
dev_dbg(dev, "Initializing F%02X for %s.\n",
@@ -540,7 +547,7 @@ static int create_function(struct rmi_device *rmi_dev,
if (!fn) {
dev_err(dev, "Failed to allocate memory for F%02X\n",
pdt->function_number);
- return -ENOMEM;
+ return RMI_SCAN_ERROR;
}
INIT_LIST_HEAD(&fn->node);
@@ -559,92 +566,78 @@ static int create_function(struct rmi_device *rmi_dev,
list_add_tail(&fn->node, &data->function_list);
- return 0;
+ return RMI_SCAN_CONTINUE;
err_free_mem:
kfree(fn);
- return error;
+ return RMI_SCAN_ERROR;
}
-/*
- * Scan the PDT for F01 so we can force a reset before anything else
- * is done. This forces the sensor into a known state, and also
- * forces application of any pending updates from reflashing the
- * firmware or configuration.
- *
- * We have to do this before actually building the PDT because the reflash
- * updates (if any) might cause various registers to move around.
- */
-static int rmi_initial_reset(struct rmi_device *rmi_dev)
+static int rmi_initial_reset(struct rmi_device *rmi_dev,
+ void *clbk_ctx, struct pdt_entry *pdt_entry, int page)
{
- struct pdt_entry pdt_entry;
- int page;
- struct device *dev = &rmi_dev->dev;
- bool done = false;
- bool has_f01 = false;
- int i;
int retval;
- const struct rmi_device_platform_data *pdata =
- to_rmi_platform_data(rmi_dev);
- dev_dbg(dev, "Initial reset.\n");
+ if (pdt_entry->function_number == 0x01) {
+ u16 cmd_addr = page + pdt_entry->command_base_addr;
+ u8 cmd_buf = RMI_DEVICE_RESET_CMD;
+ const struct rmi_device_platform_data *pdata =
+ to_rmi_platform_data(rmi_dev);
- for (page = 0; (page <= RMI4_MAX_PAGE) && !done; page++) {
- u16 page_start = RMI4_PAGE_SIZE * page;
- u16 pdt_start = page_start + PDT_START_SCAN_LOCATION;
- u16 pdt_end = page_start + PDT_END_SCAN_LOCATION;
+ retval = rmi_write_block(rmi_dev, cmd_addr, &cmd_buf, 1);
+ if (retval < 0) {
+ dev_err(&rmi_dev->dev,
+ "Initial reset failed. Code = %d.\n", retval);
+ return RMI_SCAN_ERROR;
+ }
+ mdelay(pdata->reset_delay_ms);
- done = true;
- for (i = pdt_start; i >= pdt_end; i -= RMI_PDT_ENTRY_SIZE) {
- retval = rmi_read_pdt_entry(rmi_dev, &pdt_entry, i);
- if (retval < 0)
- return retval;
+ return RMI_SCAN_DONE;
+ }
- if (RMI4_END_OF_PDT(pdt_entry.function_number))
- break;
- done = false;
+ /* F01 should always be on page 0. If we don't find it there, fail. */
+ return (!page) ? RMI_SCAN_CONTINUE : RMI_SCAN_ERROR;
+}
- if (pdt_entry.function_number == 0x01) {
- u16 cmd_addr = page_start +
- pdt_entry.command_base_addr;
- u8 cmd_buf = RMI_DEVICE_RESET_CMD;
- retval = rmi_write_block(rmi_dev, cmd_addr,
- &cmd_buf, 1);
- if (retval < 0) {
- dev_err(dev, "Initial reset failed. Code = %d.\n",
- retval);
- return retval;
- }
- mdelay(pdata->reset_delay_ms);
- done = true;
- has_f01 = true;
- break;
- }
- }
- }
+static int rmi_create_functions(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ int irq_count = 0;
+ int retval;
- if (!has_f01) {
- dev_warn(dev, "WARNING: Failed to find F01 for initial reset.\n");
- return -ENODEV;
- }
+ /*
+ * XXX need to make sure we create F01 first...
+ * XXX or do we? It might not be required in the updated structure.
+ */
+ retval = rmi_scan_pdt(rmi_dev, &irq_count, create_function);
+ if (retval)
+ return retval;
+
+ /*
+ * TODO: I think we need to count the IRQs before creating the
+ * functions.
+ */
+ data->irq_count = irq_count;
+ data->num_of_irq_regs = (irq_count + 7) / 8;
return 0;
}
-static int rmi_scan_pdt(struct rmi_device *rmi_dev)
+int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
+ int (*rmi_pdt_scan_clbk)(struct rmi_device *rmi_dev,
+ void *clbk_ctx, struct pdt_entry *entry, int page))
{
- struct rmi_driver_data *data;
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
struct pdt_entry pdt_entry;
int page;
- struct device *dev = &rmi_dev->dev;
- int irq_count = 0;
bool done = false;
int i;
- int retval;
-
- dev_dbg(dev, "Scanning PDT...\n");
+ int retval = RMI_SCAN_CONTINUE;
- data = dev_get_drvdata(&rmi_dev->dev);
+ /*
+ * TODO: With F01 and reflash as part of the core now, is this
+ * lock still required?
+ */
mutex_lock(&data->pdt_mutex);
for (page = 0; (page <= RMI4_MAX_PAGE) && !done; page++) {
@@ -655,35 +648,41 @@ static int rmi_scan_pdt(struct rmi_device *rmi_dev)
done = true;
for (i = pdt_start; i >= pdt_end; i -= RMI_PDT_ENTRY_SIZE) {
retval = rmi_read_pdt_entry(rmi_dev, &pdt_entry, i);
- if (retval < 0)
- goto error_exit;
+ if (retval < 0) {
+ retval = RMI_SCAN_ERROR;
+ goto exit_unlock;
+ }
if (RMI4_END_OF_PDT(pdt_entry.function_number))
break;
- dev_dbg(dev, "Found F%02X on page %#04x\n",
+ dev_dbg(&rmi_dev->dev, "Found F%02X on page %#04x\n",
pdt_entry.function_number, page);
done = false;
- // XXX need to make sure we create F01 first...
- retval = create_function(rmi_dev,
- &pdt_entry, &irq_count, page_start);
-
- if (retval)
- goto error_exit;
+ retval = rmi_pdt_scan_clbk(rmi_dev, ctx,
+ &pdt_entry, page);
+ if (retval < 0) {
+ goto exit_unlock;
+ } else if (retval == RMI_SCAN_DONE) {
+ done = true;
+ break;
+ }
+ }
+ if (data->f01_bootloader_mode) {
+ retval = 0;
+ goto exit_unlock;
}
- done = done || data->f01_bootloader_mode;
}
- data->irq_count = irq_count;
- data->num_of_irq_regs = (irq_count + 7) / 8;
- dev_dbg(dev, "%s: Done with PDT scan.\n", __func__);
+
retval = 0;
-error_exit:
+exit_unlock:
mutex_unlock(&data->pdt_mutex);
return retval;
}
+
#ifdef CONFIG_PM_SLEEP
static int rmi_driver_suspend(struct device *dev)
{
@@ -797,10 +796,15 @@ static int rmi_driver_probe(struct device *dev)
/*
* Right before a warm boot, the sensor might be in some unusual state,
- * such as F54 diagnostics, or F34 bootloader mode. In order to clear
- * the sensor to a known state, we issue a initial reset to clear any
+ * such as F54 diagnostics, or F34 bootloader mode after a firmware
+ * or configuration update. In order to clear the sensor to a known
+ * state and/or apply any updates, we issue a initial reset to clear any
* previous settings and force it into normal operation.
*
+ * We have to do this before actually building the PDT because
+ * the reflash updates (if any) might cause various registers to move
+ * around.
+ *
* For a number of reasons, this initial reset may fail to return
* within the specified time, but we'll still be able to bring up the
* driver normally after that failure. This occurs most commonly in
@@ -813,14 +817,14 @@ static int rmi_driver_probe(struct device *dev)
*/
if (!pdata->reset_delay_ms)
pdata->reset_delay_ms = DEFAULT_RESET_DELAY_MS;
- retval = rmi_initial_reset(rmi_dev);
+ retval = rmi_scan_pdt(rmi_dev, NULL, rmi_initial_reset);
if (retval)
dev_warn(dev, "RMI initial reset failed! Continuing in spite of this.\n");
- retval = rmi_scan_pdt(rmi_dev);
+ retval = rmi_create_functions(rmi_dev);
if (retval) {
- dev_err(dev, "PDT scan for %s failed with code %d.\n",
- pdata->sensor_name, retval);
+ dev_err(dev, "PDT scan for %s failed.\n", pdata->sensor_name);
+ retval = -ENODEV;
goto err_free_data;
}
@@ -949,7 +953,7 @@ static int rmi_driver_probe(struct device *dev)
return 0;
- err_free_data:
+err_free_data:
return retval;
}
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
index df9ddd8..2c1c63f 100644
--- a/drivers/input/rmi4/rmi_driver.h
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2013 Synaptics Incorporated
+ * Copyright (c) 2011-2014 Synaptics Incorporated
* Copyright (c) 2011 Unixphere
*
* This program is free software; you can redistribute it and/or modify it
@@ -87,14 +87,6 @@ struct rmi_driver_data {
void *data;
};
-#define RMI_PDT_ENTRY_SIZE 6
-#define RMI_PDT_FUNCTION_VERSION_MASK 0x60
-#define RMI_PDT_INT_SOURCE_COUNT_MASK 0x07
-
-#define PDT_START_SCAN_LOCATION 0x00e9
-#define PDT_END_SCAN_LOCATION 0x0005
-#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff)
-
struct pdt_entry {
u8 query_base_addr;
u8 command_base_addr;
@@ -105,8 +97,13 @@ struct pdt_entry {
u8 function_number;
};
-int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry,
- u16 pdt_address);
+#define RMI_SCAN_ERROR -1
+#define RMI_SCAN_CONTINUE 0
+#define RMI_SCAN_DONE 1
+
+int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
+ int (*rmi_pdt_scan_clbk)(struct rmi_device *rmi_dev,
+ void *clbk_ctx, struct pdt_entry *entry, int page));
bool rmi_is_physical_driver(struct device_driver *);
int rmi_register_physical_driver(void);
^ permalink raw reply related
* [PATCH] hid-microsoft: Add ID's for Surface Type/Touch Cover 2
From: Reyad Attiyat @ 2014-01-22 19:54 UTC (permalink / raw)
To: linux-input, linux-kernel, Benjamin Tissoires, Jiri Kosina
This patch fixes bug 64811 (https://bugzilla.kernel.org/show_bug.cgi?id=64811)
The Microsoft Surface Type/Touch cover 2 devices have the flag HID_DG_CONTACTID in their reports.This causes the device to bind to the hid-multitouch driver, which doesn't handle generic keyboard/mouse input events.
The patch adds the hardware id's of the device to hid-microsoft and to the HID special driver array, which makes the device get handled by hid-generic/hid-input properly.
Singed-off-by: Reyad Attiyat <reyad.attiyat@gmail.com>
Reviewed-by: Benjamin Tissoires<benjamin.tissoires@redhat.com>
---
drivers/hid/hid-core.c | 2 ++
drivers/hid/hid-ids.h | 2 ++
drivers/hid/hid-microsoft.c | 4 ++++
3 files changed, 8 insertions(+)
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 253fe23..b10f6d0 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1778,6 +1778,8 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_2) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TOUCH_COVER_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index f9304cb..bb77d22 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -611,6 +611,8 @@
#define USB_DEVICE_ID_MS_PRESENTER_8K_USB 0x0713
#define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K 0x0730
#define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500 0x076c
+#define USB_DEVICE_ID_MS_TOUCH_COVER_2 0x07a7
+#define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9
#define USB_VENDOR_ID_MOJO 0x8282
#define USB_DEVICE_ID_RETRO_ADAPTER 0x3201
diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c
index 551795b..277e545 100644
--- a/drivers/hid/hid-microsoft.c
+++ b/drivers/hid/hid-microsoft.c
@@ -207,6 +207,10 @@ static const struct hid_device_id ms_devices[] = {
.driver_data = MS_NOGET },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500),
.driver_data = MS_DUPLICATE_USAGES },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_2),
+ .driver_data = 0 },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TOUCH_COVER_2),
+ .driver_data = 0 },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
.driver_data = MS_PRESENTER },
--
1.8.4.2
^ permalink raw reply related
* Re: [PATCH 0/4] HID: implement new transport-driver callbacks
From: David Herrmann @ 2014-01-22 19:01 UTC (permalink / raw)
To: Frank Praznik; +Cc: open list:HID CORE LAYER, Jiri Kosina, Benjamin Tissoires
In-Reply-To: <1390416584-3667-1-git-send-email-frank.praznik@oh.rr.com>
Hi
On Wed, Jan 22, 2014 at 7:49 PM, Frank Praznik <frank.praznik@oh.rr.com> wrote:
> These patches are originally the work of David Herrmann who suggested that I
> update and submit them as their functionality is required for some pending
> patches to the hid-sony driver.
>
> These patches implement the SET/GET_REPORT and raw intr OUTPUT requests for all
> transport drivers. It adds two callbacks to the hid_ll_driver struct:
>
> int (*raw_request)(struct hid_device *hdev, unsigned char reportnum,
> __u8 *buf, size_t len, unsigned char rtype, int reqtype);
>
> int (*output_report)(struct hid_device *hdev, __u8 *buf, size_t len);
>
> along with the necessary support fuctions in the USBHID, and HIDP drivers.
>
> UHID is not converted yet.
Thanks for picking it up. As background, people should read my HID
summary which originally was part of this series:
http://cgit.freedesktop.org/~dvdhrm/linux/tree/Documentation/hid/hid-transport.txt?h=hid&id=86c08bb28302bb31dcd3b9aaf22b222f890397e0
Our current hid_output_raw_report() callbacks are implemented
differently in the USBHID, HIDP, I2CHID and UHID backends (I even
think they're all mutually different). We cannot easily change these
as drivers actually depend on the backends to do it differently.
Therefore, I proposed the raw_request() and output_report() functions.
raw_request() is basically the same as request() but takes a raw
buffer instead of an hid_report. output_report() is what HIDP
currently does with hid_output_raw_report() and sends the report as
asynchronous intr report.
The plan should be to use request(), raw_request() and output_report()
exclusively and carefully port drivers to use them. Once we're done,
we can remove hid_output_raw_report() (and any other legacy). This
should guarantee, that drivers can choose between ctrl-SET_REPORT and
intr-OUTPUT_REPORT messages without depending on the underlying
backend to choose the right one.
I hope that information helps.
Thanks
David
^ permalink raw reply
* [PATCH 3/4] HID: Add the transport-driver function to the uhid driver
From: Frank Praznik @ 2014-01-22 18:49 UTC (permalink / raw)
To: linux-input; +Cc: dh.herrmann, jkosina, Frank Praznik
In-Reply-To: <1390416584-3667-1-git-send-email-frank.praznik@oh.rr.com>
Add the uhid_output_report transport-driver function to the uhid driver.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
drivers/hid/uhid.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c
index cedc6da..f5a2b19 100644
--- a/drivers/hid/uhid.c
+++ b/drivers/hid/uhid.c
@@ -244,12 +244,39 @@ static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count,
return count;
}
+static int uhid_hid_output_report(struct hid_device *hid, __u8 *buf,
+ size_t count)
+{
+ struct uhid_device *uhid = hid->driver_data;
+ unsigned long flags;
+ struct uhid_event *ev;
+
+ if (count < 1 || count > UHID_DATA_MAX)
+ return -EINVAL;
+
+ ev = kzalloc(sizeof(*ev), GFP_KERNEL);
+ if (!ev)
+ return -ENOMEM;
+
+ ev->type = UHID_OUTPUT;
+ ev->u.output.size = count;
+ ev->u.output.rtype = UHID_OUTPUT_REPORT;
+ memcpy(ev->u.output.data, buf, count);
+
+ spin_lock_irqsave(&uhid->qlock, flags);
+ uhid_queue(uhid, ev);
+ spin_unlock_irqrestore(&uhid->qlock, flags);
+
+ return count;
+}
+
static struct hid_ll_driver uhid_hid_driver = {
.start = uhid_hid_start,
.stop = uhid_hid_stop,
.open = uhid_hid_open,
.close = uhid_hid_close,
.parse = uhid_hid_parse,
+ .output_report = uhid_hid_output_report,
};
#ifdef CONFIG_COMPAT
--
1.8.4.2
^ permalink raw reply related
* [PATCH 4/4] HID: Add the transport-driver functions to the HIDP driver.
From: Frank Praznik @ 2014-01-22 18:49 UTC (permalink / raw)
To: linux-input; +Cc: dh.herrmann, jkosina, Frank Praznik
In-Reply-To: <1390416584-3667-1-git-send-email-frank.praznik@oh.rr.com>
Add raw_request, set_raw_report and output_report transport-driver functions to
the HIDP driver.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
net/bluetooth/hidp/core.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 90 insertions(+)
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index 292e619..b062cee 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -353,6 +353,71 @@ err:
return ret;
}
+static int hidp_set_raw_report(struct hid_device *hid, unsigned char reportnum,
+ unsigned char *data, size_t count,
+ unsigned char report_type)
+{
+ struct hidp_session *session = hid->driver_data;
+ int ret;
+
+ switch (report_type) {
+ case HID_FEATURE_REPORT:
+ report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE;
+ break;
+ case HID_INPUT_REPORT:
+ report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_INPUT;
+ break;
+ case HID_OUTPUT_REPORT:
+ report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_OUPUT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (mutex_lock_interruptible(&session->report_mutex))
+ return -ERESTARTSYS;
+
+ /* Set up our wait, and send the report request to the device. */
+ data[0] = reportnum;
+ set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
+ ret = hidp_send_ctrl_message(session, report_type, data, count);
+ if (ret)
+ goto err;
+
+ /* Wait for the ACK from the device. */
+ while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags) &&
+ !atomic_read(&session->terminate)) {
+ int res;
+
+ res = wait_event_interruptible_timeout(session->report_queue,
+ !test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)
+ || atomic_read(&session->terminate),
+ 10*HZ);
+ if (res == 0) {
+ /* timeout */
+ ret = -EIO;
+ goto err;
+ }
+ if (res < 0) {
+ /* signal */
+ ret = -ERESTARTSYS;
+ goto err;
+ }
+ }
+
+ if (!session->output_report_success) {
+ ret = -EIO;
+ goto err;
+ }
+
+ ret = count;
+
+err:
+ clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
+ mutex_unlock(&session->report_mutex);
+ return ret;
+}
+
static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count,
unsigned char report_type)
{
@@ -411,6 +476,29 @@ err:
return ret;
}
+static int hidp_raw_request(struct hid_device *hid, unsigned char reportnum,
+ __u8 *buf, size_t len, unsigned char rtype,
+ int reqtype)
+{
+ switch (reqtype) {
+ case HID_REQ_GET_REPORT:
+ return hidp_get_raw_report(hid, reportnum, buf, len, rtype);
+ case HID_REQ_SET_REPORT:
+ return hidp_set_raw_report(hid, reportnum, buf, len, rtype);
+ default:
+ return -EIO;
+ }
+}
+
+static int hidp_output_report(struct hid_device *hid, __u8 *data, size_t count)
+{
+ struct hidp_session *session = hid->driver_data;
+
+ return hidp_send_intr_message(session,
+ HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT,
+ data, count);
+}
+
static void hidp_idle_timeout(unsigned long arg)
{
struct hidp_session *session = (struct hidp_session *) arg;
@@ -727,6 +815,8 @@ static struct hid_ll_driver hidp_hid_driver = {
.stop = hidp_stop,
.open = hidp_open,
.close = hidp_close,
+ .raw_request = hidp_raw_request,
+ .output_report = hidp_output_report,
.hidinput_input_event = hidp_hidinput_event,
};
--
1.8.4.2
^ permalink raw reply related
* [PATCH 1/4] HID: Add transport-driver callbacks to the hid_ll_driver struct
From: Frank Praznik @ 2014-01-22 18:49 UTC (permalink / raw)
To: linux-input; +Cc: dh.herrmann, jkosina, Frank Praznik
In-Reply-To: <1390416584-3667-1-git-send-email-frank.praznik@oh.rr.com>
Add raw_request and output_report callbacks to the hid_ll_driver struct.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
include/linux/hid.h | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 31b9d29..003cc8e 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -700,8 +700,14 @@ struct hid_ll_driver {
struct hid_report *report, int reqtype);
int (*wait)(struct hid_device *hdev);
- int (*idle)(struct hid_device *hdev, int report, int idle, int reqtype);
+ int (*raw_request) (struct hid_device *hdev, unsigned char reportnum,
+ __u8 *buf, size_t len, unsigned char rtype,
+ int reqtype);
+
+ int (*output_report) (struct hid_device *hdev, __u8 *buf, size_t len);
+
+ int (*idle)(struct hid_device *hdev, int report, int idle, int reqtype);
};
#define PM_HINT_FULLON 1<<5
--
1.8.4.2
^ permalink raw reply related
* [PATCH 2/4] HID: Add transport-driver functions to the USB HID interface.
From: Frank Praznik @ 2014-01-22 18:49 UTC (permalink / raw)
To: linux-input; +Cc: dh.herrmann, jkosina, Frank Praznik
In-Reply-To: <1390416584-3667-1-git-send-email-frank.praznik@oh.rr.com>
Add raw_request, set_raw_report and output_report transport-driver functions to
the USB HID driver.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
drivers/hid/usbhid/hid-core.c | 78 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 78 insertions(+)
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 44df131..f8ca312 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -884,6 +884,38 @@ static int usbhid_get_raw_report(struct hid_device *hid,
return ret;
}
+static int usbhid_set_raw_report(struct hid_device *hid, unsigned int reportnum,
+ __u8 *buf, size_t count, unsigned char rtype)
+{
+ struct usbhid_device *usbhid = hid->driver_data;
+ struct usb_device *dev = hid_to_usb_dev(hid);
+ struct usb_interface *intf = usbhid->intf;
+ struct usb_host_interface *interface = intf->cur_altsetting;
+ int ret, skipped_report_id = 0;
+
+ /* Byte 0 is the report number. Report data starts at byte 1.*/
+ buf[0] = reportnum;
+ if (buf[0] == 0x0) {
+ /* Don't send the Report ID */
+ buf++;
+ count--;
+ skipped_report_id = 1;
+ }
+
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ HID_REQ_SET_REPORT,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ ((rtype + 1) << 8) | reportnum,
+ interface->desc.bInterfaceNumber, buf, count,
+ USB_CTRL_SET_TIMEOUT);
+ /* count also the report id, if this was a numbered report. */
+ if (ret > 0 && skipped_report_id)
+ ret++;
+
+ return ret;
+}
+
+
static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count,
unsigned char report_type)
{
@@ -936,6 +968,36 @@ static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t co
return ret;
}
+static int usbhid_output_report(struct hid_device *hid, __u8 *buf, size_t count)
+{
+ struct usbhid_device *usbhid = hid->driver_data;
+ struct usb_device *dev = hid_to_usb_dev(hid);
+ int actual_length, skipped_report_id = 0, ret;
+
+ if (!usbhid->urbout)
+ return -EIO;
+
+ if (buf[0] == 0x0) {
+ /* Don't send the Report ID */
+ buf++;
+ count--;
+ skipped_report_id = 1;
+ }
+
+ ret = usb_interrupt_msg(dev, usbhid->urbout->pipe,
+ buf, count, &actual_length,
+ USB_CTRL_SET_TIMEOUT);
+ /* return the number of bytes transferred */
+ if (ret == 0) {
+ ret = actual_length;
+ /* count also the report id */
+ if (skipped_report_id)
+ ret++;
+ }
+
+ return ret;
+}
+
static void usbhid_restart_queues(struct usbhid_device *usbhid)
{
if (usbhid->urbout && !test_bit(HID_OUT_RUNNING, &usbhid->iofl))
@@ -1200,6 +1262,20 @@ static void usbhid_request(struct hid_device *hid, struct hid_report *rep, int r
}
}
+static int usbhid_raw_request(struct hid_device *hid, unsigned char reportnum,
+ __u8 *buf, size_t len, unsigned char rtype,
+ int reqtype)
+{
+ switch (reqtype) {
+ case HID_REQ_GET_REPORT:
+ return usbhid_get_raw_report(hid, reportnum, buf, len, rtype);
+ case HID_REQ_SET_REPORT:
+ return usbhid_set_raw_report(hid, reportnum, buf, len, rtype);
+ default:
+ return -EIO;
+ }
+}
+
static int usbhid_idle(struct hid_device *hid, int report, int idle,
int reqtype)
{
@@ -1223,6 +1299,8 @@ static struct hid_ll_driver usb_hid_driver = {
.power = usbhid_power,
.request = usbhid_request,
.wait = usbhid_wait_io,
+ .raw_request = usbhid_raw_request,
+ .output_report = usbhid_output_report,
.idle = usbhid_idle,
};
--
1.8.4.2
^ permalink raw reply related
* [PATCH 0/4] HID: implement new transport-driver callbacks
From: Frank Praznik @ 2014-01-22 18:49 UTC (permalink / raw)
To: linux-input; +Cc: dh.herrmann, jkosina, Frank Praznik
These patches are originally the work of David Herrmann who suggested that I
update and submit them as their functionality is required for some pending
patches to the hid-sony driver.
These patches implement the SET/GET_REPORT and raw intr OUTPUT requests for all
transport drivers. It adds two callbacks to the hid_ll_driver struct:
int (*raw_request)(struct hid_device *hdev, unsigned char reportnum,
__u8 *buf, size_t len, unsigned char rtype, int reqtype);
int (*output_report)(struct hid_device *hdev, __u8 *buf, size_t len);
along with the necessary support fuctions in the USBHID, and HIDP drivers.
UHID is not converted yet.
^ permalink raw reply
* [PATCH v4 1/2] input/uinput: replace breaks by goto out in uinput_ioctl_handler
From: Benjamin Tissoires @ 2014-01-22 17:23 UTC (permalink / raw)
To: Benjamin Tissoires, Dmitry Torokhov, David Herrmann,
Peter Hutterer, linux-input, linux-kernel
From: Benjamin Tisssoires <benjamin.tissoires@redhat.com>
The current implementation prevents us to add variable-length ioctl.
Use a bunch of gotos instead of break to allow us to do so.
No functional changes.
Signed-off-by: Benjamin Tisssoires <benjamin.tissoires@redhat.com>
---
changes since v3:
- brand new patch :)
drivers/input/misc/uinput.c | 56 ++++++++++++++++++++++-----------------------
1 file changed, 27 insertions(+), 29 deletions(-)
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index 7728359..d8ae08d 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -693,51 +693,51 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
switch (cmd) {
case UI_DEV_CREATE:
retval = uinput_create_device(udev);
- break;
+ goto out;
case UI_DEV_DESTROY:
uinput_destroy_device(udev);
- break;
+ goto out;
case UI_SET_EVBIT:
retval = uinput_set_bit(arg, evbit, EV_MAX);
- break;
+ goto out;
case UI_SET_KEYBIT:
retval = uinput_set_bit(arg, keybit, KEY_MAX);
- break;
+ goto out;
case UI_SET_RELBIT:
retval = uinput_set_bit(arg, relbit, REL_MAX);
- break;
+ goto out;
case UI_SET_ABSBIT:
retval = uinput_set_bit(arg, absbit, ABS_MAX);
- break;
+ goto out;
case UI_SET_MSCBIT:
retval = uinput_set_bit(arg, mscbit, MSC_MAX);
- break;
+ goto out;
case UI_SET_LEDBIT:
retval = uinput_set_bit(arg, ledbit, LED_MAX);
- break;
+ goto out;
case UI_SET_SNDBIT:
retval = uinput_set_bit(arg, sndbit, SND_MAX);
- break;
+ goto out;
case UI_SET_FFBIT:
retval = uinput_set_bit(arg, ffbit, FF_MAX);
- break;
+ goto out;
case UI_SET_SWBIT:
retval = uinput_set_bit(arg, swbit, SW_MAX);
- break;
+ goto out;
case UI_SET_PROPBIT:
retval = uinput_set_bit(arg, propbit, INPUT_PROP_MAX);
- break;
+ goto out;
case UI_SET_PHYS:
if (udev->state == UIST_CREATED) {
@@ -753,18 +753,18 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
kfree(udev->dev->phys);
udev->dev->phys = phys;
- break;
+ goto out;
case UI_BEGIN_FF_UPLOAD:
retval = uinput_ff_upload_from_user(p, &ff_up);
if (retval)
- break;
+ goto out;
req = uinput_request_find(udev, ff_up.request_id);
if (!req || req->code != UI_FF_UPLOAD ||
!req->u.upload.effect) {
retval = -EINVAL;
- break;
+ goto out;
}
ff_up.retval = 0;
@@ -775,65 +775,63 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
memset(&ff_up.old, 0, sizeof(struct ff_effect));
retval = uinput_ff_upload_to_user(p, &ff_up);
- break;
+ goto out;
case UI_BEGIN_FF_ERASE:
if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
retval = -EFAULT;
- break;
+ goto out;
}
req = uinput_request_find(udev, ff_erase.request_id);
if (!req || req->code != UI_FF_ERASE) {
retval = -EINVAL;
- break;
+ goto out;
}
ff_erase.retval = 0;
ff_erase.effect_id = req->u.effect_id;
if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) {
retval = -EFAULT;
- break;
+ goto out;
}
- break;
+ goto out;
case UI_END_FF_UPLOAD:
retval = uinput_ff_upload_from_user(p, &ff_up);
if (retval)
- break;
+ goto out;
req = uinput_request_find(udev, ff_up.request_id);
if (!req || req->code != UI_FF_UPLOAD ||
!req->u.upload.effect) {
retval = -EINVAL;
- break;
+ goto out;
}
req->retval = ff_up.retval;
uinput_request_done(udev, req);
- break;
+ goto out;
case UI_END_FF_ERASE:
if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
retval = -EFAULT;
- break;
+ goto out;
}
req = uinput_request_find(udev, ff_erase.request_id);
if (!req || req->code != UI_FF_ERASE) {
retval = -EINVAL;
- break;
+ goto out;
}
req->retval = ff_erase.retval;
uinput_request_done(udev, req);
- break;
-
- default:
- retval = -EINVAL;
+ goto out;
}
+ retval = -EINVAL;
out:
mutex_unlock(&udev->mutex);
return retval;
--
1.8.4.2
^ permalink raw reply related
* [PATCH v4 2/2] input/uinput: add UI_GET_SYSNAME ioctl to retrieve the sysfs path
From: Benjamin Tissoires @ 2014-01-22 17:24 UTC (permalink / raw)
To: Benjamin Tissoires, Dmitry Torokhov, David Herrmann,
Peter Hutterer, linux-input, linux-kernel
In-Reply-To: <1390411440-1158-1-git-send-email-benjamin.tissoires@redhat.com>
uinput is used in the xorg-integration-tests suite and in the wayland
test suite. These automated tests suites create many virtual input
devices and then hook something to read these newly created devices.
Currently, uinput does not provide the created input device, which means
that we rely on an heuristic to guess which input node was created.
The problem is that is heuristic is subjected to races between different
uinput devices or even with physical devices. Having a way to retrieve
the sysfs path allows us to find without any doubts the event node.
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
Hi guys,
a new version of this patch with a brand new commit message.
Happy reviewing :)
changes since v3:
- new commit message (I hope it will be better now)
- removed the -EAGAIN stuff thanks to the previous patch (not in v3)
- used "put_user(0, (char *)dest + len - 1);" instead of "copy_to_user(dest + len - 1, "\0", 1);"
- check if maxlen == 0
changes since v2:
- the ioctl returns only the device name, thus I renamed the ioctl to UI_GET_SYSNAME
- reordered uinput_str_to_user() arguments
- be sure to terminate the user string we send by \0
- abort if udev->state is not UIST_CREATED
- dropped the patch 1/2 (adding resolution to uinput) because I think David has
already it in one of his queues (ABS2 IIRC)
The corresponding libevdev patch is still here:
http://lists.freedesktop.org/archives/input-tools/2014-January/000757.html
Cheers,
Benjamin
drivers/input/misc/uinput.c | 42 ++++++++++++++++++++++++++++++++++++++++++
include/linux/uinput.h | 2 ++
include/uapi/linux/uinput.h | 13 ++++++++++++-
3 files changed, 56 insertions(+), 1 deletion(-)
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index d8ae08d..b51269f 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -20,6 +20,8 @@
* Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
*
* Changes/Revisions:
+ * 0.4 01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>)
+ * - add UI_GET_SYSNAME ioctl
* 0.3 09/04/2006 (Anssi Hannula <anssi.hannula@gmail.com>)
* - updated ff support for the changes in kernel interface
* - added MODULE_VERSION
@@ -670,6 +672,30 @@ static int uinput_ff_upload_from_user(const char __user *buffer,
__ret; \
})
+static int uinput_str_to_user(void __user *dest, const char *str,
+ unsigned int maxlen)
+{
+ int len, ret;
+
+ if (!str)
+ return -ENOENT;
+
+ if (maxlen == 0)
+ return -EINVAL;
+
+ len = strlen(str) + 1;
+ if (len > maxlen)
+ len = maxlen;
+
+ ret = copy_to_user(dest, str, len);
+ if (ret)
+ return -EFAULT;
+
+ /* force terminating '\0' */
+ ret = put_user(0, (char *)dest + len - 1);
+ return ret ? -EFAULT : len;
+}
+
static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
unsigned long arg, void __user *p)
{
@@ -679,6 +705,8 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
struct uinput_ff_erase ff_erase;
struct uinput_request *req;
char *phys;
+ const char *name;
+ unsigned int size;
retval = mutex_lock_interruptible(&udev->mutex);
if (retval)
@@ -831,6 +859,20 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
goto out;
}
+ size = _IOC_SIZE(cmd);
+
+ /* Now check variable-length commands */
+ switch (cmd & ~IOCSIZE_MASK) {
+ case UI_GET_SYSNAME(0):
+ if (udev->state != UIST_CREATED) {
+ retval = -ENOENT;
+ goto out;
+ }
+ name = dev_name(&udev->dev->dev);
+ retval = uinput_str_to_user(p, name, size);
+ goto out;
+ }
+
retval = -EINVAL;
out:
mutex_unlock(&udev->mutex);
diff --git a/include/linux/uinput.h b/include/linux/uinput.h
index 0a4487d..0994c0d 100644
--- a/include/linux/uinput.h
+++ b/include/linux/uinput.h
@@ -20,6 +20,8 @@
* Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
*
* Changes/Revisions:
+ * 0.4 01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>)
+ * - add UI_GET_SYSNAME ioctl
* 0.3 24/05/2006 (Anssi Hannula <anssi.hannulagmail.com>)
* - update ff support for the changes in kernel interface
* - add UINPUT_VERSION
diff --git a/include/uapi/linux/uinput.h b/include/uapi/linux/uinput.h
index fe46431..0389b48 100644
--- a/include/uapi/linux/uinput.h
+++ b/include/uapi/linux/uinput.h
@@ -20,6 +20,8 @@
* Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
*
* Changes/Revisions:
+ * 0.4 01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>)
+ * - add UI_GET_SYSNAME ioctl
* 0.3 24/05/2006 (Anssi Hannula <anssi.hannulagmail.com>)
* - update ff support for the changes in kernel interface
* - add UINPUT_VERSION
@@ -35,7 +37,7 @@
#include <linux/types.h>
#include <linux/input.h>
-#define UINPUT_VERSION 3
+#define UINPUT_VERSION 4
struct uinput_ff_upload {
@@ -73,6 +75,15 @@ struct uinput_ff_erase {
#define UI_BEGIN_FF_ERASE _IOWR(UINPUT_IOCTL_BASE, 202, struct uinput_ff_erase)
#define UI_END_FF_ERASE _IOW(UINPUT_IOCTL_BASE, 203, struct uinput_ff_erase)
+/**
+ * UI_GET_SYSNAME - get the sysfs name of the created uinput device
+ *
+ * @return the sysfs name of the created virtual input device.
+ * The complete sysfs path is then /sys/devices/virtual/input/--NAME--
+ * Usually, it is in the form "inputN"
+ */
+#define UI_GET_SYSNAME(len) _IOC(_IOC_READ, UINPUT_IOCTL_BASE, 300, len)
+
/*
* To write a force-feedback-capable driver, the upload_effect
* and erase_effect callbacks in input_dev must be implemented.
--
1.8.4.2
^ permalink raw reply related
* [PATCH v4] ims-pcu: Add commands supported by the new version of the FW
From: Andrey Smirnov @ 2014-01-22 17:06 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>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/input/misc/ims-pcu.c | 260 +++++++++++++++++++++++++++++++++++++++++--
1 file changed, 251 insertions(+), 9 deletions(-)
diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
index e204f26..0306e8d 100644
--- a/drivers/input/misc/ims-pcu.c
+++ b/drivers/input/misc/ims-pcu.c
@@ -51,6 +51,8 @@ struct ims_pcu_backlight {
#define IMS_PCU_BL_VERSION_LEN (9 + 1)
#define IMS_PCU_BL_RESET_REASON_LEN (2 + 1)
+#define IMS_PCU_PCU_B_DEVICE_ID 5
+
#define IMS_PCU_BUF_SIZE 128
struct ims_pcu {
@@ -68,6 +70,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 +376,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 +396,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 */
@@ -1226,7 +1236,7 @@ static struct attribute *ims_pcu_attrs[] = {
&dev_attr_reset_device.attr,
&dev_attr_update_firmware.attr,
&dev_attr_update_firmware_status.attr,
- NULL
+ NULL,
};
static umode_t ims_pcu_is_attr_visible(struct kobject *kobj,
@@ -1256,6 +1266,225 @@ static struct attribute_group ims_pcu_attr_group = {
.attrs = ims_pcu_attrs,
};
+/* Support for a separate OFN attribute group */
+
+#define OFN_REG_RESULT_OFFSET 2
+
+static int ims_pcu_read_ofn_config(struct ims_pcu *pcu, u8 addr, u8 *data)
+{
+ int error;
+ s16 result;
+
+ error = ims_pcu_execute_command(pcu, OFN_GET_CONFIG,
+ &addr, sizeof(addr));
+ if (error)
+ return error;
+
+ result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET);
+ if (result < 0)
+ return -EIO;
+
+ /* We only need LSB */
+ *data = pcu->cmd_buf[OFN_REG_RESULT_OFFSET];
+ return 0;
+}
+
+static int ims_pcu_write_ofn_config(struct ims_pcu *pcu, u8 addr, u8 data)
+{
+ u8 buffer[] = { addr, data };
+ int error;
+ s16 result;
+
+ error = ims_pcu_execute_command(pcu, OFN_SET_CONFIG,
+ &buffer, sizeof(buffer));
+ if (error)
+ return error;
+
+ result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET);
+ if (result < 0)
+ return -EIO;
+
+ return 0;
+}
+
+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;
+ u8 data;
+
+ mutex_lock(&pcu->cmd_mutex);
+ error = ims_pcu_read_ofn_config(pcu, pcu->ofn_reg_addr, &data);
+ mutex_unlock(&pcu->cmd_mutex);
+
+ if (error)
+ return error;
+
+ return scnprintf(buf, PAGE_SIZE, "%x\n", data);
+}
+
+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;
+ u8 value;
+
+ error = kstrtou8(buf, 0, &value);
+ if (error)
+ return error;
+
+ mutex_lock(&pcu->cmd_mutex);
+ error = ims_pcu_write_ofn_config(pcu, pcu->ofn_reg_addr, value);
+ mutex_unlock(&pcu->cmd_mutex);
+
+ return error ?: count;
+}
+
+static DEVICE_ATTR(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;
+ u8 value;
+
+ error = kstrtou8(buf, 0, &value);
+ if (error)
+ return error;
+
+ mutex_lock(&pcu->cmd_mutex);
+ pcu->ofn_reg_addr = value;
+ mutex_unlock(&pcu->cmd_mutex);
+
+ return error ?: count;
+}
+
+static DEVICE_ATTR(reg_addr, S_IRUGO | S_IWUSR,
+ ims_pcu_ofn_reg_addr_show, ims_pcu_ofn_reg_addr_store);
+
+struct ims_pcu_ofn_bit_attribute {
+ struct device_attribute dattr;
+ u8 addr;
+ u8 nr;
+};
+
+static ssize_t ims_pcu_ofn_bit_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);
+ struct ims_pcu_ofn_bit_attribute *attr =
+ container_of(dattr, struct ims_pcu_ofn_bit_attribute, dattr);
+ int error;
+ u8 data;
+
+ mutex_lock(&pcu->cmd_mutex);
+ error = ims_pcu_read_ofn_config(pcu, attr->addr, &data);
+ mutex_unlock(&pcu->cmd_mutex);
+
+ if (error)
+ return error;
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", !!(data & (1 << attr->nr)));
+}
+
+static ssize_t ims_pcu_ofn_bit_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);
+ struct ims_pcu_ofn_bit_attribute *attr =
+ container_of(dattr, struct ims_pcu_ofn_bit_attribute, dattr);
+ int error;
+ int value;
+ u8 data;
+
+ error = kstrtoint(buf, 0, &value);
+ if (error)
+ return error;
+
+ if (value > 1)
+ return -EINVAL;
+
+ mutex_lock(&pcu->cmd_mutex);
+
+ error = ims_pcu_read_ofn_config(pcu, attr->addr, &data);
+ if (!error) {
+ if (value)
+ data |= 1U << attr->nr;
+ else
+ data &= ~(1U << attr->nr);
+
+ error = ims_pcu_write_ofn_config(pcu, attr->addr, data);
+ }
+
+ mutex_unlock(&pcu->cmd_mutex);
+
+ return error ?: count;
+}
+
+#define IMS_PCU_OFN_BIT_ATTR(_field, _addr, _nr) \
+struct ims_pcu_ofn_bit_attribute ims_pcu_ofn_attr_##_field = { \
+ .dattr = __ATTR(_field, S_IWUSR | S_IRUGO, \
+ ims_pcu_ofn_bit_show, ims_pcu_ofn_bit_store), \
+ .addr = _addr, \
+ .nr = _nr, \
+}
+
+static IMS_PCU_OFN_BIT_ATTR(engine_enable, 0x60, 7);
+static IMS_PCU_OFN_BIT_ATTR(speed_enable, 0x60, 6);
+static IMS_PCU_OFN_BIT_ATTR(assert_enable, 0x60, 5);
+static IMS_PCU_OFN_BIT_ATTR(xyquant_enable, 0x60, 4);
+static IMS_PCU_OFN_BIT_ATTR(xyscale_enable, 0x60, 1);
+
+static IMS_PCU_OFN_BIT_ATTR(scale_x2, 0x63, 6);
+static IMS_PCU_OFN_BIT_ATTR(scale_y2, 0x63, 7);
+
+static struct attribute *ims_pcu_ofn_attrs[] = {
+ &dev_attr_reg_data.attr,
+ &dev_attr_reg_addr.attr,
+ &ims_pcu_ofn_attr_engine_enable.dattr.attr,
+ &ims_pcu_ofn_attr_speed_enable.dattr.attr,
+ &ims_pcu_ofn_attr_assert_enable.dattr.attr,
+ &ims_pcu_ofn_attr_xyquant_enable.dattr.attr,
+ &ims_pcu_ofn_attr_xyscale_enable.dattr.attr,
+ &ims_pcu_ofn_attr_scale_x2.dattr.attr,
+ &ims_pcu_ofn_attr_scale_y2.dattr.attr,
+ NULL
+};
+
+static struct attribute_group ims_pcu_ofn_attr_group = {
+ .name = "ofn",
+ .attrs = ims_pcu_ofn_attrs,
+};
+
static void ims_pcu_irq(struct urb *urb)
{
struct ims_pcu *pcu = urb->context;
@@ -1624,7 +1853,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 +1861,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 +1873,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;
}
@@ -1655,11 +1883,21 @@ static int ims_pcu_init_application_mode(struct ims_pcu *pcu)
/* Device appears to be operable, complete initialization */
pcu->device_no = atomic_inc_return(&device_no) - 1;
+ /*
+ PCU-B devices, both GEN_1 and GEN_2 do not have OFN sensor
+ */
+ if (pcu->device_id != IMS_PCU_PCU_B_DEVICE_ID) {
+ error = sysfs_create_group(&pcu->dev->kobj,
+ &ims_pcu_ofn_attr_group);
+ if (error)
+ return error;
+ }
+
error = ims_pcu_setup_backlight(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;
@@ -1674,10 +1912,10 @@ static int ims_pcu_init_application_mode(struct ims_pcu *pcu)
return 0;
-err_destroy_backlight:
- ims_pcu_destroy_backlight(pcu);
err_destroy_buttons:
ims_pcu_destroy_buttons(pcu);
+err_destroy_backlight:
+ ims_pcu_destroy_backlight(pcu);
return error;
}
@@ -1691,6 +1929,10 @@ static void ims_pcu_destroy_application_mode(struct ims_pcu *pcu)
ims_pcu_destroy_gamepad(pcu);
ims_pcu_destroy_buttons(pcu);
ims_pcu_destroy_backlight(pcu);
+
+ if (pcu->device_id != IMS_PCU_PCU_B_DEVICE_ID)
+ sysfs_remove_group(&pcu->dev->kobj,
+ &ims_pcu_ofn_attr_group);
}
}
--
1.8.3.2
^ permalink raw reply related
* Re: [PATCH v3] ims-pcu: Add commands supported by the new version of the FW
From: Andrey Smirnov @ 2014-01-22 16:58 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input, linux-kernel
In-Reply-To: <20140122164750.GA5080@core.coreip.homeip.net>
Sorry for the noise, had an old copy of the file in my out of the tree
build set-up when I was doing the testing. I will post updated version
shortly.
Thanks,
On Wed, Jan 22, 2014 at 8:47 AM, Dmitry Torokhov
<dmitry.torokhov@gmail.com> wrote:
> On Wed, Jan 22, 2014 at 05:20:25AM -0800, Andrey Smirnov wrote:
>> +static int ims_pcu_write_ofn_config(struct ims_pcu *pcu, u8 addr, u8 data)
>> +{
>> + u8 buffer[] = { addr, data };
>> + int error;
>> + u16 result;
>> +
>> + error = ims_pcu_execute_command(pcu, OFN_SET_CONFIG,
>> + &buffer, sizeof(buffer));
>> + if (error)
>> + return error;
>> +
>> + result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET);
>> + if ((result < 0)
>
> I am fairly certain this does not even compile, but even if it did,
> result is declared u16 so even if you do the cast above the comparison
> will always be false.
>
> Thanks.
>
> --
> Dmitry
^ permalink raw reply
* Re: [PATCH v3] ims-pcu: Add commands supported by the new version of the FW
From: Dmitry Torokhov @ 2014-01-22 16:47 UTC (permalink / raw)
To: Andrey Smirnov; +Cc: linux-input, linux-kernel
In-Reply-To: <1390396825-10255-1-git-send-email-andrew.smirnov@gmail.com>
On Wed, Jan 22, 2014 at 05:20:25AM -0800, Andrey Smirnov wrote:
> +static int ims_pcu_write_ofn_config(struct ims_pcu *pcu, u8 addr, u8 data)
> +{
> + u8 buffer[] = { addr, data };
> + int error;
> + u16 result;
> +
> + error = ims_pcu_execute_command(pcu, OFN_SET_CONFIG,
> + &buffer, sizeof(buffer));
> + if (error)
> + return error;
> +
> + result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET);
> + if ((result < 0)
I am fairly certain this does not even compile, but even if it did,
result is declared u16 so even if you do the cast above the comparison
will always be false.
Thanks.
--
Dmitry
^ permalink raw reply
* Re: [PATCH] Add HID's to hid-microsoft driver of Surface Type/Touch Cover 2 to fix bug
From: Benjamin Tissoires @ 2014-01-22 15:07 UTC (permalink / raw)
To: Reyad Attiyat; +Cc: linux-input, linux-kernel@vger.kernel.org, Jiri Kosina
In-Reply-To: <CA+BWVUQq4UNwSsAUZaimqnV3DZqPRQ==wx+X2XSojhwVzvpUZg@mail.gmail.com>
Hi Reyad,
On Wed, Jan 22, 2014 at 12:05 AM, Reyad Attiyat <reyad.attiyat@gmail.com> wrote:
> Hello Benjamin,
>
>>>
>>> Hi,
>>>
>>> Thanks for reminding me of hid_have_special_driver[]. I noticed that
>>> this device has the HID_DG_CONTACTID and in the comment of the
>>> hid_have_sepcial_driver[]
>>>
>>> * Please note that for multitouch devices (driven by hid-multitouch driver),
>>> * there is a proper autodetection and autoloading in place (based on presence
>>> * of HID_DG_CONTACTID), so those devices don't need to be added to this list,
>>> * as we are doing the right thing in hid_scan_usage().
>>>
>>> This device should not be driven by hid-multitouch as it does not
>>> handle keyboard/mouse input devices.
>>> I submitted a new patch below with it added. I believe it should still
>>> be part of this array, in case this kind of implementation is
>>> fixed/updated.
>>
>> This implementation is perfectly fine (I am referring to the "fixed/updated"):
>> - if your device should be driven by hid-multitouch, then you _don't_
>> add it to hid_have_special_driver
>> - if your device should not be driven by hid-multitouch, then you
>> _need_ to add it to hid_have_special_driver.
>>
>> Adding the device to hid_have_special_driver prevents the detection of
>> the group HID_GRP_MULTITOUCH, so you will not end with a race between
>> hid-multitouch and your special hid driver.
>>
>
> Thanks for clearing that up. I understand the proper use of this array
> now, under this circumstance and am glad to know that there will be no
> race when added.
>
>>>
>>> From 291742873dcf181faf9657b41279487f31302c73 Mon Sep 17 00:00:00 2001
>>> From: Reyad Attiyat <reyad.attiyat@gmail.com>
>>> Date: Tue, 21 Jan 2014 01:22:25 -0600
>>> Subject: [PATCH 1/1] Added in HID's for Microsoft Surface Type/Touch cover 2.
>>> This is to fix bug 64811 where this device is detected as a multitouch device
>>>
>>
>> You are missing a commit message here (the first message you sent
>> would fit perfectly here).
>>
>
> Sorry about that, I'm new to submitting patches to these mailing lists.
>
>> Other than that, I played a little with the report descriptor pointed
>> in the bugzilla.
>>
>> I think I will be able to handle this touch cover in hid-multitouch,
>> but that would require more testings/debugging. Microsoft seems to
>> have implemented an indirect (dual) touchpad here, but until we know
>> which mode we should put it into, it's going to be tricky to set it up
>> correctly.
>>
>> One last thing, in the bugzilla, in the comment 2 you say: "I still
>> have issues with the type cover 2 even with this fix". Are you still
>> experiencing those disconnection? If so, maybe we should switch to
>> hid-multitouch at some point.
>>
> I tried some patches that I think you posted to hid-input about hid-multitouch.
> The patches added in support for function callbacks to allow for a
> generic protocol.
> This worked after I changed mt_input_mapping() to set the protocol to
> mt_protocol_generic
>
> 851 * such as Mouse that might have the same GenericDesktop usages. */
> 852 if (field->application != HID_DG_TOUCHSCREEN &&
> 853 field->application != HID_DG_PEN &&
> 854 field->application != HID_DG_TOUCHPAD)
> 855 td->protocols[report_id] = mt_protocol_generic;
yep, I was referring to this patch series. Thanks for testing :)
>
> I still experience the disconnects with both of these solutions. Do
> you have any idea what could cause this?
Well, my first thought may be a mechanical issue (don't know if the
cover is still using magnets).
As for the software/firmware issue, I can think of two possibilities right now:
- the device expects us to empty its whole data queues, and we don't.
So after some time, it resets itself. I remember on the bug report of
having seen several USB devices, so maybe one of them is ignored and
it does not like it
- firmware issue: the device has been properly tested/certified in a
specific mode (the feature INPUT_MODE shows a maximal of 10,
multitouch devices generally have only 2 modes, not 10). So if we are
not in the same mode it is under Windows, then we are screwed.
> It seems to happen when I'm typing fast or holding a key. I'm guessing
> the only way to fix this properly is
> to snoop USB packets in Windows to see how the device is handled there.
Yep, that seems like a plan.
> Another bug is the device stays on, lit, in standby mode.
I would say that means that the suspend do not ask the usb keyboard to
go to sleep so that you can wake your device up by hitting a key (I
guess, I am not 100% sure regarding suspend and PM). Maybe play with
the suspend/resume callbacks and see if they are called.
>
> What do you think is the best solution to take? By that I mean should
> I keep the patch as part of hid-microsoft?
Yep. For the time being, keep it under hid-microsoft. If I manage to
convince myself to resubmit the patches I sent last month (which I
definitively should do), then we can think at switching it to
hid-multitouch if there is a gain (touchpad reporting full touches,
not mouse emulation, and/or fix the disconnect issue).
However, I don't plan to buy a surface 2 soon. I will not be able to
debug it easily, so you will be on your own :) Still, do not hesitate
to send me usb captures or hid-record[1] logs if you need a hand.
Cheers,
Benjamin
[1] http://bentiss.github.io/hid-replay-docs/
^ permalink raw reply
* Re: [PATCH] drivers/hid/wacom: fixed coding style issues
From: Rob Schroer @ 2014-01-22 14:48 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: Joe Perches, linux-input, linux-kernel
In-Reply-To: <20140122064045.GA4143@core.coreip.homeip.net>
On Tue, Jan 21, 2014 at 10:40:45PM -0800, Dmitry Torokhov wrote:
> I think this should be
>
> error = kstrtoint(buf, 10, &new_speed);
> if (error)
> return error;
>
I just lost my SSD/branch, so here is a fresh patch for mainline:
Fixed some coding style issues, removed unessecary spaces, added
linebreaks, converted an occurence of sscanf to kstrtoint.
Signed-off-by: Robin Schroer <sulamiification@gmail.com>
---
drivers/hid/hid-wacom.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c
index 60c75dc..c7f9d1c 100644
--- a/drivers/hid/hid-wacom.c
+++ b/drivers/hid/hid-wacom.c
@@ -77,8 +77,8 @@ static void wacom_scramble(__u8 *image)
__u16 mask;
__u16 s1;
__u16 s2;
- __u16 r1 ;
- __u16 r2 ;
+ __u16 r1;
+ __u16 r2;
__u16 r;
__u8 buf[256];
int i, w, x, y, z;
@@ -336,7 +336,8 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed)
switch (hdev->product) {
case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH:
- rep_data[0] = 0x03 ; rep_data[1] = 0x00;
+ rep_data[0] = 0x03;
+ rep_data[1] = 0x00;
limit = 3;
do {
ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
@@ -403,9 +404,11 @@ static ssize_t wacom_store_speed(struct device *dev,
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
int new_speed;
+ int error;
- if (sscanf(buf, "%1d", &new_speed ) != 1)
- return -EINVAL;
+ error = kstrtoint(buf, 10, &new_speed);
+ if (error)
+ return error;
if (new_speed == 0 || new_speed == 1) {
wacom_set_features(hdev, new_speed);
--
1.8.4.2
--
Robin
^ permalink raw reply related
* [PATCH v3] ims-pcu: Add commands supported by the new version of the FW
From: Andrey Smirnov @ 2014-01-22 13:20 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>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/input/misc/ims-pcu.c | 260 +++++++++++++++++++++++++++++++++++++++++--
1 file changed, 251 insertions(+), 9 deletions(-)
diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
index e204f26..1d084d9 100644
--- a/drivers/input/misc/ims-pcu.c
+++ b/drivers/input/misc/ims-pcu.c
@@ -51,6 +51,8 @@ struct ims_pcu_backlight {
#define IMS_PCU_BL_VERSION_LEN (9 + 1)
#define IMS_PCU_BL_RESET_REASON_LEN (2 + 1)
+#define IMS_PCU_PCU_B_DEVICE_ID 5
+
#define IMS_PCU_BUF_SIZE 128
struct ims_pcu {
@@ -68,6 +70,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 +376,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 +396,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 */
@@ -1226,7 +1236,7 @@ static struct attribute *ims_pcu_attrs[] = {
&dev_attr_reset_device.attr,
&dev_attr_update_firmware.attr,
&dev_attr_update_firmware_status.attr,
- NULL
+ NULL,
};
static umode_t ims_pcu_is_attr_visible(struct kobject *kobj,
@@ -1256,6 +1266,225 @@ static struct attribute_group ims_pcu_attr_group = {
.attrs = ims_pcu_attrs,
};
+/* Support for a separate OFN attribute group */
+
+#define OFN_REG_RESULT_OFFSET 2
+
+static int ims_pcu_read_ofn_config(struct ims_pcu *pcu, u8 addr, u8 *data)
+{
+ int error;
+ s16 result;
+
+ error = ims_pcu_execute_command(pcu, OFN_GET_CONFIG,
+ &addr, sizeof(addr));
+ if (error)
+ return error;
+
+ result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET);
+ if (result < 0)
+ return -EIO;
+
+ /* We only need LSB */
+ *data = pcu->cmd_buf[OFN_REG_RESULT_OFFSET];
+ return 0;
+}
+
+static int ims_pcu_write_ofn_config(struct ims_pcu *pcu, u8 addr, u8 data)
+{
+ u8 buffer[] = { addr, data };
+ int error;
+ u16 result;
+
+ error = ims_pcu_execute_command(pcu, OFN_SET_CONFIG,
+ &buffer, sizeof(buffer));
+ if (error)
+ return error;
+
+ result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET);
+ if ((result < 0)
+ return -EIO;
+
+ return 0;
+}
+
+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;
+ u8 data;
+
+ mutex_lock(&pcu->cmd_mutex);
+ error = ims_pcu_read_ofn_config(pcu, pcu->ofn_reg_addr, &data);
+ mutex_unlock(&pcu->cmd_mutex);
+
+ if (error)
+ return error;
+
+ return scnprintf(buf, PAGE_SIZE, "%x\n", data);
+}
+
+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;
+ u8 value;
+
+ error = kstrtou8(buf, 0, &value);
+ if (error)
+ return error;
+
+ mutex_lock(&pcu->cmd_mutex);
+ error = ims_pcu_write_ofn_config(pcu, pcu->ofn_reg_addr, value);
+ mutex_unlock(&pcu->cmd_mutex);
+
+ return error ?: count;
+}
+
+static DEVICE_ATTR(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;
+ u8 value;
+
+ error = kstrtou8(buf, 0, &value);
+ if (error)
+ return error;
+
+ mutex_lock(&pcu->cmd_mutex);
+ pcu->ofn_reg_addr = value;
+ mutex_unlock(&pcu->cmd_mutex);
+
+ return error ?: count;
+}
+
+static DEVICE_ATTR(reg_addr, S_IRUGO | S_IWUSR,
+ ims_pcu_ofn_reg_addr_show, ims_pcu_ofn_reg_addr_store);
+
+struct ims_pcu_ofn_bit_attribute {
+ struct device_attribute dattr;
+ u8 addr;
+ u8 nr;
+};
+
+static ssize_t ims_pcu_ofn_bit_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);
+ struct ims_pcu_ofn_bit_attribute *attr =
+ container_of(dattr, struct ims_pcu_ofn_bit_attribute, dattr);
+ int error;
+ u8 data;
+
+ mutex_lock(&pcu->cmd_mutex);
+ error = ims_pcu_read_ofn_config(pcu, attr->addr, &data);
+ mutex_unlock(&pcu->cmd_mutex);
+
+ if (error)
+ return error;
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", !!(data & (1 << attr->nr)));
+}
+
+static ssize_t ims_pcu_ofn_bit_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);
+ struct ims_pcu_ofn_bit_attribute *attr =
+ container_of(dattr, struct ims_pcu_ofn_bit_attribute, dattr);
+ int error;
+ int value;
+ u8 data;
+
+ error = kstrtoint(buf, 0, &value);
+ if (error)
+ return error;
+
+ if (value > 1)
+ return -EINVAL;
+
+ mutex_lock(&pcu->cmd_mutex);
+
+ error = ims_pcu_read_ofn_config(pcu, attr->addr, &data);
+ if (!error) {
+ if (value)
+ data |= 1U << attr->nr;
+ else
+ data &= ~(1U << attr->nr);
+
+ error = ims_pcu_write_ofn_config(pcu, attr->addr, data);
+ }
+
+ mutex_unlock(&pcu->cmd_mutex);
+
+ return error ?: count;
+}
+
+#define IMS_PCU_OFN_BIT_ATTR(_field, _addr, _nr) \
+struct ims_pcu_ofn_bit_attribute ims_pcu_ofn_attr_##_field = { \
+ .dattr = __ATTR(_field, S_IWUSR | S_IRUGO, \
+ ims_pcu_ofn_bit_show, ims_pcu_ofn_bit_store), \
+ .addr = _addr, \
+ .nr = _nr, \
+}
+
+static IMS_PCU_OFN_BIT_ATTR(engine_enable, 0x60, 7);
+static IMS_PCU_OFN_BIT_ATTR(speed_enable, 0x60, 6);
+static IMS_PCU_OFN_BIT_ATTR(assert_enable, 0x60, 5);
+static IMS_PCU_OFN_BIT_ATTR(xyquant_enable, 0x60, 4);
+static IMS_PCU_OFN_BIT_ATTR(xyscale_enable, 0x60, 1);
+
+static IMS_PCU_OFN_BIT_ATTR(scale_x2, 0x63, 6);
+static IMS_PCU_OFN_BIT_ATTR(scale_y2, 0x63, 7);
+
+static struct attribute *ims_pcu_ofn_attrs[] = {
+ &dev_attr_reg_data.attr,
+ &dev_attr_reg_addr.attr,
+ &ims_pcu_ofn_attr_engine_enable.dattr.attr,
+ &ims_pcu_ofn_attr_speed_enable.dattr.attr,
+ &ims_pcu_ofn_attr_assert_enable.dattr.attr,
+ &ims_pcu_ofn_attr_xyquant_enable.dattr.attr,
+ &ims_pcu_ofn_attr_xyscale_enable.dattr.attr,
+ &ims_pcu_ofn_attr_scale_x2.dattr.attr,
+ &ims_pcu_ofn_attr_scale_y2.dattr.attr,
+ NULL
+};
+
+static struct attribute_group ims_pcu_ofn_attr_group = {
+ .name = "ofn",
+ .attrs = ims_pcu_ofn_attrs,
+};
+
static void ims_pcu_irq(struct urb *urb)
{
struct ims_pcu *pcu = urb->context;
@@ -1624,7 +1853,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 +1861,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 +1873,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;
}
@@ -1655,11 +1883,21 @@ static int ims_pcu_init_application_mode(struct ims_pcu *pcu)
/* Device appears to be operable, complete initialization */
pcu->device_no = atomic_inc_return(&device_no) - 1;
+ /*
+ PCU-B devices, both GEN_1 and GEN_2 do not have OFN sensor
+ */
+ if (pcu->device_id != IMS_PCU_PCU_B_DEVICE_ID) {
+ error = sysfs_create_group(&pcu->dev->kobj,
+ &ims_pcu_ofn_attr_group);
+ if (error)
+ return error;
+ }
+
error = ims_pcu_setup_backlight(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;
@@ -1674,10 +1912,10 @@ static int ims_pcu_init_application_mode(struct ims_pcu *pcu)
return 0;
-err_destroy_backlight:
- ims_pcu_destroy_backlight(pcu);
err_destroy_buttons:
ims_pcu_destroy_buttons(pcu);
+err_destroy_backlight:
+ ims_pcu_destroy_backlight(pcu);
return error;
}
@@ -1691,6 +1929,10 @@ static void ims_pcu_destroy_application_mode(struct ims_pcu *pcu)
ims_pcu_destroy_gamepad(pcu);
ims_pcu_destroy_buttons(pcu);
ims_pcu_destroy_backlight(pcu);
+
+ if (pcu->device_id != IMS_PCU_PCU_B_DEVICE_ID)
+ sysfs_remove_group(&pcu->dev->kobj,
+ &ims_pcu_ofn_attr_group);
}
}
--
1.8.3.2
^ permalink raw reply related
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