Using Device Classes

A device class needs three matching pieces: a nonzero CFG_TUD_* instance count, class descriptors in the configuration descriptor, and the required application callbacks. Start from the nearest device example instead of writing descriptors from scratch.

Setup checklist

  1. Enable the device stack and each class in tusb_config.h. A class value is normally the maximum number of simultaneous class instances, not a boolean.

  2. Add the matching TUD_*_DESCRIPTOR macro to the configuration descriptor and include its TUD_*_DESC_LEN in the total length.

  3. Assign unique interface numbers and endpoint addresses. Some functions use more than one interface; for example, CDC ACM normally uses two.

  4. Implement the descriptor and class callbacks used by the example.

  5. Call tud_task() regularly, or run it in a dedicated RTOS task.

For example, one CDC ACM function starts with:

// tusb_config.h
#define CFG_TUD_ENABLED 1
#define CFG_TUD_CDC     1

// One entry inside the configuration descriptor
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 0, EPNUM_CDC_NOTIF, 8,
                   EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),

See Integrating TinyUSB for stack initialization and the full descriptor callback pattern.

Common configuration options

These options apply before the class-specific settings described on the other pages. Defaults come from src/tusb_option.h.

Option

Default

What it controls

CFG_TUD_ENABLED

Root-port mode

Enables the device stack. Set it explicitly when the selected root-port mode does not already select device operation.

CFG_TUD_MAX_SPEED

Root-port mode

Highest speed for which the device stack and descriptors are built. High-speed devices also need valid qualifier and other-speed descriptors.

CFG_TUD_ENDPOINT0_SIZE

64 bytes

Control endpoint maximum packet size. It must match bMaxPacketSize0 in the device descriptor and the controller’s capability.

CFG_TUD_ENDPOINT0_BUFSIZE

Endpoint 0 size

Staging buffer for control data stages; longer requests are moved through it in chunks of this size. Keep it a multiple of CFG_TUD_ENDPOINT0_SIZE. Audio uses it as its control buffer when CFG_TUD_AUDIO_CTRL_BUF_SZ fits.

CFG_TUD_INTERFACE_MAX

16

Maximum total USB interfaces across the active configuration, including every interface used by composite functions.

CFG_TUD_TASK_EVENTS_PER_RUN

16

Maximum events handled by one tud_task_ext() call. 0 removes the limit; a smaller value reduces one-call latency at the cost of more task invocations.

CFG_TUD_ENDPPOINT_MAX

Controller maximum

Highest endpoint-number pool retained by the stack. Lowering it can save RAM, but it must cover every configured endpoint number.

CFG_TUD_MEM_SECTION / CFG_TUD_MEM_ALIGN

Common USB settings / 4-byte alignment

Places and aligns controller-facing buffers for DMA. Override these when the device controller requires a particular RAM region or alignment.

CFG_TUD_ENDPPOINT_MAX contains the double P for compatibility; use the spelling shown above.

Core API and callbacks

API or callback

What it does

tusb_init()

Initializes a root port with an explicit role and speed. Call it before the task function and check its boolean result. With an RTOS, call it after the scheduler starts, from the task that runs tud_task().

tud_task() / tud_task_ext()

Dispatches bus, control, class, and completion events. The extended form selects a wait timeout; its in_isr argument is currently ignored. Never call either from an ISR.

tud_connected() / tud_mounted() / tud_ready()

Reports progressively stronger states: bus activity, configured by the host, and configured plus not suspended. Use tud_ready() before initiating normal traffic.

tud_suspended() / tud_remote_wakeup()

Tests suspend state and requests remote wakeup. Wakeup succeeds only when the host enabled it and the device is suspended.

tud_disconnect() / tud_connect()

Controls the USB pull-up to force a logical detach or attach. Both always return true; on a controller without pull-up control the call does nothing.

tud_mount_cb() / tud_umount_cb()

Announces configuration and removal. Initialize or discard configuration-dependent application state here.

tud_suspend_cb() / tud_resume_cb()

Announces bus power-state changes. The suspend callback also reports whether remote wakeup was enabled by the host.

tud_descriptor_*_cb()

Supplies device, configuration, string, BOS, device qualifier, and other-speed configuration descriptors on request. Returned storage must remain valid through the control transfer.

tud_control_xfer() / tud_control_status()

Completes the data/status stages of an application-handled control request. The data length is truncated to the request’s wLength.

Interfaces and instances

Single-instance helpers such as tud_cdc_read() operate on instance zero. Their _n_ forms, such as tud_cdc_n_read(itf, ...), select a class instance when the corresponding CFG_TUD_* value is greater than one. Class instance numbers are not necessarily USB bInterfaceNumber values.

Endpoint direction is always described from the USB device’s point of view:

  • IN sends data from the device to the host.

  • OUT receives data from the host at the device.

Buffers and callbacks

Most class callbacks run from tud_task(). *_isr callbacks, such as tud_audio_tx_done_isr(), and tud_event_hook_cb() usually run in interrupt context, but some ports run them synchronously inside the API call that arms the endpoint (on MUSB, re-arming an OUT endpoint can drain a staged packet and complete the transfer there). Make them safe in both contexts: no blocking and no ISR-only OS calls. A few, such as tud_video_prepare_payload_cb() and tud_network_xmit_cb(), can also run inside the API call that triggers them. Keep callbacks short and move lengthy work to an application task.

Buffered write APIs return the number of bytes accepted, which can be shorter than requested. Audio is the exception: its FIFOs overwrite the oldest unsent data when full (see Audio). Check the return value and use the class’s flush function when latency matters. Size endpoint and software buffers for the active bus speed; copy the full-speed/high-speed pattern from an example that supports both.

Before testing on hardware, verify that:

  • the configuration descriptor’s total length and interface count are exact;

  • every endpoint address is unique within the configuration;

  • descriptor packet sizes are legal for the endpoint type and speed, and each class endpoint buffer (CFG_TUD_*_EPSIZE or CFG_TUD_*_EP_BUFSIZE, see the class guide) holds at least one packet;

  • callbacks never retain a pointer whose documented lifetime has ended.