[alsa-devel] Writing a driver for a device with no device-side buffer pointer
Paul
paul at mrarm.io
Mon Jul 29 14:42:31 CEST 2019
Hello,
I am trying to write an ALSA driver for the T2 controller present in the
2018 MacBook Pro machine by reverse engineering the OS X driver, however
I have ran into synchronization issues.
The controller uses a ring buffer for the actual audio transfer, however
there's no way for me to tell what position the device is currently
reading from. Other than the ring buffer the device utilizes a two-way
command protocol. Additionally, the host sends a timestamp to the device
every 150ms.
When starting audio playback the host sends a StartIO command to the
device, and then the device periodically sends an UpdateTimestamp
command to the host which sends the timestamp in the host's time (by
using the timestamp the host sent to it some time before and
appropriately offsetting it; the update is sent ~every 0.346s; this is
the amount of time that can be stored in the ring buffer at a time -
it's forced at 48kHz, with 0x4100 values for each channel in the buffer
which also calculates to basically this value; I'm not 100% sure about
the full purpose of this command as I suspect it might be also used to
reset the buffer position, but this is just a guess). When the playback
is finished, a StopIO command is sent by the host.
I tried searching in the Linux kernel for devices using a similar system
but I had trouble finding any results. I have also tried returning an
approximated (by using the synched up time) pointer, however that ended
up in garbage audio output. I also tried returning the
substream->runtime->control->appl_ptr pointer, but this also ended up in
garbage audio output.
I have observed the following results while the default test tools:
- speaker-test is the closest to giving any output making sense. When
using the `sine` mode, you can however hear a clear break every 0.5s or
around that. In `pink` mode, the noise generally makes sense but sounds
distorted around this 0.5s interval as well. In `wav` mode a repeating
"front-" sound can be heard. These observations were originally made
when I returned 0 from pcm_pointer, attempting the other methods however
ended up in the same result (appl_ptr in particular had to end up this
way because it simply ended up in a zero return from the pointer method,
as speaker-test seems to fill the whole buffer).
- aplay gives either no audible output (0 return) or garbage audio (the
appl_ptr and time sync approaches)
I have trouble understanding how would this system be implemented using
the ALSA's pointer functions. Any help, explanations, code snippets or
references to the documentation or existing Linux drivers which may
share similarities with this device would be greatly appreciated.
I am also attaching code snippets in case they are of any use for my
particular issue.
Thank you,
Paul Pawlowski
Additional configuration information:
static struct snd_pcm_hardware aaudio_basic_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME |
SNDRV_PCM_INFO_SYNC_APPLPTR),
.formats = SNDRV_PCM_FMTBIT_S24_LE,
.rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000,
.rate_max = 48000,
.channels_min = 4,
.channels_max = 4,
.buffer_bytes_max = 0x41000,
.period_bytes_min = 0x10,
.period_bytes_max = 0x10,
.periods_min = 0x4100,
.periods_max = 0x4100,
};
The "time-sync" test approach way of managing the pointer:
static snd_pcm_uframes_t aaudio_pcm_pointer(struct snd_pcm_substream
*substream)
{
ktime_t b;
struct aaudio_device *a = snd_pcm_substream_chip(substream);
b = ktime_get_boottime() - a->recv_ts_remote;
b = (ktime_to_ns(b) % 346666666) * 0x41000 / 346666666;
b -= b % 0x10;
return bytes_to_frames(substream->runtime, b);
}
More information about the Alsa-devel
mailing list