SchOS/kernel/drivers/audio/ac97.c
Nexoniarz a1c965da1e include: one folder per subsystem, and includes that name it
Sixty-five headers sat side by side in include/, so `#include "irq.h"`
said nothing about where irq.h was or what it belonged to, and errno.h,
stat.h and fcntl.h at the top shadowed the system's for any host tool
built with -I./include. They are in folders now -- arch/ core/ fs/
dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and
every include names the folder, as Linux's do: "core/proc.h".

Done mechanically and checked the hard way: the kernel built from this
tree disassembles identically to the one built from the tree before
it. The full image boots, and userland is the same set of programs.
Paths in docs and comments follow; docs/guides/layout.md has a table
of what is where, and tools/fix-includes.py rewrites old-style
includes from the folders in the tree, for code written before this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
2026-10-23 15:05:12 +02:00

413 lines
12 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);
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 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,
};