Skip to content

Commit

Permalink
HID: Add support for Mega World controller force feedback
Browse files Browse the repository at this point in the history
This patch adds support for one of the several Mega World USB game
controller with integrated force feedback. It is a HID based
memory-less game controller, with a weak motor on the left, and a
strong one on the right.

Signed-off-by: frank zago <frank@zago.net>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
  • Loading branch information
frank zago authored and Jiri Kosina committed May 6, 2022
1 parent 5e20645 commit 06be0d6
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 0 deletions.
8 changes: 8 additions & 0 deletions drivers/hid/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,14 @@ config HID_MAYFLASH
Say Y here if you have HJZ Mayflash PS3 game controller adapters
and want to enable force feedback support.

config HID_MEGAWORLD_FF
tristate "Mega World based game controller force feedback support"
depends on USB_HID
select INPUT_FF_MEMLESS
help
Say Y here if you have a Mega World based game controller and want
to have force feedback support for it.

config HID_REDRAGON
tristate "Redragon keyboards"
depends on HID
Expand Down
1 change: 1 addition & 0 deletions drivers/hid/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o
obj-$(CONFIG_HID_MALTRON) += hid-maltron.o
obj-$(CONFIG_HID_MCP2221) += hid-mcp2221.o
obj-$(CONFIG_HID_MAYFLASH) += hid-mf.o
obj-$(CONFIG_HID_MEGAWORLD_FF) += hid-megaworld.o
obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o
obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o
obj-$(CONFIG_HID_MULTITOUCH) += hid-multitouch.o
Expand Down
3 changes: 3 additions & 0 deletions drivers/hid/hid-ids.h
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,9 @@
#define USB_VENDOR_ID_MCS 0x16d0
#define USB_DEVICE_ID_MCS_GAMEPADBLOCK 0x0bcc

#define USB_VENDOR_MEGAWORLD 0x07b5
#define USB_DEVICE_ID_MEGAWORLD_GAMEPAD 0x0312

#define USB_VENDOR_ID_MGE 0x0463
#define USB_DEVICE_ID_MGE_UPS 0xffff
#define USB_DEVICE_ID_MGE_UPS1 0x0001
Expand Down
125 changes: 125 additions & 0 deletions drivers/hid/hid-megaworld.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Vibration support for Mega World controllers
*
* Copyright 2022 Frank Zago
*
* Derived from hid-zpff.c:
* Copyright (c) 2005, 2006 Anssi Hannula <anssi.hannula@gmail.com>
*/

#include <linux/hid.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/slab.h>

#include "hid-ids.h"

struct mwctrl_device {
struct hid_report *report;
s32 *weak;
s32 *strong;
};

static int mwctrl_play(struct input_dev *dev, void *data,
struct ff_effect *effect)
{
struct hid_device *hid = input_get_drvdata(dev);
struct mwctrl_device *mwctrl = data;

*mwctrl->strong = effect->u.rumble.strong_magnitude >> 8;
*mwctrl->weak = effect->u.rumble.weak_magnitude >> 8;

hid_hw_request(hid, mwctrl->report, HID_REQ_SET_REPORT);

return 0;
}

static int mwctrl_init(struct hid_device *hid)
{
struct mwctrl_device *mwctrl;
struct hid_report *report;
struct hid_input *hidinput;
struct input_dev *dev;
int error;
int i;

if (list_empty(&hid->inputs)) {
hid_err(hid, "no inputs found\n");
return -ENODEV;
}
hidinput = list_entry(hid->inputs.next, struct hid_input, list);
dev = hidinput->input;

for (i = 0; i < 4; i++) {
report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, i, 1);
if (!report)
return -ENODEV;
}

mwctrl = kzalloc(sizeof(struct mwctrl_device), GFP_KERNEL);
if (!mwctrl)
return -ENOMEM;

set_bit(FF_RUMBLE, dev->ffbit);

error = input_ff_create_memless(dev, mwctrl, mwctrl_play);
if (error) {
kfree(mwctrl);
return error;
}

mwctrl->report = report;

/* Field 0 is always 2, and field 1 is always 0. The original
* windows driver has a 5 bytes command, where the 5th byte is
* a repeat of the 3rd byte, however the device has only 4
* fields. It could be a bug in the driver, or there is a
* different device that needs it.
*/
report->field[0]->value[0] = 0x02;

mwctrl->strong = &report->field[2]->value[0];
mwctrl->weak = &report->field[3]->value[0];

return 0;
}

static int mwctrl_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret;

ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "parse failed\n");
return ret;
}

ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
if (ret) {
hid_err(hdev, "hw start failed\n");
return ret;
}

ret = mwctrl_init(hdev);
if (ret)
hid_hw_stop(hdev);

return ret;
}

static const struct hid_device_id mwctrl_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_MEGAWORLD,
USB_DEVICE_ID_MEGAWORLD_GAMEPAD) },
{ }
};
MODULE_DEVICE_TABLE(hid, mwctrl_devices);

static struct hid_driver mwctrl_driver = {
.name = "megaworld",
.id_table = mwctrl_devices,
.probe = mwctrl_probe,
};
module_hid_driver(mwctrl_driver);

MODULE_LICENSE("GPL");

0 comments on commit 06be0d6

Please sign in to comment.