blob: 7ee65a0d8ad961b05c1218b208495c498f0f2c0d [file] [log] [blame]
// 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.
#include "src/media/audio/drivers/test/audio_driver_test.h"
#include <dirent.h>
#include <fcntl.h>
#include <fuchsia/hardware/audio/cpp/fidl.h>
#include <lib/fdio/fdio.h>
#include <lib/fzl/vmo-mapper.h>
#include <lib/zx/vmo.h>
#include <algorithm>
#include <cstring>
#include "src/media/audio/lib/logging/logging.h"
namespace media::audio::test {
static const struct {
const char* path;
DeviceType device_type;
} AUDIO_DEVNODES[] = {
{.path = "/dev/class/audio-input", .device_type = DeviceType::Input},
{.path = "/dev/class/audio-output", .device_type = DeviceType::Output},
};
// static
bool AudioDriverTest::no_input_devices_found_ = false;
bool AudioDriverTest::no_output_devices_found_ = false;
// static
void AudioDriverTest::SetUpTestSuite() {
// For verbose logging, set to -media::audio::TRACE or -media::audio::SPEW
#ifdef NDEBUG
Logging::Init(FX_LOG_WARNING, {"audio_driver_test"});
#else
Logging::Init(FX_LOG_INFO, {"audio_driver_test"});
#endif
}
// static
std::atomic_uint32_t AudioDriverTest::unique_transaction_id_ = 0;
// static
zx_txid_t AudioDriverTest::NextTransactionId() {
uint32_t trans_id = ++unique_transaction_id_;
if (trans_id == AUDIO_INVALID_TRANSACTION_ID) {
trans_id = ++unique_transaction_id_;
}
return trans_id;
}
void AudioDriverTest::SetUp() {
TestFixture::SetUp();
stream_channel_.reset();
}
void AudioDriverTest::TearDown() {
ring_buffer_transceiver_.Close();
stream_transceiver_.Close();
watchers_.clear();
TestFixture::TearDown();
}
bool AudioDriverTest::WaitForDevice(DeviceType device_type) {
if (device_type == DeviceType::Input && AudioDriverTest::no_input_devices_found_) {
return false;
}
if (device_type == DeviceType::Output && AudioDriverTest::no_output_devices_found_) {
return false;
}
device_type_ = device_type;
bool enumeration_done = false;
// Set up the watchers, etc. If any fail, automatically stop monitoring all device sources.
for (const auto& devnode : AUDIO_DEVNODES) {
if (device_type != devnode.device_type) {
continue;
}
auto watcher = fsl::DeviceWatcher::CreateWithIdleCallback(
devnode.path,
[this, device_type](int dir_fd, const std::string& filename) {
AUD_VLOG(TRACE) << "'" << filename << "' dir_fd " << dir_fd;
this->AddDevice(dir_fd, filename, device_type);
},
[&enumeration_done]() { enumeration_done = true; });
if (watcher == nullptr) {
EXPECT_FALSE(watcher == nullptr)
<< "AudioDriverTest failed to create DeviceWatcher for '" << devnode.path << "'.";
watchers_.clear();
return false;
}
watchers_.emplace_back(std::move(watcher));
}
//
// ... or ...
//
// Receive a call to AddDeviceByChannel(std::move(stream_channel), name, device_type);
//
RunLoopUntil([&enumeration_done]() { return enumeration_done; });
// If we timed out waiting for devices, this target may not have any. Don't waste further time.
if (!stream_channel_ready_) {
if (device_type == DeviceType::Input) {
AudioDriverTest::no_input_devices_found_ = true;
} else {
AudioDriverTest::no_output_devices_found_ = true;
}
FX_LOGS(WARNING) << "*** No audio " << ((device_type == DeviceType::Input) ? "input" : "output")
<< " devices detected on this target. ***";
return false;
}
// ASSERT that we can communicate with the driver at all.
EXPECT_TRUE(stream_channel_.is_valid());
EXPECT_TRUE(stream_channel_ready_);
zx_status_t status = stream_transceiver_.Init(
std::move(stream_channel_), fit::bind_member(this, &AudioDriverTest::OnInboundStreamMessage),
ErrorHandler());
EXPECT_EQ(ZX_OK, status);
return stream_channel_ready_ && (status == ZX_OK);
}
void AudioDriverTest::AddDevice(int dir_fd, const std::string& name, DeviceType device_type) {
// TODO(mpuryear): on systems with more than one audio device of a given type, test them all.
if (stream_channel_ready_) {
FX_LOGS(WARNING) << "More than one device detected. For now, we need to ignore it.";
return;
}
// Open the device node.
fbl::unique_fd dev_node(openat(dir_fd, name.c_str(), O_RDONLY));
if (!dev_node.is_valid()) {
FX_LOGS(ERROR) << "AudioDriverTest failed to open device node at \"" << name << "\". ("
<< strerror(errno) << " : " << errno << ")";
FAIL();
}
// Obtain the FDIO device channel, wrap it in a sync proxy, use that to get the stream channel.
zx::channel dev_channel;
zx_status_t status =
fdio_get_service_handle(dev_node.release(), dev_channel.reset_and_get_address());
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to obtain FDIO service channel to audio "
<< ((device_type == DeviceType::Input) ? "input" : "output");
FAIL();
}
// Obtain the stream channel
auto dev =
fidl::InterfaceHandle<fuchsia::hardware::audio::Device>(std::move(dev_channel)).BindSync();
fidl::InterfaceRequest<fuchsia::hardware::audio::StreamConfig> intf_req = {};
status = dev->GetChannel(&intf_req);
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to open channel to audio "
<< ((device_type == DeviceType::Input) ? "input" : "output");
FAIL();
}
stream_channel_ = intf_req.TakeChannel();
AUD_VLOG(TRACE) << "Successfully opened devnode '" << name << "' for audio "
<< ((device_type == DeviceType::Input) ? "input" : "output");
stream_channel_ready_ = true;
}
// Stream channel requests
//
// Request the driver's unique ID.
// TODO(mpuryear): ensure that this differs between input and output.
void AudioDriverTest::RequestUniqueId() {
if (error_occurred_) {
return;
}
MessageTransceiver::Message request_message;
auto& request = request_message.ResizeBytesAs<audio_stream_cmd_get_unique_id_req_t>();
unique_id_transaction_id_ = NextTransactionId();
request.hdr.transaction_id = unique_id_transaction_id_;
request.hdr.cmd = AUDIO_STREAM_CMD_GET_UNIQUE_ID;
EXPECT_EQ(ZX_OK, stream_transceiver_.SendMessage(request_message));
RunLoopUntil([this]() { return received_get_unique_id_; });
}
// Request that the driver return its manufacturer string.
void AudioDriverTest::RequestManufacturerString() {
if (error_occurred_) {
return;
}
MessageTransceiver::Message request_message;
auto& request = request_message.ResizeBytesAs<audio_stream_cmd_get_string_req_t>();
manufacturer_string_transaction_id_ = NextTransactionId();
request.hdr.transaction_id = manufacturer_string_transaction_id_;
request.hdr.cmd = AUDIO_STREAM_CMD_GET_STRING;
request.id = AUDIO_STREAM_STR_ID_MANUFACTURER;
EXPECT_EQ(ZX_OK, stream_transceiver_.SendMessage(request_message));
// This command can return an error, so we check for error_occurred_ as well
RunLoopUntil([this]() { return received_get_string_manufacturer_ || error_occurred_; });
}
// Request that the driver return its product string.
void AudioDriverTest::RequestProductString() {
if (error_occurred_) {
return;
}
MessageTransceiver::Message request_message;
auto& request = request_message.ResizeBytesAs<audio_stream_cmd_get_string_req_t>();
product_string_transaction_id_ = NextTransactionId();
request.hdr.transaction_id = product_string_transaction_id_;
request.hdr.cmd = AUDIO_STREAM_CMD_GET_STRING;
request.id = AUDIO_STREAM_STR_ID_PRODUCT;
EXPECT_EQ(ZX_OK, stream_transceiver_.SendMessage(request_message));
// This command can return an error, so we check for error_occurred_ as well
RunLoopUntil([this]() { return received_get_string_product_ || error_occurred_; });
}
// Request that the driver return its clock domain.
void AudioDriverTest::RequestClockDomain() {
if (error_occurred_) {
return;
}
MessageTransceiver::Message request_message;
auto& request = request_message.ResizeBytesAs<audio_stream_cmd_get_clock_domain_req_t>();
get_clock_domain_transaction_id_ = NextTransactionId();
request.hdr.transaction_id = get_clock_domain_transaction_id_;
request.hdr.cmd = AUDIO_STREAM_CMD_GET_CLOCK_DOMAIN;
EXPECT_EQ(ZX_OK, stream_transceiver_.SendMessage(request_message));
RunLoopUntil([this]() { return received_get_clock_domain_; });
}
// Request that the driver return its gain capabilities and current state.
void AudioDriverTest::RequestGain() {
if (error_occurred_) {
return;
}
MessageTransceiver::Message request_message;
auto& request = request_message.ResizeBytesAs<audio_stream_cmd_get_gain_req_t>();
get_gain_transaction_id_ = NextTransactionId();
request.hdr.transaction_id = get_gain_transaction_id_;
request.hdr.cmd = AUDIO_STREAM_CMD_GET_GAIN;
EXPECT_EQ(ZX_OK, stream_transceiver_.SendMessage(request_message));
RunLoopUntil([this]() { return received_get_gain_; });
}
// Determine an appropriate gain state to request, then call other method to request to the
// driver. This method assumes that the driver has already successfully responded to a GetGain
// request.
void AudioDriverTest::RequestSetGain() {
if (error_occurred_) {
return;
}
ASSERT_TRUE(received_get_gain_);
if (max_gain_ == min_gain_) {
FX_LOGS(WARNING) << "*** Audio " << ((device_type_ == DeviceType::Input) ? "input" : "output")
<< " has fixed gain (" << cur_gain_ << " dB). Skipping SetGain test. ***";
return;
}
set_gain_ = min_gain_;
if (cur_gain_ == min_gain_) {
set_gain_ += gain_step_;
}
audio_set_gain_flags_t flags = AUDIO_SGF_GAIN_VALID;
if (can_mute_) {
flags |= AUDIO_SGF_MUTE_VALID;
if (!cur_mute_) {
flags |= AUDIO_SGF_MUTE;
}
}
if (can_agc_) {
flags |= AUDIO_SGF_AGC_VALID;
if (!cur_agc_) {
flags |= AUDIO_SGF_AGC;
}
}
RequestSetGain(flags, set_gain_);
}
// Request that the driver set its gain state to the specified gain_db and flags.
// This method assumes that the driver has already successfully responded to a GetGain request.
void AudioDriverTest::RequestSetGain(audio_set_gain_flags_t flags, float gain_db) {
if (error_occurred_) {
return;
}
ASSERT_TRUE(received_get_gain_);
MessageTransceiver::Message request_message;
auto& request = request_message.ResizeBytesAs<audio_stream_cmd_set_gain_req_t>();
set_gain_transaction_id_ = NextTransactionId();
request.hdr.transaction_id = set_gain_transaction_id_;
request.hdr.cmd = AUDIO_STREAM_CMD_SET_GAIN;
request.flags = flags;
request.gain = gain_db;
set_mute_ = (flags & AUDIO_SGF_MUTE_VALID) ? (flags & AUDIO_SGF_MUTE) : cur_mute_;
set_agc_ = (flags & AUDIO_SGF_AGC_VALID) ? (flags & AUDIO_SGF_AGC) : cur_agc_;
set_gain_ = (flags & AUDIO_SGF_GAIN_VALID) ? gain_db : cur_gain_;
EXPECT_EQ(ZX_OK, stream_transceiver_.SendMessage(request_message));
// This command can return an error, so we check for error_occurred_ as well
RunLoopUntil([this]() { return received_set_gain_ || error_occurred_; });
}
// Request that the driver return the format ranges that it supports.
void AudioDriverTest::RequestFormats() {
if (error_occurred_) {
return;
}
MessageTransceiver::Message request_message;
auto& request = request_message.ResizeBytesAs<audio_stream_cmd_get_formats_req_t>();
get_formats_transaction_id_ = NextTransactionId();
request.hdr.transaction_id = get_formats_transaction_id_;
request.hdr.cmd = AUDIO_STREAM_CMD_GET_FORMATS;
EXPECT_EQ(ZX_OK, stream_transceiver_.SendMessage(request_message));
RunLoopUntil([this]() { return received_get_formats_; });
}
// For the channelization and sample_format that we've set, determine the size of each frame.
// This method assumes that the driver has already successfully responded to a SetFormat request.
void AudioDriverTest::CalculateFrameSize() {
if (error_occurred_ || !received_set_format_) {
return;
}
switch (sample_format_) {
case AUDIO_SAMPLE_FORMAT_8BIT:
frame_size_ = num_channels_;
break;
case AUDIO_SAMPLE_FORMAT_16BIT:
frame_size_ = num_channels_ * sizeof(int16_t);
break;
case AUDIO_SAMPLE_FORMAT_20BIT_PACKED:
frame_size_ = (num_channels_ * 5 + 3) / 4;
break;
case AUDIO_SAMPLE_FORMAT_24BIT_PACKED:
frame_size_ = num_channels_ * 3;
break;
case AUDIO_SAMPLE_FORMAT_20BIT_IN32:
case AUDIO_SAMPLE_FORMAT_24BIT_IN32:
case AUDIO_SAMPLE_FORMAT_32BIT:
frame_size_ = num_channels_ * sizeof(int32_t);
break;
case AUDIO_SAMPLE_FORMAT_32BIT_FLOAT:
frame_size_ = num_channels_ * sizeof(float);
break;
case AUDIO_SAMPLE_FORMAT_BITSTREAM:
default:
ASSERT_TRUE(false) << "Unknown sample_format_ " << std::hex << sample_format_;
frame_size_ = 0;
break;
}
}
void AudioDriverTest::SelectFirstFormat() {
if (received_get_formats_) {
// strip off the UNSIGNED and INVERT_ENDIAN bits...
auto first_range = format_ranges_.front();
auto first_format = first_range.sample_formats & ~AUDIO_SAMPLE_FORMAT_FLAG_MASK;
ASSERT_NE(first_format, 0u);
// just keep the lowest sample format bit.
audio_sample_format_t bit = 1;
while ((first_format & bit) == 0) {
bit <<= 1;
}
first_format &= bit;
frame_rate_ = first_range.min_frames_per_second;
sample_format_ = first_format;
num_channels_ = first_range.min_channels;
}
}
void AudioDriverTest::SelectLastFormat() {
if (received_get_formats_) {
// strip off the UNSIGNED and INVERT_ENDIAN bits...
auto last_range = format_ranges_.back();
auto last_format = last_range.sample_formats & ~AUDIO_SAMPLE_FORMAT_FLAG_MASK;
ASSERT_NE(last_format, 0u);
// and just keep the highest remaining sample format bit.
while (last_format & (last_format - 1)) {
last_format &= (last_format - 1);
}
frame_rate_ = last_range.max_frames_per_second;
sample_format_ = last_format;
num_channels_ = last_range.max_channels;
}
}
// Request that driver set format to the lowest rate/channelization of the first range reported.
// This method assumes that the driver has already successfully responded to a GetFormats request.
void AudioDriverTest::RequestSetFormatMin() {
if (error_occurred_) {
return;
}
ASSERT_TRUE(received_get_formats_);
ASSERT_GT(format_ranges_.size(), 0u);
SelectFirstFormat();
MessageTransceiver::Message request_message;
auto& request = request_message.ResizeBytesAs<audio_stream_cmd_set_format_req_t>();
set_format_transaction_id_ = NextTransactionId();
request.hdr.transaction_id = set_format_transaction_id_;
request.hdr.cmd = AUDIO_STREAM_CMD_SET_FORMAT;
request.frames_per_second = frame_rate_;
request.sample_format = sample_format_;
request.channels = num_channels_;
EXPECT_EQ(ZX_OK, stream_transceiver_.SendMessage(request_message));
// This command can return an error, so we check for error_occurred_ as well
RunLoopUntil(
[this]() { return (received_set_format_ && ring_buffer_channel_ready_) || error_occurred_; });
CalculateFrameSize();
}
// Request that driver set format to the highest rate/channelization of the final range reported.
// This method assumes that the driver has already successfully responded to a GetFormats request.
void AudioDriverTest::RequestSetFormatMax() {
if (error_occurred_) {
return;
}
ASSERT_TRUE(received_get_formats_);
ASSERT_GT(format_ranges_.size(), 0u);
SelectLastFormat();
MessageTransceiver::Message request_message;
auto& request = request_message.ResizeBytesAs<audio_stream_cmd_set_format_req_t>();
set_format_transaction_id_ = NextTransactionId();
request.hdr.transaction_id = set_format_transaction_id_;
request.hdr.cmd = AUDIO_STREAM_CMD_SET_FORMAT;
request.frames_per_second = frame_rate_;
request.sample_format = sample_format_;
request.channels = num_channels_;
EXPECT_EQ(ZX_OK, stream_transceiver_.SendMessage(request_message));
// This command can return an error, so we check for error_occurred_ as well
RunLoopUntil(
[this]() { return (received_set_format_ && ring_buffer_channel_ready_) || error_occurred_; });
CalculateFrameSize();
}
// Request that driver retrieve the current plug detection state and capabilities.
void AudioDriverTest::RequestPlugDetect() {
if (error_occurred_) {
return;
}
MessageTransceiver::Message request_message;
auto& request = request_message.ResizeBytesAs<audio_stream_cmd_plug_detect_req_t>();
plug_detect_transaction_id_ = NextTransactionId();
request.hdr.transaction_id = plug_detect_transaction_id_;
request.hdr.cmd = AUDIO_STREAM_CMD_PLUG_DETECT;
request.flags = AUDIO_PDF_ENABLE_NOTIFICATIONS;
should_plug_notify_ = true;
EXPECT_EQ(ZX_OK, stream_transceiver_.SendMessage(request_message));
RunLoopUntil([this]() { return received_plug_detect_; });
}
// Ring-buffer channel requests
//
// Request that the driver return the FIFO depth (in bytes), at the currently set format.
// This method relies on the ring buffer channel, received with response to a successful
// SetFormat.
void AudioDriverTest::RequestFifoDepth() {
if (error_occurred_) {
return;
}
ASSERT_TRUE(ring_buffer_channel_ready_);
MessageTransceiver::Message request_message;
auto& request = request_message.ResizeBytesAs<audio_rb_cmd_get_fifo_depth_req_t>();
get_fifo_depth_transaction_id_ = NextTransactionId();
request.hdr.transaction_id = get_fifo_depth_transaction_id_;
request.hdr.cmd = AUDIO_RB_CMD_GET_FIFO_DEPTH;
EXPECT_EQ(ZX_OK, ring_buffer_transceiver_.SendMessage(request_message));
// This command can return an error, so we check for error_occurred_ as well
RunLoopUntil([this]() { return received_get_fifo_depth_ || error_occurred_; });
}
// Request that the driver return a VMO handle for the ring buffer, at the currently set format.
// This method relies on the ring buffer channel, received with response to a successful
// SetFormat.
void AudioDriverTest::RequestBuffer(uint32_t min_ring_buffer_frames,
uint32_t notifications_per_ring) {
if (error_occurred_) {
return;
}
ASSERT_TRUE(ring_buffer_channel_ready_);
MessageTransceiver::Message request_message;
auto& request = request_message.ResizeBytesAs<audio_rb_cmd_get_buffer_req_t>();
get_buffer_transaction_id_ = NextTransactionId();
request.hdr.transaction_id = get_buffer_transaction_id_;
request.hdr.cmd = AUDIO_RB_CMD_GET_BUFFER;
request.min_ring_buffer_frames = min_ring_buffer_frames;
min_ring_buffer_frames_ = min_ring_buffer_frames;
request.notifications_per_ring = notifications_per_ring;
notifications_per_ring_ = notifications_per_ring;
EXPECT_EQ(ZX_OK, ring_buffer_transceiver_.SendMessage(request_message));
// This command can return an error, so we check for error_occurred_ as well
RunLoopUntil([this]() { return received_get_buffer_ || error_occurred_; });
}
// Request that the driver start the ring buffer engine, responding with the start_time.
// This method assumes that the ring buffer VMO was received in a successful GetBuffer response.
void AudioDriverTest::RequestStart() {
if (error_occurred_) {
return;
}
ASSERT_TRUE(ring_buffer_ready_);
MessageTransceiver::Message request_message;
auto& request = request_message.ResizeBytesAs<audio_rb_cmd_start_req_t>();
start_transaction_id_ = NextTransactionId();
request.hdr.transaction_id = start_transaction_id_;
request.hdr.cmd = AUDIO_RB_CMD_START;
auto send_time = zx::clock::get_monotonic().get();
EXPECT_EQ(ZX_OK, ring_buffer_transceiver_.SendMessage(request_message));
// This command can return an error, so we check for error_occurred_ as well
RunLoopUntil([this]() { return received_start_ || error_occurred_; });
EXPECT_GT(start_time_, send_time);
// TODO(mpuryear): validate start_time is not too far in the future (it includes FIFO delay).
}
// Request that the driver stop the ring buffer engine, including quieting position notifications.
// This method assumes that the ring buffer engine has previously been successfully started.
void AudioDriverTest::RequestStop() {
if (error_occurred_) {
return;
}
ASSERT_TRUE(received_start_);
MessageTransceiver::Message request_message;
auto& request = request_message.ResizeBytesAs<audio_rb_cmd_stop_req_t>();
stop_transaction_id_ = NextTransactionId();
request.hdr.transaction_id = stop_transaction_id_;
request.hdr.cmd = AUDIO_RB_CMD_STOP;
EXPECT_EQ(ZX_OK, ring_buffer_transceiver_.SendMessage(request_message));
// This command can return an error, so we check for error_occurred_ as well
RunLoopUntil([this]() { return received_stop_ || error_occurred_; });
}
// Handle an incoming stream channel message (generally a response from a previous request)
void AudioDriverTest::OnInboundStreamMessage(MessageTransceiver::Message message) {
auto& header = message.BytesAs<audio_cmd_hdr_t>();
switch (header.cmd) {
case AUDIO_STREAM_CMD_GET_UNIQUE_ID:
HandleGetUniqueIdResponse(message.BytesAs<audio_stream_cmd_get_unique_id_resp_t>());
break;
case AUDIO_STREAM_CMD_GET_STRING:
HandleGetStringResponse(message.BytesAs<audio_stream_cmd_get_string_resp_t>());
break;
case AUDIO_STREAM_CMD_GET_CLOCK_DOMAIN:
HandleGetClockDomainResponse(message.BytesAs<audio_stream_cmd_get_clock_domain_resp_t>());
break;
case AUDIO_STREAM_CMD_GET_GAIN:
HandleGetGainResponse(message.BytesAs<audio_stream_cmd_get_gain_resp_t>());
break;
case AUDIO_STREAM_CMD_SET_GAIN:
HandleSetGainResponse(message.BytesAs<audio_stream_cmd_set_gain_resp_t>());
break;
case AUDIO_STREAM_CMD_GET_FORMATS:
HandleGetFormatsResponse(message.BytesAs<audio_stream_cmd_get_formats_resp_t>());
break;
case AUDIO_STREAM_CMD_SET_FORMAT:
HandleSetFormatResponse(message.BytesAs<audio_stream_cmd_set_format_resp_t>());
// On success, a channel used to control the audio buffer will be returned.
ExtractRingBufferChannel(message);
break;
case AUDIO_STREAM_CMD_PLUG_DETECT:
HandlePlugDetectResponse(message.BytesAs<audio_stream_cmd_plug_detect_resp_t>());
break;
case AUDIO_STREAM_PLUG_DETECT_NOTIFY:
HandlePlugDetectNotify(message.BytesAs<audio_stream_cmd_plug_detect_resp_t>());
break;
default:
EXPECT_TRUE(false) << "Unrecognized header.cmd value " << header.cmd;
break;
}
}
// Validate just the command portion of the response header.
bool AudioDriverTest::ValidateResponseCommand(audio_cmd_hdr header, audio_cmd_t expected_command) {
EXPECT_EQ(header.cmd, expected_command) << "Unexpected command!";
return (expected_command == header.cmd);
}
// Validate just the transaction ID portion of the response header.
void AudioDriverTest::ValidateResponseTransaction(audio_cmd_hdr header,
zx_txid_t expected_transaction_id) {
EXPECT_EQ(header.transaction_id, expected_transaction_id) << "Unexpected transaction ID!";
}
// Validate the entire response header.
bool AudioDriverTest::ValidateResponseHeader(audio_cmd_hdr header,
zx_txid_t expected_transaction_id,
audio_cmd_t expected_command) {
ValidateResponseTransaction(header, expected_transaction_id);
return ValidateResponseCommand(header, expected_command);
}
// Handle a get_unique_id response on the stream channel.
void AudioDriverTest::HandleGetUniqueIdResponse(
const audio_stream_cmd_get_unique_id_resp_t& response) {
if (!ValidateResponseHeader(response.hdr, unique_id_transaction_id_,
AUDIO_STREAM_CMD_GET_UNIQUE_ID)) {
return;
}
EXPECT_EQ(sizeof(response.unique_id.data), sizeof(audio_stream_unique_id_t));
memcpy(unique_id_.data(), response.unique_id.data, kUniqueIdLength);
char id_buf[2 * kUniqueIdLength + 1];
std::snprintf(id_buf, sizeof(id_buf),
"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", unique_id_[0],
unique_id_[1], unique_id_[2], unique_id_[3], unique_id_[4], unique_id_[5],
unique_id_[6], unique_id_[7], unique_id_[8], unique_id_[9], unique_id_[10],
unique_id_[11], unique_id_[12], unique_id_[13], unique_id_[14], unique_id_[15]);
AUD_VLOG(TRACE) << "Received unique_id " << id_buf;
received_get_unique_id_ = true;
}
// Handle a get_string response on the stream channel (either mfr or prod).
void AudioDriverTest::HandleGetStringResponse(const audio_stream_cmd_get_string_resp_t& response) {
if (!ValidateResponseCommand(response.hdr, AUDIO_STREAM_CMD_GET_STRING)) {
return;
}
constexpr auto kMaxStringLength =
sizeof(audio_stream_cmd_get_string_resp_t) - sizeof(audio_cmd_hdr_t) - (3 * sizeof(uint32_t));
EXPECT_LE(response.strlen, kMaxStringLength);
if (response.result != ZX_OK) {
error_occurred_ = true;
FAIL();
}
auto response_str = reinterpret_cast<const char*>(response.str);
if (response.id == AUDIO_STREAM_STR_ID_MANUFACTURER) {
ValidateResponseTransaction(response.hdr, manufacturer_string_transaction_id_);
manufacturer_ = std::string(response_str, response.strlen);
received_get_string_manufacturer_ = true;
} else if (response.id == AUDIO_STREAM_STR_ID_PRODUCT) {
ValidateResponseTransaction(response.hdr, product_string_transaction_id_);
product_ = std::string(response_str, response.strlen);
received_get_string_product_ = true;
} else {
ASSERT_TRUE(false) << "Unrecognized string ID received: " << response.id;
}
}
// Handle a get_clock_domain response on the stream channel.
void AudioDriverTest::HandleGetClockDomainResponse(
const audio_stream_cmd_get_clock_domain_resp_t& response) {
if (!ValidateResponseHeader(response.hdr, get_clock_domain_transaction_id_,
AUDIO_STREAM_CMD_GET_CLOCK_DOMAIN)) {
return;
}
EXPECT_NE(response.clock_domain, kInvalidClockDomain);
clock_domain_ = response.clock_domain;
received_get_clock_domain_ = true;
}
// Handle a get_gain response on the stream channel.
void AudioDriverTest::HandleGetGainResponse(const audio_stream_cmd_get_gain_resp_t& response) {
if (!ValidateResponseHeader(response.hdr, get_gain_transaction_id_, AUDIO_STREAM_CMD_GET_GAIN)) {
return;
}
cur_mute_ = response.cur_mute;
can_mute_ = response.can_mute;
cur_agc_ = response.cur_agc;
can_agc_ = response.can_agc;
cur_gain_ = response.cur_gain;
min_gain_ = response.min_gain;
max_gain_ = response.max_gain;
gain_step_ = response.gain_step;
if (cur_mute_) {
EXPECT_TRUE(can_mute_);
}
if (cur_agc_) {
EXPECT_TRUE(can_agc_);
}
EXPECT_GE(cur_gain_, min_gain_);
EXPECT_LE(cur_gain_, max_gain_);
if (max_gain_ > min_gain_) {
EXPECT_GT(gain_step_, 0.0f);
} else {
EXPECT_EQ(gain_step_, 0.0f);
}
received_get_gain_ = true;
}
// Handle a set_gain response on the stream channel.
void AudioDriverTest::HandleSetGainResponse(const audio_stream_cmd_set_gain_resp_t& response) {
if (!ValidateResponseHeader(response.hdr, set_gain_transaction_id_, AUDIO_STREAM_CMD_SET_GAIN)) {
return;
}
if (response.result != ZX_OK) {
error_occurred_ = true;
FAIL();
}
cur_mute_ = response.cur_mute;
EXPECT_EQ(cur_mute_, set_mute_);
if (cur_mute_) {
EXPECT_TRUE(can_mute_);
}
cur_agc_ = response.cur_agc;
EXPECT_EQ(cur_agc_, set_agc_);
if (cur_agc_) {
EXPECT_TRUE(can_agc_);
}
cur_gain_ = response.cur_gain;
EXPECT_EQ(cur_gain_, set_gain_);
EXPECT_GE(cur_gain_, min_gain_);
EXPECT_LE(cur_gain_, max_gain_);
received_set_gain_ = true;
}
// Handle a get_formats response on the stream channel. This response may be a multi-part.
void AudioDriverTest::HandleGetFormatsResponse(
const audio_stream_cmd_get_formats_resp_t& response) {
if (!ValidateResponseHeader(response.hdr, get_formats_transaction_id_,
AUDIO_STREAM_CMD_GET_FORMATS)) {
return;
}
EXPECT_GT(response.format_range_count, 0u);
EXPECT_LT(response.first_format_range_ndx, response.format_range_count);
EXPECT_EQ(response.first_format_range_ndx, next_format_range_ndx_);
if (response.first_format_range_ndx == 0) {
get_formats_range_count_ = response.format_range_count;
format_ranges_.clear();
} else {
EXPECT_EQ(response.format_range_count, get_formats_range_count_)
<< "Format range count cannot change over multiple get_formats responses";
}
auto num_ranges =
std::min<uint16_t>(response.format_range_count - response.first_format_range_ndx,
AUDIO_STREAM_CMD_GET_FORMATS_MAX_RANGES_PER_RESPONSE);
for (auto i = 0; i < num_ranges; ++i) {
EXPECT_NE(response.format_ranges[i].sample_formats & ~AUDIO_SAMPLE_FORMAT_FLAG_MASK, 0u);
EXPECT_GE(response.format_ranges[i].min_frames_per_second,
fuchsia::media::MIN_PCM_FRAMES_PER_SECOND);
EXPECT_LE(response.format_ranges[i].max_frames_per_second,
fuchsia::media::MAX_PCM_FRAMES_PER_SECOND);
EXPECT_LE(response.format_ranges[i].min_frames_per_second,
response.format_ranges[i].max_frames_per_second);
EXPECT_GE(response.format_ranges[i].min_channels, fuchsia::media::MIN_PCM_CHANNEL_COUNT);
EXPECT_LE(response.format_ranges[i].max_channels, fuchsia::media::MAX_PCM_CHANNEL_COUNT);
EXPECT_LE(response.format_ranges[i].min_channels, response.format_ranges[i].max_channels);
EXPECT_NE(response.format_ranges[i].flags, 0u);
format_ranges_.push_back(response.format_ranges[i]);
}
next_format_range_ndx_ += num_ranges;
if (next_format_range_ndx_ == response.format_range_count) {
EXPECT_EQ(response.format_range_count, format_ranges_.size());
received_get_formats_ = true;
}
}
// Handle a set_format response on the stream channel. After, we will extract a ring buffer
// channel.
void AudioDriverTest::HandleSetFormatResponse(const audio_stream_cmd_set_format_resp_t& response) {
if (!ValidateResponseHeader(response.hdr, set_format_transaction_id_,
AUDIO_STREAM_CMD_SET_FORMAT)) {
return;
}
if (response.result != ZX_OK) {
if (response.result == ZX_ERR_ACCESS_DENIED) {
AUD_LOG(WARNING)
<< "ZX_ERR_ACCESS_DENIED: audio_core may already be connected to this device";
}
error_occurred_ = true;
FAIL();
}
external_delay_nsec_ = response.external_delay_nsec;
received_set_format_ = true;
}
// In concert with incoming SetFormat response on stream channel, extract the ring-buffer channel.
// With it, initialize the message transceiver that will handle messages to/from this channel.
void AudioDriverTest::ExtractRingBufferChannel(MessageTransceiver::Message message) {
if (!received_set_format_) {
return;
}
ASSERT_EQ(message.handles_.size(), 1u);
EXPECT_EQ(
ring_buffer_transceiver_.Init(
zx::channel(message.handles_[0]),
fit::bind_member(this, &AudioDriverTest::OnInboundRingBufferMessage), ErrorHandler()),
ZX_OK);
message.handles_.clear();
ring_buffer_channel_ready_ = true;
}
// Handle plug_detection on the stream channel (shared across response and notification).
void AudioDriverTest::HandlePlugDetect(audio_pd_notify_flags_t flags, zx_time_t plug_state_time) {
if (received_plug_detect_) {
EXPECT_EQ(hardwired_, (flags & AUDIO_PDNF_HARDWIRED));
}
hardwired_ = flags & AUDIO_PDNF_HARDWIRED;
if (received_plug_detect_) {
EXPECT_EQ(can_plug_notify_, (flags & AUDIO_PDNF_CAN_NOTIFY));
}
can_plug_notify_ = flags & AUDIO_PDNF_CAN_NOTIFY;
plugged_ = flags & AUDIO_PDNF_PLUGGED;
plug_state_time_ = plug_state_time;
EXPECT_LT(plug_state_time_, zx::clock::get_monotonic().get());
AUD_VLOG(TRACE) << "Plug_state_time: " << plug_state_time;
}
// Handle a plug_detect response on the stream channel (response solicited by client).
void AudioDriverTest::HandlePlugDetectResponse(
const audio_stream_cmd_plug_detect_resp_t& response) {
if (!ValidateResponseHeader(response.hdr, plug_detect_transaction_id_,
AUDIO_STREAM_CMD_PLUG_DETECT)) {
return;
}
HandlePlugDetect(response.flags, response.plug_state_time);
received_plug_detect_ = true;
}
// Handle a plug_detect notification on the stream channel (async message not solicited by
// client).
void AudioDriverTest::HandlePlugDetectNotify(const audio_stream_cmd_plug_detect_resp_t& notify) {
if (!ValidateResponseHeader(notify.hdr, AUDIO_INVALID_TRANSACTION_ID,
AUDIO_STREAM_PLUG_DETECT_NOTIFY)) {
return;
}
EXPECT_FALSE(hardwired_);
EXPECT_TRUE(can_plug_notify_);
EXPECT_TRUE(should_plug_notify_);
HandlePlugDetect(notify.flags, notify.plug_state_time);
received_plug_detect_notify_ = true;
AUD_LOG(ERROR) << "Driver autonomously generated an asynchronous plug detect notification";
}
// Handle all incoming response message types, on the ring buffer channel.
void AudioDriverTest::OnInboundRingBufferMessage(MessageTransceiver::Message message) {
auto& header = message.BytesAs<audio_cmd_hdr_t>();
switch (header.cmd) {
case AUDIO_RB_CMD_GET_FIFO_DEPTH:
HandleGetFifoDepthResponse(message.BytesAs<audio_rb_cmd_get_fifo_depth_resp_t>());
break;
case AUDIO_RB_CMD_GET_BUFFER:
HandleGetBufferResponse(message.BytesAs<audio_rb_cmd_get_buffer_resp_t>());
// On success, a VMO for the ring buffer will be returned.
ExtractRingBuffer(message);
break;
case AUDIO_RB_CMD_START:
HandleStartResponse(message.BytesAs<audio_rb_cmd_start_resp_t>());
break;
case AUDIO_RB_CMD_STOP:
HandleStopResponse(message.BytesAs<audio_rb_cmd_stop_resp_t>());
break;
case AUDIO_RB_POSITION_NOTIFY:
HandlePositionNotify(message.BytesAs<audio_rb_position_notify_t>());
break;
default:
EXPECT_TRUE(false) << "Unrecognized header.cmd value " << header.cmd;
break;
}
}
// Handle a get_fifo_depth response on the ring buffer channel.
void AudioDriverTest::HandleGetFifoDepthResponse(
const audio_rb_cmd_get_fifo_depth_resp_t& response) {
if (!ValidateResponseHeader(response.hdr, get_fifo_depth_transaction_id_,
AUDIO_RB_CMD_GET_FIFO_DEPTH)) {
return;
}
if (response.result != ZX_OK) {
error_occurred_ = true;
FAIL();
}
fifo_depth_ = response.fifo_depth;
received_get_fifo_depth_ = true;
}
// Handle a get_buffer response on the ring buffer channel.
void AudioDriverTest::HandleGetBufferResponse(const audio_rb_cmd_get_buffer_resp_t& response) {
if (!ValidateResponseHeader(response.hdr, get_buffer_transaction_id_, AUDIO_RB_CMD_GET_BUFFER)) {
return;
}
if (response.result != ZX_OK) {
error_occurred_ = true;
FAIL();
}
EXPECT_GE(response.num_ring_buffer_frames, min_ring_buffer_frames_);
ring_buffer_frames_ = response.num_ring_buffer_frames;
received_get_buffer_ = true;
}
// Given the GET_BUFFER response message, retrieve the ring buffer VMO handle and save it.
void AudioDriverTest::ExtractRingBuffer(MessageTransceiver::Message get_buffer_response) {
ASSERT_TRUE(received_get_buffer_);
EXPECT_EQ(get_buffer_response.handles_.size(), 1u);
zx::vmo ring_buffer_vmo = zx::vmo(get_buffer_response.handles_[0]);
get_buffer_response.handles_.clear();
EXPECT_TRUE(ring_buffer_vmo.is_valid());
const zx_vm_option_t option_flags = ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;
EXPECT_EQ(
ring_buffer_.CreateAndMap(ring_buffer_frames_ * frame_size_, option_flags, nullptr,
&ring_buffer_vmo, ZX_RIGHT_READ | ZX_RIGHT_MAP | ZX_RIGHT_TRANSFER),
ZX_OK);
AUD_VLOG(TRACE) << "Mapping size: " << ring_buffer_frames_ * frame_size_;
ring_buffer_ready_ = true;
}
// Handle a start response on the ring buffer channel.
void AudioDriverTest::HandleStartResponse(const audio_rb_cmd_start_resp_t& response) {
if (!ValidateResponseHeader(response.hdr, start_transaction_id_, AUDIO_RB_CMD_START)) {
return;
}
if (response.result != ZX_OK) {
error_occurred_ = true;
FAIL();
}
ASSERT_GT(response.start_time, 0u);
start_time_ = response.start_time;
received_start_ = true;
}
// Handle a stop response on the ring buffer channel. Clear out any previous position notification
// count, to enable us to detect whether any were received after the STOP command was processed.
void AudioDriverTest::HandleStopResponse(const audio_rb_cmd_stop_resp_t& response) {
if (!ValidateResponseHeader(response.hdr, stop_transaction_id_, AUDIO_RB_CMD_STOP)) {
return;
}
if (response.result != ZX_OK) {
error_occurred_ = true;
FAIL();
}
position_notification_count_ = 0;
received_stop_ = true;
}
// Handle a position notification on the ring buffer channel.
void AudioDriverTest::HandlePositionNotify(const audio_rb_position_notify_t& notify) {
if (!ValidateResponseHeader(notify.hdr, get_position_transaction_id_, AUDIO_RB_POSITION_NOTIFY)) {
return;
}
EXPECT_GT(notifications_per_ring_, 0u);
auto now = zx::clock::get_monotonic().get();
EXPECT_LT(start_time_, now);
EXPECT_LT(notify.monotonic_time, now);
if (position_notification_count_) {
EXPECT_GT(notify.monotonic_time, start_time_);
EXPECT_GT(notify.monotonic_time, last_monotonic_time_);
} else {
EXPECT_GE(notify.monotonic_time, start_time_);
}
last_monotonic_time_ = notify.monotonic_time;
ring_buffer_position_ = notify.ring_buffer_pos;
EXPECT_LT(ring_buffer_position_, ring_buffer_frames_ * frame_size_);
++position_notification_count_;
AUD_VLOG(TRACE) << "Position: " << ring_buffer_position_
<< ", notification_count: " << position_notification_count_;
}
// Wait for the specified number of position notifications, or timeout at 60 seconds.
void AudioDriverTest::ExpectPositionNotifyCount(uint32_t count) {
if (error_occurred_) {
return;
}
RunLoopUntil([this, count]() { return position_notification_count_ >= count; });
auto timestamp_duration = last_monotonic_time_ - start_time_;
auto observed_duration = zx::clock::get_monotonic().get() - start_time_;
ASSERT_GE(position_notification_count_, count) << "No position notifications received";
ASSERT_NE(frame_rate_ * notifications_per_ring_, 0u);
auto ns_per_notification =
(zx::sec(1) * ring_buffer_frames_) / (frame_rate_ * notifications_per_ring_);
auto expected_min_time = ns_per_notification.get() * (count - 1);
auto expected_time = ns_per_notification.get() * count;
auto expected_max_time = ns_per_notification.get() * (count + 2);
AUD_VLOG(TRACE) << "Timestamp delta from min/ideal/max: " << std::setw(10)
<< (expected_min_time - timestamp_duration) << " : " << std::setw(10)
<< (expected_time - timestamp_duration) << " : " << std::setw(10)
<< (expected_max_time - timestamp_duration);
EXPECT_GE(timestamp_duration, expected_min_time);
EXPECT_LT(timestamp_duration, expected_max_time);
AUD_VLOG(TRACE) << "Observed delta from min/ideal/max : " << std::setw(10)
<< (expected_min_time - observed_duration) << " : " << std::setw(10)
<< (expected_time - observed_duration) << " : " << std::setw(10)
<< (expected_max_time - observed_duration);
EXPECT_GT(observed_duration, expected_min_time);
}
// After waiting for one second, we should NOT have received any position notifications.
void AudioDriverTest::ExpectNoPositionNotifications() {
if (error_occurred_) {
return;
}
zx::nanosleep(zx::deadline_after(zx::sec(1)));
RunLoopUntilIdle();
EXPECT_EQ(position_notification_count_, 0u);
}
//
// Test cases that target each of the various driver commands
//
// Stream channel commands
//
// AUDIO_STREAM_CMD_GET_UNIQUE_ID
// For input stream, verify a valid GET_UNIQUE_ID response is successfully received.
TEST_F(AudioDriverTest, InputGetUniqueId) {
if (!WaitForDevice(DeviceType::Input)) {
return;
}
RequestUniqueId();
}
// For output stream, verify a valid GET_UNIQUE_ID response is successfully received.
TEST_F(AudioDriverTest, OutputGetUniqueId) {
if (!WaitForDevice(DeviceType::Output)) {
return;
}
RequestUniqueId();
}
// AUDIO_STREAM_CMD_GET_STRING - Manufacturer
// For input stream, verify a valid GET_STRING (MANUFACTURER) response is successfully received.
TEST_F(AudioDriverTest, InputGetManufacturer) {
if (!WaitForDevice(DeviceType::Input)) {
return;
}
RequestManufacturerString();
}
// For output stream, verify a valid GET_STRING (MANUFACTURER) response is successfully received.
TEST_F(AudioDriverTest, OutputGetManufacturer) {
if (!WaitForDevice(DeviceType::Output)) {
return;
}
RequestManufacturerString();
}
// AUDIO_STREAM_CMD_GET_STRING - Product
// For input stream, verify a valid GET_STRING (PRODUCT) response is successfully received.
TEST_F(AudioDriverTest, InputGetProduct) {
if (!WaitForDevice(DeviceType::Input)) {
return;
}
RequestProductString();
}
// For output stream, verify a valid GET_STRING (PRODUCT) response is successfully received.
TEST_F(AudioDriverTest, OutputGetProduct) {
if (!WaitForDevice(DeviceType::Output)) {
return;
}
RequestProductString();
}
// AUDIO_STREAM_CMD_GET_CLOCK_DOMAIN
// For input stream, verify a valid GET_CLOCK_DOMAIN response is successfully received.
TEST_F(AudioDriverTest, InputGetClockDomain) {
if (!WaitForDevice(DeviceType::Input)) {
return;
}
RequestClockDomain();
}
// For output stream, verify a valid GET_CLOCK_DOMAIN response is successfully received.
TEST_F(AudioDriverTest, OutputGetClockDomain) {
if (!WaitForDevice(DeviceType::Output)) {
return;
}
RequestClockDomain();
}
// AUDIO_STREAM_CMD_GET_GAIN
// For input stream, verify a valid GET_GAIN response is successfully received.
TEST_F(AudioDriverTest, InputGetGain) {
if (!WaitForDevice(DeviceType::Input)) {
return;
}
RequestGain();
}
// For output stream, verify a valid GET_GAIN response is successfully received.
TEST_F(AudioDriverTest, OutputGetGain) {
if (!WaitForDevice(DeviceType::Output)) {
return;
}
RequestGain();
}
// AUDIO_STREAM_CMD_SET_GAIN
// For input stream, verify a valid SET_GAIN response is successfully received.
TEST_F(AudioDriverTest, InputSetGain) {
if (!WaitForDevice(DeviceType::Input)) {
return;
}
RequestGain();
RequestSetGain();
}
// For output stream, verify a valid SET_GAIN response is successfully received.
TEST_F(AudioDriverTest, OutputSetGain) {
if (!WaitForDevice(DeviceType::Output)) {
return;
}
RequestGain();
RequestSetGain();
}
// AUDIO_STREAM_CMD_GET_FORMATS
// For input stream, verify a valid GET_FORMATS response is successfully received.
TEST_F(AudioDriverTest, InputGetFormats) {
if (!WaitForDevice(DeviceType::Input)) {
return;
}
RequestFormats();
}
// For output stream, verify a valid GET_FORMATS response is successfully received.
TEST_F(AudioDriverTest, OutputGetFormats) {
if (!WaitForDevice(DeviceType::Output)) {
return;
}
RequestFormats();
}
// AUDIO_STREAM_CMD_SET_FORMAT
// For output stream, verify a valid SET_FORMAT response, for low-bit-rate format -- and that a
// valid ring buffer channel is received.
TEST_F(AudioDriverTest, InputSetFormatMin) {
if (!WaitForDevice(DeviceType::Input)) {
return;
}
RequestFormats();
RequestSetFormatMin();
}
// For output stream, verify a valid SET_FORMAT response, for low-bit-rate format -- and that a
// valid ring buffer channel is received.
TEST_F(AudioDriverTest, OutputSetFormatMin) {
if (!WaitForDevice(DeviceType::Output)) {
return;
}
RequestFormats();
RequestSetFormatMin();
}
// For input stream, verify a valid SET_FORMAT response, for high-bit-rate format -- and that a
// valid ring buffer channel is received.
TEST_F(AudioDriverTest, InputSetFormatMax) {
if (!WaitForDevice(DeviceType::Input)) {
return;
}
RequestFormats();
RequestSetFormatMax();
}
// For output stream, verify a valid SET_FORMAT response, for high-bit-rate format -- and that a
// valid ring buffer channel is received.
TEST_F(AudioDriverTest, OutputSetFormatMax) {
if (!WaitForDevice(DeviceType::Output)) {
return;
}
RequestFormats();
RequestSetFormatMax();
}
// AUDIO_STREAM_CMD_PLUG_DETECT
// For input stream, verify a valid PLUG_DETECT response is successfully received.
TEST_F(AudioDriverTest, InputPlugDetect) {
if (!WaitForDevice(DeviceType::Input)) {
return;
}
RequestPlugDetect();
}
// For output stream, verify a valid PLUG_DETECT response is successfully received.
TEST_F(AudioDriverTest, OutputPlugDetect) {
if (!WaitForDevice(DeviceType::Output)) {
return;
}
RequestPlugDetect();
}
// AUDIO_STREAM_PLUG_DETECT_NOTIFY is not testable without scriptable PLUG/UNPLUG actions
// Ring Buffer channel commands
//
// AUDIO_RB_CMD_GET_FIFO_DEPTH
// For input stream, verify a valid GET_FIFO_DEPTH response is successfully received.
TEST_F(AudioDriverTest, InputGetFifoDepth) {
if (!WaitForDevice(DeviceType::Input)) {
return;
}
RequestFormats();
RequestSetFormatMin();
RequestFifoDepth();
}
// For output stream, verify a valid GET_FIFO_DEPTH response is successfully received.
TEST_F(AudioDriverTest, OutputGetFifoDepth) {
if (!WaitForDevice(DeviceType::Output)) {
return;
}
RequestFormats();
RequestSetFormatMax();
RequestFifoDepth();
}
// AUDIO_RB_CMD_GET_BUFFER
// For input stream, verify a GET_BUFFER response and ring buffer VMO is successfully received.
TEST_F(AudioDriverTest, InputGetBuffer) {
if (!WaitForDevice(DeviceType::Input)) {
return;
}
RequestFormats();
RequestSetFormatMax();
uint32_t frames = 48000;
uint32_t notifs = 8;
RequestBuffer(frames, notifs);
}
// For output stream, verify a GET_BUFFER response and ring buffer VMO is successfully received.
TEST_F(AudioDriverTest, OutputGetBuffer) {
if (!WaitForDevice(DeviceType::Output)) {
return;
}
RequestFormats();
RequestSetFormatMin();
uint32_t frames = 100;
uint32_t notifs = 1;
RequestBuffer(frames, notifs);
}
// AUDIO_RB_CMD_START
// For input stream, verify that a valid START response is successfully received.
TEST_F(AudioDriverTest, InputStart) {
if (!WaitForDevice(DeviceType::Input)) {
return;
}
RequestFormats();
RequestSetFormatMax();
RequestBuffer(100, 0);
RequestStart();
}
// For output stream, verify that a valid START response is successfully received.
TEST_F(AudioDriverTest, OutputStart) {
if (!WaitForDevice(DeviceType::Output)) {
return;
}
RequestFormats();
RequestSetFormatMin();
RequestBuffer(32000, 0);
RequestStart();
}
// AUDIO_RB_CMD_STOP
// For input stream, verify that a valid STOP response is successfully received.
TEST_F(AudioDriverTest, InputStop) {
if (!WaitForDevice(DeviceType::Input)) {
return;
}
RequestFormats();
RequestSetFormatMax();
RequestBuffer(24000, 0);
RequestStart();
RequestStop();
}
// For output stream, verify that a valid STOP response is successfully received.
TEST_F(AudioDriverTest, OutputStop) {
if (!WaitForDevice(DeviceType::Output)) {
return;
}
RequestFormats();
RequestSetFormatMin();
RequestBuffer(100, 0);
RequestStart();
RequestStop();
}
// AUDIO_RB_POSITION_NOTIFY
// For input stream, verify position notifications at fast rate (~180/sec) over approx 100 ms.
TEST_F(AudioDriverTest, InputPositionNotifyFast) {
if (!WaitForDevice(DeviceType::Input)) {
return;
}
RequestFormats();
RequestSetFormatMax();
RequestBuffer(8000, 32);
RequestStart();
ExpectPositionNotifyCount(16);
}
// For output stream, verify position notifications at fast rate (~180/sec) over approx 100 ms.
TEST_F(AudioDriverTest, OutputPositionNotifyFast) {
if (!WaitForDevice(DeviceType::Output)) {
return;
}
RequestFormats();
RequestSetFormatMax();
RequestBuffer(8000, 32);
RequestStart();
ExpectPositionNotifyCount(16);
}
// For input stream, verify position notifications at slow rate (2/sec) over approx 1 second.
TEST_F(AudioDriverTest, InputPositionNotifySlow) {
if (!WaitForDevice(DeviceType::Input)) {
return;
}
RequestFormats();
RequestSetFormatMin();
RequestBuffer(48000, 2);
RequestStart();
ExpectPositionNotifyCount(2);
}
// For output stream, verify position notifications at slow rate (2/sec) over approx 1 second.
TEST_F(AudioDriverTest, OutputPositionNotifySlow) {
if (!WaitForDevice(DeviceType::Output)) {
return;
}
RequestFormats();
RequestSetFormatMin();
RequestBuffer(48000, 2);
RequestStart();
ExpectPositionNotifyCount(2);
}
// For input stream, verify that no position notifications arrive if notifications_per_ring is 0.
TEST_F(AudioDriverTest, InputPositionNotifyNone) {
if (!WaitForDevice(DeviceType::Input)) {
return;
}
RequestFormats();
RequestSetFormatMax();
RequestBuffer(8000, 0);
RequestStart();
ExpectNoPositionNotifications();
}
// For output stream, verify that no position notifications arrive if notifications_per_ring is 0.
TEST_F(AudioDriverTest, OutputPositionNotifyNone) {
if (!WaitForDevice(DeviceType::Output)) {
return;
}
RequestFormats();
RequestSetFormatMax();
RequestBuffer(8000, 0);
RequestStart();
ExpectNoPositionNotifications();
}
// For input stream, verify that no position notificatons arrive after STOP.
TEST_F(AudioDriverTest, InputNoPositionNotifyAfterStop) {
if (!WaitForDevice(DeviceType::Input)) {
return;
}
RequestFormats();
RequestSetFormatMax();
RequestBuffer(8000, 32);
RequestStart();
ExpectPositionNotifyCount(2);
RequestStop();
ExpectNoPositionNotifications();
}
// For output stream, verify that no position notificatons arrive after STOP.
TEST_F(AudioDriverTest, OutputNoPositionNotifyAfterStop) {
if (!WaitForDevice(DeviceType::Output)) {
return;
}
RequestFormats();
RequestSetFormatMax();
RequestBuffer(8000, 32);
RequestStart();
ExpectPositionNotifyCount(2);
RequestStop();
ExpectNoPositionNotifications();
}
// For input stream, verify that monotonic_time values are close to NOW, and always increasing.
} // namespace media::audio::test