Hinara / gamecube-adapter

A Linux Kernel Driver to manage the Nintendo Gamecube Adapter
7 stars 0 forks source link

Mainlining #2

Open upintheairsheep opened 5 months ago

upintheairsheep commented 5 months ago

Can you mainline this driver please? The driver will be deployed to billions of devices, and the adapter will work with future Linux, Android, and ChromeOS devices.

Hinara commented 4 months ago

To be fair I would like to ^^

But I need to learn how exactly the kernel development work and there is also the current issue that usb-hid driver take the upper hand as the GameCube adapter report itself as a USB hid device.

I will close this issue if it got into the kernel :)

Hinara commented 3 months ago

Patch send for review :3 Cover letter Patch Well not without struggle as it seems the link between those two message didn't worked....

max-m commented 3 months ago

and there is also the current issue that usb-hid driver take the upper hand as the GameCube adapter report itself as a USB hid device.

I haven’t narrowed it down, but I got it working after many failed attempts using the following udev rules:

SUBSYSTEMS=="usb", ACTION=="add", DRIVERS=="usb", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="0337", ATTR{authorized}="0"
SUBSYSTEMS=="usb", ACTION=="add", DRIVERS=="usbhid", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="0337", ATTR{authorized}="0"
SUBSYSTEMS=="usb", ACTION=="add", DRIVERS=="hid-generic", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="0337", ATTR{authorized}="0"

SUBSYSTEM=="usb", ACTION=="add", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="0337", ATTR{authorized}="1", \
RUN+="/bin/bash -c 'echo $kernel > /sys/bus/usb/drivers/gamecube-adapter/bind'"

Those first rules were my attempt to prevent the built-in drivers to bind the USB device by not authorizing them. The last rule then authorizes the device and binds your driver to it. Note: I’ve installed the driver under the name gamecube-adapter via dkms on my system.

System log:

[33866.848513] usb 5-1.1.3: new full-speed USB device number 42 using xhci_hcd
[33866.968225] usb 5-1.1.3: New USB device found, idVendor=057e, idProduct=0337, bcdDevice= 1.00
[33866.968233] usb 5-1.1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[33866.968237] usb 5-1.1.3: Product: WUP-028
[33866.968239] usb 5-1.1.3: Manufacturer: Nintendo
[33866.968241] usb 5-1.1.3: SerialNumber: 15/07/2014
[33866.972798] hid-generic 0003:057E:0337.0034: hiddev1,hidraw2: USB HID v1.10 Device [Nintendo WUP-028] on usb-0000:03:00.0-1.1.3/input0
[33866.977629] hid-generic 0003:057E:0337.0035: hiddev1,hidraw2: USB HID v1.10 Device [Nintendo WUP-028] on usb-0000:03:00.0-1.1.3/input0
[33866.977681] usb 5-1.1.3: authorized to connect
[33866.989426] gamecube-adapter 5-1.1.3:1.0: New device registered
[33867.019990] gamecube-adapter 5-1.1.3:1.0: New device registered
[33867.028391] input: Nintendo GameCube Controller as /devices/virtual/input/input44
[33867.029764] input: Nintendo GameCube Controller as /devices/virtual/input/input45

Works fine in Firefox on https://hardwaretester.com/gamepad for example :) Chromium only supports a single controller plugged into the adapter (only the last one), not sure if that’s something you can fix.

Edit: Rumble didn’t work in Chromium nor with fftest. Power was plugged in. Hmm

Hinara commented 3 months ago

Hi @max-m , Thank for your time trying my module.

Chromium only supports a single controller plugged into the adapter (only the last one), not sure if that’s something you can fix.

Concerning Chromium support, if you tell me it work with firefox, it means that it seems to that chrome does not handle it correctly, and modifying chrome is out of scope of this project

Those first rules were my attempt to prevent the built-in drivers to bind the USB device by not authorizing them. The last rule then authorizes the device and binds your driver to it.

Works correctly might add it with an automatic install script

Edit: Rumble didn’t work in Chromium nor with fftest. Power was plugged in. Hmm

Do you test fftest on the correct device ? It should not be jsX but eventX corresponding to the controller in question and also make sure to plug the grey plug of the gamecube controller as it provide power for rumble functionnalities.

max-m commented 3 months ago

Hi :)

Concerning Chromium support, if you tell me it work with firefox, it means that it seems to that chrome does not handle it correctly, and modifying chrome is out of scope of this project

Oh, no. I meant by changing the way the controllers are reported to the system. I already tried the easy and simple things: giving each controller a unique display name (this did not change Chrome's behavior, Firefox uses those names), setting the parent device (this made Chrome pick the manufacturer and product name of the parent device, Nintendo WUP-028, as the display name), probably some other simple tweaks I tried and forgot. Chrome also refuses to detect my Switch Pro Controller which Firefox happily picks up (this one is handled by hid-nx via dkms on my system). So it's sort of expected that controller support feels wonky in that browser. :^)

Do you test fftest on the correct device ? It should not be jsX but eventX corresponding to the controller in question and also make sure to plug the grey plug of the gamecube controller as it provide power for rumble functionnalities.

I used the corresponding eventX devices. When you try to use a jsX device fftest complains :) When I opened an event device it was happy with, it let me send FF patterns to the controller, but nothing happened. As a sanity check I tried the same with a DualShock 3 and it rumbled violently. haha I also had both USB plugs connected, that was the first thing I checked :D Maybe I'll play around with that feature tomorrow, it hasn't bothered me enough yet.

Hinara commented 3 months ago

Do you obtain the same output as me with fftest ?

Features:
  * Absolute axes: X, Y, Z, RX, RY, RZ, 
    [3F 00 00 00 00 00 00 00 ]
  * Relative axes: 
    [00 00 ]
  * Force feedback effects types: Periodic, Rumble, Gain, 
    Force feedback periodic effects: Square, Triangle, Sine, 
    [00 00 00 00 00 00 00 00 00 00 03 07 01 00 00 00 ]
  * Number of simultaneous effects: 16

Setting master gain to 75% ... OK
Uploading effect #0 (Periodic sinusoidal) ... OK (id 0)
Uploading effect #1 (Constant) ... Error: Invalid argument
Uploading effect #2 (Spring) ... Error: Invalid argument
Uploading effect #3 (Damper) ... Error: Invalid argument
Uploading effect #4 (Strong rumble, with heavy motor) ... OK (id 1)
Uploading effect #5 (Weak rumble, with light motor) ... OK (id 2)
max-m commented 3 months ago

Hmm, I actually missed that it reports Error: Invalid argument for all effect types:

$ cat /sys/devices/virtual/input/input32/name
Nintendo GameCube Controller #1

$ ls /sys/devices/virtual/input/input32/
capabilities/  event8/  id/  inhibited  js0/  modalias  name  phys  power/  properties  subsystem@  uevent  uniq

$ fftest /dev/input/event8
Force feedback test program.
HOLD FIRMLY YOUR WHEEL OR JOYSTICK TO PREVENT DAMAGES

Device /dev/input/event8 opened
Features:
  * Absolute axes: X, Y, Z, RX, RY, RZ, 
    [3F 00 00 00 00 00 00 00 ]
  * Relative axes: 
    [00 00 ]
  * Force feedback effects types: Rumble, Gain, 
    Force feedback periodic effects: 
    [00 00 00 00 00 00 00 00 00 00 01 00 01 00 00 00 ]
  * Number of simultaneous effects: 16

Setting master gain to 75% ... OK
Uploading effect #0 (Periodic sinusoidal) ... Error:: Invalid argument
Uploading effect #1 (Constant) ... Error: Invalid argument
Uploading effect #2 (Spring) ... Error: Invalid argument
Uploading effect #3 (Damper) ... Error: Invalid argument
Uploading effect #4 (Strong rumble, with heavy motor) ... Error: Invalid argument
Uploading effect #5 (Weak rumble, with light motor) ... Error: Invalid argument
Enter effect number, -1 to exit

The reported ff effect types and periodic effects differ from your results. :s I’m on kernel 5.15.0-102-generic if that matters.

It does work with my DualShock 3 though:

Device /dev/input/event11 opened
Features:
  * Absolute axes: X, Y, Z, RX, RY, RZ, 
    [3F 00 00 00 00 00 00 00 ]
  * Relative axes: 
    [00 00 ]
  * Force feedback effects types: Periodic, Rumble, Gain, 
    Force feedback periodic effects: Square, Triangle, Sine, 
    [00 00 00 00 00 00 00 00 00 00 03 07 01 00 00 00 ]
  * Number of simultaneous effects: 16

Setting master gain to 75% ... OK
Uploading effect #0 (Periodic sinusoidal) ... OK (id 0)
Uploading effect #1 (Constant) ... Error: Invalid argument
Uploading effect #2 (Spring) ... Error: Invalid argument
Uploading effect #3 (Damper) ... Error: Invalid argument
Uploading effect #4 (Strong rumble, with heavy motor) ... OK (id 1)
Uploading effect #5 (Weak rumble, with light motor) ... OK (id 2)
Enter effect number, -1 to exit

By the way, the adapter also sets a bit in the controller status flags when the external power is plugged in, so you might be able to dynamically report FF support per controller and update it when this state changes. And as far as I know the Wavebird (the official wireless GC controller) does not support rumble, so I’ve excluded it in my patch to log the rumble status:

[ 2937.971212] gamecube-adapter 3-4:1.0: Port 0; Status: 00010100; Enable: 1; Rumble Allowed: 1
[ 2937.971222] gamecube-adapter 3-4:1.0: Port 1; Status: 00010100; Enable: 1; Rumble Allowed: 1
[ 2937.971225] gamecube-adapter 3-4:1.0: Port 2; Status: 00000100; Enable: 0; Rumble Allowed: 1
[ 2937.971228] gamecube-adapter 3-4:1.0: Port 3; Status: 00000100; Enable: 0; Rumble Allowed: 1
# Unplugging the power cable =>
[ 2939.419132] gamecube-adapter 3-4:1.0: Port 0; Status: 00010000; Enable: 1; Rumble Allowed: 0
[ 2939.419136] gamecube-adapter 3-4:1.0: Port 1; Status: 00010000; Enable: 1; Rumble Allowed: 0
[ 2939.419138] gamecube-adapter 3-4:1.0: Port 2; Status: 00000000; Enable: 0; Rumble Allowed: 0
[ 2939.419139] gamecube-adapter 3-4:1.0: Port 3; Status: 00000000; Enable: 0; Rumble Allowed: 0
# Plugged it back in =>
[ 2942.091189] gamecube-adapter 3-4:1.0: Port 0; Status: 00010100; Enable: 1; Rumble Allowed: 1
[ 2942.091200] gamecube-adapter 3-4:1.0: Port 1; Status: 00010100; Enable: 1; Rumble Allowed: 1
[ 2942.091204] gamecube-adapter 3-4:1.0: Port 2; Status: 00000100; Enable: 0; Rumble Allowed: 1
[ 2942.091207] gamecube-adapter 3-4:1.0: Port 3; Status: 00000100; Enable: 0; Rumble Allowed: 1
# Unplugged =>
[ 2945.091132] gamecube-adapter 3-4:1.0: Port 0; Status: 00010000; Enable: 1; Rumble Allowed: 0
[ 2945.091143] gamecube-adapter 3-4:1.0: Port 1; Status: 00010000; Enable: 1; Rumble Allowed: 0
[ 2945.091147] gamecube-adapter 3-4:1.0: Port 2; Status: 00000000; Enable: 0; Rumble Allowed: 0
[ 2945.091151] gamecube-adapter 3-4:1.0: Port 3; Status: 00000000; Enable: 0; Rumble Allowed: 0
# You get the idea
[ 2950.411101] gamecube-adapter 3-4:1.0: Port 0; Status: 00010100; Enable: 1; Rumble Allowed: 1
[ 2950.411112] gamecube-adapter 3-4:1.0: Port 1; Status: 00010100; Enable: 1; Rumble Allowed: 1
[ 2950.411115] gamecube-adapter 3-4:1.0: Port 2; Status: 00000100; Enable: 0; Rumble Allowed: 1
[ 2950.411118] gamecube-adapter 3-4:1.0: Port 3; Status: 00000100; Enable: 0; Rumble Allowed: 1

My current diff:

diff --git a/dkms.conf b/dkms.conf
new file mode 100644
index 0000000..291758a
--- /dev/null
+++ b/dkms.conf
@@ -0,0 +1,10 @@
+BUILT_MODULE_NAME[0]="gamecube-adapter"
+DEST_MODULE_LOCATION[0]="/extra"
+
+PACKAGE_NAME="$BUILT_MODULE_NAME"
+PACKAGE_VERSION="1.0.2"
+
+MAKE[0]="make KERNEL_SOURCE_DIR=$kernel_source_dir"
+CLEAN[0]="make KERNEL_SOURCE_DIR=$kernel_source_dir clean"
+
+AUTOINSTALL="yes"
diff --git a/usb-gamecube-adapter.c b/gamecube-adapter.c
similarity index 92%
rename from usb-gamecube-adapter.c
rename to gamecube-adapter.c
index aef5029..e1d4a54 100644
--- a/usb-gamecube-adapter.c
+++ b/gamecube-adapter.c
@@ -4,7 +4,7 @@
 #include <linux/input.h>
 #include <linux/usb/input.h>

-#include "usb-gamecube-adapter.h"
+#include "gamecube-adapter.h"

 /* Callers must hold gdata->odata_lock spinlock */
 static int gc_queue_rumble(struct gc_data *gdata)
@@ -116,15 +116,22 @@ static void gcc_input(struct gcc_data *gccdata, const u8 *keys)
    input_report_key(gccdata->input, BTN_DPAD_UP, !!(keys[1] & BIT(7)));

    input_report_key(gccdata->input, BTN_START, !!(keys[2] & BIT(0)));
-   input_report_key(gccdata->input, BTN_TR2, !!(keys[2] & BIT(1)));
-   input_report_key(gccdata->input, BTN_TR, !!(keys[2] & BIT(2)));
-   input_report_key(gccdata->input, BTN_TL, !!(keys[2] & BIT(3)));
+   input_report_key(gccdata->input, BTN_TR2, !!(keys[2] & BIT(1))); // Z
+   input_report_key(gccdata->input, BTN_TR, !!(keys[2] & BIT(2))); // L (digital)
+   input_report_key(gccdata->input, BTN_TL, !!(keys[2] & BIT(3))); // R (digital)

+   // Left analog stick
    input_report_abs(gccdata->input, ABS_X, keys[3]);
    input_report_abs(gccdata->input, ABS_Y, keys[4] ^ 0xFF);
+
+   // Right analog stick (C stick)
    input_report_abs(gccdata->input, ABS_RX, keys[5]);
    input_report_abs(gccdata->input, ABS_RY, keys[6] ^ 0xFF);
+
+   // Left trigger
    input_report_abs(gccdata->input, ABS_Z, keys[7]);
+
+   // Right trigger
    input_report_abs(gccdata->input, ABS_RZ, keys[8]);

    input_sync(gccdata->input);
@@ -195,9 +202,13 @@ static int gc_controller_init(struct gcc_data *gccdev, u8 status)

    input_set_drvdata(gccdev->input, gccdev);
    usb_to_input_id(gccdev->adapter->udev, &gccdev->input->id);
-   gccdev->input->name = "Nintendo GameCube Controller";
+
+   gccdev->input->name = gccdev->name;
    gccdev->input->phys = gccdev->adapter->phys;

+   // dev_set_name(&gccdev->input->dev, "input%d", gccdev->no);
+   // gccdev->input->dev.parent = &gccdev->adapter->intf->dev;
+
    set_bit(EV_KEY, gccdev->input->evbit);

    set_bit(BTN_A, gccdev->input->keybit);
@@ -249,17 +260,32 @@ gc_deinit_controller:
    return error;
 }

+#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
+#define BYTE_TO_BINARY(byte)  \
+  ((byte) & 0x80 ? '1' : '0'), \
+  ((byte) & 0x40 ? '1' : '0'), \
+  ((byte) & 0x20 ? '1' : '0'), \
+  ((byte) & 0x10 ? '1' : '0'), \
+  ((byte) & 0x08 ? '1' : '0'), \
+  ((byte) & 0x04 ? '1' : '0'), \
+  ((byte) & 0x02 ? '1' : '0'), \
+  ((byte) & 0x01 ? '1' : '0')
+
 static void gc_controller_update_work(struct work_struct *work)
 {
    int i;
    u8 status[4];
    bool enable[4];
+   bool rumble_allowed[4];
    unsigned long flags;
    struct gc_data *gdata = container_of(work, struct gc_data, work);

    for (i = 0; i < 4; i++) {
        status[i] = gdata->controllers[i].status;
        enable[i] = gc_connected_type(status[i]) != 0;
+       rumble_allowed[i] = (status[i] & GAMECUBE_WIRELESS) == 0 && (status[i] & 0x04) != 0;
+       
+       dev_info(&gdata->intf->dev, "Port %d; Status: "BYTE_TO_BINARY_PATTERN"; Enable: %d; Rumble Allowed: %d\n", i, BYTE_TO_BINARY(status[i]), enable[i], rumble_allowed[i]);
    }

    for (i = 0; i < 4; i++) {
@@ -449,6 +475,8 @@ static void gc_init_controllers(struct gc_data *gdata)
        gdata->controllers[i].no = i;
        gdata->controllers[i].status = GAMECUBE_NONE;
        gdata->controllers[i].enable = false;
+
+       snprintf(gdata->controllers[i].name, 32, "Nintendo GameCube Controller #%d", i + 1);
    }
 }

@@ -530,7 +558,7 @@ static const struct usb_device_id gc_usb_devices[] = {
 MODULE_DEVICE_TABLE(usb, gc_usb_devices);

 static struct usb_driver gc_usb_driver = {
-   .name       = "gamecube_adapter",
+   .name       = "gamecube-adapter",
    .id_table   = gc_usb_devices,
    .probe      = gc_usb_probe,
    .disconnect = gc_usb_disconnect,
diff --git a/usb-gamecube-adapter.h b/gamecube-adapter.h
similarity index 98%
rename from usb-gamecube-adapter.h
rename to gamecube-adapter.h
index 26b794b..c835167 100644
--- a/usb-gamecube-adapter.h
+++ b/gamecube-adapter.h
@@ -32,6 +32,7 @@ struct gcc_data {
    u8 no;
    u8 status;
    bool enable;
+   char name[32];
 };

 struct gc_data {
Hinara commented 3 months ago

Well I know that there is a flag for that but I first wanted a module which work ^^' and is simple enought to understand Supporting rumble change also mean recreating the controller as I'm not sure adding it on the fly won't cause some issues ^^'