add a realtime scheduler class and priority-inheritance mutexes with timeouts and abandonment, add an HDA audio driver and a SYS_AUDIO_* syscall family, flesh out kernel32 with Win32 threads, events, mutexes, critical sections, and condition variables, add SYS_RESUME_THREAD for suspended thread creation, add KeInitializeMemoryTypes to reprogram MTRRs and install a canonical PAT for WB/WC/UC control, rework the display pipeline with a pitched present path and console output flush, bring back disk.vhd with mtools since vvfat fucking sucks, harden AHCI with serialized cache flush, USB mass storage with SCSI sense-data decoding, and xHCI with cleaner DMA teardown, fix the last stray for(;;) in HalStopProcessor to while(true), pile on smoke tests for audio, win32 sync primitives, and thread suspend, implement AHCI timeout, kill readahead since it was desperetaly trying to murder me, fix doom sound x2.
This commit is contained in:
1 parent
8b254f84f4
commit
4add6789ba
177 files changed
+22565
-3070
No files matched your search
@@ -0,0 +1,729 @@
|
||||
// Native render-stream syscall smoke test.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
#define TEST_WAIT_MILLISECONDS 3000ull
|
||||
#define TEST_EXTRA_FRAMES 512u
|
||||
#define TEST_REFILL_FRAMES 1024u
|
||||
#define TEST_TONE_FRAMES 48000u
|
||||
#define TEST_AUDIBLE_FRAMES SYS_AUDIO_STREAM_CAPACITY_FRAMES
|
||||
#define TEST_AUDIBLE_RAMP_FRAMES 240u
|
||||
#define TEST_CLOSE_RUNDOWN_ITERATIONS 72u
|
||||
#define TEST_CLOSE_RUNDOWN_SLEEP_MILLISECONDS 5ull
|
||||
#define TEST_IDLE_QUIESCE_MILLISECONDS 300ull
|
||||
#define TEST_TONE_PHASE_MODULUS 1200u
|
||||
#define TEST_TONE_PHASE_STEP 11u
|
||||
#define TEST_TONE_QUARTER_PHASE 300u
|
||||
#define TEST_TONE_PEAK 8192
|
||||
|
||||
static int16_t gFrames[TEST_TONE_FRAMES * 2u];
|
||||
static int16_t gAudibleFrames[TEST_AUDIBLE_FRAMES * 2u];
|
||||
static uint32_t gOpenStream;
|
||||
|
||||
typedef char TEST_AUDIO_FORMAT_SIZE[(sizeof(SYS_AUDIO_FORMAT) == 8) ? 1 : -1];
|
||||
typedef char TEST_AUDIO_ENDPOINT_INFO_SIZE[(sizeof(SYS_AUDIO_ENDPOINT_INFO) == 64) ? 1 : -1];
|
||||
typedef char TEST_AUDIO_ENUMERATE_REQUEST_SIZE[(sizeof(SYS_AUDIO_ENUMERATE_ENDPOINTS_REQUEST) == 32) ? 1 : -1];
|
||||
typedef char TEST_AUDIO_OPEN_REQUEST_SIZE[(sizeof(SYS_AUDIO_OPEN_STREAM_REQUEST) == 40) ? 1 : -1];
|
||||
typedef char TEST_AUDIO_WRITE_REQUEST_SIZE[(sizeof(SYS_AUDIO_WRITE_REQUEST) == 32) ? 1 : -1];
|
||||
typedef char TEST_AUDIO_ARM_WAIT_REQUEST_SIZE[(sizeof(SYS_AUDIO_ARM_WAIT_REQUEST) == 56) ? 1 : -1];
|
||||
typedef char TEST_AUDIO_STREAM_INFO_SIZE[(sizeof(SYS_AUDIO_STREAM_INFO) == 128) ? 1 : -1];
|
||||
typedef char TEST_AUDIO_VOLUME_REQUEST_SIZE[(sizeof(SYS_AUDIO_SET_VOLUME_REQUEST) == 24) ? 1 : -1];
|
||||
typedef char TEST_AUDIO_MASTER_VOLUME_REQUEST_SIZE[(sizeof(SYS_AUDIO_SET_MASTER_VOLUME_REQUEST) == 24) ? 1 : -1];
|
||||
|
||||
// Writes one NUL-terminated string to the console.
|
||||
static void print(const char* text) {
|
||||
SYS_WRITE_CONSOLE_REQUEST request = { SysSvFromCstr(text) };
|
||||
(void)SysWriteConsole(&request);
|
||||
}
|
||||
|
||||
// Writes one unsigned integer to the console.
|
||||
static void print_u64(uint64_t value) {
|
||||
char digits[32];
|
||||
uint32_t count = 0;
|
||||
if (!value) {
|
||||
print("0");
|
||||
return;
|
||||
}
|
||||
while (value && count < sizeof(digits)) {
|
||||
digits[count++] = (char)('0' + value % 10u);
|
||||
value /= 10u;
|
||||
}
|
||||
while (count) {
|
||||
char character = digits[--count];
|
||||
SYS_WRITE_CONSOLE_REQUEST request = { SysSvFromParts(&character, 1) };
|
||||
(void)SysWriteConsole(&request);
|
||||
}
|
||||
}
|
||||
|
||||
// Stops and closes the canonical stream after success, failure, or process interruption.
|
||||
static void cleanup_stream(void) {
|
||||
if (!gOpenStream)
|
||||
return;
|
||||
(void)SysAudioStop(gOpenStream);
|
||||
(void)SysCloseHandle(gOpenStream);
|
||||
gOpenStream = 0;
|
||||
}
|
||||
|
||||
// Terminates the test process.
|
||||
static void exit_now(int32_t code) {
|
||||
cleanup_stream();
|
||||
while (true) (void)SysExitProcess(code);
|
||||
}
|
||||
|
||||
// Fails the test with one status value.
|
||||
static void fail_status(const char* operation, KSTATUS status) {
|
||||
print("audio_stream_smoke: ");
|
||||
print(operation);
|
||||
print(" status=");
|
||||
print_u64(status);
|
||||
print("\n");
|
||||
exit_now(1);
|
||||
}
|
||||
|
||||
// Fails the test with one plain diagnostic.
|
||||
static void fail(const char* message) {
|
||||
print("audio_stream_smoke: ");
|
||||
print(message);
|
||||
print("\n");
|
||||
exit_now(1);
|
||||
}
|
||||
|
||||
// Returns one exact 440 Hz integer triangle-wave sample for the supplied phase.
|
||||
static int16_t tone_sample(uint32_t phase) {
|
||||
int32_t sample;
|
||||
if (phase < TEST_TONE_QUARTER_PHASE) {
|
||||
sample = (int32_t)(phase * TEST_TONE_PEAK / TEST_TONE_QUARTER_PHASE);
|
||||
} else if (phase < TEST_TONE_QUARTER_PHASE * 2u) {
|
||||
sample = (int32_t)((TEST_TONE_QUARTER_PHASE * 2u - phase) *
|
||||
TEST_TONE_PEAK / TEST_TONE_QUARTER_PHASE);
|
||||
} else if (phase < TEST_TONE_QUARTER_PHASE * 3u) {
|
||||
sample = -(int32_t)((phase - TEST_TONE_QUARTER_PHASE * 2u) *
|
||||
TEST_TONE_PEAK / TEST_TONE_QUARTER_PHASE);
|
||||
} else {
|
||||
sample = -(int32_t)((TEST_TONE_PHASE_MODULUS - phase) *
|
||||
TEST_TONE_PEAK / TEST_TONE_QUARTER_PHASE);
|
||||
}
|
||||
return (int16_t)sample;
|
||||
}
|
||||
|
||||
// Fills contract and audible buffers without requiring floating-point support.
|
||||
static void fill_test_frames(void) {
|
||||
uint32_t phase = 0;
|
||||
for (uint32_t frame = 0; frame < TEST_TONE_FRAMES; frame++) {
|
||||
int16_t sample = tone_sample(phase);
|
||||
gFrames[frame * 2u] = sample;
|
||||
gFrames[frame * 2u + 1u] = sample;
|
||||
phase += TEST_TONE_PHASE_STEP;
|
||||
if (phase >= TEST_TONE_PHASE_MODULUS)
|
||||
phase -= TEST_TONE_PHASE_MODULUS;
|
||||
}
|
||||
|
||||
phase = 0;
|
||||
for (uint32_t frame = 0; frame < TEST_AUDIBLE_FRAMES; frame++) {
|
||||
int32_t sample = tone_sample(phase);
|
||||
uint32_t gain = TEST_AUDIBLE_RAMP_FRAMES;
|
||||
if (frame < gain)
|
||||
gain = frame;
|
||||
uint32_t remaining = TEST_AUDIBLE_FRAMES - 1u - frame;
|
||||
if (remaining < gain)
|
||||
gain = remaining;
|
||||
sample = sample * (int32_t)gain / (int32_t)TEST_AUDIBLE_RAMP_FRAMES;
|
||||
gAudibleFrames[frame * 2u] = (int16_t)sample;
|
||||
gAudibleFrames[frame * 2u + 1u] = (int16_t)sample;
|
||||
phase += TEST_TONE_PHASE_STEP;
|
||||
if (phase >= TEST_TONE_PHASE_MODULUS)
|
||||
phase -= TEST_TONE_PHASE_MODULUS;
|
||||
}
|
||||
}
|
||||
|
||||
// Queries one stream with the required public structure size.
|
||||
static SYS_AUDIO_STREAM_INFO query_stream(uint32_t stream) {
|
||||
SYS_AUDIO_STREAM_INFO info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.Size = sizeof(info);
|
||||
KSTATUS status = SysAudioQueryStream(stream, &info);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("query stream", status);
|
||||
if (info.Version != SYS_AUDIO_ABI_VERSION)
|
||||
fail("unexpected audio ABI version");
|
||||
return info;
|
||||
}
|
||||
|
||||
// Applies one valid stream volume, pan, and mute setting.
|
||||
static void set_stream_mix(uint32_t stream,
|
||||
uint32_t volumeQ16,
|
||||
int32_t panQ16,
|
||||
uint32_t muted) {
|
||||
SYS_AUDIO_SET_VOLUME_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.StreamHandle = stream;
|
||||
request.Muted = muted;
|
||||
request.VolumeQ16 = volumeQ16;
|
||||
request.PanQ16 = panQ16;
|
||||
KSTATUS status = SysAudioSetVolume(&request);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("set stream mix", status);
|
||||
}
|
||||
|
||||
// Selects the available default endpoint, or the first available endpoint.
|
||||
static const SYS_AUDIO_ENDPOINT_INFO* select_endpoint(const SYS_AUDIO_ENDPOINT_INFO* endpoints,
|
||||
uint32_t count) {
|
||||
const SYS_AUDIO_ENDPOINT_INFO* fallback = 0;
|
||||
for (uint32_t index = 0; index < count; index++) {
|
||||
const SYS_AUDIO_ENDPOINT_INFO* endpoint = &endpoints[index];
|
||||
if (endpoint->Size != sizeof(*endpoint) ||
|
||||
endpoint->Version != SYS_AUDIO_ABI_VERSION ||
|
||||
endpoint->State != SysAudioEndpointReady ||
|
||||
(endpoint->Flags & SYS_AUDIO_ENDPOINT_FLAG_AVAILABLE) == 0)
|
||||
continue;
|
||||
if (!fallback) fallback = endpoint;
|
||||
if (endpoint->Flags & SYS_AUDIO_ENDPOINT_FLAG_DEFAULT)
|
||||
return endpoint;
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
// Queries one endpoint by its stable identifier.
|
||||
static SYS_AUDIO_ENDPOINT_INFO query_endpoint(uint64_t endpointId) {
|
||||
SYS_AUDIO_ENDPOINT_INFO endpoints[SYS_AUDIO_MAX_ENDPOINTS];
|
||||
uint32_t count = 0;
|
||||
SYS_AUDIO_ENUMERATE_ENDPOINTS_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.Endpoints = endpoints;
|
||||
request.Capacity = SYS_AUDIO_MAX_ENDPOINTS;
|
||||
request.CountOut = &count;
|
||||
KSTATUS status = SysAudioEnumerateEndpoints(&request);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("query endpoint", status);
|
||||
|
||||
for (uint32_t index = 0; index < count; index++) {
|
||||
if (endpoints[index].EndpointId == endpointId)
|
||||
return endpoints[index];
|
||||
}
|
||||
fail("endpoint disappeared during control test");
|
||||
return endpoints[0];
|
||||
}
|
||||
|
||||
// Verifies endpoint master volume, mute, validation, and restoration.
|
||||
static void test_master_volume(const SYS_AUDIO_ENDPOINT_INFO* endpoint) {
|
||||
SYS_AUDIO_SET_MASTER_VOLUME_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.EndpointId = endpoint->EndpointId;
|
||||
request.Muted = 1u;
|
||||
request.VolumeQ16 = SYS_AUDIO_GAIN_Q16_UNITY / 2u;
|
||||
|
||||
KSTATUS status = SysAudioSetMasterVolume(&request);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("set master volume", status);
|
||||
SYS_AUDIO_ENDPOINT_INFO updated = query_endpoint(endpoint->EndpointId);
|
||||
if (updated.MasterVolumeQ16 != SYS_AUDIO_GAIN_Q16_UNITY / 2u ||
|
||||
updated.MasterMuted != 1u)
|
||||
fail("master volume snapshot mismatch");
|
||||
|
||||
request.VolumeQ16 = SYS_AUDIO_GAIN_Q16_UNITY + 1u;
|
||||
status = SysAudioSetMasterVolume(&request);
|
||||
if (status != NTLSTATUS_INVALID_PARAMETER)
|
||||
fail_status("reject master volume above unity", status);
|
||||
|
||||
request.Muted = endpoint->MasterMuted;
|
||||
request.VolumeQ16 = endpoint->MasterVolumeQ16;
|
||||
status = SysAudioSetMasterVolume(&request);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("restore master volume", status);
|
||||
}
|
||||
|
||||
// Opens one stream format and checks the expected status.
|
||||
static uint32_t open_stream_format(uint64_t endpointId,
|
||||
uint32_t sampleRate,
|
||||
uint16_t channelCount,
|
||||
uint16_t sampleFormat,
|
||||
KSTATUS expectedStatus) {
|
||||
uint32_t stream = 0;
|
||||
SYS_AUDIO_OPEN_STREAM_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.EndpointId = endpointId;
|
||||
request.Format.SampleRate = sampleRate;
|
||||
request.Format.ChannelCount = channelCount;
|
||||
request.Format.SampleFormat = sampleFormat;
|
||||
request.StreamHandleOut = &stream;
|
||||
KSTATUS status = SysAudioOpenStream(&request);
|
||||
if (status != expectedStatus)
|
||||
fail_status("open stream format", status);
|
||||
if (status == NTLSTATUS_SUCCESS && !stream)
|
||||
fail("open returned a null handle");
|
||||
if (status != NTLSTATUS_SUCCESS && stream)
|
||||
fail("rejected format returned a handle");
|
||||
return stream;
|
||||
}
|
||||
|
||||
// Proves the complete accepted and rejected source-format surface.
|
||||
static void test_format_contract(uint64_t endpointId) {
|
||||
static const uint32_t rates[] = { 11025u, 22050u, 44100u, 48000u };
|
||||
for (uint32_t rateIndex = 0; rateIndex < sizeof(rates) / sizeof(rates[0]); rateIndex++) {
|
||||
for (uint16_t channels = 1; channels <= 2; channels++) {
|
||||
uint32_t stream = open_stream_format(endpointId,
|
||||
rates[rateIndex],
|
||||
channels,
|
||||
SysAudioSampleS16,
|
||||
NTLSTATUS_SUCCESS);
|
||||
KSTATUS status = SysCloseHandle(stream);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("close format stream", status);
|
||||
}
|
||||
}
|
||||
|
||||
(void)open_stream_format(endpointId, 48000u, 2, 2, NTLSTATUS_NOT_SUPPORTED);
|
||||
(void)open_stream_format(endpointId, 32000u, 2, SysAudioSampleS16, NTLSTATUS_NOT_SUPPORTED);
|
||||
(void)open_stream_format(endpointId, 48000u, 3, SysAudioSampleS16, NTLSTATUS_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
// Opens the canonical test stream.
|
||||
static uint32_t open_stream(uint64_t endpointId) {
|
||||
return open_stream_format(endpointId,
|
||||
48000u,
|
||||
2,
|
||||
SysAudioSampleS16,
|
||||
NTLSTATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// Writes one sample run and returns the accepted frame count.
|
||||
static uint32_t write_samples(uint32_t stream,
|
||||
const int16_t* frames,
|
||||
uint32_t frameCount,
|
||||
KSTATUS expectedStatus) {
|
||||
uint32_t written = 0xFFFFFFFFu;
|
||||
SYS_AUDIO_WRITE_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.StreamHandle = stream;
|
||||
request.FrameCount = frameCount;
|
||||
request.Frames = frameCount ? frames : 0;
|
||||
request.WrittenFramesOut = &written;
|
||||
KSTATUS status = SysAudioWrite(&request);
|
||||
if (status != expectedStatus) fail_status("write stream", status);
|
||||
if (status != NTLSTATUS_SUCCESS && written != 0)
|
||||
fail("failed write reported accepted frames");
|
||||
return written;
|
||||
}
|
||||
|
||||
// Writes one contract-tone range and returns the accepted frame count.
|
||||
static uint32_t write_frames(uint32_t stream,
|
||||
uint32_t frameOffset,
|
||||
uint32_t frameCount,
|
||||
KSTATUS expectedStatus) {
|
||||
if (frameOffset > TEST_TONE_FRAMES ||
|
||||
frameCount > TEST_TONE_FRAMES - frameOffset)
|
||||
fail("test write range exceeded the tone buffer");
|
||||
return write_samples(stream,
|
||||
frameCount ? gFrames + (size_t)frameOffset * 2u : 0,
|
||||
frameCount,
|
||||
expectedStatus);
|
||||
}
|
||||
|
||||
// Arms one exact set of stream progress targets.
|
||||
static void arm_wait(uint32_t stream,
|
||||
uint32_t mask,
|
||||
uint32_t writableFrames,
|
||||
uint64_t consumedFrames,
|
||||
uint64_t presentedFrames,
|
||||
uint64_t drainedGeneration,
|
||||
uint64_t discontinuityGeneration) {
|
||||
SYS_AUDIO_ARM_WAIT_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.StreamHandle = stream;
|
||||
request.Mask = mask;
|
||||
request.WritableFrames = writableFrames;
|
||||
request.ConsumedFrames = consumedFrames;
|
||||
request.PresentedFrames = presentedFrames;
|
||||
request.DrainedGeneration = drainedGeneration;
|
||||
request.DiscontinuityGeneration = discontinuityGeneration;
|
||||
KSTATUS status = SysAudioArmWait(&request);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("arm wait", status);
|
||||
}
|
||||
|
||||
// Waits once and converts timeout or alert completion into a focused failure.
|
||||
static void wait_stream(uint32_t stream, const char* operation) {
|
||||
KSTATUS status = SysWaitSingle(stream, SYS_WAIT_FLAG_NONE, TEST_WAIT_MILLISECONDS);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status(operation, status);
|
||||
}
|
||||
|
||||
// Proves that closing the final handle retires an active stream with queued periods.
|
||||
static void test_active_close_rundown(uint64_t endpointId) {
|
||||
for (uint32_t iteration = 0;
|
||||
iteration < TEST_CLOSE_RUNDOWN_ITERATIONS;
|
||||
iteration++) {
|
||||
uint32_t stream = open_stream(endpointId);
|
||||
gOpenStream = stream;
|
||||
set_stream_mix(stream,
|
||||
SYS_AUDIO_GAIN_Q16_UNITY,
|
||||
SYS_AUDIO_PAN_Q16_CENTER,
|
||||
1u);
|
||||
uint32_t written = write_frames(stream,
|
||||
0,
|
||||
SYS_AUDIO_STREAM_CAPACITY_FRAMES,
|
||||
NTLSTATUS_SUCCESS);
|
||||
if (written != SYS_AUDIO_STREAM_CAPACITY_FRAMES)
|
||||
fail("active-close prefill was partial");
|
||||
|
||||
SYS_AUDIO_STREAM_INFO before = query_stream(stream);
|
||||
arm_wait(stream,
|
||||
SYS_AUDIO_WAIT_CONSUMED |
|
||||
SYS_AUDIO_WAIT_FAILED |
|
||||
SYS_AUDIO_WAIT_DISCONTINUITY,
|
||||
0,
|
||||
before.FramesConsumed + TEST_EXTRA_FRAMES,
|
||||
before.FramesPresented,
|
||||
before.DrainedGeneration,
|
||||
before.DiscontinuityGeneration);
|
||||
KSTATUS status = SysAudioStart(stream);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("active-close start", status);
|
||||
wait_stream(stream, "active-close consumed wait");
|
||||
|
||||
SYS_AUDIO_STREAM_INFO active = query_stream(stream);
|
||||
if (active.State == SysAudioStreamFailed)
|
||||
fail_status("active-close stream failure", active.FailureStatus);
|
||||
if (active.DiscontinuityGeneration != before.DiscontinuityGeneration ||
|
||||
active.FramesConsumed < before.FramesConsumed + TEST_EXTRA_FRAMES)
|
||||
fail("active-close progress mismatch");
|
||||
|
||||
status = SysCloseHandle(stream);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("active-close final handle", status);
|
||||
gOpenStream = 0;
|
||||
status = SysSleep(TEST_CLOSE_RUNDOWN_SLEEP_MILLISECONDS);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("active-close rundown sleep", status);
|
||||
}
|
||||
}
|
||||
|
||||
// Plays one bounded audible tone without depending on real-time user refills.
|
||||
static void play_continuous_tone(uint32_t stream) {
|
||||
KSTATUS status = SysAudioReset(stream);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("audible reset", status);
|
||||
set_stream_mix(stream,
|
||||
SYS_AUDIO_GAIN_Q16_UNITY,
|
||||
SYS_AUDIO_PAN_Q16_CENTER,
|
||||
0u);
|
||||
|
||||
SYS_AUDIO_STREAM_INFO baseline = query_stream(stream);
|
||||
uint32_t submitted = write_samples(stream,
|
||||
gAudibleFrames,
|
||||
TEST_AUDIBLE_FRAMES,
|
||||
NTLSTATUS_SUCCESS);
|
||||
if (submitted != TEST_AUDIBLE_FRAMES)
|
||||
fail("audible prefill was partial");
|
||||
|
||||
status = SysAudioStart(stream);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("audible start", status);
|
||||
status = SysAudioDrain(stream);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("audible drain", status);
|
||||
print("audio_stream_smoke: audible 440 Hz tone begin\n");
|
||||
|
||||
arm_wait(stream,
|
||||
SYS_AUDIO_WAIT_DRAINED |
|
||||
SYS_AUDIO_WAIT_FAILED |
|
||||
SYS_AUDIO_WAIT_DISCONTINUITY,
|
||||
0,
|
||||
baseline.FramesConsumed,
|
||||
baseline.FramesPresented,
|
||||
baseline.DrainedGeneration,
|
||||
baseline.DiscontinuityGeneration);
|
||||
wait_stream(stream, "audible drained wait");
|
||||
|
||||
SYS_AUDIO_STREAM_INFO drained = query_stream(stream);
|
||||
if (drained.State == SysAudioStreamFailed)
|
||||
fail_status("audible stream failure", drained.FailureStatus);
|
||||
if (drained.State != SysAudioStreamStopped ||
|
||||
drained.DrainedGeneration != baseline.DrainedGeneration + 1u ||
|
||||
drained.DiscontinuityGeneration != baseline.DiscontinuityGeneration ||
|
||||
drained.FramesSubmitted != TEST_AUDIBLE_FRAMES ||
|
||||
drained.FramesConsumed != TEST_AUDIBLE_FRAMES ||
|
||||
drained.FramesPresented != TEST_AUDIBLE_FRAMES ||
|
||||
drained.BufferedFrames != 0 ||
|
||||
drained.Underruns != 0 ||
|
||||
drained.SilenceFramesInserted != 0)
|
||||
fail("audible tone completion mismatch");
|
||||
print("audio_stream_smoke: audible 440 Hz tone complete\n");
|
||||
}
|
||||
|
||||
// Runs the complete native stream contract against one endpoint.
|
||||
static void test_stream(const SYS_AUDIO_ENDPOINT_INFO* endpoint) {
|
||||
test_format_contract(endpoint->EndpointId);
|
||||
test_active_close_rundown(endpoint->EndpointId);
|
||||
|
||||
uint32_t stream = open_stream(endpoint->EndpointId);
|
||||
gOpenStream = stream;
|
||||
KSTATUS status;
|
||||
SYS_AUDIO_STREAM_INFO info = query_stream(stream);
|
||||
if (info.State != SysAudioStreamCreated ||
|
||||
info.CapacityFrames != SYS_AUDIO_STREAM_CAPACITY_FRAMES ||
|
||||
info.Format.SampleFormat != SysAudioSampleS16)
|
||||
fail("initial stream snapshot mismatch");
|
||||
|
||||
SYS_AUDIO_SET_VOLUME_REQUEST volume;
|
||||
memset(&volume, 0, sizeof(volume));
|
||||
volume.Size = sizeof(volume);
|
||||
volume.StreamHandle = stream;
|
||||
volume.VolumeQ16 = SYS_AUDIO_GAIN_Q16_UNITY / 2u;
|
||||
volume.PanQ16 = SYS_AUDIO_PAN_Q16_CENTER;
|
||||
status = SysAudioSetVolume(&volume);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("set volume", status);
|
||||
info = query_stream(stream);
|
||||
if (info.VolumeQ16 != SYS_AUDIO_GAIN_Q16_UNITY / 2u ||
|
||||
info.PanQ16 != SYS_AUDIO_PAN_Q16_CENTER || info.Muted)
|
||||
fail("volume snapshot mismatch");
|
||||
|
||||
volume.VolumeQ16 = SYS_AUDIO_GAIN_Q16_UNITY + 1u;
|
||||
status = SysAudioSetVolume(&volume);
|
||||
if (status != NTLSTATUS_INVALID_PARAMETER)
|
||||
fail_status("reject volume above unity", status);
|
||||
volume.VolumeQ16 = SYS_AUDIO_GAIN_Q16_UNITY / 2u;
|
||||
volume.PanQ16 = SYS_AUDIO_PAN_Q16_RIGHT + 1;
|
||||
status = SysAudioSetVolume(&volume);
|
||||
if (status != NTLSTATUS_INVALID_PARAMETER)
|
||||
fail_status("reject pan outside range", status);
|
||||
|
||||
volume.VolumeQ16 = SYS_AUDIO_GAIN_Q16_UNITY;
|
||||
volume.PanQ16 = SYS_AUDIO_PAN_Q16_CENTER;
|
||||
volume.Muted = 1u;
|
||||
status = SysAudioSetVolume(&volume);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("mute control stream", status);
|
||||
|
||||
uint32_t written = write_frames(stream, 0, 0, NTLSTATUS_SUCCESS);
|
||||
if (written != 0) fail("zero-length write accepted frames");
|
||||
written = write_frames(stream,
|
||||
0,
|
||||
SYS_AUDIO_STREAM_CAPACITY_FRAMES + TEST_EXTRA_FRAMES,
|
||||
NTLSTATUS_SUCCESS);
|
||||
if (written != SYS_AUDIO_STREAM_CAPACITY_FRAMES)
|
||||
fail("partial write did not fill exactly one stream ring");
|
||||
written = write_frames(stream, 0, 1, NTLSTATUS_WOULD_BLOCK);
|
||||
if (written != 0) fail("full-ring write accepted a frame");
|
||||
|
||||
info = query_stream(stream);
|
||||
arm_wait(stream,
|
||||
SYS_AUDIO_WAIT_WRITABLE |
|
||||
SYS_AUDIO_WAIT_CONSUMED |
|
||||
SYS_AUDIO_WAIT_FAILED |
|
||||
SYS_AUDIO_WAIT_DISCONTINUITY,
|
||||
TEST_EXTRA_FRAMES,
|
||||
info.FramesConsumed + TEST_EXTRA_FRAMES,
|
||||
0,
|
||||
info.DrainedGeneration,
|
||||
info.DiscontinuityGeneration);
|
||||
status = SysAudioStart(stream);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("start", status);
|
||||
wait_stream(stream, "writable wait");
|
||||
|
||||
SYS_AUDIO_STREAM_INFO progressed = query_stream(stream);
|
||||
if (progressed.State == SysAudioStreamFailed)
|
||||
fail_status("stream failure", progressed.FailureStatus);
|
||||
if (progressed.DiscontinuityGeneration != info.DiscontinuityGeneration)
|
||||
fail("unexpected discontinuity before writable target");
|
||||
if (progressed.WritableFrames < TEST_EXTRA_FRAMES ||
|
||||
progressed.FramesConsumed < info.FramesConsumed + TEST_EXTRA_FRAMES)
|
||||
fail("writable or consumed wait did not observe its target");
|
||||
|
||||
status = SysAudioPause(stream);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("pause", status);
|
||||
if (query_stream(stream).State != SysAudioStreamPaused)
|
||||
fail("pause state mismatch");
|
||||
status = SysAudioResume(stream);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("resume", status);
|
||||
|
||||
uint32_t submitted = SYS_AUDIO_STREAM_CAPACITY_FRAMES;
|
||||
written = write_frames(stream, submitted, TEST_EXTRA_FRAMES, NTLSTATUS_SUCCESS);
|
||||
if (written != TEST_EXTRA_FRAMES)
|
||||
fail("resume refill was partial");
|
||||
submitted += written;
|
||||
|
||||
while (submitted < TEST_TONE_FRAMES) {
|
||||
SYS_AUDIO_STREAM_INFO refill = query_stream(stream);
|
||||
if (refill.State == SysAudioStreamFailed)
|
||||
fail_status("stream failure", refill.FailureStatus);
|
||||
if (refill.DiscontinuityGeneration != info.DiscontinuityGeneration)
|
||||
fail("unexpected discontinuity during muted control refill");
|
||||
|
||||
uint32_t remaining = TEST_TONE_FRAMES - submitted;
|
||||
uint32_t refillFrames = remaining < TEST_REFILL_FRAMES
|
||||
? remaining
|
||||
: TEST_REFILL_FRAMES;
|
||||
if (refill.WritableFrames < refillFrames) {
|
||||
arm_wait(stream,
|
||||
SYS_AUDIO_WAIT_WRITABLE |
|
||||
SYS_AUDIO_WAIT_FAILED |
|
||||
SYS_AUDIO_WAIT_DISCONTINUITY,
|
||||
refillFrames,
|
||||
refill.FramesConsumed,
|
||||
refill.FramesPresented,
|
||||
refill.DrainedGeneration,
|
||||
info.DiscontinuityGeneration);
|
||||
wait_stream(stream, "muted control refill wait");
|
||||
continue;
|
||||
}
|
||||
|
||||
written = write_frames(stream, submitted, refillFrames, NTLSTATUS_SUCCESS);
|
||||
if (written != refillFrames)
|
||||
fail("muted control refill was partial despite reserved capacity");
|
||||
submitted += written;
|
||||
}
|
||||
|
||||
SYS_AUDIO_STREAM_INFO beforeDrain = query_stream(stream);
|
||||
if (beforeDrain.FramesSubmitted != TEST_TONE_FRAMES)
|
||||
fail("muted control frame submission mismatch");
|
||||
uint64_t presentedTarget = beforeDrain.FramesSubmitted;
|
||||
status = SysAudioDrain(stream);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("drain", status);
|
||||
arm_wait(stream,
|
||||
SYS_AUDIO_WAIT_PRESENTED |
|
||||
SYS_AUDIO_WAIT_FAILED |
|
||||
SYS_AUDIO_WAIT_DISCONTINUITY,
|
||||
0,
|
||||
beforeDrain.FramesConsumed,
|
||||
presentedTarget,
|
||||
beforeDrain.DrainedGeneration,
|
||||
beforeDrain.DiscontinuityGeneration);
|
||||
wait_stream(stream, "presentation wait");
|
||||
|
||||
SYS_AUDIO_STREAM_INFO presented = query_stream(stream);
|
||||
if (presented.State == SysAudioStreamFailed)
|
||||
fail_status("stream failure", presented.FailureStatus);
|
||||
if (presented.DiscontinuityGeneration != beforeDrain.DiscontinuityGeneration)
|
||||
fail("unexpected discontinuity before presentation target");
|
||||
if (presented.FramesPresented < presentedTarget)
|
||||
fail("presented wait did not observe its target");
|
||||
|
||||
if (presented.State != SysAudioStreamStopped) {
|
||||
arm_wait(stream,
|
||||
SYS_AUDIO_WAIT_DRAINED |
|
||||
SYS_AUDIO_WAIT_FAILED |
|
||||
SYS_AUDIO_WAIT_DISCONTINUITY,
|
||||
0,
|
||||
presented.FramesConsumed,
|
||||
presented.FramesPresented,
|
||||
beforeDrain.DrainedGeneration,
|
||||
beforeDrain.DiscontinuityGeneration);
|
||||
wait_stream(stream, "drained wait");
|
||||
}
|
||||
|
||||
SYS_AUDIO_STREAM_INFO drained = query_stream(stream);
|
||||
if (drained.State == SysAudioStreamFailed)
|
||||
fail_status("stream failure", drained.FailureStatus);
|
||||
if (drained.DiscontinuityGeneration != beforeDrain.DiscontinuityGeneration)
|
||||
fail("unexpected discontinuity during drain");
|
||||
if (drained.State != SysAudioStreamStopped ||
|
||||
drained.DrainedGeneration != beforeDrain.DrainedGeneration + 1u ||
|
||||
drained.BufferedFrames != 0 ||
|
||||
drained.FramesPresented < presentedTarget ||
|
||||
drained.FramesPresented + drained.FramesDiscarded < drained.FramesSubmitted)
|
||||
fail("drained stream snapshot mismatch");
|
||||
|
||||
status = SysAudioReset(stream);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("reset", status);
|
||||
SYS_AUDIO_STREAM_INFO reset = query_stream(stream);
|
||||
if (reset.State != SysAudioStreamStopped || reset.FramesSubmitted != 0 ||
|
||||
reset.FramesConsumed != 0 || reset.FramesPresented != 0 ||
|
||||
reset.DrainedGeneration != drained.DrainedGeneration)
|
||||
fail("reset stream snapshot mismatch");
|
||||
|
||||
written = write_frames(stream, 0, 128, NTLSTATUS_SUCCESS);
|
||||
if (written != 128) fail("stop test write was partial");
|
||||
status = SysAudioStart(stream);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("restart", status);
|
||||
SYS_AUDIO_STREAM_INFO beforeStop = query_stream(stream);
|
||||
arm_wait(stream,
|
||||
SYS_AUDIO_WAIT_STOPPED |
|
||||
SYS_AUDIO_WAIT_FAILED |
|
||||
SYS_AUDIO_WAIT_DISCONTINUITY,
|
||||
0,
|
||||
beforeStop.FramesConsumed,
|
||||
beforeStop.FramesPresented,
|
||||
beforeStop.DrainedGeneration,
|
||||
beforeStop.DiscontinuityGeneration);
|
||||
status = SysAudioStop(stream);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("stop", status);
|
||||
wait_stream(stream, "stopped wait");
|
||||
if (query_stream(stream).State != SysAudioStreamStopped)
|
||||
fail("stop state mismatch");
|
||||
|
||||
play_continuous_tone(stream);
|
||||
|
||||
status = SysCloseHandle(stream);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("close stream", status);
|
||||
gOpenStream = 0;
|
||||
status = SysSleep(TEST_IDLE_QUIESCE_MILLISECONDS);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("endpoint quiesce sleep", status);
|
||||
}
|
||||
|
||||
// Executes the native audio syscall smoke test.
|
||||
int main(void) {
|
||||
fill_test_frames();
|
||||
print("audio_stream_smoke: begin native render contract test\n");
|
||||
|
||||
uint32_t count = 0;
|
||||
SYS_AUDIO_ENUMERATE_ENDPOINTS_REQUEST countRequest;
|
||||
memset(&countRequest, 0, sizeof(countRequest));
|
||||
countRequest.Size = sizeof(countRequest);
|
||||
countRequest.CountOut = &count;
|
||||
KSTATUS status = SysAudioEnumerateEndpoints(&countRequest);
|
||||
if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_BUFFER_TOO_SMALL)
|
||||
fail_status("count endpoints", status);
|
||||
if (!count) {
|
||||
print("audio_stream_smoke: SKIP no render endpoint\n");
|
||||
exit_now(0);
|
||||
}
|
||||
if (count > SYS_AUDIO_MAX_ENDPOINTS)
|
||||
fail("endpoint count exceeded ABI maximum");
|
||||
|
||||
SYS_AUDIO_ENDPOINT_INFO endpoints[SYS_AUDIO_MAX_ENDPOINTS];
|
||||
memset(endpoints, 0, sizeof(endpoints));
|
||||
SYS_AUDIO_ENUMERATE_ENDPOINTS_REQUEST enumerate;
|
||||
memset(&enumerate, 0, sizeof(enumerate));
|
||||
enumerate.Size = sizeof(enumerate);
|
||||
enumerate.Endpoints = endpoints;
|
||||
enumerate.Capacity = SYS_AUDIO_MAX_ENDPOINTS;
|
||||
enumerate.CountOut = &count;
|
||||
status = SysAudioEnumerateEndpoints(&enumerate);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("enumerate endpoints", status);
|
||||
|
||||
const SYS_AUDIO_ENDPOINT_INFO* endpoint = select_endpoint(endpoints, count);
|
||||
if (!endpoint) {
|
||||
print("audio_stream_smoke: SKIP no available render endpoint\n");
|
||||
exit_now(0);
|
||||
}
|
||||
|
||||
print("audio_stream_smoke: endpoint=");
|
||||
print_u64(endpoint->EndpointId);
|
||||
print("\n");
|
||||
test_master_volume(endpoint);
|
||||
test_stream(endpoint);
|
||||
print("audio_stream_smoke: PASS endpoint=");
|
||||
print_u64(endpoint->EndpointId);
|
||||
print("\n");
|
||||
exit_now(0);
|
||||
}
|
||||
Reference in new issue
Block a user