| // 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.hardware.audio; |
| |
| using zx; |
| |
| /// Delay information as returned by the driver. |
| @available(added=HEAD) |
| type DelayInfo = table { |
| /// The driver's best estimate of the delay internal to the hardware it abstracts for |
| /// the chosen format. |
| /// |
| /// "Internal" refers to the hardware on the side of any hardware interconnect (DAI) |
| /// that provides the ring buffer, for instance an SoC audio subsystem, whereas "external" |
| /// refers to hardware on the other side of any hardware interconnect (DAI), for |
| /// instance hardware codecs. |
| /// |
| /// For a given frame during playback, this is any delay after the driver/HW copies it |
| /// out of the ring-buffer, before it exits any hardware interconnect. |
| /// For a given frame during recording, this is any delay before the driver/HW copies |
| /// it into the ring-buffer, after it enters the hardware interconnect. |
| /// |
| /// `internal_delay` must be taken into account by the client when determining either |
| /// the minimum lead time requirement (for playback) or the minimum capture delay (for |
| /// capture). |
| /// |
| /// This delay must not include the inherent delay added by the temporary buffering needed |
| /// to copy data in and out of the ring buffer, specified via `RingBufferProperties` |
| /// `driver_transfer_bytes`. |
| /// |
| /// Required. |
| 1: internal_delay zx.duration; |
| |
| /// The driver's best estimate of the delay external to the hardware it abstracts for |
| /// the chosen format. |
| /// |
| /// "Internal" refers to the hardware on the side of any hardware interconnect (DAI) |
| /// that provides the ring buffer, for instance an SoC audio subsystem, whereas "external" |
| /// refers to hardware on the other side of any hardware interconnect (DAI), for |
| /// instance hardware codecs. |
| /// |
| /// If not included `external_delay` is unknown. If unknown, the client may treat |
| /// `external_delay` as it chooses, e.g. may autodetect it, consider it zero, consider |
| /// it a large number, etc. |
| /// |
| /// `external_delay` must be taken into account by the client when determining either |
| /// the minimum lead time requirement (for playback) or the minimum capture delay (for |
| /// capture). |
| /// |
| /// This delay must not include the inherent delay added by the temporary buffering needed |
| /// to copy data in and out of the ring buffer, specified via `RingBufferProperties` |
| /// `driver_transfer_bytes`. |
| /// |
| 2: external_delay zx.duration; |
| }; |
| |
| /// Properties of the ring buffer. These values don't change once the ring buffer is created. |
| type RingBufferProperties = table { |
| /// The driver's best estimate of the external delay present in the presentation pipeline for |
| /// the chosen format. When precisely synchronizing presentation across multiple entities |
| /// (e.g. devices), the external delay should be taken into account. |
| /// If not included `external_delay` is unknown. |
| // |
| // # Deprecation |
| // |
| // Not needed anymore since the functionality is available via `WatchDelayInfo` below. |
| // Should match `DelayInfo` `external_delay` until it's removed. |
| @available(deprecated=9) |
| 1: external_delay zx.duration; |
| |
| /// Size (in bytes) representing a temporary buffer used by the driver in order to consume or |
| /// generate the ring buffer contents. Required. |
| /// The ring buffer contents must be produced and consumed at the rate specified with the |
| /// `CreateRingBuffer` command, however some amount of buffering is required when the data is |
| /// written into and read from the ring buffer. For playback the data is consumed by the driver |
| /// by reading ahead up to `fifo_depth` bytes. For capture the data is produced by the driver |
| /// holding up to `fifo_depth` bytes at the time before committing it to main system |
| /// memory. Hence `fifo_depth` must be taken into account by the client when determining either |
| /// the minimum lead time requirement (for playback) or the maximum capture delay (for capture). |
| /// |
| /// To convert `fifo_depth` to the corresponding number of audio frames, use the frame size |
| /// returned by `CreateRingBuffer` in the `StreamConfig` protocol, note that the `fifo_depth` |
| /// is not necessarily a multiple size of an audio frame. |
| /// |
| /// The ring buffer data may be directly consumed/generated by hardware, in this case |
| /// `fifo_depth` maps directly to the size of a hardware FIFO block, since the hardware FIFO |
| /// block determines the amount of data read ahead or held back. |
| /// |
| /// The ring buffer data may instead be consumed/generated by audio driver software that is |
| /// conceptually situated between the ring buffer and the audio hardware. In this case, for |
| /// playback the `fifo_depth` read ahead amount is set large enough such that the driver |
| /// guarantees no undetected underruns, this assuming the client is generating the data as |
| /// determined by the `CreateRingBuffer` and `Start` commands. For capture, the |
| /// `fifo_depth` held back amount is set large enough such that the driver guarantees no |
| /// undetected underruns when generating the data as determined by the `CreateRingBuffer` and |
| /// `Start` commands. The driver must set `fifo_depth` big enough such that the potential |
| /// delays added by any software interfacing with the audio hardware do not occur under most |
| /// scenarios, and must detect and report underruns. How an underrun is reported is not defined |
| /// in this API. |
| // |
| // # Deprecation |
| // |
| // Not needed anymore since the functionality is available via `driver_transfer_bytes` below. |
| @available(deprecated=9, removed=HEAD) |
| 2: fifo_depth uint32; |
| |
| /// When set to true, indicates that the ring buffer runs in a different cache coherency domain, |
| /// hence clients need to make sure that their data has been flushed all the way to main memory |
| /// (in the case of playback) or that their view of the ring buffer will need to be invalidated |
| /// before read (in the case of capture). |
| /// |
| /// When set to false, indicates that the ring buffer runs in the same cache coherency domain, |
| /// hence the driver does not require flushing/invalidation, as in there is no hardware external |
| /// to the CPUs reading/writing from/to main memory bypassing the CPUs. |
| /// Note that in this case the driver and client still need to make sure they synchronize their |
| /// access to the data, for instance inserting the appropriate acquire fences before reading |
| /// and release fences after writing. |
| /// |
| /// Required. |
| 3: needs_cache_flush_or_invalidate bool; |
| |
| /// The driver's best estimate of the amount of time it takes the hardware to actually start |
| /// playback/capture, after a channel is activated by `SetActiveChannels`. |
| /// Restated: the driver estimates that after `SetActiveChannels` enables a channel, its data |
| /// will resume flowing at approximately `set_time` + `turn_on_delay`. |
| /// It may take some time for the hardware to get into fully operational mode, for example due |
| /// a power state change or communication delays within the driver's multiple hardware entities |
| /// as in a Bluetooth driver. This delay must be taken into account if not getting the initial |
| /// audio samples played or captured is not acceptable. |
| /// If not included `turn_on_delay` is unknown. |
| 4: turn_on_delay zx.duration; |
| |
| /// Size (in bytes) representing a temporary buffer used by the driver/HW in order to transfer |
| /// (consume or produce) the ring buffer contents. |
| /// |
| /// The ring buffer contents must be produced and consumed at the rate specified with the |
| /// `CreateRingBuffer` command; however, some amount of buffering is required when the data is |
| /// written into and read from the ring buffer. For playback, the data is consumed by the driver |
| /// by reading ahead up to `driver_transfer_bytes`. For capture, the data is produced by the |
| /// driver holding up to `driver_transfer_bytes` at a time before committing it to the ring |
| /// buffer. |
| /// |
| /// Let's define ‘begin pointer’ and 'end pointer' as position pointers in the ring buffer, |
| /// represented as 'B' and 'E' in the figures below, that bound the portion of the ring buffer |
| /// that are unsafe for reading or writing by the client at a given point in time. |
| /// |
| /// ## Playback |
| /// |
| /// For playback, it is safe for a client to write only to the part of the ring buffer that is |
| /// not simultaneously being read by the driver/HW. Before the ring buffer is started, clients |
| /// of output devices may write initial data starting at the very beginning of the ring buffer |
| /// at least `driver_transfer_bytes` of audio since they must always stay at least that far |
| /// ahead of where the driver/HW is reading. Note that if the client does not write to the |
| /// beginning of the ring buffer, then it is relying on the zeroed-out contents of the VMO as |
| /// the initial audio to be read by the driver/HW. |
| /// It is therefore not safe for the client to copy additional data to the ring buffer between |
| /// 'B' and 'E', the part already in use which may or may not already be consumed. The client |
| /// may safely write to any other portion of the ring buffer from 'E' to '0/B'. |
| /// |
| /// ``` |
| /// Ring Buffer |
| /// +-------------------------+-------------------------------------------------------------+ |
| /// |<--- unsafe --->|<--- safe (empty) --->| |
| /// |<`driver_transfer_bytes`>| | |
| /// +-------------------------+-------------------------------------------------------------+ |
| /// 0/B E 0 |
| /// ``` |
| /// |
| /// As time passes, the driver/HW reads the data in chunks of unknown size (up to the amount of |
| /// `driver_transfer_bytes`) at the rate specified in `CreateRingBuffer`, and the begin/end |
| /// pointers move to the right of '0' at the same rate. As a result, the area to which it is |
| /// unsafe for the client to write moves gradually forward through the ring buffer, but |
| /// maintains a constant size equal to `driver_transfer_bytes`. For instance after some time we |
| /// now have: |
| /// |
| /// ``` |
| /// Ring Buffer |
| /// +------------+-------------------------+------------------------------------------------+ |
| /// [<-- safe -->|<--- unsafe --->|<-- safe (empty) -->| |
| /// | |<`driver_transfer_bytes`>| | |
| /// +------------+-------------------------+------------------------------------------------+ |
| /// 0 B E 0 |
| /// ``` |
| /// |
| /// In steady state, i.e. once the process has wrapped around the ring buffer, any area outside |
| /// of the pointers 'B' and 'E' is safe to write: |
| /// |
| /// ``` |
| /// Ring Buffer |
| /// +--------------------------------+-------------------------+----------------------------+ |
| /// [<-- safe -->|<--- unsafe --->|<-- safe -->| |
| /// | |<`driver_transfer_bytes`>| | |
| /// +--------------------------------+-------------------------+----------------------------+ |
| /// 0 B E 0 |
| /// ``` |
| /// |
| /// |
| /// ## Recording |
| /// |
| /// For recording, it is safe for a client to read only the part of the ring buffer that is not |
| /// simultaneously being written by the driver/HW. At the moment when the ring buffer is started |
| /// by the client, the driver has not written any amount of data to the ring buffer, so there |
| /// is nothing for the client to read: |
| /// |
| /// ``` |
| /// Ring Buffer |
| /// +---------------------------------------------------------------------------------------+ |
| /// [<--- empty -->| |
| /// +---------------------------------------------------------------------------------------+ |
| ///0/B/E 0 |
| /// ``` |
| /// |
| /// The driver/HW begins writing to the ring buffer starting at '0'. As time passes, the |
| /// driver/HW writes data in chunks of unknown size (up to `driver_transfer_bytes`) at the rate |
| /// specified in `CreateRingBuffer`. Until an amount of data equal to `driver_transfer_bytes` |
| /// has been written into the ring buffer by the driver/HW, it is not safe for the client to |
| /// read any data: |
| /// |
| /// ``` |
| /// Ring Buffer |
| /// +----------------+----------------------------------------------------------------------+ |
| /// [<--- unsafe --->|<-- empty -->| |
| /// |<`driver_transfer_bytes`>| | |
| /// +----------------+----------------------------------------------------------------------+ |
| /// 0/B E 0 |
| /// ``` |
| /// |
| /// Once `driver_transfer_bytes` of data have been copied into the ring buffer by the driver/HW, |
| /// it is safe for the client to read the data from '0' to 'B'. It is unsafe to read the data |
| /// from ‘B’ to ‘E’, the area in which the driver/HW is simultaneously writing. This unsafe |
| /// area moves gradually forward through the ring buffer, but maintains a constant size equal |
| /// to `driver_transfer_bytes`. For instance after some time we now have: |
| /// |
| /// ``` |
| /// Ring Buffer |
| /// +------------+-------------------------+------------------------------------------------+ |
| /// [<-- safe -->|<--- unsafe --->|<-- empty -->| |
| /// | |<`driver_transfer_bytes`>| | |
| /// +------------+-------------------------+------------------------------------------------+ |
| /// 0 B E 0 |
| /// ``` |
| /// |
| /// In steady state, i.e. once the process has wrapped around the ring buffer, any area outside |
| /// of the pointers 'B' and 'E' is safe to read: |
| /// |
| /// ``` |
| /// Ring Buffer |
| /// +--------------------------------+-------------------------+----------------------------+ |
| /// [<-- safe -->|<--- unsafe --->|<-- safe -->| |
| /// | |<`driver_transfer_bytes`>| | |
| /// +--------------------------------+-------------------------+----------------------------+ |
| /// 0 B E 0 |
| /// ``` |
| /// |
| /// ## Hardware versus software |
| /// |
| /// The ring buffer data may be directly consumed/generated by hardware, i.e. |
| /// `driver_transfer_bytes` can be mapped directly to the size of a hardware FIFO block, since a |
| /// hardware FIFO block determines the upper limit amount of data read ahead or held back. |
| /// Note that if the FIFO buffer is used in a "ping pong" manner (such that only half the FIFO |
| /// buffer size is copied at a time), and this is true not only on steady state but also the |
| /// very first time the driver copies data to or from the ring buffer, then |
| /// `driver_transfer_bytes` may be set to half the FIFO buffer size. If the hardware at any time |
| /// copies data to/from the ring buffer in an amount equal to the entire FIFO buffer size (for |
| /// instance the first copy from the ring buffer to fill an initially empty hardware FIFO), then |
| /// `driver_transfer_bytes` must be set to the entire size of the FIFO buffer. |
| /// |
| /// The ring buffer data may instead be consumed/generated by audio driver software that is |
| /// conceptually situated between the ring buffer and the audio hardware. In this case, for |
| /// playback, the `driver_transfer_bytes` read ahead amount is set large enough such that the |
| /// driver guarantees no undetected underruns, this assuming the client is generating the data |
| /// as determined by the `CreateRingBuffer` and `Start` commands. For capture, the |
| /// `driver_transfer_bytes` held back amount is set large enough such that the driver guarantees |
| /// no undetected underruns when generating the data as determined by the `CreateRingBuffer` and |
| /// `Start` commands. |
| /// |
| /// |
| /// This size must not include the impact of any delays caused by hardware or software |
| /// processing abstracted by the driver; those are communicated by the `internal_delay` and |
| /// `external_delay` fields in `DelayInfo` and are entirely orthogonal to this value. |
| /// |
| /// Required. |
| @available(added=HEAD) |
| 5: driver_transfer_bytes uint32; |
| }; |
| |
| type RingBufferPositionInfo = struct { |
| /// The driver's best estimate of the time (in the CLOCK_MONOTONIC timeline) at which the |
| /// playback/capture pointer reached the position indicated by `position`. |
| /// `turn_on_delay` impact should not be incorporated into 'timestamp'. |
| /// No delays indicated in `DelayInfo` should be incorporated. |
| timestamp zx.time; |
| |
| /// The playback/capture pointer position (in bytes) in the ring buffer at time |
| /// `timestamp` as estimated by the driver. |
| position uint32; |
| }; |
| |
| type GetVmoError = strict enum { |
| /// The ring buffer setup failed due to an invalid argument, e.g. min_frames is too big. |
| INVALID_ARGS = 1; |
| |
| /// The ring buffer setup failed due to an internal error. |
| INTERNAL_ERROR = 2; |
| }; |
| |
| /// For an overview see |
| /// [Audio Driver Streaming Interface](https://fuchsia.dev/fuchsia-src/concepts/drivers/driver_architectures/audio_drivers/audio_streaming) |
| protocol RingBuffer { |
| /// Accessor for top level static properties. |
| GetProperties() -> (struct { |
| properties RingBufferProperties; |
| }); |
| |
| /// Gets the ring buffer current position via a hanging get. The driver must respond to a |
| /// client's first `WatchClockRecoveryPositionInfo` call, but will not respond to subsequent |
| /// client calls until the position information has changed from what was most recently |
| /// provided to that client. The driver must not respond to a |
| /// `WatchClockRecoveryPositionInfo` until after it has replied to the `Start` command. If |
| /// `clock_recovery_notifications_per_ring` is not zero, the driver will reply with its |
| /// estimated position to be used for clock recovery at most at |
| /// `clock_recovery_notifications_per_ring` frequency. |
| /// `WatchClockRecoveryPositionInfo` may only be called after `GetVmo` was called, hence a |
| /// `clock_recovery_notifications_per_ring` was specified. |
| /// Must be delivered with timestamps that are monotonically increasing. |
| WatchClockRecoveryPositionInfo() -> (struct { |
| position_info RingBufferPositionInfo; |
| }); |
| |
| /// Requests a shared buffer to be used for moving bulk audio data between client and driver. |
| /// The client requests `min_frames` as the size for part of the ring buffer it needs. |
| /// The driver returns the actual size of allocated ring buffer space in `num_frames`. |
| /// |
| /// `num_frames` must be at least `min_frames` plus `driver_transfer_bytes` (in frames) such |
| /// that ring buffer contents can be transfered in and out, or else the call must be failed |
| /// with GetVmoError.INVALID_ARGS. |
| /// |
| /// The driver may increase the ring buffer size beyond `min_frames` plus |
| /// `driver_transfer_bytes` (in frames) due to any internal requirements, for instance |
| /// alignment. |
| /// |
| /// Clients can treat the entire returned ring buffer as safe to access, except for the |
| /// `driver_transfer_bytes` immediately adjacent to the current position, see the |
| /// `driver_transfer_bytes` parameter specification in `RingBufferProperties` for more details. |
| /// |
| /// If `clock_recovery_notifications_per_ring` is non-zero, the driver will send replies to |
| /// `WatchClockRecoveryPositionInfo` client requests at most at |
| /// `clock_recovery_notifications_per_ring` frequency. These notifications are meant to be used |
| /// for clock recovery. |
| /// |
| // TODO(fxbug.dev/116353): Reconsider the `clock_recovery_notifications_per_ring` parameter, |
| // once we must recover a clock from a device being actively rate-adjusted in hardware. |
| GetVmo(struct { |
| min_frames uint32; |
| clock_recovery_notifications_per_ring uint32; |
| }) -> (resource struct { |
| num_frames uint32; |
| ring_buffer zx.handle:VMO; |
| }) error GetVmoError; |
| |
| /// Start the ring buffer. The `start_time` value (in the CLOCK_MONOTONIC timeline) indicates |
| /// when position began moving, starting at the beginning of the ring buffer. |
| /// external_delay impact should not be incorporated into 'start_time'. |
| /// If `Start` is called before `SetActiveChannels`, then by default all channels are active. |
| /// If `Start` is called before `GetVmo`, the channel must be closed with `ZX_ERR_BAD_STATE`. |
| /// If `Start` is called while this RingBuffer is already started, the channel must be closed |
| /// with an error `ZX_ERR_BAD_STATE` returned. |
| Start() -> (struct { |
| start_time zx.time; |
| }); |
| |
| /// Stop the ring buffer. Once this call's response is received, no further position |
| /// notifications will be sent until `Start` is called again. |
| /// If `Stop` is called before `GetVmo`, the channel must be closed with `ZX_ERR_BAD_STATE`. |
| // TODO(fxbug.dev/39494): Add timestamp parameter. |
| Stop() -> (); |
| |
| /// Sets which channels are active via a bitmask. |
| /// The least significant bit corresponds to channel index 0. |
| /// Channels not set (bits are 0) in the bitmask are inactive. |
| /// Inactive channels indicate to the driver that it may turn off hardware associated with the |
| /// inactive channels. A subsequent `SetActiveChannels` setting an inactive channel to active |
| /// may incur in a `turn_on_delay` to actually restart playback/capture of the channels. |
| /// The total number of channels is the `number_of_channels` in `Format`, specifically in |
| /// `PcmFormat`, i.e. this bitmask has up to `number_of_channels` bits set (maximum 64). |
| /// Deactivating one, several, or all channels does not `Stop` the ring buffer. |
| /// `SetActiveChannels` does not change the ring buffer's behavior with regard to |
| /// `Start`/`Stop`, specifically position. Once `Start` is called, a ring buffer's position |
| /// advances (and position notifications sent as needed) regardless of the number of active |
| /// channels, including if no channels are active. This means that the format in the |
| /// ring buffer is not changed. |
| /// By default all channels are active. |
| /// If the driver does not support deactivating channels it must return `ZX_ERR_NOT_SUPPORTED`. |
| /// If the mask is incorrect, i.e. enables channels outside the number of bits |
| /// to use for a given `number_of_channels`, then the driver must return `ZX_ERR_INVALID_ARGS`. |
| /// The `set_time` value (in the CLOCK_MONOTONIC timeline) indicates when configuring |
| /// the hardware to activate or deactivate channels is completed. `set_time` does not include |
| /// the potential `turn_on_delay`, the driver does not delay the reply waiting for the |
| /// hardware to actually turn on, the driver replies with a `set_time` indicating when the |
| /// hardware configuration was completed. |
| /// For input channels, it is not required that the driver zero-out inactive channels. |
| SetActiveChannels(struct { |
| active_channels_bitmask uint64; |
| }) -> (struct { |
| set_time zx.time; |
| }) error zx.status; |
| |
| /// Get information about delays via a hanging get. The driver will immediately reply to the |
| /// first `WatchDelayInfo` sent by the client. The driver will not respond to subsequent client |
| /// `WatchDelayInfo` calls until the delay info changes from what was most recently reported. |
| @available(added=HEAD) |
| WatchDelayInfo() -> (struct { |
| delay_info DelayInfo; |
| }); |
| }; |