usb: Add USB audio implementation
By this commit USB audio class implementation is introduced to Zephyr. The Zephyr USB audio device class follows bellow documentations: - Universal Serial Bus specification rev2.0 (usb20.pdf) - Universal Serial Bus Device Class Definition for Audio Devices (audio10.pdf) - Universal Serial Bus Device Class Definition for Audio Data Formats (frmts10.pdf) - Universal Serial Bus Device Class Definition for Terminal Types (termt10.pdf) Signed-off-by: Emil Obalski <emil.obalski@nordicsemi.no>
This commit is contained in:
parent
38aea280e7
commit
8d2f13c203
8 changed files with 2250 additions and 4 deletions
114
dts/bindings/usb/usb-audio-hp.yaml
Normal file
114
dts/bindings/usb/usb-audio-hp.yaml
Normal file
|
@ -0,0 +1,114 @@
|
|||
# Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Specific fields for USB audio headphones.
|
||||
|
||||
description: USB Audio headphones specific fields.
|
||||
|
||||
compatible: "usb-audio-hp"
|
||||
|
||||
include: usb-audio.yaml
|
||||
|
||||
properties:
|
||||
resolution:
|
||||
type: int
|
||||
default: 16
|
||||
required: false
|
||||
enum:
|
||||
- 8
|
||||
- 16
|
||||
- 24
|
||||
- 32
|
||||
# channel configuration options
|
||||
channel-l:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (l) channel.
|
||||
channel-r:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (r) channel.
|
||||
channel-c:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (c) channel.
|
||||
channel-lfe:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (lfe) channel.
|
||||
channel-ls:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (ls) channel.
|
||||
channel-rs:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (rs) channel.
|
||||
channel-lc:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (lc) channel.
|
||||
channel-rc:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (rc) channel.
|
||||
channel-s:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (s) channel.
|
||||
channel-sl:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (sl) channel.
|
||||
channel-sr:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (sr) channel.
|
||||
channel-t:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (t) channel.
|
||||
channel-cfg:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (cfg) channel.
|
||||
# feature unit configuration options
|
||||
feature-mute:
|
||||
type: boolean
|
||||
required: true
|
||||
description: Enable Mute feature.
|
||||
feature-volume:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Volume feature.
|
||||
Currently not supported.
|
||||
feature-tone-control:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Tone Control (Bass, Mid, Treble) feature.
|
||||
Currently not supported.
|
||||
feature-graphic-equalizer:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Graphic Equalizer feature.
|
||||
Currently not supported.
|
||||
feature-automatic-gain-control:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Autoamtic Gain Control feature.
|
||||
Currently not supported.
|
||||
feature-delay:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Delay feature.
|
||||
Currently not supported.
|
||||
feature-bass-boost:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Bass Boost feature.
|
||||
Currently not supported.
|
||||
feature-loduness:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Loudness feature.
|
||||
Currently not supported.
|
228
dts/bindings/usb/usb-audio-hs.yaml
Normal file
228
dts/bindings/usb/usb-audio-hs.yaml
Normal file
|
@ -0,0 +1,228 @@
|
|||
# Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Specific fields for USB audio headset.
|
||||
|
||||
description: USB Audio headset specific fields.
|
||||
|
||||
compatible: "usb-audio-hs"
|
||||
|
||||
include: usb-audio.yaml
|
||||
|
||||
properties:
|
||||
mic-resolution:
|
||||
type: int
|
||||
default: 16
|
||||
required: false
|
||||
enum:
|
||||
- 8
|
||||
- 16
|
||||
- 24
|
||||
- 32
|
||||
mic-sync-type:
|
||||
required: false
|
||||
default: "Synchronous"
|
||||
type: string
|
||||
description: Type of endpoint synchronization for IN devices.
|
||||
Default value is Sychronous.
|
||||
Adaptive is not supported.
|
||||
enum:
|
||||
- "No Synchronization"
|
||||
- "Asynchronous"
|
||||
- "Adaptive"
|
||||
- "Synchronous"
|
||||
hp-resolution:
|
||||
type: int
|
||||
default: 16
|
||||
required: false
|
||||
enum:
|
||||
- 8
|
||||
- 16
|
||||
- 24
|
||||
- 32
|
||||
# microphone channel configuration options
|
||||
mic-channel-l:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (l) channel.
|
||||
mic-channel-r:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (r) channel.
|
||||
mic-channel-c:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (c) channel.
|
||||
mic-channel-lfe:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (lfe) channel.
|
||||
mic-channel-ls:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (ls) channel.
|
||||
mic-channel-rs:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (rs) channel.
|
||||
mic-channel-lc:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (lc) channel.
|
||||
mic-channel-rc:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (rc) channel.
|
||||
mic-channel-s:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (s) channel.
|
||||
mic-channel-sl:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (sl) channel.
|
||||
mic-channel-sr:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (sr) channel.
|
||||
mic-channel-t:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (t) channel.
|
||||
mic-channel-cfg:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (cfg) channel.
|
||||
# microphone feature unit configuration options
|
||||
mic-feature-mute:
|
||||
type: boolean
|
||||
required: true
|
||||
description: Enable Mute feature.
|
||||
mic-feature-volume:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Volume feature.
|
||||
Currently not supported.
|
||||
mic-feature-tone-control:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Tone Control (Bass, Mid, Treble) feature.
|
||||
Currently not supported.
|
||||
mic-feature-graphic-equalizer:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Graphic Equalizer feature.
|
||||
Currently not supported.
|
||||
mic-feature-automatic-gain-control:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Autoamtic Gain Control feature.
|
||||
Currently not supported.
|
||||
mic-feature-delay:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Delay feature.
|
||||
Currently not supported.
|
||||
mic-feature-bass-boost:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Bass Boost feature.
|
||||
Currently not supported.
|
||||
mic-mic-feature-loduness:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Loudness feature.
|
||||
Currently not supported.
|
||||
# headphones channel configuration options
|
||||
hp-channel-l:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (l) channel.
|
||||
hp-channel-r:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (r) channel.
|
||||
hp-channel-c:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (c) channel.
|
||||
hp-channel-lfe:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (lfe) channel.
|
||||
hp-channel-ls:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (ls) channel.
|
||||
hp-channel-rs:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (rs) channel.
|
||||
hp-channel-lc:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (lc) channel.
|
||||
hp-channel-rc:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (rc) channel.
|
||||
hp-channel-s:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (s) channel.
|
||||
hp-channel-sl:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (sl) channel.
|
||||
hp-channel-sr:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (sr) channel.
|
||||
hp-channel-t:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (t) channel.
|
||||
hp-channel-cfg:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (cfg) channel.
|
||||
# headphones feature unit configuration options
|
||||
hp-feature-mute:
|
||||
type: boolean
|
||||
required: true
|
||||
description: Enable Mute feature.
|
||||
hp-feature-volume:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Volume feature.
|
||||
Currently not supported.
|
||||
hp-feature-tone-control:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Tone Control (Bass, Mid, Treble) feature.
|
||||
Currently not supported.
|
||||
hp-feature-graphic-equalizer:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Graphic Equalizer feature.
|
||||
Currently not supported.
|
||||
hp-feature-automatic-gain-control:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Autoamtic Gain Control feature.
|
||||
Currently not supported.
|
||||
hp-feature-delay:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Delay feature.
|
||||
Currently not supported.
|
||||
hp-feature-bass-boost:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Bass Boost feature.
|
||||
Currently not supported.
|
||||
hp-feature-loduness:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Loudness feature.
|
||||
Currently not supported.
|
126
dts/bindings/usb/usb-audio-mic.yaml
Normal file
126
dts/bindings/usb/usb-audio-mic.yaml
Normal file
|
@ -0,0 +1,126 @@
|
|||
# Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Specific fields for USB audio microphone.
|
||||
|
||||
description: USB Audio microphone specific fields.
|
||||
|
||||
compatible: "usb-audio-mic"
|
||||
|
||||
include: usb-audio.yaml
|
||||
|
||||
properties:
|
||||
resolution:
|
||||
type: int
|
||||
default: 16
|
||||
required: false
|
||||
enum:
|
||||
- 8
|
||||
- 16
|
||||
- 24
|
||||
- 32
|
||||
sync-type:
|
||||
required: false
|
||||
default: "Synchronous"
|
||||
type: string
|
||||
description: Type of endpoint synchronization for IN devices.
|
||||
Default value is Sychronous.
|
||||
Adaptive is not supported.
|
||||
enum:
|
||||
- "No Synchronization"
|
||||
- "Asynchronous"
|
||||
- "Adaptive"
|
||||
- "Synchronous"
|
||||
# channel configuration options
|
||||
channel-l:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (l) channel.
|
||||
channel-r:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (r) channel.
|
||||
channel-c:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (c) channel.
|
||||
channel-lfe:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (lfe) channel.
|
||||
channel-ls:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (ls) channel.
|
||||
channel-rs:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (rs) channel.
|
||||
channel-lc:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (lc) channel.
|
||||
channel-rc:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (rc) channel.
|
||||
channel-s:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (s) channel.
|
||||
channel-sl:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (sl) channel.
|
||||
channel-sr:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (sr) channel.
|
||||
channel-t:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (t) channel.
|
||||
channel-cfg:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable (cfg) channel.
|
||||
# feature unit configuration options
|
||||
feature-mute:
|
||||
type: boolean
|
||||
required: true
|
||||
description: Enable Mute feature.
|
||||
feature-volume:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Volume feature.
|
||||
Currently not supported.
|
||||
feature-tone-control:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Tone Control (Bass, Mid, Treble) feature.
|
||||
Currently not supported.
|
||||
feature-graphic-equalizer:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Graphic Equalizer feature.
|
||||
Currently not supported.
|
||||
feature-automatic-gain-control:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Autoamtic Gain Control feature.
|
||||
Currently not supported.
|
||||
feature-delay:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Delay feature.
|
||||
Currently not supported.
|
||||
feature-bass-boost:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Bass Boost feature.
|
||||
Currently not supported.
|
||||
feature-loduness:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Enable Loudness feature.
|
||||
Currently not supported.
|
13
dts/bindings/usb/usb-audio.yaml
Normal file
13
dts/bindings/usb/usb-audio.yaml
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Common fields for USB audio class
|
||||
|
||||
description: Common fields for USB audio devices
|
||||
compatible: "usb-audio"
|
||||
|
||||
properties:
|
||||
label:
|
||||
required: true
|
||||
type: string
|
||||
description: Human readable string describing the device (used by Zephyr for API name)
|
|
@ -10,11 +10,277 @@
|
|||
* @file
|
||||
* @brief USB Audio Device Class public header
|
||||
*
|
||||
* Header follows Device Class Definition for Audio Class
|
||||
* Version 1.0 document (audio10.pdf).
|
||||
* Header follows below documentation:
|
||||
* - USB Device Class Definition for Audio Devices (audio10.pdf)
|
||||
*
|
||||
* Additional documentation considered a part of USB Audio v1.0:
|
||||
* - USB Device Class Definition for Audio Data Formats (frmts10.pdf)
|
||||
* - USB Device Class Definition for Terminal Types (termt10.pdf)
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_USB_CLASS_AUDIO_H_
|
||||
#define ZEPHYR_INCLUDE_USB_CLASS_AUDIO_H_
|
||||
|
||||
#include <usb/usb_common.h>
|
||||
#include <device.h>
|
||||
#include <net/buf.h>
|
||||
#include <sys/util.h>
|
||||
|
||||
/** Audio Interface Subclass Codes
|
||||
* Refer to Table A-2 from audio10.pdf
|
||||
*/
|
||||
enum usb_audio_int_subclass_codes {
|
||||
USB_AUDIO_SUBCLASS_UNDEFINED = 0x00,
|
||||
USB_AUDIO_AUDIOCONTROL = 0x01,
|
||||
USB_AUDIO_AUDIOSTREAMING = 0x02,
|
||||
USB_AUDIO_MIDISTREAMING = 0x03
|
||||
};
|
||||
|
||||
/** Audio Class-Specific AC Interface Descriptor Subtypes
|
||||
* Refer to Table A-5 from audio10.pdf
|
||||
*/
|
||||
enum usb_audio_cs_ac_int_desc_subtypes {
|
||||
USB_AUDIO_AC_DESCRIPTOR_UNDEFINED = 0x00,
|
||||
USB_AUDIO_HEADER = 0x01,
|
||||
USB_AUDIO_INPUT_TERMINAL = 0x02,
|
||||
USB_AUDIO_OUTPUT_TERMINAL = 0x03,
|
||||
USB_AUDIO_MIXER_UNIT = 0x04,
|
||||
USB_AUDIO_SELECTOR_UNIT = 0x05,
|
||||
USB_AUDIO_FEATURE_UNIT = 0x06,
|
||||
USB_AUDIO_PROCESSING_UNIT = 0x07,
|
||||
USB_AUDIO_EXTENSION_UNIT = 0x08
|
||||
};
|
||||
|
||||
/** Audio Class-Specific AS Interface Descriptor Subtypes
|
||||
* Refer to Table A-6 from audio10.pdf
|
||||
*/
|
||||
enum usb_audio_cs_as_int_desc_subtypes {
|
||||
USB_AUDIO_AS_DESCRIPTOR_UNDEFINED = 0x00,
|
||||
USB_AUDIO_AS_GENERAL = 0x01,
|
||||
USB_AUDIO_FORMAT_TYPE = 0x02,
|
||||
USB_AUDIO_FORMAT_SPECIFIC = 0x03
|
||||
};
|
||||
|
||||
/** Audio Class-Specific Request Codes
|
||||
* Refer to Table A-9 from audio10.pdf
|
||||
*/
|
||||
enum usb_audio_cs_req_codes {
|
||||
USB_AUDIO_REQUEST_CODE_UNDEFINED = 0x00,
|
||||
USB_AUDIO_SET_CUR = 0x01,
|
||||
USB_AUDIO_GET_CUR = 0x81,
|
||||
USB_AUDIO_SET_MIN = 0x02,
|
||||
USB_AUDIO_GET_MIN = 0x82,
|
||||
USB_AUDIO_SET_MAX = 0x03,
|
||||
USB_AUDIO_GET_MAX = 0x83,
|
||||
USB_AUDIO_SET_RES = 0x04,
|
||||
USB_AUDIO_GET_RES = 0x84,
|
||||
USB_AUDIO_SET_MEM = 0x05,
|
||||
USB_AUDIO_GET_MEM = 0x85,
|
||||
USB_AUDIO_GET_STAT = 0xFF
|
||||
};
|
||||
|
||||
/** Feature Unit Control Selectors
|
||||
* Refer to Table A-11 from audio10.pdf
|
||||
*/
|
||||
enum usb_audio_fucs {
|
||||
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
|
||||
};
|
||||
|
||||
/** USB Terminal Types
|
||||
* Refer to Table 2-1 - Table 2-4 from termt10.pdf
|
||||
*/
|
||||
enum usb_audio_terminal_types {
|
||||
/* USB Terminal Types */
|
||||
USB_AUDIO_USB_UNDEFINED = 0x0100,
|
||||
USB_AUDIO_USB_STREAMING = 0x0101,
|
||||
USB_AUDIO_USB_VENDOR_SPEC = 0x01FF,
|
||||
|
||||
/* Input Terminal Types */
|
||||
USB_AUDIO_IN_UNDEFINED = 0x0200,
|
||||
USB_AUDIO_IN_MICROPHONE = 0x0201,
|
||||
USB_AUDIO_IN_DESKTOP_MIC = 0x0202,
|
||||
USB_AUDIO_IN_PERSONAL_MIC = 0x0203,
|
||||
USB_AUDIO_IN_OM_DIR_MIC = 0x0204,
|
||||
USB_AUDIO_IN_MIC_ARRAY = 0x0205,
|
||||
USB_AUDIO_IN_PROC_MIC_ARRAY = 0x0205,
|
||||
|
||||
/* Output Terminal Types */
|
||||
USB_AUDIO_OUT_UNDEFINED = 0x0300,
|
||||
USB_AUDIO_OUT_SPEAKER = 0x0301,
|
||||
USB_AUDIO_OUT_HEADPHONES = 0x0302,
|
||||
USB_AUDIO_OUT_HEAD_AUDIO = 0x0303,
|
||||
USB_AUDIO_OUT_DESKTOP_SPEAKER = 0x0304,
|
||||
USB_AUDIO_OUT_ROOM_SPEAKER = 0x0305,
|
||||
USB_AUDIO_OUT_COMM_SPEAKER = 0x0306,
|
||||
USB_AUDIO_OUT_LOW_FREQ_SPEAKER = 0x0307,
|
||||
|
||||
/* Bi-directional Terminal Types */
|
||||
USB_AUDIO_IO_UNDEFINED = 0x0400,
|
||||
USB_AUDIO_IO_HANDSET = 0x0401,
|
||||
USB_AUDIO_IO_HEADSET = 0x0402,
|
||||
USB_AUDIO_IO_SPEAKERPHONE_ECHO_NONE = 0x0403,
|
||||
USB_AUDIO_IO_SPEAKERPHONE_ECHO_SUP = 0x0404,
|
||||
USB_AUDIO_IO_SPEAKERPHONE_ECHO_CAN = 0x0405,
|
||||
};
|
||||
|
||||
enum usb_audio_direction {
|
||||
USB_AUDIO_IN = 0x00,
|
||||
USB_AUDIO_OUT = 0x01
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Feature Unit event structure.
|
||||
*
|
||||
* The event structure is used by feature_update_cb in order to inform the App
|
||||
* whenever the Host has modified one of the device features.
|
||||
*
|
||||
* @param dir The device direction that has been changed. Applicable for
|
||||
* Headset device only.
|
||||
* @param cs Control selector, feature that has been changed.
|
||||
* @param channel Device channel that has been changed. If 0xFF, then
|
||||
* all channels have been changed.
|
||||
* @param val_len Length of the val field.
|
||||
* @param val Value of the feature that has been set.
|
||||
*/
|
||||
struct usb_audio_fu_evt {
|
||||
enum usb_audio_direction dir;
|
||||
enum usb_audio_fucs cs;
|
||||
u8_t channel;
|
||||
u8_t val_len;
|
||||
const void *val;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Callback type used to inform the app that data were requested
|
||||
* from the device and may be send to the Host.
|
||||
* For sending the data usb_audio_send() API function should be used.
|
||||
*
|
||||
* @note User may not use this callback and may try to send in 1ms task
|
||||
* instead. Sending every 1ms may be unsuccessful and may return -EAGAIN
|
||||
* if Host did not required data.
|
||||
*
|
||||
* @param dev The device for which data were requested by the Host.
|
||||
*/
|
||||
typedef void (*usb_audio_data_request_cb_t)(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief Callback type used to inform the app that data were successfully
|
||||
* send/received.
|
||||
*
|
||||
* @param dev The device for which the callback was called.
|
||||
* @param buffer Pointer to the net_buf data chunk that was successfully
|
||||
* send/received. If the application uses data_written_cb and/or
|
||||
* data_received_cb callbacks it is responsible for freeing the
|
||||
* buffer by itself.
|
||||
* @param size Amount of data that were successfully send/received.
|
||||
*/
|
||||
typedef void (*usb_audio_data_completion_cb_t)(const struct device *dev,
|
||||
struct net_buf *buffer,
|
||||
size_t size);
|
||||
|
||||
/**
|
||||
* @brief Callback type used to inform the app that Host has changed
|
||||
* one of the features configured for the device.
|
||||
* Applicable for all devices.
|
||||
*
|
||||
* @warning Host may not use all of configured features.
|
||||
*
|
||||
* @param evt Pointer to an event to be parsed by the App.
|
||||
* Pointer sturct is temporary and is valid only during the
|
||||
* execution of this callback.
|
||||
*/
|
||||
typedef void (*usb_audio_feature_updated_cb_t)(struct device *dev,
|
||||
const struct usb_audio_fu_evt *evt);
|
||||
|
||||
/**
|
||||
* @brief Audio callbacks used to interact with audio devices by user App.
|
||||
*
|
||||
* usb_audio_ops structure contains all relevant callbacks to interact with
|
||||
* USB Audio devices. Each of this callbacks is optional and may be left NULL.
|
||||
* This will not break the stack but could make USB Audio device useless.
|
||||
* Depending on the device some of those callbacks are necessary to make USB
|
||||
* device work as expected sending/receiving data. For more information refer
|
||||
* to callback documentation above.
|
||||
*/
|
||||
struct usb_audio_ops {
|
||||
/* Callback called when data could be send */
|
||||
usb_audio_data_request_cb_t data_request_cb;
|
||||
|
||||
/* Callback called when data were successfully written with sending
|
||||
* capable device. Applicable for headset and microphone. Unused for
|
||||
* headphones.
|
||||
*/
|
||||
usb_audio_data_completion_cb_t data_written_cb;
|
||||
|
||||
/* Callback called when data were successfully received by receive
|
||||
* capable device. Applicable for headset and headphones. Unused for
|
||||
* microphone.
|
||||
*/
|
||||
usb_audio_data_completion_cb_t data_received_cb;
|
||||
|
||||
/* Callback called when features were modified by the Host */
|
||||
usb_audio_feature_updated_cb_t feature_update_cb;
|
||||
};
|
||||
|
||||
/** @brief Get the frame size that is accepted by the Host.
|
||||
*
|
||||
* This function returns the frame size for Input Devices that is expected
|
||||
* by the Host. Returned value rely on Input Device configuration:
|
||||
* - number of channels
|
||||
* - sampling frequency
|
||||
* - sample resolution
|
||||
* Device configuration is done via DT overlay.
|
||||
*
|
||||
* @param dev The Input device that is asked for frame size.
|
||||
*
|
||||
* @warning Do not use with OUT only devices (Headphones).
|
||||
* For OUT only devices this function shall return 0.
|
||||
*/
|
||||
size_t usb_audio_get_in_frame_size(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief Register the USB Audio device and make it useable.
|
||||
* This must be called in order to make the device work
|
||||
* and respond to all relevant requests.
|
||||
*
|
||||
* @param dev USB Audio device
|
||||
* @param ops USB audio callback structure. Callback are used to
|
||||
* inform the user about what is happening
|
||||
*/
|
||||
void usb_audio_register(struct device *dev,
|
||||
const struct usb_audio_ops *ops);
|
||||
|
||||
/**
|
||||
* @brief Send data using USB Audio device
|
||||
*
|
||||
* @param dev USB Audio device which will send the data
|
||||
* over its ISO IN endpoint
|
||||
* @param buffer Pointer to the buffer that should be send. User is
|
||||
* responsible for managing the buffer for Input devices. In case
|
||||
* of sending error user must decide if the buffer should be
|
||||
* dropped or retransmitted.
|
||||
* Afther the buffer was sent successfully it is passed to the
|
||||
* data_written_cb callback if the application uses one or
|
||||
* automatically freed otherwse.
|
||||
* User must provide proper net_buf chunk especially when
|
||||
* it comes to its size. This information can be obtained
|
||||
* using usb_audio_get_in_frame_size() API function.
|
||||
*
|
||||
* @param len Length of the data to be send
|
||||
*
|
||||
* @return 0 on success, negative error on fail
|
||||
*/
|
||||
int usb_audio_send(const struct device *dev, struct net_buf *buffer,
|
||||
size_t len);
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_USB_CLASS_AUDIO_H_ */
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
# USB AUDIO configuration options
|
||||
|
||||
# Copyright (c) 2019 Nordic Semiconductor ASA
|
||||
# Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config USB_DEVICE_AUDIO
|
||||
bool "USB Audio Device Class Driver"
|
||||
help
|
||||
USB audio device class driver
|
||||
USB Audio Device Class driver.
|
||||
Zephyr USB Audio Class is considered experimental
|
||||
and not full.
|
||||
Device configuration is done via dt overlay.
|
||||
|
||||
if USB_DEVICE_AUDIO
|
||||
|
||||
module = USB_AUDIO
|
||||
module-str = USB Audio
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
|
||||
endif #USB_DEVICE_AUDIO
|
||||
|
|
|
@ -10,3 +10,970 @@
|
|||
*
|
||||
* Driver for USB Audio device class driver
|
||||
*/
|
||||
|
||||
#include <kernel.h>
|
||||
#include <usb/usb_common.h>
|
||||
#include <usb/usb_device.h>
|
||||
#include <usb_descriptor.h>
|
||||
#include <usb/usbstruct.h>
|
||||
#include <usb/class/usb_audio.h>
|
||||
#include "usb_audio_internal.h"
|
||||
|
||||
#include <sys/byteorder.h>
|
||||
#include <sys/util.h>
|
||||
#include <net/buf.h>
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(usb_audio, CONFIG_USB_AUDIO_LOG_LEVEL);
|
||||
|
||||
/* Device data structure */
|
||||
struct usb_audio_dev_data {
|
||||
const struct usb_audio_ops *ops;
|
||||
|
||||
u8_t *controls[2];
|
||||
|
||||
u8_t ch_cnt[2];
|
||||
|
||||
const struct cs_ac_if_descriptor *desc_hdr;
|
||||
|
||||
struct usb_dev_data common;
|
||||
|
||||
struct net_buf_pool *pool;
|
||||
|
||||
/* Not applicable for Headphones, left with 0 */
|
||||
u16_t in_frame_size;
|
||||
|
||||
bool rx_enable;
|
||||
bool tx_enable;
|
||||
};
|
||||
|
||||
static sys_slist_t usb_audio_data_devlist;
|
||||
|
||||
/**
|
||||
* @brief Fill the USB Audio descriptor
|
||||
*
|
||||
* This macro fills USB descriptor for specific type of device
|
||||
* (Heahphones or Microphone) depending on dev param.
|
||||
*
|
||||
* @note Feature unit has variable length and only 1st field of
|
||||
* .bmaControls is filled. Later its fixed in usb_fix_descriptor()
|
||||
* @note Audio control and Audio streaming interfaces are numerated starting
|
||||
* from 0 and are later fixed in usb_fix_descriptor()
|
||||
*
|
||||
* @param [in] dev Device type. Must be HP/MIC
|
||||
* @param [in] i Instance of device of current type (dev)
|
||||
* @param [in] id Param for counting logic entities
|
||||
* @param [in] link ID of IN/OUT terminal to which General Descriptor
|
||||
* is linked.
|
||||
* @param [in] it_type Input terminal type
|
||||
* @param [in] ot_type Output terminal type
|
||||
*/
|
||||
#define DEFINE_AUDIO_DESCRIPTOR(dev, i, id, link, it_type, ot_type, cb, addr) \
|
||||
USBD_CLASS_DESCR_DEFINE(primary, audio) \
|
||||
struct dev##_descriptor_##i dev##_desc_##i = { \
|
||||
USB_AUDIO_IAD(2) \
|
||||
.std_ac_interface = INIT_STD_IF(USB_AUDIO_AUDIOCONTROL, 0, 0, 0), \
|
||||
.cs_ac_interface = INIT_CS_AC_IF(dev, i, 1), \
|
||||
.input_terminal = INIT_IN_TERMINAL(dev, i, id, it_type), \
|
||||
.feature_unit = INIT_FEATURE_UNIT(dev, i, id + 1, id), \
|
||||
.output_terminal = INIT_OUT_TERMINAL(id + 2, id + 1, ot_type), \
|
||||
.as_interface_alt_0 = INIT_STD_IF(USB_AUDIO_AUDIOSTREAMING, 1, 0, 0), \
|
||||
.as_interface_alt_1 = INIT_STD_IF(USB_AUDIO_AUDIOSTREAMING, 1, 1, 1), \
|
||||
.as_cs_interface = INIT_AS_GENERAL(link), \
|
||||
.format = INIT_AS_FORMAT_I(CH_CNT(dev, i), GET_RES(dev, i)), \
|
||||
.std_ep = INIT_STD_AS_AD_EP(dev, i, addr), \
|
||||
.cs_ep = INIT_CS_AS_AD_EP, \
|
||||
}; \
|
||||
static struct usb_ep_cfg_data dev##_usb_audio_ep_data_##i[] = { \
|
||||
INIT_EP_DATA(cb, addr), \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fill the USB Audio descriptor
|
||||
*
|
||||
* This macro fills USB descriptor for specific type of device.
|
||||
* Macro is used when the device uses 2 audiostreaming interfaces,
|
||||
* eg. Headset
|
||||
*
|
||||
* @note Feature units have variable length and only 1st field of
|
||||
* .bmaControls is filled. Its fixed in usb_fix_descriptor()
|
||||
* @note Audio control and Audio streaming interfaces are numerated starting
|
||||
* from 0 and are later fixed in usb_fix_descriptor()
|
||||
*
|
||||
* @param [in] dev Device type.
|
||||
* @param [in] i Instance of device of current type (dev)
|
||||
* @param [in] id Param for counting logic entities
|
||||
*/
|
||||
#define DEFINE_AUDIO_DESCRIPTOR_BIDIR(dev, i, id) \
|
||||
USBD_CLASS_DESCR_DEFINE(primary, audio) \
|
||||
struct dev##_descriptor_##i dev##_desc_##i = { \
|
||||
USB_AUDIO_IAD(3) \
|
||||
.std_ac_interface = INIT_STD_IF(USB_AUDIO_AUDIOCONTROL, 0, 0, 0), \
|
||||
.cs_ac_interface = INIT_CS_AC_IF_BIDIR(dev, i, 2), \
|
||||
.input_terminal_0 = INIT_IN_TERMINAL(dev##_MIC, i, id, \
|
||||
USB_AUDIO_IO_HEADSET), \
|
||||
.feature_unit_0 = INIT_FEATURE_UNIT(dev##_MIC, i, id+1, id), \
|
||||
.output_terminal_0 = INIT_OUT_TERMINAL(id+2, id+1, \
|
||||
USB_AUDIO_USB_STREAMING), \
|
||||
.input_terminal_1 = INIT_IN_TERMINAL(dev##_HP, i, id+3, \
|
||||
USB_AUDIO_USB_STREAMING), \
|
||||
.feature_unit_1 = INIT_FEATURE_UNIT(dev##_HP, i, id+4, id+3), \
|
||||
.output_terminal_1 = INIT_OUT_TERMINAL(id+5, id+4, \
|
||||
USB_AUDIO_IO_HEADSET), \
|
||||
.as_interface_alt_0_0 = INIT_STD_IF(USB_AUDIO_AUDIOSTREAMING, \
|
||||
1, 0, 0), \
|
||||
.as_interface_alt_0_1 = INIT_STD_IF(USB_AUDIO_AUDIOSTREAMING, \
|
||||
1, 1, 1), \
|
||||
.as_cs_interface_0 = INIT_AS_GENERAL(id+2), \
|
||||
.format_0 = INIT_AS_FORMAT_I(CH_CNT(dev##_MIC, i), \
|
||||
GET_RES(dev##_MIC, i)), \
|
||||
.std_ep_0 = INIT_STD_AS_AD_EP(dev##_MIC, i, \
|
||||
AUTO_EP_IN), \
|
||||
.cs_ep_0 = INIT_CS_AS_AD_EP, \
|
||||
.as_interface_alt_1_0 = INIT_STD_IF(USB_AUDIO_AUDIOSTREAMING, \
|
||||
2, 0, 0), \
|
||||
.as_interface_alt_1_1 = INIT_STD_IF(USB_AUDIO_AUDIOSTREAMING, \
|
||||
2, 1, 1), \
|
||||
.as_cs_interface_1 = INIT_AS_GENERAL(id+3), \
|
||||
.format_1 = INIT_AS_FORMAT_I(CH_CNT(dev##_HP, i), \
|
||||
GET_RES(dev##_HP, i)), \
|
||||
.std_ep_1 = INIT_STD_AS_AD_EP(dev##_HP, i, \
|
||||
AUTO_EP_OUT), \
|
||||
.cs_ep_1 = INIT_CS_AS_AD_EP, \
|
||||
}; \
|
||||
static struct usb_ep_cfg_data dev##_usb_audio_ep_data_##i[] = { \
|
||||
INIT_EP_DATA(usb_transfer_ep_callback, AUTO_EP_IN), \
|
||||
INIT_EP_DATA(audio_receive_cb, AUTO_EP_OUT), \
|
||||
}
|
||||
|
||||
#define DEFINE_AUDIO_DEV_DATA(dev, i, __out_pool, __in_pool_size) \
|
||||
static u8_t dev##_controls_##i[FEATURES_SIZE(dev, i)] = {0};\
|
||||
static struct usb_audio_dev_data dev##_audio_dev_data_##i = \
|
||||
{ .pool = __out_pool, \
|
||||
.in_frame_size = __in_pool_size, \
|
||||
.controls = {dev##_controls_##i, NULL}, \
|
||||
.ch_cnt = {(CH_CNT(dev, i) + 1), 0} \
|
||||
}
|
||||
|
||||
#define DEFINE_AUDIO_DEV_DATA_BIDIR(dev, i, __out_pool, __in_pool_size) \
|
||||
static u8_t dev##_controls0_##i[FEATURES_SIZE(dev##_MIC, i)] = {0};\
|
||||
static u8_t dev##_controls1_##i[FEATURES_SIZE(dev##_HP, i)] = {0}; \
|
||||
static struct usb_audio_dev_data dev##_audio_dev_data_##i = \
|
||||
{ .pool = __out_pool, \
|
||||
.in_frame_size = __in_pool_size, \
|
||||
.controls = {dev##_controls0_##i, dev##_controls1_##i}, \
|
||||
.ch_cnt = {(CH_CNT(dev##_MIC, i) + 1), \
|
||||
(CH_CNT(dev##_HP, i) + 1)} \
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for getting channel number directly from the
|
||||
* feature unit descriptor.
|
||||
*/
|
||||
static u8_t get_num_of_channels(const struct feature_unit_descriptor *fu)
|
||||
{
|
||||
return (fu->bLength - FU_FIXED_ELEMS_SIZE)/sizeof(u16_t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for getting supported controls directly from
|
||||
* the feature unit descriptor.
|
||||
*/
|
||||
static u16_t get_controls(const struct feature_unit_descriptor *fu)
|
||||
{
|
||||
return *(u16_t *)((u8_t *)fu + BMA_CONTROLS_OFFSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for getting the device streaming direction
|
||||
*/
|
||||
static enum usb_audio_direction get_fu_dir(
|
||||
const struct feature_unit_descriptor *fu)
|
||||
{
|
||||
const struct output_terminal_descriptor *ot =
|
||||
(struct output_terminal_descriptor *)
|
||||
((u8_t *)fu + fu->bLength);
|
||||
enum usb_audio_direction dir;
|
||||
|
||||
if (ot->wTerminalType == USB_AUDIO_USB_STREAMING) {
|
||||
dir = USB_AUDIO_IN;
|
||||
} else {
|
||||
dir = USB_AUDIO_OUT;
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for fixing controls in feature units descriptors.
|
||||
*/
|
||||
static void fix_fu_descriptors(struct usb_if_descriptor *iface)
|
||||
{
|
||||
struct cs_ac_if_descriptor *header;
|
||||
struct feature_unit_descriptor *fu;
|
||||
|
||||
header = (struct cs_ac_if_descriptor *)
|
||||
((u8_t *)iface + USB_PASSIVE_IF_DESC_SIZE);
|
||||
|
||||
fu = (struct feature_unit_descriptor *)((u8_t *)header +
|
||||
header->bLength +
|
||||
INPUT_TERMINAL_DESC_SIZE);
|
||||
|
||||
/* start from 1 as elem 0 is filled when descriptor is declared */
|
||||
for (int i = 1; i < get_num_of_channels(fu); i++) {
|
||||
*(fu->bmaControls + i) = fu->bmaControls[0];
|
||||
}
|
||||
|
||||
if (header->bInCollection == 2) {
|
||||
fu = (struct feature_unit_descriptor *)((u8_t *)fu +
|
||||
fu->bLength +
|
||||
INPUT_TERMINAL_DESC_SIZE +
|
||||
OUTPUT_TERMINAL_DESC_SIZE);
|
||||
for (int i = 1; i < get_num_of_channels(fu); i++) {
|
||||
*(fu->bmaControls + i) = fu->bmaControls[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for getting pointer to feature unit descriptor.
|
||||
* This is needed in order to address audio specific requests to proper
|
||||
* controls struct.
|
||||
*/
|
||||
static struct feature_unit_descriptor *get_feature_unit(
|
||||
struct usb_audio_dev_data *audio_dev_data,
|
||||
u8_t *device, u8_t fu_id)
|
||||
{
|
||||
struct feature_unit_descriptor *fu;
|
||||
|
||||
fu = (struct feature_unit_descriptor *)
|
||||
((u8_t *)audio_dev_data->desc_hdr +
|
||||
audio_dev_data->desc_hdr->bLength +
|
||||
INPUT_TERMINAL_DESC_SIZE);
|
||||
|
||||
if (fu->bUnitID == fu_id) {
|
||||
*device = 0;
|
||||
return fu;
|
||||
}
|
||||
/* skip to the next Feature Unit */
|
||||
fu = (struct feature_unit_descriptor *)
|
||||
((u8_t *)fu + fu->bLength +
|
||||
INPUT_TERMINAL_DESC_SIZE +
|
||||
OUTPUT_TERMINAL_DESC_SIZE);
|
||||
*device = 1;
|
||||
|
||||
return fu;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This is a helper function user to inform the user about
|
||||
* possibility to write the data to the device.
|
||||
*/
|
||||
static void audio_dc_sof(struct usb_cfg_data *cfg,
|
||||
struct usb_audio_dev_data *dev_data)
|
||||
{
|
||||
u8_t ep_addr;
|
||||
|
||||
/* In endpoint always at index 0 */
|
||||
ep_addr = cfg->endpoint[0].ep_addr;
|
||||
if ((ep_addr & USB_EP_DIR_MASK) && (dev_data->tx_enable)) {
|
||||
if (dev_data->ops && dev_data->ops->data_request_cb) {
|
||||
dev_data->ops->data_request_cb(
|
||||
dev_data->common.dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void audio_interface_config(struct usb_desc_header *head,
|
||||
u8_t bInterfaceNumber)
|
||||
{
|
||||
struct usb_if_descriptor *iface = (struct usb_if_descriptor *)head;
|
||||
struct cs_ac_if_descriptor *header;
|
||||
|
||||
#ifdef CONFIG_USB_COMPOSITE_DEVICE
|
||||
struct usb_association_descriptor *iad =
|
||||
(struct usb_association_descriptor *)
|
||||
((char *)iface - sizeof(struct usb_association_descriptor));
|
||||
iad->bFirstInterface = bInterfaceNumber;
|
||||
#endif
|
||||
fix_fu_descriptors(iface);
|
||||
|
||||
/* Audio Control Interface */
|
||||
iface->bInterfaceNumber = bInterfaceNumber;
|
||||
header = (struct cs_ac_if_descriptor *)
|
||||
((u8_t *)iface + iface->bLength);
|
||||
header->baInterfaceNr[0] = bInterfaceNumber + 1;
|
||||
|
||||
/* Audio Streaming Interface Passive */
|
||||
iface = (struct usb_if_descriptor *)
|
||||
((u8_t *)header + header->wTotalLength);
|
||||
iface->bInterfaceNumber = bInterfaceNumber + 1;
|
||||
|
||||
/* Audio Streaming Interface Active */
|
||||
iface = (struct usb_if_descriptor *)
|
||||
((u8_t *)iface + iface->bLength);
|
||||
iface->bInterfaceNumber = bInterfaceNumber + 1;
|
||||
|
||||
if (header->bInCollection == 2) {
|
||||
header->baInterfaceNr[1] = bInterfaceNumber + 2;
|
||||
/* Audio Streaming Interface Passive */
|
||||
iface = (struct usb_if_descriptor *)
|
||||
((u8_t *)iface + USB_ACTIVE_IF_DESC_SIZE);
|
||||
iface->bInterfaceNumber = bInterfaceNumber + 2;
|
||||
|
||||
/* Audio Streaming Interface Active */
|
||||
iface = (struct usb_if_descriptor *)
|
||||
((u8_t *)iface + USB_PASSIVE_IF_DESC_SIZE);
|
||||
iface->bInterfaceNumber = bInterfaceNumber + 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void audio_cb_usb_status(struct usb_cfg_data *cfg,
|
||||
enum usb_dc_status_code cb_status,
|
||||
const u8_t *param)
|
||||
{
|
||||
struct usb_audio_dev_data *audio_dev_data;
|
||||
struct usb_dev_data *dev_data;
|
||||
|
||||
dev_data = usb_get_dev_data_by_cfg(&usb_audio_data_devlist, cfg);
|
||||
|
||||
if (dev_data == NULL) {
|
||||
LOG_ERR("Device data not found for cfg %p", cfg);
|
||||
return;
|
||||
}
|
||||
|
||||
audio_dev_data = CONTAINER_OF(dev_data, struct usb_audio_dev_data,
|
||||
common);
|
||||
|
||||
switch (cb_status) {
|
||||
case USB_DC_SOF:
|
||||
audio_dc_sof(cfg, audio_dev_data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper funciton for checking if particular entity is a part of
|
||||
* the audio device.
|
||||
*
|
||||
* This function checks if given entity is a part of given audio device.
|
||||
* If so then true is returned and audio_dev_data is considered correct device
|
||||
* data.
|
||||
*
|
||||
* @note For now this function searches through feature units only. The
|
||||
* descriptors are known and are not using any other entity type.
|
||||
* If there is a need to add other units to audio function then this
|
||||
* must be reworked.
|
||||
*
|
||||
* @param [in] audio_dev_data USB audio device data.
|
||||
* @param [in, out] entity USB Audio entity.
|
||||
* .id [in] id of searched entity
|
||||
* .subtype [out] subtype of entity (if found)
|
||||
*
|
||||
* @return true if entity matched audio_dev_data, false otherwise.
|
||||
*/
|
||||
static bool is_entity_valid(struct usb_audio_dev_data *audio_dev_data,
|
||||
struct usb_audio_entity *entity)
|
||||
{
|
||||
const struct cs_ac_if_descriptor *header;
|
||||
const struct feature_unit_descriptor *fu;
|
||||
|
||||
header = audio_dev_data->desc_hdr;
|
||||
fu = (struct feature_unit_descriptor *)((u8_t *)header +
|
||||
header->bLength +
|
||||
INPUT_TERMINAL_DESC_SIZE);
|
||||
if (fu->bUnitID == entity->id) {
|
||||
entity->subtype = fu->bDescriptorSubtype;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (header->bInCollection == 2) {
|
||||
fu = (struct feature_unit_descriptor *)((u8_t *)fu +
|
||||
fu->bLength +
|
||||
INPUT_TERMINAL_DESC_SIZE +
|
||||
OUTPUT_TERMINAL_DESC_SIZE);
|
||||
if (fu->bUnitID == entity->id) {
|
||||
entity->subtype = fu->bDescriptorSubtype;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper funciton for getting the audio_dev_data by the entity number.
|
||||
*
|
||||
* This function searches through all audio devices the one with given
|
||||
* entity number and return the audio_dev_data structure for this entity.
|
||||
*
|
||||
* @param [in, out] entity USB Audio entity addressed by the request.
|
||||
* .id [in] id of searched entity
|
||||
* .subtype [out] subtype of entity (if found)
|
||||
*
|
||||
* @return audio_dev_data for given entity, NULL if not found.
|
||||
*/
|
||||
static struct usb_audio_dev_data *get_audio_dev_data_by_entity(
|
||||
struct usb_audio_entity *entity)
|
||||
{
|
||||
struct usb_dev_data *dev_data;
|
||||
struct usb_audio_dev_data *audio_dev_data;
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&usb_audio_data_devlist, dev_data, node) {
|
||||
audio_dev_data = CONTAINER_OF(dev_data,
|
||||
struct usb_audio_dev_data,
|
||||
common);
|
||||
|
||||
if (is_entity_valid(audio_dev_data, entity)) {
|
||||
return audio_dev_data;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper funciton for checking if particular interface is a part of
|
||||
* the audio device.
|
||||
*
|
||||
* This function checks if given interface is a part of given audio device.
|
||||
* If so then true is returned and audio_dev_data is considered correct device
|
||||
* data.
|
||||
*
|
||||
* @param [in] audio_dev_data USB audio device data.
|
||||
* @param [in] interface USB Audio interface number.
|
||||
*
|
||||
* @return true if interface matched audio_dev_data, false otherwise.
|
||||
*/
|
||||
static bool is_interface_valid(struct usb_audio_dev_data *audio_dev_data,
|
||||
u8_t interface)
|
||||
{
|
||||
const struct cs_ac_if_descriptor *header;
|
||||
|
||||
header = audio_dev_data->desc_hdr;
|
||||
u8_t desc_iface = 0;
|
||||
|
||||
for (size_t i = 0; i < header->bInCollection; i++) {
|
||||
desc_iface = header->baInterfaceNr[i];
|
||||
if (desc_iface == interface) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper funciton for getting the audio_dev_data by the interface
|
||||
* number.
|
||||
*
|
||||
* This function searches through all audio devices the one with given
|
||||
* interface number and returns the audio_dev_data structure for this device.
|
||||
*
|
||||
* @param [in] interface USB Audio interface addressed by the request.
|
||||
*
|
||||
* @return audio_dev_data for given interface, NULL if not found.
|
||||
*/
|
||||
static struct usb_audio_dev_data *get_audio_dev_data_by_iface(u8_t interface)
|
||||
{
|
||||
struct usb_dev_data *dev_data;
|
||||
struct usb_audio_dev_data *audio_dev_data;
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&usb_audio_data_devlist, dev_data, node) {
|
||||
audio_dev_data = CONTAINER_OF(dev_data,
|
||||
struct usb_audio_dev_data,
|
||||
common);
|
||||
|
||||
if (is_interface_valid(audio_dev_data, interface)) {
|
||||
return audio_dev_data;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handler for feature unit mute control requests.
|
||||
*
|
||||
* This function handles feature unit mute control request.
|
||||
*
|
||||
* @param audio_dev_data USB audio device data.
|
||||
* @param pSetup Information about the executed request.
|
||||
* @param len Size of the buffer.
|
||||
* @param data Buffer containing the request result.
|
||||
* @param evt Feature Unit Event info.
|
||||
* @param device Device part that has been addressed. Applicable for
|
||||
* bidirectional device.
|
||||
*
|
||||
* @return 0 if succesfulf, negative errno otherwise.
|
||||
*/
|
||||
static int handle_fu_mute_req(struct usb_audio_dev_data *audio_dev_data,
|
||||
struct usb_setup_packet *pSetup,
|
||||
s32_t *len, u8_t **data,
|
||||
struct usb_audio_fu_evt *evt,
|
||||
u8_t device)
|
||||
{
|
||||
u8_t ch = (pSetup->wValue) & 0xFF;
|
||||
u8_t ch_cnt = audio_dev_data->ch_cnt[device];
|
||||
u8_t *controls = audio_dev_data->controls[device];
|
||||
u8_t *control_val = &controls[POS(MUTE, ch, ch_cnt)];
|
||||
|
||||
/* Check if *len has valid value */
|
||||
if (*len != LEN(1, MUTE)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (pSetup->bRequest) {
|
||||
case USB_AUDIO_SET_CUR:
|
||||
evt->val = control_val;
|
||||
evt->val_len = *len;
|
||||
memcpy(control_val, *data, *len);
|
||||
return 0;
|
||||
case USB_AUDIO_GET_CUR:
|
||||
*data = control_val;
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handler for feature unit requests.
|
||||
*
|
||||
* This function handles feature unit specific requests.
|
||||
* If request is properly served 0 is returned. Negative errno
|
||||
* is returned in case of an error. This leads to setting stall on IN EP0.
|
||||
*
|
||||
* @param audio_dev_data USB audio device data.
|
||||
* @param pSetup Information about the executed request.
|
||||
* @param len Size of the buffer.
|
||||
* @param data Buffer containing the request result.
|
||||
*
|
||||
* @return 0 if succesfulf, negative errno otherwise.
|
||||
*/
|
||||
static int handle_feature_unit_req(struct usb_audio_dev_data *audio_dev_data,
|
||||
struct usb_setup_packet *pSetup,
|
||||
s32_t *len, u8_t **data)
|
||||
{
|
||||
const struct feature_unit_descriptor *fu;
|
||||
struct usb_audio_fu_evt evt;
|
||||
enum usb_audio_fucs cs;
|
||||
u8_t device;
|
||||
u8_t fu_id;
|
||||
u8_t ch_cnt;
|
||||
u8_t ch;
|
||||
int ret;
|
||||
|
||||
fu_id = ((pSetup->wIndex) >> 8) & 0xFF;
|
||||
fu = get_feature_unit(audio_dev_data, &device, fu_id);
|
||||
ch = (pSetup->wValue) & 0xFF;
|
||||
cs = ((pSetup->wValue) >> 8) & 0xFF;
|
||||
ch_cnt = audio_dev_data->ch_cnt[device];
|
||||
|
||||
LOG_DBG("CS: %d, CN: %d, len: %d", cs, ch, *len);
|
||||
|
||||
/* Error checking */
|
||||
if (!(BIT(cs) & (get_controls(fu) << 1))) {
|
||||
/* Feature not supported by this FU */
|
||||
return -EINVAL;
|
||||
} else if (ch >= ch_cnt) {
|
||||
/* Invalid ch */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (cs) {
|
||||
case USB_AUDIO_FU_MUTE_CONTROL:
|
||||
ret = handle_fu_mute_req(audio_dev_data, pSetup,
|
||||
len, data, &evt, device);
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Inform the app */
|
||||
if (audio_dev_data->ops && audio_dev_data->ops->feature_update_cb) {
|
||||
if (pSetup->bRequest == USB_AUDIO_SET_CUR) {
|
||||
evt.cs = cs;
|
||||
evt.channel = ch;
|
||||
evt.dir = get_fu_dir(fu);
|
||||
audio_dev_data->ops->feature_update_cb(
|
||||
audio_dev_data->common.dev, &evt);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handler called for class specific interface request.
|
||||
*
|
||||
* This function handles all class specific interface requests to a usb audio
|
||||
* device. If request is properly server then 0 is returned. Returning negative
|
||||
* value will lead to set stall on IN EP0.
|
||||
*
|
||||
* @param pSetup Information about the executed request.
|
||||
* @param len Size of the buffer.
|
||||
* @param data Buffer containing the request result.
|
||||
*
|
||||
* @return 0 on success, negative errno code on fail.
|
||||
*/
|
||||
static int handle_interface_req(struct usb_setup_packet *pSetup,
|
||||
s32_t *len,
|
||||
u8_t **data)
|
||||
{
|
||||
struct usb_audio_dev_data *audio_dev_data;
|
||||
struct usb_audio_entity entity;
|
||||
|
||||
/* parse wIndex for interface request */
|
||||
u8_t entity_id = ((pSetup->wIndex) >> 8) & 0xFF;
|
||||
|
||||
entity.id = entity_id;
|
||||
|
||||
/** Normally there should be a call to usb_get_dev_data_by_iface()
|
||||
* and addressed interface should be read from wIndex low byte.
|
||||
*
|
||||
* u8_t interface = (pSetup->wIndex) & 0xFF;
|
||||
*
|
||||
* However, Linux is using special form of Audio Requests
|
||||
* which always left wIndex low byte 0 no matter which device and
|
||||
* entity is addressed. Because of that there is a need to obtain
|
||||
* this information from the device descriptor using entity id.
|
||||
*/
|
||||
audio_dev_data = get_audio_dev_data_by_entity(&entity);
|
||||
|
||||
if (audio_dev_data == NULL) {
|
||||
LOG_ERR("Device data not found for entity %u", entity.id);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
switch (entity.subtype) {
|
||||
case USB_AUDIO_FEATURE_UNIT:
|
||||
return handle_feature_unit_req(audio_dev_data,
|
||||
pSetup, len, data);
|
||||
default:
|
||||
LOG_INF("Currently not supported");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Custom callback for USB Device requests.
|
||||
*
|
||||
* This callback is called when set/get interface request is directed
|
||||
* to the device. This is Zephyr way to address those requests.
|
||||
* It's not possible to do that in the core stack as common USB device
|
||||
* stack does not know the amount of devices that has alternate interfaces.
|
||||
*
|
||||
* @param pSetup Information about the request to execute.
|
||||
* @param len Size of the buffer.
|
||||
* @param data Buffer containing the request result.
|
||||
*
|
||||
* @return 0 on success, positive value if request is intended to be handled
|
||||
* by the core USB stack. Negative error code on fail.
|
||||
*/
|
||||
static int audio_custom_handler(struct usb_setup_packet *pSetup, s32_t *len,
|
||||
u8_t **data)
|
||||
{
|
||||
const struct cs_ac_if_descriptor *header;
|
||||
struct usb_audio_dev_data *audio_dev_data;
|
||||
const struct usb_if_descriptor *if_desc;
|
||||
const struct usb_ep_descriptor *ep_desc;
|
||||
|
||||
u8_t iface = (pSetup->wIndex) & 0xFF;
|
||||
|
||||
audio_dev_data = get_audio_dev_data_by_iface(iface);
|
||||
if (audio_dev_data == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Search for endpoint associated to addressed interface
|
||||
* Endpoint is searched in order to know the direction of
|
||||
* addressed interface.
|
||||
*/
|
||||
header = audio_dev_data->desc_hdr;
|
||||
|
||||
/* Skip to the first interface */
|
||||
if_desc = (struct usb_if_descriptor *)((u8_t *)header +
|
||||
header->wTotalLength +
|
||||
USB_PASSIVE_IF_DESC_SIZE);
|
||||
|
||||
if (if_desc->bInterfaceNumber == iface) {
|
||||
ep_desc = (struct usb_ep_descriptor *)((u8_t *)if_desc +
|
||||
USB_PASSIVE_IF_DESC_SIZE +
|
||||
USB_AC_CS_IF_DESC_SIZE +
|
||||
USB_FORMAT_TYPE_I_DESC_SIZE);
|
||||
} else {
|
||||
/* In case first interface address is not the one addressed
|
||||
* we can be sure the second one is because
|
||||
* get_audio_dev_data_by_iface() found the device. It
|
||||
* must be the second interface associated with the device.
|
||||
*/
|
||||
if_desc = (struct usb_if_descriptor *)((u8_t *)if_desc +
|
||||
USB_ACTIVE_IF_DESC_SIZE);
|
||||
ep_desc = (struct usb_ep_descriptor *)((u8_t *)if_desc +
|
||||
USB_PASSIVE_IF_DESC_SIZE +
|
||||
USB_AC_CS_IF_DESC_SIZE +
|
||||
USB_FORMAT_TYPE_I_DESC_SIZE);
|
||||
}
|
||||
|
||||
if (REQTYPE_GET_RECIP(pSetup->bmRequestType) ==
|
||||
REQTYPE_RECIP_INTERFACE) {
|
||||
switch (pSetup->bRequest) {
|
||||
case REQ_SET_INTERFACE:
|
||||
if (ep_desc->bEndpointAddress & USB_EP_DIR_MASK) {
|
||||
audio_dev_data->tx_enable = pSetup->wValue;
|
||||
} else {
|
||||
audio_dev_data->rx_enable = pSetup->wValue;
|
||||
}
|
||||
return -EINVAL;
|
||||
case REQ_GET_INTERFACE:
|
||||
if (ep_desc->bEndpointAddress & USB_EP_DIR_MASK) {
|
||||
*data[0] = audio_dev_data->tx_enable;
|
||||
} else {
|
||||
*data[0] = audio_dev_data->rx_enable;
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handler called for Class requests not handled by the USB stack.
|
||||
*
|
||||
* @param pSetup Information about the request to execute.
|
||||
* @param len Size of the buffer.
|
||||
* @param data Buffer containing the request result.
|
||||
*
|
||||
* @return 0 on success, negative errno code on fail.
|
||||
*/
|
||||
static int audio_class_handle_req(struct usb_setup_packet *pSetup,
|
||||
s32_t *len, u8_t **data)
|
||||
{
|
||||
LOG_INF("bmRequestType 0x%02x, bRequest 0x%02x, wValue 0x%04x,"
|
||||
"wIndex 0x%04x, wLength 0x%04x",
|
||||
pSetup->bmRequestType, pSetup->bRequest, pSetup->wValue,
|
||||
pSetup->wIndex, pSetup->wLength);
|
||||
|
||||
switch (REQTYPE_GET_RECIP(pSetup->bmRequestType)) {
|
||||
case REQTYPE_RECIP_INTERFACE:
|
||||
return handle_interface_req(pSetup, len, data);
|
||||
default:
|
||||
LOG_ERR("Request recipient invalid");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int usb_audio_device_init(struct device *dev)
|
||||
{
|
||||
LOG_DBG("Init Audio Device: dev %p (%s)", dev, dev->config->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void audio_write_cb(u8_t ep, int size, void *priv)
|
||||
{
|
||||
struct usb_dev_data *dev_data;
|
||||
struct usb_audio_dev_data *audio_dev_data;
|
||||
struct net_buf *buffer = priv;
|
||||
|
||||
dev_data = usb_get_dev_data_by_ep(&usb_audio_data_devlist, ep);
|
||||
audio_dev_data = dev_data->dev->driver_data;
|
||||
|
||||
LOG_DBG("Written %d bytes on ep 0x%02x, *audio_dev_data %p",
|
||||
size, ep, audio_dev_data);
|
||||
|
||||
/* Ask installed callback to process the data.
|
||||
* User is responsible for freeing the buffer.
|
||||
* In case no callback is installed free the buffer.
|
||||
*/
|
||||
if (audio_dev_data->ops && audio_dev_data->ops->data_written_cb) {
|
||||
audio_dev_data->ops->data_written_cb(dev_data->dev,
|
||||
buffer, size);
|
||||
} else {
|
||||
/* Release net_buf back to the pool */
|
||||
net_buf_unref(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
int usb_audio_send(const struct device *dev, struct net_buf *buffer,
|
||||
size_t len)
|
||||
{
|
||||
struct usb_audio_dev_data *audio_dev_data = dev->driver_data;
|
||||
struct usb_cfg_data *cfg = (void *)dev->config->config_info;
|
||||
/* EP ISO IN is always placed first in the endpoint table */
|
||||
u8_t ep = cfg->endpoint[0].ep_addr;
|
||||
|
||||
if (!(ep & USB_EP_DIR_MASK)) {
|
||||
LOG_ERR("Wrong device");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!audio_dev_data->tx_enable) {
|
||||
LOG_DBG("sending dropped -> Host chose passive interface");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (len > buffer->size) {
|
||||
LOG_ERR("Cannot send %d bytes, to much data", len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/** buffer passed to *priv because completion callback
|
||||
* needs to release it to the pool
|
||||
*/
|
||||
usb_transfer(ep, buffer->data, len, USB_TRANS_WRITE | USB_TRANS_NO_ZLP,
|
||||
audio_write_cb, buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t usb_audio_get_in_frame_size(const struct device *dev)
|
||||
{
|
||||
struct usb_audio_dev_data *audio_dev_data = dev->driver_data;
|
||||
|
||||
return audio_dev_data->in_frame_size;
|
||||
}
|
||||
|
||||
static void audio_receive_cb(u8_t ep, enum usb_dc_ep_cb_status_code status)
|
||||
{
|
||||
struct usb_audio_dev_data *audio_dev_data;
|
||||
struct usb_dev_data *common;
|
||||
struct net_buf *buffer;
|
||||
int ret_bytes;
|
||||
int ret;
|
||||
|
||||
__ASSERT(status == USB_DC_EP_DATA_OUT, "Invalid ep status");
|
||||
|
||||
common = usb_get_dev_data_by_ep(&usb_audio_data_devlist, ep);
|
||||
if (common == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
audio_dev_data = CONTAINER_OF(common, struct usb_audio_dev_data,
|
||||
common);
|
||||
|
||||
/** Check if active audiostreaming interface is selected
|
||||
* If no there is no point to read the data. Return from callback
|
||||
*/
|
||||
if (!audio_dev_data->rx_enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if application installed callback and process the data.
|
||||
* In case no callback is installed do not alloc the buffer at all.
|
||||
*/
|
||||
if (audio_dev_data->ops && audio_dev_data->ops->data_received_cb) {
|
||||
buffer = net_buf_alloc(audio_dev_data->pool, K_NO_WAIT);
|
||||
if (!buffer) {
|
||||
LOG_ERR("Failed to allocate data buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = usb_read(ep, buffer->data, buffer->size, &ret_bytes);
|
||||
|
||||
if (ret) {
|
||||
LOG_ERR("ret=%d ", ret);
|
||||
net_buf_unref(buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ret_bytes) {
|
||||
net_buf_unref(buffer);
|
||||
return;
|
||||
}
|
||||
audio_dev_data->ops->data_received_cb(common->dev,
|
||||
buffer, ret_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
void usb_audio_register(struct device *dev,
|
||||
const struct usb_audio_ops *ops)
|
||||
{
|
||||
struct usb_audio_dev_data *audio_dev_data = dev->driver_data;
|
||||
const struct usb_cfg_data *cfg = dev->config->config_info;
|
||||
const struct std_if_descriptor *iface_descr =
|
||||
cfg->interface_descriptor;
|
||||
const struct cs_ac_if_descriptor *header =
|
||||
(struct cs_ac_if_descriptor *)
|
||||
((u8_t *)iface_descr + USB_PASSIVE_IF_DESC_SIZE);
|
||||
|
||||
audio_dev_data->ops = ops;
|
||||
audio_dev_data->common.dev = dev;
|
||||
audio_dev_data->rx_enable = false;
|
||||
audio_dev_data->tx_enable = false;
|
||||
audio_dev_data->desc_hdr = header;
|
||||
|
||||
sys_slist_append(&usb_audio_data_devlist, &audio_dev_data->common.node);
|
||||
|
||||
LOG_DBG("Device dev %p dev_data %p cfg %p added to devlist %p",
|
||||
dev, audio_dev_data, dev->config->config_info,
|
||||
&usb_audio_data_devlist);
|
||||
}
|
||||
|
||||
#define DEFINE_AUDIO_DEVICE(dev, i) \
|
||||
USBD_CFG_DATA_DEFINE(primary, audio) \
|
||||
struct usb_cfg_data dev##_audio_config_##i = { \
|
||||
.usb_device_description = NULL, \
|
||||
.interface_config = audio_interface_config, \
|
||||
.interface_descriptor = &dev##_desc_##i.std_ac_interface, \
|
||||
.cb_usb_status = audio_cb_usb_status, \
|
||||
.interface = { \
|
||||
.class_handler = audio_class_handle_req, \
|
||||
.custom_handler = audio_custom_handler, \
|
||||
.vendor_handler = NULL, \
|
||||
}, \
|
||||
.num_endpoints = ARRAY_SIZE(dev##_usb_audio_ep_data_##i), \
|
||||
.endpoint = dev##_usb_audio_ep_data_##i, \
|
||||
}; \
|
||||
DEVICE_AND_API_INIT(dev##_usb_audio_device_##i, \
|
||||
DT_LABEL(DT_INST(i, COMPAT_##dev)), \
|
||||
&usb_audio_device_init, \
|
||||
&dev##_audio_dev_data_##i, \
|
||||
&dev##_audio_config_##i, APPLICATION, \
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
|
||||
DUMMY_API)
|
||||
|
||||
#define DEFINE_BUF_POOL(name, size) \
|
||||
NET_BUF_POOL_FIXED_DEFINE(name, 5, size, net_buf_destroy)
|
||||
|
||||
#define UNIDIR_DEVICE(dev, i, out_pool, in_size, it_type, ot_type, cb, addr) \
|
||||
UTIL_EXPAND( \
|
||||
DEFINE_AUDIO_DEV_DATA(dev, i, out_pool, in_size); \
|
||||
DECLARE_DESCRIPTOR(dev, i, 1); \
|
||||
DEFINE_AUDIO_DESCRIPTOR(dev, i, dev##_ID(i), dev##_LINK(i), \
|
||||
it_type, ot_type, cb, addr); \
|
||||
DEFINE_AUDIO_DEVICE(dev, i))
|
||||
|
||||
#define HEADPHONES_DEVICE(i, dev) UTIL_EXPAND( \
|
||||
DEFINE_BUF_POOL(audio_data_pool_hp_##i, EP_SIZE(dev, i)); \
|
||||
UNIDIR_DEVICE(dev, i, &audio_data_pool_hp_##i, 0, \
|
||||
USB_AUDIO_USB_STREAMING, USB_AUDIO_OUT_HEADPHONES, \
|
||||
audio_receive_cb, AUTO_EP_OUT);)
|
||||
|
||||
#define MICROPHONE_DEVICE(i, dev) UTIL_EXPAND( \
|
||||
UNIDIR_DEVICE(dev, i, NULL, EP_SIZE(dev, i), \
|
||||
USB_AUDIO_IN_MICROPHONE, USB_AUDIO_USB_STREAMING, \
|
||||
usb_transfer_ep_callback, AUTO_EP_IN);)
|
||||
|
||||
#define HEADSET_DEVICE(i, dev) UTIL_EXPAND( \
|
||||
DEFINE_BUF_POOL(audio_data_pool_hs_##i, EP_SIZE(dev##_HP, i)); \
|
||||
DEFINE_AUDIO_DEV_DATA_BIDIR(dev, i, &audio_data_pool_hs_##i, \
|
||||
EP_SIZE(dev##_MIC, i)); \
|
||||
DECLARE_DESCRIPTOR_BIDIR(dev, i, 2); \
|
||||
DEFINE_AUDIO_DESCRIPTOR_BIDIR(dev, i, dev##_ID(i)); \
|
||||
DEFINE_AUDIO_DEVICE(dev, i);)
|
||||
|
||||
UTIL_LISTIFY(HEADPHONES_DEVICE_COUNT, HEADPHONES_DEVICE, HP)
|
||||
UTIL_LISTIFY(MICROPHONE_DEVICE_COUNT, MICROPHONE_DEVICE, MIC)
|
||||
UTIL_LISTIFY(HEADSET_DEVICE_COUNT, HEADSET_DEVICE, HS)
|
||||
|
|
525
subsys/usb/class/audio/usb_audio_internal.h
Normal file
525
subsys/usb/class/audio/usb_audio_internal.h
Normal file
|
@ -0,0 +1,525 @@
|
|||
/*
|
||||
* USB audio class internal header
|
||||
*
|
||||
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief USB Audio Device Class internal header
|
||||
*
|
||||
* This header file is used to store internal configuration
|
||||
* defines.
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_USB_CLASS_AUDIO_INTERNAL_H_
|
||||
#define ZEPHYR_INCLUDE_USB_CLASS_AUDIO_INTERNAL_H_
|
||||
|
||||
#define DUMMY_API (const void *)1
|
||||
|
||||
#define USB_PASSIVE_IF_DESC_SIZE sizeof(struct usb_if_descriptor)
|
||||
#define USB_AC_CS_IF_DESC_SIZE sizeof(struct as_cs_interface_descriptor)
|
||||
#define USB_FORMAT_TYPE_I_DESC_SIZE sizeof(struct format_type_i_descriptor)
|
||||
#define USB_STD_AS_AD_EP_DESC_SIZE sizeof(struct std_as_ad_endpoint_descriptor)
|
||||
#define USB_CS_AS_AD_EP_DESC_SIZE sizeof(struct cs_as_ad_ep_descriptor)
|
||||
#define USB_ACTIVE_IF_DESC_SIZE (USB_PASSIVE_IF_DESC_SIZE + \
|
||||
USB_AC_CS_IF_DESC_SIZE + \
|
||||
USB_FORMAT_TYPE_I_DESC_SIZE + \
|
||||
USB_STD_AS_AD_EP_DESC_SIZE + \
|
||||
USB_CS_AS_AD_EP_DESC_SIZE)
|
||||
|
||||
#define INPUT_TERMINAL_DESC_SIZE sizeof(struct input_terminal_descriptor)
|
||||
#define OUTPUT_TERMINAL_DESC_SIZE sizeof(struct output_terminal_descriptor)
|
||||
|
||||
#define BMA_CONTROLS_OFFSET 6
|
||||
#define FU_FIXED_ELEMS_SIZE 7
|
||||
|
||||
/* Macros for maitaining features of feature unit entity */
|
||||
#define FEATURE_MUTE_SIZE 0x01
|
||||
#define FEATURE_VOLUME_SIZE 0x02
|
||||
#define FEATURE_BASS_SIZE 0x01
|
||||
#define FEATURE_MID_SIZE 0x01
|
||||
#define FEATURE_TREBLE_SIZE 0x01
|
||||
#define FEATURE_TONE_CONTROL_SIZE (FEATURE_BASS_SIZE +\
|
||||
FEATURE_MID_SIZE + \
|
||||
FEATURE_TREBLE_SIZE)
|
||||
#define FEATURE_GRAPHIC_EQUALIZER_SIZE 0x01
|
||||
#define FEATURE_AUTOMATIC_GAIN_CONTROL_SIZE 0x01
|
||||
#define FEATURE_DELAY_SIZE 0x02
|
||||
#define FEATURE_BASS_BOOST_SIZE 0x01
|
||||
#define FEATURE_LOUDNESS_SIZE 0x01
|
||||
|
||||
#define POS_MUTE 0
|
||||
#define POS_VOLUME (POS_MUTE + FEATURE_MUTE_SIZE)
|
||||
#define POS_BASS (POS_VOLUME + FEATURE_VOLUME_SIZE)
|
||||
#define POS_MID (POS_BASS + FEATURE_BASS_SIZE)
|
||||
#define POS_TREBLE (POS_MID + FEATURE_MID_SIZE)
|
||||
#define POS_GRAPHIC_EQUALIZER (POS_TREBLE + FEATURE_TREBLE_SIZE)
|
||||
#define POS_AUTOMATIC_GAIN_CONTROL (POS_GRAPHIC_EQUALIZER + \
|
||||
FEATURE_GRAPHIC_EQUALIZER_SIZE)
|
||||
#define POS_DELAY (POS_AUTOMATIC_GAIN_CONTROL + \
|
||||
FEATURE_AUTOMATIC_GAIN_CONTROL_SIZE)
|
||||
#define POS_BASS_BOOST (POS_DELAY + FEATURE_DELAY_SIZE)
|
||||
#define POS_LOUDNESS (POS_BASS_BOOST + FEATURE_BASS_BOOST_SIZE)
|
||||
|
||||
#define POS(prop, ch_idx, ch_cnt) (ch_cnt * POS_##prop + \
|
||||
(ch_idx * FEATURE_##prop##_SIZE))
|
||||
#define LEN(ch_cnt, prop) (ch_cnt * FEATURE_##prop##_SIZE)
|
||||
|
||||
/* Names of compatibles used for configuration of the device */
|
||||
#define COMPAT_HP usb_audio_hp
|
||||
#define COMPAT_MIC usb_audio_mic
|
||||
#define COMPAT_HS usb_audio_hs
|
||||
|
||||
#define HEADPHONES_DEVICE_COUNT DT_NUM_INST(COMPAT_HP)
|
||||
#define MICROPHONE_DEVICE_COUNT DT_NUM_INST(COMPAT_MIC)
|
||||
#define HEADSET_DEVICE_COUNT DT_NUM_INST(COMPAT_HS)
|
||||
|
||||
#define IF_USB_AUDIO_PROP_HP(i, prop, bitmask) \
|
||||
COND_CODE_1(DT_PROP(DT_INST(i, COMPAT_HP), prop), (bitmask), (0))
|
||||
#define IF_USB_AUDIO_PROP_MIC(i, prop, bitmask) \
|
||||
COND_CODE_1(DT_PROP(DT_INST(i, COMPAT_MIC), prop), (bitmask), (0))
|
||||
#define IF_USB_AUDIO_PROP_HS_HP(i, prop, bitmask) \
|
||||
COND_CODE_1(DT_PROP(DT_INST(i, COMPAT_HS), hp_##prop), (bitmask), (0))
|
||||
#define IF_USB_AUDIO_PROP_HS_MIC(i, prop, bitmask) \
|
||||
COND_CODE_1(DT_PROP(DT_INST(i, COMPAT_HS), mic_##prop), (bitmask), (0))
|
||||
#define IF_USB_AUDIO_PROP(dev, i, prop, bitmask) \
|
||||
IF_USB_AUDIO_PROP_##dev(i, prop, bitmask)
|
||||
|
||||
/* Macro for getting the bitmask of configured channels for given device */
|
||||
#define CH_CFG(dev, i) (0x0000 \
|
||||
| IF_USB_AUDIO_PROP(dev, i, channel_l, BIT(0)) \
|
||||
| IF_USB_AUDIO_PROP(dev, i, channel_r, BIT(1)) \
|
||||
| IF_USB_AUDIO_PROP(dev, i, channel_c, BIT(2)) \
|
||||
| IF_USB_AUDIO_PROP(dev, i, channel_lfe, BIT(3)) \
|
||||
| IF_USB_AUDIO_PROP(dev, i, channel_ls, BIT(4)) \
|
||||
| IF_USB_AUDIO_PROP(dev, i, channel_rs, BIT(5)) \
|
||||
| IF_USB_AUDIO_PROP(dev, i, channel_lc, BIT(6)) \
|
||||
| IF_USB_AUDIO_PROP(dev, i, channel_rc, BIT(7)) \
|
||||
| IF_USB_AUDIO_PROP(dev, i, channel_s, BIT(8)) \
|
||||
| IF_USB_AUDIO_PROP(dev, i, channel_sl, BIT(9)) \
|
||||
| IF_USB_AUDIO_PROP(dev, i, channel_sr, BIT(10))\
|
||||
| IF_USB_AUDIO_PROP(dev, i, channel_t, BIT(11))\
|
||||
)
|
||||
|
||||
/* Macro for getting the number of configured channles for given device.
|
||||
* Master channel (0) excluded.
|
||||
*/
|
||||
#define CH_CNT(dev, i) (0 \
|
||||
+ IF_USB_AUDIO_PROP(dev, i, channel_l, 1)\
|
||||
+ IF_USB_AUDIO_PROP(dev, i, channel_r, 1)\
|
||||
+ IF_USB_AUDIO_PROP(dev, i, channel_c, 1)\
|
||||
+ IF_USB_AUDIO_PROP(dev, i, channel_lfe, 1)\
|
||||
+ IF_USB_AUDIO_PROP(dev, i, channel_ls, 1)\
|
||||
+ IF_USB_AUDIO_PROP(dev, i, channel_rs, 1)\
|
||||
+ IF_USB_AUDIO_PROP(dev, i, channel_lc, 1)\
|
||||
+ IF_USB_AUDIO_PROP(dev, i, channel_rc, 1)\
|
||||
+ IF_USB_AUDIO_PROP(dev, i, channel_s, 1)\
|
||||
+ IF_USB_AUDIO_PROP(dev, i, channel_sl, 1)\
|
||||
+ IF_USB_AUDIO_PROP(dev, i, channel_sr, 1)\
|
||||
+ IF_USB_AUDIO_PROP(dev, i, channel_t, 1)\
|
||||
)
|
||||
|
||||
/* Macro for getting bitmask of supported features for given device */
|
||||
#define FEATURES(dev, i) (0x0000 \
|
||||
| IF_USB_AUDIO_PROP(dev, i, feature_mute, BIT(0)) \
|
||||
| IF_USB_AUDIO_PROP(dev, i, feature_volume, BIT(1)) \
|
||||
| IF_USB_AUDIO_PROP(dev, i, feature_tone_control, BIT(2) | BIT(3) | BIT(4))\
|
||||
| IF_USB_AUDIO_PROP(dev, i, feature_graphic_equalizer, BIT(5)) \
|
||||
| IF_USB_AUDIO_PROP(dev, i, feature_automatic_gain_control, BIT(6)) \
|
||||
| IF_USB_AUDIO_PROP(dev, i, feature_delay, BIT(7)) \
|
||||
| IF_USB_AUDIO_PROP(dev, i, feature_bass_boost, BIT(8)) \
|
||||
| IF_USB_AUDIO_PROP(dev, i, feature_loudness, BIT(9)) \
|
||||
)
|
||||
|
||||
/* Macro for getting required size to store values for supported features. */
|
||||
#define FEATURES_SIZE(dev, i) ((CH_CNT(dev, i) + 1) * (0x0000 \
|
||||
+ IF_USB_AUDIO_PROP(dev, i, feature_mute, \
|
||||
FEATURE_MUTE_SIZE) \
|
||||
+ IF_USB_AUDIO_PROP(dev, i, feature_volume, \
|
||||
FEATURE_VOLUME_SIZE) \
|
||||
+ IF_USB_AUDIO_PROP(dev, i, feature_tone_control, \
|
||||
FEATURE_TONE_CONTROL_SIZE) \
|
||||
+ IF_USB_AUDIO_PROP(dev, i, feature_graphic_equalizer, \
|
||||
FEATURE_GRAPHIC_EQUALIZER_SIZE) \
|
||||
+ IF_USB_AUDIO_PROP(dev, i, feature_automatic_gain_control, \
|
||||
FEATURE_AUTOMATIC_GAIN_CONTROL_SIZE)\
|
||||
+ IF_USB_AUDIO_PROP(dev, i, feature_delay, \
|
||||
FEATURE_DELAY_SIZE) \
|
||||
+ IF_USB_AUDIO_PROP(dev, i, feature_bass_boost, \
|
||||
FEATURE_BASS_BOOST_SIZE) \
|
||||
+ IF_USB_AUDIO_PROP(dev, i, feature_loudness, \
|
||||
FEATURE_LOUDNESS_SIZE) \
|
||||
))
|
||||
|
||||
#define GET_RES_HP(i) DT_PROP(DT_INST(i, COMPAT_HP), resolution)
|
||||
#define GET_RES_MIC(i) DT_PROP(DT_INST(i, COMPAT_MIC), resolution)
|
||||
#define GET_RES_HS_HP(i) DT_PROP(DT_INST(i, COMPAT_HS), hp_resolution)
|
||||
#define GET_RES_HS_MIC(i) DT_PROP(DT_INST(i, COMPAT_HS), mic_resolution)
|
||||
#define GET_RES(dev, i) GET_RES_##dev(i)
|
||||
|
||||
#define SYNC_TYPE_HP(i) 3
|
||||
#define SYNC_TYPE_MIC(i) DT_ENUM_IDX(DT_INST(i, COMPAT_MIC), sync_type)
|
||||
#define SYNC_TYPE_HS_HP(i) 3
|
||||
#define SYNC_TYPE_HS_MIC(i) DT_ENUM_IDX(DT_INST(i, COMPAT_HS), mic_sync_type)
|
||||
#define SYNC_TYPE(dev, i) (SYNC_TYPE_##dev(i) << 2)
|
||||
|
||||
#define EP_SIZE(dev, i) \
|
||||
((GET_RES(dev, i)/8) * CH_CNT(dev, i) * 48)
|
||||
|
||||
/* *_ID() macros are used to give proper Id to each entity describing
|
||||
* the device. Entities Id must start from 1 that's why 1 is added.
|
||||
* Multiplication by 3 for HP/MIC comes from the fact that 3 entities are
|
||||
* required to describe the device, 6 in case of HS.
|
||||
*/
|
||||
#define HP_ID(i) ((3*i) + 1)
|
||||
#define MIC_ID(i) ((3*(HEADPHONES_DEVICE_COUNT + i)) + 1)
|
||||
#define HS_ID(i) ((3*(HEADPHONES_DEVICE_COUNT + \
|
||||
MICROPHONE_DEVICE_COUNT)) + 6*i + 1)
|
||||
|
||||
/* *_LINK() macros are used to properly connect relevant audio streaming
|
||||
* class specific interface with valid entity. In case of Headphones this
|
||||
* will always be 1st entity describing the device (Input terminal). This
|
||||
* is where addition of 1 comes from. In case of Headphones thill will always
|
||||
* be 3rd entity (Output terminal) - addition of 3.
|
||||
*/
|
||||
#define HP_LINK(i) ((3*i) + 1)
|
||||
#define MIC_LINK(i) ((3*(HEADPHONES_DEVICE_COUNT + i)) + 3)
|
||||
|
||||
/**
|
||||
* Addressable logical object inside an audio function.
|
||||
* Entity is one of: Terminal or Unit.
|
||||
* Refer to 1.4 Terms and Abbreviations from audio10.pdf
|
||||
*/
|
||||
struct usb_audio_entity {
|
||||
enum usb_audio_cs_ac_int_desc_subtypes subtype;
|
||||
u8_t id;
|
||||
};
|
||||
|
||||
/**
|
||||
* @warning Size of baInterface is 2 just to make it useable
|
||||
* for all kind of devices: headphones, microphone and headset.
|
||||
* Actual size of the struct should be checked by reading
|
||||
* .bLength.
|
||||
*/
|
||||
struct cs_ac_if_descriptor {
|
||||
u8_t bLength;
|
||||
u8_t bDescriptorType;
|
||||
u8_t bDescriptorSubtype;
|
||||
u16_t bcdADC;
|
||||
u16_t wTotalLength;
|
||||
u8_t bInCollection;
|
||||
u8_t baInterfaceNr[2];
|
||||
} __packed;
|
||||
|
||||
struct input_terminal_descriptor {
|
||||
u8_t bLength;
|
||||
u8_t bDescriptorType;
|
||||
u8_t bDescriptorSubtype;
|
||||
u8_t bTerminalID;
|
||||
u16_t wTerminalType;
|
||||
u8_t bAssocTerminal;
|
||||
u8_t bNrChannels;
|
||||
u16_t wChannelConfig;
|
||||
u8_t iChannelNames;
|
||||
u8_t iTerminal;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* @note Size of Feature unit descriptor is not fixed.
|
||||
* This structure is just a helper not a common type.
|
||||
*/
|
||||
struct feature_unit_descriptor {
|
||||
u8_t bLength;
|
||||
u8_t bDescriptorType;
|
||||
u8_t bDescriptorSubtype;
|
||||
u8_t bUnitID;
|
||||
u8_t bSourceID;
|
||||
u8_t bControlSize;
|
||||
u16_t bmaControls[1];
|
||||
} __packed;
|
||||
|
||||
struct output_terminal_descriptor {
|
||||
u8_t bLength;
|
||||
u8_t bDescriptorType;
|
||||
u8_t bDescriptorSubtype;
|
||||
u8_t bTerminalID;
|
||||
u16_t wTerminalType;
|
||||
u8_t bAssocTerminal;
|
||||
u8_t bSourceID;
|
||||
u8_t iTerminal;
|
||||
} __packed;
|
||||
|
||||
struct as_cs_interface_descriptor {
|
||||
u8_t bLength;
|
||||
u8_t bDescriptorType;
|
||||
u8_t bDescriptorSubtype;
|
||||
u8_t bTerminalLink;
|
||||
u8_t bDelay;
|
||||
u16_t wFormatTag;
|
||||
} __packed;
|
||||
|
||||
struct format_type_i_descriptor {
|
||||
u8_t bLength;
|
||||
u8_t bDescriptorType;
|
||||
u8_t bDescriptorSubtype;
|
||||
u8_t bFormatType;
|
||||
u8_t bNrChannels;
|
||||
u8_t bSubframeSize;
|
||||
u8_t bBitResolution;
|
||||
u8_t bSamFreqType;
|
||||
u8_t tSamFreq[3];
|
||||
} __packed;
|
||||
|
||||
struct std_as_ad_endpoint_descriptor {
|
||||
u8_t bLength;
|
||||
u8_t bDescriptorType;
|
||||
u8_t bEndpointAddress;
|
||||
u8_t bmAttributes;
|
||||
u16_t wMaxPacketSize;
|
||||
u8_t bInterval;
|
||||
u8_t bRefresh;
|
||||
u8_t bSynchAddress;
|
||||
} __packed;
|
||||
|
||||
struct cs_as_ad_ep_descriptor {
|
||||
u8_t bLength;
|
||||
u8_t bDescriptorType;
|
||||
u8_t bDescriptorSubtype;
|
||||
u8_t bmAttributes;
|
||||
u8_t bLockDelayUnits;
|
||||
u16_t wLockDelay;
|
||||
} __packed;
|
||||
|
||||
#define DECLARE_HEADER(dev, i, ifaces) \
|
||||
struct dev##_cs_ac_if_descriptor_##i { \
|
||||
u8_t bLength; \
|
||||
u8_t bDescriptorType; \
|
||||
u8_t bDescriptorSubtype; \
|
||||
u16_t bcdADC; \
|
||||
u16_t wTotalLength; \
|
||||
u8_t bInCollection; \
|
||||
u8_t baInterfaceNr[ifaces]; \
|
||||
} __packed
|
||||
|
||||
#define DECLARE_FEATURE_UNIT(dev, i) \
|
||||
struct dev##_feature_unit_descriptor_##i { \
|
||||
u8_t bLength; \
|
||||
u8_t bDescriptorType; \
|
||||
u8_t bDescriptorSubtype; \
|
||||
u8_t bUnitID; \
|
||||
u8_t bSourceID; \
|
||||
u8_t bControlSize; \
|
||||
u16_t bmaControls[CH_CNT(dev, i) + 1]; \
|
||||
u8_t iFeature; \
|
||||
} __packed
|
||||
|
||||
#define INIT_IAD(iface_subclass, if_cnt) \
|
||||
{ \
|
||||
.bLength = sizeof(struct usb_association_descriptor), \
|
||||
.bDescriptorType = USB_ASSOCIATION_DESC, \
|
||||
.bFirstInterface = 0, \
|
||||
.bInterfaceCount = if_cnt, \
|
||||
.bFunctionClass = AUDIO_CLASS, \
|
||||
.bFunctionSubClass = iface_subclass, \
|
||||
.bFunctionProtocol = 0, \
|
||||
.iFunction = 0, \
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_COMPOSITE_DEVICE
|
||||
#define USB_AUDIO_IAD_DECLARE struct usb_association_descriptor iad;
|
||||
#define USB_AUDIO_IAD(if_cnt) .iad = INIT_IAD(USB_AUDIO_AUDIOCONTROL, if_cnt),
|
||||
#else
|
||||
#define USB_AUDIO_IAD_DECLARE
|
||||
#define USB_AUDIO_IAD(if_cnt)
|
||||
#endif
|
||||
|
||||
#define DECLARE_DESCRIPTOR(dev, i, ifaces) \
|
||||
DECLARE_HEADER(dev, i, ifaces); \
|
||||
DECLARE_FEATURE_UNIT(dev, i); \
|
||||
struct dev##_descriptor_##i { \
|
||||
USB_AUDIO_IAD_DECLARE \
|
||||
struct usb_if_descriptor std_ac_interface; \
|
||||
struct dev##_cs_ac_if_descriptor_##i cs_ac_interface; \
|
||||
struct input_terminal_descriptor input_terminal; \
|
||||
struct dev##_feature_unit_descriptor_##i feature_unit; \
|
||||
struct output_terminal_descriptor output_terminal; \
|
||||
struct usb_if_descriptor as_interface_alt_0; \
|
||||
struct usb_if_descriptor as_interface_alt_1; \
|
||||
struct as_cs_interface_descriptor as_cs_interface; \
|
||||
struct format_type_i_descriptor format; \
|
||||
struct std_as_ad_endpoint_descriptor std_ep; \
|
||||
struct cs_as_ad_ep_descriptor cs_ep; \
|
||||
} __packed
|
||||
|
||||
#define DECLARE_DESCRIPTOR_BIDIR(dev, i, ifaces) \
|
||||
DECLARE_HEADER(dev, i, ifaces); \
|
||||
DECLARE_FEATURE_UNIT(dev##_MIC, i); \
|
||||
DECLARE_FEATURE_UNIT(dev##_HP, i); \
|
||||
struct dev##_descriptor_##i { \
|
||||
USB_AUDIO_IAD_DECLARE \
|
||||
struct usb_if_descriptor std_ac_interface; \
|
||||
struct dev##_cs_ac_if_descriptor_##i cs_ac_interface; \
|
||||
struct input_terminal_descriptor input_terminal_0; \
|
||||
struct dev##_MIC_feature_unit_descriptor_##i feature_unit_0; \
|
||||
struct output_terminal_descriptor output_terminal_0; \
|
||||
struct input_terminal_descriptor input_terminal_1; \
|
||||
struct dev##_HP_feature_unit_descriptor_##i feature_unit_1; \
|
||||
struct output_terminal_descriptor output_terminal_1; \
|
||||
struct usb_if_descriptor as_interface_alt_0_0; \
|
||||
struct usb_if_descriptor as_interface_alt_0_1; \
|
||||
struct as_cs_interface_descriptor as_cs_interface_0; \
|
||||
struct format_type_i_descriptor format_0; \
|
||||
struct std_as_ad_endpoint_descriptor std_ep_0; \
|
||||
struct cs_as_ad_ep_descriptor cs_ep_0; \
|
||||
struct usb_if_descriptor as_interface_alt_1_0; \
|
||||
struct usb_if_descriptor as_interface_alt_1_1; \
|
||||
struct as_cs_interface_descriptor as_cs_interface_1; \
|
||||
struct format_type_i_descriptor format_1; \
|
||||
struct std_as_ad_endpoint_descriptor std_ep_1; \
|
||||
struct cs_as_ad_ep_descriptor cs_ep_1; \
|
||||
} __packed
|
||||
|
||||
#define INIT_STD_IF(iface_subclass, iface_num, alt_setting, eps_num) \
|
||||
{ \
|
||||
.bLength = sizeof(struct usb_if_descriptor), \
|
||||
.bDescriptorType = USB_INTERFACE_DESC, \
|
||||
.bInterfaceNumber = iface_num, \
|
||||
.bAlternateSetting = alt_setting, \
|
||||
.bNumEndpoints = eps_num, \
|
||||
.bInterfaceClass = AUDIO_CLASS, \
|
||||
.bInterfaceSubClass = iface_subclass, \
|
||||
.bInterfaceProtocol = 0, \
|
||||
.iInterface = 0, \
|
||||
}
|
||||
|
||||
#define INIT_CS_AC_IF(dev, i, ifaces) \
|
||||
{ \
|
||||
.bLength = sizeof(struct dev##_cs_ac_if_descriptor_##i), \
|
||||
.bDescriptorType = USB_CS_INTERFACE_DESC, \
|
||||
.bDescriptorSubtype = USB_AUDIO_HEADER, \
|
||||
.bcdADC = sys_cpu_to_le16(0x0100), \
|
||||
.wTotalLength = sys_cpu_to_le16( \
|
||||
sizeof(struct dev##_cs_ac_if_descriptor_##i) + \
|
||||
INPUT_TERMINAL_DESC_SIZE + \
|
||||
sizeof(struct dev##_feature_unit_descriptor_##i) + \
|
||||
OUTPUT_TERMINAL_DESC_SIZE), \
|
||||
.bInCollection = ifaces, \
|
||||
.baInterfaceNr = {0}, \
|
||||
}
|
||||
|
||||
#define INIT_CS_AC_IF_BIDIR(dev, i, ifaces) \
|
||||
{ \
|
||||
.bLength = sizeof(struct dev##_cs_ac_if_descriptor_##i), \
|
||||
.bDescriptorType = USB_CS_INTERFACE_DESC, \
|
||||
.bDescriptorSubtype = USB_AUDIO_HEADER, \
|
||||
.bcdADC = sys_cpu_to_le16(0x0100), \
|
||||
.wTotalLength = sys_cpu_to_le16( \
|
||||
sizeof(struct dev##_cs_ac_if_descriptor_##i) + \
|
||||
2*INPUT_TERMINAL_DESC_SIZE + \
|
||||
sizeof(struct dev##_MIC_feature_unit_descriptor_##i) + \
|
||||
sizeof(struct dev##_HP_feature_unit_descriptor_##i) + \
|
||||
2*OUTPUT_TERMINAL_DESC_SIZE), \
|
||||
.bInCollection = ifaces, \
|
||||
.baInterfaceNr = {0}, \
|
||||
}
|
||||
|
||||
#define INIT_IN_TERMINAL(dev, i, terminal_id, type) \
|
||||
{ \
|
||||
.bLength = INPUT_TERMINAL_DESC_SIZE, \
|
||||
.bDescriptorType = USB_CS_INTERFACE_DESC, \
|
||||
.bDescriptorSubtype = USB_AUDIO_INPUT_TERMINAL, \
|
||||
.bTerminalID = terminal_id, \
|
||||
.wTerminalType = sys_cpu_to_le16(type), \
|
||||
.bAssocTerminal = 0, \
|
||||
.bNrChannels = MAX(1, CH_CNT(dev, i)), \
|
||||
.wChannelConfig = sys_cpu_to_le16(CH_CFG(dev, i)), \
|
||||
.iChannelNames = 0, \
|
||||
.iTerminal = 0, \
|
||||
}
|
||||
|
||||
#define INIT_OUT_TERMINAL(terminal_id, source_id, type) \
|
||||
{ \
|
||||
.bLength = OUTPUT_TERMINAL_DESC_SIZE, \
|
||||
.bDescriptorType = USB_CS_INTERFACE_DESC, \
|
||||
.bDescriptorSubtype = USB_AUDIO_OUTPUT_TERMINAL,\
|
||||
.bTerminalID = terminal_id, \
|
||||
.wTerminalType = sys_cpu_to_le16(type), \
|
||||
.bAssocTerminal = 0, \
|
||||
.bSourceID = source_id, \
|
||||
.iTerminal = 0, \
|
||||
}
|
||||
|
||||
/** refer to Table 4-7 from audio10.pdf
|
||||
*/
|
||||
#define INIT_FEATURE_UNIT(dev, i, unit_id, source_id) \
|
||||
{ \
|
||||
.bLength = sizeof(struct dev##_feature_unit_descriptor_##i), \
|
||||
.bDescriptorType = USB_CS_INTERFACE_DESC, \
|
||||
.bDescriptorSubtype = USB_AUDIO_FEATURE_UNIT, \
|
||||
.bUnitID = unit_id, \
|
||||
.bSourceID = source_id, \
|
||||
.bControlSize = sizeof(u16_t), \
|
||||
.bmaControls = { FEATURES(dev, i) }, \
|
||||
.iFeature = 0, \
|
||||
}
|
||||
|
||||
/* Class-Specific AS Interface Descriptor 4.5.2 audio10.pdf */
|
||||
#define INIT_AS_GENERAL(link) \
|
||||
{ \
|
||||
.bLength = USB_AC_CS_IF_DESC_SIZE, \
|
||||
.bDescriptorType = USB_CS_INTERFACE_DESC, \
|
||||
.bDescriptorSubtype = USB_AUDIO_AS_GENERAL, \
|
||||
.bTerminalLink = link, \
|
||||
.bDelay = 0, \
|
||||
.wFormatTag = sys_cpu_to_le16(0x0001), \
|
||||
}
|
||||
|
||||
/** Class-Specific AS Format Type Descriptor 4.5.3 audio10.pdf
|
||||
* For more information refer to 2.2.5 Type I Format Type Descriptor
|
||||
* from frmts10.pdf
|
||||
*/
|
||||
#define INIT_AS_FORMAT_I(ch_cnt, res) \
|
||||
{ \
|
||||
.bLength = sizeof(struct format_type_i_descriptor), \
|
||||
.bDescriptorType = USB_CS_INTERFACE_DESC, \
|
||||
.bDescriptorSubtype = USB_AUDIO_FORMAT_TYPE, \
|
||||
.bFormatType = 0x01, \
|
||||
.bNrChannels = MAX(1, ch_cnt), \
|
||||
.bSubframeSize = res/8, \
|
||||
.bBitResolution = res, \
|
||||
.bSamFreqType = 1, \
|
||||
.tSamFreq = {0x80, 0xBB, 0x00}, \
|
||||
}
|
||||
|
||||
#define INIT_STD_AS_AD_EP(dev, i, addr) \
|
||||
{ \
|
||||
.bLength = sizeof(struct std_as_ad_endpoint_descriptor), \
|
||||
.bDescriptorType = USB_ENDPOINT_DESC, \
|
||||
.bEndpointAddress = addr, \
|
||||
.bmAttributes = (USB_DC_EP_ISOCHRONOUS | SYNC_TYPE(dev, i)), \
|
||||
.wMaxPacketSize = sys_cpu_to_le16(EP_SIZE(dev, i)), \
|
||||
.bInterval = 0x01, \
|
||||
.bRefresh = 0x00, \
|
||||
.bSynchAddress = 0x00, \
|
||||
}
|
||||
|
||||
#define INIT_CS_AS_AD_EP \
|
||||
{ \
|
||||
.bLength = sizeof(struct cs_as_ad_ep_descriptor), \
|
||||
.bDescriptorType = USB_CS_ENDPOINT_DESC, \
|
||||
.bDescriptorSubtype = 0x01, \
|
||||
.bmAttributes = 0x00, \
|
||||
.bLockDelayUnits = 0x00, \
|
||||
.wLockDelay = 0, \
|
||||
}
|
||||
|
||||
#define INIT_EP_DATA(cb, addr) \
|
||||
{ \
|
||||
.ep_cb = cb, \
|
||||
.ep_addr = addr,\
|
||||
}
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_USB_CLASS_AUDIO_INTERNAL_H_ */
|
Loading…
Add table
Add a link
Reference in a new issue