Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions avf/shim/avfboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,17 @@ void avfboot_main(uint64_t fdt_pa)
uart_kv("+", scgpu_size);
uart_putc('\n');
}
/* Scion's speaker (SCSND, xnu/files/bsd/sys/hakios.scsnd.h): a ring of samples, likewise */
uint64_t scsnd_pa = arg_num(m.bootargs, "scsnd.addr="), scsnd_size = arg_num(m.bootargs, "scsnd.size=");
if (scsnd_pa || scsnd_size) {
if (scsnd_pa < m.ram_lo || scsnd_pa + scsnd_size > m.ram_hi || (scsnd_pa & 0xfff) || scsnd_size < 0x10000)
die("scsnd.addr/size do not describe a region in RAM");
if (scsnd_pa < win_end)
win_end = scsnd_pa & ~(BASE_ALIGN - 1);
uart_kv("avfboot: scsnd ", scsnd_pa);
uart_kv("+", scsnd_size);
uart_putc('\n');
}
if (win_end <= phys_base)
die("not enough RAM");
uint64_t win_size = win_end - phys_base;
Expand Down
92 changes: 92 additions & 0 deletions xnu/files/bsd/dev/hakios.scsnd.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* SCSND: the sink of /dev/hakios-audio under AVF, the Scion app (sys/hakios.scsnd.h, patch 1401).
*
* With scsnd.addr= and scsnd.size= the app has lent a region with a header and a ring of samples.
* What the audio server writes goes into the ring as it comes, and the app plays it through Android's
* audio. Never waits (the writer is the audio server's real-time thread): what does not fit is lost,
* which only happens when the app has stopped taking. iOS's volume is passed on in the header.
*/
#include <kern/locks.h>
#include <libkern/libkern.h>
#include <mach/mach_types.h>
#include <mach/vm_param.h>
#include <pexpert/pexpert.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <vm/memory_types.h>
#include <sys/hakios.audio.h>
#include <sys/hakios.scsnd.h>

extern vm_offset_t ml_io_map_unmappable(vm_offset_t phys_addr, vm_size_t size, unsigned int flags);

static volatile struct scsnd_header *scsnd;
static uint8_t *scsnd_ring;
static uint32_t scsnd_bytes;
static uint32_t volume_seen = ~0u;

/* Called with /dev/hakios-audio's lock held: one writer at a time. */
static uint32_t
scsnd_play(const int16_t *frames, uint32_t count)
{
volatile struct scsnd_header *h = scsnd;
uint32_t below, generation;
bool muted;

if (hakios_audio_volume(&below, &muted, &generation) && generation != volume_seen) {
h->below = below;
h->muted = muted ? 1 : 0;
volume_seen = generation;
}
uint32_t head = h->head; /* (ours alone) */
uint32_t tail = __atomic_load_n((uint32_t *)(uintptr_t)&h->tail, __ATOMIC_ACQUIRE);
uint32_t used = head - tail, room = used <= scsnd_bytes ? scsnd_bytes - used : 0;
uint32_t bytes = count * HAKIOS_AUDIO_FRAME;

if (bytes > room) {
bytes = room - room % HAKIOS_AUDIO_FRAME;
}
uint32_t at = head & (scsnd_bytes - 1);
uint32_t first = bytes < scsnd_bytes - at ? bytes : scsnd_bytes - at;
memcpy(scsnd_ring + at, frames, first);
memcpy(scsnd_ring, (const uint8_t *)frames + first, bytes - first);
__atomic_store_n((uint32_t *)(uintptr_t)&h->head, head + bytes, __ATOMIC_RELEASE);
if (bytes < count * HAKIOS_AUDIO_FRAME) {
h->dropped += count - bytes / HAKIOS_AUDIO_FRAME;
}
return bytes / HAKIOS_AUDIO_FRAME;
}

void
hakios_scsnd_init(void)
{
uint64_t pa = 0, size = 0;
volatile struct scsnd_header *h;

if (!PE_parse_boot_argn("scsnd.addr", &pa, sizeof(pa)) || pa == 0) {
return;
}
(void)PE_parse_boot_argn("scsnd.size", &size, sizeof(size));
if ((pa & PAGE_MASK) || size < 0x10000 || size > (64ull << 20)) {
printf("scsnd: scsnd.addr=%#llx scsnd.size=%#llx: not a region\n", pa, size);
return;
}
h = (volatile struct scsnd_header *)ml_io_map_unmappable((vm_offset_t)pa, (vm_size_t)round_page(size), VM_WIMG_DEFAULT);
if (h == NULL || h->magic != SCSND_MAGIC || h->version != SCSND_VERSION) {
printf("scsnd: no header from the app at %#llx (magic %#x): no sound\n", pa, h != NULL ? h->magic : 0);
return;
}
uint32_t off = h->ring_off, bytes = h->ring_bytes;
if (off < sizeof(struct scsnd_header) || bytes < 4096 || (bytes & (bytes - 1)) || (uint64_t)off + bytes > size ||
h->rate != HAKIOS_AUDIO_RATE || h->channels != HAKIOS_AUDIO_CHANNELS) {
printf("scsnd: the app's ring (%u bytes at %u, %u Hz, %u channels) is not one to write into\n", bytes, off,
h->rate, h->channels);
return;
}
scsnd = h;
scsnd_ring = (uint8_t *)(uintptr_t)h + off;
scsnd_bytes = bytes;
__atomic_store_n((uint32_t *)(uintptr_t)&h->guest_ready, 1, __ATOMIC_RELEASE);
printf("scsnd: the app's speaker at %#llx: a ring of %u ms\n", pa,
bytes / HAKIOS_AUDIO_FRAME * 1000 / HAKIOS_AUDIO_RATE);
hakios_audio_set_sink("the Scion app (SCSND)", scsnd_play);
}
44 changes: 44 additions & 0 deletions xnu/files/bsd/sys/hakios.scsnd.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* SCSND: iOS's sound played by the Scion app (patch 1401, for the Android Virtualization Framework).
*
* AVF's own sound device (crosvm's vhost-user virtio-snd) failed to start two times in three on the
* Pixel 10, and the VM went with it. Instead the app lends the guest a region of RAM, as it lends
* the framebuffer's (SCFB), the camera's (SCCAM) and the GPU's (SCGPU), and says where with
* scsnd.addr= scsnd.size= in the kernel parameters; avfboot keeps it out of the kernel's memory.
* The region begins with the header below, which the app writes before the VM starts, and holds a
* ring of samples in /dev/hakios-audio's one format (sys/hakios.audio.h: 48000 frames a second,
* 16-bit, left then right). The kernel's sink (bsd/dev/hakios.scsnd.c) puts in what the audio server
* writes; the app takes it out and plays it through Android's audio (AAudio).
*
* The ring: ring_bytes (a power of two, whole frames) at ring_off from the region's start. head and
* tail count bytes, free-running, modulo 2^32: the guest alone moves head (after the samples are in,
* with a barrier), the app alone moves tail (after it has taken them). What is in: head - tail. The
* guest never waits: what does not fit is lost (counted in dropped). The app never lets more than a
* little build up: if it finds too much, it skips ahead (counted in skipped).
*
* The volume iOS sets on its speaker (HAKIOS_AUDIO_SET_VOLUME) is passed on in below/muted, and the
* app scales by it: iOS's volume is then a part of Android's media volume.
*/
#ifndef _SYS_HAKIOS_SCSND_H_
#define _SYS_HAKIOS_SCSND_H_

#include <stdint.h>

#define SCSND_MAGIC 0x4e534353u /* "SCSN" */
#define SCSND_VERSION 1u

struct scsnd_header {
uint32_t magic, version; /* the app's */
uint32_t ring_off, ring_bytes; /* the app's: where the ring is, and how big */
uint32_t rate, channels; /* the app's: 48000, 2 (what it plays) */
uint32_t head; /* the guest's: bytes put in */
uint32_t tail; /* the app's: bytes taken out */
uint32_t below, muted; /* the guest's: iOS's volume, eighths of a dB under the loudest; muted */
uint32_t guest_ready; /* the guest's: 1 once its sink is in place */
uint32_t dropped; /* the guest's: frames that did not fit */
uint32_t skipped; /* the app's: frames it skipped to catch up */
uint32_t underruns; /* the app's: times it had less than it was asked for */
uint32_t reserved[18]; /* (128 bytes in all) */
};

#endif
27 changes: 27 additions & 0 deletions xnu/patches/1401-avf-scion-sound.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
diff --git a/bsd/conf/files b/bsd/conf/files
index dcc51a5..48d9d32 100644
--- a/bsd/conf/files
+++ b/bsd/conf/files
@@ -145,6 +145,7 @@ bsd/dev/hakios.apfs_check.c optional apfs
bsd/dev/hakios.sound.c standard
bsd/dev/hakios.vgpu3d.c standard
bsd/dev/hakios.vgpu3d_xnu.c standard
+bsd/dev/hakios.scsnd.c standard
bsd/dev/hakios.qmi.c standard
bsd/dev/hakios.nas.c standard
bsd/dev/hakios.wlfw.c standard
diff --git a/bsd/dev/hakios.audio.c b/bsd/dev/hakios.audio.c
index de97d92..77b49c4 100644
--- a/bsd/dev/hakios.audio.c
+++ b/bsd/dev/hakios.audio.c
@@ -347,6 +347,10 @@ hakios_audio_init(void)
report_call = thread_call_allocate(audio_report, NULL);
audio_recording_init();
hakios_virtio_sound_init();
+ {
+ extern void hakios_scsnd_init(void);
+ hakios_scsnd_init(); /* scsnd.addr=: the Scion app plays it (bsd/dev/hakios.scsnd.c) */
+ }
hakios_speaker_init();
hakios_haptic_init();
hakios_torch_init();
Loading