Just for fun I thought I'd try to make more than 2 relative axes, by adding REL_RX and REL_RY but SDL seems to only find 1 "ball".

/usr/src/linux-headers-5.4.0-67/include/uapi/linux/input-event-codes.h has a bunch of axes to choose from:

#define REL_X                   0x00
#define REL_Y                   0x01
#define REL_Z                   0x02
#define REL_RX                  0x03
#define REL_RY                  0x04
#define REL_RZ                  0x05
#define REL_HWHEEL              0x06
#define REL_DIAL                0x07
#define REL_WHEEL               0x08
#define REL_MISC                0x09

so let's add some more axes:

  ioctl(fd, UI_SET_EVBIT, EV_REL); // enable relative event handling

  ioctl(fd, UI_SET_RELBIT, REL_X);
  ioctl(fd, UI_SET_RELBIT, REL_Y);
  ioctl(fd, UI_SET_RELBIT, REL_Z);
  ioctl(fd, UI_SET_RELBIT, REL_RX);
  ioctl(fd, UI_SET_RELBIT, REL_RY);

Launching mame with -verbose:

says I have 3 axes, 18 buttons 0 hats 1 balls

Joystick: Start initialization
Input: Adding joystick #0: MicrosoftSideWinderJoystick (device id: )
Joystick: Microsoft SideWinder Joystick [GUID ]
Joystick:   ...  3 axes, 8 buttons 0 hats 0 balls
Joystick:   ...  Physical id 0 mapped to logical id 1
Joystick:   ...  Does not have haptic capability
Input: Adding joystick #1: UserspaceJoystick (device id: 03000000030000000300000002000000)
Joystick: Userspace Joystick [GUID 03000000030000000300000002000000]
Joystick:   ...  3 axes, 18 buttons 0 hats 1 balls
Joystick:   ...  Physical id 1 mapped to logical id 2
Joystick:   ...  Does not have haptic capability
Joystick: End initialization

looking in 3rdparty/SDL2/src/joystick/linux/SDL_sysjoystick.c it looks like SDL only checks REL_X and REL_Y to set the number of balls, so even if I add other axes they'll be ignored.

        if (test_bit(REL_X, relbit) || test_bit(REL_Y, relbit)) {

One workaround could be to make another virtual joystick with uinput and translate events to the second virtual device if I only get 1 "ball" per device.