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
|
* @file
|
||||||
* @brief USB Audio Device Class public header
|
* @brief USB Audio Device Class public header
|
||||||
*
|
*
|
||||||
* Header follows Device Class Definition for Audio Class
|
* Header follows below documentation:
|
||||||
* Version 1.0 document (audio10.pdf).
|
* - 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_
|
#ifndef ZEPHYR_INCLUDE_USB_CLASS_AUDIO_H_
|
||||||
#define 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_ */
|
#endif /* ZEPHYR_INCLUDE_USB_CLASS_AUDIO_H_ */
|
||||||
|
|
|
@ -1,13 +1,20 @@
|
||||||
# USB AUDIO configuration options
|
# USB AUDIO configuration options
|
||||||
|
|
||||||
# Copyright (c) 2019 Nordic Semiconductor ASA
|
# Copyright (c) 2020 Nordic Semiconductor ASA
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
config USB_DEVICE_AUDIO
|
config USB_DEVICE_AUDIO
|
||||||
bool "USB Audio Device Class Driver"
|
bool "USB Audio Device Class Driver"
|
||||||
help
|
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
|
if USB_DEVICE_AUDIO
|
||||||
|
|
||||||
|
module = USB_AUDIO
|
||||||
|
module-str = USB Audio
|
||||||
|
source "subsys/logging/Kconfig.template.log_config"
|
||||||
|
|
||||||
endif #USB_DEVICE_AUDIO
|
endif #USB_DEVICE_AUDIO
|
||||||
|
|
|
@ -10,3 +10,970 @@
|
||||||
*
|
*
|
||||||
* Driver for USB Audio device class driver
|
* 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