From: Sean Young <sean@mess.org>
To: Jiri Kosina <jkosina@suse.cz>,
Jussi Kivilinna <jussi.kivilinna@mbnet.fi>,
linux-input@vger.kernel.org
Cc: Sean Young <sean@mess.org>
Subject: [PATCH] HID: Add support for pressure sensitive buttons
Date: Mon, 14 Nov 2011 20:58:34 +0000 [thread overview]
Message-ID: <1321304314-17424-1-git-send-email-sean@mess.org> (raw)
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>
---
drivers/hid/Kconfig | 3 +-
drivers/hid/hid-sjoy.c | 221 +++++++++++++++++++++++++++++++++++++++++++-
drivers/input/ff-memless.c | 8 ++
include/linux/input.h | 1 +
4 files changed, 228 insertions(+), 5 deletions(-)
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..b458470 100644
--- a/drivers/hid/hid-sjoy.c
+++ b/drivers/hid/hid-sjoy.c
@@ -35,8 +35,25 @@
#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];
};
static int hid_sjoyff_play(struct input_dev *dev, void *data,
@@ -53,14 +70,170 @@ 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;
}
+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; /* if this field is 0, then pressure
+ buttons won't be reported as digital button presses any more */
+ report->field[0]->value[3] = 0;
+
+ usbhid_submit_report(hid, report, USB_DIR_OUT);
+}
+
+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 +288,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 +367,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 +377,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 +387,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
next reply other threads:[~2011-11-14 20:59 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-11-14 20:58 Sean Young [this message]
2011-11-15 14:27 ` [PATCH] HID: Add support for pressure sensitive buttons Jiri Kosina
2011-11-15 17:10 ` simon
2011-11-15 20:18 ` Sean Young
2011-11-16 10:23 ` 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=1321304314-17424-1-git-send-email-sean@mess.org \
--to=sean@mess.org \
--cc=jkosina@suse.cz \
--cc=jussi.kivilinna@mbnet.fi \
--cc=linux-input@vger.kernel.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).