[RESEND PATCH 1/2] HID: Add driver for RC Simulator Controllers

Marcus Folkesson
2022-08-22T06:05:09Z

Several RC Simulator Controllers are HID compliant with similar
interface.

Add support for these controllers:
- Phoenix RC (HID variant)
- Car VRC2.0
- Real Flight G5/G6/G7
- Aero Fly, FMS
- OrangeRX FrSky

Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
---
Documentation/hid/index.rst | 1 +
Documentation/hid/rcsim.rst | 142 ++++++++++++++++
drivers/hid/Kconfig | 11 ++
drivers/hid/Makefile | 1 +
drivers/hid/hid-ids.h | 5 +
drivers/hid/hid-rcsim.c | 315 ++++++++++++++++++++++++++++++++++++
6 files changed, 475 insertions(+)
create mode 100644 Documentation/hid/rcsim.rst
create mode 100644 drivers/hid/hid-rcsim.c

diff --git a/Documentation/hid/index.rst b/Documentation/hid/index.rst
index e50f513c579c..e5813d264f37 100644
--- a/Documentation/hid/index.rst
+++ b/Documentation/hid/index.rst
@@ -17,3 +17,4 @@ Human Interface Devices (HID)
hid-alps
intel-ish-hid
amd-sfh-hid
+ rcsim
diff --git a/Documentation/hid/rcsim.rst b/Documentation/hid/rcsim.rst
new file mode 100644
index 000000000000..1a031f7189cb
--- /dev/null
+++ b/Documentation/hid/rcsim.rst
@@ -0,0 +1,142 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=================================
+rcsim - RC Simulator Controllers
+=================================
+
+:Author: Marcus Folkesson <marcus.folkesson@gmail.com>
+
+This driver let you use your own RC controller plugged
+into your computer using an HID compatible USB dongle.
+
+There are several HID compatible USB dongles from different
+vendors. The driver currently supports:
+
+- Phoenix RC (HID variant) (8ch)
+- Car VRC2.0 (2ch)
+- Real Flight G5/G6/G7 (6ch)
+- Aero Fly, FMS (8ch)
+- OrangeRX FrSky (6ch)
+
+Many RC controllers is able to configure which stick goes to which channel.
+This is also configurable in most simulators, so a matching is not necessary.
+
+Supported dongles
+==================
+
+PhoenixRC
+----------
+
+The PhoenixRC has one HID compatible variant which is supported by this driver.
+The controller has support for 8 analog channels.
+
+The driver is generating the following input event for on channels:
+
++---------+----------------+
+| Channel | Event |
++=========+================+
+| 1 | ABS_Y |
++---------+----------------+
+| 2 | ABS_X |
++---------+----------------+
+| 3 | ABS_RY |
++---------+----------------+
+| 4 | ABS_RX |
++---------+----------------+
+| 5 | ABS_RUDDER |
++---------+----------------+
+| 6 | ABS_THROTTLE |
++---------+----------------+
+| 7 | ABS_Z |
++---------+----------------+
+| 8 | ABS_RZ |
++---------+----------------+
+
+VRC2.0
+----------
+VRC2.0 is a controller for RC Cars.
+The controller has support for 2 analog channels.
+
+The driver is generating the following input event for on channels:
+
++---------+----------------+
+| Channel | Event |
++=========+================+
+| 1 | ABS_GAS |
++---------+----------------+
+| 2 | ABS_WHEEL |
++---------+----------------+
+
+RealFlight
+----------
+
+This driver supports Realflight G4-G7 and above
+The controller has support for 4 analog channels and two buttons.
+
+The driver is generating the following input event for on channels:
+
++---------+----------------+
+| Channel | Event |
++=========+================+
+| 1 | ABS_Y |
++---------+----------------+
+| 2 | ABS_X |
++---------+----------------+
+| 3 | ABS_RY |
++---------+----------------+
+| 4 | ABS_RX |
++---------+----------------+
+| 5 | BTN_A |
++---------+----------------+
+| 6 | BTN_B |
++---------+----------------+
+
+XTR+G2+FMS Controllers
+--------------------------------
+
+The controllers has support for 8 analog channels.
+
+The driver is generating the following input event for on channels:
+
++---------+----------------+
+| Channel | Event |
++=========+================+
+| 1 | ABS_Y |
++---------+----------------+
+| 2 | ABS_X |
++---------+----------------+
+| 3 | ABS_RY |
++---------+----------------+
+| 4 | ABS_RX |
++---------+----------------+
+| 5 | ABS_RUDDER |
++---------+----------------+
+| 6 | ABS_THROTTLE |
++---------+----------------+
+| 7 | ABS_Z |
++---------+----------------+
+| 8 | ABS_RZ |
++---------+----------------+
+
+OrangeRX
+----------
+
+The controllers has support for 6 analog channels.
+
+The driver is generating the following input event for on channels:
+
++---------+----------------+
+| Channel | Event |
++=========+================+
+| 1 | ABS_Y |
++---------+----------------+
+| 2 | ABS_X |
++---------+----------------+
+| 3 | ABS_RY |
++---------+----------------+
+| 4 | ABS_RX |
++---------+----------------+
+| 5 | ABS_RUDDER |
++---------+----------------+
+| 6 | ABS_THROTTLE |
++---------+----------------+
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 70da5931082f..d8313d36086c 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -957,6 +957,17 @@ config HID_RAZER
Support for Razer devices that are not fully compliant with the
HID standard.

+config HID_RCSIM
+ tristate "RC Simulator Controllers"
+ depends on HID
+ help
+ Support for several HID compatible RC Simulator Controllers including
+ - Phoenix RC
+ - Car VRC2.0
+ - Real Flight
+ - Aero Fly, FMS
+ - OrangeRX FrSky
+
config HID_PRIMAX
tristate "Primax non-fully HID-compliant devices"
depends on HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index cac2cbe26d11..85d50ab352ee 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -102,6 +102,7 @@ obj-$(CONFIG_HID_PLANTRONICS) += hid-plantronics.o
obj-$(CONFIG_HID_PLAYSTATION) += hid-playstation.o
obj-$(CONFIG_HID_PRIMAX) += hid-primax.o
obj-$(CONFIG_HID_RAZER) += hid-razer.o
+obj-$(CONFIG_HID_RCSIM) += hid-rcsim.o
obj-$(CONFIG_HID_REDRAGON) += hid-redragon.o
obj-$(CONFIG_HID_RETRODE) += hid-retrode.o
obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index d9eb676abe96..baf5f74d5bed 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -1381,6 +1381,11 @@

#define USB_VENDOR_ID_MULTIPLE_1781 0x1781
#define USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD 0x0a9d
+#define USB_DEVICE_ID_PHOENIXRC 0x0898
+#define USB_DEVICE_ID_REALFLIGHT 0x0e56
+
+#define USB_VENDOR_ID_DIPLING 0x0B9B
+#define USB_DEVICE_ID_DIPLING_RCCONTROLLER 0x4012

#define USB_VENDOR_ID_DRACAL_RAPHNET 0x289b
#define USB_DEVICE_ID_RAPHNET_2NES2SNES 0x0002
diff --git a/drivers/hid/hid-rcsim.c b/drivers/hid/hid-rcsim.c
new file mode 100644
index 000000000000..0f214cb5816a
--- /dev/null
+++ b/drivers/hid/hid-rcsim.c
@@ -0,0 +1,315 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for several HID compatible RC Simulator Controllers.
+ * Currently supported controllers are:
+ *
+ * - Phoenix RC (HID variant) (8ch)
+ * - Car VRC2.0 (2ch)
+ * - Real Flight G5/G6/G7 (6ch)
+ * - Aero Fly, FMS (8ch)
+ * - OrangeRX FrSky (6ch)
+ *
+ * Copyright (C) 2022 Marcus Folkesson <marcus.folkesson@gmail.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#include "hid-ids.h"
+
+/*
+ * Some of these VID/PID are probably "borrowed", so keep them locally and
+ * do not populate hid-ids.h with those.
+ */
+
+/* PHOENIXRC Controlloer (HID variant) */
+#define PHOENIXRC_VID (USB_VENDOR_ID_MULTIPLE_1781)
+#define PHOENIXRC_PID (USB_DEVICE_ID_PHOENIXRC)
+#define PHOENIXRC_DSIZE (8)
+
+/* VRC2 Controlloer */
+#define VRC2_VID (0x07c0)
+#define VRC2_PID (0x1125)
+#define VRC2_DSIZE (7)
+
+/* Realflight G4-&7 and Above Controller */
+#define REALFLIGHT_VID (USB_VENDOR_ID_MULTIPLE_1781)
+#define REALFLIGHT_PID (USB_DEVICE_ID_REALFLIGHT)
+#define REALFLIGHT_DSIZE (8)
+
+#define REALFLIGHT_BTN_A BIT(0)
+#define REALFLIGHT_BTN_B BIT(1)
+
+/* XTR+G2+FMS Controller */
+#define XTRG2FMS_VID (USB_VENDOR_ID_DIPLING)
+#define XTRG2FMS_PID (USB_DEVICE_ID_DIPLING_RCCONTROLLER)
+#define XTRG2FMS_DSIZE (8)
+
+#define XTRG2FMS_X_HI GENMASK(3, 2)
+#define XTRG2FMS_Y_HI GENMASK(1, 0)
+#define XTRG2FMS_RX_HI GENMASK(7, 6)
+#define XTRG2FMS_RY_HI GENMASK(5, 4)
+#define XTRG2FMS_ALT1_HI GENMASK(3, 2)
+#define XTRG2FMS_ALT2_HI GENMASK(1, 0)
+
+/* OrangeRX FrSky */
+#define ORANGERX_VID (0x0451)
+#define ORANGERX_PID (0x16a5)
+#define ORANGERX_DSIZE (8)
+
+enum rcsim_controller {
+ PHOENIXRC,
+ VRC2,
+ REALFLIGHT,
+ XTRG2FMS,
+ ORANGERX
+};
+
+struct rcsim_priv {
+ struct hid_device *hdev;
+ struct input_dev *input;
+ enum rcsim_controller controller;
+ u8 alt;
+};
+
+static int rcsim_open(struct input_dev *dev)
+{
+ struct rcsim_priv *priv = input_get_drvdata(dev);
+
+ return hid_hw_open(priv->hdev);
+}
+
+static void rcsim_close(struct input_dev *dev)
+{
+ struct rcsim_priv *priv = input_get_drvdata(dev);
+
+ hid_hw_close(priv->hdev);
+}
+
+static int rcsim_setup_input(struct rcsim_priv *priv)
+{
+ struct input_dev *input;
+
+ input = devm_input_allocate_device(&priv->hdev->dev);
+ if (!input)
+ return -ENOMEM;
+
+ input->id.bustype = priv->hdev->bus;
+ input->id.vendor = priv->hdev->vendor;
+ input->id.product = priv->hdev->product;
+ input->id.version = priv->hdev->bus;
+ input->phys = priv->hdev->phys;
+ input->uniq = priv->hdev->uniq;
+ input->open = rcsim_open;
+ input->close = rcsim_close;
+
+ input_set_drvdata(input, priv);
+
+ switch (priv->controller) {
+ case PHOENIXRC:
+ input_set_abs_params(input, ABS_X, 0, 255, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, 255, 0, 0);
+ input_set_abs_params(input, ABS_RX, 0, 255, 0, 0);
+ input_set_abs_params(input, ABS_RY, 0, 255, 0, 0);
+ input_set_abs_params(input, ABS_Z, 0, 255, 0, 0);
+ input_set_abs_params(input, ABS_RZ, 0, 255, 0, 0);
+ input_set_abs_params(input, ABS_RUDDER, 0, 255, 0, 0);
+ input_set_abs_params(input, ABS_THROTTLE, 0, 255, 0, 0);
+ input->name = "RC Simuator Controller PhoenixRC";
+ break;
+ case VRC2:
+ input_set_abs_params(input, ABS_GAS, 0, 2048, 0, 0);
+ input_set_abs_params(input, ABS_WHEEL, 0, 2048, 0, 0);
+ input->name = "RC Simuator Controller VRC2.0";
+ break;
+ case REALFLIGHT:
+ input_set_abs_params(input, ABS_X, 0, 1024, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, 1024, 0, 0);
+ input_set_abs_params(input, ABS_RX, 0, 1024, 0, 0);
+ input_set_abs_params(input, ABS_RY, 0, 1024, 0, 0);
+ input_set_capability(input, EV_KEY, BTN_A);
+ input_set_capability(input, EV_KEY, BTN_B);
+ input->name = "RC Simuator Controller Realflight";
+ break;
+ case XTRG2FMS:
+ input_set_abs_params(input, ABS_X, 0, 1024, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, 1024, 0, 0);
+ input_set_abs_params(input, ABS_RX, 0, 1024, 0, 0);
+ input_set_abs_params(input, ABS_RY, 0, 1024, 0, 0);
+ input_set_abs_params(input, ABS_Z, 0, 1024, 0, 0);
+ input_set_abs_params(input, ABS_RZ, 0, 1024, 0, 0);
+ input_set_abs_params(input, ABS_RUDDER, 0, 1024, 0, 0);
+ input_set_abs_params(input, ABS_THROTTLE, 0, 1024, 0, 0);
+ input->name = "RC Simuator Controller AeroFly, FMS";
+ priv->alt = 0;
+ break;
+ case ORANGERX:
+ input_set_abs_params(input, ABS_X, 0, 255, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, 255, 0, 0);
+ input_set_abs_params(input, ABS_RX, 0, 255, 0, 0);
+ input_set_abs_params(input, ABS_RY, 0, 255, 0, 0);
+ input_set_abs_params(input, ABS_RUDDER, 0, 255, 0, 0);
+ input_set_abs_params(input, ABS_THROTTLE, 0, 255, 0, 0);
+ input->name = "RC Simuator Controller OrangeRX FrSky";
+ break;
+ };
+
+ priv->input = input;
+ return input_register_device(priv->input);
+}
+
+static int rcsim_raw_event(struct hid_device *hdev,
+ struct hid_report *report,
+ u8 *raw_data, int size)
+{
+ struct rcsim_priv *priv = hid_get_drvdata(hdev);
+ u16 value;
+
+ switch (priv->controller) {
+ case PHOENIXRC:
+ if (size != PHOENIXRC_DSIZE)
+ break;
+
+ /* X, RX, Y and RY, RUDDER and THROTTLE are sent every time */
+ input_report_abs(priv->input, ABS_X, raw_data[2]);
+ input_report_abs(priv->input, ABS_Y, raw_data[0]);
+ input_report_abs(priv->input, ABS_RX, raw_data[4]);
+ input_report_abs(priv->input, ABS_RY, raw_data[3]);
+ input_report_abs(priv->input, ABS_RUDDER, raw_data[5]);
+ input_report_abs(priv->input, ABS_THROTTLE, raw_data[6]);
+
+ /* Z and RZ are sent every other time */
+ if (priv->alt)
+ input_report_abs(priv->input, ABS_Z, raw_data[7]);
+ else
+ input_report_abs(priv->input, ABS_RZ, raw_data[7]);
+
+ priv->alt ^= 1;
+ break;
+ case VRC2:
+ if (size != VRC2_DSIZE)
+ break;
+ value = (raw_data[1] << 8 | raw_data[0]) & GENMASK(10, 0);
+ input_report_abs(priv->input, ABS_GAS, value);
+ value = (raw_data[3] << 8 | raw_data[2]) & GENMASK(10, 0);
+ input_report_abs(priv->input, ABS_WHEEL, value);
+ break;
+ case REALFLIGHT:
+ if (size != REALFLIGHT_DSIZE)
+ break;
+ input_report_abs(priv->input, ABS_X, raw_data[2]);
+ input_report_abs(priv->input, ABS_Y, raw_data[1]);
+ input_report_abs(priv->input, ABS_RX, raw_data[5]);
+ input_report_abs(priv->input, ABS_RY, raw_data[3]);
+ input_report_abs(priv->input, ABS_MISC, raw_data[4]);
+ input_report_key(priv->input, BTN_A,
+ raw_data[7] & REALFLIGHT_BTN_A);
+ input_report_key(priv->input, BTN_B,
+ raw_data[7] & REALFLIGHT_BTN_B);
+ break;
+ case XTRG2FMS:
+ if (size != XTRG2FMS_DSIZE)
+ break;
+
+ /* X, RX, Y and RY are sent every time */
+ value = FIELD_GET(XTRG2FMS_X_HI, raw_data[3]);
+ value = (value << 8) | raw_data[1];
+ input_report_abs(priv->input, ABS_X, value);
+
+ value = FIELD_GET(XTRG2FMS_Y_HI, raw_data[3]);
+ value = (value << 8) | raw_data[2];
+ input_report_abs(priv->input, ABS_Y, value);
+
+ value = FIELD_GET(XTRG2FMS_RX_HI, raw_data[3]);
+ value = (value << 8) | raw_data[0];
+ input_report_abs(priv->input, ABS_RX, value);
+
+ value = FIELD_GET(XTRG2FMS_RY_HI, raw_data[3]);
+ value = (value << 8) | raw_data[4];
+ input_report_abs(priv->input, ABS_RY, value);
+
+ /* Z, RZ, RUDDER and THROTTLE are sent every other time */
+ value = FIELD_GET(XTRG2FMS_ALT1_HI, raw_data[7]);
+ value = (value << 8) | raw_data[6];
+ if (priv->alt)
+ input_report_abs(priv->input, ABS_Z, value);
+ else
+ input_report_abs(priv->input, ABS_RUDDER, value);
+
+ value = FIELD_GET(XTRG2FMS_ALT2_HI, raw_data[7]);
+ value = (value << 8) | raw_data[5];
+ if (priv->alt)
+ input_report_abs(priv->input, ABS_RZ, value);
+ else
+ input_report_abs(priv->input, ABS_THROTTLE, value);
+
+ priv->alt ^= 1;
+ break;
+ case ORANGERX:
+ if (size != ORANGERX_DSIZE)
+ break;
+ input_report_abs(priv->input, ABS_X, raw_data[0]);
+ input_report_abs(priv->input, ABS_Y, raw_data[2]);
+ input_report_abs(priv->input, ABS_RX, raw_data[3]);
+ input_report_abs(priv->input, ABS_RY, raw_data[1]);
+ input_report_abs(priv->input, ABS_RUDDER, raw_data[5]);
+ input_report_abs(priv->input, ABS_THROTTLE, raw_data[6]);
+ break;
+ };
+
+ input_sync(priv->input);
+ return 0;
+}
+
+static int rcsim_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+ struct device *dev = &hdev->dev;
+ struct rcsim_priv *priv;
+ int ret;
+
+ if (!hid_is_using_ll_driver(hdev, &usb_hid_driver))
+ return -ENODEV;
+
+ ret = hid_parse(hdev);
+ if (ret)
+ return ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->hdev = hdev;
+ priv->controller = id->driver_data;
+ hid_set_drvdata(hdev, priv);
+
+ ret = rcsim_setup_input(priv);
+ if (ret)
+ return ret;
+
+ return hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+}
+
+static const struct hid_device_id rcsim_devices[] = {
+ { HID_USB_DEVICE(PHOENIXRC_VID, PHOENIXRC_PID), .driver_data = PHOENIXRC },
+ { HID_USB_DEVICE(VRC2_VID, VRC2_PID), .driver_data = VRC2 },
+ { HID_USB_DEVICE(REALFLIGHT_VID, REALFLIGHT_PID), .driver_data = REALFLIGHT },
+ { HID_USB_DEVICE(XTRG2FMS_VID, XTRG2FMS_PID), .driver_data = XTRG2FMS },
+ { HID_USB_DEVICE(ORANGERX_VID, ORANGERX_PID), .driver_data = ORANGERX },
+ { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(hid, rcsim_devices);
+
+static struct hid_driver rcsim_driver = {
+ .name = "rcsim",
+ .id_table = rcsim_devices,
+ .probe = rcsim_probe,
+ .raw_event = rcsim_raw_event,
+};
+module_hid_driver(rcsim_driver);
+
+MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
+MODULE_LICENSE("GPL");
--
2.37.1