Linux USB-Audio Gadget (RPi4 OTG)

On RPi4/5.18+ the USB gadget works up to maximum USB2 isochronous bandwidth of 1kbyte * 8000 microframes, tested in linux and windows wasapi exclusive (using slightly modified https://github.com/HEnquist/wasapi-rs/blob/master/examples/loopback.rs ). This is 768kHz/2ch/32bit duplex I2S DAC-> ADC 350kHz@-150dB with a dirty SMPS @54kHz (hence the peak), REW running in linux: <snip>

Yup - I can set 192K and 32 bit on the linux side but the OSX side sees the device but indicates it's a bad format and ignores the device. Further work is needed as OSX can run 192K 32bit.. so I suspect it's either an OSX USB limitation (ie using USBAudio 1.0) but it shows as highspeed. I've tried capturing the device on the virtual box ubuntu linux VM but it seems to have issues with OSX this maybe because I have MIDI setup to configure it for use as it's connected. Anyways I digress.
I will look into it at some point.
 
Too bad. I did not buy an RPi4 in time. So I tried to get my RPi 3A+ and 3B+ into gadget mode instead: No success, accordingly with the common sense in the internet. It is widely claimed not possible to do so, explaining this fact with different arguments along different models.

In my case with both the 3A+ and the 3B+ running on aarch64 and having tried different approaches, I always end up with the same result:
$dmesg complains about an unavailable UDC for g_audio, dwc2 instead seems ok
$modprobe shows g_audio active, but not dwc2
$aplay -l shows no traces of a g_audio related device.

Therefore, after multifailing ... may I ask this question here: Is the Internet sadly right, or is there anybody here having succeeded to convert either an RPi 3A+ or 3B+ into a USB gadget? And if so - what was the magic?
 
Last edited:
Too bad. I did not buy an RPi4 in time. So I tried to get my RPi 3A+ and 3B+ into gadget mode instead: No success, accordingly with the common sense in the internet. It is widely claimed not possible to do so, explaining this fact with different arguments along different models.

In my case with both the 3A+ and the 3B+ running on aarch64 and having tried different approaches, I always end up with the same result:
$dmesg complains about an unavailable UDC for g_audio, dwc2 instead seems ok
$modprobe shows g_audio active, but not dwc2
$aplay -l shows no traces of a g_audio related device.

Therefore, after multifailing ... may I ask this question here: Is the Internet sadly right, or is there anybody here having succeeded to convert either an RPi 3A+ or 3B+ into a USB gadget? And if so - what was the magic?
You may be able to get the 3A+ to work with OTG mode based on info from this thread. It looks like there may be a few caveats to that so make sure to thoroughly read that thread. Specifically the part about adding
dtoverlay=dwc2,dr_mode=peripheral
to /boot/config.txt

It looks like the 3B+ uses a USB hub controller that does not allow USB device mode (supposedly this one) to control the extra USB ports. I haven't found any evidence suggesting there is any possibility on making the 3B+ work with OTG mode after a brief search online.

The USB port on the 3A+ looks like its directly attached to the CPU. I've also seen conflicting information online about if it will work, but try the solution from the linked thread and see if that works for you.
 
Thank you for your advice, this is exactly my setup as you descripbe.

Specifically, it might be helpful to me to know something more precise about the missing dwc2 driver $modprobe, and about the unavailable UDC in $dmesg to debug things.

Maybe, as a starting point, it could also be helpful für "us all" to have a full setup how-to for the RPi4. I have the feeling that we dummies down here are watching our experts here flying very high, and instead we would like to know how to liftoff ... So please, could someone e.g. post an up-to-date and lean how-to, either with a debian64 or aarch64 gusto? I would be very thankful for this guidance.
 
Do you have the dwc2 module loaded (lsmod)? If not:

clean the modules:

rmmod g_audio; rmmod usb_f_uac2; rmmod u_audio; rmmod libcomposite; rmmod dwc2

And start from the beginning:

modprobe dwc2

check dmesg

modprobe g_audio c_srate=48000 p_srate=48000 p_chmask=3 c_chmask=3 c_ssize=2 p_ssize=2

check dmesg
 
Do you have the dwc2 module loaded (lsmod)? If not:

clean the modules:

rmmod g_audio; rmmod usb_f_uac2; rmmod u_audio; rmmod libcomposite; rmmod dwc2

And start from the beginning:

modprobe dwc2

check dmesg

modprobe g_audio c_srate=48000 p_srate=48000 p_chmask=3 c_chmask=3 c_ssize=2 p_ssize=2

check dmesg
Thank you for your answer.

As said, I am on 5.19.8-1-aarch64 and a RPi3A+.

For the dwc2 part of the story:
$ lsmod -->> dwc2 does not appear in the readout. I think because of the (just now discovered) builtin nature of the module:
# rmmod dwc2 -->> throws the message "...rmmod: ERROR: Module dwc2 is builtin..."
$ dmesg | grep dwc2 -->> all seems well, with 6 messages like " ... DWC OTG Controller ..." and "new USB bus registered ..."
When booting the RPi, dwc2 starts up without any other statements, neither in /boot/config.txt, nor in /etc/modules-load.d/*.conf. I think because of it's kernel builtin state.

For the g_audio part of the story:
$ dmesg | grep g_audio -->> complains about "...UDC core: g_audio: couldn't find an available UDC..."
$ lsmod -->> g_audio and libcomposite do appear in the readout (instead, rmmod usb_f_uac2 and rmmod u_audio are absent).
# rmmod g_audio -->> g_audio gets removed.
# modprobe g_audio x y z
$ dmesg | grep g_audio -->> complains twice about "...UDC core: g_audio: couldn't find an available UDC..."

Summarizing my impressions as far as I think to understand the meccano, but maybe I am wrong: The culprit is the fact that dwc2 is "builtin" and configured at kernel compilation time. So there is no possibility to traditionally load dwc2 with an argument. But in a RPi3A+, dwc2 shoud be loaded with an optional argument dr_mode=peripheral, as the RPi3A+ is hardcoded to work in host mode. dr_mode=peripheral would force it back into peripheral (=OTG) mode by this very argument.

In the widespread HowTo's for the RaspiOS (Debian) there is the advice to include this statement in the /boot/config.txt as
dtoverlay=dwc2,dr_mode=peripheral
This does not help for the aarch64 distribution. Maybe Debian has a non-builtin, dynamic dwc2 management.

So then, does this mean that not only the RPi3A+, but also the aarch64 kernel is static host-mode, "dwc2-hardcoded"? And if so, is there a workaround (except of compiling a more gentle kernel) in order to teach a RPi3A+ the OTG mode?

Nota bene: maybe (and hopefully) my hypothesis is false altogether, which would open a way to another solution?
 
Look at this issue on rpi4 kernel https://github.com/raspberrypi/linux/issues/4992 . It's perfectly possible your kernel + DTS files do not have all necessary configs for your RPi3.

Why do you need that distribution? For support of specialty RPi features I would recommend staying with official RPi kernel/distro. Still you would need to compile rpi 5.18+ kernel for proper UAC2 gadget support.
 
Ok, I understand. Thank you for your hint.
I am using Arch on my other Linux systems, so I am a bit familiar with it. Of course, choosing the RaspiOS for a raspi would be an obvious move. RaspiOS currently is 5.15, and being aware of the development made since 5.15 (cudos and many thanks to you!), as aarch64 comes along with 5.19, I thought I would comfortably choose this one. Bad choice. Compiling an actual kernel for RaspiOS would have been less time consuming altogether ...
 
Ok, I understand. Thank you for your hint.
I am using Arch on my other Linux systems, so I am a bit familiar with it. Of course, choosing the RaspiOS for a raspi would be an obvious move. RaspiOS currently is 5.15, and being aware of the development made since 5.15 (cudos and many thanks to you!), as aarch64 comes along with 5.19, I thought I would comfortably choose this one. Bad choice. Compiling an actual kernel for RaspiOS would have been less time consuming altogether ...
I have a whole guide you can follow if you use RaspiOS. It is targeted towards RPi Z2W, but you should be able to pretty easily adapt it to the 3A+. In the process, you can update to much never kernel versions with the latest g_audio additions
 
  • Like
Reactions: 1 users
I have a whole guide you can follow if you use RaspiOS. It is targeted towards RPi Z2W, but you should be able to pretty easily adapt it to the 3A+. In the process, you can update to much never kernel versions with the latest g_audio additions

Yes!!! Seen it before, and also bookmarked it along with this other one, but I did not make the link to you on this forum. Very nice! This indeed is the most comprehensive guide I found until now, thank you for this one. And furthermore, these words of yours inside the guide do open a very appealing perspective also:

" ... I will probably update this guide periodically as new features get added to the g_audio driver. ..."

Keep this guide up to date and alive, please.
 
I have a whole guide you can follow if you use RaspiOS. It is targeted towards RPi Z2W, but you should be able to pretty easily adapt it to the 3A+. In the process, you can update to much never kernel versions with the latest g_audio additions

I followed all basic instructions of the above mentionned guide and failed again at the very early tests:
$lsmod -->> now shows dwc2 and one role related to it, along and as previosly g_audio with the depending libcomposite
BUT
$dmesg --> complains "...udc-core: couldn't find an available UDC - added [g_audio] to list of pending drivers..."

So, the same error occurs. It also occurs the same way if testwise I try to lad the g_ether module, so it's not an issue of a partially flawed g_audio driver of the 5.15 kernel. And my initial suspicion was wrong. It's not a kernel/module issue of aarch64 because of g_audio as an inbuilt module. Instead, there might be something wrong with the RPi3A+ and the host/client mode (non-)switching. Despite the dtoverlay=dwc2,dr_mode=peripheral statement in /boot/config.txt.

Therefore my next question: How can be checked wheter a pi is in host or in device mode, eventually within the /sys/kernel/debug/... filesystem?
 
Last edited:
I found that guide (very useful thank you) for a 64bit kernel8 implementation on the zero 2w with (rpi-5.18.y) but the kernel seems to fail due to the networking obtaining a DCHP address and wlan0 starts only for it to be shut down completely. I'm unsure what is going on but it's clear in the logs.
This stuff should be easy (I'm a BSc in software engineering).. but I've never got on with Linux.
 
Finally good news!

RPi 3 A+ runs in OTG mode (RaspiOS lite, either 32Bit or 64Bit, OS as is, actually with the flawed 5.15 Kernel, full audio functionality not tested yet)
and also
RPi Zero W runs in OTG mode (RaspiOS lite 32Bit only, OS as is, actually with the flawed 5.15 Kernel, full audio functionality not tested yet)

The successful setup for all three variants was as follows:

/boot/config.txt - file was edited
dtoverlay=dwc2 for the RPi Zero W
dtoverlay=dwc2,dr_mode=peripheral for the RPi 3A+ (because otherwise it's preset to work as a host. Must be a client instead to allow OTG mode)
When using the RPi3A+ along with the 64Bit OS version you may best also delete all specific references for the RPI4's family from this file

/etc/modprobe.d/g_audio.conf - file was added
options g_audio c_chmask=3 p_chmask=3 c_srate=48000 p_srate=48000 c_ssize=2 p_ssize=2 iProduct="AnyDesignationYouWant"
Or so, set these values to what you need

/etc/modules-load.d/g_audio.conf - file was added
g_audio
/etc/modules-load.d/dwc2.conf - file was added
dwc2
I chose to load the modules this way, because either the /boot/cmdline.txt, or the /etc/modules files do not seem universally present in all OS versions I tried until now. Instead, /etc/modules-load.d/*.conf was an option found everywhere.

Other approaches may or may not work. I am unfortunately not able to track down which configurations made some of my former attempts fail in retrospective. I had fails with both 64Bit versions of the RaspiOS and Arch.

So we have 4 different RPi's reportedly suited as OTG's until now: The RPi Zero W, the RPi Zero 2 W, the RPi 3A+ and the RPi 4.
 
Last edited:
  • Like
Reactions: 1 user
I found that guide (very useful thank you) for a 64bit kernel8 implementation on the zero 2w with (rpi-5.18.y) but the kernel seems to fail due to the networking obtaining a DCHP address and wlan0 starts only for it to be shut down completely. I'm unsure what is going on but it's clear in the logs.
This stuff should be easy (I'm a BSc in software engineering).. but I've never got on with Linux.
I may have encountered a similar bug on early 5.18 builds. I'm not really sure as I never ended up checking the logs, but I wouldn't be able to see the Pi on a network though it looked like the Pi booted. I hooked the pi up to a monitor and rebooted. That fixed whatever problem I was having. I guess that could be related to your problem but that would be a weird fix for a network issue.


Finally good news!

RPi 3 A+ runs in OTG mode (RaspiOS lite, either 32Bit or 64Bit, OS as is, actually with the flawed 5.15 Kernel, full audio functionality not tested yet)
and also
RPi Zero W runs in OTG mode (RaspiOS lite 32Bit only, OS as is, actually with the flawed 5.15 Kernel, full audio functionality not tested yet)

The successful setup for all three variants was as follows:

/boot/config.txt - file was edited
dtoverlay=dwc2 for the RPi Zero W
dtoverlay=dwc2,dr_mode=peripheral for the RPi 3A+ (because otherwise it's preset to work as a host. Must be a client instead to allow OTG mode)
When using the RPi3A+ along with the 64Bit OS version you may best also delete all specific references for the RPI4's family from this file

/etc/modprobe.d/g_audio.conf - file was added
options g_audio c_chmask=3 p_chmask=3 c_srate=48000 p_srate=48000 c_ssize=2 p_ssize=2 iProduct="AnyDesignationYouWant"
Or so, set these values to what you need

/etc/modules-load.d/g_audio.conf - file was added
g_audio
/etc/modules-load.d/dwc2.conf - file was added
dwc2
I chose to load the modules this way, because either the /boot/cmdline.txt, or the /etc/modules files do not seem universally present in all OS versions I tried until now. Instead, /etc/modules-load.d/*.conf was an option found everywhere.

Other approaches may or may not work. I am unfortunately not able to track down which configurations made some of my former attempts fail in retrospective. I had fails with both 64Bit versions of the RaspiOS and Arch.

So we have 4 different RPi's reportedly suited as OTG's until now: The RPi Zero W, the RPi Zero 2 W, the RPi 3A+ and the RPi 4.

So you were able to fix the issue with the 3A+ by editing/adding the 3 files listed like so?
/boot/config.txt - file was edited
dtoverlay=dwc2,dr_mode=peripheral
/etc/modules-load.d/g_audio.conf - file was added
g_audio
/etc/modules-load.d/dwc2.conf - file was added
dwc2

Also, have you tried this on 5.18+ kernels?
If so, I would like to update my guide with this info for the 3A+.
 
I may have encountered a similar bug on early 5.18 builds. I'm not really sure as I never ended up checking the logs, but I wouldn't be able to see the Pi on a network though it looked like the Pi booted. I hooked the pi up to a monitor and rebooted. That fixed whatever problem I was having. I guess that could be related to your problem but that would be a weird fix for a network issue.
Searching seems to show that the old kernels did have this but but for whatever reason it manifested itself very infrequently. The later kernel has somehow managed to exacerbate the issue to being a showstopper.
 
/boot/config.txt - file was edited
dtoverlay=dwc2,dr_mode=peripheral
/etc/modules-load.d/g_audio.conf - file was added
g_audio
/etc/modules-load.d/dwc2.conf - file was added
dwc2

So you were able to fix the issue with the 3A+ by editing/adding the 3 files listed like so?

Yes. This goes for both the mentionned RPi's Zero W and 3A+ (no success with a RPi 3B+ because of it's inbuilt 1-to-4 USB hub).

Don't bother with neither any /boot/cmdline.txt if present, nor with the /etc/modules file, if present. And also get rid of these dedicated "OTG"-Statements in the /boot/config.txt for the RPi4 family.

The dtoverlay=dwc2,dr_mode=peripheral statement is mandatory for the 3A+. Instead, the dr_mode=peripheral part is not necessary for the Zero W, but if present, it is not harmful.

After these interventions both the Zero W and the 3A+ appear as a USB Gadget in the windows sound system. 3A+ has to be connected along with a normal A-to-A USB strip to the Windows host.

Also, have you tried this on 5.18+ kernels?
If so, I would like to update my guide with this info for the 3A+.

I did not compile an extra kernel myself. So no testing with 5.15+.
 
Hello,

I am interested in whether RPi can be used to process bit-perfect audio.
(Well, in my project I am trying to use this for datatransfer over audio - hence the want for the bit-perfectness).
So far I have the following results:
RPi Zero W - for some reason, I obtain some noise - some samples are shifted by +1 or -1
RPi Zero 2W - for some reason, "stuffing" bytes are inserted -by "stuffing" I mean, that the first byte in every four bytes is "80".
When feeding 24bit audio, these stuffing bytes are really stuffing, as I get "80""true_sample", but for 32bit audio the first byte gets overwritten by the stuffing byte.

Do you have any idea what could be causing this behavior?
I have tested this both on Windows host and on Linux host, the behavior stays the same.
(If you would like to reproduce it on Windows, do not use the Groove media player - it always somehow scrambles the audio).

(the kernel version I used is 5.19.0-rc1+)

Best
 
The rpi depends on the bus bandwidth and the OS in use.
I had a look at the RPI previously, the issues are:
  • no USB developer documentation from broadcom. Thus it’s impossible to start coding without using a linux OS (ie no bare metal).
  • RPI clocks are terrible, so an external clock is required.. at high MHz makes an issue for the RPI realtime support.
 
I am interested in whether RPi can be used to process bit-perfect audio.
RPi4B UAC2 gadget is bitperfect up to full USB2 isochronous bandwidth for one transaction ( = 1024 bytes x 1 packet x 8000 uframes per second).

What is your USB UAC2 packet size and bInterval?

Do you have the same problem on capture and playback?

Buffer underruns are possible, but these do not change the transferred data, but skip blocks of data completely.

It's possible your Zero W having just one core is not capable of handling large amounts of data. 2W with 4 cores less likely. Anyway as I said that causes blocks of data missing, not rewritten individual bytes or even bits.

If I were to make a guess, the problem is somewhere else in your chain. Please recheck thoroughly every node of your chain, at best by analyzing integrity of the data entering/leaving each node.
 
  • RPI clocks are terrible, so an external clock is required.. at high MHz makes an issue for the RPI realtime support.
IIUC you are talking about the I2S peripheral, not the UAC2 gadget. RPi4B with standard non-RT linux handles I2S at 1.5MHz 16bit loopback bitperfectly https://forums.raspberrypi.com/viewtopic.php?p=1865884&sid=b5bdbc5593ca57042eb2023133f1578d#p1865884

The PCM I2S controller uses DMA for data transfer from the 64-frame I2S ser/deser FIFOs to RAM, the CPU IRQ rate is defined by the RAM buffer as configured by alsa.
 
Last edited:
  • Like
Reactions: 1 user