Linux bit depth crash - fixed

Sub forums for various specialist XMOS applications. e.g. USB audio, motor control and robotics.
Posts: 20
Joined: Fri Jul 01, 2016 6:36 am

Linux bit depth crash - fixed

Postby LumiCore » Thu Feb 08, 2018 10:15 am

Hi All,

This is for anyone who experienced USB audio dropouts on Linux based hosts. I could not find any references to this issue online, forgive me if I post something here that is old news.

Our design is based on the sw_usb_audio-[sw]_6.15.2rc1 release. The issue we encountered is that if you play music on Linux based hosts and go from a track with a certain bit depth to another one, the system would hang. A short white noise like sound is to be heard, after which the host looses connection with the USB stack.

So for example switching from a 44.1k/16b to a 48k/24b is no problem, but if the sample rate stays equal; (for example 44.1/16b to 44.1/24b) you will get this bug.
(Assuming you have your alternate defines set to "STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS 16")
Test platform in our case is a Roon Ready app (v1.1.27) running on a Antipodes (v2.5) server, the USB output to our Kii Control. In hindsight we have also experienced this bug with other Linux based streamers, but never have been able to connect the dots.

I've hooked up the XTAG to our Kii Control to see what happened during a crash. It turns out that there was a readout of an array (outAudioBuff) which was out of bounds. The read pointer (g_aud_from_host_rdptr) was way beyond anything valid.
If I am correct, it is caused by the fact that "aud_data_remaining_to_device" does not get reset when there is a "SET_STREAM_FORMAT_OUT" action. So just add "SET_SHARED_GLOBAL(aud_data_remaining_to_device, 0);" to this part of the code:

Code: Select all

else if(tmp == SET_STREAM_FORMAT_OUT)
   unsigned dataFormat, sampRes;
   unsigned dsdMode = DSD_MODE_OFF;

   /* Change in OUT channel count - note we expect this on every stream start event */
   SET_SHARED_GLOBAL(g_freqChange_flag, 0);
   GET_SHARED_GLOBAL(g_numUsbChan_Out, g_formatChange_NumChans);
   GET_SHARED_GLOBAL(g_curSubSlot_Out, g_formatChange_SubSlot);
   GET_SHARED_GLOBAL(dataFormat, g_formatChange_DataFormat);
   GET_SHARED_GLOBAL(sampRes, g_formatChange_SampRes);

   /* Reset OUT buffer state */
   SET_SHARED_GLOBAL(g_aud_from_host_rdptr, aud_from_host_fifo_start);
   SET_SHARED_GLOBAL(g_aud_from_host_wrptr, aud_from_host_fifo_start);
   SET_SHARED_GLOBAL(aud_data_remaining_to_device, 0);
   unpackState = 0;

   outUnderflow = 1;
      /* If we were previously in overflow we wont have marked as ready */
      XUD_SetReady_OutPtr(aud_from_host_usb_ep, aud_from_host_fifo_start+4);
      outOverflow = 0;

   #ifdef NATIVE_DSD
   if(dataFormat == UAC_FORMAT_TYPEI_RAW_DATA)
      dsdMode = DSD_MODE_NATIVE;
   /* Wait for the audio code to request samples and respond with command */
   outct(c_mix_out, SET_STREAM_FORMAT_OUT);
   outuint(c_mix_out, dsdMode);
   outuint(c_mix_out, sampRes);

   /* Wait for handshake back */
   chkct(c_mix_out, XS1_CT_END);
    asm volatile("outct res[%0],%1"::"r"(buffer_aud_ctl_chan),"r"(XS1_CT_END));

   SET_SHARED_GLOBAL(g_freqChange, 0);

Maybe I'm wrong here, but it fixes the problem on our end. If anyone else can reproduce this in the field, that would be great!

Another thing i've noticed is that in the start of decouple thread a buffer limit seems to be invalid:

Code: Select all

aud_from_host_fifo_end = aud_from_host_fifo_start + BUFF_SIZE_OUT*4;

Because the buffer size is set to:

Code: Select all

unsigned outAudioBuff[BUFF_SIZE_OUT + (MAX_USB_AUD_PACKET_SIZE>>2) + 4];

Which in my case (max sample rate is 768k) results in a buffer size of 1053, where the limit is now set to 3168.
Maybe someone at XMOS can shed some light on this?

Bart van der Laan
User avatar
XCore Legend
Posts: 1120
Joined: Thu May 27, 2010 10:08 am

Postby infiniteimprobability » Mon Feb 19, 2018 3:21 pm

Hi Bart,
thanks for reporting this and appreciate the level of detail provided. The buffer sizes have been calculated and tested (pretty rigorously in house and by hundreds of customers) but few designers have pushed it to such high rates and array bounds checking is disabled at runtime for performance reasons. We are not immune to latent bugs that have not yet been exposed so it is certainly possible that issues may creep in at high (and less well tested) rates.

Your fix looks completely sensible - I cannot see why we shouldn't reset aud_data_remaining_to_device always just as we do in the SET_SAMPLE_FREQ case. I'll file a bug against the latest version.

Thanks again for reporting this.

Who is online

Users browsing this forum: No registered users and 0 guests