From: Sean Young <sean@mess.org>
To: Jiri Kosina <jkosina@suse.cz>,
Dmitry Torokhov <dmitry.torokhov@gmail.com>,
linux-input@vger.kernel.org
Cc: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>, simon@mungewell.org
Subject: Re: [PATCH v2] HID: Add support for pressure sensitive buttons
Date: Mon, 21 Nov 2011 09:58:18 +0000 [thread overview]
Message-ID: <20111121095818.GA3568@pip.mess.org> (raw)
In-Reply-To: <1321393219-19394-1-git-send-email-sean@mess.org>
On Tue, Nov 15, 2011 at 09:40:19PM +0000, Sean Young wrote:
> This driver needs drvdata for each input device, but this has already
> been used for storing a pointer to the hid device. The drvdata of the
> hid device could be used if there was one for each input device, but
> this is not so (HID_QUIRK_MULTI_INPUT with up to four ports). So I've
> reused the private data of the forced feedback to store the data.
>
> Signed-off-by: Sean Young <sean@mess.org>
> ---
I sent this almost a week ago. Any review comments please or is it
good for merging?
Sean
> v2:
> - Explaining what the device is being instructed to do more exactly
> - Fix comment style
> - Add documentation for sysfs attributes in Documentation/ABI
>
> Note:
> - With the documented fields, do we need the available_* sysfs attributes?
> - Is using the same hid report from force feedback and sysfs racey?
>
> Documentation/ABI/testing/sysfs-driver-hid-sjoy | 25 +++
> drivers/hid/Kconfig | 3 +-
> drivers/hid/hid-sjoy.c | 241 ++++++++++++++++++++++-
> drivers/input/ff-memless.c | 8 +
> include/linux/input.h | 1 +
> 5 files changed, 273 insertions(+), 5 deletions(-)
> create mode 100644 Documentation/ABI/testing/sysfs-driver-hid-sjoy
>
> diff --git a/Documentation/ABI/testing/sysfs-driver-hid-sjoy b/Documentation/ABI/testing/sysfs-driver-hid-sjoy
> new file mode 100644
> index 0000000..63845c7
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-driver-hid-sjoy
> @@ -0,0 +1,25 @@
> +What: /sys/bus/hid/drivers/hid-sjoy/<dev>/controller_mode
> +What: /sys/bus/hid/drivers/hid-sjoy/<dev>/available_controller_modes
> +Date November 2011
> +KernelVersion: 3.3
> +Contact: Sean Young <sean@mess.org>
> +Description: available_controller_modes lists the available modes:
> + auto : analog sticks can be enabled or disabled on
> + the controler itself with the analog button (default)
> + digital : disable analog sticks, analog button does not work
> + analog : enable analog sticks, analog button does not work
> + pressure : enable analog sticks and two buttons as pressure
> + sensitive buttons, analog button does not work
> +
> +What: /sys/bus/hid/drivers/hid-sjoy/<dev>/pressure_button_0
> +What: /sys/bus/hid/drivers/hid-sjoy/<dev>/pressure_button_1
> +What: /sys/bus/hid/drivers/hid-sjoy/<dev>/available_pressure_buttons
> +Date November 2011
> +KernelVersion: 3.3
> +Contact: Sean Young <sean@mess.org>
> +Description: Only two buttons can be configured to read pressure
> + sensitivity. The available buttons are listed in
> + available_pressure_buttons: triangle circle cross square l1
> + r1 l2 r2. Note that the two buttons must be different.
> +
> + The default buttons are cross and square.
> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> index 4d07288..92b1e9c 100644
> --- a/drivers/hid/Kconfig
> +++ b/drivers/hid/Kconfig
> @@ -549,8 +549,7 @@ config HID_SMARTJOYPLUS
> Support for SmartJoy PLUS PS2/USB adapter, Super Dual Box,
> Super Joy Box 3 Pro, Super Dual Box Pro, and Super Joy Box 5 Pro.
>
> - Note that DDR (Dance Dance Revolution) mode is not supported, nor
> - is pressure sensitive buttons on the pro models.
> + Note that DDR (Dance Dance Revolution) mode is not supported.
>
> config SMARTJOYPLUS_FF
> bool "SmartJoy PLUS PS2/USB adapter force feedback support"
> diff --git a/drivers/hid/hid-sjoy.c b/drivers/hid/hid-sjoy.c
> index 670da91..cd9ef2a 100644
> --- a/drivers/hid/hid-sjoy.c
> +++ b/drivers/hid/hid-sjoy.c
> @@ -35,10 +35,31 @@
> #ifdef CONFIG_SMARTJOYPLUS_FF
> #include "usbhid/usbhid.h"
>
> +enum mode {
> + MODE_AUTO,
> + MODE_ANALOG,
> + MODE_DIGITAL,
> + MODE_PRESSURE
> +};
> +
> +static const char * const mode_names[] = {
> + "auto", "analog", "digital", "pressure"
> +};
> +
> +static const char * const button_names[] = {
> + "triangle", "circle", "cross", "square", "l1", "r1", "l2", "r2"
> +};
> +
> struct sjoyff_device {
> struct hid_report *report;
> + enum mode mode;
> + int buttons[2];
> };
>
> +/*
> + * The dual shock controller has two vibration feedback motors, one weak
> + * and one strong. The weak one only can only be turned on or off.
> + */
> static int hid_sjoyff_play(struct input_dev *dev, void *data,
> struct ff_effect *effect)
> {
> @@ -53,14 +74,186 @@ static int hid_sjoyff_play(struct input_dev *dev, void *data,
> left = left * 0xff / 0xffff;
> right = (right != 0); /* on/off only */
>
> + sjoyff->report->field[0]->value[0] = 1;
> sjoyff->report->field[0]->value[1] = right;
> sjoyff->report->field[0]->value[2] = left;
> - dev_dbg(&dev->dev, "running with 0x%02x 0x%02x\n", left, right);
> + dev_dbg(&dev->dev, "port %u running with 0x%02x 0x%02x\n",
> + sjoyff->report->id, left, right);
> usbhid_submit_report(hid, sjoyff->report, USB_DIR_OUT);
>
> return 0;
> }
>
> +/*
> + * When the controller is in pressure sensitive mode, two buttons can
> + * be configured to provide pressure sensitivity. The low four bits
> + * of the second byte specify the first button and the high four bits
> + * the second button. If the third byte is 0, the buttons will no
> + * longer report digital presses, when it is 1 they will continue to
> + * report digital presses as normal as well as pressure sensitivity.
> + */
> +static void hid_sjoy_set_buttons(struct device *dev)
> +{
> + struct input_dev *idev = to_input_dev(dev);
> + struct sjoyff_device *sjoy = input_ff_get_data(idev);
> + struct hid_device *hid = input_get_drvdata(idev);
> + struct hid_report *report = sjoy->report;
> +
> + report->field[0]->value[0] = 6;
> + report->field[0]->value[1] = sjoy->buttons[0] | (sjoy->buttons[1] << 4);
> + report->field[0]->value[2] = 1;
> + report->field[0]->value[3] = 0;
> +
> + usbhid_submit_report(hid, report, USB_DIR_OUT);
> +}
> +
> +/*
> + * The controller can be set into different modes. In auto mode, the
> + * analog sticks can be turned on or off using the analog button on the
> + * controller. In analog mode, the analog sticks are enabled and cannot
> + * be turned off with the analog button; with digital mode, the analog
> + * sticks are turned off and cannot be enabled; in pressure mode, the
> + * analog sticks are enabled, and two buttons will report pressure
> + * sensitivity.
> + */
> +static void hid_sjoy_set_mode(struct device *dev, enum mode mode)
> +{
> + struct input_dev *idev = to_input_dev(dev);
> + struct sjoyff_device *sjoy = input_ff_get_data(idev);
> + struct hid_device *hid = input_get_drvdata(idev);
> + struct hid_report *report = sjoy->report;
> +
> + sjoy->mode = mode;
> +
> + report->field[0]->value[0] = 3;
> +
> + switch (mode) {
> + case MODE_AUTO:
> + report->field[0]->value[1] = 0;
> + report->field[0]->value[2] = 2;
> + report->field[0]->value[3] = 0;
> + break;
> + case MODE_ANALOG:
> + report->field[0]->value[1] = 1;
> + report->field[0]->value[2] = 3;
> + report->field[0]->value[3] = 0;
> + break;
> + case MODE_DIGITAL:
> + report->field[0]->value[1] = 0;
> + report->field[0]->value[2] = 3;
> + report->field[0]->value[3] = 0;
> + break;
> + case MODE_PRESSURE:
> + report->field[0]->value[1] = 1;
> + report->field[0]->value[2] = 3;
> + report->field[0]->value[3] = 1;
> + break;
> + }
> +
> + dev_dbg(&hid->dev, "switching port %u mode to %s\n", report->id,
> + mode_names[mode]);
> + usbhid_submit_report(hid, report, USB_DIR_OUT);
> +}
> +
> +static ssize_t store_mode(struct device *dev, struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + int i, rc = -EINVAL;
> +
> + for (i = 0; i < ARRAY_SIZE(mode_names); i++) {
> + if (sysfs_streq(mode_names[i], buf)) {
> + hid_sjoy_set_mode(dev, i);
> + rc = count;
> + break;
> + }
> + }
> +
> + return rc;
> +}
> +
> +#define store_button(button) \
> +static ssize_t store_button_##button(struct device *dev, \
> + struct device_attribute *attr, const char *buf, size_t count) \
> +{ \
> + struct input_dev *idev = to_input_dev(dev); \
> + struct sjoyff_device *sjoy = input_ff_get_data(idev); \
> + struct hid_device *hid = input_get_drvdata(idev); \
> + int i, rc = -EINVAL; \
> + \
> + for (i = 0; i < ARRAY_SIZE(button_names); i++) { \
> + if (sysfs_streq(button_names[i], buf)) { \
> + if (sjoy->buttons[!button] == i) \
> + break; \
> + \
> + dev_dbg(&hid->dev, "port %u pressure button %d: %s\n",\
> + sjoy->report->id, button, button_names[i]); \
> + sjoy->buttons[button] = i; \
> + hid_sjoy_set_buttons(dev); \
> + rc = count; \
> + break; \
> + } \
> + } \
> + \
> + return rc; \
> +} \
> + \
> +static ssize_t show_button_##button(struct device *dev, \
> + struct device_attribute *attr, char *buf) \
> +{ \
> + struct input_dev *idev = to_input_dev(dev); \
> + struct sjoyff_device *sjoy = input_ff_get_data(idev); \
> + \
> + return snprintf(buf, PAGE_SIZE, "%s\n", \
> + button_names[sjoy->buttons[button]]); \
> +}
> +
> +store_button(0);
> +store_button(1);
> +
> +static ssize_t show_mode(struct device *dev, struct device_attribute *attr,
> + char *buf)
> +{
> + struct input_dev *idev = to_input_dev(dev);
> + struct sjoyff_device *sjoy = input_ff_get_data(idev);
> +
> + return snprintf(buf, PAGE_SIZE, "%s\n", mode_names[sjoy->mode]);
> +}
> +
> +static ssize_t show_modes(struct device *dev, struct device_attribute *attr,
> + char *buf)
> +{
> + return snprintf(buf, PAGE_SIZE, "%s\n", "auto analog digital pressure");
> +}
> +
> +static ssize_t show_buttons(struct device *dev, struct device_attribute *attr,
> + char *buf)
> +{
> + return snprintf(buf, PAGE_SIZE, "%s\n",
> + "triangle circle cross square l1 r1 l2 r2");
> +}
> +
> +static DEVICE_ATTR(pressure_button_0, S_IRUGO | S_IWUSR | S_IWGRP,
> + show_button_0, store_button_0);
> +static DEVICE_ATTR(pressure_button_1, S_IRUGO | S_IWUSR | S_IWGRP,
> + show_button_1, store_button_1);
> +static DEVICE_ATTR(available_pressure_buttons, S_IRUGO, show_buttons, NULL);
> +static DEVICE_ATTR(controller_mode, S_IRUGO | S_IWUSR | S_IWGRP, show_mode,
> + store_mode);
> +static DEVICE_ATTR(available_controller_modes, S_IRUGO, show_modes, NULL);
> +
> +static struct attribute *sysfs_attrs[] = {
> + &dev_attr_controller_mode.attr,
> + &dev_attr_available_controller_modes.attr,
> + &dev_attr_pressure_button_0.attr,
> + &dev_attr_pressure_button_1.attr,
> + &dev_attr_available_pressure_buttons.attr,
> + NULL
> +};
> +
> +static struct attribute_group sjoy_attribute_group = {
> + .attrs = sysfs_attrs
> +};
> +
> static int sjoyff_init(struct hid_device *hid)
> {
> struct sjoyff_device *sjoyff;
> @@ -115,17 +308,57 @@ static int sjoyff_init(struct hid_device *hid)
> sjoyff->report->field[0]->value[1] = 0x00;
> sjoyff->report->field[0]->value[2] = 0x00;
> usbhid_submit_report(hid, sjoyff->report, USB_DIR_OUT);
> +
> + /*
> + * Only the pro models support changing mode, and the
> + * same devices also have the noget quirk.
> + */
> + if (!(hid->quirks & HID_QUIRK_NOGET))
> + continue;
> +
> + if (report->field[0]->report_count < 4) {
> + hid_err(hid, "not enough values in the field\n");
> + continue;
> + }
> +
> + hid_sjoy_set_mode(&dev->dev, MODE_AUTO);
> +
> + sjoyff->buttons[0] = 2; /* cross */
> + sjoyff->buttons[1] = 3; /* square */
> +
> + hid_sjoy_set_buttons(&dev->dev);
> +
> + error = sysfs_create_group(&dev->dev.kobj,
> + &sjoy_attribute_group);
> + if (error)
> + hid_warn(hid, "failed to create sysfs attributes: %d\n",
> + error);
> }
>
> hid_info(hid, "Force feedback for SmartJoy PLUS PS2/USB adapter\n");
>
> return 0;
> }
> +
> +static void sjoy_remove(struct hid_device *hid)
> +{
> + struct hid_input *hidinput;
> + struct input_dev *dev;
> +
> + list_for_each_entry(hidinput, &hid->inputs, list) {
> + dev = hidinput->input;
> + sysfs_remove_group(&dev->dev.kobj, &sjoy_attribute_group);
> + }
> +
> + hid_hw_stop(hid);
> +}
> #else
> static inline int sjoyff_init(struct hid_device *hid)
> {
> return 0;
> }
> +
> +#define sjoy_remove NULL
> #endif
>
> static int sjoy_probe(struct hid_device *hdev, const struct hid_device_id *id)
> @@ -154,7 +387,8 @@ err:
> }
>
> static const struct hid_device_id sjoy_devices[] = {
> - { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO) },
> + { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO),
> + .driver_data = HID_QUIRK_NOGET },
> { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_DUAL_BOX_PRO),
> .driver_data = HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET |
> HID_QUIRK_SKIP_OUTPUT_REPORTS },
> @@ -163,7 +397,7 @@ static const struct hid_device_id sjoy_devices[] = {
> HID_QUIRK_SKIP_OUTPUT_REPORTS },
> { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) },
> { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD),
> - .driver_data = HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET |
> + .driver_data = HID_QUIRK_MULTI_INPUT |
> HID_QUIRK_SKIP_OUTPUT_REPORTS },
> { }
> };
> @@ -173,6 +407,7 @@ static struct hid_driver sjoy_driver = {
> .name = "smartjoyplus",
> .id_table = sjoy_devices,
> .probe = sjoy_probe,
> + .remove = sjoy_remove
> };
>
> static int __init sjoy_init(void)
> diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c
> index 117a59a..42c83a5 100644
> --- a/drivers/input/ff-memless.c
> +++ b/drivers/input/ff-memless.c
> @@ -544,3 +544,11 @@ int input_ff_create_memless(struct input_dev *dev, void *data,
> return 0;
> }
> EXPORT_SYMBOL_GPL(input_ff_create_memless);
> +
> +void *input_ff_get_data(struct input_dev *input)
> +{
> + struct ml_device *ml = input->ff->private;
> +
> + return ml->private;
> +}
> +EXPORT_SYMBOL_GPL(input_ff_get_data);
> diff --git a/include/linux/input.h b/include/linux/input.h
> index 771d6d8..d7a1ce2 100644
> --- a/include/linux/input.h
> +++ b/include/linux/input.h
> @@ -1617,6 +1617,7 @@ int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file);
>
> int input_ff_create_memless(struct input_dev *dev, void *data,
> int (*play_effect)(struct input_dev *, void *, struct ff_effect *));
> +void *input_ff_get_data(struct input_dev *dev);
>
> #endif
> #endif
> --
> 1.7.7.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
next prev parent reply other threads:[~2011-11-21 9:58 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-11-15 21:40 [PATCH v2] HID: Add support for pressure sensitive buttons Sean Young
2011-11-21 9:58 ` Sean Young [this message]
2011-11-21 11:34 ` Jussi Kivilinna
2011-11-22 11:47 ` Sean Young
2011-11-22 17:17 ` Jiri Kosina
2011-11-22 17:55 ` Dmitry Torokhov
2011-11-23 10:39 ` Sean Young
2011-11-23 10:43 ` Sean Young
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20111121095818.GA3568@pip.mess.org \
--to=sean@mess.org \
--cc=dmitry.torokhov@gmail.com \
--cc=jkosina@suse.cz \
--cc=jussi.kivilinna@mbnet.fi \
--cc=linux-input@vger.kernel.org \
--cc=simon@mungewell.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.