Skip to content

Add UAC2 feature unit support #92855

Open
Open
@ddavidebor

Description

@ddavidebor

Problem Description

Currently UAC2 is missing feature unit support, necessary for basic functionality such as volume and mute. Most I2S codecs and amplifiers offer both mute and volume gain control through I2C, so it's quite important to implement these feature units.

Proposed Change (Summary)

The chance would expand uac2_ops with callbacks to handle CUR and RANGE requests.

the explicit feedback sample would be expanded to handle these requests for volume and mute.

A draft implementation is provided here https://github.com/Smartbox-Assistive-Technology/zephyr/pull/1/files

Proposed Change (Detailed)

Introduce the following new APIs:

/**
 * @brief USB Audio 2 Feature Unit event handlers
 */
struct uac2_feature_unit_ops {
	/**
	 * @brief Callback for host SET CUR requests on a Feature Unit.
	 * @param[in] buf Raw buffer from the host containing the new value.
	 * @param dev USB Audio 2 device
	 * @param entity_id Feature Unit ID
	 * @param control_selector Control selector (mute, volume, etc.)
	 * @param channel_num Channel number (0 = master, 1+ = individual channels)
	 * @param buf Buffer containing the new control value
	 * @param user_data Opaque user data pointer
	 * @return 0 on success, negative value on error
	 */
	int (*set_cur_cb)(const struct device *dev, uint8_t entity_id,
		      enum usb_audio_fucs control_selector, uint8_t channel_num,
		      const struct net_buf *buf, void *user_data);

	/**
	 * @brief Callback for host GET CUR requests on a Feature Unit.
	 * @param dev USB Audio 2 device
	 * @param entity_id Feature Unit ID
	 * @param control_selector Control selector (mute, volume, etc.)
	 * @param channel_num Channel number (0 = master, 1+ = individual channels)
	 * @param[out] value Pointer to be filled with the current control value.
	 * @param user_data Opaque user data pointer
	 * @return 0 on success, negative value on error
	 */
	int (*get_cur_cb)(const struct device *dev, uint8_t entity_id,
		      enum usb_audio_fucs control_selector, uint8_t channel_num,
		      uint32_t *value, void *user_data);

	/**
	 * @brief Callback for host GET RANGE requests on a Feature Unit.
	 * @param dev USB Audio 2 device
	 * @param entity_id Feature Unit ID
	 * @param control_selector Control selector (mute, volume, etc.)
	 * @param channel_num Channel number (0 = master, 1+ = individual channels)
	 * @param[out] range Pointer to the uac2_range struct to be filled.
	 * @param user_data Opaque user data pointer
	 * @return 0 on success, negative value on error
	 */
	int (*get_range_cb)(const struct device *dev, uint8_t entity_id,
		        enum usb_audio_fucs control_selector, uint8_t channel_num,
		        struct uac2_range *range, void *user_data);
};

Add a new enum, similar to what was present in usb_audio.h

 * Refer Table A-23 from UAC2 specification
 */
enum usb_audio_fucs {
	/* UAC1 and UAC2 controls */
	USB_AUDIO_FU_CONTROL_UNDEFINED			= 0x00,
	USB_AUDIO_FU_MUTE_CONTROL				= 0x01,
	USB_AUDIO_FU_VOLUME_CONTROL				= 0x02,
	USB_AUDIO_FU_BASS_CONTROL				= 0x03,
	USB_AUDIO_FU_MID_CONTROL				= 0x04,
	USB_AUDIO_FU_TREBLE_CONTROL				= 0x05,
	USB_AUDIO_FU_GRAPHIC_EQUALIZER_CONTROL	= 0x06,
	USB_AUDIO_FU_AUTOMATIC_GAIN_CONTROL		= 0x07,
	USB_AUDIO_FU_DELAY_CONTROL				= 0x08,
	USB_AUDIO_FU_BASS_BOOST_CONTROL			= 0x09,
	USB_AUDIO_FU_LOUDNESS_CONTROL			= 0x0A,
	/* UAC2-only controls */
	USB_AUDIO_FU_INPUT_GAIN_CONTROL			= 0x0B,
	USB_AUDIO_FU_INPUT_GAIN_PAD_CONTROL		= 0x0C,
	USB_AUDIO_FU_PHASE_INVERTER_CONTROL		= 0x0D,
	USB_AUDIO_FU_UNDERFLOW_CONTROL			= 0x0E,
	USB_AUDIO_FU_OVERFLOW_CONTROL			= 0x0F,
	USB_AUDIO_FU_LATENCY_CONTROL			= 0x10
};

Add a new struc for the feature units, and expand uac2_ops with feature_unit_ops

static const struct uac2_feature_unit_ops usb_audio_fu_ops = {
	.set_cur_cb = app_set_feature_unit_cb,
	.get_cur_cb = app_get_fu_cur_cb,
	.get_range_cb = app_get_fu_range_cb,
};

static struct uac2_ops usb_audio_ops = {
	.sof_cb = uac2_sof,
	.terminal_update_cb = uac2_terminal_update_cb,
	.get_recv_buf = uac2_get_recv_buf,
	.data_recv_cb = uac2_data_recv_cb,
	.buf_release_cb = uac2_buf_release_cb,
	.feedback_cb = uac2_feedback_cb,
	.feature_unit_ops = &usb_audio_fu_ops,
};

Update sample to include a sample implementation, and print some logs when the volume or mute status changes.

Dependencies

No response

Concerns and Unresolved Questions

No response

Alternatives Considered

I considered providing callbacks just for the volume and the mute, but that would introduce a bit too many callbacks for my taste, and also would need to be expanded extensively to handle all the other requests.

Metadata

Metadata

Assignees

No one assigned

    Labels

    RFCRequest For Comments: want input from the communityarea: Audioarea: USBUniversal Serial Bus

    Type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions