Sound through ALSA, a 2D game, and a stack walk that cannot panic

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
This commit is contained in:
Nexoniarz 2026-10-25 05:39:16 +01:00
commit c77841ce3d
11 changed files with 237 additions and 12 deletions

View file

@ -798,6 +798,7 @@ char* shm_page(int id, int pg);
uint64 memfd_size(int id); uint64 memfd_size(int id);
int memfd_truncate(int id, uint64 len); int memfd_truncate(int id, uint64 len);
int memfd_seals(int id, int add); int memfd_seals(int id, int add);
int dsp_writable(void);
int memfd_rw(int id, int user, uint64 buf, uint64 off, int n, int writing); int memfd_rw(int id, int user, uint64 buf, uint64 off, int n, int writing);
void shm_detach(int id); void shm_detach(int id);

View file

@ -79,6 +79,7 @@ int audio_configure(struct audio_format *f); // 0, or negative
int audio_write(const void *pcm, int bytes); // bytes queued, or negative int audio_write(const void *pcm, int bytes); // bytes queued, or negative
void audio_drain(void); void audio_drain(void);
void audio_stop(void); void audio_stop(void);
int dsp_writable(void); // poll(): room for a write
// --- what userspace uses --------------------------------------------------- // --- what userspace uses ---------------------------------------------------
// //
@ -92,6 +93,23 @@ void audio_stop(void);
#define SNDCTL_DSP_SYNC 0x00005001 // wait for the queue to drain #define SNDCTL_DSP_SYNC 0x00005001 // wait for the queue to drain
#define SNDCTL_DSP_GETOSPACE 0x8010500C // struct audio_buf_info out #define SNDCTL_DSP_GETOSPACE 0x8010500C // struct audio_buf_info out
#define SNDCTL_DSP_RESET 0x00005000 // stop, discard #define SNDCTL_DSP_RESET 0x00005000 // stop, discard
// The rest of what a buffer-and-pointer client needs -- ALSA's OSS plugin, which
// is how SDL and everything else that speaks ALSA reaches this device.
#define SNDCTL_DSP_SETFRAGMENT 0xC004500A // int in/out: count<<16 | log2(size)
#define SNDCTL_DSP_GETFMTS 0x8004500B // int out: AFMT_* mask
#define SNDCTL_DSP_GETCAPS 0x8004500F // int out: DSP_CAP_*
#define SNDCTL_DSP_SETTRIGGER 0x40045010 // int in: PCM_ENABLE_OUTPUT or 0
#define SNDCTL_DSP_GETOPTR 0x800C5012 // struct count_info out
#define SNDCTL_DSP_GETODELAY 0x80045017 // int out: bytes not yet heard
#define DSP_CAP_TRIGGER 0x00001000
#define PCM_ENABLE_OUTPUT 0x00000002
struct count_info {
int bytes; // played since the last reset
int blocks; // fragments played since the last GETOPTR
int ptr; // where playback is in a ring of fragsize * fragments
};
#define AFMT_S16_LE 0x00000010 #define AFMT_S16_LE 0x00000010

View file

@ -90,7 +90,19 @@ getstackpcs(addr_t *rbp, addr_t pcs[])
if(((addr_t)rbp & 7) != 0) if(((addr_t)rbp & 7) != 0)
break; break;
pcs[i] = rbp[1]; pcs[i] = rbp[1];
rbp = (addr_t*)rbp[0]; // The next frame is the caller's, further up the same stack: higher, and
// by less than a whole kernel stack. The chain ends at the system call or
// interrupt entry, and the rbp saved there is the user's -- which an
// optimised program uses as an ordinary register. lbreakouthd left -1024
// in it, 0xfffffffffffffc00, which passed every test above and faulted
// here under the syscall tracer, taking the kernel down from inside the
// lock it was recording.
addr_t *next = (addr_t*)rbp[0];
if(next <= rbp || (addr_t)next - (addr_t)rbp >= KSTACKSIZE){
i++; // keep the return address just read
break;
}
rbp = next;
} }
for(; i < 10; i++) for(; i < 10; i++)
pcs[i] = 0; pcs[i] = 0;

View file

@ -26,6 +26,16 @@ static int audio_lock_init;
// to be told once and then have it stay told. // to be told once and then have it stay told.
static struct audio_format current_fmt = { 48000, 2, AUDIO_FMT_S16LE }; static struct audio_format current_fmt = { 48000, 2, AUDIO_FMT_S16LE };
// The OSS view of the stream, for clients that track a play pointer in a ring
// rather than just writing: bytes accepted by the driver since boot, where the
// last reset left the count, the ring geometry SETFRAGMENT asked for, and the
// played count GETOPTR last reported (for its fragment count).
static uint64 queued_total;
static uint64 reset_base;
static uint64 last_optr;
static int frag_size = 4096;
static int frag_count = 16;
static void static void
lock_once(void) lock_once(void)
{ {
@ -114,6 +124,10 @@ audio_write(const void *pcm, int bytes)
} }
done += n; done += n;
lock_once();
acquire(&audio_lock);
queued_total += (uint64)n;
release(&audio_lock);
} }
if (o->start) if (o->start)
@ -211,6 +225,45 @@ dspclose(struct inode *ip)
audio_stop(); audio_stop();
} }
// Bytes the speaker has played since boot: everything queued, less what is
// still waiting. A reset discards the waiting part, which then counts as played
// -- the count only goes forward, and GETOPTR measures from the reset.
static uint64
played_total(struct device *d, const struct audio_ops *o)
{
uint64 q, p;
int pend = o->pending ? o->pending(d) : 0;
lock_once();
acquire(&audio_lock);
q = queued_total;
release(&audio_lock);
p = (pend > 0 && (uint64)pend <= q) ? q - (uint64)pend : q;
return p;
}
// Writable once a whole fragment fits, as OSS defines it. ALSA's plugin sets
// the fragment to its period and waits in poll() for one to be free; reporting
// writable at the first free byte had it wake, find less than a period of
// room, and poll again -- 76% of a CPU to play a sine wave.
int
dsp_writable(void)
{
struct device *d = audio_get();
const struct audio_ops *o = ops_of(d);
int want;
if (!o || !o->space)
return 0;
lock_once();
acquire(&audio_lock);
want = frag_size;
release(&audio_lock);
if (want > 65536)
want = 65536; // never more than a driver's ring can free at once
return o->space(d) >= want;
}
int int
dsp_ioctl(int request, uint64 argp) dsp_ioctl(int request, uint64 argp)
{ {
@ -262,18 +315,78 @@ dsp_ioctl(int request, uint64 argp)
audio_drain(); audio_drain();
return 0; return 0;
case SNDCTL_DSP_RESET: case SNDCTL_DSP_RESET: {
audio_stop(); audio_stop();
uint64 p = played_total(d, o);
acquire(&audio_lock);
reset_base = last_optr = p;
release(&audio_lock);
return 0; return 0;
}
// The ring the client thinks in. The driver's own ring is whatever it is;
// these only shape the numbers GETOPTR and GETOSPACE report, which is all a
// client does with them. Fragments of 256 bytes to 64 KiB, 2 to 1024 of them.
case SNDCTL_DSP_SETFRAGMENT: {
if (copy_from_user(&val, argp, sizeof(val)) < 0)
return -EFAULT;
int shift = val & 0xffff, count = (val >> 16) & 0xffff;
if (shift < 8) shift = 8;
if (shift > 16) shift = 16;
if (count < 2) count = 2;
if (count > 1024 || count == 0x7fff) count = 1024;
acquire(&audio_lock);
frag_size = 1 << shift;
frag_count = count;
release(&audio_lock);
val = (count << 16) | shift;
return copy_to_user(argp, &val, sizeof(val)) < 0 ? -EFAULT : 0;
}
case SNDCTL_DSP_GETFMTS:
val = AFMT_S16_LE;
return copy_to_user(argp, &val, sizeof(val)) < 0 ? -EFAULT : 0;
case SNDCTL_DSP_GETCAPS:
val = DSP_CAP_TRIGGER;
return copy_to_user(argp, &val, sizeof(val)) < 0 ? -EFAULT : 0;
// Enabling output starts whatever is queued. Disabling is accepted and does
// nothing: the stream stops by itself when the queue runs dry, and stopping
// it here would throw away what is queued, which a pause must not.
case SNDCTL_DSP_SETTRIGGER:
if (copy_from_user(&val, argp, sizeof(val)) < 0)
return -EFAULT;
if ((val & PCM_ENABLE_OUTPUT) && o->start)
o->start(d);
return 0;
case SNDCTL_DSP_GETOPTR: {
struct count_info ci;
uint64 p = played_total(d, o);
acquire(&audio_lock);
uint64 since = p - reset_base;
uint64 ring = (uint64)frag_size * (uint64)frag_count;
ci.bytes = (int)since;
ci.blocks = (int)((p - last_optr) / (uint64)frag_size);
ci.ptr = (int)(since % ring);
last_optr = p;
release(&audio_lock);
return copy_to_user(argp, &ci, sizeof(ci)) < 0 ? -EFAULT : 0;
}
case SNDCTL_DSP_GETODELAY:
val = o->pending ? o->pending(d) : 0;
return copy_to_user(argp, &val, sizeof(val)) < 0 ? -EFAULT : 0;
case SNDCTL_DSP_GETOSPACE: { case SNDCTL_DSP_GETOSPACE: {
struct audio_buf_info bi; struct audio_buf_info bi;
int space = o->space ? o->space(d) : 0; int space = o->space ? o->space(d) : 0;
bi.fragsize = 4096; bi.fragsize = frag_size;
bi.bytes = space; bi.bytes = space;
bi.fragments = space / bi.fragsize; bi.fragments = space / bi.fragsize;
bi.fragstotal = 0; bi.fragstotal = frag_count;
if (copy_to_user(argp, &bi, sizeof(bi)) < 0) if (copy_to_user(argp, &bi, sizeof(bi)) < 0)
return -EFAULT; return -EFAULT;
return 0; return 0;

View file

@ -147,6 +147,23 @@ fbclose(struct inode *ip)
// the largest struct here -- fb_fix_screeninfo is half the size of // the largest struct here -- fb_fix_screeninfo is half the size of
// fb_var_screeninfo, and a buffer sitting near the end of a mapping is a // fb_var_screeninfo, and a buffer sitting near the end of a mapping is a
// legitimate place to put the smaller one. // legitimate place to put the smaller one.
// Timings for a mode the firmware set and never described. GOP and VBE give a
// size and a pixel format and nothing about the signal, so these are made up --
// and made up the way Linux's efifb makes them up, which is what every program
// that reads them was written against. They used to be zero, which is not "no
// answer" but the answer "0 Hz": Xorg's fbdev driver built a RandR mode with a
// dot clock of 0 from it, and SDL 3 sized a fullscreen window from that mode
// as 1x1. Physical size stays zero; nothing reads it as a number.
static void
nominal_timings(struct fb_var_screeninfo *v)
{
if (v->xres == 0 || v->yres == 0)
return;
v->pixclock = 10000000 / v->xres * 1000 / v->yres;
v->left_margin = (v->xres / 8) & 0xf8;
v->hsync_len = (v->xres / 8) & 0xf8;
}
int int
fb_ioctl(int request, addr_t arg) fb_ioctl(int request, addr_t arg)
{ {
@ -169,8 +186,7 @@ fb_ioctl(int request, addr_t arg)
v->bits_per_pixel = gfx_fb_bpp(); v->bits_per_pixel = gfx_fb_bpp();
v->activate = FB_ACTIVATE_NOW; v->activate = FB_ACTIVATE_NOW;
pixel_format(v, v->bits_per_pixel); pixel_format(v, v->bits_per_pixel);
// height/width (physical millimetres) and every timing field stay zero: nominal_timings(v);
// VBE does not report them and a guess would be worse than an admission.
return 0; return 0;
} }
@ -212,6 +228,7 @@ fb_ioctl(int request, addr_t arg)
v->bits_per_pixel = gfx_fb_bpp(); v->bits_per_pixel = gfx_fb_bpp();
v->activate = act; v->activate = act;
pixel_format(v, v->bits_per_pixel); pixel_format(v, v->bits_per_pixel);
nominal_timings(v);
return 0; return 0;
} }

View file

@ -234,6 +234,12 @@ ac97_queue(struct device *d, const void *pcm, int bytes)
// just written is what makes this a queue rather than a one-shot. // just written is what makes this a queue rather than a one-shot.
outb(a->nabm + PO_LVI, (uint8)idx); 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); release(&a->lock);
return take; return take;
} }
@ -263,6 +269,13 @@ ac97_stop(struct device *d)
acquire(&a->lock); acquire(&a->lock);
outb(a->nabm + PO_CR, 0); outb(a->nabm + PO_CR, 0);
outb(a->nabm + PO_CR, CR_RR); // reset the box: CIV, LVI, PICB to 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++) for (int i = 0; i < NDESC; i++)
a->filled[i] = 0; a->filled[i] = 0;
a->head = 0; a->head = 0;

View file

@ -427,6 +427,11 @@ file_poll(struct file *f, int events)
(f->ip->major == PTM_MAJOR || f->ip->major == PTS_MAJOR)) (f->ip->major == PTM_MAJOR || f->ip->major == PTS_MAJOR))
return pty_poll(f->ip, events, f->readable, f->writable); return pty_poll(f->ip, events, f->readable, f->writable);
// /dev/dsp is writable when the driver has room. ALSA's OSS plugin waits
// in poll() for exactly this between periods.
if(f->ip && f->ip->type == T_DEV && f->ip->major == AUDIO_MAJOR)
return (events & POLLOUT) && f->writable && dsp_writable() ? POLLOUT : 0;
if(f->ip && f->ip->type == T_DEV && f->ip->major == CONSOLE){ if(f->ip && f->ip->type == T_DEV && f->ip->major == CONSOLE){
int rev = 0; int rev = 0;
if((events & POLLIN) && f->readable && console_input_ready(f->ip ? f->ip->minor : 1)) if((events & POLLIN) && f->readable && console_input_ready(f->ip ? f->ip->minor : 1))

View file

@ -2794,6 +2794,10 @@ procdump(void)
for(i=0; i<10 && pc[i] != 0; i++) for(i=0; i<10 && pc[i] != 0; i++)
cprintf(" %p", pc[i]); cprintf(" %p", pc[i]);
} }
// Where a thread that is not asleep last was in user space: the question
// when a program spins without making a system call.
if((p->state == RUNNABLE || p->state == RUNNING) && p->tf && p->pgdir)
cprintf(" user rip %p rsp %p", p->tf->rip, p->tf->rsp);
cprintf("\n"); cprintf("\n");
} }
} }

View file

@ -34,16 +34,25 @@ tmux
mc mc
ncdu ncdu
fastfetch fastfetch
# Games. In the terminal: nethack, the BSD set, and two toys. On X: lbreakout2 # Games. In the terminal: nethack, the BSD set, and two toys. On X:
# (SDL 1.2) and the classic X clients. # lbreakouthd (SDL 2) and the classic X clients. Not lbreakout2: its stk.c
# hands vsnprintf a size of 511 + the prefix length, and Alpine's fortified
# headers trap on that before it loads its first image -- on Alpine too.
nethack nethack
bsd-games bsd-games
cmatrix cmatrix
sl sl
lbreakout2 lbreakouthd
xeyes xeyes
xclock xclock
xcalc xcalc
# A browser: NetSurf, on GTK 3 -- glib's threads, cairo, pango, TLS. The step # A browser: NetSurf, on GTK 3 -- glib's threads, cairo, pango, TLS. The step
# before WebKit (epiphany), which is `apk add` away. # before WebKit (epiphany), which is `apk add` away.
netsurf netsurf
# curl's command line; libcurl is already here for NetSurf.
curl
# Sound for everything that speaks ALSA, SDL included: alsa-lib with the OSS
# plugin, pointed at /dev/dsp by schos-base's /etc/asound.conf. SchOS has OSS,
# not ALSA's kernel side, and SDL 3 has no OSS backend of its own.
alsa-lib
alsa-plugins

View file

@ -1,6 +1,8 @@
# Generated by tools/posix/alpine-lock.sh from world. Do not edit; # Generated by tools/posix/alpine-lock.sh from world. Do not edit;
# change world and run `make alpine-lock`. # change world and run `make alpine-lock`.
# file sha256 repository # file sha256 repository
alsa-lib-1.2.15.3-r0.apk 052de5639df635a03cfae1d7504ed4ed75c4e2d2cfff59efc116bb847c1f4ac4 main
alsa-plugins-1.2.12-r2.apk 0ac6513add2a5268f67231b241473b4b7b43f5aad9f4674c900525e5152e0752 community
at-spi2-core-2.60.5-r0.apk c9989ba9b2ff44f87a2106ca9560c2e554a56811d0fc2863b22741d576f1d84f main at-spi2-core-2.60.5-r0.apk c9989ba9b2ff44f87a2106ca9560c2e554a56811d0fc2863b22741d576f1d84f main
at-spi2-core-libs-2.60.5-r0.apk bcd4846311c8b19ad7e08ffc3cf726ddbe3d36bbc524386bda64758f5c5981a6 main at-spi2-core-libs-2.60.5-r0.apk bcd4846311c8b19ad7e08ffc3cf726ddbe3d36bbc524386bda64758f5c5981a6 main
avahi-libs-0.9_rc4-r0.apk 380d59c58cfce9cb5cf342cf73947749ca77a880f729970e2f508aaa6fc2f713 main avahi-libs-0.9_rc4-r0.apk 380d59c58cfce9cb5cf342cf73947749ca77a880f729970e2f508aaa6fc2f713 main
@ -17,6 +19,8 @@ cairo-gobject-1.18.4-r1.apk ec302c75a24edaa4a2f867cfb5eec85cf30efe184044bab6e2b9
c-ares-1.34.9-r0.apk 3539a3e11c1ef1516117d8179a546ac882086e94e0d78ad51d687b3fa2b3b5f2 main c-ares-1.34.9-r0.apk 3539a3e11c1ef1516117d8179a546ac882086e94e0d78ad51d687b3fa2b3b5f2 main
cmatrix-2.0-r2.apk d9f6bea3851492a6db2a9cd3f7e2df0f0756c6651a702b36ed2b887328ad1f00 community cmatrix-2.0-r2.apk d9f6bea3851492a6db2a9cd3f7e2df0f0756c6651a702b36ed2b887328ad1f00 community
cups-libs-2.4.19-r1.apk 5739c568d90f23006d74ff82619e1a18447a6f698ae2af61970debe6658a7bea main cups-libs-2.4.19-r1.apk 5739c568d90f23006d74ff82619e1a18447a6f698ae2af61970debe6658a7bea main
curl-8.22.0-r0.apk 9262352e6341bb87e3047d1ca0b4051b2a9f4011409df25e4190e6be0334b860 main
curl-zsh-completion-8.22.0-r0.apk 182a5f0afb5fd8786137f4610c065c305aa593fd18994df908c146335fb6011c main
dbus-libs-1.16.2-r2.apk 105f6ba17454c587ca9345f9deb348012d1950588a22da42120c954d396c99b6 main dbus-libs-1.16.2-r2.apk 105f6ba17454c587ca9345f9deb348012d1950588a22da42120c954d396c99b6 main
e2fsprogs-libs-1.47.4-r0.apk 2e067395a6981b06cfd2cd3abf0e6c6574f7ef64d02e01f75fc5323d4a337fb9 main e2fsprogs-libs-1.47.4-r0.apk 2e067395a6981b06cfd2cd3abf0e6c6574f7ef64d02e01f75fc5323d4a337fb9 main
encodings-1.1.0-r0.apk f7c0eb762638d0249b4fbbe07a8e4ed6aab8e86b74c239c40e47a36219777d52 main encodings-1.1.0-r0.apk f7c0eb762638d0249b4fbbe07a8e4ed6aab8e86b74c239c40e47a36219777d52 main
@ -44,7 +48,8 @@ harfbuzz-13.2.1-r0.apk 01f7862265ef2256bbadd0fc998df3d1243dee606df3bd8284471cf39
htop-3.5.3-r0.apk 6f6d444ca9a4133c2c13323ded04128783d59f924bcff0276d150a982904a2f3 main htop-3.5.3-r0.apk 6f6d444ca9a4133c2c13323ded04128783d59f924bcff0276d150a982904a2f3 main
hwdata-pci-0.408-r0.apk fe61404e6d2bd1e04c021d53641c0ebce97911084d77994e43a288a1278686c1 main hwdata-pci-0.408-r0.apk fe61404e6d2bd1e04c021d53641c0ebce97911084d77994e43a288a1278686c1 main
hwdata-usb-0.408-r0.apk 272d365cb05dd085dd8022154cf71e3f3b0aaddd41c1269eeaf939c9d38675c8 main hwdata-usb-0.408-r0.apk 272d365cb05dd085dd8022154cf71e3f3b0aaddd41c1269eeaf939c9d38675c8 main
lbreakout2-2.6.5-r2.apk 2c0f448af62e8138d9e612a858198f4bb906af33db50a32bb4bd468239521449 community lbreakouthd-1.2.2-r0.apk 4d7f78d866a7c544132e5f159d18f978515ac948517f2b8a6c625756bb81a3e0 community
lbreakouthd-data-1.2.2-r0.apk 28ce98df27bfbb442fa397fbb9eb3eb5464e5b7b040ea034f207be52f4f4f07f community
lcms2-2.19-r0.apk b4cb038af3c2f02bedd8456ae876c97ac0a9f29789add77871bfd6ce084ff136 main lcms2-2.19-r0.apk b4cb038af3c2f02bedd8456ae876c97ac0a9f29789add77871bfd6ce084ff136 main
libatk-1.0-2.60.5-r0.apk ab5f603b17f0f0c5577d259b8263c5cbc6c7b3fd031f06ddc1e3e26417e1609a main libatk-1.0-2.60.5-r0.apk ab5f603b17f0f0c5577d259b8263c5cbc6c7b3fd031f06ddc1e3e26417e1609a main
libatk-bridge-2.0-2.60.5-r0.apk f5e38bc54bae780680134f7b96ce8e01ef24e98ae0ac67ded7229af07aa0b0f1 main libatk-bridge-2.0-2.60.5-r0.apk f5e38bc54bae780680134f7b96ce8e01ef24e98ae0ac67ded7229af07aa0b0f1 main
@ -78,6 +83,7 @@ libpciaccess-0.19-r0.apk 03fdccd1e2a0825e7f7fabbac67212e05d271d31d2eff44cc92f707
libpng-1.6.59-r0.apk d16aeb0597f2bc43e0583ac9ff779388ec160c3e22568b640122e94ba55be6d7 main libpng-1.6.59-r0.apk d16aeb0597f2bc43e0583ac9ff779388ec160c3e22568b640122e94ba55be6d7 main
libpsl-0.21.5-r3.apk ec665a344b6e887f00f0a47015cc732c3a281b69e84641dc8662ea234fde6cdb main libpsl-0.21.5-r3.apk ec665a344b6e887f00f0a47015cc732c3a281b69e84641dc8662ea234fde6cdb main
librsvg-2.62.3-r0.apk e2aeb4a2b6b1ab20627dbb0e9136ea90f7120ec61412ed52f4335ee231361fde community librsvg-2.62.3-r0.apk e2aeb4a2b6b1ab20627dbb0e9136ea90f7120ec61412ed52f4335ee231361fde community
libsamplerate-0.2.2-r3.apk d47765972e0f4b7c5585a5b27957ffe9bc63d4cc9729134fd876ee5d3f5f986d main
libseccomp-2.6.1-r0.apk b22ccfe8cb072e4c8d3bc17222eeda132e270ce6258c27622acb193bf27b48e1 main libseccomp-2.6.1-r0.apk b22ccfe8cb072e4c8d3bc17222eeda132e270ce6258c27622acb193bf27b48e1 main
libsharpyuv-1.6.0-r0.apk 34621ff53f43030d24d1224fe9cb53b1590ab614cbce6080005cde703e6c874a main libsharpyuv-1.6.0-r0.apk 34621ff53f43030d24d1224fe9cb53b1590ab614cbce6080005cde703e6c874a main
libsm-1.2.6-r0.apk dd7eff58a46fd60e4afee9c7c1c3508920b0e1d6560129e59aee82329f68afc8 main libsm-1.2.6-r0.apk dd7eff58a46fd60e4afee9c7c1c3508920b0e1d6560129e59aee82329f68afc8 main
@ -138,10 +144,11 @@ pango-1.57.1-r0.apk c616b62df908026ef413b338b9043e36706b05b6d035049394e5d14022e6
pcre2-10.49-r0.apk 2919897914820f42a17a8d7d37df310d2dfad1c82ebe5f539cc5c765f916752a main pcre2-10.49-r0.apk 2919897914820f42a17a8d7d37df310d2dfad1c82ebe5f539cc5c765f916752a main
pixman-0.46.4-r0.apk 378550cbe348dda9b0733014375dba87a89f0077c5f3dbeba7dce8dbcdda707e main pixman-0.46.4-r0.apk 378550cbe348dda9b0733014375dba87a89f0077c5f3dbeba7dce8dbcdda707e main
pkgconf-2.5.1-r0.apk 20cc47b76b82e9424a35ecc4fdd7bd904ef49ada1684214910fae31bc0cab737 main pkgconf-2.5.1-r0.apk 20cc47b76b82e9424a35ecc4fdd7bd904ef49ada1684214910fae31bc0cab737 main
sdl12-compat-1.2.76-r1.apk 124791270d613d1e33115625b95725487fe76ccfbc08638b8baa1b180287deb9 community
sdl2-compat-2.32.68-r0.apk 0c4c1279446cba3c2065a3d3c2cacfa9eb4aeff507757c5a37193aaf028c5cf7 community sdl2-compat-2.32.68-r0.apk 0c4c1279446cba3c2065a3d3c2cacfa9eb4aeff507757c5a37193aaf028c5cf7 community
sdl2_image-2.8.12-r0.apk 9d9f337de7fe25c363700701b8dd76ff4026de9c3d977e321ada08058b57eeda community
sdl2_mixer-2.8.2-r0.apk 504b68ae9cf6705942853bb6977d3b341615c72066031e5aff54d0c6aca76964 community
sdl2_ttf-2.24.0-r0.apk 0efcd7f03a1c70fc87706b0bb1e95b8747a1b796f089af56f296bf764ac8d988 community
sdl3-3.4.10-r0.apk e9e4537d455f8b710a1f6fed7607c3cdb492fdc155684cf38e1b054bba809427 community sdl3-3.4.10-r0.apk e9e4537d455f8b710a1f6fed7607c3cdb492fdc155684cf38e1b054bba809427 community
sdl_mixer-1.2.12-r3.apk 9733a4e906b76f8bb5f246578ffc123a39a489fbfe60212abbaaaa0782e62e1c community
shared-mime-info-2.4-r7.apk b0ed78b502cacfa2dfb6ff87aaef36a034258026873ab5b73f5e603d6623a147 main shared-mime-info-2.4-r7.apk b0ed78b502cacfa2dfb6ff87aaef36a034258026873ab5b73f5e603d6623a147 main
sl-5.02-r1.apk aa3ceb845953904ab61459f0f97416903c56a80b428f8bc320d879b9e58c1a57 community sl-5.02-r1.apk aa3ceb845953904ab61459f0f97416903c56a80b428f8bc320d879b9e58c1a57 community
slang-2.3.3-r3.apk 96daf8e02cbcc0d4fd7163d087ae2a71a1aea279a0ac8d2a00a1612ccdccf644 main slang-2.3.3-r3.apk 96daf8e02cbcc0d4fd7163d087ae2a71a1aea279a0ac8d2a00a1612ccdccf644 main

View file

@ -211,6 +211,32 @@ exec "$@"
BEOF BEOF
chmod 755 "$P/usr/bin/bwrap" chmod 755 "$P/usr/bin/bwrap"
# ALSA's default device, through the OSS plugin to /dev/dsp. The plug in front
# converts whatever a program plays to what every SchOS audio driver accepts:
# 16-bit stereo at 48 kHz (AC'97 without variable rate plays nothing else, and
# the plugin refuses a rate the device rounds by more than 1%).
mkdir -p "$P/etc"
cat > "$P/etc/asound.conf" <<'AEOF'
# SchOS: ALSA plays through OSS's /dev/dsp (kernel/dev/audio.c).
pcm.!default {
type plug
slave {
pcm "dsp"
format S16_LE
channels 2
rate 48000
}
}
pcm.dsp {
type oss
device /dev/dsp
}
ctl.!default {
type hw
card 0
}
AEOF
# Mount points and runtime directories are alpine-baselayout's; an empty # Mount points and runtime directories are alpine-baselayout's; an empty
# directory here only duplicates it. # directory here only duplicates it.
for d in dev proc sys run tmp srv; do for d in dev proc sys run tmp srv; do