Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

usb: device_next: new USB Audio 2 implementation #67614

Merged
merged 3 commits into from
Feb 1, 2024

Conversation

tmon-nordic
Copy link
Contributor

Introduce new USB Audio 2 implementation written from scratch. Main goal behind new implementation was to perform entity configuration with devicetree bindings, hiding the descriptor complexity from application.

Initial implementation is working at Full-Speed only. High-Speed support will come later, but even at Full-Speed only this is viable replacement for old stack USB Audio 1 class (USB Audio 1 is limited to Full-Speed by specification, i.e. it is explicitly forbidden for USB Audio 1 device to work at High-Speed).

Implemented is only absolute minimum set of features required for basic implicit and explicit feedback samples. Only one sample frequency is currently supported. Optional interrupt notifications are not supported.


The implementation does achieve zero-copy for audio samples in the provided explicit feedback sample. Only explicit feedback sample is provided for the time being, but implicit feedback is known to work with this class implementation. The only reason why implicit feedback is not yet published is code quality - and that I want to have a chance to get the class merged before feature freeze. The implicit feedback will come in later.

@tmon-nordic
Copy link
Contributor Author

@jzipperer-fb @larsgk @koffes @alexsven
Finally this PR is open and your feedback is welcome. I understand that grasping through this PR will be challenging - there is a reason why I skipped the implicit feedback for now. I tried to capture all the "I wish I realized that earlier" in the comments, but likely I've missed something.

@jfischer-no jfischer-no added this to the v3.6.0 milestone Jan 15, 2024
@jfischer-no jfischer-no added the Experimental Experimental features not enabled by default label Jan 15, 2024
@koffes koffes requested review from koffes and alexsven January 16, 2024 15:20
Copy link
Collaborator

@koffes koffes left a comment

Choose a reason for hiding this comment

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

I have looked over the PR. It looks well structured and very well commented. I have some minor comments. Though, I am not seasoned enough in USB development to either request changes nor approve this PR.

samples/subsys/usb/uac2_explicit_feedback/src/main.c Outdated Show resolved Hide resolved
/* This sample does not send audio data so this won't be called */
}

#if IS_ENABLED(CONFIG_SOC_COMPATIBLE_NRF5340_CPUAPP)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Looks strange to have this kind of check in the middle of a sample.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree. I just didn't have an idea how to cleanly separate it, i.e. how the "interface" between this target specific code and rest of application should look like. I can try to think of something but not sure if I'll come up with anything better than just inline ifdef.

Copy link
Contributor

Choose a reason for hiding this comment

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

can it be solved without using anything nrfx specific?

Copy link
Contributor Author

@tmon-nordic tmon-nordic Jan 18, 2024

Choose a reason for hiding this comment

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

I don't think so. The only non-specific solution would be to gather tick timestamps in both I2S and USB interrupts. The problematic part is to expose such timestamp information to application and to actually capture the tick timestamp inside the intterupt handler (in all I2S and all USB drivers). This would still most likely need additional logic to filter out for software scheduling overhead (to function properly this needs sub-sample precision, here the PI controller gets sample estimate with 1/32 sample resolution, i.e. value 16 means half-sample, from nRF specific code).

Copy link
Contributor Author

@tmon-nordic tmon-nordic Jan 18, 2024

Choose a reason for hiding this comment

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

Of course, I would like to be proven wrong. But to prove me wrong someone would have to actually come up with actual implementation that works. I agree that is is not ideal to have target-specific code in samples, but here for the time being it seems to be the only sensible approach. The sample can be compiled for other targets (with a compile time warning) and it will operate but will suffer underruns/overruns because the lack of proper feedback (which must be computed at run-time, because it essentially is ratio between actual I2S master clock and USB host SOF clock, i.e. nominally both are the same, but the actual deviations from nominal value is the key here).

For the USB Audio 2 class implementation however, the explicit feedback value is just something that application must provide (unless it either uses implicit feedback in which case it has to send audio samples at appropriate rate to host; or unless it claims that the clock is sof-synchronized in which case there'll always be nominal number of samples). The sample is in my opinion mandatory for such non-obvious functionality. While it would be best to have it target independent, it is always helpful to have some reference implementation that is known to work.

Copy link
Collaborator

@kartben kartben Jan 18, 2024

Choose a reason for hiding this comment

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

I don't think we would want a sample living among the generic samples to be specific to Nordic? I had a quick look and I would say that so far we seem to have done a pretty good job at avoiding that (not sure about this guy, but that seems to be pretty much it) .
Could this be moved to boards/nrf? Is the "common" (and I understand maybe degraded?) version of this sample still something thart could live in an agnostic way in the locaiton currently proposed?

Note: did a quick check this way: grep "include <" samples/subsys -R | sort -u

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@kartben We could remove the sample altogether then because without target specific code it does not make sense to have it. We could then just have class implementation alone without any sample. Would doing so be any better?

Copy link
Contributor Author

@tmon-nordic tmon-nordic Jan 18, 2024

Choose a reason for hiding this comment

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

@kartben Not sure if you noticed but the sample is not really specific to Nordic as it is now. It does have the #else with warning and empty implementation of the necessary feedback functionality. This shows how to do the feedback, with two different methods, on Nordic which is IMHO better than removing the Nordic specific part and leaving all targets essentially not doing what this sample is supposed to show. Because the targeet specific code is also extremely application (i.e. sample) specific it doesn't make slightest sense to put it in boards/nrf. Having target specific part for at least one target is in my opinion the proper way to have sample with unavoidable target specific functionality in it.

subsys/usb/device_next/class/usbd_uac2.c Outdated Show resolved Hide resolved
@larsgk
Copy link
Contributor

larsgk commented Jan 17, 2024

This is a very interesting PR, well done and well documented - and I'd like to try it out if I get some time this weekend. That said, I think it would be great if it was somehow possible to not have too much vendor/board specific code in generic samples.

BTW: I do realize that bot I2S, PWM and probably more higher level abstractions in Zephyr may need to be revisited (e.g. the PWM API can do 'beep', while the nrfx pwm can play complex sample sequences).

Copy link
Member

@carlescufi carlescufi left a comment

Choose a reason for hiding this comment

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

Minor nits

samples/subsys/usb/uac2_explicit_feedback/prj.conf Outdated Show resolved Hide resolved
samples/subsys/usb/uac2_explicit_feedback/README.rst Outdated Show resolved Hide resolved
samples/subsys/usb/uac2_explicit_feedback/README.rst Outdated Show resolved Hide resolved
samples/subsys/usb/uac2_explicit_feedback/README.rst Outdated Show resolved Hide resolved
carlescufi
carlescufi previously approved these changes Jan 31, 2024

#include "../app.overlay"

&pinctrl {
Copy link
Collaborator

@jfischer-no jfischer-no Jan 31, 2024

Choose a reason for hiding this comment

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

Someone might try to use an Arduino shield with this sample code or copy&paste it to own application, for whatever reason. Should arduino_spi node be deleted? @gmarull

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Collaborator

@jfischer-no jfischer-no left a comment

Choose a reason for hiding this comment

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

A few comments, otherwise it looks very presentable.

subsys/usb/device_next/usbd_class_api.h Outdated Show resolved Hide resolved
include/zephyr/usb/class/usbd_uac2.h Outdated Show resolved Hide resolved
#define CONTROL_SELECTOR(setup) ((setup->wValue & 0xFF00) >> 8)
#define CONTROL_CHANNEL_NUMBER(setup) (setup->wValue & 0x00FF)

typedef enum __packed {
Copy link
Collaborator

Choose a reason for hiding this comment

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

curious, what is the reason to have __packed here?

Copy link
Contributor Author

@tmon-nordic tmon-nordic Feb 1, 2024

Choose a reason for hiding this comment

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

To instruct compiler that that the enum should use smallest type available. There is a constant array of all entity types placed in code memory (flash) for every UAC2 instance (necessary for e.g. handling control transfers). Such construct (enum __packed) is used in internal Bluetooth API as well.

Copy link
Collaborator

@jfischer-no jfischer-no Feb 1, 2024

Choose a reason for hiding this comment

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

I mean, do you really need it here? It does not use more memory without. Not sure fshort-enums flag is set for GCC in Zephyr.

git grep "fshort-enums"
boards/posix/nrf_bsim/CMakeLists.txt:  -fshort-enums
boards/posix/nrf_bsim/CMakeLists.txt:target_compile_options(native_simulator INTERFACE -fshort-enums)
cmake/compiler/armclang/target.cmake:  -fshort-enums
cmake/compiler/clang/target.cmake:      -fshort-enums
cmake/compiler/clang/target.cmake:      -fshort-enums

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed the __packed from enum typedef.

subsys/usb/device_next/class/usbd_uac2.c Outdated Show resolved Hide resolved
subsys/usb/device_next/class/usbd_uac2.c Show resolved Hide resolved
subsys/usb/device_next/class/usbd_uac2.c Show resolved Hide resolved
subsys/usb/device_next/class/usbd_uac2.c Show resolved Hide resolved
subsys/usb/device_next/class/usbd_uac2.c Outdated Show resolved Hide resolved
return -ENOTSUP;
}

if (setup->bmRequestType == GET_CLASS_REQUEST_TYPE) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is probably okay, but you only need to differentiate the request type class to the recipient interface and endpoint, otherwise the stack would not call this callback.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, Audio class does support interface and endpoint requests. Here, currently the absolute minimum of control requests are handled to satisfy the most basic example.

samples/subsys/usb/uac2_explicit_feedback/Kconfig Outdated Show resolved Hide resolved
Start of Frame is not relevant for most classes, but it is crucial for
isochronous operation. The most prominent example where SOF is necessary
is USB Audio class.

Signed-off-by: Tomasz Moń <tomasz.mon@nordicsemi.no>
jfischer-no
jfischer-no previously approved these changes Feb 1, 2024
Copy link
Collaborator

@jfischer-no jfischer-no left a comment

Choose a reason for hiding this comment

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

🔈

Introduce new USB Audio 2 implementation written from scratch. Main goal
behind new implementation was to perform entity configuration with
devicetree bindings, hiding the descriptor complexity from application.

Initial implementation is working at Full-Speed only. High-Speed support
will come later, but even at Full-Speed only this is viable replacement
for old stack USB Audio 1 class (USB Audio 1 is limited to Full-Speed by
specification, i.e. it is explicitly forbidden for USB Audio 1 device to
work at High-Speed).

Implemented is only absolute minimum set of features required for basic
implicit and explicit feedback samples. Only one sample frequency is
currently supported. Optional interrupt notifications are not supported.

Signed-off-by: Tomasz Moń <tomasz.mon@nordicsemi.no>
The sample illustrates how to calculate feedback value based on I2S
sample clock measurement or indirect I2S FRAMESTART and USBD SOF.
The sample is currently only supported on nrf5340dk_nrf5340_cpuapp
target because the feedback measurement uses target specific
peripherals.

While it should be possible to perform feedback value calculation
entirely in software (possibly with some additional filtering due
to software scheduling jitter), the I2S API does not provide necessary
timestamps.

Signed-off-by: Tomasz Moń <tomasz.mon@nordicsemi.no>
Copy link
Collaborator

@jfischer-no jfischer-no left a comment

Choose a reason for hiding this comment

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

🔈 🔈

@carlescufi carlescufi merged commit f00f846 into zephyrproject-rtos:main Feb 1, 2024
21 checks passed
@diegoherranz
Copy link
Contributor

Very nice to see this. Thanks for the work here!
Any plans to add MIDI support? Thanks!

@tmon-nordic
Copy link
Contributor Author

Very nice to see this. Thanks for the work here! Any plans to add MIDI support? Thanks!

MIDI is completely separate interface that is linked with USB Audio class only because of its original bundling with USB Audio 1. When USB Audio 2 devices implement MIDI, they really have separate interface (not bundled in the interface association descriptor with the USB Audio 2 interfaces) for the MIDIStreaming in USB Audio 1 interface. In fact, inside USB MIDI 2.0 Specification there is:

USB MIDI functionality resides at the interface level in the USB Device Framework hierarchy. The MIDI Streaming interface represents the entire functionality of the USB MIDI function. It is defined as a subclass of the Audio Interface Class. But this subclass assignment remains for legacy compatibility to the prior USB MIDI class only.

I currently do not plan on adding MIDI support myself as I have no use case for it. If someone comes up with the implementation, I can review it.

@diegoherranz
Copy link
Contributor

Fair enough. Thanks for the quick reply.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: Samples Samples area: USB Universal Serial Bus Experimental Experimental features not enabled by default
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants