diff --git a/devices/common/viacuda.cpp b/devices/common/viacuda.cpp index fc88929d29..cd91837b62 100644 --- a/devices/common/viacuda.cpp +++ b/devices/common/viacuda.cpp @@ -790,7 +790,8 @@ void ViaCuda::pseudo_command() { response_header(CUDA_PKT_PSEUDO, 0); break; case CUDA_SET_POWER_MESSAGES: - LOG_F(WARNING, "Cuda: unsupported pseudo command 0x%X SET_POWER_MESSAGES", cmd); + this->power_messages_enabled = !!this->in_buf[2]; + LOG_F(INFO, "Cuda: power messages %s", this->power_messages_enabled ? "enabled" : "disabled"); response_header(CUDA_PKT_PSEUDO, 0); break; case CUDA_READ_WRITE_I2C: @@ -798,7 +799,18 @@ void ViaCuda::pseudo_command() { i2c_simple_transaction(this->in_buf[2], &this->in_buf[3], this->in_count - 3); break; case CUDA_TIMER_TICKLE: - LOG_F(WARNING, "Cuda: unsupported pseudo command 0x%X TIMER_TICKLE - Byte Sent: 0x%02x", cmd, this->in_buf[2]); + // The guest re-arms the Cuda shutdown watchdog on every tickle; the + // firmware counts it down at 16 counts/sec and powers the machine off + // when it reaches zero. We track the count so an arm value change is + // visible in the log, but never act on expiry to avoid spurious + // power-offs if the guest stops tickling. + if (this->tickle_value != this->in_buf[2]) { + this->tickle_value = this->in_buf[2]; + LOG_F(INFO, "Cuda: timer tickle set to %d counts (%.1f s)", this->tickle_value, + (double)this->tickle_value / 16.0); + } else { + LOG_F(9, "Cuda: timer tickle"); + } response_header(CUDA_PKT_PSEUDO, 0); break; case CUDA_COMB_FMT_I2C: diff --git a/devices/common/viacuda.h b/devices/common/viacuda.h index 3054dcc790..e37b0dee4b 100644 --- a/devices/common/viacuda.h +++ b/devices/common/viacuda.h @@ -241,6 +241,8 @@ class ViaCuda : public I2CBus { bool one_sec_missed = false; // ERS: missed pkt -> fallback to mode $01 bool file_server = false; bool mono_stable = false; + bool power_messages_enabled = false; + uint8_t tickle_value = 0; // last shutdown watchdog count from the guest uint8_t ipl_level = 0; uint16_t device_mask = 0; diff --git a/devices/sound/soundserver_cubeb.cpp b/devices/sound/soundserver_cubeb.cpp index 320bf8fda8..f31cb3019b 100644 --- a/devices/sound/soundserver_cubeb.cpp +++ b/devices/sound/soundserver_cubeb.cpp @@ -167,22 +167,26 @@ static void status_callback(cubeb_stream *stream, void *user_data, cubeb_state s int SoundServer::open_out_stream(uint32_t sample_rate, DmaOutChannel *dma_ch) { - if (is_deterministic) { - impl->deterministic_poll_cb = [dma_ch] { - if (!dma_ch->is_out_active()) { - return; - } - // Drain the DMA buffer, but don't do anything else. - while(1) { - uint8_t *chunk; - uint32_t chunk_size; - if (DmaPullResult::MoreData == dma_ch->pull_data(1024, &chunk_size, &chunk)) { - ; - } else { - break; - } + // Set up a cyclic-timer DMA drain callback. It keeps the guest sound DMA + // advancing even if the host audio stream cannot be started, otherwise + // the guest sound driver would stall forever waiting for DMA interrupts. + impl->deterministic_poll_cb = [dma_ch] { + if (!dma_ch->is_out_active()) { + return; + } + // Drain the DMA buffer, but don't do anything else. + while(1) { + uint8_t *chunk; + uint32_t chunk_size; + if (DmaPullResult::MoreData == dma_ch->pull_data(1024, &chunk_size, &chunk)) { + ; + } else { + break; } - }; + } + }; + + if (is_deterministic) { impl->status = SND_STREAM_OPENED; LOG_F(9, "Deterministic sound output callback set up."); return 0; @@ -228,16 +232,27 @@ int SoundServer::start_out_stream() TimerManager::get_instance()->add_cyclic_timer(MSECS_TO_NSECS(10), impl->deterministic_poll_cb); return 0; } - return cubeb_stream_start(impl->out_stream); + int res = cubeb_stream_start(impl->out_stream); + if (res != CUBEB_OK) { + // Fall back to draining the guest sound DMA via a cyclic timer so + // that the guest sound driver does not stall waiting for DMA progress + // that the failed host stream will never provide. + if (!impl->deterministic_poll_timer) { + impl->deterministic_poll_timer = + TimerManager::get_instance()->add_cyclic_timer(MSECS_TO_NSECS(10), impl->deterministic_poll_cb); + LOG_F(9, "Host sound output stream start failed; falling back to cyclic DMA drain."); + } + } + return res; } void SoundServer::close_out_stream() { - if (!impl->out_stream) - return; - if (is_deterministic) { - LOG_F(9, "Stopping sound output deterministic polling."); + if (impl->deterministic_poll_timer) { TimerManager::get_instance()->cancel_timer(impl->deterministic_poll_timer); + impl->deterministic_poll_timer = 0; + } + if (!impl->out_stream) { impl->status = SND_STREAM_CLOSED; return; }