Linux Input/HID development
 help / color / mirror / Atom feed
* [PATCH 0/7] Input: xpad: Fix wireless controller connection and leds
From: Greg Kroah-Hartman @ 2014-01-31 13:03 UTC (permalink / raw)
  To: dmitry.torokhov; +Cc: linux-input

Here's a series of patches from Pierre-Loup and I that rework the xpad
driver to fix the issue where when a wireless xpad controller is plugged
in, 4 joystick devices are created, no matter how many are really
attached to the system.  Pierre-Loup's patches fix this by dynamically
creating the devices only when they are found by the wireless
controller.

Bonus is that the LEDs now work correctly with this series too, when
mixing wireless and wired, we can properly identify which device is
which on the X-LED.  We also now name the LED device based on the
joystick id, not on an atomic number that keeps incrementing, never
decrementing when removed from the system.

Note, patch 4/7 is a crazy hack to get the minor number of the joystick
id that we have registered with the system.  Pierre-Loup came up with
this, and I really can't figure out any other way to do it, the joydev
layer doesn't even know this information, otherwise I would have added
it to that layer so the xpad driver could call it.  Am I missing
something here that we could do instead?

The first patch in the series fixes a really annoying bug when plugging
in one of these controllers to a USB 3 system.  The URB type is
incorrect so the xhci driver complains about it, rightly so.  I think
the original code was incorrect, but left it alone incase there is
really a crazy device with a bulk endpoint instead of interrupt.

Also, even with this series applied, the xpad driver needs a bunch of
work.  The LED device seems pointless as it doesn't actually work, and
given that the number/name of the LED device was impossible to actually
map to the proper xpad device, there's no way userspace code could ever
actually use it.  I think it should be removed entirely.  There's also
some other USB things that should be cleaned up, the bulk urb doesn't
need to be always running (with lots of disconnect/connect cycles you
can get a warning about it running when trying to submit it again), and
the urb callbacks seem a bit strange.  I'll work on those issues next...

This series was based on a larger patch from Pierre-Loup that I broke up
into pieces, and added some of my own where needed to resolve other
issues.


^ permalink raw reply

* Re: [PATCH RFC] Input: Add Microchip AR1021 i2c touchscreen
From: Christian Gmeiner @ 2014-01-31 11:40 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input
In-Reply-To: <20140131010131.GA29566@core.coreip.homeip.net>

Hi Dmitry.


> On Thu, Jan 30, 2014 at 10:29:45AM +0100, Christian Gmeiner wrote:
>> This driver is quite simple and only supports the Touch
>> Reporting Protocol.
>
> Pretty clean and neat, just a few comments.
>

Thanks for the review.

>>
>> Signed-off-by: Christian Gmeiner <christian.gmeiner@gmail.com>
>> ---
>>  drivers/input/touchscreen/Kconfig      |   12 ++
>>  drivers/input/touchscreen/Makefile     |    1 +
>>  drivers/input/touchscreen/ar1021_i2c.c |  201 ++++++++++++++++++++++++++++++++
>>  3 files changed, 214 insertions(+)
>>  create mode 100644 drivers/input/touchscreen/ar1021_i2c.c
>>
>> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
>> index 07e9e82..15cc9a1 100644
>> --- a/drivers/input/touchscreen/Kconfig
>> +++ b/drivers/input/touchscreen/Kconfig
>> @@ -86,6 +86,18 @@ config TOUCHSCREEN_AD7879_SPI
>>         To compile this driver as a module, choose M here: the
>>         module will be called ad7879-spi.
>>
>> +config TOUCHSCREEN_AR1021_I2C
>> +     tristate "Microchip AR1021 i2c touchscreen"
>> +     depends on I2C && OF
>> +     help
>> +       Say Y here if you have the Microchip AR1021 touchscreen controller
>> +       chip in your system.
>> +
>> +       If unsure, say N.
>> +
>> +       To compile this driver as a module, choose M here: the
>> +       module will be called microchip_ar1021_i2c.
>
> s/microchip_ar1021_i2c/ar1021_i2c
>

ups

>> +
>>  config TOUCHSCREEN_ATMEL_MXT
>>       tristate "Atmel mXT I2C Touchscreen"
>>       depends on I2C
>> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
>> index 62801f2..efaa328 100644
>> --- a/drivers/input/touchscreen/Makefile
>> +++ b/drivers/input/touchscreen/Makefile
>> @@ -12,6 +12,7 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879)    += ad7879.o
>>  obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C) += ad7879-i2c.o
>>  obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o
>>  obj-$(CONFIG_TOUCHSCREEN_ADS7846)    += ads7846.o
>> +obj-$(CONFIG_TOUCHSCREEN_AR1021_I2C) += ar1021_i2c.o
>>  obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT)  += atmel_mxt_ts.o
>>  obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC)       += atmel_tsadcc.o
>>  obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o
>> diff --git a/drivers/input/touchscreen/ar1021_i2c.c b/drivers/input/touchscreen/ar1021_i2c.c
>> new file mode 100644
>> index 0000000..c77ce05
>> --- /dev/null
>> +++ b/drivers/input/touchscreen/ar1021_i2c.c
>> @@ -0,0 +1,201 @@
>> +/*
>> + * Microchip AR1021 driver for I2C
>> + *
>> + * Author: Christian Gmeiner <christian.gmeiner@gmail.com>
>> + *
>> + * License: GPL as published by the FSF.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/input.h>
>> +#include <linux/of.h>
>> +#include <linux/i2c.h>
>> +#include <linux/slab.h>
>> +#include <linux/irq.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/gpio.h>
>> +#include <asm/unaligned.h>
>> +
>> +#define AR1021_TOCUH_PKG_SIZE        5
>> +
>> +struct ar1021_i2c {
>> +     struct i2c_client *client;
>> +     struct input_dev *input;
>> +     u8 data[AR1021_TOCUH_PKG_SIZE];
>> +};
>> +
>> +static irqreturn_t ar1021_i2c_irq(int irq, void *dev_id)
>> +{
>> +     struct ar1021_i2c *ar1021 = dev_id;
>> +     struct input_dev *input = ar1021->input;
>> +     u8 *data = ar1021->data;
>> +     unsigned int x, y, button;
>> +     int error;
>> +
>> +     error = i2c_master_recv(ar1021->client,
>> +                             ar1021->data, sizeof(ar1021->data));
>> +     if (error < 0)
>> +             goto out;
>> +
>> +     button = !(data[0] & BIT(0));
>> +     x = ((data[2] & 0x1f) << 7) | (data[1] & 0x7f);
>> +     y = ((data[4] & 0x1f) << 7) | (data[3] & 0x7f);
>> +
>> +     input_report_key(input, BTN_TOUCH, button);
>> +     input_report_abs(input, ABS_X, x);
>> +     input_report_abs(input, ABS_Y, y);
>> +     input_sync(input);
>> +
>> +out:
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +static int ar1021_i2c_open(struct input_dev *dev)
>> +{
>> +     struct ar1021_i2c *wac_i2c = input_get_drvdata(dev);
>> +     struct i2c_client *client = wac_i2c->client;
>> +
>> +     enable_irq(client->irq);
>> +
>> +     return 0;
>> +}
>> +
>> +static void ar1021_i2c_close(struct input_dev *dev)
>> +{
>> +     struct ar1021_i2c *wac_i2c = input_get_drvdata(dev);
>> +     struct i2c_client *client = wac_i2c->client;
>> +
>> +     disable_irq(client->irq);
>> +}
>> +
>> +static int ar1021_i2c_probe(struct i2c_client *client,
>> +                                  const struct i2c_device_id *id)
>> +{
>> +     struct ar1021_i2c *ar1021;
>> +     struct input_dev *input;
>> +     int error;
>> +
>> +     if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
>> +             dev_err(&client->dev, "i2c_check_functionality error\n");
>> +             return -EIO;
>> +     }
>> +
>> +     ar1021 = devm_kzalloc(client->dev, sizeof(*ar1021), GFP_KERNEL);
>> +     input = input_allocate_device();
>
> Use devm_input_allocate_device() and later devm_request_threaded_irq()
> as well.
>

Thats a very good idea...

>> +     if (!ar1021 || !input) {
>> +             error = -ENOMEM;
>> +             goto err_free_mem;
>> +     }
>> +
>> +     ar1021->client = client;
>> +     ar1021->input = input;
>> +
>> +     input->name = "ar1021 I2C Touchscreen";
>> +     input->id.bustype = BUS_I2C;
>> +     input->dev.parent = &client->dev;
>> +     input->open = ar1021_i2c_open;
>> +     input->close = ar1021_i2c_close;
>> +
>> +     input->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
>> +
>> +     __set_bit(BTN_TOOL_PEN, input->keybit);
>> +
>> +     input_set_abs_params(input, ABS_X, 0, 4095, 0, 0);
>> +     input_set_abs_params(input, ABS_Y, 0, 4095, 0, 0);
>> +
>> +     input_set_drvdata(input, ar1021);
>> +
>> +     error = request_threaded_irq(client->irq, NULL, ar1021_i2c_irq,
>> +                                  IRQF_TRIGGER_LOW | IRQF_ONESHOT,
>> +                                  "ar1021_i2c", ar1021);
>> +     if (error) {
>> +             dev_err(&client->dev,
>> +                     "Failed to enable IRQ, error: %d\n", error);
>> +             goto err_free_mem;
>> +     }
>> +
>> +     /* Disable the IRQ, we'll enable it in wac_i2c_open() */
>
> No, not in wac_i2c_open ;) It looks like you might want to update
> copyright notice to mentioned what driver you used as a base...
>

Will do :)

>> +     disable_irq(client->irq);
>> +
>> +     error = input_register_device(ar1021->input);
>> +     if (error) {
>> +             dev_err(&client->dev,
>> +                     "Failed to register input device, error: %d\n", error);
>> +             goto err_free_irq;
>> +     }
>> +
>> +     i2c_set_clientdata(client, ar1021);


Do I need the i2c_set_clientdata(..) call at all?

>> +     return 0;
>> +
>> +err_free_irq:
>> +     free_irq(client->irq, ar1021);
>> +err_free_mem:
>> +     input_free_device(input);
>> +
>> +     return error;
>> +}
>> +
>> +static int ar1021_i2c_remove(struct i2c_client *client)
>> +{
>> +     struct ar1021_i2c *ar1021 = i2c_get_clientdata(client);
>> +
>> +     free_irq(client->irq, ar1021);
>> +     input_unregister_device(ar1021->input);
>> +
>> +     return 0;
>> +}
>
> If you use devm throughout you won't need ar1021_i2c_remove method at
> all.
>

Correct.. will do it in the final patch.

>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +static int ar1021_i2c_suspend(struct device *dev)
>> +{
>> +     struct i2c_client *client = to_i2c_client(dev);
>> +
>> +     disable_irq(client->irq);
>> +
>> +     return 0;
>> +}
>> +
>> +static int ar1021_i2c_resume(struct device *dev)
>> +{
>> +     struct i2c_client *client = to_i2c_client(dev);
>> +
>> +     enable_irq(client->irq);
>
> You do not want to enable IRQ if there are no users (nobody opened
> device).
>

Okay.. but then I also do not need the disable_irq(..) call in
ar1021_i2c_suspend
and can totally remove the PM stuff - or?

>> +
>> +     return 0;
>> +}
>> +#endif
>> +
>> +static SIMPLE_DEV_PM_OPS(ar1021_i2c_pm, ar1021_i2c_suspend, ar1021_i2c_resume);
>> +
>> +static const struct i2c_device_id ar1021_i2c_id[] = {
>> +     { "MICROCHIP_AR1021_I2C", 0 },
>> +     { },
>> +};
>> +MODULE_DEVICE_TABLE(i2c, ar1021_i2c_id);
>> +
>> +#ifdef CONFIG_OF
>> +static struct of_device_id ar1021_i2c_of_match[] = {
>> +     { .compatible = "mc,ar1021-i2c", },
>> +     { },
>> +};
>> +MODULE_DEVICE_TABLE(of, ar1021_i2c_of_match);
>> +#endif
>> +
>> +static struct i2c_driver ar1021_i2c_driver = {
>> +     .driver = {
>> +             .name   = "ar1021_i2c",
>> +             .owner  = THIS_MODULE,
>> +             .pm     = &ar1021_i2c_pm,
>> +             .of_match_table = of_match_ptr(ar1021_i2c_of_match),
>> +     },
>> +
>> +     .probe          = ar1021_i2c_probe,
>> +     .remove         = ar1021_i2c_remove,
>> +     .id_table       = ar1021_i2c_id,
>> +};
>> +module_i2c_driver(ar1021_i2c_driver);
>> +
>> +MODULE_AUTHOR("Christian Gmeiner <christian.gmeiner@gmail.com>");
>> +MODULE_DESCRIPTION("Microchip AR1021 I2C Driver");
>> +MODULE_LICENSE("GPL");
>> +MODULE_ALIAS("platform:ar1021_i2c");
>
> Why platform if it is I2C driver? This MODULE_ALIAS is not needed at
> all.

Ok

Thanks
--
Christian Gmeiner, MSc

^ permalink raw reply

* Re: Sony Vaio Duo 11: getting middle mouse button to work
From: Stephan Mueller @ 2014-01-31  4:31 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Mattia Dongili, platform-driver-x86, Jiri Kosina, linux-input
In-Reply-To: <CAN+gG=HAXE4LT_tN+3tE8-T_L5g+2HAE35CN+z8KCEQcmJ=JWw@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 1431 bytes --]

Am Donnerstag, 30. Januar 2014, 22:50:25 schrieb Benjamin Tissoires:

Hi Benjamin,

> > Can you please help me how to do that? I install Windows in a KVM with
> > the mouse device as a USB-passthrough device -- shall I detach the Linux
> > USB driver?
> 
> No, you don't have to detach the Linux USB driver (it's done for you
> by KVM IIRC).

Correct, libvirtd is the one that detaches the driver when I start the VM.
> 
> > Now, shall I use hid-recorder on that device or rather usbmon while
> > Windows is booting?
> 
> usbmon is the tool you need. hid-recorder will not work because the
> hid subsystem will be disconnected as the device is used by Windows.
> 
> You can even use wireshark now. It gives you a nice interface and
> interpretation of the USB packets :)

Please see attached for the USB sniffing on the USB bus with the Crucialtec 
touch pad when Windows starts up and shuts down. To drive the device, Windows 
uses the Crucialtec driver according to its device settings display.

The attached log contains the log information for the mouse (device 001:005) 
and the USB bridge (device 001:002). I cut out the listing for the touch pad 
which is also attached to that bridge.

The strange thing, however, is the following: the mouse works in Windows when 
I do not sniff on the USB bus using usbmon. But as soon as I start sniffing, 
it does not work any more.

Is that log helpful?

Ciao
Stephan
-- 
| Cui bono? |

[-- Attachment #2: sony-vaio-duo-11-usbsniff.bz2 --]
[-- Type: application/x-bzip, Size: 1338 bytes --]

^ permalink raw reply

* Re: Sony Vaio Duo 11: getting middle mouse button to work
From: Benjamin Tissoires @ 2014-01-31  3:50 UTC (permalink / raw)
  To: Stephan Mueller
  Cc: Mattia Dongili, platform-driver-x86, Jiri Kosina, linux-input
In-Reply-To: <2193004.SWLhWZ2AvA@tauon>

On Thu, Jan 30, 2014 at 5:07 PM, Stephan Mueller <smueller@chronox.de> wrote:
> Am Donnerstag, 30. Januar 2014, 16:44:02 schrieb Benjamin Tissoires:
>
> Hi Benjamin,
>
>>Hi Stephan,
>>
>>thanks for the traces.
>>Well, the device definitively presents an Output and an Input report
>>on the report ID 2. That means that the windows driver can send and
>>read configuration to the trackpoint through this report.
>>
>>I think our best chance here is to capture the initialization protocol
>>from a Windows virtual machine, and then mimic the behavior under
>>Linux.
>
> Thanks.
>
> Can you please help me how to do that? I install Windows in a KVM with
> the mouse device as a USB-passthrough device -- shall I detach the Linux
> USB driver?

No, you don't have to detach the Linux USB driver (it's done for you
by KVM IIRC).

>
> Now, shall I use hid-recorder on that device or rather usbmon while
> Windows is booting?

usbmon is the tool you need. hid-recorder will not work because the
hid subsystem will be disconnected as the device is used by Windows.

You can even use wireshark now. It gives you a nice interface and
interpretation of the USB packets :)

Cheers,
Benjamin

^ permalink raw reply

* Re: [PATCH RFC] Input: Add Microchip AR1021 i2c touchscreen
From: Dmitry Torokhov @ 2014-01-31  1:01 UTC (permalink / raw)
  To: Christian Gmeiner; +Cc: linux-input
In-Reply-To: <1391074185-26973-1-git-send-email-christian.gmeiner@gmail.com>

Hi Christian,

On Thu, Jan 30, 2014 at 10:29:45AM +0100, Christian Gmeiner wrote:
> This driver is quite simple and only supports the Touch
> Reporting Protocol.

Pretty clean and neat, just a few comments.

> 
> Signed-off-by: Christian Gmeiner <christian.gmeiner@gmail.com>
> ---
>  drivers/input/touchscreen/Kconfig      |   12 ++
>  drivers/input/touchscreen/Makefile     |    1 +
>  drivers/input/touchscreen/ar1021_i2c.c |  201 ++++++++++++++++++++++++++++++++
>  3 files changed, 214 insertions(+)
>  create mode 100644 drivers/input/touchscreen/ar1021_i2c.c
> 
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index 07e9e82..15cc9a1 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -86,6 +86,18 @@ config TOUCHSCREEN_AD7879_SPI
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called ad7879-spi.
>  
> +config TOUCHSCREEN_AR1021_I2C
> +	tristate "Microchip AR1021 i2c touchscreen"
> +	depends on I2C && OF
> +	help
> +	  Say Y here if you have the Microchip AR1021 touchscreen controller
> +	  chip in your system.
> +
> +	  If unsure, say N.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called microchip_ar1021_i2c.

s/microchip_ar1021_i2c/ar1021_i2c

> +
>  config TOUCHSCREEN_ATMEL_MXT
>  	tristate "Atmel mXT I2C Touchscreen"
>  	depends on I2C
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index 62801f2..efaa328 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -12,6 +12,7 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879)	+= ad7879.o
>  obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C)	+= ad7879-i2c.o
>  obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI)	+= ad7879-spi.o
>  obj-$(CONFIG_TOUCHSCREEN_ADS7846)	+= ads7846.o
> +obj-$(CONFIG_TOUCHSCREEN_AR1021_I2C)	+= ar1021_i2c.o
>  obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT)	+= atmel_mxt_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC)	+= atmel_tsadcc.o
>  obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR)	+= auo-pixcir-ts.o
> diff --git a/drivers/input/touchscreen/ar1021_i2c.c b/drivers/input/touchscreen/ar1021_i2c.c
> new file mode 100644
> index 0000000..c77ce05
> --- /dev/null
> +++ b/drivers/input/touchscreen/ar1021_i2c.c
> @@ -0,0 +1,201 @@
> +/*
> + * Microchip AR1021 driver for I2C
> + *
> + * Author: Christian Gmeiner <christian.gmeiner@gmail.com>
> + *
> + * License: GPL as published by the FSF.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/input.h>
> +#include <linux/of.h>
> +#include <linux/i2c.h>
> +#include <linux/slab.h>
> +#include <linux/irq.h>
> +#include <linux/interrupt.h>
> +#include <linux/gpio.h>
> +#include <asm/unaligned.h>
> +
> +#define AR1021_TOCUH_PKG_SIZE	5
> +
> +struct ar1021_i2c {
> +	struct i2c_client *client;
> +	struct input_dev *input;
> +	u8 data[AR1021_TOCUH_PKG_SIZE];
> +};
> +
> +static irqreturn_t ar1021_i2c_irq(int irq, void *dev_id)
> +{
> +	struct ar1021_i2c *ar1021 = dev_id;
> +	struct input_dev *input = ar1021->input;
> +	u8 *data = ar1021->data;
> +	unsigned int x, y, button;
> +	int error;
> +
> +	error = i2c_master_recv(ar1021->client,
> +				ar1021->data, sizeof(ar1021->data));
> +	if (error < 0)
> +		goto out;
> +
> +	button = !(data[0] & BIT(0));
> +	x = ((data[2] & 0x1f) << 7) | (data[1] & 0x7f);
> +	y = ((data[4] & 0x1f) << 7) | (data[3] & 0x7f);
> +
> +	input_report_key(input, BTN_TOUCH, button);
> +	input_report_abs(input, ABS_X, x);
> +	input_report_abs(input, ABS_Y, y);
> +	input_sync(input);
> +
> +out:
> +	return IRQ_HANDLED;
> +}
> +
> +static int ar1021_i2c_open(struct input_dev *dev)
> +{
> +	struct ar1021_i2c *wac_i2c = input_get_drvdata(dev);
> +	struct i2c_client *client = wac_i2c->client;
> +
> +	enable_irq(client->irq);
> +
> +	return 0;
> +}
> +
> +static void ar1021_i2c_close(struct input_dev *dev)
> +{
> +	struct ar1021_i2c *wac_i2c = input_get_drvdata(dev);
> +	struct i2c_client *client = wac_i2c->client;
> +
> +	disable_irq(client->irq);
> +}
> +
> +static int ar1021_i2c_probe(struct i2c_client *client,
> +				     const struct i2c_device_id *id)
> +{
> +	struct ar1021_i2c *ar1021;
> +	struct input_dev *input;
> +	int error;
> +
> +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
> +		dev_err(&client->dev, "i2c_check_functionality error\n");
> +		return -EIO;
> +	}
> +
> +	ar1021 = devm_kzalloc(client->dev, sizeof(*ar1021), GFP_KERNEL);
> +	input = input_allocate_device();

Use devm_input_allocate_device() and later devm_request_threaded_irq()
as well.

> +	if (!ar1021 || !input) {
> +		error = -ENOMEM;
> +		goto err_free_mem;
> +	}
> +
> +	ar1021->client = client;
> +	ar1021->input = input;
> +
> +	input->name = "ar1021 I2C Touchscreen";
> +	input->id.bustype = BUS_I2C;
> +	input->dev.parent = &client->dev;
> +	input->open = ar1021_i2c_open;
> +	input->close = ar1021_i2c_close;
> +
> +	input->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
> +
> +	__set_bit(BTN_TOOL_PEN, input->keybit);
> +
> +	input_set_abs_params(input, ABS_X, 0, 4095, 0, 0);
> +	input_set_abs_params(input, ABS_Y, 0, 4095, 0, 0);
> +
> +	input_set_drvdata(input, ar1021);
> +
> +	error = request_threaded_irq(client->irq, NULL, ar1021_i2c_irq,
> +				     IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> +				     "ar1021_i2c", ar1021);
> +	if (error) {
> +		dev_err(&client->dev,
> +			"Failed to enable IRQ, error: %d\n", error);
> +		goto err_free_mem;
> +	}
> +
> +	/* Disable the IRQ, we'll enable it in wac_i2c_open() */

No, not in wac_i2c_open ;) It looks like you might want to update
copyright notice to mentioned what driver you used as a base...

> +	disable_irq(client->irq);
> +
> +	error = input_register_device(ar1021->input);
> +	if (error) {
> +		dev_err(&client->dev,
> +			"Failed to register input device, error: %d\n", error);
> +		goto err_free_irq;
> +	}
> +
> +	i2c_set_clientdata(client, ar1021);
> +	return 0;
> +
> +err_free_irq:
> +	free_irq(client->irq, ar1021);
> +err_free_mem:
> +	input_free_device(input);
> +
> +	return error;
> +}
> +
> +static int ar1021_i2c_remove(struct i2c_client *client)
> +{
> +	struct ar1021_i2c *ar1021 = i2c_get_clientdata(client);
> +
> +	free_irq(client->irq, ar1021);
> +	input_unregister_device(ar1021->input);
> +
> +	return 0;
> +}

If you use devm throughout you won't need ar1021_i2c_remove method at
all.

> +
> +#ifdef CONFIG_PM_SLEEP
> +static int ar1021_i2c_suspend(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +
> +	disable_irq(client->irq);
> +
> +	return 0;
> +}
> +
> +static int ar1021_i2c_resume(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +
> +	enable_irq(client->irq);

You do not want to enable IRQ if there are no users (nobody opened
device).

> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(ar1021_i2c_pm, ar1021_i2c_suspend, ar1021_i2c_resume);
> +
> +static const struct i2c_device_id ar1021_i2c_id[] = {
> +	{ "MICROCHIP_AR1021_I2C", 0 },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(i2c, ar1021_i2c_id);
> +
> +#ifdef CONFIG_OF
> +static struct of_device_id ar1021_i2c_of_match[] = {
> +	{ .compatible = "mc,ar1021-i2c", },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, ar1021_i2c_of_match);
> +#endif
> +
> +static struct i2c_driver ar1021_i2c_driver = {
> +	.driver	= {
> +		.name	= "ar1021_i2c",
> +		.owner	= THIS_MODULE,
> +		.pm	= &ar1021_i2c_pm,
> +		.of_match_table = of_match_ptr(ar1021_i2c_of_match),
> +	},
> +
> +	.probe		= ar1021_i2c_probe,
> +	.remove		= ar1021_i2c_remove,
> +	.id_table	= ar1021_i2c_id,
> +};
> +module_i2c_driver(ar1021_i2c_driver);
> +
> +MODULE_AUTHOR("Christian Gmeiner <christian.gmeiner@gmail.com>");
> +MODULE_DESCRIPTION("Microchip AR1021 I2C Driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:ar1021_i2c");

Why platform if it is I2C driver? This MODULE_ALIAS is not needed at
all.

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: Is the hid-multitouch default driver for multitouch screen ?
From: Benjamin Tissoires @ 2014-01-30 23:47 UTC (permalink / raw)
  To: Yufeng Shen; +Cc: linux-input
In-Reply-To: <CAPDwgkMf2j9kmDa1Q7LgMjtqGXfqEDMv7JNC+eO6j3Vk9DPysQ@mail.gmail.com>

Hi Yufeng,

On Thu, Jan 30, 2014 at 5:52 PM, Yufeng Shen <miletus@chromium.org> wrote:
> Hi,
>
> I was under the impression that in recent kernel (I am testing on 3.8)
> hid-multitouch
> is the default driver for touchscreen.

yes, and no. hid-multitouch is the default for Win 7 / Win 8 certified
touchscreens, which tends to be a lot.
For instance, here is a list of device I know of:
http://lii-enac.fr/en/architecture/linux-input/multitouch-devices.html

So given that on x86 most manufacturers wants the Microsoft
certification, their devices use hid-multitouch. However, on the
Android side, or the embedded world more generally, people use their
own protocol and communication bus and they have to write their own
driver.
Side note, I think they should consider switching to HID over i2c,
they will not need to write any driver this way. But that's just my
opinion.

> And I found one case that there is this
> RNDPLUS PulseIR Touchscreen pid/vid 2512:5004 defaults to generic-usb driver.

Hmm... the RNDPLUS 5003 (as written in the table I mentioned above)
uses hid-multitouch. And given the fact that you refer to generic-usb
driver, I think (but I may be wrong) that last time you checked this
device, it was on a kernel before 3.2. Now it should be handled by
hid-multitouch. Or it is just a single touch touchscreen.

>
> If you search
> "HID-Multitouch Kernel Driver Installation for Android OS IDSPulse"
> there is a setup document shows one need to add its pid/vid to
> hid_have_special_driver[]
> list so it will pick up hid-multitouch.  Do we still need to do that
> in recent kernel ?

No.
- Starting from 3.2 multitouch devices where rejected by hid-core and
had to be added manually to hid-multiouch
- in 3.4, there were a non generic attempt to automatically bind them
to hid-multitouch (attempt which was doing its job though)
- starting from 3.5, there is a much better generic solution and
multitouch screens are bound to hid-multitouch, and sensors to
hid-sensorhub for instance.

So to sum up, no, multitouch screen do not need to be added to
hid_have_special_driver[] except some rare cases which are already in
the kernel.

Cheers,
Benjamin

>
>
> Thanks,
>
> Yufeng Shen
> --
> 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

* Is the hid-multitouch default driver for multitouch screen ?
From: Yufeng Shen @ 2014-01-30 22:52 UTC (permalink / raw)
  To: linux-input

Hi,

I was under the impression that in recent kernel (I am testing on 3.8)
hid-multitouch
is the default driver for touchscreen. And I found one case that there is this
RNDPLUS PulseIR Touchscreen pid/vid 2512:5004 defaults to generic-usb driver.

If you search
"HID-Multitouch Kernel Driver Installation for Android OS IDSPulse"
there is a setup document shows one need to add its pid/vid to
hid_have_special_driver[]
list so it will pick up hid-multitouch.  Do we still need to do that
in recent kernel ?


Thanks,

Yufeng Shen

^ permalink raw reply

* Re: Sony Vaio Duo 11: getting middle mouse button to work
From: Stephan Mueller @ 2014-01-30 22:07 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Mattia Dongili, platform-driver-x86, Jiri Kosina, linux-input
In-Reply-To: <CAN+gG=F_L1UdSqpi2d--JeerADT66s2d04t8hv+ZoVSL6dQgbg@mail.gmail.com>

Am Donnerstag, 30. Januar 2014, 16:44:02 schrieb Benjamin Tissoires:

Hi Benjamin,

>Hi Stephan,
>
>thanks for the traces.
>Well, the device definitively presents an Output and an Input report
>on the report ID 2. That means that the windows driver can send and
>read configuration to the trackpoint through this report.
>
>I think our best chance here is to capture the initialization protocol
>from a Windows virtual machine, and then mimic the behavior under
>Linux.

Thanks.

Can you please help me how to do that? I install Windows in a KVM with 
the mouse device as a USB-passthrough device -- shall I detach the Linux 
USB driver?

Now, shall I use hid-recorder on that device or rather usbmon while 
Windows is booting?

Thanks
Stephan

^ permalink raw reply

* Re: Sony Vaio Duo 11: getting middle mouse button to work
From: Benjamin Tissoires @ 2014-01-30 21:44 UTC (permalink / raw)
  To: Stephan Mueller
  Cc: Mattia Dongili, platform-driver-x86, Jiri Kosina, linux-input
In-Reply-To: <2981174.bDZe6OjLCj@myon.chronox.de>

Hi Stephan,

thanks for the traces.
Well, the device definitively presents an Output and an Input report
on the report ID 2. That means that the windows driver can send and
read configuration to the trackpoint through this report.

I think our best chance here is to capture the initialization protocol
from a Windows virtual machine, and then mimic the behavior under
Linux.

Cheers,
Benjamin


On Wed, Jan 29, 2014 at 10:48 PM, Stephan Mueller <smueller@chronox.de> wrote:
> Am Mittwoch, 29. Januar 2014, 10:07:40 schrieb Benjamin Tissoires:
>
> Hi Benjamin,
>
>> On Wed, Jan 29, 2014 at 9:59 AM, Stephan Mueller <smueller@chronox.de>
> wrote:
>> > Am Mittwoch, 29. Januar 2014, 09:53:03 schrieb Benjamin Tissoires:
>> >
>> > Hi Benjamin,
>> >
>> >>On Fri, Jan 24, 2014 at 11:51 PM, Stephan Mueller <smueller@chronox.de>
>> >>
>> > wrote:
>> >>> Am Samstag, 25. Januar 2014, 12:17:13 schrieb Mattia Dongili:
>> >>>
>> >>> Hi Mattia,
>> >>>
>> >>>> I'd try with the input subsystem and the synaptics_usb driver first
>> >>>> but it's just a wild guess. Your kernel log should give you more
>> >>>> hints about which driver is bound to the device and the sysfs tree
>> >>>> under
>> >>>> /sys/class/input/event*/device/* has all the capabilities and
>> >>>> identifiers.
>> >>>
>> >>> The following did not help:
>> >>>
>> >>> modprobe synaptics_usb
>> >>> cd /sys/bus/usb/drivers/usbhid
>> >>> echo -n "1-1.3:1.0" > unbind
>> >>> #now the mouse is without driver, does not move, and
>> >>> #/sys/class/input/event2/device/device is without driver
>> >>> cd /sys/bus/usb/drivers/synaptics_usb
>> >>> echo -n "1-1.3:1.0" > bind
>> >>> #error: no such device, mouse does not work, nothing in dmesg
>> >>> cd /sys/bus/usb/drivers/usbhid
>> >>> echo -n "1-1.3:1.0" > bind
>> >>> #mouse works again without middle button
>> >>
>> >>Hi Stephan,
>> >>
>> >>in this case, you definitively want to talk to HID (and input) folks.
>> >>Adding Jiri, the HID maintainer in the discussion.
>> >>
>> >>Your mouse does not seem to be handled properly by the hid subsystem
>> >>and needs quirks, or fix.
>> >>
>> >>Can you send us some hid-recorder[1] traces of your device? We should
>> >>then be able to check what's wrong and hopefully fix the problem.
>> >>
>> > Thanks a lot for the helping hand. I will try your suggestion tonight
>> > and report back.
>> >
>> > But please allow me to point out that I have doubts that HID or input is
>> > at fault, because when sniffing on the USB bus with usbmon, I do *not*
>> > see any information transported when pressing the middle button.
>> > Therefore, I would suspect it is rather the base USB driver that somehow
>> > needs a quirk to access the mouse properly.
>>
>> Oh, then in this case it may be that your device needs to be put in a
>> special mode, and the report descriptors will show us some hints on
>> how to do it (maybe).
>> I strongly doubt that USB is in fault here. I can not see any reasons
>> why the USB or underlying driver would select which packets are
>> transmitted.
>>
>> What you can also do is setup a windows virtual machine, assign the
>> usb device to it, and sniff through usbmon or wireshark what packets
>> are emitted from/to the mouse. Then, we will duplicate this behavior
>> in the hid driver, and you would be good to go. Still, having the
>> reports descriptors (which are provided by hid-recorder, or in
>> /sys/kernel/debug/hid/DEVICE/rdesc, or in lsusb -vv when the usbhid
>> driver is not bound) would help us to understand the mouse firmware.
>>
>> Cheers,
>> Benjamin
>
> The device is 26E1:C1A0; when doing a cat
> /sys/kernel/debug/hid/0003\:26E1\:C1A0.0003/events, I see the mouse movements
> and the left and right mouse button, but not the middle one.
>
> # cat /sys/kernel/debug/hid/0003\:26E1\:C1A0.0003/rdesc
> 05 01 09 02 a1 01 85 01 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 75 01 95 03
> 81 02 95 05 81 01 05 01 15 81 25 7f 75 08 95 03 09 30 09 31 09 38 81 06 c0 c0
> 06 a0 ff 09 01 a1 01 85 02 09 02 a1 00 06 a1 ff 09 01 15 80 25 7f 35 00 45 ff
> 75 08 95 04 81 02 09 11 15 80 25 7f 35 00 45 ff 75 08 95 04 91 02 c0 c0
>
>   INPUT(1)[INPUT]
>     Field(0)
>       Physical(GenericDesktop.Pointer)
>       Application(GenericDesktop.Mouse)
>       Usage(3)
>         Button.0001
>         Button.0002
>         Button.0003
>       Logical Minimum(0)
>       Logical Maximum(1)
>       Report Size(1)
>       Report Count(3)
>       Report Offset(0)
>       Flags( Variable Absolute )
>     Field(1)
>       Physical(GenericDesktop.Pointer)
>       Application(GenericDesktop.Mouse)
>       Usage(3)
>         GenericDesktop.X
>         GenericDesktop.Y
>         GenericDesktop.Wheel
>       Logical Minimum(-127)
>       Logical Maximum(127)
>       Report Size(8)
>       Report Count(3)
>       Report Offset(8)
>       Flags( Variable Relative )
>   INPUT(2)[INPUT]
>     Field(0)
>       Physical(ffa0.0002)
>       Application(ffa0.0001)
>       Usage(4)
>         ffa1.0001
>         ffa1.0001
>         ffa1.0001
>         ffa1.0001
>       Logical Minimum(-128)
>       Logical Maximum(127)
>       Physical Minimum(0)
>       Physical Maximum(255)
>       Report Size(8)
>       Report Count(4)
>       Report Offset(0)
>       Flags( Variable Absolute )
>   OUTPUT(2)[OUTPUT]
>     Field(0)
>       Physical(ffa0.0002)
>       Application(ffa0.0001)
>       Usage(4)
>         ffa1.0011
>         ffa1.0011
>         ffa1.0011
>         ffa1.0011
>       Logical Minimum(-128)
>       Logical Maximum(127)
>       Physical Minimum(0)
>       Physical Maximum(255)
>       Report Size(8)
>       Report Count(4)
>       Report Offset(0)
>       Flags( Variable Absolute )
>
> Button.0001 ---> Key.LeftBtn
> Button.0002 ---> Key.RightBtn
> Button.0003 ---> Key.MiddleBtn
> GenericDesktop.X ---> Relative.X
> GenericDesktop.Y ---> Relative.Y
> GenericDesktop.Wheel ---> Relative.Wheel
> ffa1.0001 ---> Absolute.Misc
> ffa1.0001 ---> Absolute.?
> ffa1.0001 ---> Absolute.?
> ffa1.0001 ---> Absolute.?
> ffa1.0011 ---> Sync.Report
> ffa1.0011 ---> Sync.Report
> ffa1.0011 ---> Sync.Report
> ffa1.0011 ---> Sync.Report
>
> Here is the hid-recorder output -- the mouse clicks are at the bottom. The
> middle button did not generate anything:
>
> hid-recorder /dev/hidraw0
> R: 102 05 01 09 02 a1 01 85 01 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 75 01
> 95 03 81 02 95 05 81 01 05 01 15 81 25 7f 75 08 95 03 09 30 09 31 09 38 81 06
> c0 c0 06 a0 ff 09 01 a1 01 85 02 09 02 a1 00 06 a1 ff 09 01 15 80 25 7f 35 00
> 45 ff 75 08 95 04 81 02 09 11 15 80 25 7f 35 00 45 ff 75 08 95 04 91 02 c0 c0
> N: Crucialtek co.,LTD Optical Track Pad
> P: usb-0000:00:1a.0-1.3/input0
> I: 3 26e1 c1a0
> E: 0.000000 5 01 00 00 01 00
> E: 0.007998 5 01 00 00 01 00
> E: 0.031999 5 01 00 01 00 00
> E: 0.160003 5 01 00 01 fe 00
> E: 0.167999 5 01 00 01 ff 00
> E: 0.175997 5 01 00 01 fe 00
> E: 0.191999 5 01 00 01 fe 00
> E: 0.199948 5 01 00 01 fd 00
> E: 0.215935 5 01 00 01 fe 00
> E: 0.223858 5 01 00 01 fe 00
> E: 0.231940 5 01 00 00 ff 00
> E: 0.247945 5 01 00 00 ff 00
> E: 0.295953 5 01 00 00 ff 00
> E: 0.319953 5 01 00 01 fe 00
> E: 0.335936 5 01 00 02 fe 00
> E: 0.343944 5 01 00 01 fe 00
> E: 0.359938 5 01 00 01 ff 00
> E: 0.383963 5 01 00 ff 00 00
> E: 2.407955 5 01 02 00 00 00
> E: 2.623954 5 01 00 00 00 00
> E: 5.024015 5 01 01 00 00 00
> E: 5.320041 5 01 00 00 00 00
>
>
> Ciao
> Stephan
> --
> | Cui bono? |

^ permalink raw reply

* [PATCH 4/4] Input: wacom: Handle 1024 pressure levels in wacom_tpc_pen
From: Jason Gerecke @ 2014-01-30 18:48 UTC (permalink / raw)
  To: linux-input, linuxwacom-devel, pinglinux, skomra, dmitry.torokhov
  Cc: Jason Gerecke
In-Reply-To: <1391107728-1306-1-git-send-email-killertofu@gmail.com>

Some tablet PC sensors (e.g. the 0xEC found in the Thinkpad
Yoga) report more than 256 pressure levels and will experience
wraparound unless the full range is read.

Signed-off-by: Jason Gerecke <killertofu@gmail.com>
---
 drivers/input/tablet/wacom_wac.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
index 9ff5613..ae1437b 100644
--- a/drivers/input/tablet/wacom_wac.c
+++ b/drivers/input/tablet/wacom_wac.c
@@ -1087,7 +1087,7 @@ static int wacom_tpc_pen(struct wacom_wac *wacom)
 		input_report_key(input, BTN_STYLUS2, data[1] & 0x10);
 		input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2]));
 		input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4]));
-		input_report_abs(input, ABS_PRESSURE, ((data[7] & 0x01) << 8) | data[6]);
+		input_report_abs(input, ABS_PRESSURE, ((data[7] & 0x03) << 8) | data[6]);
 		input_report_key(input, BTN_TOUCH, data[1] & 0x05);
 		input_report_key(input, wacom->tool[0], prox);
 		return 1;
-- 
1.8.5.3


^ permalink raw reply related

* [PATCH 3/4] Input: wacom: References to 'wacom->data' should use 'unsigned char*'
From: Jason Gerecke @ 2014-01-30 18:48 UTC (permalink / raw)
  To: linux-input, linuxwacom-devel, pinglinux, skomra, dmitry.torokhov
  Cc: Jason Gerecke
In-Reply-To: <1391107728-1306-1-git-send-email-killertofu@gmail.com>

'wacom->data' contains raw binary data and can lead to unexpected
behavior if a byte under examination happens to have its MSB set.

Signed-off-by: Jason Gerecke <killertofu@gmail.com>
---
 drivers/input/tablet/wacom_wac.c | 27 +++++++++------------------
 1 file changed, 9 insertions(+), 18 deletions(-)

diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
index 05f371d..9ff5613 100644
--- a/drivers/input/tablet/wacom_wac.c
+++ b/drivers/input/tablet/wacom_wac.c
@@ -178,10 +178,9 @@ static int wacom_ptu_irq(struct wacom_wac *wacom)
 
 static int wacom_dtu_irq(struct wacom_wac *wacom)
 {
-	struct wacom_features *features = &wacom->features;
-	char *data = wacom->data;
+	unsigned char *data = wacom->data;
 	struct input_dev *input = wacom->input;
-	int prox = data[1] & 0x20, pressure;
+	int prox = data[1] & 0x20;
 
 	dev_dbg(input->dev.parent,
 		"%s: received report #%d", __func__, data[0]);
@@ -198,10 +197,7 @@ static int wacom_dtu_irq(struct wacom_wac *wacom)
 	input_report_key(input, BTN_STYLUS2, data[1] & 0x10);
 	input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2]));
 	input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4]));
-	pressure = ((data[7] & 0x01) << 8) | data[6];
-	if (pressure < 0)
-		pressure = features->pressure_max + pressure + 1;
-	input_report_abs(input, ABS_PRESSURE, pressure);
+	input_report_abs(input, ABS_PRESSURE, ((data[7] & 0x01) << 8) | data[6]);
 	input_report_key(input, BTN_TOUCH, data[1] & 0x05);
 	if (!prox) /* out-prox */
 		wacom->id[0] = 0;
@@ -906,7 +902,7 @@ static int int_dist(int x1, int y1, int x2, int y2)
 static int wacom_24hdt_irq(struct wacom_wac *wacom)
 {
 	struct input_dev *input = wacom->input;
-	char *data = wacom->data;
+	unsigned char *data = wacom->data;
 	int i;
 	int current_num_contacts = data[61];
 	int contacts_to_send = 0;
@@ -959,7 +955,7 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom)
 static int wacom_mt_touch(struct wacom_wac *wacom)
 {
 	struct input_dev *input = wacom->input;
-	char *data = wacom->data;
+	unsigned char *data = wacom->data;
 	int i;
 	int current_num_contacts = data[2];
 	int contacts_to_send = 0;
@@ -1038,7 +1034,7 @@ static int wacom_tpc_mt_touch(struct wacom_wac *wacom)
 
 static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len)
 {
-	char *data = wacom->data;
+	unsigned char *data = wacom->data;
 	struct input_dev *input = wacom->input;
 	bool prox;
 	int x = 0, y = 0;
@@ -1074,10 +1070,8 @@ static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len)
 
 static int wacom_tpc_pen(struct wacom_wac *wacom)
 {
-	struct wacom_features *features = &wacom->features;
-	char *data = wacom->data;
+	unsigned char *data = wacom->data;
 	struct input_dev *input = wacom->input;
-	int pressure;
 	bool prox = data[1] & 0x20;
 
 	if (!wacom->shared->stylus_in_proximity) /* first in prox */
@@ -1093,10 +1087,7 @@ static int wacom_tpc_pen(struct wacom_wac *wacom)
 		input_report_key(input, BTN_STYLUS2, data[1] & 0x10);
 		input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2]));
 		input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4]));
-		pressure = ((data[7] & 0x01) << 8) | data[6];
-		if (pressure < 0)
-			pressure = features->pressure_max + pressure + 1;
-		input_report_abs(input, ABS_PRESSURE, pressure);
+		input_report_abs(input, ABS_PRESSURE, ((data[7] & 0x01) << 8) | data[6]);
 		input_report_key(input, BTN_TOUCH, data[1] & 0x05);
 		input_report_key(input, wacom->tool[0], prox);
 		return 1;
@@ -1107,7 +1098,7 @@ static int wacom_tpc_pen(struct wacom_wac *wacom)
 
 static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
 {
-	char *data = wacom->data;
+	unsigned char *data = wacom->data;
 
 	dev_dbg(wacom->input->dev.parent,
 		"%s: received report #%d\n", __func__, data[0]);
-- 
1.8.5.3


^ permalink raw reply related

* [PATCH 2/4] Input: wacom: Override 'pressure_max' with value from HID_USAGE_PRESSURE
From: Jason Gerecke @ 2014-01-30 18:48 UTC (permalink / raw)
  To: linux-input, linuxwacom-devel, pinglinux, skomra, dmitry.torokhov
  Cc: Jason Gerecke
In-Reply-To: <1391107728-1306-1-git-send-email-killertofu@gmail.com>

The 0xEC sensor is used in multiple tablet PCs and curiously has
versions that report 256 levels of pressure (Samsung Slate 7)
as well as versions that report 1024 levels (Lenovo Thinkpad Yoga).
To allow both versions to work properly, we allow the value of
HID_USAGE_PRESSURE reported to override pressure_max.

Signed-off-by: Jason Gerecke <killertofu@gmail.com>
---
 drivers/input/tablet/wacom_sys.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c
index 5be6177..611fc39 100644
--- a/drivers/input/tablet/wacom_sys.c
+++ b/drivers/input/tablet/wacom_sys.c
@@ -24,6 +24,7 @@
 #define HID_USAGE			0x09
 #define HID_USAGE_X			((HID_USAGE_PAGE_DESKTOP << 16) | 0x30)
 #define HID_USAGE_Y			((HID_USAGE_PAGE_DESKTOP << 16) | 0x31)
+#define HID_USAGE_PRESSURE		((HID_USAGE_PAGE_DIGITIZER << 16) | 0x30)
 #define HID_USAGE_X_TILT		((HID_USAGE_PAGE_DIGITIZER << 16) | 0x3d)
 #define HID_USAGE_Y_TILT		((HID_USAGE_PAGE_DIGITIZER << 16) | 0x3e)
 #define HID_USAGE_FINGER		((HID_USAGE_PAGE_DIGITIZER << 16) | 0x22)
@@ -465,6 +466,14 @@ static int wacom_parse_hid(struct usb_interface *intf,
 					wacom_retrieve_report_data(intf, features);
 				i++;
 				break;
+
+			case HID_USAGE_PRESSURE:
+				if (pen) {
+					features->pressure_max =
+						get_unaligned_le16(&report[i + 3]);
+					i += 4;
+				}
+				break;
 			}
 			break;
 
-- 
1.8.5.3


^ permalink raw reply related

* [PATCH 1/4] Input: wacom: Use full 32-bit HID Usage value in switch statement
From: Jason Gerecke @ 2014-01-30 18:48 UTC (permalink / raw)
  To: linux-input, linuxwacom-devel, pinglinux, skomra, dmitry.torokhov
  Cc: Jason Gerecke

A HID Usage is a 32-bit value: an upper 16-bit "page" and a lower
16-bit ID. While the two halves are normally reported seperately,
only the combination uniquely idenfifes a particular HID Usage.

The existing code performs the comparison in two steps, first
performing a switch on the ID and then verifying the page within
each case. While this works fine, it is very akward to handle
two Usages that share a single ID, such as HID_USAGE_PRESSURE
and HID_USAGE_X because the case statement can only have a
single identifier.

To work around this, we now check the full 32-bit HID Usage
directly rather than first checking the ID and then the page.
This allows the switch statement to have distinct cases for
e.g. HID_USAGE_PRESSURE and HID_USAGE_X.

Signed-off-by: Jason Gerecke <killertofu@gmail.com>
---
 drivers/input/tablet/wacom_sys.c | 237 ++++++++++++++++++---------------------
 1 file changed, 109 insertions(+), 128 deletions(-)

diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c
index b16ebef..5be6177 100644
--- a/drivers/input/tablet/wacom_sys.c
+++ b/drivers/input/tablet/wacom_sys.c
@@ -22,23 +22,17 @@
 #define HID_USAGE_PAGE_DIGITIZER	0x0d
 #define HID_USAGE_PAGE_DESKTOP		0x01
 #define HID_USAGE			0x09
-#define HID_USAGE_X			0x30
-#define HID_USAGE_Y			0x31
-#define HID_USAGE_X_TILT		0x3d
-#define HID_USAGE_Y_TILT		0x3e
-#define HID_USAGE_FINGER		0x22
-#define HID_USAGE_STYLUS		0x20
-#define HID_USAGE_CONTACTMAX		0x55
+#define HID_USAGE_X			((HID_USAGE_PAGE_DESKTOP << 16) | 0x30)
+#define HID_USAGE_Y			((HID_USAGE_PAGE_DESKTOP << 16) | 0x31)
+#define HID_USAGE_X_TILT		((HID_USAGE_PAGE_DIGITIZER << 16) | 0x3d)
+#define HID_USAGE_Y_TILT		((HID_USAGE_PAGE_DIGITIZER << 16) | 0x3e)
+#define HID_USAGE_FINGER		((HID_USAGE_PAGE_DIGITIZER << 16) | 0x22)
+#define HID_USAGE_STYLUS		((HID_USAGE_PAGE_DIGITIZER << 16) | 0x20)
+#define HID_USAGE_CONTACTMAX		((HID_USAGE_PAGE_DIGITIZER << 16) | 0x55)
 #define HID_COLLECTION			0xa1
 #define HID_COLLECTION_LOGICAL		0x02
 #define HID_COLLECTION_END		0xc0
 
-enum {
-	WCM_UNDEFINED = 0,
-	WCM_DESKTOP,
-	WCM_DIGITIZER,
-};
-
 struct hid_descriptor {
 	struct usb_descriptor_header header;
 	__le16   bcdHID;
@@ -305,7 +299,7 @@ static int wacom_parse_hid(struct usb_interface *intf,
 	char limit = 0;
 	/* result has to be defined as int for some devices */
 	int result = 0, touch_max = 0;
-	int i = 0, usage = WCM_UNDEFINED, finger = 0, pen = 0;
+	int i = 0, page = 0, finger = 0, pen = 0;
 	unsigned char *report;
 
 	report = kzalloc(hid_desc->wDescriptorLength, GFP_KERNEL);
@@ -332,134 +326,121 @@ static int wacom_parse_hid(struct usb_interface *intf,
 
 		switch (report[i]) {
 		case HID_USAGE_PAGE:
-			switch (report[i + 1]) {
-			case HID_USAGE_PAGE_DIGITIZER:
-				usage = WCM_DIGITIZER;
-				i++;
-				break;
-
-			case HID_USAGE_PAGE_DESKTOP:
-				usage = WCM_DESKTOP;
-				i++;
-				break;
-			}
+			page = report[i + 1];
+			i++;
 			break;
 
 		case HID_USAGE:
-			switch (report[i + 1]) {
+			switch (page << 16 | report[i + 1]) {
 			case HID_USAGE_X:
-				if (usage == WCM_DESKTOP) {
-					if (finger) {
-						features->device_type = BTN_TOOL_FINGER;
-						/* touch device at least supports one touch point */
-						touch_max = 1;
-						switch (features->type) {
-						case TABLETPC2FG:
-							features->pktlen = WACOM_PKGLEN_TPC2FG;
-							break;
-
-						case MTSCREEN:
-						case WACOM_24HDT:
-							features->pktlen = WACOM_PKGLEN_MTOUCH;
-							break;
-
-						case MTTPC:
-							features->pktlen = WACOM_PKGLEN_MTTPC;
-							break;
-
-						case BAMBOO_PT:
-							features->pktlen = WACOM_PKGLEN_BBTOUCH;
-							break;
-
-						default:
-							features->pktlen = WACOM_PKGLEN_GRAPHIRE;
-							break;
-						}
-
-						switch (features->type) {
-						case BAMBOO_PT:
-							features->x_phy =
-								get_unaligned_le16(&report[i + 5]);
-							features->x_max =
-								get_unaligned_le16(&report[i + 8]);
-							i += 15;
-							break;
-
-						case WACOM_24HDT:
-							features->x_max =
-								get_unaligned_le16(&report[i + 3]);
-							features->x_phy =
-								get_unaligned_le16(&report[i + 8]);
-							features->unit = report[i - 1];
-							features->unitExpo = report[i - 3];
-							i += 12;
-							break;
-
-						default:
-							features->x_max =
-								get_unaligned_le16(&report[i + 3]);
-							features->x_phy =
-								get_unaligned_le16(&report[i + 6]);
-							features->unit = report[i + 9];
-							features->unitExpo = report[i + 11];
-							i += 12;
-							break;
-						}
-					} else if (pen) {
-						/* penabled only accepts exact bytes of data */
-						if (features->type >= TABLETPC)
-							features->pktlen = WACOM_PKGLEN_GRAPHIRE;
-						features->device_type = BTN_TOOL_PEN;
+				if (finger) {
+					features->device_type = BTN_TOOL_FINGER;
+					/* touch device at least supports one touch point */
+					touch_max = 1;
+					switch (features->type) {
+					case TABLETPC2FG:
+						features->pktlen = WACOM_PKGLEN_TPC2FG;
+						break;
+
+					case MTSCREEN:
+					case WACOM_24HDT:
+						features->pktlen = WACOM_PKGLEN_MTOUCH;
+						break;
+
+					case MTTPC:
+						features->pktlen = WACOM_PKGLEN_MTTPC;
+						break;
+
+					case BAMBOO_PT:
+						features->pktlen = WACOM_PKGLEN_BBTOUCH;
+						break;
+
+					default:
+						features->pktlen = WACOM_PKGLEN_GRAPHIRE;
+						break;
+					}
+
+					switch (features->type) {
+					case BAMBOO_PT:
+						features->x_phy =
+							get_unaligned_le16(&report[i + 5]);
+						features->x_max =
+							get_unaligned_le16(&report[i + 8]);
+						i += 15;
+						break;
+
+					case WACOM_24HDT:
 						features->x_max =
 							get_unaligned_le16(&report[i + 3]);
-						i += 4;
+						features->x_phy =
+							get_unaligned_le16(&report[i + 8]);
+						features->unit = report[i - 1];
+						features->unitExpo = report[i - 3];
+						i += 12;
+						break;
+
+					default:
+						features->x_max =
+							get_unaligned_le16(&report[i + 3]);
+						features->x_phy =
+							get_unaligned_le16(&report[i + 6]);
+						features->unit = report[i + 9];
+						features->unitExpo = report[i + 11];
+						i += 12;
+						break;
 					}
+				} else if (pen) {
+					/* penabled only accepts exact bytes of data */
+					if (features->type >= TABLETPC)
+						features->pktlen = WACOM_PKGLEN_GRAPHIRE;
+					features->device_type = BTN_TOOL_PEN;
+					features->x_max =
+						get_unaligned_le16(&report[i + 3]);
+					i += 4;
 				}
 				break;
 
 			case HID_USAGE_Y:
-				if (usage == WCM_DESKTOP) {
-					if (finger) {
-						switch (features->type) {
-						case TABLETPC2FG:
-						case MTSCREEN:
-						case MTTPC:
-							features->y_max =
-								get_unaligned_le16(&report[i + 3]);
-							features->y_phy =
-								get_unaligned_le16(&report[i + 6]);
-							i += 7;
-							break;
-
-						case WACOM_24HDT:
-							features->y_max =
-								get_unaligned_le16(&report[i + 3]);
-							features->y_phy =
-								get_unaligned_le16(&report[i - 2]);
-							i += 7;
-							break;
-
-						case BAMBOO_PT:
-							features->y_phy =
-								get_unaligned_le16(&report[i + 3]);
-							features->y_max =
-								get_unaligned_le16(&report[i + 6]);
-							i += 12;
-							break;
-
-						default:
-							features->y_max =
-								features->x_max;
-							features->y_phy =
-								get_unaligned_le16(&report[i + 3]);
-							i += 4;
-							break;
-						}
-					} else if (pen) {
+				if (finger) {
+					switch (features->type) {
+					case TABLETPC2FG:
+					case MTSCREEN:
+					case MTTPC:
+						features->y_max =
+							get_unaligned_le16(&report[i + 3]);
+						features->y_phy =
+							get_unaligned_le16(&report[i + 6]);
+						i += 7;
+						break;
+
+					case WACOM_24HDT:
+						features->y_max =
+							get_unaligned_le16(&report[i + 3]);
+						features->y_phy =
+							get_unaligned_le16(&report[i - 2]);
+						i += 7;
+						break;
+
+					case BAMBOO_PT:
+						features->y_phy =
+							get_unaligned_le16(&report[i + 3]);
+						features->y_max =
+							get_unaligned_le16(&report[i + 6]);
+						i += 12;
+						break;
+
+					default:
 						features->y_max =
+							features->x_max;
+						features->y_phy =
 							get_unaligned_le16(&report[i + 3]);
 						i += 4;
+						break;
 					}
+				} else if (pen) {
+					features->y_max =
+						get_unaligned_le16(&report[i + 3]);
+					i += 4;
 				}
 				break;
 
@@ -489,7 +470,7 @@ static int wacom_parse_hid(struct usb_interface *intf,
 
 		case HID_COLLECTION_END:
 			/* reset UsagePage and Finger */
-			finger = usage = 0;
+			finger = page = 0;
 			break;
 
 		case HID_COLLECTION:
-- 
1.8.5.3


^ permalink raw reply related

* [PATCH v2 5/6] HID: sony: Set initial battery level to 100% to avoid false low battery warnings
From: Frank Praznik @ 2014-01-30 17:24 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
In-Reply-To: <1391102648-19381-1-git-send-email-frank.praznik@oh.rr.com>

Set initial battery level to 100% to avoid false low battery warnings if a the
battery level is polled before a report containing the battery information is
received.

Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>

---
 drivers/hid/hid-sony.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 9aa20ff..5564446 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -1404,6 +1404,11 @@ static int sony_battery_probe(struct sony_sc *sc)
 	struct hid_device *hdev = sc->hdev;
 	int ret;
 
+	/* Set the default battery level to 100% to avoid low battery warnings
+	 * if the battery is polled before the first device report is received.
+	 */
+	sc->battery_capacity = 100;
+
 	power_id = (unsigned long)atomic_inc_return(&power_id_seq);
 
 	sc->battery.properties = sony_battery_props;
-- 
1.8.5.3


^ permalink raw reply related

* [PATCH v2 6/6] HID: sony: Add conditionals to enable all features in Bluetooth mode
From: Frank Praznik @ 2014-01-30 17:24 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
In-Reply-To: <1391102648-19381-1-git-send-email-frank.praznik@oh.rr.com>

Add the necessary conditionals to enable battery reporting, rumble, LED
settings and touchpad parsing.

Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>

---
 drivers/hid/hid-sony.c | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index eee56d7..d920362 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -44,8 +44,10 @@
 #define DUALSHOCK4_CONTROLLER_USB BIT(5)
 #define DUALSHOCK4_CONTROLLER_BT  BIT(6)
 
-#define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER_USB | BUZZ_CONTROLLER | DUALSHOCK4_CONTROLLER_USB)
-#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT | DUALSHOCK4_CONTROLLER_USB)
+#define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER_USB | BUZZ_CONTROLLER | \
+			  DUALSHOCK4_CONTROLLER_USB | DUALSHOCK4_CONTROLLER_BT)
+#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT | \
+			      DUALSHOCK4_CONTROLLER_USB | DUALSHOCK4_CONTROLLER_BT)
 
 #define MAX_LEDS 4
 
@@ -939,8 +941,9 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
 		swap(rd[47], rd[48]);
 
 		sixaxis_parse_report(sc, rd, size);
-	} else if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && rd[0] == 0x01 &&
-			size == 64) {
+	} else if (((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && rd[0] == 0x01 &&
+			size == 64) || ((sc->quirks & DUALSHOCK4_CONTROLLER_BT)
+			&& rd[0] == 0x11 && size == 78)) {
 		dualshock4_parse_report(sc, rd, size);
 	}
 
@@ -1079,7 +1082,8 @@ static void sony_set_leds(struct hid_device *hdev, const __u8 *leds, int count)
 	if (drv_data->quirks & BUZZ_CONTROLLER && count == 4) {
 		buzz_set_leds(hdev, leds);
 	} else if ((drv_data->quirks & SIXAXIS_CONTROLLER_USB) ||
-		   (drv_data->quirks & DUALSHOCK4_CONTROLLER_USB)) {
+		   (drv_data->quirks & DUALSHOCK4_CONTROLLER_USB) ||
+		   (drv_data->quirks & DUALSHOCK4_CONTROLLER_BT)) {
 		for (n = 0; n < count; n++)
 			drv_data->led_state[n] = leds[n];
 		schedule_work(&drv_data->state_worker);
@@ -1184,7 +1188,8 @@ static int sony_leds_init(struct hid_device *hdev)
 		/* Validate expected report characteristics. */
 		if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7))
 			return -ENODEV;
-	} else if (drv_data->quirks & DUALSHOCK4_CONTROLLER_USB) {
+	} else if ((drv_data->quirks & DUALSHOCK4_CONTROLLER_USB) ||
+		   (drv_data->quirks & DUALSHOCK4_CONTROLLER_BT)) {
 		drv_data->led_count = 3;
 		max_brightness = 255;
 		use_colors = 1;
@@ -1509,7 +1514,8 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	}
 	else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
 		ret = sixaxis_set_operational_bt(hdev);
-	else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
+	else if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) ||
+		 (sc->quirks & DUALSHOCK4_CONTROLLER_BT)) {
 		if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) &&
 		     hdev->ll_driver->output_report == NULL) {
 			hid_err(hdev, "NULL output_report handler\n");
-- 
1.8.5.3


^ permalink raw reply related

* [PATCH v2 4/6] HID: sony: Add Dualshock 4 Bluetooth battery and touchpad parsing
From: Frank Praznik @ 2014-01-30 17:24 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
In-Reply-To: <1391102648-19381-1-git-send-email-frank.praznik@oh.rr.com>

Add offsets for retrieving the battery and touchpad data in Dualshock 4
Bluetooth reports.

Adjust battery capacity calculations when in wireless mode.

Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
 drivers/hid/hid-sony.c | 28 ++++++++++++++++++++--------
 1 file changed, 20 insertions(+), 8 deletions(-)

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 688da52..9aa20ff 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -861,25 +861,34 @@ static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size)
 						struct hid_input, list);
 	struct input_dev *input_dev = hidinput->input;
 	unsigned long flags;
-	int n, offset = 35;
+	int n, offset;
 	__u8 cable_state, battery_capacity, battery_charging;
 
+	/* Battery and touchpad data starts at byte 30 in the USB report and
+	 * 32 in Bluetooth report.
+	 */
+	offset = (sc->quirks & DUALSHOCK4_CONTROLLER_USB) ? 30 : 32;
+
 	/* The lower 4 bits of byte 30 contain the battery level
 	 * and the 5th bit contains the USB cable state.
 	 */
-	cable_state = (rd[30] >> 4) & 0x01;
-	battery_capacity = rd[30] & 0x0F;
+	cable_state = (rd[offset] >> 4) & 0x01;
+	battery_capacity = rd[offset] & 0x0F;
 
-	/* On USB the Dualshock 4 battery level goes from 0 to 11.
-	 * A battery level of 11 means fully charged.
+	/* When a USB power source is connected the battery level ranges from
+	 * 0 to 10, and when running on battery power it ranges from 0 to 9.
+	 * A battery level above 10 when plugged in means charge completed.
 	 */
-	if (cable_state && battery_capacity == 11)
+	if (!cable_state || battery_capacity > 10)
 		battery_charging = 0;
 	else
 		battery_charging = 1;
 
+	if (!cable_state)
+		battery_capacity++;
 	if (battery_capacity > 10)
-		battery_capacity--;
+		battery_capacity = 10;
+
 	battery_capacity *= 10;
 
 	spin_lock_irqsave(&sc->lock, flags);
@@ -888,7 +897,10 @@ static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size)
 	sc->battery_charging = battery_charging;
 	spin_unlock_irqrestore(&sc->lock, flags);
 
-	/* The Dualshock 4 multi-touch trackpad data starts at offset 35 on USB.
+	offset += 5;
+
+	/* The Dualshock 4 multi-touch trackpad data starts at offset 35 on USB
+	 * and 37 on Bluetooth.
 	 * The first 7 bits of the first byte is a counter and bit 8 is a touch
 	 * indicator that is 0 when pressed and 1 when not pressed.
 	 * The next 3 bytes are two 12 bit touch coordinates, X and Y.
-- 
1.8.5.3


^ permalink raw reply related

* [PATCH v2 2/6] HID: sony: Add modified Dualshock 4 Bluetooth HID descriptor
From: Frank Praznik @ 2014-01-30 17:24 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
In-Reply-To: <1391102648-19381-1-git-send-email-frank.praznik@oh.rr.com>

By default, the Dualshock 4 sends controller data via report 1. Once an output
report is received the controller changes from sending data in report 1 to
sending data in report 17, which is unmapped in the default descriptor. The
mappings have to be moved to report 17 to let the HID driver properly process
the incoming reports.

Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>

---
 drivers/hid/hid-sony.c | 214 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 214 insertions(+)

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 7a6d7c9..52162a9 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -336,6 +336,216 @@ static u8 dualshock4_usb_rdesc[] = {
 	0xC0                /*  End Collection                      */
 };
 
+/* The default behavior of the Dualshock 4 is to send reports using report
+ * type 1 when running over Bluetooth. However, as soon as it receives a
+ * report of type 17 to set the LEDs or rumble it starts returning it's state
+ * in report 17 instead of 1.  Since report 17 is undefined in the default HID
+ * descriptor the button and axis definitions must be moved to report 17 or
+ * the HID layer won't process the received input once a report is sent.
+ */
+static u8 dualshock4_bt_rdesc[] = {
+	0x05, 0x01,         /*  Usage Page (Desktop),               */
+	0x09, 0x05,         /*  Usage (Gamepad),                    */
+	0xA1, 0x01,         /*  Collection (Application),           */
+	0x85, 0x01,         /*      Report ID (1),                  */
+	0x75, 0x08,         /*      Report Size (8),                */
+	0x95, 0x0A,         /*      Report Count (9),               */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x06, 0x04, 0xFF,   /*      Usage Page (FF04h),             */
+	0x85, 0x02,         /*      Report ID (2),                  */
+	0x09, 0x24,         /*      Usage (24h),                    */
+	0x95, 0x24,         /*      Report Count (36),              */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0xA3,         /*      Report ID (163),                */
+	0x09, 0x25,         /*      Usage (25h),                    */
+	0x95, 0x30,         /*      Report Count (48),              */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0x05,         /*      Report ID (5),                  */
+	0x09, 0x26,         /*      Usage (26h),                    */
+	0x95, 0x28,         /*      Report Count (40),              */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0x06,         /*      Report ID (6),                  */
+	0x09, 0x27,         /*      Usage (27h),                    */
+	0x95, 0x34,         /*      Report Count (52),              */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0x07,         /*      Report ID (7),                  */
+	0x09, 0x28,         /*      Usage (28h),                    */
+	0x95, 0x30,         /*      Report Count (48),              */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0x08,         /*      Report ID (8),                  */
+	0x09, 0x29,         /*      Usage (29h),                    */
+	0x95, 0x2F,         /*      Report Count (47),              */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x06, 0x03, 0xFF,   /*      Usage Page (FF03h),             */
+	0x85, 0x03,         /*      Report ID (3),                  */
+	0x09, 0x21,         /*      Usage (21h),                    */
+	0x95, 0x26,         /*      Report Count (38),              */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0x04,         /*      Report ID (4),                  */
+	0x09, 0x22,         /*      Usage (22h),                    */
+	0x95, 0x2E,         /*      Report Count (46),              */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0xF0,         /*      Report ID (240),                */
+	0x09, 0x47,         /*      Usage (47h),                    */
+	0x95, 0x3F,         /*      Report Count (63),              */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0xF1,         /*      Report ID (241),                */
+	0x09, 0x48,         /*      Usage (48h),                    */
+	0x95, 0x3F,         /*      Report Count (63),              */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0xF2,         /*      Report ID (242),                */
+	0x09, 0x49,         /*      Usage (49h),                    */
+	0x95, 0x0F,         /*      Report Count (15),              */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0x11,         /*      Report ID (17),                 */
+	0x06, 0x00, 0xFF,   /*      Usage Page (FF00h),             */
+	0x09, 0x20,         /*      Usage (20h),                    */
+	0x95, 0x02,         /*      Report Count (2),               */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x05, 0x01,         /*      Usage Page (Desktop),           */
+	0x09, 0x30,         /*      Usage (X),                      */
+	0x09, 0x31,         /*      Usage (Y),                      */
+	0x09, 0x32,         /*      Usage (Z),                      */
+	0x09, 0x35,         /*      Usage (Rz),                     */
+	0x15, 0x00,         /*      Logical Minimum (0),            */
+	0x26, 0xFF, 0x00,   /*      Logical Maximum (255),          */
+	0x75, 0x08,         /*      Report Size (8),                */
+	0x95, 0x04,         /*      Report Count (4),               */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x09, 0x39,         /*      Usage (Hat Switch),             */
+	0x15, 0x00,         /*      Logical Minimum (0),            */
+	0x25, 0x07,         /*      Logical Maximum (7),            */
+	0x75, 0x04,         /*      Report Size (4),                */
+	0x95, 0x01,         /*      Report Count (1),               */
+	0x81, 0x42,         /*      Input (Variable, Null State),   */
+	0x05, 0x09,         /*      Usage Page (Button),            */
+	0x19, 0x01,         /*      Usage Minimum (01h),            */
+	0x29, 0x0E,         /*      Usage Maximum (0Eh),            */
+	0x15, 0x00,         /*      Logical Minimum (0),            */
+	0x25, 0x01,         /*      Logical Maximum (1),            */
+	0x75, 0x01,         /*      Report Size (1),                */
+	0x95, 0x0E,         /*      Report Count (14),              */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x75, 0x06,         /*      Report Size (6),                */
+	0x95, 0x01,         /*      Report Count (1),               */
+	0x81, 0x01,         /*      Input (Constant),               */
+	0x05, 0x01,         /*      Usage Page (Desktop),           */
+	0x09, 0x33,         /*      Usage (Rx),                     */
+	0x09, 0x34,         /*      Usage (Ry),                     */
+	0x15, 0x00,         /*      Logical Minimum (0),            */
+	0x26, 0xFF, 0x00,   /*      Logical Maximum (255),          */
+	0x75, 0x08,         /*      Report Size (8),                */
+	0x95, 0x02,         /*      Report Count (2),               */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x06, 0x00, 0xFF,   /*      Usage Page (FF00h),             */
+	0x09, 0x20,         /*      Usage (20h),                    */
+	0x95, 0x03,         /*      Report Count (3),               */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x05, 0x01,         /*      Usage Page (Desktop),           */
+	0x19, 0x40,         /*      Usage Minimum (40h),            */
+	0x29, 0x42,         /*      Usage Maximum (42h),            */
+	0x16, 0x00, 0x80,   /*      Logical Minimum (-32768),       */
+	0x26, 0x00, 0x7F,   /*      Logical Maximum (32767),        */
+	0x75, 0x10,         /*      Report Size (16),               */
+	0x95, 0x03,         /*      Report Count (3),               */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x19, 0x43,         /*      Usage Minimum (43h),            */
+	0x29, 0x45,         /*      Usage Maximum (45h),            */
+	0x16, 0xFF, 0xBF,   /*      Logical Minimum (-16385),       */
+	0x26, 0x00, 0x40,   /*      Logical Maximum (16384),        */
+	0x95, 0x03,         /*      Report Count (3),               */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x06, 0x00, 0xFF,   /*      Usage Page (FF00h),             */
+	0x09, 0x20,         /*      Usage (20h),                    */
+	0x15, 0x00,         /*      Logical Minimum (0),            */
+	0x26, 0xFF, 0x00,   /*      Logical Maximum (255),          */
+	0x75, 0x08,         /*      Report Size (8),                */
+	0x95, 0x31,         /*      Report Count (51),              */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x09, 0x21,         /*      Usage (21h),                    */
+	0x75, 0x08,         /*      Report Size (8),                */
+	0x95, 0x4D,         /*      Report Count (77),              */
+	0x91, 0x02,         /*      Output (Variable),              */
+	0x85, 0x12,         /*      Report ID (18),                 */
+	0x09, 0x22,         /*      Usage (22h),                    */
+	0x95, 0x8D,         /*      Report Count (141),             */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x09, 0x23,         /*      Usage (23h),                    */
+	0x91, 0x02,         /*      Output (Variable),              */
+	0x85, 0x13,         /*      Report ID (19),                 */
+	0x09, 0x24,         /*      Usage (24h),                    */
+	0x95, 0xCD,         /*      Report Count (205),             */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x09, 0x25,         /*      Usage (25h),                    */
+	0x91, 0x02,         /*      Output (Variable),              */
+	0x85, 0x14,         /*      Report ID (20),                 */
+	0x09, 0x26,         /*      Usage (26h),                    */
+	0x96, 0x0D, 0x01,   /*      Report Count (269),             */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x09, 0x27,         /*      Usage (27h),                    */
+	0x91, 0x02,         /*      Output (Variable),              */
+	0x85, 0x15,         /*      Report ID (21),                 */
+	0x09, 0x28,         /*      Usage (28h),                    */
+	0x96, 0x4D, 0x01,   /*      Report Count (333),             */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x09, 0x29,         /*      Usage (29h),                    */
+	0x91, 0x02,         /*      Output (Variable),              */
+	0x85, 0x16,         /*      Report ID (22),                 */
+	0x09, 0x2A,         /*      Usage (2Ah),                    */
+	0x96, 0x8D, 0x01,   /*      Report Count (397),             */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x09, 0x2B,         /*      Usage (2Bh),                    */
+	0x91, 0x02,         /*      Output (Variable),              */
+	0x85, 0x17,         /*      Report ID (23),                 */
+	0x09, 0x2C,         /*      Usage (2Ch),                    */
+	0x96, 0xCD, 0x01,   /*      Report Count (461),             */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x09, 0x2D,         /*      Usage (2Dh),                    */
+	0x91, 0x02,         /*      Output (Variable),              */
+	0x85, 0x18,         /*      Report ID (24),                 */
+	0x09, 0x2E,         /*      Usage (2Eh),                    */
+	0x96, 0x0D, 0x02,   /*      Report Count (525),             */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x09, 0x2F,         /*      Usage (2Fh),                    */
+	0x91, 0x02,         /*      Output (Variable),              */
+	0x85, 0x19,         /*      Report ID (25),                 */
+	0x09, 0x30,         /*      Usage (30h),                    */
+	0x96, 0x22, 0x02,   /*      Report Count (546),             */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x09, 0x31,         /*      Usage (31h),                    */
+	0x91, 0x02,         /*      Output (Variable),              */
+	0x06, 0x80, 0xFF,   /*      Usage Page (FF80h),             */
+	0x85, 0x82,         /*      Report ID (130),                */
+	0x09, 0x22,         /*      Usage (22h),                    */
+	0x95, 0x3F,         /*      Report Count (63),              */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0x83,         /*      Report ID (131),                */
+	0x09, 0x23,         /*      Usage (23h),                    */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0x84,         /*      Report ID (132),                */
+	0x09, 0x24,         /*      Usage (24h),                    */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0x90,         /*      Report ID (144),                */
+	0x09, 0x30,         /*      Usage (30h),                    */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0x91,         /*      Report ID (145),                */
+	0x09, 0x31,         /*      Usage (31h),                    */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0x92,         /*      Report ID (146),                */
+	0x09, 0x32,         /*      Usage (32h),                    */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0x93,         /*      Report ID (147),                */
+	0x09, 0x33,         /*      Usage (33h),                    */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0xA0,         /*      Report ID (160),                */
+	0x09, 0x40,         /*      Usage (40h),                    */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0xA4,         /*      Report ID (164),                */
+	0x09, 0x44,         /*      Usage (44h),                    */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0xC0                /*  End Collection                      */
+};
+
 static __u8 ps3remote_rdesc[] = {
 	0x05, 0x01,          /* GUsagePage Generic Desktop */
 	0x09, 0x05,          /* LUsage 0x05 [Game Pad] */
@@ -591,6 +801,10 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
 		hid_info(hdev, "Using modified Dualshock 4 report descriptor with gyroscope axes\n");
 		rdesc = dualshock4_usb_rdesc;
 		*rsize = sizeof(dualshock4_usb_rdesc);
+	} else if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) && *rsize == 357) {
+		hid_info(hdev, "Using modified Dualshock 4 Bluetooth report descriptor\n");
+		rdesc = dualshock4_bt_rdesc;
+		*rsize = sizeof(dualshock4_bt_rdesc);
 	}
 
 	/* The HID descriptor exposed over BT has a trailing zero byte */
-- 
1.8.5.3


^ permalink raw reply related

* [PATCH v2 3/6] HID: sony: Add Bluetooth output report formatting
From: Frank Praznik @ 2014-01-30 17:24 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
In-Reply-To: <1391102648-19381-1-git-send-email-frank.praznik@oh.rr.com>

Add formatting and reporting for Dualshock 4 output reports on Bluetooth.

Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>

---
 drivers/hid/hid-sony.c | 29 +++++++++++++++++++++++------
 1 file changed, 23 insertions(+), 6 deletions(-)

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 52162a9..4d12b4e 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -1268,11 +1268,18 @@ static void dualshock4_state_worker(struct work_struct *work)
 	struct hid_device *hdev = sc->hdev;
 	int offset;
 
-	__u8 buf[32] = { 0 };
+	__u8 buf[78] = { 0 };
 
-	buf[0] = 0x05;
-	buf[1] = 0x03;
-	offset = 4;
+	if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
+		buf[0] = 0x05;
+		buf[1] = 0x03;
+		offset = 4;
+	} else {
+		buf[0] = 0x11;
+		buf[1] = 0xB0;
+		buf[3] = 0x0F;
+		offset = 6;
+	}
 
 #ifdef CONFIG_SONY_FF
 	buf[offset++] = sc->right;
@@ -1285,7 +1292,11 @@ static void dualshock4_state_worker(struct work_struct *work)
 	buf[offset++] = sc->led_state[1];
 	buf[offset++] = sc->led_state[2];
 
-	hdev->ll_driver->output_report(hdev, buf, sizeof(buf));
+	if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
+		hdev->ll_driver->output_report(hdev, buf, 32);
+	else
+		hdev->ll_driver->raw_request(hdev, 0x11, buf, 78,
+				HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
 }
 
 #ifdef CONFIG_SONY_FF
@@ -1482,10 +1493,16 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
 		ret = sixaxis_set_operational_bt(hdev);
 	else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
-		if (hdev->ll_driver->output_report == NULL) {
+		if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) &&
+		     hdev->ll_driver->output_report == NULL) {
 			hid_err(hdev, "NULL output_report handler\n");
 			ret = -EINVAL;
 			goto err_stop;
+		} else if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) &&
+			    hdev->ll_driver->raw_request == NULL) {
+			hid_err(hdev, "NULL raw_request handler\n");
+			ret = -EINVAL;
+			goto err_stop;
 		}
 
 		/* The Dualshock 4 touchpad supports 2 touches and has a
-- 
1.8.5.3


^ permalink raw reply related

* [PATCH v2 1/6] HID: sony: Use low-level transport driver functions
From: Frank Praznik @ 2014-01-30 17:24 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, dh.herrmann, Franz Frank Praznik
In-Reply-To: <1391102648-19381-1-git-send-email-frank.praznik@oh.rr.com>

From: Franz Frank Praznik <frank.praznik@oh.rr.com>

Change the dualshock4_state_worker function to use the low-level output_report
function.

Remove sony_set_output_report since it is no longer used.

Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
 drivers/hid/hid-sony.c | 58 ++++++++++++++++----------------------------------
 1 file changed, 18 insertions(+), 40 deletions(-)

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 2bd3f13..7a6d7c9 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -502,7 +502,6 @@ struct sony_sc {
 	spinlock_t lock;
 	struct hid_device *hdev;
 	struct led_classdev *leds[MAX_LEDS];
-	struct hid_report *output_report;
 	unsigned long quirks;
 	struct work_struct state_worker;
 	struct power_supply battery;
@@ -1053,21 +1052,26 @@ static void dualshock4_state_worker(struct work_struct *work)
 {
 	struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
 	struct hid_device *hdev = sc->hdev;
-	struct hid_report *report = sc->output_report;
-	__s32 *value = report->field[0]->value;
+	int offset;
+
+	__u8 buf[32] = { 0 };
 
-	value[0] = 0x03;
+	buf[0] = 0x05;
+	buf[1] = 0x03;
+	offset = 4;
 
 #ifdef CONFIG_SONY_FF
-	value[3] = sc->right;
-	value[4] = sc->left;
+	buf[offset++] = sc->right;
+	buf[offset++] = sc->left;
+#else
+	offset += 2;
 #endif
 
-	value[5] = sc->led_state[0];
-	value[6] = sc->led_state[1];
-	value[7] = sc->led_state[2];
+	buf[offset++] = sc->led_state[0];
+	buf[offset++] = sc->led_state[1];
+	buf[offset++] = sc->led_state[2];
 
-	hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
+	hdev->ll_driver->output_report(hdev, buf, sizeof(buf));
 }
 
 #ifdef CONFIG_SONY_FF
@@ -1200,33 +1204,6 @@ static void sony_battery_remove(struct sony_sc *sc)
 	sc->battery.name = NULL;
 }
 
-static int sony_set_output_report(struct sony_sc *sc, int req_id, int req_size)
-{
-	struct list_head *head, *list;
-	struct hid_report *report;
-	struct hid_device *hdev = sc->hdev;
-
-	list = &hdev->report_enum[HID_OUTPUT_REPORT].report_list;
-
-	list_for_each(head, list) {
-		report = list_entry(head, struct hid_report, list);
-
-		if (report->id == req_id) {
-			if (report->size < req_size) {
-				hid_err(hdev, "Output report 0x%02x (%i bits) is smaller than requested size (%i bits)\n",
-					req_id, report->size, req_size);
-				return -EINVAL;
-			}
-			sc->output_report = report;
-			return 0;
-		}
-	}
-
-	hid_err(hdev, "Unable to locate output report 0x%02x\n", req_id);
-
-	return -EINVAL;
-}
-
 static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
 					int w, int h)
 {
@@ -1291,10 +1268,11 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
 		ret = sixaxis_set_operational_bt(hdev);
 	else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
-		/* Report 5 (31 bytes) is used to send data to the controller via USB */
-		ret = sony_set_output_report(sc, 0x05, 248);
-		if (ret < 0)
+		if (hdev->ll_driver->output_report == NULL) {
+			hid_err(hdev, "NULL output_report handler\n");
+			ret = -EINVAL;
 			goto err_stop;
+		}
 
 		/* The Dualshock 4 touchpad supports 2 touches and has a
 		 * resolution of 1920x940.
-- 
1.8.5.3


^ permalink raw reply related

* [PATCH v2 0/6] HID: sony: Add full Dualshock 4 support in Bluetooth mode
From: Frank Praznik @ 2014-01-30 17:24 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik

This updated set of patches adds NULL pointer checking for the ll_driver
functions and makes the battery calculation code more robust.

The last patch from the first set that checked for duplicate controllers is
on-hold for now as without a guaranteed way to retrieve the MAC address
of a Bluetooth device from a module it has the potential to break.

^ permalink raw reply

* [PATCH] HID: hid-sensor-hub: Processing for duplicate physical ids
From: Srinivas Pandruvada @ 2014-01-30 16:40 UTC (permalink / raw)
  To: jkosina; +Cc: jic23, linux-input, Srinivas Pandruvada, Archana Patni

In HID sensor hub, HID physical ids are used to represent different
sensors. For example physical id of 0x73 in usage page = 0x20, represents
an accelerometer. The HID sensor hub driver uses this physical ids to
create platform devices using MFD. There is 1:1 correspondence between
an phy id and a client driver.
But in some cases these physical ids are reused. There is a phy id 0xe1,
which specifies a custom sensor, which can exist multiple times to represent
various custom sensors. In this case there can be multiple instances
of client MFD drivers, processing specific custom sensor. In this
case when client driver looks for report id or a field index, it
should still get the report id specific to its own type. This is
also true for reports, they should be directed towards correct instance.
This change introduce a way to parse and tie physical devices to their
correct instance.
Summary of changes:
- To get physical ids, use collections. If a collection of type=physical
exist then use usage id as in the name of platform device name
- As part of the platform data, we assign a hdsev instance, which has
start and end of collection indexes. Using these indexes attributes
can be tied to correct MFD client instances
- When a report is received, call callback with correct hsdev instance.
In this way using its private data stored as part of its registry, it
can distinguish different sensors even when they have same physical and
logical ids.
This patch is co-authored with Archana Patni<archna.patni@intel.com>.

Reported-by: Archana Patni <archana.patni@intel.com>
Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Signed-off-by: Archana Patni <archana.patni@intel.com>
---
 drivers/hid/hid-sensor-hub.c   | 182 ++++++++++++++++++++++-------------------
 include/linux/hid-sensor-hub.h |   6 +-
 2 files changed, 102 insertions(+), 86 deletions(-)

diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c
index ad2b869..80be0c0 100644
--- a/drivers/hid/hid-sensor-hub.c
+++ b/drivers/hid/hid-sensor-hub.c
@@ -56,9 +56,9 @@ struct sensor_hub_pending {
  * @dyn_callback_lock:	spin lock to protect callback list
  * @hid_sensor_hub_client_devs:	Stores all MFD cells for a hub instance.
  * @hid_sensor_client_cnt: Number of MFD cells, (no of sensors attached).
+ * @ref_cnt:		Number of MFD clients have opened this device
  */
 struct sensor_hub_data {
-	struct hid_sensor_hub_device *hsdev;
 	struct mutex mutex;
 	spinlock_t lock;
 	struct sensor_hub_pending pending;
@@ -67,6 +67,7 @@ struct sensor_hub_data {
 	struct mfd_cell *hid_sensor_hub_client_devs;
 	int hid_sensor_client_cnt;
 	unsigned long quirks;
+	int ref_cnt;
 };
 
 /**
@@ -79,6 +80,7 @@ struct sensor_hub_data {
 struct hid_sensor_hub_callbacks_list {
 	struct list_head list;
 	u32 usage_id;
+	struct hid_sensor_hub_device *hsdev;
 	struct hid_sensor_hub_callbacks *usage_callback;
 	void *priv;
 };
@@ -97,20 +99,18 @@ static struct hid_report *sensor_hub_report(int id, struct hid_device *hdev,
 	return NULL;
 }
 
-static int sensor_hub_get_physical_device_count(
-				struct hid_report_enum *report_enum)
+static int sensor_hub_get_physical_device_count(struct hid_device *hdev)
 {
-	struct hid_report *report;
-	struct hid_field *field;
-	int cnt = 0;
+	int i;
+	int count = 0;
 
-	list_for_each_entry(report, &report_enum->report_list, list) {
-		field = report->field[0];
-		if (report->maxfield && field && field->physical)
-			cnt++;
+	for (i = 0; i < hdev->maxcollection; ++i) {
+		struct hid_collection *collection = &hdev->collection[i];
+		if (collection->type == HID_COLLECTION_PHYSICAL)
+			++count;
 	}
 
-	return cnt;
+	return count;
 }
 
 static void sensor_hub_fill_attr_info(
@@ -128,15 +128,23 @@ static void sensor_hub_fill_attr_info(
 
 static struct hid_sensor_hub_callbacks *sensor_hub_get_callback(
 					struct hid_device *hdev,
-					u32 usage_id, void **priv)
+					u32 usage_id,
+					int collection_index,
+					struct hid_sensor_hub_device **hsdev,
+					void **priv)
 {
 	struct hid_sensor_hub_callbacks_list *callback;
 	struct sensor_hub_data *pdata = hid_get_drvdata(hdev);
 
 	spin_lock(&pdata->dyn_callback_lock);
 	list_for_each_entry(callback, &pdata->dyn_callback_list, list)
-		if (callback->usage_id == usage_id) {
+		if (callback->usage_id == usage_id &&
+			(collection_index >=
+				callback->hsdev->start_collection_index) &&
+			(collection_index <
+				callback->hsdev->end_collection_index)) {
 			*priv = callback->priv;
+			*hsdev = callback->hsdev;
 			spin_unlock(&pdata->dyn_callback_lock);
 			return callback->usage_callback;
 		}
@@ -163,6 +171,7 @@ int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev,
 		spin_unlock(&pdata->dyn_callback_lock);
 		return -ENOMEM;
 	}
+	callback->hsdev = hsdev;
 	callback->usage_callback = usage_callback;
 	callback->usage_id = usage_id;
 	callback->priv = NULL;
@@ -320,8 +329,7 @@ int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
 				struct hid_sensor_hub_attribute_info *info)
 {
 	int ret = -1;
-	int i, j;
-	int collection_index = -1;
+	int i;
 	struct hid_report *report;
 	struct hid_field *field;
 	struct hid_report_enum *report_enum;
@@ -335,44 +343,31 @@ int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
 	info->units = -1;
 	info->unit_expo = -1;
 
-	for (i = 0; i < hdev->maxcollection; ++i) {
-		struct hid_collection *collection = &hdev->collection[i];
-		if (usage_id == collection->usage) {
-			collection_index = i;
-			break;
-		}
-	}
-	if (collection_index == -1)
-		goto err_ret;
-
 	report_enum = &hdev->report_enum[type];
 	list_for_each_entry(report, &report_enum->report_list, list) {
 		for (i = 0; i < report->maxfield; ++i) {
 			field = report->field[i];
-			if (field->physical == usage_id &&
-				field->logical == attr_usage_id) {
-				sensor_hub_fill_attr_info(info, i, report->id,
-							  field);
-				ret = 0;
-			} else {
-				for (j = 0; j < field->maxusage; ++j) {
-					if (field->usage[j].hid ==
-					attr_usage_id &&
-					field->usage[j].collection_index ==
-					collection_index) {
-						sensor_hub_fill_attr_info(info,
-							  i, report->id, field);
-						ret = 0;
-						break;
-					}
+			if (field->maxusage) {
+				if (field->physical == usage_id &&
+					(field->logical == attr_usage_id ||
+					field->usage[0].hid ==
+							attr_usage_id) &&
+					(field->usage[0].collection_index >=
+					hsdev->start_collection_index) &&
+					(field->usage[0].collection_index <
+					hsdev->end_collection_index)) {
+
+					sensor_hub_fill_attr_info(info, i,
+								report->id,
+								field);
+					ret = 0;
+					break;
 				}
 			}
-			if (ret == 0)
-				break;
 		}
+
 	}
 
-err_ret:
 	return ret;
 }
 EXPORT_SYMBOL_GPL(sensor_hub_input_get_attribute_info);
@@ -388,7 +383,7 @@ static int sensor_hub_suspend(struct hid_device *hdev, pm_message_t message)
 	list_for_each_entry(callback, &pdata->dyn_callback_list, list) {
 		if (callback->usage_callback->suspend)
 			callback->usage_callback->suspend(
-					pdata->hsdev, callback->priv);
+					callback->hsdev, callback->priv);
 	}
 	spin_unlock(&pdata->dyn_callback_lock);
 
@@ -405,7 +400,7 @@ static int sensor_hub_resume(struct hid_device *hdev)
 	list_for_each_entry(callback, &pdata->dyn_callback_list, list) {
 		if (callback->usage_callback->resume)
 			callback->usage_callback->resume(
-					pdata->hsdev, callback->priv);
+					callback->hsdev, callback->priv);
 	}
 	spin_unlock(&pdata->dyn_callback_lock);
 
@@ -432,6 +427,7 @@ static int sensor_hub_raw_event(struct hid_device *hdev,
 	struct hid_sensor_hub_callbacks *callback = NULL;
 	struct hid_collection *collection = NULL;
 	void *priv = NULL;
+	struct hid_sensor_hub_device *hsdev = NULL;
 
 	hid_dbg(hdev, "sensor_hub_raw_event report id:0x%x size:%d type:%d\n",
 			 report->id, size, report->type);
@@ -466,23 +462,26 @@ static int sensor_hub_raw_event(struct hid_device *hdev,
 				report->field[i]->usage->collection_index];
 		hid_dbg(hdev, "collection->usage %x\n",
 					collection->usage);
-		callback = sensor_hub_get_callback(pdata->hsdev->hdev,
-						report->field[i]->physical,
-							&priv);
+
+		callback = sensor_hub_get_callback(hdev,
+				report->field[i]->physical,
+				report->field[i]->usage[0].collection_index,
+				&hsdev, &priv);
+
 		if (callback && callback->capture_sample) {
 			if (report->field[i]->logical)
-				callback->capture_sample(pdata->hsdev,
+				callback->capture_sample(hsdev,
 					report->field[i]->logical, sz, ptr,
 					callback->pdev);
 			else
-				callback->capture_sample(pdata->hsdev,
+				callback->capture_sample(hsdev,
 					report->field[i]->usage->hid, sz, ptr,
 					callback->pdev);
 		}
 		ptr += sz;
 	}
 	if (callback && collection && callback->send_event)
-		callback->send_event(pdata->hsdev, collection->usage,
+		callback->send_event(hsdev, collection->usage,
 				callback->pdev);
 	spin_unlock_irqrestore(&pdata->lock, flags);
 
@@ -495,7 +494,7 @@ int sensor_hub_device_open(struct hid_sensor_hub_device *hsdev)
 	struct sensor_hub_data *data =  hid_get_drvdata(hsdev->hdev);
 
 	mutex_lock(&data->mutex);
-	if (!hsdev->ref_cnt) {
+	if (!data->ref_cnt) {
 		ret = hid_hw_open(hsdev->hdev);
 		if (ret) {
 			hid_err(hsdev->hdev, "failed to open hid device\n");
@@ -503,7 +502,7 @@ int sensor_hub_device_open(struct hid_sensor_hub_device *hsdev)
 			return ret;
 		}
 	}
-	hsdev->ref_cnt++;
+	data->ref_cnt++;
 	mutex_unlock(&data->mutex);
 
 	return ret;
@@ -515,8 +514,8 @@ void sensor_hub_device_close(struct hid_sensor_hub_device *hsdev)
 	struct sensor_hub_data *data =  hid_get_drvdata(hsdev->hdev);
 
 	mutex_lock(&data->mutex);
-	hsdev->ref_cnt--;
-	if (!hsdev->ref_cnt)
+	data->ref_cnt--;
+	if (!data->ref_cnt)
 		hid_hw_close(hsdev->hdev);
 	mutex_unlock(&data->mutex);
 }
@@ -563,26 +562,19 @@ static int sensor_hub_probe(struct hid_device *hdev,
 	struct sensor_hub_data *sd;
 	int i;
 	char *name;
-	struct hid_report *report;
-	struct hid_report_enum *report_enum;
-	struct hid_field *field;
 	int dev_cnt;
+	struct hid_sensor_hub_device *hsdev;
+	struct hid_sensor_hub_device *last_hsdev = NULL;
 
 	sd = devm_kzalloc(&hdev->dev, sizeof(*sd), GFP_KERNEL);
 	if (!sd) {
 		hid_err(hdev, "cannot allocate Sensor data\n");
 		return -ENOMEM;
 	}
-	sd->hsdev = devm_kzalloc(&hdev->dev, sizeof(*sd->hsdev), GFP_KERNEL);
-	if (!sd->hsdev) {
-		hid_err(hdev, "cannot allocate hid_sensor_hub_device\n");
-		return -ENOMEM;
-	}
+
 	hid_set_drvdata(hdev, sd);
 	sd->quirks = id->driver_data;
-	sd->hsdev->hdev = hdev;
-	sd->hsdev->vendor_id = hdev->vendor;
-	sd->hsdev->product_id = hdev->product;
+
 	spin_lock_init(&sd->lock);
 	spin_lock_init(&sd->dyn_callback_lock);
 	mutex_init(&sd->mutex);
@@ -600,9 +592,8 @@ static int sensor_hub_probe(struct hid_device *hdev,
 	}
 	INIT_LIST_HEAD(&sd->dyn_callback_list);
 	sd->hid_sensor_client_cnt = 0;
-	report_enum = &hdev->report_enum[HID_INPUT_REPORT];
 
-	dev_cnt = sensor_hub_get_physical_device_count(report_enum);
+	dev_cnt = sensor_hub_get_physical_device_count(hdev);
 	if (dev_cnt > HID_MAX_PHY_DEVICES) {
 		hid_err(hdev, "Invalid Physical device count\n");
 		ret = -EINVAL;
@@ -616,42 +607,63 @@ static int sensor_hub_probe(struct hid_device *hdev,
 			ret = -ENOMEM;
 			goto err_stop_hw;
 	}
-	list_for_each_entry(report, &report_enum->report_list, list) {
-		hid_dbg(hdev, "Report id:%x\n", report->id);
-		field = report->field[0];
-		if (report->maxfield && field &&
-					field->physical) {
+
+	for (i = 0; i < hdev->maxcollection; ++i) {
+		struct hid_collection *collection = &hdev->collection[i];
+
+		if (collection->type == HID_COLLECTION_PHYSICAL) {
+
+			hsdev = kzalloc(sizeof(*hsdev), GFP_KERNEL);
+			if (!hsdev) {
+				hid_err(hdev, "cannot allocate hid_sensor_hub_device\n");
+				ret = -ENOMEM;
+				goto err_no_mem;
+			}
+			hsdev->hdev = hdev;
+			hsdev->vendor_id = hdev->vendor;
+			hsdev->product_id = hdev->product;
+			hsdev->start_collection_index = i;
+			if (last_hsdev)
+				last_hsdev->end_collection_index = i;
+			last_hsdev = hsdev;
 			name = kasprintf(GFP_KERNEL, "HID-SENSOR-%x",
-						field->physical);
+					collection->usage);
 			if (name == NULL) {
 				hid_err(hdev, "Failed MFD device name\n");
 					ret = -ENOMEM;
-					goto err_free_names;
+					goto err_no_mem;
 			}
 			sd->hid_sensor_hub_client_devs[
-				sd->hid_sensor_client_cnt].id = PLATFORM_DEVID_AUTO;
+				sd->hid_sensor_client_cnt].id =
+							PLATFORM_DEVID_AUTO;
 			sd->hid_sensor_hub_client_devs[
 				sd->hid_sensor_client_cnt].name = name;
 			sd->hid_sensor_hub_client_devs[
 				sd->hid_sensor_client_cnt].platform_data =
-						sd->hsdev;
+							hsdev;
 			sd->hid_sensor_hub_client_devs[
 				sd->hid_sensor_client_cnt].pdata_size =
-						sizeof(*sd->hsdev);
-			hid_dbg(hdev, "Adding %s:%p\n", name, sd);
+							sizeof(*hsdev);
+			hid_dbg(hdev, "Adding %s:%d\n", name,
+					hsdev->start_collection_index);
 			sd->hid_sensor_client_cnt++;
 		}
 	}
+	if (last_hsdev)
+		last_hsdev->end_collection_index = i;
+
 	ret = mfd_add_devices(&hdev->dev, 0, sd->hid_sensor_hub_client_devs,
 		sd->hid_sensor_client_cnt, NULL, 0, NULL);
 	if (ret < 0)
-		goto err_free_names;
+		goto err_no_mem;
 
 	return ret;
 
-err_free_names:
-	for (i = 0; i < sd->hid_sensor_client_cnt ; ++i)
+err_no_mem:
+	for (i = 0; i < sd->hid_sensor_client_cnt; ++i) {
 		kfree(sd->hid_sensor_hub_client_devs[i].name);
+		kfree(sd->hid_sensor_hub_client_devs[i].platform_data);
+	}
 	kfree(sd->hid_sensor_hub_client_devs);
 err_stop_hw:
 	hid_hw_stop(hdev);
@@ -673,8 +685,10 @@ static void sensor_hub_remove(struct hid_device *hdev)
 		complete(&data->pending.ready);
 	spin_unlock_irqrestore(&data->lock, flags);
 	mfd_remove_devices(&hdev->dev);
-	for (i = 0; i < data->hid_sensor_client_cnt ; ++i)
+	for (i = 0; i < data->hid_sensor_client_cnt; ++i) {
 		kfree(data->hid_sensor_hub_client_devs[i].name);
+		kfree(data->hid_sensor_hub_client_devs[i].platform_data);
+	}
 	kfree(data->hid_sensor_hub_client_devs);
 	hid_set_drvdata(hdev, NULL);
 	mutex_destroy(&data->mutex);
diff --git a/include/linux/hid-sensor-hub.h b/include/linux/hid-sensor-hub.h
index 205eba0..b70cfd7 100644
--- a/include/linux/hid-sensor-hub.h
+++ b/include/linux/hid-sensor-hub.h
@@ -51,13 +51,15 @@ struct hid_sensor_hub_attribute_info {
  * @hdev:		Stores the hid instance.
  * @vendor_id:		Vendor id of hub device.
  * @product_id:		Product id of hub device.
- * @ref_cnt:		Number of MFD clients have opened this device
+ * @start_collection_index: Starting index for a phy type collection
+ * @end_collection_index: Last index for a phy type collection
  */
 struct hid_sensor_hub_device {
 	struct hid_device *hdev;
 	u32 vendor_id;
 	u32 product_id;
-	int ref_cnt;
+	int start_collection_index;
+	int end_collection_index;
 };
 
 /**
-- 
1.8.3.2


^ permalink raw reply related

* [PATCH v4 RESEND] ims-pcu: Add commands supported by the new version of the FW
From: Andrey Smirnov @ 2014-01-30 15:40 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: Can I limit the scope of an input device to specific processes ?
From: Fabien André @ 2014-01-30 14:42 UTC (permalink / raw)
  To: R.Zimmermann, xorg; +Cc: David Herrmann, linux-input
In-Reply-To: <52EA1A94.8090300@UKE.Uni-Hamburg.de>

Hi Roger,


On Thu, Jan 30, 2014 at 10:25 AM, Dr. Zimmermann
<R.Zimmermann@uke.uni-hamburg.de> wrote:
> Or is there a better forum to ask about X (input) stuff?

xorg list (added) would definitely be a better place to discuss this topic.

> that looks promising: I was able to separate our response-box-keyboards from
> the core keyboard so that pressed (response-)keys are no longer transmitted
> by default anymore.
>
> Can You shortly point me to the next steps necessary?
> -> Create e new core pointer/keyboard pair and connect the
> reponse-box-keyboard to it?
>

The next steps would be to read events through xinput, that is,
instead of consuming event from your GUI toolkit, retrieve them
directly from X. ( It may exist supporting tool for that).
Main steps are
 * retrieve a display, (XOpenDisplay)
 * retrieve your device based on name (oR id),  (XIQueryDevice )
 * bind your display with some events of your device, (XISetMask,
XISelectEvents )
 * retrieve a file descriptor for your display event, feed your
mainloop with this file descriptor (ConnectionNumber)
 * read and decode event (XPending, XNextEvent, XGetEventData)

Here is an example tool relaying xinput events to a software bus you
may find interseting to throw a glance:
http://svn.tls.cena.fr/wsvn/ivy/xinput2-ivy/trunk/xinput2_ivy.c

I do not see the need for another pointer/keyboard (that would not be
core by the way) unless you use a tool that support multiple keyboard
(I know of no such tool).

Regards,
Fabien


On Thu, Jan 30, 2014 at 10:25 AM, Dr. Zimmermann
<R.Zimmermann@uke.uni-hamburg.de> wrote:
> Am 29.01.2014 13:31, schrieb Fabien André:
>
>> An alternative to X config file is the xinput command line tool
>>
>> $> xinput list
>> to get the name and id of your device
>> $>xinput float NAME_OR_ID
>> to "detach" the device from the virtual core keyboard
>> (the name will be stable while id can change after unplug or restart, thus
>> name is a better candidate for scripting)
>
>
> Hi Fabien,
>
> that looks promising: I was able to separate our response-box-keyboards from
> the core keyboard so that pressed (response-)keys are no longer transmitted
> by default anymore.
>
> Can You shortly point me to the next steps necessary?
> -> Create e new core pointer/keyboard pair and connect the
> reponse-box-keyboard to it?
>
> Or is there a better forum to ask about X (input) stuff?
>
> Thanx, Roger
>
>>
>>
>>
>> On Wed, Jan 29, 2014 at 10:44 AM, Dr. Zimmermann <
>> R.Zimmermann@uke.uni-hamburg.de> wrote:
>>
>>> Am 28.01.2014 13:49, schrieb David Herrmann:
>>>
>>>> Hi
>>>>
>>>>   Hi David,
>>>
>>>
>>>   On Tue, Jan 28, 2014 at 1:27 PM, Dr. Zimmermann
>>>>
>>>> <R.Zimmermann@uke.uni-hamburg.de> wrote:
>>>>
>>>>> Am 28.01.2014 12:15, schrieb David Herrmann:
>>>>>
>>>>>   Hi
>>>>>>
>>>>>>
>>>>>> On Tue, Jan 28, 2014 at 11:20 AM, Dr. Zimmermann
>>>>>> <R.Zimmermann@uke.uni-hamburg.de> wrote:
>>>>>>
>>>>>>>
>>>>>>> Hi,
>>>>>>>
>>>>>>> we've got a special type of keyboard which should only be available
>>>>>>> in
>>>>>>> one
>>>>>>> specific process and not publicly, as usually keyboards are.
>>>>>>>
>>>>>>> Is it possible to limit the scope of a specific keyboard?
>>>>>>>
>>>>>>> Or is there a more suited forum where to ask?
>>>>>>>
>>>>>>
>>>>>
>>>>> Hi David,
>>>>>
>>>>>
>>>>>
>>>>>> There is no such interface in the kernel. However, with careful
>>>>>> access-management, you can get what you want by just allowing your
>>>>>> single process access to the device. But I guess you are working with
>>>>>> an X11 system, so a description of what you actually want to achieve
>>>>>> would help a lot.
>>>>>>
>>>>>
>>>>>
>>>>> Thnx for Your quick reply.
>>>>>
>>>>> (Yes, we are using X. No, the problem is not specific to X: It'll also
>>>>> occur
>>>>> when You switch between system consoles.)
>>>>>
>>>>
>>>> Just don't use the system-console for that. It's not intended for that
>>>> purpose and no-one will enhance it to do device-separation (why would
>>>> you.. it's mainly meant as system-administrator fallback). But see
>>>> below..
>>>>
>>>
>>> We've never intended to use it for our purposes, still the effect that
>>> we've got is the same than under X.
>>>
>>>
>>>>   For research purposes we are measuring human brain activity under the
>>>>>
>>>>> influence of external stimuli (visual/auditory/somatosensory). The
>>>>> subject
>>>>> has to reply to tasks using 'button boxes' which are implemented as
>>>>> keyboards. Usually, type and time of the buttonpress are of importance
>>>>> (and
>>>>> will probably change further procedere). Keyboard responses usually go
>>>>> to
>>>>> that window (or system console) which has the current focus or is
>>>>> active
>>>>> and
>>>>> *this* is something, we do not want.
>>>>>
>>>>> Sometimes we show visual stimuli on one screen(#1) and operate the
>>>>> system
>>>>> from another screen(#2). Sometimes we even have no visual stimuli at
>>>>> all,
>>>>> but like to read subject responses and control the stimuli from within
>>>>> a
>>>>> control window. In all cases it would be helpful if the response-boxes
>>>>> would
>>>>> not automatically send their output to the active window resp.
>>>>> application.
>>>>>
>>>>> Helpful would be, if the output of the response-box-keyboards
>>>>> * could be restricted to a specific process
>>>>> * is not sent automatically, but only to a program which 'opened' the
>>>>> device
>>>>> * is not sent automatically, but has to be read from a device.
>>>>> (Could this be done preserving the exact timepoint of each
>>>>> buttonpress?)
>>>>>
>>>>
>>>> First of all, nothing is "sent automatically". Processes have to read
>>>> the events from the kernel, so point #3 doesn't make much sense to me.
>>>>
>>>
>>> Think of me as quite unaware in these matters. I'm seeing this 'signal'
>>> (keyboard events) distributed by default to every application which gets
>>> active/the focus.
>>>
>>>   But regarding your issue: You should simply configure you xserver to
>>>>
>>>> not use the devices in question. Add an X11 config to
>>>> /etc/X11/xorg.conf.d/50-ignore-custom-devices.conf which contains
>>>> something like:
>>>>
>>>> Section "InputClass"
>>>>       Identifier "Custom Input Blacklist"
>>>>       MatchProduct "<Product-Name-of-Your-Device>"
>>>>       MatchDevicePath "/dev/input/event*"
>>>>       Option "Ignore" "on"
>>>> EndSection
>>>>
>>>> You can read the product-name of your device via: `cat
>>>> /sys/class/input/input<num>/name`
>>>> where "input<num>" is your device in question.
>>>>
>>>> This will cause the Xserver to stop using your input devices and no
>>>> window will get any events from them. Now you can program your custom
>>>> applications to open the correct devices manually and reading events
>>>> from them. However, this requires modifications to these applications.
>>>> But if you just want your xserver to route specific events to specific
>>>> processes, your applications must be XInput2-aware/capable.
>>>>
>>>> Instructing the kernel to route specific events to specific processes
>>>> is the wrong approach here (except regarding the linux-console, which
>>>> is special but *really* shouldn't be used for such stuff). The kernel
>>>> does not care for policy but only provides hardware-abstraction. It's
>>>> up to your applications to use the correct X11 APIs to only read
>>>> events from specific XInput2-devices. So the correct people to ask
>>>> about this are actually the developers of your applications and the
>>>> X11 people. The kernel, by default, restricts input-device access to
>>>> root, so basically *no* user gets input access. The X-Server now
>>>> applies some default policies on these devices to route events to X11
>>>> applications. By default, hot-plugging is enabled in new X-Servers and
>>>> all devices are markes as Master devices. This means, their events are
>>>> broad-casted to all X11 windows. If you don't want that, you can mark
>>>> devices as "Floating", which means, the device will not broadcast
>>>> events, but use the XInput API to report input events. Only
>>>> applications which use XInput can now request events from the device.
>>>>
>>>> You can mark devices as floating via:
>>>> Section "InputClass"
>>>>       Identifier "Custom Input Blacklist"
>>>>       MatchProduct "<Product-Name-of-Your-Device>"
>>>>       MatchDevicePath "/dev/input/event*"
>>>>       Option "Floating" "on"
>>>> EndSection
>>>>
>>>> Also see "man xorg.conf" and search for "Floating".
>>>>
>>>
>>> Thanks for this explanation. It helped a lot and I now see, which 'path'
>>> to go on...
>>>
>>>>
>>>> I hope that helps! I it's still unclear, let me know.
>>>> As a side-note, please always put the mailing-lists on CC. I'm not
>>>> doing that myself now, as I don't know whether you excluded them on
>>>> purpose. But if you start a discussion on the mailing-list, you should
>>>> keep it there so other people can comment, too. Most mail-clients
>>>> provide the "reply to all" feature to do that automatically.
>>>>
>>> My mailer didn't do it correctly. But short time after the mail to You,
>>> the same mail was sent to the list manually...
>>>
>>>>
>>>> Thanks
>>>> David
>>>>
>>>
>>> Thanks again & Regards,
>>> Roger
>>>
>>>   .
>>>>
>>>>
>>>>
>>> --
>>>
>>> Besuchen Sie uns auf: www.uke.de
>>> _____________________________________________________________________
>>>
>>> Universitätsklinikum Hamburg-Eppendorf; Körperschaft des öffentlichen
>>> Rechts; Gerichtsstand: Hamburg
>>> Vorstandsmitglieder: Prof. Dr. Christian Gerloff (Vertreter des
>>> Vorsitzenden), Prof. Dr. Dr. Uwe Koch-Gromus, Joachim Prölß, Rainer
>>> Schoppik
>>> _____________________________________________________________________
>>>
>>> SAVE PAPER - THINK BEFORE PRINTING
>>>
>>
>
> --
>
> Besuchen Sie uns auf: www.uke.de
> _____________________________________________________________________
>
> Universitätsklinikum Hamburg-Eppendorf; Körperschaft des öffentlichen
> Rechts; Gerichtsstand: Hamburg
> Vorstandsmitglieder: Prof. Dr. Christian Gerloff (Vertreter des
> Vorsitzenden), Prof. Dr. Dr. Uwe Koch-Gromus, Joachim Prölß, Rainer Schoppik
> _____________________________________________________________________
>
> SAVE PAPER - THINK BEFORE PRINTING
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v2] HID: i2c-hid: add runtime PM support
From: Jiri Kosina @ 2014-01-30 12:05 UTC (permalink / raw)
  To: Mika Westerberg; +Cc: linux-input, Benjamin Tissoires, linux-kernel
In-Reply-To: <1390987476-1905-1-git-send-email-mika.westerberg@linux.intel.com>

On Wed, 29 Jan 2014, Mika Westerberg wrote:

> This patch adds runtime PM support for the HID over I2C driver. When the
> i2c-hid device is first opened we power it on and on the last close we
> power it off. This is actually what the driver is already doing but in
> addition it allows subsystems, like ACPI power domain to power off the
> device during runtime PM suspend, which should save even more power.
> 
> The implementation is not the most power efficient because it needs some
> interaction from the userspace (e.g close the device node whenever we are
> no more interested in getting events), nevertheless it allows us to save
> some power and works with devices that are not wake capable.
> 
> Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
> ---
> v2:
>   - Update changelog to mention that ACPI power domain for example can
>     power off the device on suspend.
>   - Make i2c_hid_power to just call RPM get/put

Applied, thanks Mika, thanks Benjamin.

> 
>  drivers/hid/i2c-hid/i2c-hid.c | 68 ++++++++++++++++++++++++++++++++++++-------
>  1 file changed, 57 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
> index d1f81f52481a..923ff818a1bf 100644
> --- a/drivers/hid/i2c-hid/i2c-hid.c
> +++ b/drivers/hid/i2c-hid/i2c-hid.c
> @@ -25,6 +25,7 @@
>  #include <linux/delay.h>
>  #include <linux/slab.h>
>  #include <linux/pm.h>
> +#include <linux/pm_runtime.h>
>  #include <linux/device.h>
>  #include <linux/wait.h>
>  #include <linux/err.h>
> @@ -454,10 +455,18 @@ static void i2c_hid_init_reports(struct hid_device *hid)
>  		return;
>  	}
>  
> +	/*
> +	 * The device must be powered on while we fetch initial reports
> +	 * from it.
> +	 */
> +	pm_runtime_get_sync(&client->dev);
> +
>  	list_for_each_entry(report,
>  		&hid->report_enum[HID_FEATURE_REPORT].report_list, list)
>  		i2c_hid_init_report(report, inbuf, ihid->bufsize);
>  
> +	pm_runtime_put(&client->dev);
> +
>  	kfree(inbuf);
>  }
>  
> @@ -703,8 +712,8 @@ static int i2c_hid_open(struct hid_device *hid)
>  
>  	mutex_lock(&i2c_hid_open_mut);
>  	if (!hid->open++) {
> -		ret = i2c_hid_set_power(client, I2C_HID_PWR_ON);
> -		if (ret) {
> +		ret = pm_runtime_get_sync(&client->dev);
> +		if (ret < 0) {
>  			hid->open--;
>  			goto done;
>  		}
> @@ -712,7 +721,7 @@ static int i2c_hid_open(struct hid_device *hid)
>  	}
>  done:
>  	mutex_unlock(&i2c_hid_open_mut);
> -	return ret;
> +	return ret < 0 ? ret : 0;
>  }
>  
>  static void i2c_hid_close(struct hid_device *hid)
> @@ -729,7 +738,7 @@ static void i2c_hid_close(struct hid_device *hid)
>  		clear_bit(I2C_HID_STARTED, &ihid->flags);
>  
>  		/* Save some power */
> -		i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
> +		pm_runtime_put(&client->dev);
>  	}
>  	mutex_unlock(&i2c_hid_open_mut);
>  }
> @@ -738,19 +747,18 @@ static int i2c_hid_power(struct hid_device *hid, int lvl)
>  {
>  	struct i2c_client *client = hid->driver_data;
>  	struct i2c_hid *ihid = i2c_get_clientdata(client);
> -	int ret = 0;
>  
>  	i2c_hid_dbg(ihid, "%s lvl:%d\n", __func__, lvl);
>  
>  	switch (lvl) {
>  	case PM_HINT_FULLON:
> -		ret = i2c_hid_set_power(client, I2C_HID_PWR_ON);
> +		pm_runtime_get_sync(&client->dev);
>  		break;
>  	case PM_HINT_NORMAL:
> -		ret = i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
> +		pm_runtime_put(&client->dev);
>  		break;
>  	}
> -	return ret;
> +	return 0;
>  }
>  
>  static struct hid_ll_driver i2c_hid_ll_driver = {
> @@ -973,13 +981,17 @@ static int i2c_hid_probe(struct i2c_client *client,
>  	if (ret < 0)
>  		goto err;
>  
> +	pm_runtime_get_noresume(&client->dev);
> +	pm_runtime_set_active(&client->dev);
> +	pm_runtime_enable(&client->dev);
> +
>  	ret = i2c_hid_fetch_hid_descriptor(ihid);
>  	if (ret < 0)
> -		goto err;
> +		goto err_pm;
>  
>  	ret = i2c_hid_init_irq(client);
>  	if (ret < 0)
> -		goto err;
> +		goto err_pm;
>  
>  	hid = hid_allocate_device();
>  	if (IS_ERR(hid)) {
> @@ -1010,6 +1022,7 @@ static int i2c_hid_probe(struct i2c_client *client,
>  		goto err_mem_free;
>  	}
>  
> +	pm_runtime_put(&client->dev);
>  	return 0;
>  
>  err_mem_free:
> @@ -1018,6 +1031,10 @@ err_mem_free:
>  err_irq:
>  	free_irq(client->irq, ihid);
>  
> +err_pm:
> +	pm_runtime_put_noidle(&client->dev);
> +	pm_runtime_disable(&client->dev);
> +
>  err:
>  	i2c_hid_free_buffers(ihid);
>  	kfree(ihid);
> @@ -1029,6 +1046,11 @@ static int i2c_hid_remove(struct i2c_client *client)
>  	struct i2c_hid *ihid = i2c_get_clientdata(client);
>  	struct hid_device *hid;
>  
> +	pm_runtime_get_sync(&client->dev);
> +	pm_runtime_disable(&client->dev);
> +	pm_runtime_set_suspended(&client->dev);
> +	pm_runtime_put_noidle(&client->dev);
> +
>  	hid = ihid->hid;
>  	hid_destroy_device(hid);
>  
> @@ -1074,7 +1096,31 @@ static int i2c_hid_resume(struct device *dev)
>  }
>  #endif
>  
> -static SIMPLE_DEV_PM_OPS(i2c_hid_pm, i2c_hid_suspend, i2c_hid_resume);
> +#ifdef CONFIG_PM_RUNTIME
> +static int i2c_hid_runtime_suspend(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +
> +	i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
> +	disable_irq(client->irq);
> +	return 0;
> +}
> +
> +static int i2c_hid_runtime_resume(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +
> +	enable_irq(client->irq);
> +	i2c_hid_set_power(client, I2C_HID_PWR_ON);
> +	return 0;
> +}
> +#endif
> +
> +static const struct dev_pm_ops i2c_hid_pm = {
> +	SET_SYSTEM_SLEEP_PM_OPS(i2c_hid_suspend, i2c_hid_resume)
> +	SET_RUNTIME_PM_OPS(i2c_hid_runtime_suspend, i2c_hid_runtime_resume,
> +			   NULL)
> +};
>  
>  static const struct i2c_device_id i2c_hid_id_table[] = {
>  	{ "hid", 0 },
> -- 
> 1.8.5.2
> 

-- 
Jiri Kosina
SUSE Labs

^ permalink raw reply

* Re: [PATCH] HID: i2c-hid: add runtime PM support
From: Mika Westerberg @ 2014-01-30 11:41 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: linux-input, Jiri Kosina, Benjamin Tissoires,
	linux-kernel@vger.kernel.org
In-Reply-To: <CAN+gG=HoPBBkM7v88y-K=q7mkMET-2dn2YcSN6zWi50cLnBqVA@mail.gmail.com>

On Wed, Jan 29, 2014 at 09:57:00AM -0500, Benjamin Tissoires wrote:
> That's great. I hope this will also fix my Lenovo Miix 2 (the T100 is
> too expensive compared to the Dell Venue pro 8 and the Lenovo one) :)
> I think most of my problems are ACPI related (WIFI card not powered
> up, SD-ext reader not working, suspend/resume not there, etc...), so I
> expect great of you :)

Yeah, those devices use some ACPI features that are not well supported in
Linux. Most notably GPIO/Serialbus operation regions are currently not
supported at all. We are in progress of writing support for them.

^ permalink raw reply


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