Skip to content

drivers/usbdev: add UVC gadget class driver #18609

Open
JianyuWang0623 wants to merge 4 commits intoapache:masterfrom
JianyuWang0623:uvc_camera
Open

drivers/usbdev: add UVC gadget class driver #18609
JianyuWang0623 wants to merge 4 commits intoapache:masterfrom
JianyuWang0623:uvc_camera

Conversation

@JianyuWang0623
Copy link
Contributor

@JianyuWang0623 JianyuWang0623 commented Mar 26, 2026

Note: Please adhere to Contributing Guidelines.

Summary

This PR adds USB Video Class (UVC) gadget support for NuttX, enabling an ESP32-S3 board to appear as a standard USB webcam on a Linux host — no custom host driver required.

The change consists of three commits:

  1. arch/xtensa/esp32s3: fix CAM DMA race and heap-allocate descriptors

    • Stops DMA before invoking the frame-complete callback in the ISR, preventing a race where set_buf() rewrites DMA descriptors while GDMA is still draining the CAM AFIFO.
    • Checks priv->capturing after the callback to avoid restarting DMA when the V4L2 layer has already called stop_capture inside the callback (single-buffer FIFO case).
    • Moves DMA descriptors from the driver struct to heap (kmm_memalign) so that a stale/corrupted descriptor scribbles on heap instead of overwriting g_cam_priv fields.
    • Removes leftover debug polling loop from start_capture.
  2. drivers/video/gc0308: add YUYV output format support

    • Adds V4L2_PIX_FMT_YUYV to the GC0308 sensor's format descriptor list.
    • Dynamically configures the output format register (0x24) in start_capture() based on the requested pixel format (RGB565 or YCbCr422).
    • Tracks the active pixel format in priv->pixelformat.
  3. drivers/usbdev: add UVC gadget class driver

    • Implements a USB Video Class 1.1 Bulk-transport gadget (drivers/usbdev/uvc.c, include/nuttx/usb/uvc.h).
    • Exposes /dev/uvc0 character device; userspace writes raw YUY2 frames, the driver prepends UVC Payload Headers and splits into USB transfers.
    • Supports PROBE/COMMIT negotiation with dynamic resolution and frame interval from the camera sensor.
    • Handles host disconnect gracefully: 30-second nxsem_tickwait timeout, double EP_CANCEL guard, wrsem drain on re-COMMIT.
    • Adds BOARDIOC_USBDEV_UVC to boardctl.h for standalone initialization.
    • Includes Kconfig (CONFIG_USBUVC) and build system integration.
    • Composite device support is compile-gated (CONFIG_USBUVC_COMPOSITE) but not exercised in this PR.

Impact

  • New feature: NuttX devices with a camera sensor can now enumerate as a standard UVC webcam. Works out-of-the-box with Linux V4L2 applications (ffmpeg, Guvcview, Cheese, etc.).
  • Existing code: The CAM DMA fix (commit 1) changes ISR behavior for esp32s3_cam.c — DMA is now stopped before the callback and only restarted if priv->capturing is still true. This is a bugfix with no impact on existing users who don't use the CAM driver.
  • Build: No impact on existing configurations. The UVC driver is only built when CONFIG_USBUVC=y.
  • Hardware: Tested on ESP32-S3 with GC0308 sensor. The UVC driver is hardware-agnostic and should work with any NuttX USB device controller and V4L2 camera sensor.

Testing

Host: Ubuntu 24.04 x86_64, kernel 6.8
Board: LCKFB-SZPI-ESP32S3 (ESP32-S3 + GC0308 camera)
Config: lckfb-szpi-esp32s3:uvc (standalone UVC, no composite)
Build: make distclean && ./tools/configure.sh lckfb-szpi-esp32s3:uvc && make -j$(nproc)

Enumeration: Device enumerates as 1d6b:0102 and is recognized by V4L2:

[180982.972486] usb 1-9.3.3: new full-speed USB device number 117 using xhci_hcd
[180983.062615] usb 1-9.3.3: New USB device found, idVendor=1d6b, idProduct=0102, bcdDevice= 1.00
[180983.062620] usb 1-9.3.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[180983.062621] usb 1-9.3.3: Product: NuttX UVC Camera
[180983.062622] usb 1-9.3.3: Manufacturer: NuttX
[180983.062623] usb 1-9.3.3: SerialNumber: 0001
[180983.066126] usb 1-9.3.3: Found UVC 1.10 device NuttX UVC Camera (1d6b:0102)
$ v4l2-ctl --list-devices
NuttX UVC Camera (usb-0000:00:14.0-1):
        /dev/video1

Streaming (ffmpeg): 10-round open/close stress test at 320x240 YUY2 @ 5fps, all passed:

$ for i in $(seq 1 10); do
    ffmpeg -f v4l2 -video_size 320x240 -i /dev/video1 -frames:v 10 -y /dev/null 2>&1
  done
Round 1: PASS
Round 2: PASS
...
Round 10: PASS
=== RESULT: 10/10 passed, 0 failed ===

Photo capture (Guvcview): JPEG 320×240 captured successfully.
Video recording (Guvcview): MKV 320×240 @ 5fps, 5.02s, MPEG4 + MP2 audio recorded successfully.

- Remove debug polling loop from esp32s3_cam_start_capture() that
  busy-waited on DMA status register.

- Fix DMA ISR race: stop DMA before invoking capture callback to
  prevent ISR re-triggering while callback processes the buffer.
  Check priv->capturing before restarting DMA in ISR.

- Move DMA descriptors from struct to heap allocation, avoiding
  stack/BSS alignment issues with cache-line-aligned descriptors.

Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
GC0308 sensor supports multiple output formats via register 0x24.
Add YUYV (YCbCr422) alongside existing RGB565 in fmtdescs and
configure the output format register dynamically in start_capture()
based on the requested pixel format.

Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
@github-actions github-actions bot added Area: Build system Arch: xtensa Issues related to the Xtensa architecture Size: XL The size of the change in this PR is very large. Consider breaking down the PR into smaller pieces. labels Mar 26, 2026
ifeq ($(CONFIG_USBUVC),y)
CSRCS += uvc.c
endif

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, thank you

Copy link
Contributor

@acassis acassis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JianyuWang0623 WOW!!! That is really cool! Two things: 1) please include a Documentation about this feature; 2) please include the board profile that use created to test it.

fdcavalcanti
fdcavalcanti previously approved these changes Mar 26, 2026
@JianyuWang0623
Copy link
Contributor Author

@JianyuWang0623 WOW!!! That is really cool! Two things: 1) please include a Documentation about this feature; 2) please include the board profile that use created to test it.

@acassis done

acassis
acassis previously approved these changes Mar 26, 2026
Add USB Video Class 1.1 gadget driver supporting Bulk transport
with uncompressed YUY2 video streaming. Resolution and frame
interval are negotiated dynamically via PROBE/COMMIT control.

- uvc.h: protocol constants, streaming control struct, public API
- uvc.c: class driver with PROBE/COMMIT, bulk EP, /dev/uvc0 chardev
- Kconfig/Make.defs: USBUVC config and build rules
- boardctl.c: BOARDIOC_USBDEV_UVC standalone init path

Hardened against host disconnect:
- Removed nxmutex_lock from USB interrupt context paths
- Added 30s semaphore timeout in uvc_write with EP_CANCEL fallback
- Drain stale wrsem counts in VS_COMMIT before new stream
- Guard uvc_streaming_stop() against double EP_CANCEL race
- Handle EP_SUBMIT returning -ESHUTDOWN gracefully

Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
Add UVC configuration for lckfb-szpi-esp32s3 board based on gc0308
camera config, with USB OTG and UVC gadget driver enabled in
standalone (non-composite) mode.

- defconfig: enable ESP32S3_OTG, USBUVC, UVC example app
- board doc: add uvc section with usage and host verification

Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
@acassis
Copy link
Contributor

acassis commented Mar 26, 2026

@JianyuWang0623 please fix this Documentation issue:

Warning, treated as error:
/home/runner/work/nuttx/nuttx/Documentation/components/drivers/special/uvc.rst:1:Title overline too short.

==================================
USB Video Class (UVC) Gadget Driver
==================================
make: *** [Makefile:52: html] Error 2
Error: Process completed with exit code 2.

simbit18
simbit18 previously approved these changes Mar 26, 2026
@JianyuWang0623
Copy link
Contributor Author

@JianyuWang0623 please fix this Documentation issue:

Warning, treated as error:
/home/runner/work/nuttx/nuttx/Documentation/components/drivers/special/uvc.rst:1:Title overline too short.

==================================
USB Video Class (UVC) Gadget Driver
==================================
make: *** [Makefile:52: html] Error 2
Error: Process completed with exit code 2.

@acassis done

$ make html

... ...

dumping object inventory... done
build succeeded, 1 warning (with warnings treated as errors).

The HTML pages are in _build/html.

/* Configure CAM controller */

return esp32s3_cam_config(priv);
int ret = esp32s3_cam_config(priv);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
int ret = esp32s3_cam_config(priv);
return esp32s3_cam_config(priv);

@acassis
Copy link
Contributor

acassis commented Mar 26, 2026

@JianyuWang0623 I have not idea why the CI is failing now, even spending time scrolling it I only saw some warnings:

Configuration/Tool: fvp-armv8r/pnsh_smp
2026-03-26 12:39:55
------------------------------------------------------------------------------------
  Cleaning...
  Configuring...
  Building NuttX...
Makefile:142: target 'arm64_perf.o' given more than once in the same rule
Makefile:142: target 'arm64_perf.o' given more than once in the same rule
Makefile:142: target 'arm64_perf.o' given more than once in the same rule
Makefile:142: target 'arm64_perf.o' given more than once in the same rule
Makefile:142: target 'arm64_perf.o' given more than once in the same rule
aarch64-none-elf-ld: warning: nuttx_user has a LOAD segment with RWX permissions
Makefile:142: target 'arm64_perf.o' given more than once in the same rule
  [1/1] Normalize fvp-armv8r/pnsh_smp

@simbit18 any idea?

@acassis
Copy link
Contributor

acassis commented Mar 26, 2026

@simbit18 @lupyuen did you see @btashton comments here:
#9420

Seems like he was working on something to improve our CI before SpaceX killed him :-D

@simbit18
Copy link
Contributor

@JianyuWang0623 I have not idea why the CI is failing now, even spending time scrolling it I only saw some warnings:

Configuration/Tool: fvp-armv8r/pnsh_smp
2026-03-26 12:39:55
------------------------------------------------------------------------------------
  Cleaning...
  Configuring...
  Building NuttX...
Makefile:142: target 'arm64_perf.o' given more than once in the same rule
Makefile:142: target 'arm64_perf.o' given more than once in the same rule
Makefile:142: target 'arm64_perf.o' given more than once in the same rule
Makefile:142: target 'arm64_perf.o' given more than once in the same rule
Makefile:142: target 'arm64_perf.o' given more than once in the same rule
aarch64-none-elf-ld: warning: nuttx_user has a LOAD segment with RWX permissions
Makefile:142: target 'arm64_perf.o' given more than once in the same rule
  [1/1] Normalize fvp-armv8r/pnsh_smp

@simbit18 any idea?

@acassis I've restarted the workflow; let's see if it freezes again.

@simbit18
Copy link
Contributor

@simbit18 @lupyuen did you see @btashton comments here: #9420

Seems like he was working on something to improve our CI before SpaceX killed him :-D

@acassis Very interesting
@lupyuen take a look
@btashton Generates JUnit XML test reports for CI jobs
#9438

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Arch: xtensa Issues related to the Xtensa architecture Area: Build system Board: xtensa Size: XL The size of the change in this PR is very large. Consider breaking down the PR into smaller pieces.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants