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
profile_reset(alsa_device_profile * profile)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
profile_init(alsa_device_profile * profile,int direction)76 void profile_init(alsa_device_profile* profile, int direction)
77 {
78 profile->direction = direction;
79 profile_reset(profile);
80 }
81
profile_is_initialized(const alsa_device_profile * profile)82 bool profile_is_initialized(const alsa_device_profile* profile)
83 {
84 return profile->card >= 0 && profile->device >= 0;
85 }
86
profile_is_valid(const alsa_device_profile * profile)87 bool profile_is_valid(const alsa_device_profile* profile) {
88 return profile->is_valid;
89 }
90
profile_is_cached_for(const alsa_device_profile * profile,int card,int device)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
profile_decache(alsa_device_profile * profile)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 */
round_to_16_mult(unsigned int size)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 */
profile_calc_min_period_size(const alsa_device_profile * profile,unsigned sample_rate)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
profile_get_period_size(const alsa_device_profile * profile,unsigned sample_rate)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 */
profile_get_default_sample_rate(const alsa_device_profile * profile)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
profile_is_sample_rate_valid(const alsa_device_profile * profile,unsigned rate)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 */
profile_get_default_format(const alsa_device_profile * profile)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
profile_is_format_valid(const alsa_device_profile * profile,enum pcm_format fmt)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 */
profile_get_default_channel_count(const alsa_device_profile * profile)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
profile_get_closest_channel_count(const alsa_device_profile * profile,unsigned count)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
profile_is_channel_count_valid(const alsa_device_profile * profile,unsigned count)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
profile_test_sample_rate(const alsa_device_profile * profile,unsigned rate)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
profile_enum_sample_rates(alsa_device_profile * profile,unsigned min,unsigned max)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
profile_enum_sample_formats(alsa_device_profile * profile,struct pcm_mask * mask)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
profile_enum_channel_counts(alsa_device_profile * profile,unsigned min,unsigned max)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 */
read_alsa_device_config(alsa_device_profile * profile,struct pcm_config * config)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
profile_read_device_info(alsa_device_profile * profile)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
profile_get_sample_rate_strs(const alsa_device_profile * profile)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
profile_get_format_strs(const alsa_device_profile * profile)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
profile_get_channel_count_strs(const alsa_device_profile * profile)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
profile_dump(const alsa_device_profile * profile,int fd)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