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 * This is probably a poor algorithm. The default sample rate should be the highest (within
140 * limits) rate that is available for both input and output. HOWEVER, the profile has only
141 * one or the other, so that will need to be done at a higher level, like in the HAL.
142 */
143 /*
144 * TODO this won't be right in general. we should store a preferred rate as we are scanning.
145 * But right now it will return the highest rate, which may be correct.
146 */
147 return profile_is_valid(profile) ? profile->sample_rates[0] : DEFAULT_SAMPLE_RATE;
148 }
149
profile_get_highest_sample_rate(const alsa_device_profile * profile)150 unsigned profile_get_highest_sample_rate(const alsa_device_profile* profile) {
151 /* The hightest sample rate is always stored in the first element of sample_rates.
152 * Note that profile_reset() initiaizes the first element of samples_rates to 0
153 * Which is what we want to return if the profile had not been read anyway.
154 */
155 return profile->sample_rates[0];
156 }
157
profile_is_sample_rate_valid(const alsa_device_profile * profile,unsigned rate)158 bool profile_is_sample_rate_valid(const alsa_device_profile* profile, unsigned rate)
159 {
160 if (profile_is_valid(profile)) {
161 size_t index;
162 for (index = 0; profile->sample_rates[index] != 0; index++) {
163 if (profile->sample_rates[index] == rate) {
164 return true;
165 }
166 }
167
168 return false;
169 } else {
170 ALOGW("**** PROFILE NOT VALID!");
171 return rate == DEFAULT_SAMPLE_RATE;
172 }
173 }
174
175 /*
176 * Format
177 */
profile_get_default_format(const alsa_device_profile * profile)178 enum pcm_format profile_get_default_format(const alsa_device_profile* profile)
179 {
180 /*
181 * TODO this won't be right in general. we should store a preferred format as we are scanning.
182 */
183 return profile_is_valid(profile) ? profile->formats[0] : DEFAULT_SAMPLE_FORMAT;
184 }
185
profile_is_format_valid(const alsa_device_profile * profile,enum pcm_format fmt)186 bool profile_is_format_valid(const alsa_device_profile* profile, enum pcm_format fmt) {
187 if (profile_is_valid(profile)) {
188 size_t index;
189 for (index = 0; profile->formats[index] != PCM_FORMAT_INVALID; index++) {
190 if (profile->formats[index] == fmt) {
191 return true;
192 }
193 }
194
195 return false;
196 } else {
197 return fmt == DEFAULT_SAMPLE_FORMAT;
198 }
199 }
200
201 /*
202 * Channels
203 */
profile_get_default_channel_count(const alsa_device_profile * profile)204 unsigned profile_get_default_channel_count(const alsa_device_profile* profile)
205 {
206 return profile_is_valid(profile) ? profile->channel_counts[0] : DEFAULT_CHANNEL_COUNT;
207 }
208
profile_get_closest_channel_count(const alsa_device_profile * profile,unsigned count)209 unsigned profile_get_closest_channel_count(const alsa_device_profile* profile, unsigned count)
210 {
211 if (profile_is_valid(profile)) {
212 if (count < profile->min_channel_count) {
213 return profile->min_channel_count;
214 } else if (count > profile->max_channel_count) {
215 return profile->max_channel_count;
216 } else {
217 return count;
218 }
219 } else {
220 return 0;
221 }
222 }
223
profile_is_channel_count_valid(const alsa_device_profile * profile,unsigned count)224 bool profile_is_channel_count_valid(const alsa_device_profile* profile, unsigned count)
225 {
226 if (profile_is_initialized(profile)) {
227 return count >= profile->min_channel_count && count <= profile->max_channel_count;
228 } else {
229 return count == DEFAULT_CHANNEL_COUNT;
230 }
231 }
232
profile_test_sample_rate(const alsa_device_profile * profile,unsigned rate)233 static bool profile_test_sample_rate(const alsa_device_profile* profile, unsigned rate)
234 {
235 struct pcm_config config = profile->default_config;
236 config.rate = rate;
237
238 bool works = false; /* let's be pessimistic */
239 struct pcm * pcm = pcm_open(profile->card, profile->device,
240 profile->direction, &config);
241
242 if (pcm != NULL) {
243 works = pcm_is_ready(pcm);
244 pcm_close(pcm);
245 }
246
247 return works;
248 }
249
profile_enum_sample_rates(alsa_device_profile * profile,unsigned min,unsigned max)250 static unsigned profile_enum_sample_rates(alsa_device_profile* profile, unsigned min, unsigned max)
251 {
252 unsigned num_entries = 0;
253 unsigned index;
254
255 for (index = 0; index < ARRAY_SIZE(std_sample_rates) &&
256 num_entries < ARRAY_SIZE(profile->sample_rates) - 1;
257 index++) {
258 if (std_sample_rates[index] >= min && std_sample_rates[index] <= max
259 && profile_test_sample_rate(profile, std_sample_rates[index])) {
260 profile->sample_rates[num_entries++] = std_sample_rates[index];
261 }
262 }
263 profile->sample_rates[num_entries] = 0; /* terminate */
264 return num_entries; /* return # of supported rates */
265 }
266
profile_enum_sample_formats(alsa_device_profile * profile,struct pcm_mask * mask)267 static unsigned profile_enum_sample_formats(alsa_device_profile* profile, struct pcm_mask * mask)
268 {
269 const int num_slots = ARRAY_SIZE(mask->bits);
270 const int bits_per_slot = sizeof(mask->bits[0]) * 8;
271
272 const int table_size = ARRAY_SIZE(pcm_format_value_map);
273
274 int slot_index, bit_index, table_index;
275 table_index = 0;
276 int num_written = 0;
277 for (slot_index = 0; slot_index < num_slots && table_index < table_size;
278 slot_index++) {
279 unsigned bit_mask = 1;
280 for (bit_index = 0;
281 bit_index < bits_per_slot && table_index < table_size;
282 bit_index++) {
283 if ((mask->bits[slot_index] & bit_mask) != 0) {
284 enum pcm_format format = pcm_format_value_map[table_index];
285 /* Never return invalid (unrecognized) or 8-bit */
286 if (format != PCM_FORMAT_INVALID && format != PCM_FORMAT_S8) {
287 profile->formats[num_written++] = format;
288 if (num_written == ARRAY_SIZE(profile->formats) - 1) {
289 /* leave at least one PCM_FORMAT_INVALID at the end */
290 goto end;
291 }
292 }
293 }
294 bit_mask <<= 1;
295 table_index++;
296 }
297 }
298 end:
299 profile->formats[num_written] = PCM_FORMAT_INVALID;
300 return num_written;
301 }
302
profile_enum_channel_counts(alsa_device_profile * profile,unsigned min,unsigned max)303 static unsigned profile_enum_channel_counts(alsa_device_profile* profile, unsigned min,
304 unsigned max)
305 {
306 /* modify alsa_device_profile.h if you change the std_channel_counts[] array. */
307 static const unsigned std_channel_counts[] = {8, 7, 6, 5, 4, 3, 2, 1};
308
309 unsigned num_counts = 0;
310 unsigned index;
311 /* TODO write a profile_test_channel_count() */
312 /* Ensure there is at least one invalid channel count to terminate the channel counts array */
313 for (index = 0; index < ARRAY_SIZE(std_channel_counts) &&
314 num_counts < ARRAY_SIZE(profile->channel_counts) - 1;
315 index++) {
316 /* TODO Do we want a channel counts test? */
317 if (std_channel_counts[index] >= min && std_channel_counts[index] <= max /* &&
318 profile_test_channel_count(profile, channel_counts[index])*/) {
319 profile->channel_counts[num_counts++] = std_channel_counts[index];
320 }
321 }
322 // if we have no match with the standard counts, we use the largest (preferred) std count.
323 if (num_counts == 0) {
324 ALOGW("usb device does not match std channel counts, setting to %d",
325 std_channel_counts[0]);
326 profile->channel_counts[num_counts++] = std_channel_counts[0];
327 }
328 profile->channel_counts[num_counts] = 0;
329 return num_counts; /* return # of supported counts */
330 }
331
332 /*
333 * Reads and decodes configuration info from the specified ALSA card/device.
334 */
read_alsa_device_config(alsa_device_profile * profile,struct pcm_config * config)335 static int read_alsa_device_config(alsa_device_profile * profile, struct pcm_config * config)
336 {
337 ALOGV("usb:audio_hw - read_alsa_device_config(c:%d d:%d t:0x%X)",
338 profile->card, profile->device, profile->direction);
339
340 if (profile->card < 0 || profile->device < 0) {
341 return -EINVAL;
342 }
343
344 struct pcm_params * alsa_hw_params =
345 pcm_params_get(profile->card, profile->device, profile->direction);
346 if (alsa_hw_params == NULL) {
347 return -EINVAL;
348 }
349
350 profile->min_period_size = pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIOD_SIZE);
351 profile->max_period_size = pcm_params_get_max(alsa_hw_params, PCM_PARAM_PERIOD_SIZE);
352
353 profile->min_channel_count = pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS);
354 profile->max_channel_count = pcm_params_get_max(alsa_hw_params, PCM_PARAM_CHANNELS);
355
356 int ret = 0;
357
358 /*
359 * This Logging will be useful when testing new USB devices.
360 */
361 #ifdef LOG_PCM_PARAMS
362 log_pcm_params(alsa_hw_params);
363 #endif
364
365 config->channels = pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS);
366 // For output devices, let's make sure we choose at least stereo
367 // (assuming the device supports it).
368 if (profile->direction == PCM_OUT &&
369 config->channels < 2 && pcm_params_get_max(alsa_hw_params, PCM_PARAM_CHANNELS) >= 2) {
370 config->channels = 2;
371 }
372 config->rate = pcm_params_get_min(alsa_hw_params, PCM_PARAM_RATE);
373 // Prefer 48K or 44.1K
374 if (config->rate < 48000 &&
375 pcm_params_get_max(alsa_hw_params, PCM_PARAM_RATE) >= 48000) {
376 config->rate = 48000;
377 } else if (config->rate < 44100 &&
378 pcm_params_get_max(alsa_hw_params, PCM_PARAM_RATE) >= 44100) {
379 config->rate = 44100;
380 }
381 config->period_size = profile_calc_min_period_size(profile, config->rate);
382 config->period_count = pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIODS);
383 config->format = get_pcm_format_for_mask(pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT));
384 #ifdef LOG_PCM_PARAMS
385 log_pcm_config(config, "read_alsa_device_config");
386 #endif
387 if (config->format == PCM_FORMAT_INVALID) {
388 ret = -EINVAL;
389 }
390
391 pcm_params_free(alsa_hw_params);
392
393 return ret;
394 }
395
profile_read_device_info(alsa_device_profile * profile)396 bool profile_read_device_info(alsa_device_profile* profile)
397 {
398 if (!profile_is_initialized(profile)) {
399 return false;
400 }
401
402 /* let's get some defaults */
403 read_alsa_device_config(profile, &profile->default_config);
404 ALOGV("default_config chans:%d rate:%d format:%d count:%d size:%d",
405 profile->default_config.channels, profile->default_config.rate,
406 profile->default_config.format, profile->default_config.period_count,
407 profile->default_config.period_size);
408
409 struct pcm_params * alsa_hw_params = pcm_params_get(profile->card,
410 profile->device,
411 profile->direction);
412 if (alsa_hw_params == NULL) {
413 return false;
414 }
415
416 /* Formats */
417 struct pcm_mask * format_mask = pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT);
418 profile_enum_sample_formats(profile, format_mask);
419
420 /* Channels */
421 profile_enum_channel_counts(
422 profile, pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS),
423 pcm_params_get_max(alsa_hw_params, PCM_PARAM_CHANNELS));
424
425 /* Sample Rates */
426 profile_enum_sample_rates(
427 profile, pcm_params_get_min(alsa_hw_params, PCM_PARAM_RATE),
428 pcm_params_get_max(alsa_hw_params, PCM_PARAM_RATE));
429
430 profile->is_valid = true;
431
432 pcm_params_free(alsa_hw_params);
433 return true;
434 }
435
profile_get_sample_rate_strs(const alsa_device_profile * profile)436 char * profile_get_sample_rate_strs(const alsa_device_profile* profile)
437 {
438 /* if we assume that rate strings are about 5 characters (48000 is 5), plus ~1 for a
439 * delimiter "|" this buffer has room for about 22 rate strings which seems like
440 * way too much, but it's a stack variable so only temporary.
441 */
442 char buffer[128];
443 buffer[0] = '\0';
444 size_t buffSize = ARRAY_SIZE(buffer);
445 size_t curStrLen = 0;
446
447 char numBuffer[32];
448
449 size_t numEntries = 0;
450 size_t index;
451 for (index = 0; profile->sample_rates[index] != 0; index++) {
452 snprintf(numBuffer, sizeof(numBuffer), "%u", profile->sample_rates[index]);
453 // account for both the null, and potentially the bar.
454 if (buffSize - curStrLen < strlen(numBuffer) + (numEntries != 0 ? 2 : 1)) {
455 /* we don't have room for another, so bail at this point rather than
456 * return a malformed rate string
457 */
458 break;
459 }
460 if (numEntries++ != 0) {
461 strlcat(buffer, "|", buffSize);
462 }
463 curStrLen = strlcat(buffer, numBuffer, buffSize);
464 }
465
466 return strdup(buffer);
467 }
468
profile_get_format_strs(const alsa_device_profile * profile)469 char * profile_get_format_strs(const alsa_device_profile* profile)
470 {
471 /* if we assume that format strings are about 24 characters (AUDIO_FORMAT_PCM_16_BIT is 23),
472 * plus ~1 for a delimiter "|" this buffer has room for about 10 format strings which seems
473 * like way too much, but it's a stack variable so only temporary.
474 */
475 char buffer[256];
476 buffer[0] = '\0';
477 size_t buffSize = ARRAY_SIZE(buffer);
478 size_t curStrLen = 0;
479
480 size_t numEntries = 0;
481 size_t index = 0;
482 for (index = 0; profile->formats[index] != PCM_FORMAT_INVALID; index++) {
483 // account for both the null, and potentially the bar.
484 if (buffSize - curStrLen < strlen(format_string_map[profile->formats[index]])
485 + (numEntries != 0 ? 2 : 1)) {
486 /* we don't have room for another, so bail at this point rather than
487 * return a malformed rate string
488 */
489 break;
490 }
491 if (numEntries++ != 0) {
492 strlcat(buffer, "|", buffSize);
493 }
494 curStrLen = strlcat(buffer, format_string_map[profile->formats[index]], buffSize);
495 }
496
497 return strdup(buffer);
498 }
499
profile_get_channel_count_strs(const alsa_device_profile * profile)500 char * profile_get_channel_count_strs(const alsa_device_profile* profile)
501 {
502 // FIXME implicit fixed channel count assumption here (FCC_8).
503 // we use only the canonical even number channel position masks.
504 static const char * const out_chans_strs[] = {
505 /* 0 */"AUDIO_CHANNEL_NONE", /* will never be taken as this is a terminator */
506 /* 1 */"AUDIO_CHANNEL_OUT_MONO",
507 /* 2 */"AUDIO_CHANNEL_OUT_STEREO",
508 /* 3 */ /* "AUDIO_CHANNEL_OUT_STEREO|AUDIO_CHANNEL_OUT_FRONT_CENTER" */ NULL,
509 /* 4 */"AUDIO_CHANNEL_OUT_QUAD",
510 /* 5 */ /* "AUDIO_CHANNEL_OUT_QUAD|AUDIO_CHANNEL_OUT_FRONT_CENTER" */ NULL,
511 /* 6 */"AUDIO_CHANNEL_OUT_5POINT1",
512 /* 7 */ /* "AUDIO_CHANNEL_OUT_5POINT1|AUDIO_CHANNEL_OUT_BACK_CENTER" */ NULL,
513 /* 8 */"AUDIO_CHANNEL_OUT_7POINT1",
514 /* channel counts greater than this not considered */
515 };
516
517 static const char * const in_chans_strs[] = {
518 /* 0 */"AUDIO_CHANNEL_NONE", /* will never be taken as this is a terminator */
519 /* 1 */"AUDIO_CHANNEL_IN_MONO",
520 /* 2 */"AUDIO_CHANNEL_IN_STEREO",
521 /* channel counts greater than this not considered */
522 };
523
524 static const char * const index_chans_strs[] = {
525 /* 0 */"AUDIO_CHANNEL_NONE", /* will never be taken as this is a terminator */
526 /* 1 */"AUDIO_CHANNEL_INDEX_MASK_1",
527 /* 2 */"AUDIO_CHANNEL_INDEX_MASK_2",
528 /* 3 */"AUDIO_CHANNEL_INDEX_MASK_3",
529 /* 4 */"AUDIO_CHANNEL_INDEX_MASK_4",
530 /* 5 */"AUDIO_CHANNEL_INDEX_MASK_5",
531 /* 6 */"AUDIO_CHANNEL_INDEX_MASK_6",
532 /* 7 */"AUDIO_CHANNEL_INDEX_MASK_7",
533 /* 8 */"AUDIO_CHANNEL_INDEX_MASK_8",
534 };
535
536 const bool isOutProfile = profile->direction == PCM_OUT;
537
538 const char * const * const chans_strs = isOutProfile ? out_chans_strs : in_chans_strs;
539 const size_t chans_strs_size =
540 isOutProfile ? ARRAY_SIZE(out_chans_strs) : ARRAY_SIZE(in_chans_strs);
541
542 /*
543 * If we assume each channel string is 26 chars ("AUDIO_CHANNEL_INDEX_MASK_8" is 26) + 1 for,
544 * the "|" delimiter, then we allocate room for 16 strings.
545 */
546 char buffer[27 * 16 + 1]; /* caution, may need to be expanded */
547 buffer[0] = '\0';
548 size_t buffSize = ARRAY_SIZE(buffer);
549 size_t curStrLen = 0;
550
551 /* We currently support MONO and STEREO, and always report STEREO but some (many)
552 * USB Audio Devices may only announce support for MONO (a headset mic for example), or
553 * The total number of output channels. SO, if the device itself doesn't explicitly
554 * support STEREO, append to the channel config strings we are generating.
555 *
556 * The MONO and STEREO positional channel masks are provided for legacy compatibility.
557 * For multichannel (n > 2) we only expose channel index masks.
558 */
559 // Always support stereo
560 curStrLen = strlcat(buffer, chans_strs[2], buffSize);
561
562 size_t index;
563 unsigned channel_count;
564 for (index = 0;
565 (channel_count = profile->channel_counts[index]) != 0;
566 index++) {
567
568 /* we only show positional information for mono (stereo handled already) */
569 if (channel_count < chans_strs_size
570 && chans_strs[channel_count] != NULL
571 && channel_count < 2 /* positional only for fewer than 2 channels */) {
572 // account for the '|' and the '\0'
573 if (buffSize - curStrLen < strlen(chans_strs[channel_count]) + 2) {
574 /* we don't have room for another, so bail at this point rather than
575 * return a malformed rate string
576 */
577 break;
578 }
579
580 strlcat(buffer, "|", buffSize);
581 curStrLen = strlcat(buffer, chans_strs[channel_count], buffSize);
582 }
583
584 // handle channel index masks for both input and output
585 // +2 to account for the '|' and the '\0'
586 if (buffSize - curStrLen < strlen(index_chans_strs[channel_count]) + 2) {
587 /* we don't have room for another, so bail at this point rather than
588 * return a malformed rate string
589 */
590 break;
591 }
592
593 strlcat(buffer, "|", buffSize);
594 curStrLen = strlcat(buffer, index_chans_strs[channel_count], buffSize);
595 }
596
597 return strdup(buffer);
598 }
599
profile_dump(const alsa_device_profile * profile,int fd)600 void profile_dump(const alsa_device_profile* profile, int fd)
601 {
602 if (profile == NULL) {
603 dprintf(fd, " %s\n", "No USB Profile");
604 return; /* bail early */
605 }
606
607 if (!profile->is_valid) {
608 dprintf(fd, " Profile is INVALID");
609 }
610
611 /* card/device/direction */
612 dprintf(fd, " card:%d, device:%d - %s\n",
613 profile->card, profile->device, profile->direction == PCM_OUT ? "OUT" : "IN");
614
615 /* formats */
616 dprintf(fd, " Formats: ");
617 for (int fmtIndex = 0;
618 profile->formats[fmtIndex] != PCM_FORMAT_INVALID && fmtIndex < MAX_PROFILE_FORMATS;
619 fmtIndex++) {
620 dprintf(fd, "%d ", profile->formats[fmtIndex]);
621 }
622 dprintf(fd, "\n");
623
624 /* sample rates */
625 dprintf(fd, " Rates: ");
626 for (int rateIndex = 0;
627 profile->sample_rates[rateIndex] != 0 && rateIndex < MAX_PROFILE_SAMPLE_RATES;
628 rateIndex++) {
629 dprintf(fd, "%u ", profile->sample_rates[rateIndex]);
630 }
631 dprintf(fd, "\n");
632
633 // channel counts
634 dprintf(fd, " Channel Counts: ");
635 for (int cntIndex = 0;
636 profile->channel_counts[cntIndex] != 0 && cntIndex < MAX_PROFILE_CHANNEL_COUNTS;
637 cntIndex++) {
638 dprintf(fd, "%u ", profile->channel_counts[cntIndex]);
639 }
640 dprintf(fd, "\n");
641
642 dprintf(fd, " min/max period size [%u : %u]\n",
643 profile->min_period_size,profile-> max_period_size);
644 dprintf(fd, " min/max channel count [%u : %u]\n",
645 profile->min_channel_count, profile->max_channel_count);
646
647 // struct pcm_config default_config;
648 dprintf(fd, " Default Config:\n");
649 dprintf(fd, " channels: %d\n", profile->default_config.channels);
650 dprintf(fd, " rate: %d\n", profile->default_config.rate);
651 dprintf(fd, " period_size: %d\n", profile->default_config.period_size);
652 dprintf(fd, " period_count: %d\n", profile->default_config.period_count);
653 dprintf(fd, " format: %d\n", profile->default_config.format);
654 }
655