[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