| // Copyright 2022 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.audio.device; |
| |
| using fuchsia.audio; |
| using fuchsia.hardware.audio; |
| using fuchsia.hardware.audio.signalprocessing; |
| using zx; |
| |
| /// Parameters specified by a caller when creating a ring buffer. |
| type RingBufferOptions = table { |
| /// The format (sample format, channelization, frame rate) of the ring |
| /// buffer to be created. |
| /// |
| /// Required. |
| // |
| // TODO(https://fxbug.dev/42056326): Use a union, to accommodate non-PCM |
| 1: format fuchsia.audio.Format; |
| |
| /// The minimum number of bytes required in the ring buffer. The actual |
| /// buffer may be larger, as required by the encoding, driver, device or OS. |
| /// |
| /// Required. |
| 2: ring_buffer_min_bytes uint32; |
| }; |
| |
| /// Information about the ring buffer or associated audio stream. |
| type RingBufferProperties = table { |
| /// The number of bits (starting with the most significant) that are valid, |
| /// within each individual sample. This may be be smaller than the actual |
| /// sample size, in the case of an input ring buffer fed by an 18-bit ADC |
| /// for example. Any additional bits of precision should be ignored. |
| /// |
| /// Required. |
| // |
| // TODO(https://fxbug.dev/42056326): Adopt `fuchsia.mediastreams.AudioEncoding`, and |
| // change to the following comment: |
| // Optional (Required for PCM encodings, including 'packed' formats). |
| 1: valid_bits_per_sample uint8; |
| |
| /// The maximum delay until disabled channels become fully operational, |
| /// after calling `SetActiveChannels`. This is the worst-case duration when |
| /// reenabling all channels. The value must be non-negative. |
| /// |
| /// Required. |
| 2: turn_on_delay zx.Duration; |
| }; |
| |
| type DelayInfo = table { |
| /// The driver's best estimate of the delay internal to the hardware it abstracts for |
| /// the chosen format. This duration must be non-negative. |
| /// |
| /// Required. |
| 1: internal_delay zx.Duration; |
| |
| /// The amount of pipeline delay beyond the interconnect (subsequent to the |
| /// DMA "read" position for output devices, or prior to the DMA "write" |
| /// position for input devices). If present, this duration must be non-negative. |
| /// |
| /// Optional. |
| 2: external_delay zx.Duration; |
| }; |
| |
| /// A `ControlCreator` interface creates `Control` instances. Each `Control` binds |
| /// to a single device. A device can only be bound to one `Control` at any time. |
| @discoverable |
| closed protocol ControlCreator { |
| /// Create a `Control` for the specified device. |
| strict Create(resource table { |
| /// The token id for the device to be controlled. |
| /// |
| /// Required. |
| 1: token_id TokenId; |
| |
| /// The server_end of the `Control` to be created. |
| /// |
| /// Required. |
| 2: control_server server_end:Control; |
| }) -> (table {}) error ControlCreatorError; |
| }; |
| |
| /// Errors returned by `ControlCreator/Create`. |
| type ControlCreatorError = flexible enum { |
| /// The required `token_id` is missing. |
| INVALID_TOKEN_ID = 1; |
| |
| /// The required `control_server` is missing. |
| INVALID_CONTROL = 2; |
| |
| /// No device with `token_id` was found. Either this token has never been |
| /// used, or the device with `token_id` has been removed. |
| DEVICE_NOT_FOUND = 3; |
| |
| /// The device with `token_id` encountered an error and cannot be controlled. |
| DEVICE_ERROR = 4; |
| |
| /// A `Control` associated with `token_id` already exists. This device is |
| /// already being actively controlled. |
| ALREADY_ALLOCATED = 5; |
| }; |
| |
| /// A `Control` instance is used to change the settings or state of an audio |
| /// device. It also creates the ring buffer used to pass audio data between |
| /// client and device. Each `Control` is associated with an initialized audio |
| /// device; conversely each device is associated with either zero or one |
| /// `Control` at any time. |
| closed protocol Control { |
| /// Change the processing topology (via `SetTopology`) or the state of a |
| /// single processing node (via `SetElementState`). |
| compose fuchsia.hardware.audio.signalprocessing.SignalProcessing; |
| |
| /// Change the device's overall gain state. |
| /// |
| /// Should only be called for StreamConfig devices. |
| // |
| // TODO(https://fxbug.dev/42052918): Remove legacy gain aspects once driver API does. |
| // Going forward, gain will be handled by `SignalProcessing`. |
| strict SetGain(table { |
| /// The gain state to be set. |
| /// |
| /// Required. |
| 1: target_state GainState; |
| }) -> (table {}) error ControlSetGainError; |
| |
| /// Create the ring buffer used to pass audio to/from this device. If the device is |
| /// Composite, then the targeted RING_BUFFER ENDPOINT must be identified by `element_id`. |
| /// |
| /// Should only be called for Composite and StreamConfig devices. |
| strict CreateRingBuffer(resource table { |
| /// The element ID for an `ENDPOINT` of type `RING_BUFFER`. |
| /// |
| /// Required for Composite; ignored for StreamConfig. |
| 1: element_id ElementId; |
| |
| /// Additional requirements about the actual ring buffer being created. |
| /// |
| /// Required. |
| 2: options RingBufferOptions; |
| |
| /// The server_end of the `RingBuffer` to be created. |
| /// |
| /// Required. |
| 3: ring_buffer_server server_end:RingBuffer; |
| }) -> (resource table { |
| /// Properties about the ring buffer and active audio stream as created. |
| 1: properties RingBufferProperties; |
| |
| /// An object that represents the audio stream and ring memory itself. |
| /// Note: ring-buffer VMO memory ranges must be cache-invalidated before |
| /// each read, and cache-flushed after each write. |
| 2: ring_buffer fuchsia.audio.RingBuffer; |
| }) error ControlCreateRingBufferError; |
| |
| /// Set the wire format for the digital interconnect connected to this Codec endpoint. |
| /// This method returns information related to the format that was set, including delay values. |
| /// If the device is Composite, then the targeted DAI_INTERCONNECT ENDPOINT must be identified |
| /// by `element_id`. |
| /// |
| /// Should only be called for Codec and Composite devices. |
| strict SetDaiFormat(table { |
| /// The element ID for an `ENDPOINT` of type `DAI_INTERCONNECT`. |
| /// |
| /// Required for Composite; ignored for Codec. |
| 1: element_id ElementId; |
| |
| 2: dai_format fuchsia.hardware.audio.DaiFormat; |
| }) -> (table { |
| 1: state fuchsia.hardware.audio.CodecFormatInfo; |
| }) error ControlSetDaiFormatError; |
| |
| /// Start the Codec hardware. If successful, this returns after the Codec was started and |
| /// `start_time` indicates the time when the hardware started. Note that the Codec's DaiFormat |
| /// must be set (by a successful `SetDaiFormat` call) before calling this method. |
| /// |
| /// Should only be called for Codec devices. |
| strict CodecStart() -> (table { |
| 1: start_time zx.Time; |
| }) error ControlCodecStartError; |
| |
| /// Stop the Codec hardware. If successful, this returns after the Codec was stopped and |
| /// `stop_time` indicates the time when the hardware stopped. Note that the Codec's DaiFormat |
| /// must be set (by a successful `SetDaiFormat` call) before calling this method. |
| /// |
| /// Should only be called for Codec devices. |
| strict CodecStop() -> (table { |
| 1: stop_time zx.Time; |
| }) error ControlCodecStopError; |
| |
| /// Reset the hardware -- stopping the hardware, releasing any ring buffers, and clearing any |
| /// DaiFormats or RingBufferFormats that were set. |
| /// |
| /// This method returns when the hardware reset is complete. |
| /// After calling this method, the device is still controlled, but any ring buffers must be |
| /// re-created and re-started. |
| /// For devices with DAI_INTERCONNECTs (such as Codecs and some Composites), `SetDaiFormat` and |
| /// `CodecStart` must be called again (in that order) to return the interconnect to the active |
| /// operational mode. |
| /// As applicable, `SetTopology` and `SetElementState` must also be called. |
| /// |
| /// Should only be called for Codec and Composite devices. |
| strict Reset() -> (table {}) error ControlResetError; |
| }; |
| |
| /// Errors returned by `Control/SetGain`. |
| type ControlSetGainError = flexible enum { |
| /// This device has encountered an error and can no longer be controlled. |
| DEVICE_ERROR = 1; |
| |
| /// This device type does not support the method that was called. |
| WRONG_DEVICE_TYPE = 2; |
| |
| /// The required `target_state` is missing. |
| INVALID_GAIN_STATE = 3; |
| |
| /// The required `target_state.gain_db` is missing. |
| INVALID_GAIN_DB = 4; |
| |
| /// The specified gain is outside the device's allowed range. |
| GAIN_OUT_OF_RANGE = 5; |
| |
| /// MUTE is requested, but the device has no MUTE control. |
| MUTE_UNAVAILABLE = 6; |
| |
| /// Enabling AGC is requested, but the device has no AGC. |
| AGC_UNAVAILABLE = 7; |
| }; |
| |
| /// Errors returned by `Control/CreateRingBuffer`. |
| type ControlCreateRingBufferError = flexible enum { |
| /// This device has encountered an error and can no longer be controlled. |
| DEVICE_ERROR = 1; |
| |
| /// This device type does not support the method that was called. |
| WRONG_DEVICE_TYPE = 2; |
| |
| /// The previous `CreateRingBuffer` call has not yet completed. |
| ALREADY_PENDING = 3; |
| |
| /// The required `element_id` is missing or does not refer to a `RING_BUFFER` element. |
| INVALID_ELEMENT_ID = 4; |
| |
| /// The required `options` is missing. |
| INVALID_OPTIONS = 5; |
| |
| /// The required `options.format` is missing. |
| INVALID_FORMAT = 6; |
| |
| /// The required `options.ring_buffer_min_bytes` is missing. |
| INVALID_MIN_BYTES = 7; |
| |
| /// The required `ring_buffer_server` is missing. |
| INVALID_RING_BUFFER = 8; |
| |
| /// An active `RingBuffer` instance already exists for this `Control`. |
| ALREADY_ALLOCATED = 9; |
| |
| /// The device does not support the specified format. |
| FORMAT_MISMATCH = 10; |
| |
| /// The device cannot create a ring buffer with the specified options. |
| BAD_RING_BUFFER_OPTION = 11; |
| |
| /// The driver returned some other error. This call may be retried. |
| OTHER = 12; |
| }; |
| |
| /// Errors returned by `Control/SetDaiFormat`. |
| type ControlSetDaiFormatError = flexible enum { |
| /// This device has encountered an error and can no longer be controlled. |
| DEVICE_ERROR = 1; |
| |
| /// This device type does not support the method that was called. |
| WRONG_DEVICE_TYPE = 2; |
| |
| /// The previous `SetDaiFormat` call has not yet completed. |
| ALREADY_PENDING = 3; |
| |
| /// The required `element_id` is missing or does not refer to a `DAI_INTERCONNECT` element. |
| INVALID_ELEMENT_ID = 4; |
| |
| /// The required `dai_format` is missing or invalid. |
| INVALID_DAI_FORMAT = 5; |
| |
| /// The device does not support the specified dai_format. |
| FORMAT_MISMATCH = 6; |
| |
| /// The driver returned some other error. This call may be retried. |
| OTHER = 7; |
| }; |
| |
| /// Errors returned by `Control/CodecStart`. |
| type ControlCodecStartError = flexible enum { |
| /// This device has encountered an error and can no longer be controlled. |
| DEVICE_ERROR = 1; |
| |
| /// This device type does not support the method that was called. |
| WRONG_DEVICE_TYPE = 2; |
| |
| /// The previous `CodecStart` call has not yet completed. |
| ALREADY_PENDING = 3; |
| |
| /// `SetDaiFormat` was not called before making this call. |
| DAI_FORMAT_NOT_SET = 4; |
| |
| /// The device was already started when this call was made. |
| ALREADY_STARTED = 5; |
| |
| /// The driver returned some other error. This call may be retried. |
| OTHER = 6; |
| }; |
| |
| /// Errors returned by `Control/CodecStop`. |
| type ControlCodecStopError = flexible enum { |
| /// This device has encountered an error and can no longer be controlled. |
| DEVICE_ERROR = 1; |
| |
| /// This device type does not support the method that was called. |
| WRONG_DEVICE_TYPE = 2; |
| |
| /// The previous `CodecStop` call has not yet completed. |
| ALREADY_PENDING = 3; |
| |
| /// `SetDaiFormat` was not called before making this call. |
| DAI_FORMAT_NOT_SET = 4; |
| |
| /// The device was already stopped when this call was made. |
| ALREADY_STOPPED = 5; |
| |
| /// The driver returned some other error. This call may be retried. |
| OTHER = 6; |
| }; |
| |
| /// Errors returned by `Control/CodecReset`. |
| type ControlResetError = flexible enum { |
| /// This device has encountered an error and can no longer be controlled. |
| DEVICE_ERROR = 1; |
| |
| /// This device type does not support the method that was called. |
| WRONG_DEVICE_TYPE = 2; |
| }; |
| |
| /// A `RingBuffer` instance controls data flow for the associated audio stream. |
| closed protocol RingBuffer { |
| /// Request that specific individual channels be powered down/up, if the |
| /// device supports this. This is intended for idle power conservation. |
| /// |
| /// Channels are specified by bitmask; the least significant bit corresponds |
| /// to channel 0. Each bit not set indicates that the channel can be |
| /// deactivated. `SetActiveChannels` does not change how a ring buffer |
| /// responds to `Start`/`Stop`, specifically with regards to position. |
| /// |
| /// Devices are not required to obey `SetActiveChannels`. For example, they |
| /// are not required to zero-out an input stream's inactive channels, and |
| /// data written to inactive channels of an output stream's ring buffer may |
| /// still be played. |
| /// |
| /// If not called, then by default all channels will be active. |
| strict SetActiveChannels(table { |
| /// The channels to be activated (all others should be deactivated). No |
| /// bit should be set above the `channel_count` specified in the ring |
| /// buffer format (e.g. for a four-channel stream, `channel_bitmask` |
| /// must be in the [0x00, 0x0F] range). |
| /// |
| /// Required. |
| 1: channel_bitmask uint64; |
| }) -> (table { |
| /// The CLOCK_MONOTONIC time when the hardware was configured. Note: |
| /// this does not include the effects of `turn_on_delay` on streams. |
| /// |
| /// Required. |
| 1: set_time zx.Time; |
| }) error RingBufferSetActiveChannelsError; |
| |
| /// Start the ring buffer, beginning at the first frame of the ring buffer. |
| strict Start(table {}) -> (table { |
| /// The CLOCK_MONOTONIC time when the stream was started. |
| /// |
| /// Required. |
| 1: start_time zx.Time; |
| }) error RingBufferStartError; |
| |
| /// Stop the ring buffer. |
| strict Stop(table {}) -> (table {}) error RingBufferStopError; |
| |
| /// Request delay information via a hanging get. The RingBuffer will respond |
| /// immediately to the first `WatchDelayInfo` call. Subsequent calls will |
| /// only be completed when the delay info has changed from previously |
| /// communicated values. |
| strict WatchDelayInfo() -> (table { |
| /// Required. |
| 1: delay_info DelayInfo; |
| }) error RingBufferWatchDelayInfoError; |
| }; |
| |
| /// Errors returned by `RingBuffer/SetActiveChannels`. |
| type RingBufferSetActiveChannelsError = flexible enum { |
| /// This device has encountered an error and can no longer be controlled. |
| DEVICE_ERROR = 1; |
| |
| /// The previous `SetActiveChannels` call has not yet completed. |
| ALREADY_PENDING = 2; |
| |
| /// The device does not support `SetActiveChannels`. Individual channels |
| /// cannot be deactivated (all channels are always active). |
| METHOD_NOT_SUPPORTED = 3; |
| |
| /// The required `channel_bitmask` is missing. |
| INVALID_CHANNEL_BITMASK = 4; |
| |
| /// The passed `channel_bitmask` specifies channels that are beyond the |
| /// range of channels currently configured for this ring buffer. |
| CHANNEL_OUT_OF_RANGE = 5; |
| }; |
| |
| /// Errors returned by `RingBuffer/Start`. |
| type RingBufferStartError = flexible enum { |
| /// This device has encountered an error and can no longer be controlled. |
| DEVICE_ERROR = 1; |
| |
| /// The previous `Start` call has not yet completed. |
| ALREADY_PENDING = 2; |
| |
| /// `Start` was called on a ring buffer that is already started. |
| ALREADY_STARTED = 3; |
| }; |
| |
| /// Errors returned by `RingBuffer/Stop`. |
| type RingBufferStopError = flexible enum { |
| /// This device has encountered an error and can no longer be controlled. |
| DEVICE_ERROR = 1; |
| |
| /// The previous `Stop` call has not yet completed. |
| ALREADY_PENDING = 2; |
| |
| /// `Stop` was called on a ring buffer that is already stopped. |
| ALREADY_STOPPED = 3; |
| }; |
| |
| /// Errors returned by `RingBuffer/WatchDelayInfo`. |
| type RingBufferWatchDelayInfoError = flexible enum { |
| /// This device has encountered an error and can no longer be observed. |
| DEVICE_ERROR = 1; |
| |
| /// The previous `WatchDelayInfo` call has not yet completed. |
| ALREADY_PENDING = 2; |
| }; |