// 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; }