Gamepad with rumble / vibration

,

Hi,

I am trying to achieve rumble / vibration / force feedback functionality from any type of USB gamepad / controller as we need such feedback for upcoming user tests. I was happy to see that at least the PS4 controller works well with recent versions of GLFW; however, patching in rumble feedback has proven difficult.

Now, it is fair to say that I am out of my element when it comes to ioctl and drivers, but leveraging (lifting) from mainly the GLFW source and this article from Linux Journal I have managed to produce a debug script (see below), which for a given controller returns its supported functions.

Running this with either a PS4 controller, a PS3 (dualshock) controller, or a Switch Pro Controller, returns that Synch events, Buttons, Absolute Axes, as well as Miscellaneous are available. None are showing rumble (Force feedback).

Does anyone have any ideas of what is wrong? Or better yet, a way to make any controller rumble? (I am willing to purchase any other kind of controller that is known to work).

Thanks!

#include <linux/input.h>
#include <regex.h>
#include <fcntl.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>


int main(int argc, char *argv[]) {

    /*From glfw*/
    const char* dirname = "/dev/input";

    regex_t regex;

    if (regcomp(&regex, "^event[0-9]\\+$", 0) != 0)
    {
        printf("Linux: Failed to compile regex");
    }

    DIR* dir = opendir(dirname);
    printf("Try these as path: \n");
    if (dir)
    {
        struct dirent* entry;

        while ((entry = readdir(dir)))
        {
            regmatch_t match;

            if (regexec(&regex, entry->d_name, 1, &match, 0) != 0)
                continue;

            char path[PATH_MAX];

            snprintf(path, sizeof(path), "%s/%s", dirname, entry->d_name);

            printf("%s\n",path);
        }
    }
    printf("\n");
    /* *** *** */

    int fd;
    const char* path = "/dev/input/event2";
    fd = open(path, O_RDWR | O_NONBLOCK);

    if (fd == -1){
        printf("Couldn't open file descriptor. Test other path or permission?\n");
        return -1;
    }


    char name[256]= "Unknown";
    if(ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0) {
        perror("evdev ioctl");
    }
    printf("The device says its name is %s\n",
        name);
        
    char phys[256]= "Unknown";
    if(ioctl(fd, EVIOCGPHYS(sizeof(phys)), phys) < 0) {
    perror("event ioctl");
    }
    printf("The device says its path is %s\n",
        phys);


    unsigned long evtype_bitmask[EV_MAX];
	memset(evtype_bitmask, 0, sizeof(evtype_bitmask));
    if (ioctl(fd, EVIOCGBIT(0, EV_MAX), evtype_bitmask) < 0) {
        perror("evdev ioctl");
    }

    printf("Supported event types:\n");
    
    printf("  Bitmask: %ld,",*evtype_bitmask);
    printf(" 0x%02lx, ",*evtype_bitmask);
    
    long binaryOut = *evtype_bitmask;
    for (int i = EV_MAX-1; i >= 0; --i) {
        if ((binaryOut >> i) & 1)
            printf("1");
        else
            printf("0");
    }
    printf("\n");

#define TEST_BIT(bit, array) (array[bit / 8] & (1 << (bit % 8)))

    for (int yalv = 0; yalv < EV_MAX; yalv++) {
        if (TEST_BIT(yalv, evtype_bitmask)) {
        printf("  Event type 0x%02x ", yalv);
        switch ( yalv)
            {
            case EV_SYN :
                printf(" (Synch Events)\n");
                break;
            case EV_KEY :
                printf(" (Keys or Buttons)\n");
                break;
            case EV_REL :
                printf(" (Relative Axes)\n");
                break;
            case EV_ABS :
                printf(" (Absolute Axes)\n");
                break;
            case EV_MSC :
                printf(" (Miscellaneous)\n");
                break;
            case EV_LED :
                printf(" (LEDs)\n");
                break;
            case EV_SND :
                printf(" (Sounds)\n");
                break;
            case EV_REP :
                printf(" (Repeat)\n");
                break;
            case EV_FF :
                printf(" (Force Feedback)\n");
                break;
            case EV_FF_STATUS:
                printf(" (Force Feedback (Status))\n");
                break;
            case EV_PWR:
                printf(" (Power Management)\n");
                break;
            default:
                printf(" (Unknown: 0x%04hx)\n",
                yalv);
            }
        }
    }

    return 0;
}

Hi,
You may need to enable certain kernel configs. There is a topic about Logitech GamePad:


FYR.

Thank you for the pointer! After around half-a-dozen tries I managed to follow the instructions and reconfigure the kernel, and now I have rumble! I enabled most vendors force feedback-drivers, but I assume that the one that enables the PS4 controller’s were CONFIG_SONY_FF.

If anyone else attempts such a kernel build I’d like to add that I needed suppress warnings in the kernel Makefile (as some warnings when building were treated as errors).
In Makefile, add the following after KBUILD_CFLAGS to suppress ALL warnings

KBUILD_CFLAGS += -w

Also, I ran out of space during one build. I’m not sure how much was ultimately needed, but I wouldn’t try it with < 10 GB of free space.

Also, as I manually downloaded the source files via a mac, I can also point out that it was necessary for me to unpack them on the nano – as the mac apparently have case-insensitive filenames (and the kernel files apparently require case-sensitivity). Unsure if this is the case on PCs as well.