| // Copyright 2017 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. |
| |
| #pragma once |
| |
| #include <assert.h> |
| #include <zircon/compiler.h> |
| #include <zircon/device/ioctl.h> |
| #include <zircon/device/ioctl-wrapper.h> |
| #include <zircon/types.h> |
| |
| #define AUDIO_IOCTL_GET_CHANNEL IOCTL(IOCTL_KIND_GET_HANDLE, 0xFE, 0x00) |
| IOCTL_WRAPPER_OUT(ioctl_audio_get_channel, AUDIO_IOCTL_GET_CHANNEL, zx_handle_t); |
| |
| // When communicating with an Audio driver using zx_channel_call, do not use |
| // the AUDIO_INVALID_TRANSACTION_ID as your message's transaction ID. It is |
| // reserved for async notifications sent from the driver to the application. |
| #define AUDIO_INVALID_TRANSACTION_ID ((zx_txid_t)0) |
| |
| __BEGIN_CDECLS |
| |
| typedef enum audio_cmd { |
| // Commands sent on the stream channel |
| AUDIO_STREAM_CMD_GET_FORMATS = 0x1000, |
| AUDIO_STREAM_CMD_SET_FORMAT = 0x1001, |
| AUDIO_STREAM_CMD_GET_GAIN = 0x1002, |
| AUDIO_STREAM_CMD_SET_GAIN = 0x1003, |
| AUDIO_STREAM_CMD_PLUG_DETECT = 0x1004, |
| AUDIO_STREAM_CMD_GET_UNIQUE_ID = 0x1005, |
| AUDIO_STREAM_CMD_GET_STRING = 0x1006, |
| |
| // Async notifications sent on the stream channel. |
| AUDIO_STREAM_PLUG_DETECT_NOTIFY = 0x2000, |
| |
| // Commands sent on the ring buffer channel |
| AUDIO_RB_CMD_GET_FIFO_DEPTH = 0x3000, |
| AUDIO_RB_CMD_GET_BUFFER = 0x3001, |
| AUDIO_RB_CMD_START = 0x3002, |
| AUDIO_RB_CMD_STOP = 0x3003, |
| |
| // Async notifications sent on the ring buffer channel. |
| AUDIO_RB_POSITION_NOTIFY = 0x4000, |
| |
| // Flags used to modify commands. |
| AUDIO_FLAG_NO_ACK = 0x80000000, |
| } audio_cmd_t; |
| |
| static_assert(sizeof(audio_cmd_t) == sizeof(uint32_t), |
| "audio_cmd_t must be 32 bits!"); |
| |
| typedef struct audio_cmd_hdr { |
| zx_txid_t transaction_id; |
| audio_cmd_t cmd; |
| } audio_cmd_hdr_t; |
| |
| static_assert(sizeof(audio_cmd_hdr_t) == 8, |
| "audio_cmd_hdr_t should be 12 bytes!"); |
| |
| // audio_sample_format_t |
| // |
| // Bitfield which describes audio sample format as they reside in memory. |
| // |
| typedef enum audio_sample_format { |
| AUDIO_SAMPLE_FORMAT_BITSTREAM = (1u << 0), |
| AUDIO_SAMPLE_FORMAT_8BIT = (1u << 1), |
| AUDIO_SAMPLE_FORMAT_16BIT = (1u << 2), |
| AUDIO_SAMPLE_FORMAT_20BIT_PACKED = (1u << 4), |
| AUDIO_SAMPLE_FORMAT_24BIT_PACKED = (1u << 5), |
| AUDIO_SAMPLE_FORMAT_20BIT_IN32 = (1u << 6), |
| AUDIO_SAMPLE_FORMAT_24BIT_IN32 = (1u << 7), |
| AUDIO_SAMPLE_FORMAT_32BIT = (1u << 8), |
| AUDIO_SAMPLE_FORMAT_32BIT_FLOAT = (1u << 9), |
| |
| AUDIO_SAMPLE_FORMAT_FLAG_UNSIGNED = (1u << 30), |
| AUDIO_SAMPLE_FORMAT_FLAG_INVERT_ENDIAN = (1u << 31), |
| AUDIO_SAMPLE_FORMAT_FLAG_MASK = AUDIO_SAMPLE_FORMAT_FLAG_UNSIGNED | |
| AUDIO_SAMPLE_FORMAT_FLAG_INVERT_ENDIAN, |
| } audio_sample_format_t; |
| |
| static_assert(sizeof(audio_sample_format_t) == sizeof(uint32_t), |
| "audio_sample_format_t must be 32 bits!"); |
| |
| // audio_stream_format_range_t |
| // |
| // A structure used along with the AUDIO_STREAM_CMD_GET_FORMATS command in order |
| // to describe the formats supported by an audio stream. |
| #define ASF_RANGE_FLAG_FPS_CONTINUOUS ((uint16_t)(1u << 0)) |
| #define ASF_RANGE_FLAG_FPS_48000_FAMILY ((uint16_t)(1u << 1)) |
| #define ASF_RANGE_FLAG_FPS_44100_FAMILY ((uint16_t)(1u << 2)) |
| typedef struct audio_stream_format_range { |
| audio_sample_format_t sample_formats; |
| uint32_t min_frames_per_second; |
| uint32_t max_frames_per_second; |
| uint8_t min_channels; |
| uint8_t max_channels; |
| uint16_t flags; |
| } __PACKED audio_stream_format_range_t; |
| |
| static_assert(sizeof(audio_stream_format_range_t) == 16, |
| "audio_stream_format_range_t should be 16 bytes!"); |
| |
| // audio_set_gain_flags_t |
| // |
| // Flags used by the AUDIO_STREAM_CMD_SET_GAIN message. |
| // |
| typedef enum audio_set_gain_flags { |
| AUDIO_SGF_MUTE_VALID = 0x1, // Whether or not the mute flag is valid. |
| AUDIO_SGF_AGC_VALID = 0x2, // Whether or not the agc flag is valid. |
| AUDIO_SGF_GAIN_VALID = 0x4, // Whether or not the gain float is valid. |
| AUDIO_SGF_MUTE = 0x40000000, // Whether or not to mute the stream. |
| AUDIO_SGF_AGC = 0x80000000, // Whether or not enable AGC for the stream. |
| } audio_set_gain_flags_t; |
| |
| static_assert(sizeof(audio_set_gain_flags_t) == sizeof(uint32_t), |
| "audio_set_gain_flags_t must be 32 bits!"); |
| |
| // audio_pd_flags_t |
| // |
| // Flags used by AUDIO_STREAM_CMD_PLUG_DETECT commands to enable or disable |
| // asynchronous plug detect notifications. |
| // |
| typedef enum audio_pd_flags { |
| AUDIO_PDF_NONE = 0, |
| AUDIO_PDF_ENABLE_NOTIFICATIONS = 0x40000000, |
| AUDIO_PDF_DISABLE_NOTIFICATIONS = 0x80000000, |
| } audio_pd_flags_t; |
| |
| static_assert(sizeof(audio_pd_flags_t) == sizeof(uint32_t), |
| "audio_pd_flags_t must be 32 bits!"); |
| |
| // audio_pd_notify_flags_t |
| // |
| // Flags used by responses to the AUDIO_STREAM_CMD_PLUG_DETECT |
| // message, and by AUDIO_STREAM_PLUG_DETECT_NOTIFY messages. |
| // |
| typedef enum audio_pd_notify_flags { |
| AUDIO_PDNF_HARDWIRED = 0x1, // Stream is hardwired (will always be plugged in) |
| AUDIO_PDNF_CAN_NOTIFY = 0x2, // Stream is able to notify of plug state changes. |
| AUDIO_PDNF_PLUGGED = 0x80000000, // Stream is currently plugged in. |
| } audio_pd_notify_flags_t; |
| |
| static_assert(sizeof(audio_pd_notify_flags_t) == sizeof(uint32_t), |
| "audio_pd_resp_flags_t must be 32 bits!"); |
| |
| // AUDIO_STREAM_CMD_GET_FORMATS |
| // |
| // May not be used with the NO_ACK flag. |
| #define AUDIO_STREAM_CMD_GET_FORMATS_MAX_RANGES_PER_RESPONSE (15u) |
| typedef struct audio_stream_cmd_get_formats_req { |
| audio_cmd_hdr_t hdr; |
| } audio_stream_cmd_get_formats_req_t; |
| |
| // TODO(johngro) : Figure out if zx_txid_t is ever going to go up to 8 bytes or |
| // not. If it is, just remove the _pad field below. If not, either keep it as |
| // a _pad field, or repurpose it for some flags of some form. Right now, we use |
| // it to make sure that format_ranges is aligned to a 16 byte boundary. |
| typedef struct audio_stream_cmd_get_formats_resp { |
| audio_cmd_hdr_t hdr; |
| uint32_t _pad; |
| uint16_t format_range_count; |
| uint16_t first_format_range_ndx; |
| audio_stream_format_range_t format_ranges[AUDIO_STREAM_CMD_GET_FORMATS_MAX_RANGES_PER_RESPONSE]; |
| } audio_stream_cmd_get_formats_resp_t; |
| |
| static_assert(sizeof(audio_stream_cmd_get_formats_resp_t) == 256, |
| "audio_stream_cmd_get_formats_resp_t must be 256 bytes"); |
| |
| // AUDIO_STREAM_CMD_SET_FORMAT |
| // |
| // May not be used with the NO_ACK flag. |
| typedef struct audio_stream_cmd_set_format_req { |
| audio_cmd_hdr_t hdr; |
| uint32_t frames_per_second; |
| audio_sample_format_t sample_format; |
| uint16_t channels; |
| } audio_stream_cmd_set_format_req_t; |
| |
| typedef struct audio_stream_cmd_set_format_resp { |
| audio_cmd_hdr_t hdr; |
| zx_status_t result; |
| uint64_t external_delay_nsec; |
| |
| // Note: Upon success, a channel used to control the audio buffer will also |
| // be returned. |
| } audio_stream_cmd_set_format_resp_t; |
| |
| // AUDIO_STREAM_CMD_GET_GAIN |
| // |
| // Request that a gain notification be sent with the current details of the |
| // streams current gain settings as well as gain setting capabilities. |
| // |
| // May not be used with the NO_ACK flag. |
| typedef struct audio_stream_cmd_get_gain_req { |
| audio_cmd_hdr_t hdr; |
| } audio_stream_cmd_get_gain_req_t; |
| |
| typedef struct audio_stream_cmd_get_gain_resp { |
| // TODO(johngro) : Is there value in exposing the gain step to the level |
| // above the lowest level stream interface, or should we have all drivers |
| // behave as if they have continuous control at all times? |
| audio_cmd_hdr_t hdr; |
| |
| bool cur_mute; // True if the stream is currently muted. |
| bool cur_agc; // True if the stream has AGC currently enabled. |
| float cur_gain; // The current setting gain of the stream in dB |
| |
| bool can_mute; // True if the stream is capable of muting |
| bool can_agc; // True if the stream has support for AGC |
| float min_gain; // The minimum valid gain setting, in dB |
| float max_gain; // The maximum valid gain setting, in dB |
| float gain_step; // The smallest valid gain increment, counted from the minimum gain. |
| } audio_stream_cmd_get_gain_resp_t; |
| |
| // AUDIO_STREAM_CMD_SET_GAIN |
| // |
| // Request that a stream change its gain settings to most closely match those |
| // requested. Gain values for Valid requests will be rounded to the nearest |
| // gain step. For example, if a stream can control its gain on the range from |
| // -60.0 to 0.0 dB, a request to set the gain to -33.3 dB will result in a gain |
| // of -33.5 being applied. |
| // |
| // Gain change requests outside of the capabilities of the stream's |
| // amplifier will be rejected with a result of ZX_ERR_INVALID_ARGS. Using the |
| // previous example, requests for gains of -65.0 or +3dB would be rejected. |
| // Similarly, If an amplifier is capable of gain control but cannot mute, a |
| // request to mute will be rejected. |
| // |
| // TODO(johngro) : Is this the correct behavior? Should we just apply sensible |
| // limits instead? IOW - If the user requests a gain of -1000 dB, should we |
| // just set the gain to -60dB? Likewise, if they request mute but the amplifier |
| // has no hard mute feature, should we just set the gain to the minimum |
| // permitted gain? |
| // |
| // May be used with the NO_ACK flag. |
| typedef struct audio_stream_cmd_set_gain_req { |
| audio_cmd_hdr_t hdr; |
| audio_set_gain_flags_t flags; |
| float gain; |
| } audio_stream_cmd_set_gain_req_t; |
| |
| typedef struct audio_stream_cmd_set_gain_resp { |
| audio_cmd_hdr_t hdr; |
| zx_status_t result; |
| // The current gain settings observed immediately after processing the set |
| // gain request. |
| bool cur_mute; |
| bool cur_agc; |
| float cur_gain; |
| } audio_stream_cmd_set_gain_resp_t; |
| |
| // AUDIO_STREAM_CMD_PLUG_DETECT |
| // |
| // Trigger a plug detect operation and/or enable/disable asynchronous plug |
| // detect notifications. |
| // |
| typedef struct audio_stream_cmd_plug_detect_req { |
| audio_cmd_hdr_t hdr; |
| audio_pd_flags_t flags; // Options used to enable or disable notifications |
| } audio_stream_cmd_plug_detect_req_t; |
| |
| typedef struct audio_stream_cmd_plug_detect_resp { |
| audio_cmd_hdr_t hdr; |
| audio_pd_notify_flags_t flags; // The current plug state and capabilities |
| zx_time_t plug_state_time; // The time of the plug state last change. |
| } audio_stream_cmd_plug_detect_resp_t; |
| |
| // AUDIO_STREAM_PLUG_DETECT_NOTIFY |
| // |
| // Message asynchronously in response to a plug state change to clients who have |
| // registered for plug state notifications. |
| // |
| // Note: Solicited and unsolicited plug detect messages currently use the same |
| // structure and contain the same information. The difference between the two |
| // is that Solicited messages, use AUDIO_STREAM_CMD_PLUG_DETECT as the value of |
| // the `cmd` field of their header and the transaction ID of the request sent by |
| // the client. Unsolicited messages use AUDIO_STREAM_PLUG_DETECT_NOTIFY as the |
| // value value of the `cmd` field of their header, and |
| // AUDIO_INVALID_TRANSACTION_ID for their transaction ID. |
| typedef audio_stream_cmd_plug_detect_resp_t audio_stream_plug_detect_notify_t; |
| |
| // AUDIO_STREAM_CMD_GET_UNIQUE_ID |
| // |
| // Fetch a globally unique, but persistent ID for the stream. |
| // |
| // Drivers should make every effort to return as unique an identifier as |
| // possible for each stream that they publish. This ID must not change between |
| // boots. When available, using a globally unique device serial number is |
| // strongly encouraged. Other possible sources of unique-ness include a |
| // driver's physical connection path, driver binding information, manufacturer |
| // calibration data, and so on. |
| // |
| // Note: a small number of hardcoded unique ID has been provided for built-in |
| // devices. Platform drivers for systems with hardwired audio devices may use |
| // these unique IDs as appropriate to signal which audio streams represent the |
| // built-in devices for the system. Drivers for hot-plugable audio devices |
| // should *never* use these identifiers. |
| // |
| // Even given this, higher level code should *not* depend on these identifiers |
| // being perfectly unique, and should be prepared to take steps to de-dupe |
| // identifiers when needed. |
| typedef struct audio_stream_cmd_get_unique_id_req { |
| audio_cmd_hdr_t hdr; |
| } audio_stream_cmd_get_unique_id_req_t; |
| |
| typedef struct audio_stream_unique_id { |
| uint8_t data[16]; |
| } audio_stream_unique_id_t; |
| |
| #define AUDIO_STREAM_UNIQUE_ID_BUILTIN_SPEAKERS { .data = { 0x01, 0x00 } } |
| #define AUDIO_STREAM_UNIQUE_ID_BUILTIN_HEADPHONE_JACK { .data = { 0x02, 0x00 } } |
| #define AUDIO_STREAM_UNIQUE_ID_BUILTIN_MICROPHONE { .data = { 0x03, 0x00 } } |
| #define AUDIO_STREAM_UNIQUE_ID_BUILTIN_HEADSET_JACK { .data = { 0x04, 0x00 } } |
| |
| typedef struct audio_stream_cmd_get_unique_id_resp { |
| audio_cmd_hdr_t hdr; |
| audio_stream_unique_id_t unique_id; |
| } audio_stream_cmd_get_unique_id_resp_t; |
| |
| // AUDIO_STREAM_CMD_GET_STRING |
| // |
| // Fetch the specified string from a device's static string table. Strings |
| // returned by the device driver... |
| // |
| // ++ Must be encoded using UTF8 |
| // ++ May contain embedded NULLs |
| // ++ May not be NULL terminated |
| // |
| // Drivers are encouraged to NULL terminate all of their strings whenever |
| // possible, but are not required to do so if the response buffer is too small. |
| // |
| typedef enum audio_stream_string_id { |
| AUDIO_STREAM_STR_ID_MANUFACTURER = 0x80000000, |
| AUDIO_STREAM_STR_ID_PRODUCT = 0x80000001, |
| } audio_stream_string_id_t; |
| |
| static_assert(sizeof(audio_stream_string_id_t) == sizeof(uint32_t), |
| "audio_stream_string_id_t must be 32 bits!"); |
| |
| typedef struct audio_stream_cmd_get_string_req { |
| audio_cmd_hdr_t hdr; |
| audio_stream_string_id_t id; |
| } audio_stream_cmd_get_string_req_t; |
| |
| typedef struct audio_stream_cmd_get_string_resp { |
| audio_cmd_hdr_t hdr; |
| zx_status_t result; |
| audio_stream_string_id_t id; |
| uint32_t strlen; |
| uint8_t str[256 - sizeof(audio_cmd_hdr_t) - (3 *sizeof(uint32_t))]; |
| } audio_stream_cmd_get_string_resp_t; |
| |
| static_assert(sizeof(audio_stream_cmd_get_string_resp_t) == 256, |
| "audio_stream_cmd_get_string_resp_t must be exactly 256 bytes"); |
| |
| // AUDIO_RB_CMD_GET_FIFO_DEPTH |
| // |
| // TODO(johngro) : Is calling this "FIFO" depth appropriate? Should it be some |
| // direction neutral form of something like "max-read-ahead-amount" or something |
| // instead? |
| // |
| // May be not used with the NO_ACK flag. |
| typedef struct audio_rb_cmd_get_fifo_depth_req { |
| audio_cmd_hdr_t hdr; |
| } audio_rb_cmd_get_fifo_depth_req_t; |
| |
| typedef struct audio_rb_cmd_get_fifo_depth_resp { |
| audio_cmd_hdr_t hdr; |
| zx_status_t result; |
| |
| // A representation (in bytes) of how far ahead audio hardware may read |
| // into the stream (in the case of output) or may hold onto audio before |
| // writing it to memory (in the case of input). |
| uint32_t fifo_depth; |
| } audio_rb_cmd_get_fifo_depth_resp_t; |
| |
| // AUDIO_RB_CMD_GET_BUFFER |
| typedef struct audio_rb_cmd_get_buffer_req { |
| audio_cmd_hdr_t hdr; |
| |
| uint32_t min_ring_buffer_frames; |
| uint32_t notifications_per_ring; |
| } audio_rb_cmd_get_buffer_req_t; |
| |
| typedef struct audio_rb_cmd_get_buffer_resp { |
| audio_cmd_hdr_t hdr; |
| zx_status_t result; |
| uint32_t num_ring_buffer_frames; |
| |
| // NOTE: If result == ZX_OK, a VMO handle representing the ring buffer to |
| // be used will be returned as well. Clients may map this buffer with |
| // read-write permissions in the case of an output stream, or read-only |
| // permissions in the case of an input stream. The size of the VMO |
| // indicates where the wrap point of the ring (in bytes) is located in the |
| // VMO. This size *must* always be an integral number of audio frames. |
| // |
| // TODO(johngro) : Should we provide some indication of whether or not this |
| // memory is being used directly for HW DMA and may need explicit cache |
| // flushing/invalidation? |
| } audio_rb_cmd_get_buffer_resp_t; |
| |
| // AUDIO_RB_CMD_START |
| typedef struct audio_rb_cmd_start_req { |
| audio_cmd_hdr_t hdr; |
| } audio_rb_cmd_start_req_t; |
| |
| typedef struct audio_rb_cmd_start_resp { |
| audio_cmd_hdr_t hdr; |
| zx_status_t result; |
| uint64_t start_time; |
| } audio_rb_cmd_start_resp_t; |
| |
| // AUDIO_RB_CMD_STOP |
| typedef struct audio_rb_cmd_stop_req { |
| audio_cmd_hdr_t hdr; |
| } audio_rb_cmd_stop_req_t; |
| |
| typedef struct audio_rb_cmd_stop_resp { |
| audio_cmd_hdr_t hdr; |
| zx_status_t result; |
| } audio_rb_cmd_stop_resp_t; |
| |
| // AUDIO_RB_POSITION_NOTIFY |
| typedef struct audio_rb_position_notify { |
| audio_cmd_hdr_t hdr; |
| |
| // The current position (in bytes) of the driver/hardware's read (output) or |
| // write (input) pointer in the ring buffer. |
| uint32_t ring_buffer_pos; |
| } audio_rb_position_notify_t; |
| |
| __END_CDECLS |