blob: 3847b1eee4758bebbce2836a9a154742d41211b8 [file] [log] [blame]
// 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;
};