Home | History | Annotate | Download | only in alsa_utils
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "alsa_device_profile"
     18 /*#define LOG_NDEBUG 0*/
     19 /*#define LOG_PCM_PARAMS 0*/
     20 
     21 #include <errno.h>
     22 #include <inttypes.h>
     23 #include <stdint.h>
     24 #include <stdlib.h>
     25 #include <cutils/properties.h>
     26 
     27 #include <log/log.h>
     28 
     29 #include "include/alsa_device_profile.h"
     30 #include "include/alsa_format.h"
     31 #include "include/alsa_logging.h"
     32 
     33 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
     34 
     35 #define PERIOD_DURATION_US (5 * 1000)
     36 
     37 #define DEFAULT_PERIOD_SIZE 1024
     38 
     39 static const char * const format_string_map[] = {
     40     "AUDIO_FORMAT_PCM_16_BIT",      /* "PCM_FORMAT_S16_LE", */
     41     "AUDIO_FORMAT_PCM_32_BIT",      /* "PCM_FORMAT_S32_LE", */
     42     "AUDIO_FORMAT_PCM_8_BIT",       /* "PCM_FORMAT_S8", */
     43     "AUDIO_FORMAT_PCM_8_24_BIT",    /* "PCM_FORMAT_S24_LE", */
     44     "AUDIO_FORMAT_PCM_24_BIT_PACKED"/* "PCM_FORMAT_S24_3LE" */
     45 };
     46 
     47 extern int8_t const pcm_format_value_map[50];
     48 
     49 /* Sort these in terms of preference (best first).
     50    192 kHz is not first because it requires significant resources for possibly worse
     51    quality and driver instability (depends on device).
     52    The order here determines the default sample rate for the device.
     53    AudioPolicyManager may not respect this ordering when picking sample rates.
     54    Update MAX_PROFILE_SAMPLE_RATES after changing the array size.
     55 
     56    TODO: remove 32000, 22050, 12000, 11025?  Each sample rate check
     57    requires opening the device which may cause pops. */
     58 static const unsigned std_sample_rates[] =
     59     {96000, 88200, 192000, 176400, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000};
     60 
     61 static void profile_reset(alsa_device_profile* profile)
     62 {
     63     profile->card = profile->device = -1;
     64 
     65     /* terminate the attribute arrays with invalid values */
     66     profile->formats[0] = PCM_FORMAT_INVALID;
     67     profile->sample_rates[0] = 0;
     68     profile->channel_counts[0] = 0;
     69 
     70     profile->min_period_size = profile->max_period_size = 0;
     71     profile->min_channel_count = profile->max_channel_count = DEFAULT_CHANNEL_COUNT;
     72 
     73     profile->is_valid = false;
     74 }
     75 
     76 void profile_init(alsa_device_profile* profile, int direction)
     77 {
     78     profile->direction = direction;
     79     profile_reset(profile);
     80 }
     81 
     82 bool profile_is_initialized(const alsa_device_profile* profile)
     83 {
     84     return profile->card >= 0 && profile->device >= 0;
     85 }
     86 
     87 bool profile_is_valid(const alsa_device_profile* profile) {
     88     return profile->is_valid;
     89 }
     90 
     91 bool profile_is_cached_for(const alsa_device_profile* profile, int card, int device) {
     92     return card == profile->card && device == profile->device;
     93 }
     94 
     95 void profile_decache(alsa_device_profile* profile) {
     96     profile_reset(profile);
     97 }
     98 
     99 /*
    100  * Returns the supplied value rounded up to the next even multiple of 16
    101  */
    102 static unsigned int round_to_16_mult(unsigned int size)
    103 {
    104     return (size + 15) & ~15;   /* 0xFFFFFFF0; */
    105 }
    106 
    107 /*
    108  * Returns the system defined minimum period size based on the supplied sample rate.
    109  */
    110 unsigned profile_calc_min_period_size(const alsa_device_profile* profile, unsigned sample_rate)
    111 {
    112     ALOGV("profile_calc_min_period_size(%p, rate:%d)", profile, sample_rate);
    113     if (profile == NULL) {
    114         return DEFAULT_PERIOD_SIZE;
    115     } else {
    116         unsigned period_us = property_get_int32("ro.audio.usb.period_us", PERIOD_DURATION_US);
    117         unsigned num_sample_frames = ((uint64_t)sample_rate * period_us) / 1000000;
    118 
    119         if (num_sample_frames < profile->min_period_size) {
    120             num_sample_frames = profile->min_period_size;
    121         }
    122         return round_to_16_mult(num_sample_frames);
    123     }
    124 }
    125 
    126 unsigned int profile_get_period_size(const alsa_device_profile* profile, unsigned sample_rate)
    127 {
    128     unsigned int period_size = profile_calc_min_period_size(profile, sample_rate);
    129     ALOGV("profile_get_period_size(rate:%d) = %d", sample_rate, period_size);
    130     return period_size;
    131 }
    132 
    133 /*
    134  * Sample Rate
    135  */
    136 unsigned profile_get_default_sample_rate(const alsa_device_profile* profile)
    137 {
    138     /*
    139      * TODO this won't be right in general. we should store a preferred rate as we are scanning.
    140      * But right now it will return the highest rate, which may be correct.
    141      */
    142     return profile_is_valid(profile) ? profile->sample_rates[0] : DEFAULT_SAMPLE_RATE;
    143 }
    144 
    145 bool profile_is_sample_rate_valid(const alsa_device_profile* profile, unsigned rate)
    146 {
    147     if (profile_is_valid(profile)) {
    148         size_t index;
    149         for (index = 0; profile->sample_rates[index] != 0; index++) {
    150             if (profile->sample_rates[index] == rate) {
    151                 return true;
    152             }
    153         }
    154 
    155         return false;
    156     } else {
    157         return rate == DEFAULT_SAMPLE_RATE;
    158     }
    159 }
    160 
    161 /*
    162  * Format
    163  */
    164 enum pcm_format profile_get_default_format(const alsa_device_profile* profile)
    165 {
    166     /*
    167      * TODO this won't be right in general. we should store a preferred format as we are scanning.
    168      */
    169     return profile_is_valid(profile) ? profile->formats[0] : DEFAULT_SAMPLE_FORMAT;
    170 }
    171 
    172 bool profile_is_format_valid(const alsa_device_profile* profile, enum pcm_format fmt) {
    173     if (profile_is_valid(profile)) {
    174         size_t index;
    175         for (index = 0; profile->formats[index] != PCM_FORMAT_INVALID; index++) {
    176             if (profile->formats[index] == fmt) {
    177                 return true;
    178             }
    179         }
    180 
    181         return false;
    182     } else {
    183         return fmt == DEFAULT_SAMPLE_FORMAT;
    184     }
    185 }
    186 
    187 /*
    188  * Channels
    189  */
    190 unsigned profile_get_default_channel_count(const alsa_device_profile* profile)
    191 {
    192     return profile_is_valid(profile) ? profile->channel_counts[0] : DEFAULT_CHANNEL_COUNT;
    193 }
    194 
    195 unsigned profile_get_closest_channel_count(const alsa_device_profile* profile, unsigned count)
    196 {
    197     if (profile_is_valid(profile)) {
    198         if (count < profile->min_channel_count) {
    199             return profile->min_channel_count;
    200         } else if (count > profile->max_channel_count) {
    201             return profile->max_channel_count;
    202         } else {
    203             return count;
    204         }
    205     } else {
    206         return 0;
    207     }
    208 }
    209 
    210 bool profile_is_channel_count_valid(const alsa_device_profile* profile, unsigned count)
    211 {
    212     if (profile_is_initialized(profile)) {
    213         return count >= profile->min_channel_count && count <= profile->max_channel_count;
    214     } else {
    215         return count == DEFAULT_CHANNEL_COUNT;
    216     }
    217 }
    218 
    219 static bool profile_test_sample_rate(const alsa_device_profile* profile, unsigned rate)
    220 {
    221     struct pcm_config config = profile->default_config;
    222     config.rate = rate;
    223 
    224     bool works = false; /* let's be pessimistic */
    225     struct pcm * pcm = pcm_open(profile->card, profile->device,
    226                                 profile->direction, &config);
    227 
    228     if (pcm != NULL) {
    229         works = pcm_is_ready(pcm);
    230         pcm_close(pcm);
    231     }
    232 
    233     return works;
    234 }
    235 
    236 static unsigned profile_enum_sample_rates(alsa_device_profile* profile, unsigned min, unsigned max)
    237 {
    238     unsigned num_entries = 0;
    239     unsigned index;
    240 
    241     for (index = 0; index < ARRAY_SIZE(std_sample_rates) &&
    242                     num_entries < ARRAY_SIZE(profile->sample_rates) - 1;
    243          index++) {
    244         if (std_sample_rates[index] >= min && std_sample_rates[index] <= max
    245                 && profile_test_sample_rate(profile, std_sample_rates[index])) {
    246             profile->sample_rates[num_entries++] = std_sample_rates[index];
    247         }
    248     }
    249     profile->sample_rates[num_entries] = 0; /* terminate */
    250     return num_entries; /* return # of supported rates */
    251 }
    252 
    253 static unsigned profile_enum_sample_formats(alsa_device_profile* profile, struct pcm_mask * mask)
    254 {
    255     const int num_slots = ARRAY_SIZE(mask->bits);
    256     const int bits_per_slot = sizeof(mask->bits[0]) * 8;
    257 
    258     const int table_size = ARRAY_SIZE(pcm_format_value_map);
    259 
    260     int slot_index, bit_index, table_index;
    261     table_index = 0;
    262     int num_written = 0;
    263     for (slot_index = 0; slot_index < num_slots && table_index < table_size;
    264             slot_index++) {
    265         unsigned bit_mask = 1;
    266         for (bit_index = 0;
    267                 bit_index < bits_per_slot && table_index < table_size;
    268                 bit_index++) {
    269             if ((mask->bits[slot_index] & bit_mask) != 0) {
    270                 enum pcm_format format = pcm_format_value_map[table_index];
    271                 /* Never return invalid (unrecognized) or 8-bit */
    272                 if (format != PCM_FORMAT_INVALID && format != PCM_FORMAT_S8) {
    273                     profile->formats[num_written++] = format;
    274                     if (num_written == ARRAY_SIZE(profile->formats) - 1) {
    275                         /* leave at least one PCM_FORMAT_INVALID at the end */
    276                         goto end;
    277                     }
    278                 }
    279             }
    280             bit_mask <<= 1;
    281             table_index++;
    282         }
    283     }
    284 end:
    285     profile->formats[num_written] = PCM_FORMAT_INVALID;
    286     return num_written;
    287 }
    288 
    289 static unsigned profile_enum_channel_counts(alsa_device_profile* profile, unsigned min,
    290         unsigned max)
    291 {
    292     /* modify alsa_device_profile.h if you change the std_channel_counts[] array. */
    293     static const unsigned std_channel_counts[] = {8, 7, 6, 5, 4, 3, 2, 1};
    294 
    295     unsigned num_counts = 0;
    296     unsigned index;
    297     /* TODO write a profile_test_channel_count() */
    298     /* Ensure there is at least one invalid channel count to terminate the channel counts array */
    299     for (index = 0; index < ARRAY_SIZE(std_channel_counts) &&
    300                     num_counts < ARRAY_SIZE(profile->channel_counts) - 1;
    301          index++) {
    302         /* TODO Do we want a channel counts test? */
    303         if (std_channel_counts[index] >= min && std_channel_counts[index] <= max /* &&
    304             profile_test_channel_count(profile, channel_counts[index])*/) {
    305             profile->channel_counts[num_counts++] = std_channel_counts[index];
    306         }
    307     }
    308     // if we have no match with the standard counts, we use the largest (preferred) std count.
    309     if (num_counts == 0) {
    310         ALOGW("usb device does not match std channel counts, setting to %d",
    311                 std_channel_counts[0]);
    312         profile->channel_counts[num_counts++] = std_channel_counts[0];
    313     }
    314     profile->channel_counts[num_counts] = 0;
    315     return num_counts; /* return # of supported counts */
    316 }
    317 
    318 /*
    319  * Reads and decodes configuration info from the specified ALSA card/device.
    320  */
    321 static int read_alsa_device_config(alsa_device_profile * profile, struct pcm_config * config)
    322 {
    323     ALOGV("usb:audio_hw - read_alsa_device_config(c:%d d:%d t:0x%X)",
    324           profile->card, profile->device, profile->direction);
    325 
    326     if (profile->card < 0 || profile->device < 0) {
    327         return -EINVAL;
    328     }
    329 
    330     struct pcm_params * alsa_hw_params =
    331         pcm_params_get(profile->card, profile->device, profile->direction);
    332     if (alsa_hw_params == NULL) {
    333         return -EINVAL;
    334     }
    335 
    336     profile->min_period_size = pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIOD_SIZE);
    337     profile->max_period_size = pcm_params_get_max(alsa_hw_params, PCM_PARAM_PERIOD_SIZE);
    338 
    339     profile->min_channel_count = pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS);
    340     profile->max_channel_count = pcm_params_get_max(alsa_hw_params, PCM_PARAM_CHANNELS);
    341 
    342     int ret = 0;
    343 
    344     /*
    345      * This Logging will be useful when testing new USB devices.
    346      */
    347 #ifdef LOG_PCM_PARAMS
    348     log_pcm_params(alsa_hw_params);
    349 #endif
    350 
    351     config->channels = pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS);
    352     // For output devices, let's make sure we choose at least stereo
    353     // (assuming the device supports it).
    354     if (profile->direction == PCM_OUT &&
    355         config->channels < 2 && pcm_params_get_max(alsa_hw_params, PCM_PARAM_CHANNELS) >= 2) {
    356         config->channels = 2;
    357     }
    358     config->rate = pcm_params_get_min(alsa_hw_params, PCM_PARAM_RATE);
    359     // Prefer 48K or 44.1K
    360     if (config->rate < 48000 &&
    361         pcm_params_get_max(alsa_hw_params, PCM_PARAM_RATE) >= 48000) {
    362         config->rate = 48000;
    363     } else if (config->rate < 44100 &&
    364                pcm_params_get_max(alsa_hw_params, PCM_PARAM_RATE) >= 44100) {
    365         config->rate = 44100;
    366     }
    367     config->period_size = profile_calc_min_period_size(profile, config->rate);
    368     config->period_count = pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIODS);
    369     config->format = get_pcm_format_for_mask(pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT));
    370 #ifdef LOG_PCM_PARAMS
    371     log_pcm_config(config, "read_alsa_device_config");
    372 #endif
    373     if (config->format == PCM_FORMAT_INVALID) {
    374         ret = -EINVAL;
    375     }
    376 
    377     pcm_params_free(alsa_hw_params);
    378 
    379     return ret;
    380 }
    381 
    382 bool profile_read_device_info(alsa_device_profile* profile)
    383 {
    384     if (!profile_is_initialized(profile)) {
    385         return false;
    386     }
    387 
    388     /* let's get some defaults */
    389     read_alsa_device_config(profile, &profile->default_config);
    390     ALOGV("default_config chans:%d rate:%d format:%d count:%d size:%d",
    391           profile->default_config.channels, profile->default_config.rate,
    392           profile->default_config.format, profile->default_config.period_count,
    393           profile->default_config.period_size);
    394 
    395     struct pcm_params * alsa_hw_params = pcm_params_get(profile->card,
    396                                                         profile->device,
    397                                                         profile->direction);
    398     if (alsa_hw_params == NULL) {
    399         return false;
    400     }
    401 
    402     /* Formats */
    403     struct pcm_mask * format_mask = pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT);
    404     profile_enum_sample_formats(profile, format_mask);
    405 
    406     /* Channels */
    407     profile_enum_channel_counts(
    408             profile, pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS),
    409             pcm_params_get_max(alsa_hw_params, PCM_PARAM_CHANNELS));
    410 
    411     /* Sample Rates */
    412     profile_enum_sample_rates(
    413             profile, pcm_params_get_min(alsa_hw_params, PCM_PARAM_RATE),
    414             pcm_params_get_max(alsa_hw_params, PCM_PARAM_RATE));
    415 
    416     profile->is_valid = true;
    417 
    418     pcm_params_free(alsa_hw_params);
    419     return true;
    420 }
    421 
    422 char * profile_get_sample_rate_strs(const alsa_device_profile* profile)
    423 {
    424     /* if we assume that rate strings are about 5 characters (48000 is 5), plus ~1 for a
    425      * delimiter "|" this buffer has room for about 22 rate strings which seems like
    426      * way too much, but it's a stack variable so only temporary.
    427      */
    428     char buffer[128];
    429     buffer[0] = '\0';
    430     size_t buffSize = ARRAY_SIZE(buffer);
    431     size_t curStrLen = 0;
    432 
    433     char numBuffer[32];
    434 
    435     size_t numEntries = 0;
    436     size_t index;
    437     for (index = 0; profile->sample_rates[index] != 0; index++) {
    438         snprintf(numBuffer, sizeof(numBuffer), "%u", profile->sample_rates[index]);
    439         // account for both the null, and potentially the bar.
    440         if (buffSize - curStrLen < strlen(numBuffer) + (numEntries != 0 ? 2 : 1)) {
    441             /* we don't have room for another, so bail at this point rather than
    442              * return a malformed rate string
    443              */
    444             break;
    445         }
    446         if (numEntries++ != 0) {
    447             strlcat(buffer, "|", buffSize);
    448         }
    449         curStrLen = strlcat(buffer, numBuffer, buffSize);
    450     }
    451 
    452     return strdup(buffer);
    453 }
    454 
    455 char * profile_get_format_strs(const alsa_device_profile* profile)
    456 {
    457     /* if we assume that format strings are about 24 characters (AUDIO_FORMAT_PCM_16_BIT is 23),
    458      * plus ~1 for a delimiter "|" this buffer has room for about 10 format strings which seems
    459      *  like way too much, but it's a stack variable so only temporary.
    460      */
    461     char buffer[256];
    462     buffer[0] = '\0';
    463     size_t buffSize = ARRAY_SIZE(buffer);
    464     size_t curStrLen = 0;
    465 
    466     size_t numEntries = 0;
    467     size_t index = 0;
    468     for (index = 0; profile->formats[index] != PCM_FORMAT_INVALID; index++) {
    469         // account for both the null, and potentially the bar.
    470         if (buffSize - curStrLen < strlen(format_string_map[profile->formats[index]])
    471                                    + (numEntries != 0 ? 2 : 1)) {
    472             /* we don't have room for another, so bail at this point rather than
    473              * return a malformed rate string
    474              */
    475             break;
    476         }
    477         if (numEntries++ != 0) {
    478             strlcat(buffer, "|", buffSize);
    479         }
    480         curStrLen = strlcat(buffer, format_string_map[profile->formats[index]], buffSize);
    481     }
    482 
    483     return strdup(buffer);
    484 }
    485 
    486 char * profile_get_channel_count_strs(const alsa_device_profile* profile)
    487 {
    488     // FIXME implicit fixed channel count assumption here (FCC_8).
    489     // we use only the canonical even number channel position masks.
    490     static const char * const out_chans_strs[] = {
    491         /* 0 */"AUDIO_CHANNEL_NONE", /* will never be taken as this is a terminator */
    492         /* 1 */"AUDIO_CHANNEL_OUT_MONO",
    493         /* 2 */"AUDIO_CHANNEL_OUT_STEREO",
    494         /* 3 */ /* "AUDIO_CHANNEL_OUT_STEREO|AUDIO_CHANNEL_OUT_FRONT_CENTER" */ NULL,
    495         /* 4 */"AUDIO_CHANNEL_OUT_QUAD",
    496         /* 5 */ /* "AUDIO_CHANNEL_OUT_QUAD|AUDIO_CHANNEL_OUT_FRONT_CENTER" */ NULL,
    497         /* 6 */"AUDIO_CHANNEL_OUT_5POINT1",
    498         /* 7 */ /* "AUDIO_CHANNEL_OUT_5POINT1|AUDIO_CHANNEL_OUT_BACK_CENTER" */ NULL,
    499         /* 8 */"AUDIO_CHANNEL_OUT_7POINT1",
    500         /* channel counts greater than this not considered */
    501     };
    502 
    503     static const char * const in_chans_strs[] = {
    504         /* 0 */"AUDIO_CHANNEL_NONE", /* will never be taken as this is a terminator */
    505         /* 1 */"AUDIO_CHANNEL_IN_MONO",
    506         /* 2 */"AUDIO_CHANNEL_IN_STEREO",
    507         /* channel counts greater than this not considered */
    508     };
    509 
    510     static const char * const index_chans_strs[] = {
    511         /* 0 */"AUDIO_CHANNEL_NONE", /* will never be taken as this is a terminator */
    512         /* 1 */"AUDIO_CHANNEL_INDEX_MASK_1",
    513         /* 2 */"AUDIO_CHANNEL_INDEX_MASK_2",
    514         /* 3 */"AUDIO_CHANNEL_INDEX_MASK_3",
    515         /* 4 */"AUDIO_CHANNEL_INDEX_MASK_4",
    516         /* 5 */"AUDIO_CHANNEL_INDEX_MASK_5",
    517         /* 6 */"AUDIO_CHANNEL_INDEX_MASK_6",
    518         /* 7 */"AUDIO_CHANNEL_INDEX_MASK_7",
    519         /* 8 */"AUDIO_CHANNEL_INDEX_MASK_8",
    520     };
    521 
    522     const bool isOutProfile = profile->direction == PCM_OUT;
    523 
    524     const char * const * const chans_strs = isOutProfile ? out_chans_strs : in_chans_strs;
    525     const size_t chans_strs_size =
    526             isOutProfile ? ARRAY_SIZE(out_chans_strs) : ARRAY_SIZE(in_chans_strs);
    527 
    528     /*
    529      * If we assume each channel string is 26 chars ("AUDIO_CHANNEL_INDEX_MASK_8" is 26) + 1 for,
    530      * the "|" delimiter, then we allocate room for 16 strings.
    531      */
    532     char buffer[27 * 16 + 1]; /* caution, may need to be expanded */
    533     buffer[0] = '\0';
    534     size_t buffSize = ARRAY_SIZE(buffer);
    535     size_t curStrLen = 0;
    536 
    537     /* We currently support MONO and STEREO, and always report STEREO but some (many)
    538      * USB Audio Devices may only announce support for MONO (a headset mic for example), or
    539      * The total number of output channels. SO, if the device itself doesn't explicitly
    540      * support STEREO, append to the channel config strings we are generating.
    541      *
    542      * The MONO and STEREO positional channel masks are provided for legacy compatibility.
    543      * For multichannel (n > 2) we only expose channel index masks.
    544      */
    545     // Always support stereo
    546     curStrLen = strlcat(buffer, chans_strs[2], buffSize);
    547 
    548     size_t index;
    549     unsigned channel_count;
    550     for (index = 0;
    551          (channel_count = profile->channel_counts[index]) != 0;
    552          index++) {
    553 
    554         /* we only show positional information for mono (stereo handled already) */
    555         if (channel_count < chans_strs_size
    556                 && chans_strs[channel_count] != NULL
    557                 && channel_count < 2 /* positional only for fewer than 2 channels */) {
    558             // account for the '|' and the '\0'
    559             if (buffSize - curStrLen < strlen(chans_strs[channel_count]) + 2) {
    560                 /* we don't have room for another, so bail at this point rather than
    561                  * return a malformed rate string
    562                  */
    563                 break;
    564             }
    565 
    566             strlcat(buffer, "|", buffSize);
    567             curStrLen = strlcat(buffer, chans_strs[channel_count], buffSize);
    568         }
    569 
    570         // handle channel index masks for both input and output
    571         // +2 to account for the '|' and the '\0'
    572          if (buffSize - curStrLen < strlen(index_chans_strs[channel_count]) + 2) {
    573              /* we don't have room for another, so bail at this point rather than
    574               * return a malformed rate string
    575               */
    576              break;
    577          }
    578 
    579          strlcat(buffer, "|", buffSize);
    580          curStrLen = strlcat(buffer, index_chans_strs[channel_count], buffSize);
    581     }
    582 
    583     return strdup(buffer);
    584 }
    585 
    586 void profile_dump(const alsa_device_profile* profile, int fd)
    587 {
    588     if (profile == NULL) {
    589         dprintf(fd, "  %s\n", "No USB Profile");
    590         return; /* bail early */
    591     }
    592 
    593     if (!profile->is_valid) {
    594         dprintf(fd, "  Profile is INVALID");
    595     }
    596 
    597     /* card/device/direction */
    598     dprintf(fd, "  card:%d, device:%d - %s\n",
    599                 profile->card, profile->device, profile->direction == PCM_OUT ? "OUT" : "IN");
    600 
    601     /* formats */
    602     dprintf(fd, "  Formats: ");
    603     for (int fmtIndex = 0;
    604           profile->formats[fmtIndex] != PCM_FORMAT_INVALID && fmtIndex < MAX_PROFILE_FORMATS;
    605           fmtIndex++) {
    606         dprintf(fd, "%d ", profile->formats[fmtIndex]);
    607     }
    608     dprintf(fd, "\n");
    609 
    610     /* sample rates */
    611     dprintf(fd, "  Rates: ");
    612     for (int rateIndex = 0;
    613           profile->sample_rates[rateIndex] != 0 && rateIndex < MAX_PROFILE_SAMPLE_RATES;
    614           rateIndex++) {
    615         dprintf(fd, "%u ", profile->sample_rates[rateIndex]);
    616     }
    617     dprintf(fd, "\n");
    618 
    619     // channel counts
    620     dprintf(fd, "  Channel Counts: ");
    621     for (int cntIndex = 0;
    622           profile->channel_counts[cntIndex] != 0 && cntIndex < MAX_PROFILE_CHANNEL_COUNTS;
    623           cntIndex++) {
    624         dprintf(fd, "%u ", profile->channel_counts[cntIndex]);
    625     }
    626     dprintf(fd, "\n");
    627 
    628     dprintf(fd, "  min/max period size [%u : %u]\n",
    629             profile->min_period_size,profile-> max_period_size);
    630     dprintf(fd, "  min/max channel count [%u : %u]\n",
    631             profile->min_channel_count, profile->max_channel_count);
    632 
    633     // struct pcm_config default_config;
    634     dprintf(fd, "  Default Config:\n");
    635     dprintf(fd, "    channels: %d\n", profile->default_config.channels);
    636     dprintf(fd, "    rate: %d\n", profile->default_config.rate);
    637     dprintf(fd, "    period_size: %d\n", profile->default_config.period_size);
    638     dprintf(fd, "    period_count: %d\n", profile->default_config.period_count);
    639     dprintf(fd, "    format: %d\n", profile->default_config.format);
    640 }
    641