@@ -360,6 +360,14 @@ config HID_GAMECUBE_ADAPTER
To compile this driver as a module, choose M here: the
module will be called hid-gamecube-adapter.
+config HID_GAMECUBE_ADAPTER_FF
+ bool "Nintendo Gamecube Controller Adapter force feedback"
+ depends on HID_GAMECUBE_ADAPTER
+ select INPUT_FF_MEMLESS
+ ---help---
+ Say Y here if you want to enable force feedback support for Nintendo
+ Gamecube Controller Adapters.
+
config HID_GEMBIRD
tristate "Gembird Joypad"
depends on HID
@@ -20,7 +20,8 @@
#include "usbhid/usbhid.h"
enum gamecube_output {
- GC_CMD_INIT = 0x13
+ GC_CMD_INIT = 0x13,
+ GC_CMD_RUMBLE = 0x11
};
enum gamecube_input {
@@ -54,14 +55,17 @@ enum gamecube_btn {
struct gamecube_ctrl {
struct input_dev __rcu *input;
enum gamecube_ctrl_flags flags;
+ u8 rumble;
struct gamecube_adapter *adpt;
struct work_struct work_connect;
spinlock_t flags_lock;
+ spinlock_t rumble_lock;
};
struct gamecube_adapter {
struct gamecube_ctrl ctrls[4];
struct hid_device *hdev;
+ struct work_struct work_rumble;
};
static int gamecube_hid_send(struct hid_device *hdev, const u8 *data, size_t n)
@@ -83,6 +87,49 @@ static int gamecube_send_cmd_init(struct hid_device *hdev)
return gamecube_hid_send(hdev, initcmd, sizeof(initcmd));
}
+#ifdef CONFIG_HID_GAMECUBE_ADAPTER_FF
+static int gamecube_send_cmd_rumble(struct hid_device *hdev, struct gamecube_ctrl *ctrls)
+{
+ u8 cmd[5] = {GC_CMD_RUMBLE};
+ unsigned long flags;
+ unsigned int i;
+
+ for (i = 0; i < 4; i++) {
+ if (!(ctrls[i].flags & GC_TYPES) || !(ctrls[i].flags & GC_FLAG_EXTRAPOWER))
+ continue;
+ spin_lock_irqsave(&ctrls[i].rumble_lock, flags);
+ cmd[i + 1] = ctrls[i].rumble;
+ spin_unlock_irqrestore(&ctrls[i].rumble_lock, flags);
+ }
+ return gamecube_hid_send(hdev, cmd, sizeof(cmd));
+}
+
+static void gamecube_rumble_worker(struct work_struct *work)
+{
+ struct gamecube_adapter *adpt = container_of(work, struct gamecube_adapter,
+ work_rumble);
+
+ gamecube_send_cmd_rumble(adpt->hdev, adpt->ctrls);
+}
+
+static int gamecube_rumble_play(struct input_dev *dev, void *data,
+ struct ff_effect *eff)
+{
+ struct gamecube_ctrl *ctrl = input_get_drvdata(dev);
+ struct gamecube_adapter *adpt = ctrl->adpt;
+ unsigned long flags;
+
+ if (eff->type != FF_RUMBLE)
+ return 0;
+
+ spin_lock_irqsave(&ctrl->rumble_lock, flags);
+ ctrl->rumble = (eff->u.rumble.strong_magnitude || eff->u.rumble.weak_magnitude);
+ spin_unlock_irqrestore(&ctrl->rumble_lock, flags);
+ schedule_work(&adpt->work_rumble);
+ return 0;
+}
+#endif
+
static const unsigned int gamecube_buttons[] = {
BTN_START, BTN_TR2, BTN_TR, BTN_TL,
BTN_SOUTH, BTN_WEST, BTN_EAST, BTN_NORTH,
@@ -134,6 +181,11 @@ static int gamecube_ctrl_create(struct gamecube_ctrl *ctrl)
input_set_capability(input, EV_KEY, gamecube_buttons[i]);
for (i = 0; i < ARRAY_SIZE(gamecube_axes); i++)
input_set_abs_params(input, gamecube_axes[i], 0, 255, 0, 0);
+#ifdef CONFIG_HID_GAMECUBE_ADAPTER_FF
+ input_set_capability(input, EV_FF, FF_RUMBLE);
+ if (input_ff_create_memless(input, NULL, gamecube_rumble_play))
+ hid_warn(hdev, "failed to create ff memless\n");
+#endif
ret = input_register_device(input);
if (ret)
@@ -269,7 +321,11 @@ static struct gamecube_adapter* gamecube_adpt_create(struct hid_device *hdev)
adpt->ctrls[i].adpt = adpt;
INIT_WORK(&adpt->ctrls[i].work_connect, gamecube_work_connect_cb);
spin_lock_init(&adpt->ctrls[i].flags_lock);
+ spin_lock_init(&adpt->ctrls[i].rumble_lock);
}
+#ifdef CONFIG_HID_GAMECUBE_ADAPTER_FF
+ INIT_WORK(&adpt->work_rumble, gamecube_rumble_worker);
+#endif
return adpt;
}
@@ -281,6 +337,9 @@ static void gamecube_adpt_destroy(struct gamecube_adapter* adpt)
for (i = 0; i < 4; i++) {
gamecube_ctrl_destroy(adpt->ctrls + i);
}
+#ifdef CONFIG_HID_GAMECUBE_ADAPTER_FF
+ cancel_work_sync(&adpt->work_rumble);
+#endif
hid_hw_close(adpt->hdev);
hid_hw_stop(adpt->hdev);
kfree(adpt);
Add rumble support for the hid-gamecube-adapter driver. Rumble is reported with a single output report for all four controllers. Signed-off-by: François-Xavier Carton <fx.carton91@gmail.com> --- drivers/hid/Kconfig | 8 ++++ drivers/hid/hid-gamecube-adapter.c | 61 +++++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-)