SDL 3 has no OSS backend and SchOS has no ALSA kernel side, so sound now goes SDL -> alsa-lib -> alsa-plugins' OSS plugin -> /dev/dsp. That path found two bugs underneath it: - ac97: resetting the PCM-out box also clears the descriptor list address, and nothing set it again. Every stream after the first stop played a "list" at physical address 0 -- silence -- and ALSA resets before each stream. If the engine halts at the end of the queue, the run bit is set again. - /dev/dsp: SETFRAGMENT, GETFMTS, GETCAPS, SETTRIGGER, GETOPTR and GETODELAY for the plugin's ring-and-pointer model, and poll(), writable once a whole fragment fits (ALSA spun at 76% CPU otherwise). speaker-test now plays in real time at 0% CPU, verified in a recording of QEMU's AC'97. - /etc/asound.conf: the default device is a plug to the OSS device at 16-bit stereo 48 kHz, what every SchOS audio driver plays. Also: - getstackpcs: each frame link must go up the same stack by less than KSTACKSIZE. The chain ends at the syscall entry with the user's rbp, which lbreakouthd uses as a register (-1024); the walk dereferenced it under the syscall tracer and the kernel panicked inside acquire(). - fb: FBIOGET_VSCREENINFO reports nominal timings, as Linux's efifb does, instead of zeros, which Xorg's fbdev turned into a 0 Hz RandR mode. - procdump (Ctrl-P) shows where runnable threads are in user space. - World: lbreakouthd instead of lbreakout2 (its stk.c passes vsnprintf a size of 511 plus the prefix length, and Alpine's fortified headers trap on that), plus curl, alsa-lib and alsa-plugins. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
426 lines
13 KiB
C
426 lines
13 KiB
C
// AC'97, the audio class's first driver.
|
|
//
|
|
// Written from the Intel 82801 datasheet's audio chapter. It is the simplest
|
|
// PC audio device that exists on real machines and in QEMU both, which is why
|
|
// it is first: the point of this driver is to prove the class and /dev/dsp
|
|
// with the least chip-specific code possible. Intel HDA -- which is what a
|
|
// laptop built this century actually has -- is a much larger driver, and
|
|
// writing it against an interface nothing had ever played a sound through
|
|
// would be writing two unproven things at once.
|
|
//
|
|
// Two register files, both **I/O ports** rather than MMIO:
|
|
//
|
|
// NAM BAR0, the mixer. Volume, and the codec's own capabilities.
|
|
// NABM BAR1, bus mastering. The descriptor list, and the DMA engine that
|
|
// walks it.
|
|
//
|
|
// The DMA engine reads a *buffer descriptor list*: 32 entries, each naming a
|
|
// physical address and a length in samples. It plays entry CIV, and stops when
|
|
// it reaches LVI -- the "last valid index". Playing continuously is therefore
|
|
// not a matter of starting it and walking away; it is a matter of keeping LVI
|
|
// ahead of CIV, which is what queue() does.
|
|
|
|
#include "core/types.h"
|
|
#include "core/defs.h"
|
|
#include "core/param.h"
|
|
#include "arch/x86.h"
|
|
#include "core/spinlock.h"
|
|
#include "core/sleeplock.h"
|
|
#include "fs/fs.h"
|
|
#include "fs/file.h"
|
|
#include "arch/memlayout.h"
|
|
#include "arch/mmu.h"
|
|
#include "dev/pci.h"
|
|
#include "dev/device.h"
|
|
#include "dev/audio.h"
|
|
#include "core/dma.h"
|
|
#include "core/irq.h"
|
|
|
|
// --- NAM, the mixer ---------------------------------------------------------
|
|
|
|
#define NAM_RESET 0x00
|
|
#define NAM_MASTER_VOL 0x02
|
|
#define NAM_PCM_OUT_VOL 0x18
|
|
#define NAM_EXT_ID 0x28
|
|
#define NAM_EXT_CTRL 0x2A
|
|
#define NAM_PCM_FRONT_RATE 0x2C
|
|
|
|
#define EXT_VRA 0x0001 // variable rate: the codec can do 44100
|
|
|
|
// --- NABM, the bus master ---------------------------------------------------
|
|
//
|
|
// The PCM *out* box starts at 0x10. There are three boxes (in, out, mic) and
|
|
// they have the same shape, which is why the offsets below are written as a
|
|
// base plus a field rather than as constants.
|
|
|
|
#define PO_BASE 0x10
|
|
#define PO_BDBAR (PO_BASE + 0x00) // uint32: physical address of the list
|
|
#define PO_CIV (PO_BASE + 0x04) // uint8: entry being played now
|
|
#define PO_LVI (PO_BASE + 0x05) // uint8: last entry the engine may play
|
|
#define PO_SR (PO_BASE + 0x06) // uint16: status
|
|
#define PO_PICB (PO_BASE + 0x08) // uint16: samples left in this entry
|
|
#define PO_CR (PO_BASE + 0x0B) // uint8: control
|
|
|
|
#define CR_RPBM 0x01 // run
|
|
#define CR_RR 0x02 // reset the box
|
|
#define CR_IOCE 0x10 // interrupt on completion
|
|
|
|
#define SR_DCH 0x01 // DMA halted
|
|
#define SR_BCIS 0x08 // buffer completion interrupt
|
|
#define SR_LVBCI 0x04 // last valid buffer played
|
|
|
|
#define GLOB_CTRL 0x2C
|
|
#define GLOB_STA 0x30
|
|
|
|
// --- the ring ---------------------------------------------------------------
|
|
//
|
|
// 32 descriptors of 4 KB each: 128 KB, which at 48 kHz stereo is about
|
|
// two thirds of a second. Long enough that a writer can fall behind for a
|
|
// scheduling quantum without a gap, short enough that stopping is prompt.
|
|
|
|
#define NDESC 32
|
|
#define DESC_BYTES 4096
|
|
|
|
struct bdl_entry {
|
|
uint32 addr;
|
|
uint16 samples; // *samples*, not bytes: 16-bit words, so bytes/2
|
|
uint16 flags;
|
|
};
|
|
|
|
#define BDL_IOC 0x8000 // interrupt when this one finishes
|
|
|
|
struct ac97 {
|
|
uint16 nam, nabm; // I/O port bases
|
|
struct bdl_entry *bdl; // NDESC entries, DMA memory
|
|
uint8 *buf; // NDESC * DESC_BYTES, DMA memory
|
|
uint32 bdl_phys, buf_phys;
|
|
|
|
int head; // next descriptor to fill
|
|
int tail; // oldest one still queued
|
|
int filled[NDESC]; // bytes in each, 0 when free
|
|
int running;
|
|
struct spinlock lock;
|
|
struct pci_dev *pdev;
|
|
};
|
|
|
|
static struct ac97 ac97dev;
|
|
|
|
static struct ac97 *
|
|
of(struct device *d)
|
|
{
|
|
(void)d;
|
|
return ac97dev.nabm ? &ac97dev : 0;
|
|
}
|
|
|
|
// --- mixer ------------------------------------------------------------------
|
|
|
|
// Volume is attenuation: 0 is loudest, and the mute bit is 0x8000. Writing 0
|
|
// rather than a "sensible" level on purpose -- the kernel has no business
|
|
// deciding how loud a machine is, and a device that comes up silent reads as
|
|
// broken to whoever is testing it.
|
|
static void
|
|
mixer_init(struct ac97 *a)
|
|
{
|
|
outw(a->nam + NAM_RESET, 0);
|
|
outw(a->nam + NAM_MASTER_VOL, 0x0000);
|
|
outw(a->nam + NAM_PCM_OUT_VOL, 0x0000);
|
|
}
|
|
|
|
// --- the class ops ----------------------------------------------------------
|
|
|
|
static int
|
|
ac97_configure(struct device *d, struct audio_format *f)
|
|
{
|
|
struct ac97 *a = of(d);
|
|
uint16 ext;
|
|
|
|
if (!a)
|
|
return -1;
|
|
|
|
// Two channels always. AC'97 plays stereo; a mono stream would have to be
|
|
// doubled, and doing that in the driver would hide it from the caller that
|
|
// asked -- which is what the write-back in the ioctl is for.
|
|
f->channels = 2;
|
|
f->bits = AUDIO_FMT_S16LE;
|
|
|
|
ext = inw(a->nam + NAM_EXT_ID);
|
|
if (ext & EXT_VRA) {
|
|
outw(a->nam + NAM_EXT_CTRL, inw(a->nam + NAM_EXT_CTRL) | EXT_VRA);
|
|
outw(a->nam + NAM_PCM_FRONT_RATE, (uint16)f->rate);
|
|
// What the codec settled on, which is not always what was asked: it is
|
|
// entitled to round to something it can generate.
|
|
f->rate = inw(a->nam + NAM_PCM_FRONT_RATE);
|
|
} else {
|
|
// No variable rate. 48 kHz is the only thing this codec plays, and saying
|
|
// so is more useful than accepting 44100 and playing it 9% fast.
|
|
f->rate = 48000;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// How many descriptors the engine has finished with.
|
|
// Descriptors with nothing in them, counted forward from where the next write
|
|
// would go. Stops at the first one still holding audio, which is what makes
|
|
// this the number of bytes a write can take without waiting.
|
|
static int
|
|
free_descs(struct ac97 *a)
|
|
{
|
|
int n = 0;
|
|
|
|
for (int i = 0; i < NDESC; i++) {
|
|
if (a->filled[(a->head + i) % NDESC])
|
|
break;
|
|
n++;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
static int
|
|
ac97_space(struct device *d)
|
|
{
|
|
struct ac97 *a = of(d);
|
|
int n;
|
|
|
|
if (!a)
|
|
return 0;
|
|
acquire(&a->lock);
|
|
n = free_descs(a) * DESC_BYTES;
|
|
release(&a->lock);
|
|
return n;
|
|
}
|
|
|
|
static int
|
|
ac97_queue(struct device *d, const void *pcm, int bytes)
|
|
{
|
|
struct ac97 *a = of(d);
|
|
int civ, idx, take;
|
|
|
|
if (!a)
|
|
return -1;
|
|
|
|
acquire(&a->lock);
|
|
|
|
civ = inb(a->nabm + PO_CIV) % NDESC;
|
|
idx = a->head;
|
|
|
|
// Retire what the engine has finished with.
|
|
//
|
|
// `tail` is the oldest descriptor we still believe is queued; CIV is the one
|
|
// being played. Everything between them is done. Doing this here as well as
|
|
// in the interrupt handler is deliberate -- the handler is an optimisation,
|
|
// and audio that only works when its interrupt arrives is audio that stops
|
|
// dead on a machine whose line is misrouted.
|
|
while (a->tail != civ && a->filled[a->tail]) {
|
|
a->filled[a->tail] = 0;
|
|
a->tail = (a->tail + 1) % NDESC;
|
|
}
|
|
|
|
if (a->filled[idx]) {
|
|
release(&a->lock);
|
|
return 0; // ring full
|
|
}
|
|
|
|
take = bytes > DESC_BYTES ? DESC_BYTES : bytes;
|
|
memmove(a->buf + (uint64)idx * DESC_BYTES, pcm, take);
|
|
|
|
a->bdl[idx].addr = a->buf_phys + (uint32)idx * DESC_BYTES;
|
|
a->bdl[idx].samples = (uint16)(take / 2); // 16-bit words
|
|
a->bdl[idx].flags = BDL_IOC;
|
|
a->filled[idx] = take;
|
|
a->head = (idx + 1) % NDESC;
|
|
|
|
// LVI is the last entry the engine may play. Keeping it at the descriptor
|
|
// just written is what makes this a queue rather than a one-shot.
|
|
outb(a->nabm + PO_LVI, (uint8)idx);
|
|
|
|
// An engine that ran off the end of the queue halts. QEMU resumes it when
|
|
// LVI moves; the datasheet asks for the run bit again, so if it is still
|
|
// halted after the write above, give it that.
|
|
if (a->running && (inw(a->nabm + PO_SR) & SR_DCH))
|
|
outb(a->nabm + PO_CR, CR_RPBM | CR_IOCE);
|
|
|
|
release(&a->lock);
|
|
return take;
|
|
}
|
|
|
|
static void
|
|
ac97_start(struct device *d)
|
|
{
|
|
struct ac97 *a = of(d);
|
|
|
|
if (!a)
|
|
return;
|
|
acquire(&a->lock);
|
|
if (!a->running) {
|
|
outb(a->nabm + PO_CR, CR_RPBM | CR_IOCE);
|
|
a->running = 1;
|
|
}
|
|
release(&a->lock);
|
|
}
|
|
|
|
static void
|
|
ac97_stop(struct device *d)
|
|
{
|
|
struct ac97 *a = of(d);
|
|
|
|
if (!a)
|
|
return;
|
|
acquire(&a->lock);
|
|
outb(a->nabm + PO_CR, 0);
|
|
outb(a->nabm + PO_CR, CR_RR); // reset the box: CIV, LVI, PICB to 0
|
|
for (int spin = 0; spin < 100000 && (inb(a->nabm + PO_CR) & CR_RR); spin++)
|
|
;
|
|
// The reset clears the descriptor list's address too, and the engine then
|
|
// walks a "list" at physical address 0: every write after the first stop
|
|
// played zeros. ALSA resets the device before each stream, so that was all
|
|
// of them. Linux's intel8x0 rewrites it after every reset for this reason.
|
|
outl(a->nabm + PO_BDBAR, a->bdl_phys);
|
|
for (int i = 0; i < NDESC; i++)
|
|
a->filled[i] = 0;
|
|
a->head = 0;
|
|
a->tail = 0;
|
|
a->running = 0;
|
|
release(&a->lock);
|
|
}
|
|
|
|
static int
|
|
ac97_pending(struct device *d)
|
|
{
|
|
struct ac97 *a = of(d);
|
|
int n = 0;
|
|
|
|
if (!a)
|
|
return 0;
|
|
acquire(&a->lock);
|
|
if (a->running && !(inw(a->nabm + PO_SR) & SR_DCH)) {
|
|
for (int i = 0; i < NDESC; i++)
|
|
if (a->filled[i])
|
|
n += a->filled[i];
|
|
}
|
|
release(&a->lock);
|
|
return n;
|
|
}
|
|
|
|
static const struct audio_ops ac97_ops = {
|
|
.configure = ac97_configure,
|
|
.space = ac97_space,
|
|
.queue = ac97_queue,
|
|
.start = ac97_start,
|
|
.stop = ac97_stop,
|
|
.pending = ac97_pending,
|
|
};
|
|
|
|
// --- interrupt --------------------------------------------------------------
|
|
|
|
static void
|
|
ac97_intr(void *arg)
|
|
{
|
|
struct ac97 *a = arg;
|
|
uint16 sr;
|
|
|
|
if (!a || !a->nabm)
|
|
return;
|
|
|
|
sr = inw(a->nabm + PO_SR);
|
|
|
|
// Acknowledge by writing the bits back. A status bit left set on a
|
|
// level-triggered line is an interrupt that re-fires for ever -- the same
|
|
// shape as the e1000 bug in docs/history/kernel.md section 13.
|
|
if (sr & (SR_BCIS | SR_LVBCI | SR_DCH))
|
|
outw(a->nabm + PO_SR, sr & (SR_BCIS | SR_LVBCI | SR_DCH));
|
|
|
|
{
|
|
int civ = inb(a->nabm + PO_CIV) % NDESC;
|
|
|
|
// Everything the engine has passed is free again. Through `tail` rather
|
|
// than by clearing one entry, so this and ac97_queue() cannot disagree
|
|
// about which descriptors are in flight.
|
|
while (a->tail != civ && a->filled[a->tail]) {
|
|
a->filled[a->tail] = 0;
|
|
a->tail = (a->tail + 1) % NDESC;
|
|
}
|
|
}
|
|
|
|
// The writer is asleep on the tick rather than on this device, so there is
|
|
// nothing to wake -- see audio_wait(). Keeping the handler to "acknowledge
|
|
// and account" is deliberate: an interrupt handler that can be deleted
|
|
// without the audio stopping is one that cannot deadlock the audio either.
|
|
}
|
|
|
|
// --- probe ------------------------------------------------------------------
|
|
|
|
static int
|
|
ac97_probe(struct pci_dev *d)
|
|
{
|
|
struct ac97 *a = &ac97dev;
|
|
|
|
if (a->nabm)
|
|
return -1; // one is enough
|
|
|
|
// Both BARs are I/O ports on this device, which is why nothing here maps
|
|
// anything: `is_io` is what says so, and a driver that ioremapped these
|
|
// would be mapping a port number as if it were an address.
|
|
if (!d->bar[0].is_io || !d->bar[1].is_io ||
|
|
!d->bar[0].base || !d->bar[1].base) {
|
|
cprintf("ac97: expected two I/O BARs; not driving this device\n");
|
|
return -1;
|
|
}
|
|
|
|
a->nam = (uint16)d->bar[0].base;
|
|
a->nabm = (uint16)d->bar[1].base;
|
|
a->pdev = d;
|
|
initlock(&a->lock, "ac97");
|
|
|
|
pci_enable(d, PCI_CMD_IO | PCI_CMD_BUS_MASTER);
|
|
|
|
// The descriptor list and the audio itself, both from the DMA arena: the
|
|
// engine reads them by physical address, and kalloc's pages are adjacent
|
|
// only by chance.
|
|
a->bdl = dma_alloc(NDESC * sizeof(struct bdl_entry), 4096);
|
|
a->buf = dma_alloc(NDESC * DESC_BYTES, 4096);
|
|
if (!a->bdl || !a->buf) {
|
|
cprintf("ac97: no DMA memory for a %d KB ring\n",
|
|
(NDESC * DESC_BYTES) / 1024);
|
|
a->nabm = 0;
|
|
return -1;
|
|
}
|
|
a->bdl_phys = (uint32)dma_phys(a->bdl);
|
|
a->buf_phys = (uint32)dma_phys(a->buf);
|
|
memset(a->bdl, 0, NDESC * sizeof(struct bdl_entry));
|
|
memset(a->buf, 0, NDESC * DESC_BYTES);
|
|
|
|
// Cold reset of the whole controller, then the PCM-out box.
|
|
outl(a->nabm + GLOB_CTRL, 0x00000002);
|
|
mixer_init(a);
|
|
outb(a->nabm + PO_CR, CR_RR);
|
|
for (int spin = 0; spin < 100000 && (inb(a->nabm + PO_CR) & CR_RR); spin++)
|
|
;
|
|
|
|
outl(a->nabm + PO_BDBAR, a->bdl_phys);
|
|
|
|
if (d->irq != 0xFF && d->irq < 24)
|
|
irq_register(d->irq, ac97_intr, a, "ac97", 1);
|
|
|
|
kinfo("ac97: nam %x nabm %x, %d KB ring, irq %d\n",
|
|
a->nam, a->nabm, (NDESC * DESC_BYTES) / 1024, d->irq);
|
|
return 0;
|
|
}
|
|
|
|
static const struct pci_match ac97_match[] = {
|
|
{ 0x8086, 0x2415, 0, 0 }, // 82801AA, which is what QEMU's -device AC97 is
|
|
{ 0x8086, 0x2425, 0, 0 }, // 82801AB
|
|
{ 0x8086, 0x2445, 0, 0 }, // 82801BA
|
|
{ 0x8086, 0x24C5, 0, 0 }, // ICH4
|
|
{ 0x8086, 0x24D5, 0, 0 }, // ICH5
|
|
{ 0x8086, 0x266E, 0, 0 }, // ICH6
|
|
{ 0, 0, 0, 0 },
|
|
};
|
|
|
|
const struct pci_driver ac97_driver = {
|
|
.name = "ac97",
|
|
.devname = "audio",
|
|
.match = ac97_match,
|
|
.ops = &ac97_ops,
|
|
.probe = ac97_probe,
|
|
};
|