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:
Emil Obalski 2020-02-18 12:21:50 +01:00 committed by Carles Cufí
commit 8d2f13c203
8 changed files with 2250 additions and 4 deletions

View 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.

View 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.

View 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.

View 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)

View file

@ -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_ */

View file

@ -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

View file

@ -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)

View 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_ */