USBJoystick - rumble (force feedback) and device output
=========================================================

How USBJoystick sends data TO a device (as opposed to reading its reports),
what is built on that, and what is known about the hardware. Output is
driven from *commands only; there is no SWI yet.

Packet layouts marked "SDL" were checked against SDL's HIDAPI drivers
(src/joystick/hidapi, zlib licence). "(unverified)" means not yet seen
working on hardware.



Status
--------
  Xbox 360 wired      rumble packet built, matches SDL. (unverified): the
                      only wired pad to hand (a DRM RF dongle) has no working
                      rumble, even under Windows. The transport is proven on
                      it: the stream opens, USBDriver takes the 8 bytes, the
                      transfer completes.

  Xbox 360 wireless   rumble packet built, matches SDL (unverified: a
                      tester's pad did not rumble, at a time when every write
                      to the receiver was stalling). The player LED packet is
                      confirmed on a genuine pad. The module sets the player
                      LED when a pad connects (unverified). Writes to the
                      receiver are retried; see "Failed writes".

  PlayStation USB     built and working, with a first-generation PlayStation
  adaptor             rumble pad (0b43:0003).

  PlayStation 3 pad   start-up and rumble built from SDL's driver
                      (unverified).

  PlayStation 4       rumble built from SDL's driver for 054c:05c4 and
                      054c:09cc (unverified). No lightbar yet.

  PlayStation 5       not built; layout below.

  Xbox One / Series   not supported.



How output reaches a device
-----------------------------
Reports are READ from the interrupt IN endpoint through a DeviceFS stream
(c.joydev open_joystick). Output uses one of two transports, chosen per
driver (joy_device_struct.output_transport):

  JOY_OUTPUT_ENDPOINT   a DeviceFS stream opened for OUTPUT on the
      interface's interrupt OUT endpoint, written with OS_GBPB.
        - joy_check_device records an OUT endpoint (bEndpointAddress bit 7
          clear) on an accepted joystick's interface in
          joy_data[slot].output.endpoint, whether the descriptors list it
          before or after the IN endpoint (the PlayStation 3 lists it
          first). OUT endpoints on other interfaces are ignored.
        - The stream is opened on the first send to the slot, so a device
          that is never sent anything is never touched.
        - Path: devices#interface=<n>;endpoint=<e>;noblock:USB<m>. The
          endpoint number has no direction bit; USBDriver takes the
          direction from the stream being opened for output.
        - A failed send closes the stream and the next send reopens it.
          Removing the device closes it.
  JOY_OUTPUT_CONTROL    a HID SET_REPORT on the control pipe, for devices
      with no OUT endpoint (c.usb usb_hid_set_report).

The OUT endpoint is read from the descriptors, never assumed. Captured
layouts ($.Dev.USBJoyInfo):

  DRM Controller (045e:028e), a USB RF dongle presenting as a wired Xbox 360
  pad, one interface (ff/5d/1):  IN 0x81, OUT 0x02, interrupt, 32 bytes.

  Xbox 360 wireless receiver (045e:0719), eight interfaces. The even ones
  (0, 2, 4, 6; protocol 129) are the four pads:
      interface 0: IN 0x81 OUT 0x01     interface 2: IN 0x83 OUT 0x03
      interface 4: IN 0x85 OUT 0x05     interface 6: IN 0x87 OUT 0x07
  The odd ones (protocol 130) are audio/chatpad and never joysticks.

  PlayStation USB adaptor (0b43:0003): plain HID, one IN endpoint, no OUT
  endpoint.

Per-slot output state (joy_data[slot].output) holds the OUT endpoint and
stream, the last rumble strengths, the stop deadline and the LED state. A
family's builder writes the whole packet from that state, so a rumble update
does not reset anything else in the packet.



Rumble packets
----------------
Two motors: a large one (low frequency) and a small one (high frequency).
Callers give 16-bit strengths (0..65535) as SDL does; the builders scale.

  Xbox 360 wired, 8 bytes to the OUT endpoint (SDL):
      00 08 00 <low> <high> 00 00 00

  Xbox 360 wireless receiver, 12 bytes to the pad's own OUT endpoint (SDL):
      00 01 0F C0 00 <low> <high> 00 00 00 00 00

  PlayStation USB adaptor, report id 1 (also the first data byte), 3 data
  bytes by SET_REPORT:
      00 <small> <big>
  <small> is the small motor, on when bit 0 is set; <big> is the big
  motor's amplitude. Found by experiment: 01, 03, 05 ran the small motor
  and 02, 04 did not; FF on <big> was violent and 77 medium; the first byte
  and the other bits of <small> do nothing. Through *USBJoystick_Rumble,
  low is the big motor (left of the pad) and high the small one (right).

  PlayStation USB adaptor input: two ports, report id 1 and 2, alternating
  on the one endpoint (port 2 reads all 0xFF with nothing plugged in). Each
  report is: id, two button bytes, X, Y, Rz, Z, then the pad type (0x41
  digital, 0x73 analogue). Buttons: Select 8, Start 9, D-pad up 12, right 13,
  down 14, left 15. The D-pad is always on those buttons; in digital mode it
  is also on both axis pairs (the sticks do nothing). In analogue mode
  buttons 10 and 11 read as held all the time, and a stick click changes
  nothing (Windows shows the same), so the driver table clears them in that
  mode and leaves the D-pad buttons out of the automatic mapping.

  PlayStation 3, report id 1, 49 bytes with the id, by SET_REPORT (SDL).
  Byte 0 is the id; SDL's fixed bytes are shown, the rest are zero:
      byte  1       01
      byte  2       FF     right motor duration
      byte  3       right motor, 01 on or 00 off
      byte  4       FF     left motor duration
      byte  5       left motor amplitude
      byte 10       player LED bit: 02, 04, 08, 10 for players 1 to 4
      bytes 11-30   four groups of FF 27 10 00 32 (not labelled in SDL)

  PlayStation 4, report id 5, 32 bytes (SDL, over USB; no checksum):
      byte 1        effect mask: 01 rumble, 02 lightbar, 04 lightbar flash
      byte 4        right (small, high-frequency) motor
      byte 5        left (big, low-frequency) motor
      bytes 6-8     lightbar red, green, blue
      bytes 9, 10   lightbar flash on and off times

  PlayStation 5 (DualSense), report id 2, 48 bytes (SDL, over USB; no
  checksum):
      byte 1        01: rumble emulation
      byte 2        enables: 04 lightbar colour, 10 player lights
      byte 3        right (small, high-frequency) motor
      byte 4        left (big, low-frequency) motor
      byte 39       04: "improved rumble" (firmware 2.24 on); without it
                    SDL halves the left value
      byte 44       player lights bitmask
      bytes 45-47   lightbar red, green, blue

LED packets
  Xbox 360 wireless receiver, to the pad's own OUT endpoint, 12 bytes (SDL):
      00 00 08 <0x40 + pattern> 00 00 00 00 00 00 00 00
  Confirmed on a genuine pad with *USBJoystick_SendOut: patterns 06, 07 and
  08 gave LED 1, 2 and 3 steady and stopped the ring flashing (09 is LED 4).
  The other patterns: 00 all off, 01 all blinking, 02..05 LED 1..4 flash then
  steady, 0A rotating. The 3-byte form 01 03 <pattern>, which SDL uses for
  wired Xbox 360 pads, had no effect on the wireless receiver.

A device reports Joystick_Cap_Rumble (Joystick_DeviceInfo, *USBJoystick_List)
when its driver has a rumble builder and, for the endpoint transport, the
interface has an OUT endpoint. Joystick_Cap_LED is the same for an LED builder.



Commands
----------
*USBJoystick_Rumble <slot> <low> <high> [<ms>]
    Run the slot's motors. <low> is the big (low-frequency) motor and <high>
    the small (high-frequency) one, 0..65535 each; both zero stops. <ms> is
    the run time, 1..65535, default 1000; the motors are stopped for you
    when it expires. Numbers are decimal, or hex with & or 0x.

        *USBJoystick_Rumble 0 65535 0 800

*USBJoystick_SendOut <slot> <byte> [<byte>...]
    Send up to 64 bytes to the slot's OUT endpoint, whether or not the
    driver knows the device. Bytes are hexadecimal. A rumble started this way
    is not stopped by the timer; send the zero packet.

        *USBJoystick_SendOut 0 00 08 00 FF FF 00 00 00

*USBJoystick_SendReport <slot> <report-id> [<byte>...]
    Send a HID output report over the control pipe, for devices with no OUT
    endpoint. Use report-id 0 if the device has none; otherwise the id is
    sent as the report id and as the first data byte. Hexadecimal, up to 64
    data bytes. The PlayStation adaptor, for instance, declares output
    reports 1 and 2 (one per port) of three vendor-defined bytes each:

        *USBJoystick_SendReport 0 1 00 01 80

Each command prints one line on success saying what was sent to which slot,
and a specific error otherwise: a bad slot, "Bad byte 'xyz' - use
hexadecimal, 00 to FF", "Bad strength ... use 0 to 65535", "Bad time ... use
1 to 65535 milliseconds", or "Joystick has no OUT endpoint".

*USBJoystick_List shows each slot's capabilities. *USBJoystick_Debug shows
what the driver table and descriptors gave the slot: output transport (OUT
endpoint or control pipe, and whether the output stream is open), report
descriptor source, report id, data offset, whether there is a data validator
and a start-up routine.

To try a pad:

    *USBJoystick_List
    *USBJoystick_Rumble 0 65535 0 800      big motor, stops after 0.8s
    *USBJoystick_Rumble 0 0 65535 800      small motor

If nothing moves, Reporter (module built with debug output) shows whether the
stream opened and the write was accepted. A pad that accepts the packet and
does not move is ignoring it or has no motors.



Retries
---------
What is retried, and where. Rows 1 and 2 are the transport; 3 to 5 sit above
them. Stalls are dealt with in row 1 only (see "Failed writes").

  #  Retry         When it fires       What it does         Applies to
  -- ------------- ------------------- -------------------- ------------------
  1  write         an OUT-endpoint     3 attempts, 5 cs     Xbox 360 rumble,
                   write fails (a      apart; Clear Stall   PS4 rumble, Xbox
                   stall included)     after each failure   360 wireless LED,
                                                            SendOut
  2  control       a control request   3 attempts, 5 cs     descriptors,
                   fails               apart; no Clear      strings, PSX and
                                       Stall (USBDriver     PS3 rumble, PS3
                                       does it)             start-up,
                                                            SendReport
  3  stop          a rumble stop       retry every 30 cs,   every device's
                   fails after 1 or 2  up to 5 failures,    rumble stop
                                       then give up
  4  stop repeat   a stop succeeds on  resend twice more,   Xbox 360 wireless
                   a LOSSY_OUTPUT      30 cs apart          only
                   device
  5  LED repeat    a pad connects      3 sends: 30 cs,      Xbox 360 wireless
                                       then every 50 cs     only

  Rows 1 and 2 are usb_write and control_retry in c.usb; 3 and 4 are in
  c.rumble and 5 is in c.led.

Not retried: a rumble start beyond rows 1 and 2 (the error goes to the
caller), input reads, and a command the device accepts and ignores (only
rows 4 and 5 guard against that).



Failed writes
---------------
A write to an OUT endpoint can fail with "USB transfer failed: Stalled": the
device answered the transfer with a STALL. The Xbox 360 wireless receiver does
this, often on the first write.

  - USBDriver does not recover from it. The stream's transfer status stays
    at STALLED, nothing more is sent, and every later write returns the same
    error until the stall is cleared. (Control requests are different:
    USBDriver clears a stall on the control pipe itself.)
  - Clearing it is DeviceFS_CallDevice reason &80000005 (Clear Stall) with
    the device name and the output stream's file handle. USBDriver sends
    CLEAR_FEATURE(ENDPOINT_HALT) to the endpoint, resets the host's data
    toggle, and sets the stream's status back to normal.
  - usb_write (c.usb) tries a write up to 3 times. After every failure, the
    last included, it clears the stall, and waits 5 cs before the next
    attempt. If a clear fails, or the third attempt fails, rumble_send
    closes the stream (the next send reopens it) and returns the error.
    Reporter shows each attempt, with the time taken so far (and every
    successful write, first time or not):
        usb_write: USB5: attempt 1 of 3 failed after 0 cs: USB transfer failed: Stalled
        usb_write: USB5: 12 bytes, attempt 2 of 3 succeeded, 6 cs in all
  - Control requests are retried the same way, with no clear:
        usb_control: USB5: set report: attempt 1 of 3 failed after 0 cs: Bad request
  - Why the receiver stalls is not known. A STALL is a deliberate reply from
    the device, not a symptom of low power.



Stopping safely
-----------------
Xbox 360 and PlayStation 4/5 pads rumble until told to stop. So:

  - A timed rumble arms OS_CallAfter. That fires where no USB write is
    possible, so it schedules an OS_AddCallBack and the callback writes the
    stop. One timer serves all slots, set for the earliest deadline.
  - A stop that fails (all three write attempts) leaves the motors running,
    so the state keeps the strengths and the stop is tried again 30 cs later,
    up to 5 failures in a row; then it is abandoned and logged.
  - A device that can drop a command without any error (the Xbox 360
    wireless receiver; JOY_QUIRK_LOSSY_OUTPUT in c.device) has its stop sent
    three times in all, 30 cs apart, once it has gone through. A new rumble
    cancels the repeats.
  - Module finalise stops running motors and closes the streams.
  - Device removal drops the stream and any pending stop without talking to
    the device.



Wireless Xbox 360
-------------------
  - One receiver carries four pads, one USB interface each, and the module
    sets up a slot for every interface whether or not a pad is connected.
  - Slot n is receiver interface 2n, whose OUT endpoint is 2n+1 (slots 0 to
    3 are interfaces 0, 2, 4, 6). Output goes to that OUT endpoint; the
    receiver rejected a control SET_REPORT.
  - Rumble to a pad that is not connected does nothing.
  - The receiver puts non-input packets on the same endpoint as input. Only
    packets starting 00 01 are input (xbox360wireless_data_validator); the
    others are dropped before decoding, or they would overwrite the real
    axes and buttons.
  - A packet is 29 bytes: a 4-byte header, the 20-byte pad report, then 5
    more bytes.

Player LED
  The ring LEDs flash until the pad is told which player it is. Whether that
  also drains the battery faster is not known. c.led sets the LED:

  - A 2-byte packet 08 80 from the receiver means a pad has connected. (A pad
    already connected when the module started is noticed by its first input
    packet instead.) 08 00 means it has gone, and nothing acts on it.
  - The LED command is then sent to the slot's OUT endpoint three times, the
    first 30 cs after the connect and then every 50 cs, because a pad can
    drop a command. The upcall handler only notes the connect and asks for an
    OS_AddCallBack callback; the callback makes the USB write.
  - The player is (slot AND 3) + 1, so each receiver port has a fixed LED:
    LED 1 to 4 steady is pattern 06 to 09. Slots 4 to 7 (a second receiver)
    wrap round to 1 to 4 again.
  - Reporter shows each send, with no feedback from the pad:
        led: slot 0: setting player 1 LED, send 1 of 3

Packet capture
  For a driver with a validator, the upcall handler reads the whole packet (up
  to 32 bytes) and hands it to c.pktlog, which keeps:

  - every packet the validator rejects, and every short read, when it differs
    from the last one rejected;
  - an input packet when anything outside its 20-byte report body (the header
    or the tail) differs from the last one kept.

  Kept packets go into a ring of 32 shared by all slots and are sent to
  Reporter shortly afterwards by an OS_AddCallBack callback, because the
  handler itself cannot call Reporter:

      pktlog: slot 0: non-input packet, 2 bytes: 08 80

  Reporter stops after 256 lines per slot. *USBJoystick_Debug <slot> shows
  the slot's last 8 kept packets, oldest first, and how many non-input
  packets it has sent.

Non-input packets seen on a genuine pad (29 bytes unless noted):
  08 80, 08 00            2 bytes: the pad connected, the pad gone (it
                          switches itself off after about 10 minutes idle).
                          SDL reads them the same way.
  00 0f 00 f0 ...         after connecting: an identity packet; fd 8e ac 00 is
                          the receiver's serial number. SDL takes the initial
                          battery level from byte 17 and replies with
                          00 00 02 80 and zeros.
  00 00 00 13 <level>     SDL reads byte 4 as the battery level. Seen as
                          00 00 00 13 a2; the scale is not known.
  00 00 00 20 1d, 00 00 00 00 05, 00 00 00 40 01
                          sent after connecting; meaning unknown.
  00 00 00 f0 00 ...      idle.
  00 f8 02, 00 f8 03      one of each soon after connecting, then alternating
                          while idle; meaning unknown (SDL ignores them).
Holding the Guide button produced no non-input packet.

SDL also sends 08 00 0F C0 and zeros to the receiver's OUT endpoint when it
starts, to ask which pads are connected. This module does not.



The device table
------------------
c.device holds one joy_device_struct per known device or class of device:

  class, subclass, protocol, vendor id, product id
      How an interface is matched. Exact vendor/product rows are tried
      first, then rows with vendor and product 0 (class only).
  report_descriptor, report_descriptor_length
      A baked-in HID report descriptor used instead of reading one from the
      device. The Xbox 360 pads are not HID and have none.
  report_data_offset
      Bytes in front of the report data in each packet. The wireless
      receiver puts 4 bytes ahead of the pad's data.
  init
      Start-up routine run once after the descriptor has parsed, for a
      device that sends nothing until asked. Runs before the slot's
      endpoints are known, so it can only use the control pipe.
  auto_invert_axes
      Axis types the automatic mapping inverts (Y and RY on the Xbox 360).
  data_valid
      Called on every packet before it is decoded; returning 0 drops it.
  packet
      Called with every packet the upcall handler reads, input or not. The
      wireless receiver uses it to notice a pad connecting.
  fixup
      Called on every packet after the validator, to correct the report in
      place before it is decoded. The PlayStation adaptor uses it to clear
      buttons 10 and 11, which it holds on in analogue mode.
  auto_map_skip_buttons
      Buttons the automatic mapping leaves unbound, so they cannot read as
      fire to a game or the serial port emulation. The PlayStation adaptor
      skips 12-15, its D-pad, which it also reports as buttons. They can
      still be bound by hand.
  quirks
      JOY_QUIRK_* (unreliable serial number).
  output_transport, rumble, led
      JOY_OUTPUT_* and the family's rumble and LED builders; a builder is
      what makes a device rumble or LED capable.

The Xbox 360 rows have no start-up routine: wired pads work without one, and
the wireless receiver needs only report_data_offset and data_valid.

The PlayStation 3 row (054c:0268) has a start-up routine: read feature
reports 0xF2 (17 bytes) and 0xF5 (8 bytes), then write one byte back (SDL). The
write is tried once and a refusal is only logged, as SDL ignores its result.
SDL skips the write for ShanWan clones, which it says would rumble
continuously; this module does not. Bluetooth uses a different start-up;
there is no Bluetooth here. (unverified)

PlayStation 4 and 5 pads need no start-up of their own over USB. SDL first
sends one effects report with an empty effect mask, which it describes as
switching to enhanced report mode; whether a pad needs it is not known.

An Xbox One / Series pad (class ff/47/d0) is not HID: it needs a start-up
message on the OUT endpoint before it sends anything, its own input decoding
and acknowledgements. HID PID force feedback (usage page &0F, wheels and
joysticks) needs the report descriptor parser to find the effect reports,
and many wheels use vendor protocols instead.



Using it from SDL
-------------------
SDL's joystick driver interface (SDL_JoystickDriver, src/joystick/
SDL_sysjoystick.h) is what a RISC OS SDL backend would implement. Its
output calls, and how this module's output maps on to them:

  Rumble(low, high)        16-bit strengths, as here. Maps to the rumble
                           builders. SDL gives the driver no duration: it
                           keeps its own expiry, resends while a rumble
                           lasts, and calls Rumble(0, 0) to stop. So the
                           SWI must be cheap and idempotent to call
                           repeatedly, and the stop timer here is a backstop
                           (an app that dies mid-rumble) rather than the
                           normal way a rumble ends.
  RumbleTriggers(l, r)     Xbox One trigger motors. Not built.
  SetLED(r, g, b)          Lightbar. PlayStation 4/5 only; not built.
  SetDevicePlayerIndex(n)  Player LEDs. Set automatically on the Xbox 360
                           ring; no call.
  SendEffect(data, size)   Raw bytes to the device. Equivalent to
                           *USBJoystick_SendOut.

Consequences for the SWIs:
  - Duration is optional on the SWI; 0 should mean "until told otherwise",
    still bounded by a module-side maximum so a dead client cannot leave a
    motor running.
  - SDL treats RGB LEDs and player LEDs as separate capabilities, and trigger
    rumble as a third. Joystick_Cap_LED is a single bit; split it into RGB LED,
    player LED and trigger rumble before any SWI reports it, so the meanings
    do not change afterwards.
  - Raw access: SDL ships its own per-device output code (the HIDAPI drivers
    for PlayStation 3/4/5, Xbox 360 and others), which would make these
    builders redundant for SDL clients if a raw report transport were
    offered. The builders would still serve classic applications and
    JoySetup. If both paths can write to one device, the stop timer must not
    fire against a slot whose output SDL is driving.
  - SendOut and SendReport exist only as commands. SendEffect and any raw
    transport need them as SWIs.
