audio: /dev/dsp, AudioEngine, and an AC'97 under it
A program that wants to make a sound opens /dev/dsp, writes PCM, and closes
it. That is what UNIX audio has always been and it now exists here:
tone 440 2
Three pieces, in the order a sample passes through them. **/dev/dsp** is a
character device where write() is audio and a handful of OSS ioctls say what
the samples are. **AudioEngine** is everything above a driver that is not
specific to a chip -- the format, whether there is room, and what a write does
when there is not. **A driver** is bound by class, exactly as a display driver
is: device_find_class(DEVCLASS_AUDIO).
OSS numbering rather than something new, because every UNIX audio program ever
written already knows it and the only thing a different set would achieve is
making ports harder. They are read/write ioctls, which is the part that
matters: a program asks for 44100, the device sets 48000 and writes 48000
back, the program resamples. One that ignores the answer plays 9% fast, and
that is its bug rather than the driver's.
**The engine is deliberately one stream.** Mixing several programs is a real
feature with a real design behind it, and building it before anything had
played a single sound would be exactly what docs/plan/1.0.md keeps a register
for: work that exists and is connected to nothing. The second program that
wants to play at the same time is what should cause it to be written.
**audio_wait() sleeps on the timer tick, not on the device.** That looks like
the lazy choice and is not: a ring that drains is a ring with room, so asking
again in ten milliseconds always makes progress, whereas sleeping on the card
makes audio depend on an interrupt arriving. On a machine whose line is
misrouted that is not a stutter, it is a writer that never wakes. The handler
still accounts for finished buffers -- deleting it would cost efficiency, not
correctness -- and it retires descriptors through the same `tail` pointer
queue() uses, so the two cannot disagree about what is in flight.
**AC'97 first because HDA is two unproven things at once.** It is the simplest
audio device that exists on real machines and in QEMU both. Intel HDA is what
the laptop actually has and is a much larger driver; writing it against an
interface nothing had ever played through would have meant debugging the
driver and the interface simultaneously. HDA arrives as a second
struct audio_ops and nothing above it changes -- which is the entire point of
the class.
Two things the wiring needed: PCI class 0x04 now maps to DEVCLASS_AUDIO (the
driver bound and /proc/devices showed it, but audio_get() found nothing,
because the class byte had never been translated), and include/x86.h grew
inw() -- AC'97's mixer is all 16-bit registers and reading one a byte at a
time returns the low half twice on some codecs.
**Tested by measuring what came out, not by not crashing.** QEMU's wav backend
writes the guest's audio to a file; `tone 440 2` produced 1.97 seconds of
44.1 kHz stereo with peak amplitude 14177 against the generator's table
maximum of 14178, and 880 zero crossings per second -- 440 Hz exactly. wav is
on every QEMU target rather than a host backend, because a build machine may
have no sound card and it is the only backend whose output can be checked.
Both supported configurations: one boot each, no traps, no panics, socktest
and memfdtest green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
3eb2e97f7b
commit
9fea18b18b
18 changed files with 1110 additions and 5 deletions
284
kernel/dev/audio.c
Normal file
284
kernel/dev/audio.c
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
// AudioEngine: the audio class, and /dev/dsp above it.
|
||||
//
|
||||
// See include/audio.h for what this is and what it deliberately is not. In
|
||||
// short: a program opens /dev/dsp, says what its samples are, writes them, and
|
||||
// stops. Everything here is either the class vtable's thin wrapper or the part
|
||||
// of "play this" that is not specific to a chip.
|
||||
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "spinlock.h"
|
||||
#include "sleeplock.h"
|
||||
#include "fs.h"
|
||||
#include "file.h"
|
||||
#include "device.h"
|
||||
#include "audio.h"
|
||||
#include "proc.h"
|
||||
#include "uaccess.h"
|
||||
#include "errno.h"
|
||||
|
||||
static struct spinlock audio_lock;
|
||||
static int audio_lock_init;
|
||||
|
||||
// The format the device is currently in. Kept here rather than asked of the
|
||||
// driver on every write, because a caller that set 44100 and got 48000 needs
|
||||
// to be told once and then have it stay told.
|
||||
static struct audio_format current_fmt = { 48000, 2, AUDIO_FMT_S16LE };
|
||||
|
||||
static void
|
||||
lock_once(void)
|
||||
{
|
||||
if (!audio_lock_init) {
|
||||
initlock(&audio_lock, "audio");
|
||||
audio_lock_init = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// --- the class -------------------------------------------------------------
|
||||
|
||||
struct device *
|
||||
audio_get(void)
|
||||
{
|
||||
return device_find_class(DEVCLASS_AUDIO);
|
||||
}
|
||||
|
||||
static const struct audio_ops *
|
||||
ops_of(struct device *d)
|
||||
{
|
||||
return d && d->ops ? (const struct audio_ops *)d->ops : 0;
|
||||
}
|
||||
|
||||
int
|
||||
audio_configure(struct audio_format *f)
|
||||
{
|
||||
struct device *d = audio_get();
|
||||
const struct audio_ops *o = ops_of(d);
|
||||
int r;
|
||||
|
||||
if (!o || !o->configure)
|
||||
return -ENODEV;
|
||||
if (f->bits != AUDIO_FMT_S16LE)
|
||||
return -EINVAL;
|
||||
if (f->channels != 1 && f->channels != 2)
|
||||
return -EINVAL;
|
||||
if (f->rate < 4000 || f->rate > 192000)
|
||||
return -EINVAL;
|
||||
|
||||
r = o->configure(d, f);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
lock_once();
|
||||
acquire(&audio_lock);
|
||||
current_fmt = *f; // what the driver settled on, not what was asked
|
||||
release(&audio_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Queue bytes, blocking while the hardware is full.
|
||||
//
|
||||
// The blocking is here rather than in a driver because it is the same in every
|
||||
// driver: there is a ring, it has room or it does not, and a writer that is
|
||||
// ahead of the speaker has nothing useful to do but wait. Sleeping on the
|
||||
// device and being woken by its interrupt is the whole of it.
|
||||
int
|
||||
audio_write(const void *pcm, int bytes)
|
||||
{
|
||||
struct device *d = audio_get();
|
||||
const struct audio_ops *o = ops_of(d);
|
||||
const char *p = pcm;
|
||||
int done = 0;
|
||||
|
||||
if (!o || !o->queue)
|
||||
return -ENODEV;
|
||||
if (bytes <= 0)
|
||||
return 0;
|
||||
|
||||
while (done < bytes) {
|
||||
int n = o->queue(d, p + done, bytes - done);
|
||||
|
||||
if (n < 0)
|
||||
return done ? done : n;
|
||||
|
||||
if (n == 0) {
|
||||
// Full. Start what is already queued -- a writer that fills the ring
|
||||
// and then waits for space would otherwise wait for a stream nobody
|
||||
// started -- and give the CPU up until some of it has been played.
|
||||
if (o->start)
|
||||
o->start(d);
|
||||
if (myproc() && myproc()->killed)
|
||||
return done ? done : -EINTR;
|
||||
audio_wait(d);
|
||||
continue;
|
||||
}
|
||||
|
||||
done += n;
|
||||
}
|
||||
|
||||
if (o->start)
|
||||
o->start(d);
|
||||
return done;
|
||||
}
|
||||
|
||||
// Wait for the hardware to make room. A driver signals this from its
|
||||
// interrupt handler with audio_wakeup().
|
||||
//
|
||||
// The timeout is not a nicety: a driver whose interrupt never arrives -- a
|
||||
// misrouted line, a chip that needs a kick -- would otherwise hang the writer
|
||||
// for ever with no way to tell what happened. Waking up anyway and asking the
|
||||
// hardware again turns that into audio that stutters, which is a bug report
|
||||
// somebody can make.
|
||||
void
|
||||
audio_wait(struct device *d)
|
||||
{
|
||||
uint t0;
|
||||
|
||||
(void)d;
|
||||
// One timer tick, the same way kernel/sys/sys_poll.c waits: there is no
|
||||
// timed sleep() in this kernel, and adding one for a single caller would be
|
||||
// a scheduler change to avoid a ten-line loop.
|
||||
//
|
||||
// Sleeping on `ticks` rather than on the device means a driver interrupt is
|
||||
// not strictly required to make progress -- which is the point. A ring that
|
||||
// drains is a ring that has room, and asking again in ten milliseconds
|
||||
// costs one wakeup per buffer.
|
||||
acquire(&tickslock);
|
||||
t0 = ticks;
|
||||
while (ticks == t0 && !(myproc() && myproc()->killed))
|
||||
sleep(&ticks, &tickslock);
|
||||
release(&tickslock);
|
||||
}
|
||||
|
||||
void
|
||||
audio_drain(void)
|
||||
{
|
||||
struct device *d = audio_get();
|
||||
const struct audio_ops *o = ops_of(d);
|
||||
int spins;
|
||||
|
||||
if (!o || !o->pending)
|
||||
return;
|
||||
|
||||
// Bounded, for the same reason the wait above is. Ten seconds of audio is
|
||||
// longer than any buffer this engine will hold, so a `pending` that has not
|
||||
// reached zero by then is a driver that is not playing rather than one that
|
||||
// is busy.
|
||||
for (spins = 0; spins < 1000 && o->pending(d) > 0; spins++) {
|
||||
if (myproc() && myproc()->killed)
|
||||
return;
|
||||
audio_wait(d);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
audio_stop(void)
|
||||
{
|
||||
struct device *d = audio_get();
|
||||
const struct audio_ops *o = ops_of(d);
|
||||
|
||||
if (o && o->stop)
|
||||
o->stop(d);
|
||||
}
|
||||
|
||||
// --- /dev/dsp ---------------------------------------------------------------
|
||||
|
||||
int
|
||||
dspwrite(struct inode *ip, uint64 off, char *src, int n)
|
||||
{
|
||||
(void)ip; (void)off;
|
||||
return audio_write(src, n);
|
||||
}
|
||||
|
||||
// Recording is not implemented, and says so. Returning zero would look like
|
||||
// end-of-file and a recorder would write an empty file and call it a success.
|
||||
int
|
||||
dspread(struct inode *ip, uint64 off, char *dst, int n)
|
||||
{
|
||||
(void)ip; (void)off; (void)dst; (void)n;
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
// The last descriptor is closing. Anything still queued is played out first:
|
||||
// a program that writes a sound and exits expects to hear the end of it, and
|
||||
// dropping the tail is the one behaviour that would make this device feel
|
||||
// broken while technically working.
|
||||
void
|
||||
dspclose(struct inode *ip)
|
||||
{
|
||||
(void)ip;
|
||||
audio_drain();
|
||||
audio_stop();
|
||||
}
|
||||
|
||||
int
|
||||
dsp_ioctl(int request, uint64 argp)
|
||||
{
|
||||
struct device *d = audio_get();
|
||||
const struct audio_ops *o = ops_of(d);
|
||||
struct audio_format f;
|
||||
int val;
|
||||
|
||||
if (!o)
|
||||
return -ENODEV;
|
||||
|
||||
lock_once();
|
||||
acquire(&audio_lock);
|
||||
f = current_fmt;
|
||||
release(&audio_lock);
|
||||
|
||||
switch (request) {
|
||||
case SNDCTL_DSP_SPEED:
|
||||
case SNDCTL_DSP_CHANNELS:
|
||||
case SNDCTL_DSP_SETFMT: {
|
||||
if (copy_from_user(&val, argp, sizeof(val)) < 0)
|
||||
return -EFAULT;
|
||||
|
||||
if (request == SNDCTL_DSP_SPEED) f.rate = (unsigned)val;
|
||||
else if (request == SNDCTL_DSP_CHANNELS) f.channels = (unsigned)val;
|
||||
else {
|
||||
if (val != AFMT_S16_LE)
|
||||
return -EINVAL;
|
||||
f.bits = AUDIO_FMT_S16LE;
|
||||
}
|
||||
|
||||
int r = audio_configure(&f);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
// OSS writes back what was actually set, which is the entire reason these
|
||||
// are read/write ioctls: a program asks for 44100, is told 48000, and
|
||||
// resamples. One that ignores the answer gets a chipmunk, and that is its
|
||||
// bug rather than ours.
|
||||
val = (request == SNDCTL_DSP_SPEED) ? (int)f.rate
|
||||
: (request == SNDCTL_DSP_CHANNELS) ? (int)f.channels
|
||||
: AFMT_S16_LE;
|
||||
if (copy_to_user(argp, &val, sizeof(val)) < 0)
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
case SNDCTL_DSP_SYNC:
|
||||
audio_drain();
|
||||
return 0;
|
||||
|
||||
case SNDCTL_DSP_RESET:
|
||||
audio_stop();
|
||||
return 0;
|
||||
|
||||
case SNDCTL_DSP_GETOSPACE: {
|
||||
struct audio_buf_info bi;
|
||||
int space = o->space ? o->space(d) : 0;
|
||||
|
||||
bi.fragsize = 4096;
|
||||
bi.bytes = space;
|
||||
bi.fragments = space / bi.fragsize;
|
||||
bi.fragstotal = 0;
|
||||
if (copy_to_user(argp, &bi, sizeof(bi)) < 0)
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
|
@ -211,4 +211,8 @@ struct devsw devsw[] = {
|
|||
// reverse of major 9: that hands a device to a process, this lets a process
|
||||
// be one. See include/netuser.h.
|
||||
[11] = { netuser_read, netuser_write, netuser_ready },
|
||||
// /dev/dsp (major 12) -- UNIX audio, kernel/dev/audio.c. Write PCM to it.
|
||||
// The close hook plays out what is still queued: a program that writes a
|
||||
// sound and exits expects to hear the end of it.
|
||||
[12] = { dspread, dspwrite, 0, dspclose },
|
||||
};
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ static const char *class_names[DEVCLASS_NCLASS] = {
|
|||
[DEVCLASS_STORAGE] = "storage",
|
||||
[DEVCLASS_NET] = "net",
|
||||
[DEVCLASS_BUS] = "bus",
|
||||
[DEVCLASS_AUDIO] = "audio",
|
||||
};
|
||||
|
||||
const char *
|
||||
|
|
|
|||
|
|
@ -365,6 +365,7 @@ pci_class_of(const struct pci_dev *d)
|
|||
case PCI_CLASS_STORAGE: return DEVCLASS_STORAGE;
|
||||
case PCI_CLASS_NETWORK: return DEVCLASS_NET;
|
||||
case PCI_CLASS_SERIAL_BUS: return DEVCLASS_BUS;
|
||||
case PCI_CLASS_MULTIMEDIA: return DEVCLASS_AUDIO;
|
||||
default: return DEVCLASS_OTHER;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
413
kernel/drivers/audio/ac97.c
Normal file
413
kernel/drivers/audio/ac97.c
Normal file
|
|
@ -0,0 +1,413 @@
|
|||
// 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 "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "x86.h"
|
||||
#include "spinlock.h"
|
||||
#include "sleeplock.h"
|
||||
#include "fs.h"
|
||||
#include "file.h"
|
||||
#include "memlayout.h"
|
||||
#include "mmu.h"
|
||||
#include "pci.h"
|
||||
#include "device.h"
|
||||
#include "audio.h"
|
||||
#include "dma.h"
|
||||
#include "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,
|
||||
};
|
||||
|
|
@ -32,6 +32,7 @@ extern const struct pci_driver vga_pci_driver;
|
|||
extern const struct pci_driver xhci_driver;
|
||||
extern const struct pci_driver virtio_gpu_driver;
|
||||
extern const struct pci_driver e1000_driver;
|
||||
extern const struct pci_driver ac97_driver;
|
||||
|
||||
// Order matters here in one respect: virtio-gpu is offered the bus before the
|
||||
// VBE framebuffer driver, so that on a machine with both, the device with real
|
||||
|
|
@ -43,6 +44,7 @@ static const struct pci_driver *drivers[] = {
|
|||
&vga_pci_driver,
|
||||
&e1000_driver,
|
||||
&xhci_driver,
|
||||
&ac97_driver,
|
||||
};
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -96,6 +96,8 @@ static const struct devnode devnodes[] = {
|
|||
// include/dbg.h and cmd/schdbgd.
|
||||
{ "dbgro", 10, 1, D_ROOT },
|
||||
{ "netdev", 11, 0, D_ROOT },
|
||||
// Audio, spelled the way UNIX spells it. See include/audio.h.
|
||||
{ "dsp", 12, 0, D_ROOT },
|
||||
{ "mouse", 3, 1, D_ROOT },
|
||||
|
||||
// Raw disks. Created whether or not a drive is present: opening one with no
|
||||
|
|
|
|||
|
|
@ -1286,6 +1286,8 @@ addr_t sys_ioctl(void)
|
|||
return pciuser_ioctl(request, argp);
|
||||
if (is_dev && f->ip->major == NETUSER_MAJOR)
|
||||
return netuser_ioctl(request, argp);
|
||||
if (is_dev && f->ip->major == AUDIO_MAJOR)
|
||||
return dsp_ioctl((int)request, argp);
|
||||
|
||||
int is_tty = (is_dev && f->ip->major == 1);
|
||||
if (!is_tty) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue