Skip to content

Commit

Permalink
hid: add support for msi-claw controller
Browse files Browse the repository at this point in the history
  • Loading branch information
NeroReflex committed Jul 10, 2024
1 parent 23e5d80 commit cc8538e
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 0 deletions.
11 changes: 11 additions & 0 deletions drivers/hid/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,17 @@ config HID_ASUS
- GL553V series
- GL753V series

config HID_MSI_CLAW
tristate "MSI Claw"
depends on USB_HID
select POWER_SUPPLY
help
Support for MSI Claw HID device used to configure the device
to act either as an xbox controller or keyboard and mouse.

Supported devices:
- MSI Claw

config HID_AUREAL
tristate "Aureal"
help
Expand Down
1 change: 1 addition & 0 deletions drivers/hid/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ 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_MSI_CLAW) += hid-msi-claw.o
obj-$(CONFIG_HID_MULTITOUCH) += hid-multitouch.o
obj-$(CONFIG_HID_NINTENDO) += hid-nintendo.o
obj-$(CONFIG_HID_NTI) += hid-nti.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 @@ -965,6 +965,9 @@
#define USB_VENDOR_ID_MONTEREY 0x0566
#define USB_DEVICE_ID_GENIUS_KB29E 0x3004

#define USB_VENDOR_ID_MSI_FIRST 0x0DB0
#define USB_DEVICE_ID_MSI_CLAW 0x1901

#define USB_VENDOR_ID_MSI 0x1770
#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00

Expand Down
188 changes: 188 additions & 0 deletions drivers/hid/hid-msi-claw.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
#include <linux/dmi.h>
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/usb.h>

#include "hid-ids.h"

#define FEATURE_GAMEPAD_REPORT_ID 0x0f

enum msi_claw_gamepad_mode {
MSI_CLAW_GAMEPAD_MODE_OFFLINE = 0,
MSI_CLAW_GAMEPAD_MODE_XINPUT,
MSI_CLAW_GAMEPAD_MODE_DINPUT,
MSI_CLAW_GAMEPAD_MODE_MSI,
MSI_CLAW_GAMEPAD_MODE_DESKTOP,
MSI_CLAW_GAMEPAD_MODE_BIOS,
MSI_CLAW_GAMEPAD_MODE_TESTING,
MSI_CLAW_GAMEPAD_MODE_MAX
};

enum msi_claw_mkeys_function {
MSI_CLAW_MKEY_FUNCTION_MACRO,
MSI_CLAW_MKEY_FUNCTION_COMBINATION,
};

enum msi_claw_command_type {
MSI_CLAW_COMMAND_TYPE_ENTER_PROFILE_CONFIG = 1,
MSI_CLAW_COMMAND_TYPE_EXIT_PROFILE_CONFIG = 2,
MSI_CLAW_COMMAND_TYPE_WRITE_PROFILE = 3,
MSI_CLAW_COMMAND_TYPE_READ_PROFILE = 4,
MSI_CLAW_COMMAND_TYPE_READ_PROFILE_ACK = 5,
MSI_CLAW_COMMAND_TYPE_ACK = 6,
MSI_CLAW_COMMAND_TYPE_SWITCH_PROFILE = 7,
MSI_CLAW_COMMAND_TYPE_WRITE_PROFILE_TO_EEPROM = 8,
MSI_CLAW_COMMAND_TYPE_READ_FIRMWARE_VERSION = 9,
MSI_CLAW_COMMAND_TYPE_READ_RGB_STATUS_ACK = 10,
MSI_CLAW_COMMAND_TYPE_READ_CURRENT_PROFILE = 11,
MSI_CLAW_COMMAND_TYPE_READ_CURRENT_PROFILE_ACK = 12,
MSI_CLAW_COMMAND_TYPE_READ_RGB_STATUS = 13,
MSI_CLAW_COMMAND_TYPE_SYNC_TO_ROM = 34,
MSI_CLAW_COMMAND_TYPE_RESTORE_FROM_ROM = 35,
MSI_CLAW_COMMAND_TYPE_SWITCH_MODE = 36,
MSI_CLAW_COMMAND_TYPE_READ_GAMEPAD_MODE = 38,
MSI_CLAW_COMMAND_TYPE_GAMEPAD_MODE_ACK = 39,
MSI_CLAW_COMMAND_TYPE_RESET_DEVICE = 40,
MSI_CLAW_COMMAND_TYPE_RGB_CONTROL = 224,
MSI_CLAW_COMMAND_TYPE_CALIBRATION_CONTROL = 253,
MSI_CLAW_COMMAND_TYPE_CALIBRATION_ACK = 254,
};

struct msi_claw_drvdata {
struct hid_device *hdev;
struct input_dev *input;
struct input_dev *tp_kbd_input;
};

static int msi_claw_switch_gamepad_mode(struct hid_device *hdev, enum msi_claw_gamepad_mode mode,
enum msi_claw_mkeys_function mkeys)
{
int ret;
const unsigned char buf[] = {
FEATURE_GAMEPAD_REPORT_ID, 0, 0, 60, MSI_CLAW_COMMAND_TYPE_SWITCH_MODE, (unsigned char)mode, (unsigned char)mkeys
};
unsigned char *dmabuf = kmemdup(buf, sizeof(buf), GFP_KERNEL);

if (!dmabuf) {
ret = -ENOMEM;
hid_err(hdev, "msi-claw failed to alloc dma buf: %d\n", ret);
return ret;
}

ret = hid_hw_raw_request(hdev, dmabuf[0], dmabuf, sizeof(buf),
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);

kfree(dmabuf);

if (ret != sizeof(buf)) {
hid_err(hdev, "msi-claw failed to switch controller mode: %d\n", ret);
return ret;
}

return 0;
}

static int msi_claw_raw_event(struct hid_device *hdev,
struct hid_report *report, u8 *data, int size)
{
struct msi_claw_drvdata *drvdata = hid_get_drvdata(hdev);

return 0;
}

/*
#define asus_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, \
max, EV_KEY, (c))
*/
static int msi_claw_input_mapping(struct hid_device *hdev,
struct hid_input *hi, struct hid_field *field,
struct hid_usage *usage, unsigned long **bit,
int *max)
{
struct msi_claw_drvdata *drvdata = hid_get_drvdata(hdev);

/*
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_ASUSVENDOR) {
switch (usage->hid & HID_USAGE) {
case 0x10: asus_map_key_clear(KEY_BRIGHTNESSDOWN); break;
*/

return 0;
}

static int msi_claw_event(struct hid_device *hdev, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
struct msi_claw_drvdata *drvdata = hid_get_drvdata(hdev);

return 0;
}

static int msi_claw_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret;
struct msi_claw_drvdata *drvdata;

drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
if (drvdata == NULL) {
hid_err(hdev, "msi-claw can't alloc descriptor\n");
return -ENOMEM;
}

hid_set_drvdata(hdev, drvdata);

ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "msi-claw hid parse failed: %d\n", ret);
return ret;
}

ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret) {
hid_err(hdev, "msi-claw hw start failed: %d\n", ret);
return ret;
}

// TODO: remove me
hid_err(hdev, "msi-claw started\n", ret);

ret = msi_claw_switch_gamepad_mode(hdev, MSI_CLAW_GAMEPAD_MODE_MSI, MSI_CLAW_MKEY_FUNCTION_MACRO);
if (ret != 0) {
hid_err(hdev, "msi-claw failed to initialize controller mode: %d\n", ret);
goto err_stop_hw;
}

return 0;

err_stop_hw:
hid_hw_stop(hdev);
return ret;
}

static void msi_claw_remove(struct hid_device *hdev)
{
struct msi_claw_drvdata *drvdata = hid_get_drvdata(hdev);

hid_hw_stop(hdev);
}

static const struct hid_device_id msi_claw_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MSI_FIRST,
USB_DEVICE_ID_MSI_CLAW) },
{ }
};
MODULE_DEVICE_TABLE(hid, msi_claw_devices);

static struct hid_driver msi_claw_driver = {
.name = "msi-claw",
.id_table = msi_claw_devices,
//.report_fixup = asus_report_fixup,
.probe = msi_claw_probe,
.remove = msi_claw_remove,
.input_mapping = msi_claw_input_mapping,
.event = msi_claw_event,
.raw_event = msi_claw_raw_event
};
module_hid_driver(msi_claw_driver);

MODULE_LICENSE("GPL");

0 comments on commit cc8538e

Please sign in to comment.