3 Commits
Author SHA1 Message Date
libretroadmin e6c914df98 USB: Perfect Pool's trackball and table camera
A Python 1 game in Perfect Pool's I/O mode now gets the two USB devices
the cabinet has, in place of whatever the ports are set to: a trackball
on port 0 and the table camera on port 1. The trackball is the HID mouse
moved as Reliquary moves it, half the pointer's motion with the half
counts carried over and none while the left button is held; the first
pad's left stick rolls it as well, for players without a mouse. The
camera (usb-perfect-pool/ppcam.c, C89, after Reliquary's) is the Cypress
EZ-USB FX the game downloads its 8051 firmware into: vendor request 0xa0
reads and writes the firmware RAM, letting the reset latch go with
firmware in brings it back on the bus, and its endpoints answer with
nothing seen.

The camera's 64 KiB of firmware RAM rides in the USB savestate block
past the port slots, only for a session with the camera, so the block
of every other game keeps its size. tests/usbstate checks both devices,
the trackball's scaling and the camera's RAM through a state and a bus
reset.
2026-11-08 21:05:58 +01:00
libretroadmin 913e43a37e USB: C89 throughout, and the Konami Python 2 I/O board on it
The USB stack is C89 again, as the QEMU code it came from was: the OHCI
controller, the generic device core, the descriptor builder, HID and the
libretro front end (libretro/USB.c). Descriptor trees are counted arrays
built by usb_desc_parse_config and released by usb_desc_device_free in
place of std::vector; savestate copies go through usb_state_bytes. What
the stack needs from the core - IOP RAM, the USB interrupt, the input
callback - is declared with C linkage in USB/usb-host.h, and the EyeToy,
still C++ on its camera side, is reached through usb-eyetoy/eyetoy.h. The
unused RingBuffer goes. Pcsx2Types.h now reads as strict ISO C: u128's
unnamed lo/hi view is left out only there, layout unchanged.

On it, Konami Python 2 games load from their .py2 descriptor
(usb-python2/py2desc.c). The P2IO board (p2io.c, C89, byte level, its
whole state one struct) answers the game's commands: JAMMA inputs with the
watchdog bit, Dallas dongles in either dump layout, DIP switches, the AV
report, coins, and the two serial ports - ICCA card readers with keypads,
the DDR extio, Thrill Drive's wheel and seatbelt, Toy's March's drum pads
and Dance 86.4's stage handshake. The board takes USB port 1, read from
RetroPads 1 and 2 with keypads on the keyboard, and rides in savestates.
The descriptor's HDD image (raw, VHD or CHD) becomes the DEV9 drive with
its paired SCE identity (DEV9.HddIdFile, else <image>.hddid); its NVRAM
replaces the BIOS's and an i.Link ID file seeds a new one
(cdvd_set_nvram_files); its pnach replaces the GameDB patches. Unlike
reliquary, an SCI close request closes the port, and command arguments
past a short packet read as zero.

Tests: tests/p2io drives the board byte for byte and parses descriptors;
tests/usbstate builds the C stack, parses every device model's
descriptors and saves the board mid-answer; tests/ata loads HDD
identities; tests/cdvdscmd loads, saves and seeds the game's NVRAM.
2026-11-08 19:52:42 +01:00
Chris Dixonandlibretroadmin d4908f40fb USB: EyeToy camera and microphone, through the frontend or V4L2/ALSA on Linux (#179)
* USB/EyeToy: the EyeToy and Capture Eye device models

PCSX2's Sony EyeToy (OV519) and Konami Capture Eye (OV511+) device
models, on the libretro-usb OHCI core the HID devices already use.
Nothing attaches them yet.

Pictures come from RETRO_ENVIRONMENT_GET_CAMERA_INTERFACE when the
frontend offers it, and otherwise, on Linux, from V4L2: the first node
that streams video capture, in YUYV. Sound comes from
GET_MICROPHONE_INTERFACE, or on Linux from ALSA: the capture card on the
camera's own USB device, else any USB one. libasound is opened with
dlopen, so the core gains no library dependency. Camera frames and
microphone samples are taken on the main thread, as the libretro API
requires, and handed to the emulator thread under a lock.

Against PCSX2:
- The microphone is read straight from the EyeToy's audio endpoint, so
  the separate USB microphone device it embedded is not needed.
- Frames become RGB once, at the size the game asked for whatever the
  camera delivers, mirrored there; MPEG, JPEG and the Capture Eye's
  greyscale are all encoded from that.
- JPEG mode has its own baseline encoder (4:2:2, as libjpeg was asked
  for) instead of libjpeg.
- jo_mpeg: Cb and Cr were B-Y and R-Y without the 1.772 and 1.402
  scale, so saturated colours overflowed the DC size table and the
  stream would not decode; YUYV chroma landed in the wrong columns; AC
  levels past +-127 kept an MPEG-1 prefix inside the MPEG-2 escape.
- The frame size the game writes is clamped to the 640x480 the buffers
  hold.

* libretro: the USB Camera option plugs an EyeToy in

System > USB Camera puts a Sony EyeToy or a Konami Capture Eye on the
first USB port that no keyboard or mouse holds. The frontend's camera
and microphone are asked for in retro_load_game, only when the option
is on, and pumped from retro_run; the per-frame HID update leaves the
camera's port alone.

* USB: savestates hold the controller and its devices

USBfreeze was a stub, so loading a state left the guest's USB driver
talking to a controller and devices that had forgotten their addresses,
configuration and registers; a keyboard, mouse or EyeToy in use stopped
answering.

The state gains a fixed-size USB block just before the extension block,
with a footer of its own: the OHCI registers, then per port what every
USB device has (address, configuration, endpoints) and what the device
adds, the HID event queues or the EyeToy's and its sensor's registers.
The EyeToy's frame in flight is not kept; it starts a new one. A state
without the block, or one whose ports held other devices, plugs those
devices in afresh, as PCSX2 does.

* USB/EyeToy: lock-free frame and sample handoffs, stack frames in budget, state slots validated, harnesses

The camera frame and the microphone samples crossed from the thread that
captures (the frontend's camera callback, or the V4L2 thread) to the one
that runs the device under a mutex, with GetImage copying the frame while
holding it. They now cross through two primitives in cam-frame.h built for
exactly that, on libretro-common atomics and nothing else. FrameMailbox is
three buffers and one exchanged slot: the producer encodes into its own,
publishes by swapping it into the slot, and the consumer swaps its own in
to take the newest; a frame published while the consumer is away is simply
replaced. SampleRing is a single-producer single-consumer ring; a full ring
drops at the producer, and the consumer trims its own backlog by advancing
its own index. What the device asked of the camera - wanted, format,
mirror, size, generation - travels in one atomic word, so an Open from the
emulator thread and a frame from the main thread never meet in a lock.

The stackframe lane was over budget on four new frames: the JPEG encoder
built its four Huffman tables on the stack per call, jo_write_mpeg kept its
macroblock scratch there, and the ALSA device search had two PATH_MAX
buffers. The tables are built once as statics, the scratch is a
FrameEncoder the caller owns, and realpath allocates.

A savestate slot naming a configuration or interface the device does not
have was half loaded and the device left running on it; the slot is now an
error, and a port whose slot errs, or holds another device, is cycled with
usb_port_reset, so the device model is at its defaults as a guest's port
reset would leave it.

tests/cam runs the mailbox and the ring from two threads, plain and under
TSan, and parses what the JPEG, IPU and YUV400 encoders emit; tests/usbstate
drives the real controller and device models through write, strip and read,
and the states that must replug: no block, another device on the port, a
bad configuration, a bad interface. Both run from run-all.sh.

---------

Co-authored-by: libretroadmin <[email protected]>
2026-10-17 20:17:25 +02:00