diff mbox

[RFC_v2,1/4] HID: hid-sony: Add basic IIO support for SixAxis Controller

Message ID 1435105830-2297-2-git-send-email-simon@mungewell.org (mailing list archive)
State New, archived
Delegated to: Jiri Kosina
Headers show

Commit Message

simon@mungewell.org June 24, 2015, 12:30 a.m. UTC
---
 drivers/hid/hid-sony.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 152 insertions(+), 1 deletion(-)

Comments

Antonio Ospite June 24, 2015, 9:13 a.m. UTC | #1
On Tue, 23 Jun 2015 18:30:27 -0600
Simon Wood <simon@mungewell.org> wrote:

> ---
>  drivers/hid/hid-sony.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 152 insertions(+), 1 deletion(-)
> 

Hi Simon, I don't know much about the IIO API, I just have some generic
comments.

> diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
> index 6ca96ce..c4686e3 100644
> --- a/drivers/hid/hid-sony.c
> +++ b/drivers/hid/hid-sony.c
> @@ -36,6 +36,7 @@
>  #include <linux/list.h>
>  #include <linux/idr.h>
>  #include <linux/input/mt.h>
> +#include <linux/iio/iio.h>
>  
>  #include "hid-ids.h"
>  
> @@ -54,6 +55,7 @@
>  				DUALSHOCK4_CONTROLLER)
>  #define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER)
>  #define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER)
> +#define SONY_IIO_SUPPORT SIXAXIS_CONTROLLER
>  
>  #define MAX_LEDS 4
>  
> @@ -835,6 +837,22 @@ struct sony_sc {
>  	__u8 led_delay_on[MAX_LEDS];
>  	__u8 led_delay_off[MAX_LEDS];
>  	__u8 led_count;
> +
> +#if IS_BUILTIN(CONFIG_IIO) || \
> +	(IS_MODULE(CONFIG_IIO) && IS_MODULE(CONFIG_HID_SONY))

This check can be factored out, just define a HAVE_IIO constant when it
passes and check for that. This is mainly for readability, it
avoids having to read the condition over and over.

> +	struct iio_dev *indio_dev;
> +	__u16 last_data[3];
> +};
> +
> +enum sony_iio_axis {
> +	AXIS_ACC_X,
> +	AXIS_ACC_Y,
> +	AXIS_ACC_Z,
> +};
> +
> +struct sony_iio {
> +	struct sony_sc *sc;
> +#endif
>  };
>  
>  static __u8 *sixaxis_fixup(struct hid_device *hdev, __u8 *rdesc,
> @@ -843,7 +861,6 @@ static __u8 *sixaxis_fixup(struct hid_device *hdev, __u8 *rdesc,
>  	*rsize = sizeof(sixaxis_rdesc);
>  	return sixaxis_rdesc;
>  }
> -

unrelated change, which you undo in patch 2 anyway :)

>  static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc,
>  			     unsigned int *rsize)
>  {
> @@ -1047,6 +1064,12 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
>  		swap(rd[45], rd[46]);
>  		swap(rd[47], rd[48]);
>  
> +#if IS_BUILTIN(CONFIG_IIO) || \
> +	(IS_MODULE(CONFIG_IIO) && IS_MODULE(CONFIG_HID_SONY))
> +		sc->last_data[AXIS_ACC_X] = (rd[42] << 8) + rd[41];
> +		sc->last_data[AXIS_ACC_Y] = (rd[44] << 8) + rd[43];
> +		sc->last_data[AXIS_ACC_Z] = (rd[46] << 8) + rd[45];
> +#endif
>  		sixaxis_parse_report(sc, rd, size);
>  	} else if (((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && rd[0] == 0x01 &&
>  			size == 64) || ((sc->quirks & DUALSHOCK4_CONTROLLER_BT)
> @@ -1769,6 +1792,114 @@ static void sony_battery_remove(struct sony_sc *sc)
>  	sc->battery_desc.name = NULL;
>  }
>  
> +#if IS_BUILTIN(CONFIG_IIO) || \
> +	(IS_MODULE(CONFIG_IIO) && IS_MODULE(CONFIG_HID_SONY))
> +
> +static int sony_iio_read_raw(struct iio_dev *indio_dev,
> +			      struct iio_chan_spec const *chan,
> +			      int *val, int *val2,
> +			      long mask)
> +{
> +	struct sony_iio *data = iio_priv(indio_dev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		switch (chan->type) {
> +		case IIO_ACCEL:
> +			*val = data->sc->last_data[chan->address];
> +			return IIO_VAL_INT;
> +		default:
> +			return -EINVAL;
> +		}
> +	case IIO_CHAN_INFO_SCALE:
> +		switch (chan->type) {
> +		case IIO_ACCEL:
> +			*val = 0;	/* 9.80665/117 = 0.084540086 */
> +			*val2 = 84540;

I guess 9.80665 is 'g', but is the 117 taken from the accelerometer
datasheet? What is it? And BTW I get 9.80665/117 = 0.083817521

> +			return IIO_VAL_INT_PLUS_MICRO;
> +		default:
> +			return -EINVAL;
> +		}
> +	case IIO_CHAN_INFO_OFFSET:
> +		switch (chan->type) {
> +		case IIO_ACCEL:
> +			*val = -512;
> +			return IIO_VAL_INT;
> +		default:
> +			return -EINVAL;
> +		}
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +#define SONY_ACC_CHANNEL(_axis) {                                       \
> +	.type = IIO_ACCEL,                                              \
> +	.modified = 1,                                                  \
> +	.channel2 = IIO_MOD_##_axis,                                    \
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),                   \
> +	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |          \
> +		BIT(IIO_CHAN_INFO_OFFSET),                              \
> +	.address = AXIS_ACC_##_axis,                                    \
> +}
> +
> +static const struct iio_chan_spec sony_sixaxis_channels[] = {
> +	SONY_ACC_CHANNEL(X),
> +	SONY_ACC_CHANNEL(Y),
> +	SONY_ACC_CHANNEL(Z),
> +};
> +
> +static const struct iio_info sony_iio_info = {
> +	.read_raw = &sony_iio_read_raw,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static int sony_iio_probe(struct sony_sc *sc)
> +{
> +	struct hid_device *hdev = sc->hdev;
> +	struct iio_dev *indio_dev;
> +	struct sony_iio *data;
> +	int ret;
> +
> +	indio_dev = iio_device_alloc(sizeof(*data));

In general for new code the devm_ variants are preferred, but I am
not sure in this case, maybe others have comments about that?

> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	sc->indio_dev = indio_dev;
> +	data = iio_priv(indio_dev);
> +	data->sc = sc;
> +
> +	indio_dev->dev.parent = &hdev->dev;
> +	indio_dev->name = dev_name(&hdev->dev);
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->info = &sony_iio_info;
> +	indio_dev->channels = sony_sixaxis_channels;
> +	indio_dev->num_channels = 3;
> +
> +	ret = iio_device_register(indio_dev);

if you used the devm_ variant here and in the other patches, the cleanup
below and the sony_iio_remove() function could go away.

> +	if (ret < 0) {
> +		hid_err(hdev, "Unable to register iio device\n");
> +		goto err;
> +	}
> +	return 0;
> +
> +err:
> +	kfree(indio_dev);
> +	sc->indio_dev = NULL;
> +	return ret;
> +}
> +
> +static void sony_iio_remove(struct sony_sc *sc)
> +{
> +	if (!sc->indio_dev)
> +		return;
> +
> +	iio_device_unregister(sc->indio_dev);
> +	kfree(sc->indio_dev);
> +	sc->indio_dev = NULL;
> +}
> +#endif
> +
>  /*
>   * If a controller is plugged in via USB while already connected via Bluetooth
>   * it will show up as two devices. A global list of connected controllers and
> @@ -2073,6 +2204,15 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
>  		}
>  	}
>  
> +#if IS_BUILTIN(CONFIG_IIO) || \
> +	(IS_MODULE(CONFIG_IIO) && IS_MODULE(CONFIG_HID_SONY))
> +	if (sc->quirks & SONY_IIO_SUPPORT) {
> +		ret = sony_iio_probe(sc);
> +		if (ret < 0)
> +			goto err_stop;
> +	}
> +#endif
> +
>  	if (sc->quirks & SONY_FF_SUPPORT) {
>  		ret = sony_init_ff(sc);
>  		if (ret < 0)
> @@ -2087,6 +2227,11 @@ err_stop:
>  		sony_leds_remove(sc);
>  	if (sc->quirks & SONY_BATTERY_SUPPORT)
>  		sony_battery_remove(sc);
> +#if IS_BUILTIN(CONFIG_IIO) || \
> +	(IS_MODULE(CONFIG_IIO) && IS_MODULE(CONFIG_HID_SONY))
> +	if (sc->quirks & SONY_IIO_SUPPORT)
> +		sony_iio_remove(sc);
> +#endif
>  	sony_cancel_work_sync(sc);
>  	kfree(sc->output_report_dmabuf);
>  	sony_remove_dev_list(sc);
> @@ -2107,6 +2252,12 @@ static void sony_remove(struct hid_device *hdev)
>  		sony_battery_remove(sc);
>  	}
>  
> +#if IS_BUILTIN(CONFIG_IIO) || \
> +	(IS_MODULE(CONFIG_IIO) && IS_MODULE(CONFIG_HID_SONY))
> +	if (sc->quirks & SONY_IIO_SUPPORT)
> +		sony_iio_remove(sc);
> +#endif
> +
>  	sony_cancel_work_sync(sc);
>  
>  	kfree(sc->output_report_dmabuf);
> -- 
> 2.1.4
> 
> --
> 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
Daniel Baluta June 24, 2015, 2:29 p.m. UTC | #2
<snip>

>> +static const struct iio_info sony_iio_info = {
>> +     .read_raw = &sony_iio_read_raw,
>> +     .driver_module = THIS_MODULE,
>> +};
>> +
>> +static int sony_iio_probe(struct sony_sc *sc)
>> +{
>> +     struct hid_device *hdev = sc->hdev;
>> +     struct iio_dev *indio_dev;
>> +     struct sony_iio *data;
>> +     int ret;
>> +
>> +     indio_dev = iio_device_alloc(sizeof(*data));
>
> In general for new code the devm_ variants are preferred, but I am
> not sure in this case, maybe others have comments about that?
>
>> +     if (!indio_dev)
>> +             return -ENOMEM;
>> +
>> +     sc->indio_dev = indio_dev;
>> +     data = iio_priv(indio_dev);
>> +     data->sc = sc;
>> +
>> +     indio_dev->dev.parent = &hdev->dev;
>> +     indio_dev->name = dev_name(&hdev->dev);
>> +     indio_dev->modes = INDIO_DIRECT_MODE;
>> +     indio_dev->info = &sony_iio_info;
>> +     indio_dev->channels = sony_sixaxis_channels;
>> +     indio_dev->num_channels = 3;

Use ARRAY_SIZE(sony_sixaxis_channels) here.

>> +
>> +     ret = iio_device_register(indio_dev);
>
> if you used the devm_ variant here and in the other patches, the cleanup
> below and the sony_iio_remove() function could go away.
>
>> +     if (ret < 0) {
>> +             hid_err(hdev, "Unable to register iio device\n");
>> +             goto err;
>> +     }
>> +     return 0;
>> +
>> +err:
>> +     kfree(indio_dev);

Not to mention that the correct way to free an iio_dev is iio_device_free.

>> +     sc->indio_dev = NULL;
>> +     return ret;
>> +}
>> +
>> +static void sony_iio_remove(struct sony_sc *sc)
>> +{
>> +     if (!sc->indio_dev)
>> +             return;
>> +
>> +     iio_device_unregister(sc->indio_dev);
>> +     kfree(sc->indio_dev);
The same here.

>> +     sc->indio_dev = NULL;
>> +}

So, better use the devm_ variant at least for indio_dev allocation.

thanks,
Daniel.
--
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
simon@mungewell.org June 24, 2015, 3:20 p.m. UTC | #3
>> +	case IIO_CHAN_INFO_SCALE:
>> +		switch (chan->type) {
>> +		case IIO_ACCEL:
>> +			*val = 0;	/* 9.80665/117 = 0.084540086 */
>> +			*val2 = 84540;
>
> I guess 9.80665 is 'g', but is the 117 taken from the accelerometer
> datasheet? What is it? And BTW I get 9.80665/117 = 0.083817521

The '117' was taken from experimentation, average value accessed across
all accelerometer axis on a stationary device.

It does appear however that I can not use a calculator ;-) (...must have
been a prior value left there).
Simon.

--
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
Jonathan Cameron July 5, 2015, 1:49 p.m. UTC | #4
On 24/06/15 10:13, Antonio Ospite wrote:
> On Tue, 23 Jun 2015 18:30:27 -0600
> Simon Wood <simon@mungewell.org> wrote:
> 
>> ---
>>  drivers/hid/hid-sony.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++-
>>  1 file changed, 152 insertions(+), 1 deletion(-)
>>
> 
> Hi Simon, I don't know much about the IIO API, I just have some generic
> comments.
> 
>> diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
>> index 6ca96ce..c4686e3 100644
>> --- a/drivers/hid/hid-sony.c
>> +++ b/drivers/hid/hid-sony.c
>> @@ -36,6 +36,7 @@
>>  #include <linux/list.h>
>>  #include <linux/idr.h>
>>  #include <linux/input/mt.h>
>> +#include <linux/iio/iio.h>
>>  
>>  #include "hid-ids.h"
>>  
>> @@ -54,6 +55,7 @@
>>  				DUALSHOCK4_CONTROLLER)
>>  #define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER)
>>  #define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER)
>> +#define SONY_IIO_SUPPORT SIXAXIS_CONTROLLER
>>  
>>  #define MAX_LEDS 4
>>  
>> @@ -835,6 +837,22 @@ struct sony_sc {
>>  	__u8 led_delay_on[MAX_LEDS];
>>  	__u8 led_delay_off[MAX_LEDS];
>>  	__u8 led_count;
>> +
>> +#if IS_BUILTIN(CONFIG_IIO) || \
>> +	(IS_MODULE(CONFIG_IIO) && IS_MODULE(CONFIG_HID_SONY))
> 
> This check can be factored out, just define a HAVE_IIO constant when it
> passes and check for that. This is mainly for readability, it
> avoids having to read the condition over and over.
> 
>> +	struct iio_dev *indio_dev;
>> +	__u16 last_data[3];
>> +};
>> +
>> +enum sony_iio_axis {
>> +	AXIS_ACC_X,
>> +	AXIS_ACC_Y,
>> +	AXIS_ACC_Z,
>> +};
>> +
>> +struct sony_iio {
>> +	struct sony_sc *sc;
>> +#endif
>>  };
>>  
>>  static __u8 *sixaxis_fixup(struct hid_device *hdev, __u8 *rdesc,
>> @@ -843,7 +861,6 @@ static __u8 *sixaxis_fixup(struct hid_device *hdev, __u8 *rdesc,
>>  	*rsize = sizeof(sixaxis_rdesc);
>>  	return sixaxis_rdesc;
>>  }
>> -
> 
> unrelated change, which you undo in patch 2 anyway :)
> 
>>  static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc,
>>  			     unsigned int *rsize)
>>  {
>> @@ -1047,6 +1064,12 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
>>  		swap(rd[45], rd[46]);
>>  		swap(rd[47], rd[48]);
>>  
>> +#if IS_BUILTIN(CONFIG_IIO) || \
>> +	(IS_MODULE(CONFIG_IIO) && IS_MODULE(CONFIG_HID_SONY))
>> +		sc->last_data[AXIS_ACC_X] = (rd[42] << 8) + rd[41];
>> +		sc->last_data[AXIS_ACC_Y] = (rd[44] << 8) + rd[43];
>> +		sc->last_data[AXIS_ACC_Z] = (rd[46] << 8) + rd[45];
>> +#endif
>>  		sixaxis_parse_report(sc, rd, size);
>>  	} else if (((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && rd[0] == 0x01 &&
>>  			size == 64) || ((sc->quirks & DUALSHOCK4_CONTROLLER_BT)
>> @@ -1769,6 +1792,114 @@ static void sony_battery_remove(struct sony_sc *sc)
>>  	sc->battery_desc.name = NULL;
>>  }
>>  
>> +#if IS_BUILTIN(CONFIG_IIO) || \
>> +	(IS_MODULE(CONFIG_IIO) && IS_MODULE(CONFIG_HID_SONY))
>> +
>> +static int sony_iio_read_raw(struct iio_dev *indio_dev,
>> +			      struct iio_chan_spec const *chan,
>> +			      int *val, int *val2,
>> +			      long mask)
>> +{
>> +	struct sony_iio *data = iio_priv(indio_dev);
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_RAW:
>> +		switch (chan->type) {
>> +		case IIO_ACCEL:
>> +			*val = data->sc->last_data[chan->address];
>> +			return IIO_VAL_INT;
>> +		default:
>> +			return -EINVAL;
>> +		}
>> +	case IIO_CHAN_INFO_SCALE:
>> +		switch (chan->type) {
>> +		case IIO_ACCEL:
>> +			*val = 0;	/* 9.80665/117 = 0.084540086 */
>> +			*val2 = 84540;
> 
> I guess 9.80665 is 'g', but is the 117 taken from the accelerometer
> datasheet? What is it? And BTW I get 9.80665/117 = 0.083817521
> 
>> +			return IIO_VAL_INT_PLUS_MICRO;
>> +		default:
>> +			return -EINVAL;
>> +		}
>> +	case IIO_CHAN_INFO_OFFSET:
>> +		switch (chan->type) {
>> +		case IIO_ACCEL:
>> +			*val = -512;
>> +			return IIO_VAL_INT;
>> +		default:
>> +			return -EINVAL;
>> +		}
>> +	}
>> +
>> +	return -EINVAL;
>> +}
>> +
>> +#define SONY_ACC_CHANNEL(_axis) {                                       \
>> +	.type = IIO_ACCEL,                                              \
>> +	.modified = 1,                                                  \
>> +	.channel2 = IIO_MOD_##_axis,                                    \
>> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),                   \
>> +	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |          \
>> +		BIT(IIO_CHAN_INFO_OFFSET),                              \
>> +	.address = AXIS_ACC_##_axis,                                    \
>> +}
>> +
>> +static const struct iio_chan_spec sony_sixaxis_channels[] = {
>> +	SONY_ACC_CHANNEL(X),
>> +	SONY_ACC_CHANNEL(Y),
>> +	SONY_ACC_CHANNEL(Z),
>> +};
>> +
>> +static const struct iio_info sony_iio_info = {
>> +	.read_raw = &sony_iio_read_raw,
>> +	.driver_module = THIS_MODULE,
>> +};
>> +
>> +static int sony_iio_probe(struct sony_sc *sc)
>> +{
>> +	struct hid_device *hdev = sc->hdev;
>> +	struct iio_dev *indio_dev;
>> +	struct sony_iio *data;
>> +	int ret;
>> +
>> +	indio_dev = iio_device_alloc(sizeof(*data));
> 
> In general for new code the devm_ variants are preferred, but I am
> not sure in this case, maybe others have comments about that?
Absolutely for the allocation.   No ordering issues with this one.
> 
>> +	if (!indio_dev)
>> +		return -ENOMEM;
>> +
>> +	sc->indio_dev = indio_dev;
>> +	data = iio_priv(indio_dev);
>> +	data->sc = sc;
>> +
>> +	indio_dev->dev.parent = &hdev->dev;
>> +	indio_dev->name = dev_name(&hdev->dev);
>> +	indio_dev->modes = INDIO_DIRECT_MODE;
>> +	indio_dev->info = &sony_iio_info;
>> +	indio_dev->channels = sony_sixaxis_channels;
>> +	indio_dev->num_channels = 3;
>> +
>> +	ret = iio_device_register(indio_dev);
> 
> if you used the devm_ variant here and in the other patches, the cleanup
> below and the sony_iio_remove() function could go away.
I haven't checked this driver, but rule of thumb for this one is that, if there
is any other related cleanup done without using devm functions then you should
not use the devm_iio_device_register variant as it will mean something has been
cleaned up BEFORE we remove the userspace interfaces that might need it.

> 
>> +	if (ret < 0) {
>> +		hid_err(hdev, "Unable to register iio device\n");
>> +		goto err;
>> +	}
>> +	return 0;
>> +
>> +err:
>> +	kfree(indio_dev);
>> +	sc->indio_dev = NULL;
>> +	return ret;
>> +}
>> +
>> +static void sony_iio_remove(struct sony_sc *sc)
>> +{
>> +	if (!sc->indio_dev)
>> +		return;
>> +
>> +	iio_device_unregister(sc->indio_dev);
>> +	kfree(sc->indio_dev);
>> +	sc->indio_dev = NULL;
>> +}
>> +#endif
>> +
>>  /*
>>   * If a controller is plugged in via USB while already connected via Bluetooth
>>   * it will show up as two devices. A global list of connected controllers and
>> @@ -2073,6 +2204,15 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
>>  		}
>>  	}
>>  
>> +#if IS_BUILTIN(CONFIG_IIO) || \
>> +	(IS_MODULE(CONFIG_IIO) && IS_MODULE(CONFIG_HID_SONY))
>> +	if (sc->quirks & SONY_IIO_SUPPORT) {
>> +		ret = sony_iio_probe(sc);
>> +		if (ret < 0)
>> +			goto err_stop;
>> +	}
>> +#endif
>> +
>>  	if (sc->quirks & SONY_FF_SUPPORT) {
>>  		ret = sony_init_ff(sc);
>>  		if (ret < 0)
>> @@ -2087,6 +2227,11 @@ err_stop:
>>  		sony_leds_remove(sc);
>>  	if (sc->quirks & SONY_BATTERY_SUPPORT)
>>  		sony_battery_remove(sc);
>> +#if IS_BUILTIN(CONFIG_IIO) || \
>> +	(IS_MODULE(CONFIG_IIO) && IS_MODULE(CONFIG_HID_SONY))
>> +	if (sc->quirks & SONY_IIO_SUPPORT)
>> +		sony_iio_remove(sc);
>> +#endif
>>  	sony_cancel_work_sync(sc);
>>  	kfree(sc->output_report_dmabuf);
>>  	sony_remove_dev_list(sc);
>> @@ -2107,6 +2252,12 @@ static void sony_remove(struct hid_device *hdev)
>>  		sony_battery_remove(sc);
>>  	}
>>  
>> +#if IS_BUILTIN(CONFIG_IIO) || \
>> +	(IS_MODULE(CONFIG_IIO) && IS_MODULE(CONFIG_HID_SONY))
>> +	if (sc->quirks & SONY_IIO_SUPPORT)
>> +		sony_iio_remove(sc);
>> +#endif
>> +
>>  	sony_cancel_work_sync(sc);
>>  
>>  	kfree(sc->output_report_dmabuf);
>> -- 
>> 2.1.4
>>
>> --
>> 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
> 
> 

--
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
diff mbox

Patch

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 6ca96ce..c4686e3 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -36,6 +36,7 @@ 
 #include <linux/list.h>
 #include <linux/idr.h>
 #include <linux/input/mt.h>
+#include <linux/iio/iio.h>
 
 #include "hid-ids.h"
 
@@ -54,6 +55,7 @@ 
 				DUALSHOCK4_CONTROLLER)
 #define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER)
 #define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER)
+#define SONY_IIO_SUPPORT SIXAXIS_CONTROLLER
 
 #define MAX_LEDS 4
 
@@ -835,6 +837,22 @@  struct sony_sc {
 	__u8 led_delay_on[MAX_LEDS];
 	__u8 led_delay_off[MAX_LEDS];
 	__u8 led_count;
+
+#if IS_BUILTIN(CONFIG_IIO) || \
+	(IS_MODULE(CONFIG_IIO) && IS_MODULE(CONFIG_HID_SONY))
+	struct iio_dev *indio_dev;
+	__u16 last_data[3];
+};
+
+enum sony_iio_axis {
+	AXIS_ACC_X,
+	AXIS_ACC_Y,
+	AXIS_ACC_Z,
+};
+
+struct sony_iio {
+	struct sony_sc *sc;
+#endif
 };
 
 static __u8 *sixaxis_fixup(struct hid_device *hdev, __u8 *rdesc,
@@ -843,7 +861,6 @@  static __u8 *sixaxis_fixup(struct hid_device *hdev, __u8 *rdesc,
 	*rsize = sizeof(sixaxis_rdesc);
 	return sixaxis_rdesc;
 }
-
 static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc,
 			     unsigned int *rsize)
 {
@@ -1047,6 +1064,12 @@  static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
 		swap(rd[45], rd[46]);
 		swap(rd[47], rd[48]);
 
+#if IS_BUILTIN(CONFIG_IIO) || \
+	(IS_MODULE(CONFIG_IIO) && IS_MODULE(CONFIG_HID_SONY))
+		sc->last_data[AXIS_ACC_X] = (rd[42] << 8) + rd[41];
+		sc->last_data[AXIS_ACC_Y] = (rd[44] << 8) + rd[43];
+		sc->last_data[AXIS_ACC_Z] = (rd[46] << 8) + rd[45];
+#endif
 		sixaxis_parse_report(sc, rd, size);
 	} else if (((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && rd[0] == 0x01 &&
 			size == 64) || ((sc->quirks & DUALSHOCK4_CONTROLLER_BT)
@@ -1769,6 +1792,114 @@  static void sony_battery_remove(struct sony_sc *sc)
 	sc->battery_desc.name = NULL;
 }
 
+#if IS_BUILTIN(CONFIG_IIO) || \
+	(IS_MODULE(CONFIG_IIO) && IS_MODULE(CONFIG_HID_SONY))
+
+static int sony_iio_read_raw(struct iio_dev *indio_dev,
+			      struct iio_chan_spec const *chan,
+			      int *val, int *val2,
+			      long mask)
+{
+	struct sony_iio *data = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		switch (chan->type) {
+		case IIO_ACCEL:
+			*val = data->sc->last_data[chan->address];
+			return IIO_VAL_INT;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_ACCEL:
+			*val = 0;	/* 9.80665/117 = 0.084540086 */
+			*val2 = 84540;
+			return IIO_VAL_INT_PLUS_MICRO;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_OFFSET:
+		switch (chan->type) {
+		case IIO_ACCEL:
+			*val = -512;
+			return IIO_VAL_INT;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	return -EINVAL;
+}
+
+#define SONY_ACC_CHANNEL(_axis) {                                       \
+	.type = IIO_ACCEL,                                              \
+	.modified = 1,                                                  \
+	.channel2 = IIO_MOD_##_axis,                                    \
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),                   \
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |          \
+		BIT(IIO_CHAN_INFO_OFFSET),                              \
+	.address = AXIS_ACC_##_axis,                                    \
+}
+
+static const struct iio_chan_spec sony_sixaxis_channels[] = {
+	SONY_ACC_CHANNEL(X),
+	SONY_ACC_CHANNEL(Y),
+	SONY_ACC_CHANNEL(Z),
+};
+
+static const struct iio_info sony_iio_info = {
+	.read_raw = &sony_iio_read_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static int sony_iio_probe(struct sony_sc *sc)
+{
+	struct hid_device *hdev = sc->hdev;
+	struct iio_dev *indio_dev;
+	struct sony_iio *data;
+	int ret;
+
+	indio_dev = iio_device_alloc(sizeof(*data));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	sc->indio_dev = indio_dev;
+	data = iio_priv(indio_dev);
+	data->sc = sc;
+
+	indio_dev->dev.parent = &hdev->dev;
+	indio_dev->name = dev_name(&hdev->dev);
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->info = &sony_iio_info;
+	indio_dev->channels = sony_sixaxis_channels;
+	indio_dev->num_channels = 3;
+
+	ret = iio_device_register(indio_dev);
+	if (ret < 0) {
+		hid_err(hdev, "Unable to register iio device\n");
+		goto err;
+	}
+	return 0;
+
+err:
+	kfree(indio_dev);
+	sc->indio_dev = NULL;
+	return ret;
+}
+
+static void sony_iio_remove(struct sony_sc *sc)
+{
+	if (!sc->indio_dev)
+		return;
+
+	iio_device_unregister(sc->indio_dev);
+	kfree(sc->indio_dev);
+	sc->indio_dev = NULL;
+}
+#endif
+
 /*
  * If a controller is plugged in via USB while already connected via Bluetooth
  * it will show up as two devices. A global list of connected controllers and
@@ -2073,6 +2204,15 @@  static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 		}
 	}
 
+#if IS_BUILTIN(CONFIG_IIO) || \
+	(IS_MODULE(CONFIG_IIO) && IS_MODULE(CONFIG_HID_SONY))
+	if (sc->quirks & SONY_IIO_SUPPORT) {
+		ret = sony_iio_probe(sc);
+		if (ret < 0)
+			goto err_stop;
+	}
+#endif
+
 	if (sc->quirks & SONY_FF_SUPPORT) {
 		ret = sony_init_ff(sc);
 		if (ret < 0)
@@ -2087,6 +2227,11 @@  err_stop:
 		sony_leds_remove(sc);
 	if (sc->quirks & SONY_BATTERY_SUPPORT)
 		sony_battery_remove(sc);
+#if IS_BUILTIN(CONFIG_IIO) || \
+	(IS_MODULE(CONFIG_IIO) && IS_MODULE(CONFIG_HID_SONY))
+	if (sc->quirks & SONY_IIO_SUPPORT)
+		sony_iio_remove(sc);
+#endif
 	sony_cancel_work_sync(sc);
 	kfree(sc->output_report_dmabuf);
 	sony_remove_dev_list(sc);
@@ -2107,6 +2252,12 @@  static void sony_remove(struct hid_device *hdev)
 		sony_battery_remove(sc);
 	}
 
+#if IS_BUILTIN(CONFIG_IIO) || \
+	(IS_MODULE(CONFIG_IIO) && IS_MODULE(CONFIG_HID_SONY))
+	if (sc->quirks & SONY_IIO_SUPPORT)
+		sony_iio_remove(sc);
+#endif
+
 	sony_cancel_work_sync(sc);
 
 	kfree(sc->output_report_dmabuf);