Introduction.
USB is a highly efficient interface for transporting audio and voice data, offering sufficient bandwidth for applications from voice telephony to high-quality audio playback and recording. To ensure compatibility between devices, the USB Audio Class specification defines standardized mechanisms for audio transport and control, allowing audio peripherals to operate with generic drivers and minimizing platform software requirements.Required equipment.
To start, we need to flash our Linux BSP into the board. You can build it by yourself or use the pre-built BSP to facilitate the process.
This post was tested with Linux 6.18.20_2.0.0, if you have issues to flash the board, please refer to this article:
This is how my hardware setup looks:
USB gadget setup.
The following script configures the board to work as an USB audio gadget in Linux. This setup allows the board to be detected as a standard audio device, enabling audio streaming over the USB connection without requiring a custom USB protocol on the host side.
The script creates the USB gadget configuration, assigns the required USB descriptors, with predefined audio parameters such as 16 kHz sample rate, 32-bit sample size, and single-channel capture/playback support. This example is useful for applications such as voice capture, audio processing, USB microphones, or embedded audio demos.
#!/bin/sh
# USB gadget ConfigFS paths
CONFIGFS=/sys/kernel/config/usb_gadget
GADGET=$CONFIGFS/g1
CONFIG=$GADGET/configs/c.1
FUNCTIONS=$GADGET/functions
# USB device descriptors and audio configuration
VID="0x1fc9"
PID="0x0330"
SERIALNUMBER="0123456789"
MANUFACTURER="NXP Semiconductors"
PRODUCT="i.MX USB Audio Gadget"
SAMPLE_RATE=16000
SAMPLE_SIZE=4 # 32-bit audio samples
echo "Setting up USB gadget with the following parameters:"
echo " Vendor ID: $VID"
echo " Product ID: $PID"
echo " Serial Number: $SERIALNUMBER"
echo " Manufacturer: $MANUFACTURER"
echo " Product: $PRODUCT"
echo " Sample Rate: $SAMPLE_RATE Hz"
function add_uac2_function() {
# Create a USB Audio Class function instance
mkdir $FUNCTIONS/uac2.$1
echo "$PRODUCT $1" > $FUNCTIONS/uac2.$1/function_name
# c_* parameters configure USB Capture
# p_* parameters configure USB Playback
# Audio sample rate configuration
echo $SAMPLE_RATE > $FUNCTIONS/uac2.$1/c_srate
echo $SAMPLE_RATE > $FUNCTIONS/uac2.$1/p_srate
# Audio sample size in bytes
echo $SAMPLE_SIZE > $FUNCTIONS/uac2.$1/c_ssize
echo $SAMPLE_SIZE > $FUNCTIONS/uac2.$1/p_ssize
echo $2 > $FUNCTIONS/uac2.$1/c_chmask
echo $3 > $FUNCTIONS/uac2.$1/p_chmask
# Enable mute and volume controls visible to the USB host
echo 0x1 > $FUNCTIONS/uac2.$1/c_mute_present
echo 0x1 > $FUNCTIONS/uac2.$1/c_volume_present
echo 0x1 > $FUNCTIONS/uac2.$1/p_mute_present
echo 0x1 > $FUNCTIONS/uac2.$1/p_volume_present
# Add the UAC2 function to the active USB configuration
ln -s $FUNCTIONS/uac2.$1 $CONFIG
}
function create_config() {
# Create the USB gadget device
mkdir $GADGET
# Set USB Vendor ID and Product ID
echo $VID > $GADGET/idVendor
echo $PID > $GADGET/idProduct
# Standard USB string descriptors
mkdir $GADGET/strings/0x409
echo $SERIALNUMBER > $GADGET/strings/0x409/serialnumber
echo $MANUFACTURER > $GADGET/strings/0x409/manufacturer
echo $PRODUCT > $GADGET/strings/0x409/product
# Create USB configuration description
mkdir -p $CONFIG/strings/0x409
echo $1 > $CONFIG/strings/0x409/configuration
}
# Create a UAC2 audio gadget with mono capture and mono playback
create_config "UAC2"
add_uac2_function "Active" 0x1 0x1
echo "Done"Clean USB gadget.
If you need to free up the USB port, it is necessary to run the following script.
#!/bin/sh
# USB gadget ConfigFS paths
CONFIGFS=/sys/kernel/config/usb_gadget
GADGET=$CONFIGFS/g1
CONFIG=$GADGET/configs/c.1
FUNCTIONS=$GADGET/functions
# Verify that the gadget exists before attempting cleanup
if [ ! -d "$GADGET" ]; then
echo "Gadget does not exist"
exit 0
fi
# Disconnects the gadget from the host before removal.
echo "" > $GADGET/UDC 2>/dev/null
# Remove UAC2 function links from the configuration
rm -f $CONFIG/uac2.* 2>/dev/null
# Remove UAC2 function instances
rmdir $FUNCTIONS/uac2.* 2>/dev/null
# Remove configuration strings and configuration directory
rmdir $CONFIG/strings/0x409 2>/dev/null
rmdir $CONFIG/strings 2>/dev/null
rmdir $CONFIG 2>/dev/null
# Remove gadget string descriptors
rmdir $GADGET/strings/0x409 2>/dev/null
rmdir $GADGET/strings 2>/dev/null
# Remove the gadget itself
rmdir $GADGET 2>/dev/null
echo "Cleanup completed"
Testing.
Both scripts can be created into the board using nano or a similar text editor through the console but is easier to do it in your host machine and send it for example using SCP.
After you created both scripts you can send them with the next command:
scp <file_to_transfer> <user>@<ip_address>:<destination_path>
Once you connected the Ethernet cable in any of the available RJ45 connectors in your board, you can get the IP address with the next command:
ifconfig
Now, in your host machine, you can go to the path where are your USB scripts and send them:
scp cleanup_usb_gadget.sh root@<frdm_board_ip>:~/
scp setup_usb_gadget.sh root@<frdm_board_ip>:~/
After transfer the scripts to the board, you need to change file permissions to execute the scripts with chmod and run USB gadget setup script to configure the board as a USB audio interface.
chmod u+x *_usb_gadget.sh
Once this script have been executed you can see the USB port in ALSA as a sink/source device:
Then, you need to connect the USB port to a host computer and will be recognized as an USB interface:
You can validate that the device is working according to the configuration defined in setup script:
I will use Audacity to test audio transfer from on board microphones, in Edit -> Preferences -> Audio settings select i.MX Active as recording device:
Note: Use the same sample rate of setup_usb_gadget.sh configuration to avoid errors with the DAW.
In the board, I will start the recording by running the following GStreamer pipeline, which captures audio from the onboard microphones and streams it through the USB port:
gst-launch-1.0 alsasrc device=hw:micfilaudio,0 ! queue ! audioconvert ! audio/x-raw,channels=1,rate=16000,format=F32LE ! audioconvert ! alsasink device=hw:UAC2Gadget,0
After this configuration, add an audio track and now, is possible to get the audio and record it directly in the DAW:
If you want to play audio from host PC trough the i.MX board, you need to run the next command before start the audio playback in host. Make sure that the audio card selected is not busy:
gst-launch-1.0 -v alsasrc device=hw:UAC2Gadget,0 ! audio/x-raw,format=S32LE,rate=16000,channels=1 ! audioconvert ! audioresample ! audio/x-raw,format=S16LE,rate=48000,channels=2 ! audioconvert ! alsasink device=plughw:mqsaudio,0
In this case, I selected the MQS output in 3.5mm output jack, you can connect the speakers or headphones with this connector.
After run the clean USB gadget script, the USB audio device is not longer detected in host PC and the board output is the next:
References.
USB Implementers Forum. (1998, March 18). Universal Serial Bus device class definition for audio devices (Release 1.0) [PDF]. USB-IF. https://www.usb.org/sites/default/files/audio10.pdf