| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| library fuchsia.virtualaudio; |
| |
| using zx; |
| using fuchsia.hardware.audio; |
| using fuchsia.hardware.audio.signalprocessing; |
| |
| const MAX_UI_STRING_SIZE uint32 = 256; |
| const MAX_FORMAT_RANGES uint32 = 64; |
| // Can't have more ring buffers than processing elements. |
| const MAX_COUNT_RING_BUFFERS uint32 |
| = fuchsia.hardware.audio.signalprocessing.MAX_COUNT_PROCESSING_ELEMENTS; |
| // Can't have more DAI interconnects than processing elements. |
| const MAX_COUNT_DAI_INTERCONNECTS uint32 |
| = fuchsia.hardware.audio.signalprocessing.MAX_COUNT_PROCESSING_ELEMENTS; |
| |
| const CONTROL_NODE_NAME string = "sys/platform/00:00:2f/virtual_audio"; |
| |
| type Direction = table { |
| /// Device type is input (true) or output (false) |
| /// If unspecified, then the driver may be used for both input and output. |
| /// |
| /// For StreamConfig devices `is_input` is required. |
| /// For Dai devices `is_input` is required. |
| /// For Codec devices `is_input` is optional. |
| /// For Composite devices `is_input` is ignored. |
| /// |
| /// Optional. |
| 1: is_input bool; |
| }; |
| |
| /// This protocol provides methods for adding and removing virtual audio |
| /// devices. This protocol is made available through the device tree via |
| /// `CONTROL_NODE_NAME`. |
| closed protocol Control { |
| /// Returns the default configuration for the given device type and direction. |
| strict GetDefaultConfiguration(struct { |
| type DeviceType; |
| direction Direction; |
| }) -> (struct { |
| config Configuration; |
| }) error Error; |
| |
| /// Adds a device to the device tree. |
| /// The device lives until the `Device` FIDL channel is closed. |
| strict AddDevice(resource struct { |
| config Configuration; |
| server server_end:Device; |
| }) -> () error Error; |
| |
| /// Returns the number of active input and output devices and devices with unspecified |
| /// direction. |
| // TODO(https://fxbug.dev/42075676): Consider adding per-driver type info here. |
| strict GetNumDevices() -> (struct { |
| num_input_devices uint32; |
| num_output_devices uint32; |
| num_unspecified_direction_devices uint32; |
| }); |
| |
| /// Synchronously remove all all active input and output devices. |
| strict RemoveAll() -> (); |
| }; |
| |
| /// Configures a virtual audio device. |
| type Configuration = table { |
| /// Device's name. |
| /// |
| /// Optional. |
| 1: device_name string:MAX_UI_STRING_SIZE; |
| |
| /// Device manufacturer's name. |
| /// |
| /// Optional. |
| 2: manufacturer_name string:MAX_UI_STRING_SIZE; |
| |
| /// Device's product name. |
| /// |
| /// Optional. |
| 3: product_name string:MAX_UI_STRING_SIZE; |
| |
| /// Device's unique identifier, a 16 byte string. |
| /// This field is only valid for `device_type` `STREAM_CONFIG` and `CODEC`. |
| /// If it is specified for another device_type, the AddDevice call will |
| /// fail with `INVALID_ARGS`. |
| /// |
| /// Optional. |
| 4: unique_id array<uint8, 16>; |
| |
| /// The configuration specific to this device type. |
| /// The device type is determined by which `DeviceSpecific` union |
| /// member is defined. |
| /// |
| /// Required. |
| 5: device_specific DeviceSpecific; |
| }; |
| |
| /// Configuration for various types of drivers. |
| type DeviceSpecific = flexible union { |
| /// Configuration for a fuchsia.hardware.audio/StreamConfig driver. |
| 1: stream_config StreamConfig; |
| |
| /// Configuration for a fuchsia.hardware.audio/Dai driver. |
| 2: dai Dai; |
| |
| /// Configuration for a fuchsia.hardware.audio/Codec driver. |
| 3: codec Codec; |
| |
| /// Configuration for a fuchsia.hardware.audio/Composite driver. |
| 4: composite Composite; |
| }; |
| |
| /// Configuration for a fuchsia.hardware.audio/StreamConfig driver. |
| type StreamConfig = table { |
| /// Device type is input (true) or output (false). |
| /// This is required such that a user can find a device in devfs in either |
| /// the `/dev/class/audio-output' or `/dev/class/audio-input`, otherwise it |
| /// would be optional such that a driver could be misconfigured on purpose to |
| /// not have an `is_input` field. |
| /// |
| /// Required. |
| 1: is_input bool; |
| |
| /// Configuration for the device ring buffer. |
| /// |
| /// Required. |
| 2: ring_buffer RingBuffer; |
| |
| /// Configuration for the device's clock. |
| /// |
| /// Required. |
| 3: clock_properties ClockProperties; |
| |
| /// Configuration of the device gain. |
| /// |
| /// Required. |
| 4: gain_properties GainProperties; |
| |
| /// Configuration of the device's plug state. |
| /// |
| /// Required. |
| 5: plug_properties PlugProperties; |
| }; |
| |
| /// Configuration for a fuchsia.hardware.audio/Dai driver. |
| type Dai = table { |
| /// Device type is input (true) or output (false). |
| /// |
| /// Optional. |
| 1: is_input bool; |
| |
| /// Configuration for the device ring buffer. |
| /// |
| /// Required. |
| 2: ring_buffer RingBuffer; |
| |
| /// Configuration for the device DAI interconnect. |
| /// |
| /// Required. |
| 3: dai_interconnect DaiInterconnect; |
| |
| /// Configuration for the device's clock. |
| /// |
| /// Required. |
| 4: clock_properties ClockProperties; |
| }; |
| |
| /// Configuration for a fuchsia.hardware.audio/Codec driver. |
| type Codec = table { |
| /// Device type is input (true) or output (false). |
| /// If unspecified, then the driver may be used for both input and output. |
| /// |
| /// Optional. |
| 1: is_input bool; |
| |
| /// Configuration for the device's DAI interconnect. |
| /// |
| /// Required. |
| 2: dai_interconnect DaiInterconnect; |
| |
| /// Configuration of the device's plug state. |
| /// |
| /// Required. |
| 3: plug_properties PlugProperties; |
| }; |
| |
| /// Configuration for a fuchsia.hardware.audio/Composite driver. |
| type Composite = table { |
| /// Ring buffers configuration. |
| /// |
| /// Required. |
| 1: ring_buffers vector<CompositeRingBuffer>:MAX_COUNT_RING_BUFFERS; |
| |
| /// DAI interconnects configurations. |
| /// |
| /// Required. |
| 2: dai_interconnects vector<CompositeDaiInterconnect>:MAX_COUNT_DAI_INTERCONNECTS; |
| |
| /// Configuration for the device's clock. |
| /// |
| /// Required. |
| 3: clock_properties ClockProperties; |
| |
| /// Topologies supported via the signalprocessing API. |
| /// |
| /// Optional. |
| 4: topologies |
| vector<fuchsia.hardware.audio.signalprocessing.Topology>:fuchsia.hardware.audio.signalprocessing.MAX_COUNT_TOPOLOGIES; |
| }; |
| |
| /// Configuration for a fuchsia.hardware.audio/RingBuffer. |
| type RingBuffer = table { |
| /// Driver transfer bytes. |
| /// |
| /// Optional. |
| 1: driver_transfer_bytes uint32; |
| |
| /// Internal delay. |
| /// |
| /// Optional. |
| 2: internal_delay zx.Duration; |
| |
| /// External delay. |
| /// |
| /// Optional. |
| 3: external_delay zx.Duration; |
| |
| /// Supported ring buffer format ranges for this ring buffer. |
| /// |
| /// Required. |
| 4: supported_formats vector<FormatRange>:MAX_FORMAT_RANGES; |
| |
| /// Constraints on the ring buffer. |
| /// If unspecified, there are no constraints. |
| /// |
| /// Optional. |
| 5: ring_buffer_constraints RingBufferConstraints; |
| |
| /// Notification frequency. |
| /// This can be changed later with SetNotificationFrequency. |
| /// If unspecified, the notification frequency will be the same as that |
| /// specified by the client when retrieving the RingBuffer VMO. |
| /// |
| /// Optional. |
| 6: notifications_per_ring uint32; |
| }; |
| |
| type FormatRange = struct { |
| /// Has type audio_sample_format_t (see zircon/device/audio.h) |
| sample_format_flags uint32; |
| |
| min_frame_rate uint32; |
| max_frame_rate uint32; |
| min_channels uint8; |
| max_channels uint8; |
| |
| /// Bitfield of `ASF_RANGE_FLAG_FPS_*` flags (see zircon/device/audio.h) |
| rate_family_flags uint16; |
| }; |
| |
| /// Configuration for a DAI interconnect. |
| type DaiInterconnect = table { |
| /// DAI interconnect supported formats. |
| /// |
| /// Required. |
| 1: dai_supported_formats |
| vector<fuchsia.hardware.audio.DaiSupportedFormats>:fuchsia.hardware.audio.MAX_COUNT_DAI_FORMATS; |
| }; |
| |
| /// Configuration for a fuchsia.hardware.audio/RingBuffer as used by a Composite driver. |
| type CompositeRingBuffer = table { |
| /// Signal processing id for this composite device ring buffer. |
| /// |
| /// Required. |
| 1: id uint64; |
| |
| /// Configuration for this ring buffer. |
| /// |
| /// Required. |
| 2: ring_buffer RingBuffer; |
| }; |
| |
| /// Configuration for a DAI interconnect as used by a Composite driver. |
| type CompositeDaiInterconnect = table { |
| /// Signal processing id for this composite device DAI interconnect. |
| /// |
| /// Required. |
| 1: id uint64; |
| |
| /// Configuration for this DAI interconnect. |
| /// |
| /// Required. |
| 2: dai_interconnect DaiInterconnect; |
| }; |
| |
| type ClockProperties = table { |
| /// The clock domain is an int32 provided by the clock tree to an audio |
| /// driver. special values for `CLOCK_DOMAIN_MONOTONIC` (0), and |
| /// `CLOCK_DOMAIN_EXTERNAL` (-1) (not locally controllable) are defined in |
| /// `fuchsia.hardware.audio`. Note: other than -1, clients should treat any |
| /// negative `clock_domain` value as invalid. |
| /// |
| /// Optional. |
| 1: domain int32; |
| |
| /// Rate-adjustment value for this clock. If omitted, treated as 0 ppm. |
| /// |
| /// Optional. |
| 2: rate_adjustment_ppm int32; |
| }; |
| |
| /// Set restrictions for the device ring buffer. This must be called before |
| /// calling `Add()`, or after `Remove()`. Once the device is activated, the |
| /// ring buffer and its size are returned by the driver in response to an |
| /// `AUDIO_RB_CMD_GET_BUFFER` command on the ring buffer channel. |
| /// Note: both min_frames and max_frames must be multiples of modulo_frames. |
| type RingBufferConstraints = struct { |
| /// The ring buffer must have at least this many frames. |
| /// Must be a multiple of `modulo_frames`. |
| min_frames uint32; |
| |
| /// The ring buffer can have at most this many frames. |
| /// Must be a multiple of `modulo_frames`. |
| max_frames uint32; |
| |
| /// The ring buffer must have a multiple of this many frames. |
| /// Cannot be zero. |
| modulo_frames uint32; |
| }; |
| |
| type GainProperties = table { |
| /// The initial gain state at device initialization time. |
| /// |
| /// Required. |
| 1: gain_state fuchsia.hardware.audio.GainState; |
| |
| /// The device's minimum gain, in decibels. |
| /// |
| /// Optional. |
| 2: min_gain_db float32; |
| |
| /// The device's maximum gain, in decibels. |
| /// |
| /// Optional. |
| 3: max_gain_db float32; |
| |
| /// The precision of each gain-change step, in decibels. |
| /// |
| /// Optional. |
| 4: gain_step_db float32; |
| |
| /// If true, the device contains a distinct MUTE control. If false or |
| /// absent, it does not. |
| /// |
| /// Optional. |
| 5: can_mute bool; |
| |
| /// Automatic Gain Control. If absent, this hardware does not support AGC. |
| /// |
| /// Optional. |
| 6: can_agc bool; |
| }; |
| |
| type PlugProperties = table { |
| /// The initial plug state at device initialization time. |
| /// |
| /// Required. |
| 1: plug_state fuchsia.hardware.audio.PlugState; |
| |
| /// Plug Detect Capabilities. |
| /// |
| /// Optional. |
| 2: plug_detect_capabilities fuchsia.hardware.audio.PlugDetectCapabilities; |
| }; |
| |
| /// This protocol represents the base functionality of active audio devices. A |
| /// device is active until this protocol is closed, at which point the device is |
| /// automatically removed. |
| closed protocol Device { |
| /// Returns the format selected by the client, or `NO_RING_BUFFER` if the |
| /// client has not yet selected a ring buffer format. |
| strict GetFormat() -> (struct { |
| frames_per_second uint32; |
| sample_format uint32; |
| num_channels uint32; |
| external_delay zx.Duration; |
| }) error Error; |
| |
| /// Notifies all subscribed listeners when the above format is set or changed. |
| strict -> OnSetFormat(struct { |
| frames_per_second uint32; |
| sample_format uint32; |
| num_channels uint32; |
| external_delay zx.Duration; |
| }); |
| |
| /// Returns the current gain state for this device. |
| strict GetGain() -> (struct { |
| current_mute bool; |
| current_agc bool; |
| current_gain_db float32; |
| }) error Error; |
| |
| /// Notifies all subscribed listeners when the above gain is set or changed. |
| strict -> OnSetGain(struct { |
| current_mute bool; |
| current_agc bool; |
| current_gain_db float32; |
| }); |
| |
| /// Returns details about the ring buffer. Returns `NO_RING_BUFFER` if the |
| /// client has not yet created the ring buffer. |
| strict GetBuffer() -> (resource struct { |
| ring_buffer zx.Handle:VMO; |
| num_ring_buffer_frames uint32; |
| notifications_per_ring uint32; |
| }) error Error; |
| |
| /// Notifies all subscribed listeners when the above buffer has been |
| /// created. |
| strict -> OnBufferCreated(resource struct { |
| ring_buffer zx.Handle:VMO; |
| num_ring_buffer_frames uint32; |
| notifications_per_ring uint32; |
| }); |
| |
| /// Overrides the position notification frequency for this stream. |
| /// This affects the frequency of `OnPositionNotify` events only. It does |
| /// not affect the frequency of notification events sent through the audio |
| /// driver APIs. |
| strict SetNotificationFrequency(struct { |
| notifications_per_ring uint32; |
| }) -> () error Error; |
| |
| /// Notifies all subscribed listeners when the device is commanded to Start |
| /// streaming. This can only occur after a device is fully configured |
| /// (format is set; ring buffer is established and fetched). |
| strict -> OnStart(struct { |
| start_time zx.Time; |
| }); |
| |
| /// Notifies all subscribed listeners when the device is commanded to Stop |
| /// streaming. This can only occur when the device is already Started. Stop |
| /// returns the device to a fully-configured state. Upon this command, the |
| /// already-set format and ring buffer are retained without change, but |
| /// position will re-begin at 0, if the device is again Started. |
| strict -> OnStop(struct { |
| stop_time zx.Time; |
| ring_position uint32; |
| }); |
| |
| /// Returns the current position (in bytes) within the ring buffer, along |
| /// with the time (per MONOTONIC clock) that corresponds with that position. |
| /// This can only be called after the ring buffer is established. Returns |
| /// `NOT_STARTED` if the device has not yet Started streaming. |
| strict GetPosition() -> (struct { |
| monotonic_time zx.Time; |
| ring_position uint32; // position in bytes |
| }) error Error; |
| |
| /// Notifies all subscribed listeners when any position notification is |
| /// issued by the driver. The frequency of these per-stream notifications is |
| /// set by whoever opened the device,, though the frequency can be overriden |
| /// by `SetNotificationFrequency`. |
| strict -> OnPositionNotify(struct { |
| monotonic_time zx.Time; |
| ring_position uint32; // position in bytes |
| }); |
| |
| /// Hot-plugs or hot-unplugs an active virtual device, at the specified |
| /// time. |
| strict ChangePlugState(struct { |
| plug_change_time zx.Time; |
| plugged bool; |
| }) -> () error Error; |
| |
| /// Immediately change the virtual device's clock rate, as expressed in the |
| /// timing and content of position notifications the driver emits. |
| /// 'ppm_monotonic' cannot exceed [-1000,+1000]. Each rate change in rate is |
| /// standalone; i.e. successive rate changes are not cumulative. |
| strict AdjustClockRate(struct { |
| ppm_from_monotonic int32; |
| }) -> () error Error; |
| }; |
| |
| type DeviceType = flexible enum { |
| /// Device supports the fuchsia.hardware.audio/StreamConfig protocol. |
| STREAM_CONFIG = 1; |
| |
| /// Device supports the fuchsia.hardware.audio/Dai protocol. |
| DAI = 2; |
| |
| /// Device supports the fuchsia.hardware.audio/Codec protocol. |
| CODEC = 3; |
| |
| /// Device supports the fuchsia.hardware.audio/Composite protocol. |
| COMPOSITE = 4; |
| }; |
| |
| type Error = flexible enum { |
| /// Unknown internal error occurred. |
| INTERNAL = 1; |
| |
| /// The ring buffer has not been created yet. |
| NO_RING_BUFFER = 2; |
| |
| /// The device has not yet started streaming. |
| NOT_STARTED = 3; |
| |
| /// The operation is not implemented, supported, or enabled. |
| NOT_SUPPORTED = 4; |
| |
| /// An argument is invalid. |
| INVALID_ARGS = 5; |
| }; |