diff --git a/kernel/src/Drivers/Audio/Mixer.cpp b/kernel/src/Drivers/Audio/Mixer.cpp index fc764b3..00f7242 100644 --- a/kernel/src/Drivers/Audio/Mixer.cpp +++ b/kernel/src/Drivers/Audio/Mixer.cpp @@ -383,17 +383,36 @@ namespace Drivers::Audio::Mixer { g_lock.Acquire(); VirtualStream& s = g_streams[handle]; bool changed = false; + // Pull the ring pointer out of the slot before freeing it. Once + // s.active=false and s.ring=nullptr are visible under the lock no + // other path can reach this pointer, so the actual PFA free can run + // with the lock released — keeping the HDA buffer-complete IRQ from + // spinning on g_lock while ReallocConsecutive walks the free list. + int16_t* ringToFree = nullptr; + bool lastStream = false; if (s.active) { - FreeRing(s.ring); + ringToFree = s.ring; s.ring = nullptr; s.active = false; s.ownerPid = 0; s.name[0] = '\0'; if (g_activeCount > 0) g_activeCount--; + lastStream = (g_activeCount == 0); BumpSerialLocked(); changed = true; } + // Quiesce the hardware when the final virtual stream goes away so + // the HDA DMA ring stops looping its last 32 KiB of samples. The + // next Open() reopens the HDA stream via EnsureHdaOpen(). + bool closeHda = lastStream && g_hdaOpened; + int hdaHandle = g_hdaHandle; + if (closeHda) { + g_hdaOpened = false; + g_hdaHandle = -1; + } g_lock.Release(); + if (closeHda) IntelHda::Close(hdaHandle); + if (ringToFree) FreeRing(ringToFree); if (changed) Sched::WakeObjectWaiters((void*)&g_serial); } @@ -495,12 +514,14 @@ namespace Drivers::Audio::Mixer { } void CleanupProcess(int pid) { + int16_t* ringsToFree[MAX_STREAMS] = {}; + int ringCount = 0; g_lock.Acquire(); bool changed = false; for (int i = 0; i < MAX_STREAMS; i++) { VirtualStream& s = g_streams[i]; if (s.active && s.ownerPid == pid) { - FreeRing(s.ring); + ringsToFree[ringCount++] = s.ring; s.ring = nullptr; s.active = false; s.ownerPid = 0; @@ -509,8 +530,16 @@ namespace Drivers::Audio::Mixer { changed = true; } } + bool closeHda = changed && (g_activeCount == 0) && g_hdaOpened; + int hdaHandle = g_hdaHandle; + if (closeHda) { + g_hdaOpened = false; + g_hdaHandle = -1; + } if (changed) BumpSerialLocked(); g_lock.Release(); + if (closeHda) IntelHda::Close(hdaHandle); + for (int i = 0; i < ringCount; i++) FreeRing(ringsToFree[i]); if (changed) Sched::WakeObjectWaiters((void*)&g_serial); }