| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright (C) 2012 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 |  | 
| Paul McLean | 9ab869a | 2015-01-13 09:37:06 -0800 | [diff] [blame] | 17 | #define LOG_TAG "modules.usbaudio.audio_hal" | 
| Paul McLean | cf5310a | 2018-08-22 14:33:12 -0700 | [diff] [blame] | 18 | /* #define LOG_NDEBUG 0 */ | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 19 |  | 
 | 20 | #include <errno.h> | 
| Mark Salyzyn | 88e458a | 2014-04-28 12:30:44 -0700 | [diff] [blame] | 21 | #include <inttypes.h> | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 22 | #include <pthread.h> | 
 | 23 | #include <stdint.h> | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 24 | #include <stdlib.h> | 
| Mark Salyzyn | 88e458a | 2014-04-28 12:30:44 -0700 | [diff] [blame] | 25 | #include <sys/time.h> | 
| Tri Vo | 00a8673 | 2017-06-27 11:33:36 -0700 | [diff] [blame] | 26 | #include <unistd.h> | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 27 |  | 
| Mark Salyzyn | 88e458a | 2014-04-28 12:30:44 -0700 | [diff] [blame] | 28 | #include <log/log.h> | 
| Paul McLean | 6a75e4e | 2016-05-25 14:09:02 -0600 | [diff] [blame] | 29 | #include <cutils/list.h> | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 30 | #include <cutils/str_parms.h> | 
 | 31 | #include <cutils/properties.h> | 
 | 32 |  | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 33 | #include <hardware/audio.h> | 
| Paul McLean | e32cbc1 | 2014-06-25 10:42:07 -0700 | [diff] [blame] | 34 | #include <hardware/audio_alsaops.h> | 
 | 35 | #include <hardware/hardware.h> | 
 | 36 |  | 
 | 37 | #include <system/audio.h> | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 38 |  | 
 | 39 | #include <tinyalsa/asoundlib.h> | 
 | 40 |  | 
| Paul McLean | c220115 | 2014-07-16 13:46:07 -0700 | [diff] [blame] | 41 | #include <audio_utils/channels.h> | 
 | 42 |  | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 43 | #include "alsa_device_profile.h" | 
 | 44 | #include "alsa_device_proxy.h" | 
| Paul McLean | 9ab869a | 2015-01-13 09:37:06 -0800 | [diff] [blame] | 45 | #include "alsa_logging.h" | 
| Paul McLean | f62d75e | 2014-07-11 15:14:19 -0700 | [diff] [blame] | 46 |  | 
| Paul McLean | 1d585cc | 2016-05-24 11:28:10 -0600 | [diff] [blame] | 47 | /* Lock play & record samples rates at or above this threshold */ | 
 | 48 | #define RATELOCK_THRESHOLD 96000 | 
 | 49 |  | 
| Paul McLean | cfdbd6b | 2018-07-27 11:16:02 -0600 | [diff] [blame] | 50 | #define max(a, b) ((a) > (b) ? (a) : (b)) | 
 | 51 | #define min(a, b) ((a) < (b) ? (a) : (b)) | 
 | 52 |  | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 53 | struct audio_device { | 
 | 54 |     struct audio_hw_device hw_device; | 
 | 55 |  | 
 | 56 |     pthread_mutex_t lock; /* see note below on mutex acquisition order */ | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 57 |  | 
 | 58 |     /* output */ | 
| Paul McLean | 6a75e4e | 2016-05-25 14:09:02 -0600 | [diff] [blame] | 59 |     struct listnode output_stream_list; | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 60 |  | 
 | 61 |     /* input */ | 
| Paul McLean | 6a75e4e | 2016-05-25 14:09:02 -0600 | [diff] [blame] | 62 |     struct listnode input_stream_list; | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 63 |  | 
| Paul McLean | 1d585cc | 2016-05-24 11:28:10 -0600 | [diff] [blame] | 64 |     /* lock input & output sample rates */ | 
 | 65 |     /*FIXME - How do we address multiple output streams? */ | 
| Paul McLean | cfdbd6b | 2018-07-27 11:16:02 -0600 | [diff] [blame] | 66 |     uint32_t device_sample_rate;    // this should be a rate that is common to both input & output | 
| Paul McLean | 1d585cc | 2016-05-24 11:28:10 -0600 | [diff] [blame] | 67 |  | 
| Eric Laurent | 253def9 | 2014-09-14 12:18:18 -0700 | [diff] [blame] | 68 |     bool mic_muted; | 
 | 69 |  | 
| Andy Hung | b10ce6d | 2017-10-27 19:37:58 -0700 | [diff] [blame] | 70 |     int32_t inputs_open; /* number of input streams currently open. */ | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 71 | }; | 
 | 72 |  | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 73 | struct stream_lock { | 
 | 74 |     pthread_mutex_t lock;               /* see note below on mutex acquisition order */ | 
 | 75 |     pthread_mutex_t pre_lock;           /* acquire before lock to avoid DOS by playback thread */ | 
 | 76 | }; | 
 | 77 |  | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 78 | struct alsa_device_info { | 
 | 79 |     alsa_device_profile profile;        /* The profile of the ALSA device */ | 
 | 80 |     alsa_device_proxy proxy;            /* The state */ | 
 | 81 |     struct listnode list_node; | 
 | 82 | }; | 
 | 83 |  | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 84 | struct stream_out { | 
 | 85 |     struct audio_stream_out stream; | 
 | 86 |  | 
| Paul McLean | cf5310a | 2018-08-22 14:33:12 -0700 | [diff] [blame] | 87 |     struct stream_lock lock; | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 88 |  | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 89 |     bool standby; | 
 | 90 |  | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 91 |     struct audio_device *adev;           /* hardware information - only using this for the lock */ | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 92 |  | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 93 |     struct listnode alsa_devices;       /* The ALSA devices connected to the stream. */ | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 94 |  | 
| Andy Hung | 03576be | 2014-07-21 21:16:45 -0700 | [diff] [blame] | 95 |     unsigned hal_channel_count;         /* channel count exposed to AudioFlinger. | 
 | 96 |                                          * This may differ from the device channel count when | 
 | 97 |                                          * the device is not compatible with AudioFlinger | 
 | 98 |                                          * capabilities, e.g. exposes too many channels or | 
 | 99 |                                          * too few channels. */ | 
| Paul McLean | 64345f8 | 2016-06-06 13:26:05 -0600 | [diff] [blame] | 100 |     audio_channel_mask_t hal_channel_mask;  /* USB devices deal in channel counts, not masks | 
 | 101 |                                              * so the proxy doesn't have a channel_mask, but | 
 | 102 |                                              * audio HALs need to talk about channel masks | 
 | 103 |                                              * so expose the one calculated by | 
 | 104 |                                              * adev_open_output_stream */ | 
| Andy Hung | 182ddc7 | 2015-05-05 23:37:30 -0700 | [diff] [blame] | 105 |  | 
| Paul McLean | 6a75e4e | 2016-05-25 14:09:02 -0600 | [diff] [blame] | 106 |     struct listnode list_node; | 
 | 107 |  | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 108 |     void * conversion_buffer;           /* any conversions are put into here | 
 | 109 |                                          * they could come from here too if | 
 | 110 |                                          * there was a previous conversion */ | 
 | 111 |     size_t conversion_buffer_size;      /* in bytes */ | 
 | 112 | }; | 
 | 113 |  | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 114 | struct stream_in { | 
 | 115 |     struct audio_stream_in stream; | 
 | 116 |  | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 117 |     struct stream_lock  lock; | 
 | 118 |  | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 119 |     bool standby; | 
 | 120 |  | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 121 |     struct audio_device *adev;           /* hardware information - only using this for the lock */ | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 122 |  | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 123 |     struct listnode alsa_devices;       /* The ALSA devices connected to the stream. */ | 
| Paul McLean | f62d75e | 2014-07-11 15:14:19 -0700 | [diff] [blame] | 124 |  | 
| Paul McLean | 2cfd81b | 2014-09-15 12:32:23 -0700 | [diff] [blame] | 125 |     unsigned hal_channel_count;         /* channel count exposed to AudioFlinger. | 
 | 126 |                                          * This may differ from the device channel count when | 
 | 127 |                                          * the device is not compatible with AudioFlinger | 
 | 128 |                                          * capabilities, e.g. exposes too many channels or | 
 | 129 |                                          * too few channels. */ | 
| Paul McLean | 64345f8 | 2016-06-06 13:26:05 -0600 | [diff] [blame] | 130 |     audio_channel_mask_t hal_channel_mask;  /* USB devices deal in channel counts, not masks | 
 | 131 |                                              * so the proxy doesn't have a channel_mask, but | 
 | 132 |                                              * audio HALs need to talk about channel masks | 
 | 133 |                                              * so expose the one calculated by | 
 | 134 |                                              * adev_open_input_stream */ | 
| Andy Hung | 780f1f8 | 2015-04-22 22:14:39 -0700 | [diff] [blame] | 135 |  | 
| Paul McLean | 6a75e4e | 2016-05-25 14:09:02 -0600 | [diff] [blame] | 136 |     struct listnode list_node; | 
 | 137 |  | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 138 |     /* We may need to read more data from the device in order to data reduce to 16bit, 4chan */ | 
| Paul McLean | 30f4185 | 2014-04-16 15:44:20 -0700 | [diff] [blame] | 139 |     void * conversion_buffer;           /* any conversions are put into here | 
 | 140 |                                          * they could come from here too if | 
 | 141 |                                          * there was a previous conversion */ | 
 | 142 |     size_t conversion_buffer_size;      /* in bytes */ | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 143 | }; | 
 | 144 |  | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 145 | /* | 
 | 146 |  * Locking Helpers | 
 | 147 |  */ | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 148 | /* | 
| Eric Laurent | 7031809 | 2015-06-19 17:49:17 -0700 | [diff] [blame] | 149 |  * NOTE: when multiple mutexes have to be acquired, always take the | 
 | 150 |  * stream_in or stream_out mutex first, followed by the audio_device mutex. | 
 | 151 |  * stream pre_lock is always acquired before stream lock to prevent starvation of control thread by | 
 | 152 |  * higher priority playback or capture thread. | 
 | 153 |  */ | 
 | 154 |  | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 155 | static void stream_lock_init(struct stream_lock *lock) { | 
 | 156 |     pthread_mutex_init(&lock->lock, (const pthread_mutexattr_t *) NULL); | 
 | 157 |     pthread_mutex_init(&lock->pre_lock, (const pthread_mutexattr_t *) NULL); | 
 | 158 | } | 
 | 159 |  | 
 | 160 | static void stream_lock(struct stream_lock *lock) { | 
 | 161 |     pthread_mutex_lock(&lock->pre_lock); | 
 | 162 |     pthread_mutex_lock(&lock->lock); | 
 | 163 |     pthread_mutex_unlock(&lock->pre_lock); | 
 | 164 | } | 
 | 165 |  | 
 | 166 | static void stream_unlock(struct stream_lock *lock) { | 
 | 167 |     pthread_mutex_unlock(&lock->lock); | 
 | 168 | } | 
 | 169 |  | 
 | 170 | static void device_lock(struct audio_device *adev) { | 
 | 171 |     pthread_mutex_lock(&adev->lock); | 
 | 172 | } | 
 | 173 |  | 
 | 174 | static int device_try_lock(struct audio_device *adev) { | 
 | 175 |     return pthread_mutex_trylock(&adev->lock); | 
 | 176 | } | 
 | 177 |  | 
 | 178 | static void device_unlock(struct audio_device *adev) { | 
 | 179 |     pthread_mutex_unlock(&adev->lock); | 
 | 180 | } | 
 | 181 |  | 
 | 182 | /* | 
 | 183 |  * streams list management | 
 | 184 |  */ | 
 | 185 | static void adev_add_stream_to_list( | 
 | 186 |     struct audio_device* adev, struct listnode* list, struct listnode* stream_node) { | 
 | 187 |     device_lock(adev); | 
 | 188 |  | 
 | 189 |     list_add_tail(list, stream_node); | 
 | 190 |  | 
 | 191 |     device_unlock(adev); | 
 | 192 | } | 
 | 193 |  | 
 | 194 | static void adev_remove_stream_from_list( | 
 | 195 |     struct audio_device* adev, struct listnode* stream_node) { | 
 | 196 |     device_lock(adev); | 
 | 197 |  | 
 | 198 |     list_remove(stream_node); | 
 | 199 |  | 
 | 200 |     device_unlock(adev); | 
 | 201 | } | 
 | 202 |  | 
| Eric Laurent | 7031809 | 2015-06-19 17:49:17 -0700 | [diff] [blame] | 203 | /* | 
| Paul McLean | 0f1753e | 2014-12-02 12:36:45 -0700 | [diff] [blame] | 204 |  * Extract the card and device numbers from the supplied key/value pairs. | 
 | 205 |  *   kvpairs    A null-terminated string containing the key/value pairs or card and device. | 
 | 206 |  *              i.e. "card=1;device=42" | 
 | 207 |  *   card   A pointer to a variable to receive the parsed-out card number. | 
 | 208 |  *   device A pointer to a variable to receive the parsed-out device number. | 
 | 209 |  * NOTE: The variables pointed to by card and device return -1 (undefined) if the | 
 | 210 |  *  associated key/value pair is not found in the provided string. | 
| Paul McLean | 65ec72b | 2015-02-18 09:13:24 -0800 | [diff] [blame] | 211 |  *  Return true if the kvpairs string contain a card/device spec, false otherwise. | 
| Paul McLean | 0f1753e | 2014-12-02 12:36:45 -0700 | [diff] [blame] | 212 |  */ | 
| Paul McLean | 65ec72b | 2015-02-18 09:13:24 -0800 | [diff] [blame] | 213 | static bool parse_card_device_params(const char *kvpairs, int *card, int *device) | 
| Paul McLean | 0f1753e | 2014-12-02 12:36:45 -0700 | [diff] [blame] | 214 | { | 
 | 215 |     struct str_parms * parms = str_parms_create_str(kvpairs); | 
 | 216 |     char value[32]; | 
 | 217 |     int param_val; | 
 | 218 |  | 
 | 219 |     // initialize to "undefined" state. | 
 | 220 |     *card = -1; | 
 | 221 |     *device = -1; | 
 | 222 |  | 
 | 223 |     param_val = str_parms_get_str(parms, "card", value, sizeof(value)); | 
 | 224 |     if (param_val >= 0) { | 
 | 225 |         *card = atoi(value); | 
 | 226 |     } | 
 | 227 |  | 
 | 228 |     param_val = str_parms_get_str(parms, "device", value, sizeof(value)); | 
 | 229 |     if (param_val >= 0) { | 
 | 230 |         *device = atoi(value); | 
 | 231 |     } | 
 | 232 |  | 
 | 233 |     str_parms_destroy(parms); | 
| Paul McLean | 65ec72b | 2015-02-18 09:13:24 -0800 | [diff] [blame] | 234 |  | 
 | 235 |     return *card >= 0 && *device >= 0; | 
| Paul McLean | 0f1753e | 2014-12-02 12:36:45 -0700 | [diff] [blame] | 236 | } | 
 | 237 |  | 
| Andy Hung | 4e6a1c0 | 2017-10-27 20:31:46 -0700 | [diff] [blame] | 238 | static char *device_get_parameters(const alsa_device_profile *profile, const char * keys) | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 239 | { | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 240 |     if (profile->card < 0 || profile->device < 0) { | 
 | 241 |         return strdup(""); | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 242 |     } | 
| Paul McLean | e32cbc1 | 2014-06-25 10:42:07 -0700 | [diff] [blame] | 243 |  | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 244 |     struct str_parms *query = str_parms_create_str(keys); | 
 | 245 |     struct str_parms *result = str_parms_create(); | 
| Paul McLean | e32cbc1 | 2014-06-25 10:42:07 -0700 | [diff] [blame] | 246 |  | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 247 |     /* These keys are from hardware/libhardware/include/audio.h */ | 
 | 248 |     /* supported sample rates */ | 
 | 249 |     if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES)) { | 
 | 250 |         char* rates_list = profile_get_sample_rate_strs(profile); | 
 | 251 |         str_parms_add_str(result, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES, | 
 | 252 |                           rates_list); | 
 | 253 |         free(rates_list); | 
| Paul McLean | e32cbc1 | 2014-06-25 10:42:07 -0700 | [diff] [blame] | 254 |     } | 
 | 255 |  | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 256 |     /* supported channel counts */ | 
 | 257 |     if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_CHANNELS)) { | 
 | 258 |         char* channels_list = profile_get_channel_count_strs(profile); | 
 | 259 |         str_parms_add_str(result, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, | 
 | 260 |                           channels_list); | 
 | 261 |         free(channels_list); | 
| Paul McLean | e32cbc1 | 2014-06-25 10:42:07 -0700 | [diff] [blame] | 262 |     } | 
 | 263 |  | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 264 |     /* supported sample formats */ | 
 | 265 |     if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_FORMATS)) { | 
 | 266 |         char * format_params = profile_get_format_strs(profile); | 
 | 267 |         str_parms_add_str(result, AUDIO_PARAMETER_STREAM_SUP_FORMATS, | 
 | 268 |                           format_params); | 
 | 269 |         free(format_params); | 
| Paul McLean | f62d75e | 2014-07-11 15:14:19 -0700 | [diff] [blame] | 270 |     } | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 271 |     str_parms_destroy(query); | 
| Paul McLean | f62d75e | 2014-07-11 15:14:19 -0700 | [diff] [blame] | 272 |  | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 273 |     char* result_str = str_parms_to_str(result); | 
 | 274 |     str_parms_destroy(result); | 
| Paul McLean | f62d75e | 2014-07-11 15:14:19 -0700 | [diff] [blame] | 275 |  | 
| Paul McLean | 65ec72b | 2015-02-18 09:13:24 -0800 | [diff] [blame] | 276 |     ALOGV("device_get_parameters = %s", result_str); | 
| Paul McLean | f62d75e | 2014-07-11 15:14:19 -0700 | [diff] [blame] | 277 |  | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 278 |     return result_str; | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 279 | } | 
 | 280 |  | 
 | 281 | /* | 
 | 282 |  * HAl Functions | 
 | 283 |  */ | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 284 | /** | 
 | 285 |  * NOTE: when multiple mutexes have to be acquired, always respect the | 
 | 286 |  * following order: hw device > out stream | 
 | 287 |  */ | 
 | 288 |  | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 289 | static struct alsa_device_info* stream_get_first_alsa_device(const struct listnode *alsa_devices) { | 
 | 290 |     if (list_empty(alsa_devices)) { | 
 | 291 |         return NULL; | 
 | 292 |     } | 
 | 293 |     return node_to_item(list_head(alsa_devices), struct alsa_device_info, list_node); | 
 | 294 | } | 
 | 295 |  | 
 | 296 | static void stream_dump_alsa_devices(const struct listnode *alsa_devices, int fd) { | 
 | 297 |     struct listnode *node; | 
 | 298 |     size_t i = 0; | 
 | 299 |     list_for_each(node, alsa_devices) { | 
 | 300 |         struct alsa_device_info *device_info = | 
 | 301 |                 node_to_item(node, struct alsa_device_info, list_node); | 
 | 302 |         dprintf(fd, "Output Profile %zu:\n", i); | 
 | 303 |         profile_dump(&device_info->profile, fd); | 
 | 304 |  | 
 | 305 |         dprintf(fd, "Output Proxy %zu:\n", i); | 
 | 306 |         proxy_dump(&device_info->proxy, fd); | 
 | 307 |     } | 
 | 308 | } | 
 | 309 |  | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 310 | /* | 
 | 311 |  * OUT functions | 
 | 312 |  */ | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 313 | static uint32_t out_get_sample_rate(const struct audio_stream *stream) | 
 | 314 | { | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 315 |     struct alsa_device_info *device_info = stream_get_first_alsa_device( | 
 | 316 |             &((struct stream_out*)stream)->alsa_devices); | 
 | 317 |     if (device_info == NULL) { | 
 | 318 |         ALOGW("%s device info is null", __func__); | 
 | 319 |         return 0; | 
 | 320 |     } | 
 | 321 |     uint32_t rate = proxy_get_sample_rate(&device_info->proxy); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 322 |     ALOGV("out_get_sample_rate() = %d", rate); | 
 | 323 |     return rate; | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 324 | } | 
 | 325 |  | 
 | 326 | static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate) | 
 | 327 | { | 
 | 328 |     return 0; | 
 | 329 | } | 
 | 330 |  | 
 | 331 | static size_t out_get_buffer_size(const struct audio_stream *stream) | 
 | 332 | { | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 333 |     const struct stream_out* out = (const struct stream_out*)stream; | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 334 |     const struct alsa_device_info* device_info = stream_get_first_alsa_device(&out->alsa_devices); | 
 | 335 |     if (device_info == NULL) { | 
 | 336 |         ALOGW("%s device info is null", __func__); | 
 | 337 |         return 0; | 
 | 338 |     } | 
 | 339 |     return proxy_get_period_size(&device_info->proxy) * audio_stream_out_frame_size(&(out->stream)); | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 340 | } | 
 | 341 |  | 
 | 342 | static uint32_t out_get_channels(const struct audio_stream *stream) | 
 | 343 | { | 
| Andy Hung | 03576be | 2014-07-21 21:16:45 -0700 | [diff] [blame] | 344 |     const struct stream_out *out = (const struct stream_out*)stream; | 
| Andy Hung | 182ddc7 | 2015-05-05 23:37:30 -0700 | [diff] [blame] | 345 |     return out->hal_channel_mask; | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 346 | } | 
 | 347 |  | 
 | 348 | static audio_format_t out_get_format(const struct audio_stream *stream) | 
 | 349 | { | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 350 |     /* Note: The HAL doesn't do any FORMAT conversion at this time. It | 
 | 351 |      * Relies on the framework to provide data in the specified format. | 
 | 352 |      * This could change in the future. | 
 | 353 |      */ | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 354 |     struct alsa_device_info *device_info = stream_get_first_alsa_device( | 
 | 355 |             &((struct stream_out*)stream)->alsa_devices); | 
 | 356 |     if (device_info == NULL) { | 
 | 357 |         ALOGW("%s device info is null", __func__); | 
 | 358 |         return AUDIO_FORMAT_DEFAULT; | 
 | 359 |     } | 
 | 360 |     audio_format_t format = audio_format_from_pcm_format(proxy_get_format(&device_info->proxy)); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 361 |     return format; | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 362 | } | 
 | 363 |  | 
 | 364 | static int out_set_format(struct audio_stream *stream, audio_format_t format) | 
 | 365 | { | 
 | 366 |     return 0; | 
 | 367 | } | 
 | 368 |  | 
 | 369 | static int out_standby(struct audio_stream *stream) | 
 | 370 | { | 
 | 371 |     struct stream_out *out = (struct stream_out *)stream; | 
 | 372 |  | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 373 |     stream_lock(&out->lock); | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 374 |     if (!out->standby) { | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 375 |         struct listnode *node; | 
 | 376 |         list_for_each(node, &out->alsa_devices) { | 
 | 377 |             struct alsa_device_info *device_info = | 
 | 378 |                     node_to_item(node, struct alsa_device_info, list_node); | 
 | 379 |             proxy_close(&device_info->proxy); | 
 | 380 |         } | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 381 |         out->standby = true; | 
 | 382 |     } | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 383 |     stream_unlock(&out->lock); | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 384 |     return 0; | 
 | 385 | } | 
 | 386 |  | 
| Paul McLean | 6a75e4e | 2016-05-25 14:09:02 -0600 | [diff] [blame] | 387 | static int out_dump(const struct audio_stream *stream, int fd) { | 
 | 388 |     const struct stream_out* out_stream = (const struct stream_out*) stream; | 
 | 389 |  | 
 | 390 |     if (out_stream != NULL) { | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 391 |         stream_dump_alsa_devices(&out_stream->alsa_devices, fd); | 
| Paul McLean | 6a75e4e | 2016-05-25 14:09:02 -0600 | [diff] [blame] | 392 |     } | 
 | 393 |  | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 394 |     return 0; | 
 | 395 | } | 
 | 396 |  | 
 | 397 | static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) | 
 | 398 | { | 
| Paul McLean | 65ec72b | 2015-02-18 09:13:24 -0800 | [diff] [blame] | 399 |     ALOGV("out_set_parameters() keys:%s", kvpairs); | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 400 |  | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 401 |     struct stream_out *out = (struct stream_out *)stream; | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 402 |  | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 403 |     int ret_value = 0; | 
| Eric Laurent | 05333d4 | 2014-08-04 20:29:17 -0700 | [diff] [blame] | 404 |     int card = -1; | 
 | 405 |     int device = -1; | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 406 |  | 
| Paul McLean | 65ec72b | 2015-02-18 09:13:24 -0800 | [diff] [blame] | 407 |     if (!parse_card_device_params(kvpairs, &card, &device)) { | 
 | 408 |         // nothing to do | 
 | 409 |         return ret_value; | 
 | 410 |     } | 
 | 411 |  | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 412 |     stream_lock(&out->lock); | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 413 |     struct alsa_device_info* device_info = stream_get_first_alsa_device(&out->alsa_devices); | 
 | 414 |     if (device_info != NULL && !profile_is_cached_for(&device_info->profile, card, device)) { | 
| Eric Laurent | 05333d4 | 2014-08-04 20:29:17 -0700 | [diff] [blame] | 415 |         /* cannot read pcm device info if playback is active */ | 
 | 416 |         if (!out->standby) | 
 | 417 |             ret_value = -ENOSYS; | 
 | 418 |         else { | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 419 |             int saved_card = device_info->profile.card; | 
 | 420 |             int saved_device = device_info->profile.device; | 
 | 421 |             device_info->profile.card = card; | 
 | 422 |             device_info->profile.device = device; | 
 | 423 |             ret_value = profile_read_device_info(&device_info->profile) ? 0 : -EINVAL; | 
| Eric Laurent | 05333d4 | 2014-08-04 20:29:17 -0700 | [diff] [blame] | 424 |             if (ret_value != 0) { | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 425 |                 device_info->profile.card = saved_card; | 
 | 426 |                 device_info->profile.device = saved_device; | 
| Eric Laurent | 05333d4 | 2014-08-04 20:29:17 -0700 | [diff] [blame] | 427 |             } | 
 | 428 |         } | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 429 |     } | 
| Paul McLean | 2c6196f | 2014-08-20 16:50:25 -0700 | [diff] [blame] | 430 |  | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 431 |     stream_unlock(&out->lock); | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 432 |  | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 433 |     return ret_value; | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 434 | } | 
 | 435 |  | 
| Paul McLean | f62d75e | 2014-07-11 15:14:19 -0700 | [diff] [blame] | 436 | static char * out_get_parameters(const struct audio_stream *stream, const char *keys) | 
 | 437 | { | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 438 |     struct stream_out *out = (struct stream_out *)stream; | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 439 |     stream_lock(&out->lock); | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 440 |     struct alsa_device_info *device_info = stream_get_first_alsa_device(&out->alsa_devices); | 
 | 441 |     char *params_str = NULL; | 
 | 442 |     if (device_info != NULL) { | 
 | 443 |         params_str =  device_get_parameters(&device_info->profile, keys); | 
 | 444 |     } | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 445 |     stream_unlock(&out->lock); | 
| Paul McLean | f62d75e | 2014-07-11 15:14:19 -0700 | [diff] [blame] | 446 |     return params_str; | 
 | 447 | } | 
 | 448 |  | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 449 | static uint32_t out_get_latency(const struct audio_stream_out *stream) | 
 | 450 | { | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 451 |     struct alsa_device_info *device_info = stream_get_first_alsa_device( | 
 | 452 |             &((struct stream_out*)stream)->alsa_devices); | 
 | 453 |     if (device_info == NULL) { | 
 | 454 |         ALOGW("%s device info is null", __func__); | 
 | 455 |         return 0; | 
 | 456 |     } | 
 | 457 |     return proxy_get_latency(&device_info->proxy); | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 458 | } | 
 | 459 |  | 
| Paul McLean | 30f4185 | 2014-04-16 15:44:20 -0700 | [diff] [blame] | 460 | static int out_set_volume(struct audio_stream_out *stream, float left, float right) | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 461 | { | 
 | 462 |     return -ENOSYS; | 
 | 463 | } | 
 | 464 |  | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 465 | /* must be called with hw device and output stream mutexes locked */ | 
 | 466 | static int start_output_stream(struct stream_out *out) | 
 | 467 | { | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 468 |     int status = 0; | 
 | 469 |     struct listnode *node; | 
 | 470 |     list_for_each(node, &out->alsa_devices) { | 
 | 471 |         struct alsa_device_info *device_info = | 
 | 472 |                 node_to_item(node, struct alsa_device_info, list_node); | 
 | 473 |         ALOGV("start_output_stream(card:%d device:%d)", | 
 | 474 |                 device_info->profile.card, device_info->profile.device); | 
 | 475 |         status = proxy_open(&device_info->proxy); | 
 | 476 |         if (status != 0) { | 
 | 477 |             ALOGE("%s failed to open device(card: %d device: %d)", | 
 | 478 |                     __func__, device_info->profile.card, device_info->profile.device); | 
 | 479 |             goto exit; | 
 | 480 |         } | 
 | 481 |     } | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 482 |  | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 483 | exit: | 
 | 484 |     if (status != 0) { | 
 | 485 |         list_for_each(node, &out->alsa_devices) { | 
 | 486 |             struct alsa_device_info *device_info = | 
 | 487 |                     node_to_item(node, struct alsa_device_info, list_node); | 
 | 488 |             proxy_close(&device_info->proxy); | 
 | 489 |         } | 
 | 490 |  | 
 | 491 |     } | 
 | 492 |     return status; | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 493 | } | 
 | 494 |  | 
 | 495 | static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, size_t bytes) | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 496 | { | 
 | 497 |     int ret; | 
 | 498 |     struct stream_out *out = (struct stream_out *)stream; | 
 | 499 |  | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 500 |     stream_lock(&out->lock); | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 501 |     if (out->standby) { | 
 | 502 |         ret = start_output_stream(out); | 
 | 503 |         if (ret != 0) { | 
 | 504 |             goto err; | 
 | 505 |         } | 
 | 506 |         out->standby = false; | 
 | 507 |     } | 
| Eric Laurent | 05333d4 | 2014-08-04 20:29:17 -0700 | [diff] [blame] | 508 |  | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 509 |     struct listnode* node; | 
 | 510 |     list_for_each(node, &out->alsa_devices) { | 
 | 511 |         struct alsa_device_info* device_info = | 
 | 512 |                 node_to_item(node, struct alsa_device_info, list_node); | 
 | 513 |         alsa_device_proxy* proxy = &device_info->proxy; | 
 | 514 |         const void * write_buff = buffer; | 
 | 515 |         int num_write_buff_bytes = bytes; | 
 | 516 |         const int num_device_channels = proxy_get_channel_count(proxy); /* what we told alsa */ | 
 | 517 |         const int num_req_channels = out->hal_channel_count; /* what we told AudioFlinger */ | 
 | 518 |         if (num_device_channels != num_req_channels) { | 
 | 519 |             /* allocate buffer */ | 
 | 520 |             const size_t required_conversion_buffer_size = | 
 | 521 |                      bytes * num_device_channels / num_req_channels; | 
 | 522 |             if (required_conversion_buffer_size > out->conversion_buffer_size) { | 
 | 523 |                 out->conversion_buffer_size = required_conversion_buffer_size; | 
 | 524 |                 out->conversion_buffer = realloc(out->conversion_buffer, | 
 | 525 |                                                  out->conversion_buffer_size); | 
 | 526 |             } | 
 | 527 |             /* convert data */ | 
 | 528 |             const audio_format_t audio_format = out_get_format(&(out->stream.common)); | 
 | 529 |             const unsigned sample_size_in_bytes = audio_bytes_per_sample(audio_format); | 
 | 530 |             num_write_buff_bytes = | 
 | 531 |                     adjust_channels(write_buff, num_req_channels, | 
 | 532 |                                     out->conversion_buffer, num_device_channels, | 
 | 533 |                                     sample_size_in_bytes, num_write_buff_bytes); | 
 | 534 |             write_buff = out->conversion_buffer; | 
| Andy Hung | 03576be | 2014-07-21 21:16:45 -0700 | [diff] [blame] | 535 |         } | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 536 |  | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 537 |         if (write_buff != NULL && num_write_buff_bytes != 0) { | 
 | 538 |             proxy_write(proxy, write_buff, num_write_buff_bytes); | 
 | 539 |         } | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 540 |     } | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 541 |  | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 542 |     stream_unlock(&out->lock); | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 543 |  | 
 | 544 |     return bytes; | 
 | 545 |  | 
 | 546 | err: | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 547 |     stream_unlock(&out->lock); | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 548 |     if (ret != 0) { | 
| Eric Laurent | c5ae6a0 | 2014-07-02 13:45:32 -0700 | [diff] [blame] | 549 |         usleep(bytes * 1000000 / audio_stream_out_frame_size(stream) / | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 550 |                out_get_sample_rate(&stream->common)); | 
 | 551 |     } | 
 | 552 |  | 
 | 553 |     return bytes; | 
 | 554 | } | 
 | 555 |  | 
| Paul McLean | 30f4185 | 2014-04-16 15:44:20 -0700 | [diff] [blame] | 556 | static int out_get_render_position(const struct audio_stream_out *stream, uint32_t *dsp_frames) | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 557 | { | 
 | 558 |     return -EINVAL; | 
 | 559 | } | 
 | 560 |  | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 561 | static int out_get_presentation_position(const struct audio_stream_out *stream, | 
 | 562 |                                          uint64_t *frames, struct timespec *timestamp) | 
 | 563 | { | 
| Andy Hung | c9515ce | 2015-08-04 15:05:19 -0700 | [diff] [blame] | 564 |     struct stream_out *out = (struct stream_out *)stream; // discard const qualifier | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 565 |     stream_lock(&out->lock); | 
| Andy Hung | c9515ce | 2015-08-04 15:05:19 -0700 | [diff] [blame] | 566 |  | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 567 |     const struct alsa_device_info* device_info = stream_get_first_alsa_device(&out->alsa_devices); | 
 | 568 |     const int ret = device_info == NULL ? -ENODEV : | 
 | 569 |             proxy_get_presentation_position(&device_info->proxy, frames, timestamp); | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 570 |     stream_unlock(&out->lock); | 
| Andy Hung | c9515ce | 2015-08-04 15:05:19 -0700 | [diff] [blame] | 571 |     return ret; | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 572 | } | 
 | 573 |  | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 574 | static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) | 
 | 575 | { | 
 | 576 |     return 0; | 
 | 577 | } | 
 | 578 |  | 
 | 579 | static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) | 
 | 580 | { | 
 | 581 |     return 0; | 
 | 582 | } | 
 | 583 |  | 
| Paul McLean | 30f4185 | 2014-04-16 15:44:20 -0700 | [diff] [blame] | 584 | static int out_get_next_write_timestamp(const struct audio_stream_out *stream, int64_t *timestamp) | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 585 | { | 
 | 586 |     return -EINVAL; | 
 | 587 | } | 
 | 588 |  | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 589 | static int adev_open_output_stream(struct audio_hw_device *hw_dev, | 
| Mike Lockwood | 46a9809 | 2012-04-24 16:41:18 -0700 | [diff] [blame] | 590 |                                    audio_io_handle_t handle, | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 591 |                                    audio_devices_t devicesSpec __unused, | 
| Mike Lockwood | 46a9809 | 2012-04-24 16:41:18 -0700 | [diff] [blame] | 592 |                                    audio_output_flags_t flags, | 
 | 593 |                                    struct audio_config *config, | 
| Eric Laurent | f5e2469 | 2014-07-27 16:14:57 -0700 | [diff] [blame] | 594 |                                    struct audio_stream_out **stream_out, | 
| Paul McLean | 0f1753e | 2014-12-02 12:36:45 -0700 | [diff] [blame] | 595 |                                    const char *address /*__unused*/) | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 596 | { | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 597 |     ALOGV("adev_open_output_stream() handle:0x%X, devicesSpec:0x%X, flags:0x%X, addr:%s", | 
 | 598 |           handle, devicesSpec, flags, address); | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 599 |  | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 600 |     struct stream_out *out; | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 601 |  | 
 | 602 |     out = (struct stream_out *)calloc(1, sizeof(struct stream_out)); | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 603 |     if (out == NULL) { | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 604 |         return -ENOMEM; | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 605 |     } | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 606 |  | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 607 |     /* setup function pointers */ | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 608 |     out->stream.common.get_sample_rate = out_get_sample_rate; | 
 | 609 |     out->stream.common.set_sample_rate = out_set_sample_rate; | 
 | 610 |     out->stream.common.get_buffer_size = out_get_buffer_size; | 
 | 611 |     out->stream.common.get_channels = out_get_channels; | 
 | 612 |     out->stream.common.get_format = out_get_format; | 
 | 613 |     out->stream.common.set_format = out_set_format; | 
 | 614 |     out->stream.common.standby = out_standby; | 
 | 615 |     out->stream.common.dump = out_dump; | 
 | 616 |     out->stream.common.set_parameters = out_set_parameters; | 
 | 617 |     out->stream.common.get_parameters = out_get_parameters; | 
 | 618 |     out->stream.common.add_audio_effect = out_add_audio_effect; | 
 | 619 |     out->stream.common.remove_audio_effect = out_remove_audio_effect; | 
 | 620 |     out->stream.get_latency = out_get_latency; | 
 | 621 |     out->stream.set_volume = out_set_volume; | 
 | 622 |     out->stream.write = out_write; | 
 | 623 |     out->stream.get_render_position = out_get_render_position; | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 624 |     out->stream.get_presentation_position = out_get_presentation_position; | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 625 |     out->stream.get_next_write_timestamp = out_get_next_write_timestamp; | 
 | 626 |  | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 627 |     stream_lock_init(&out->lock); | 
| Eric Laurent | 7031809 | 2015-06-19 17:49:17 -0700 | [diff] [blame] | 628 |  | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 629 |     out->adev = (struct audio_device *)hw_dev; | 
| Paul McLean | cf5310a | 2018-08-22 14:33:12 -0700 | [diff] [blame] | 630 |  | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 631 |     list_init(&out->alsa_devices); | 
 | 632 |     struct alsa_device_info *device_info = | 
 | 633 |             (struct alsa_device_info *)calloc(1, sizeof(struct alsa_device_info)); | 
 | 634 |     profile_init(&device_info->profile, PCM_OUT); | 
| Paul McLean | f62d75e | 2014-07-11 15:14:19 -0700 | [diff] [blame] | 635 |  | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 636 |     // build this to hand to the alsa_device_proxy | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 637 |     struct pcm_config proxy_config = {}; | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 638 |  | 
| Paul McLean | 0f1753e | 2014-12-02 12:36:45 -0700 | [diff] [blame] | 639 |     /* Pull out the card/device pair */ | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 640 |     parse_card_device_params(address, &device_info->profile.card, &device_info->profile.device); | 
| Paul McLean | 0f1753e | 2014-12-02 12:36:45 -0700 | [diff] [blame] | 641 |  | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 642 |     profile_read_device_info(&device_info->profile); | 
| Paul McLean | 0f1753e | 2014-12-02 12:36:45 -0700 | [diff] [blame] | 643 |  | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 644 |     int ret = 0; | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 645 |  | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 646 |     /* Rate */ | 
 | 647 |     if (config->sample_rate == 0) { | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 648 |         proxy_config.rate = profile_get_default_sample_rate(&device_info->profile); | 
 | 649 |     } else if (profile_is_sample_rate_valid(&device_info->profile, config->sample_rate)) { | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 650 |         proxy_config.rate = config->sample_rate; | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 651 |     } else { | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 652 |         proxy_config.rate = config->sample_rate = | 
 | 653 |                 profile_get_default_sample_rate(&device_info->profile); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 654 |         ret = -EINVAL; | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 655 |     } | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 656 |  | 
| Paul McLean | cfdbd6b | 2018-07-27 11:16:02 -0600 | [diff] [blame] | 657 |     /* TODO: This is a problem if the input does not support this rate */ | 
| Paul McLean | cf5310a | 2018-08-22 14:33:12 -0700 | [diff] [blame] | 658 |     device_lock(out->adev); | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 659 |     out->adev->device_sample_rate = config->sample_rate; | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 660 |     device_unlock(out->adev); | 
| Paul McLean | 1d585cc | 2016-05-24 11:28:10 -0600 | [diff] [blame] | 661 |  | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 662 |     /* Format */ | 
 | 663 |     if (config->format == AUDIO_FORMAT_DEFAULT) { | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 664 |         proxy_config.format = profile_get_default_format(&device_info->profile); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 665 |         config->format = audio_format_from_pcm_format(proxy_config.format); | 
 | 666 |     } else { | 
 | 667 |         enum pcm_format fmt = pcm_format_from_audio_format(config->format); | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 668 |         if (profile_is_format_valid(&device_info->profile, fmt)) { | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 669 |             proxy_config.format = fmt; | 
 | 670 |         } else { | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 671 |             proxy_config.format = profile_get_default_format(&device_info->profile); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 672 |             config->format = audio_format_from_pcm_format(proxy_config.format); | 
 | 673 |             ret = -EINVAL; | 
 | 674 |         } | 
 | 675 |     } | 
 | 676 |  | 
 | 677 |     /* Channels */ | 
| Paul McLean | 64345f8 | 2016-06-06 13:26:05 -0600 | [diff] [blame] | 678 |     bool calc_mask = false; | 
 | 679 |     if (config->channel_mask == AUDIO_CHANNEL_NONE) { | 
 | 680 |         /* query case */ | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 681 |         out->hal_channel_count = profile_get_default_channel_count(&device_info->profile); | 
| Paul McLean | 64345f8 | 2016-06-06 13:26:05 -0600 | [diff] [blame] | 682 |         calc_mask = true; | 
| Andy Hung | 182ddc7 | 2015-05-05 23:37:30 -0700 | [diff] [blame] | 683 |     } else { | 
| Paul McLean | 64345f8 | 2016-06-06 13:26:05 -0600 | [diff] [blame] | 684 |         /* explicit case */ | 
 | 685 |         out->hal_channel_count = audio_channel_count_from_out_mask(config->channel_mask); | 
| Andy Hung | 182ddc7 | 2015-05-05 23:37:30 -0700 | [diff] [blame] | 686 |     } | 
| Paul McLean | 698dbd7 | 2015-11-03 12:24:30 -0800 | [diff] [blame] | 687 |  | 
| Paul McLean | 64345f8 | 2016-06-06 13:26:05 -0600 | [diff] [blame] | 688 |     /* The Framework is currently limited to no more than this number of channels */ | 
 | 689 |     if (out->hal_channel_count > FCC_8) { | 
 | 690 |         out->hal_channel_count = FCC_8; | 
 | 691 |         calc_mask = true; | 
 | 692 |     } | 
 | 693 |  | 
 | 694 |     if (calc_mask) { | 
 | 695 |         /* need to calculate the mask from channel count either because this is the query case | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 696 |          * or the specified mask isn't valid for this device, or is more than the FW can handle */ | 
| Paul McLean | 64345f8 | 2016-06-06 13:26:05 -0600 | [diff] [blame] | 697 |         config->channel_mask = out->hal_channel_count <= FCC_2 | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 698 |                 /* position mask for mono and stereo*/ | 
 | 699 |                 ? audio_channel_out_mask_from_count(out->hal_channel_count) | 
 | 700 |                 /* otherwise indexed */ | 
 | 701 |                 : audio_channel_mask_for_index_assignment_from_count(out->hal_channel_count); | 
| Paul McLean | 64345f8 | 2016-06-06 13:26:05 -0600 | [diff] [blame] | 702 |     } | 
 | 703 |  | 
| Andy Hung | 182ddc7 | 2015-05-05 23:37:30 -0700 | [diff] [blame] | 704 |     out->hal_channel_mask = config->channel_mask; | 
 | 705 |  | 
| Paul McLean | 64345f8 | 2016-06-06 13:26:05 -0600 | [diff] [blame] | 706 |     // Validate the "logical" channel count against support in the "actual" profile. | 
 | 707 |     // if they differ, choose the "actual" number of channels *closest* to the "logical". | 
 | 708 |     // and store THAT in proxy_config.channels | 
| Paul McLean | cf5310a | 2018-08-22 14:33:12 -0700 | [diff] [blame] | 709 |     proxy_config.channels = | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 710 |             profile_get_closest_channel_count(&device_info->profile, out->hal_channel_count); | 
 | 711 |     proxy_prepare(&device_info->proxy, &device_info->profile, &proxy_config); | 
 | 712 |  | 
 | 713 |     list_add_tail(&out->alsa_devices, &device_info->list_node); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 714 |  | 
| Paul McLean | 96c4d30 | 2017-12-05 09:50:26 -0800 | [diff] [blame] | 715 |     /* TODO The retry mechanism isn't implemented in AudioPolicyManager/AudioFlinger | 
 | 716 |      * So clear any errors that may have occurred above. | 
 | 717 |      */ | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 718 |     ret = 0; | 
 | 719 |  | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 720 |     out->conversion_buffer = NULL; | 
 | 721 |     out->conversion_buffer_size = 0; | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 722 |  | 
 | 723 |     out->standby = true; | 
 | 724 |  | 
| Paul McLean | 6a75e4e | 2016-05-25 14:09:02 -0600 | [diff] [blame] | 725 |     /* Save the stream for adev_dump() */ | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 726 |     adev_add_stream_to_list(out->adev, &out->adev->output_stream_list, &out->list_node); | 
| Paul McLean | 6a75e4e | 2016-05-25 14:09:02 -0600 | [diff] [blame] | 727 |  | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 728 |     *stream_out = &out->stream; | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 729 |  | 
 | 730 |     return ret; | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 731 | } | 
 | 732 |  | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 733 | static void adev_close_output_stream(struct audio_hw_device *hw_dev, | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 734 |                                      struct audio_stream_out *stream) | 
 | 735 | { | 
 | 736 |     struct stream_out *out = (struct stream_out *)stream; | 
| Paul McLean | 6a75e4e | 2016-05-25 14:09:02 -0600 | [diff] [blame] | 737 |  | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 738 |     /* Close the pcm device */ | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 739 |     out_standby(&stream->common); | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 740 |  | 
 | 741 |     free(out->conversion_buffer); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 742 |  | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 743 |     out->conversion_buffer = NULL; | 
 | 744 |     out->conversion_buffer_size = 0; | 
 | 745 |  | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 746 |     struct listnode *node, *temp; | 
 | 747 |     struct alsa_device_info *item = NULL; | 
 | 748 |     stream_lock(&out->lock); | 
 | 749 |     list_for_each_safe (node, temp, &out->alsa_devices) { | 
 | 750 |         item = node_to_item(node, struct alsa_device_info, list_node); | 
 | 751 |         if (item != NULL) { | 
 | 752 |             list_remove(&item->list_node); | 
 | 753 |             free(item); | 
 | 754 |         } | 
 | 755 |     } | 
 | 756 |     stream_unlock(&out->lock); | 
 | 757 |  | 
| Paul McLean | cf5310a | 2018-08-22 14:33:12 -0700 | [diff] [blame] | 758 |     adev_remove_stream_from_list(out->adev, &out->list_node); | 
 | 759 |  | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 760 |     device_lock(out->adev); | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 761 |     out->adev->device_sample_rate = 0; | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 762 |     device_unlock(out->adev); | 
| Paul McLean | 1d585cc | 2016-05-24 11:28:10 -0600 | [diff] [blame] | 763 |  | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 764 |     free(stream); | 
 | 765 | } | 
 | 766 |  | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 767 | static size_t adev_get_input_buffer_size(const struct audio_hw_device *hw_dev, | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 768 |                                          const struct audio_config *config) | 
 | 769 | { | 
 | 770 |     /* TODO This needs to be calculated based on format/channels/rate */ | 
 | 771 |     return 320; | 
 | 772 | } | 
 | 773 |  | 
 | 774 | /* | 
 | 775 |  * IN functions | 
 | 776 |  */ | 
 | 777 | static uint32_t in_get_sample_rate(const struct audio_stream *stream) | 
 | 778 | { | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 779 |     struct alsa_device_info *device_info = stream_get_first_alsa_device( | 
 | 780 |             &((const struct stream_in *)stream)->alsa_devices); | 
 | 781 |     if (device_info == NULL) { | 
 | 782 |         ALOGW("%s device info is null", __func__); | 
 | 783 |         return 0; | 
 | 784 |     } | 
 | 785 |     uint32_t rate = proxy_get_sample_rate(&device_info->proxy); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 786 |     ALOGV("in_get_sample_rate() = %d", rate); | 
 | 787 |     return rate; | 
 | 788 | } | 
 | 789 |  | 
 | 790 | static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate) | 
 | 791 | { | 
 | 792 |     ALOGV("in_set_sample_rate(%d) - NOPE", rate); | 
 | 793 |     return -ENOSYS; | 
 | 794 | } | 
 | 795 |  | 
 | 796 | static size_t in_get_buffer_size(const struct audio_stream *stream) | 
 | 797 | { | 
 | 798 |     const struct stream_in * in = ((const struct stream_in*)stream); | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 799 |     struct alsa_device_info *device_info = stream_get_first_alsa_device(&in->alsa_devices); | 
 | 800 |     if (device_info == NULL) { | 
 | 801 |         ALOGW("%s device info is null", __func__); | 
 | 802 |         return 0; | 
 | 803 |     } | 
 | 804 |     return proxy_get_period_size(&device_info->proxy) * audio_stream_in_frame_size(&(in->stream)); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 805 | } | 
 | 806 |  | 
 | 807 | static uint32_t in_get_channels(const struct audio_stream *stream) | 
 | 808 | { | 
| Paul McLean | 2cfd81b | 2014-09-15 12:32:23 -0700 | [diff] [blame] | 809 |     const struct stream_in *in = (const struct stream_in*)stream; | 
| Andy Hung | 780f1f8 | 2015-04-22 22:14:39 -0700 | [diff] [blame] | 810 |     return in->hal_channel_mask; | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 811 | } | 
 | 812 |  | 
 | 813 | static audio_format_t in_get_format(const struct audio_stream *stream) | 
 | 814 | { | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 815 |     struct alsa_device_info *device_info = stream_get_first_alsa_device( | 
 | 816 |             &((const struct stream_in *)stream)->alsa_devices); | 
 | 817 |     if (device_info == NULL) { | 
 | 818 |         ALOGW("%s device info is null", __func__); | 
 | 819 |         return AUDIO_FORMAT_DEFAULT; | 
 | 820 |     } | 
 | 821 |      alsa_device_proxy *proxy = &device_info->proxy; | 
| Andy Hung | 780f1f8 | 2015-04-22 22:14:39 -0700 | [diff] [blame] | 822 |      audio_format_t format = audio_format_from_pcm_format(proxy_get_format(proxy)); | 
 | 823 |      return format; | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 824 | } | 
 | 825 |  | 
 | 826 | static int in_set_format(struct audio_stream *stream, audio_format_t format) | 
 | 827 | { | 
 | 828 |     ALOGV("in_set_format(%d) - NOPE", format); | 
 | 829 |  | 
 | 830 |     return -ENOSYS; | 
 | 831 | } | 
 | 832 |  | 
 | 833 | static int in_standby(struct audio_stream *stream) | 
 | 834 | { | 
 | 835 |     struct stream_in *in = (struct stream_in *)stream; | 
 | 836 |  | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 837 |     stream_lock(&in->lock); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 838 |     if (!in->standby) { | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 839 |         struct listnode *node; | 
 | 840 |         list_for_each (node, &in->alsa_devices) { | 
 | 841 |             struct alsa_device_info *device_info = | 
 | 842 |                     node_to_item(node, struct alsa_device_info, list_node); | 
 | 843 |             proxy_close(&device_info->proxy); | 
 | 844 |         } | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 845 |         in->standby = true; | 
 | 846 |     } | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 847 |     stream_unlock(&in->lock); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 848 |  | 
 | 849 |     return 0; | 
 | 850 | } | 
 | 851 |  | 
 | 852 | static int in_dump(const struct audio_stream *stream, int fd) | 
 | 853 | { | 
| Paul McLean | 6a75e4e | 2016-05-25 14:09:02 -0600 | [diff] [blame] | 854 |   const struct stream_in* in_stream = (const struct stream_in*)stream; | 
 | 855 |   if (in_stream != NULL) { | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 856 |       stream_dump_alsa_devices(&in_stream->alsa_devices, fd); | 
| Paul McLean | 6a75e4e | 2016-05-25 14:09:02 -0600 | [diff] [blame] | 857 |   } | 
 | 858 |  | 
 | 859 |   return 0; | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 860 | } | 
 | 861 |  | 
 | 862 | static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) | 
 | 863 | { | 
| Paul McLean | 65ec72b | 2015-02-18 09:13:24 -0800 | [diff] [blame] | 864 |     ALOGV("in_set_parameters() keys:%s", kvpairs); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 865 |  | 
 | 866 |     struct stream_in *in = (struct stream_in *)stream; | 
 | 867 |  | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 868 |     int ret_value = 0; | 
| Eric Laurent | 05333d4 | 2014-08-04 20:29:17 -0700 | [diff] [blame] | 869 |     int card = -1; | 
 | 870 |     int device = -1; | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 871 |  | 
| Paul McLean | 65ec72b | 2015-02-18 09:13:24 -0800 | [diff] [blame] | 872 |     if (!parse_card_device_params(kvpairs, &card, &device)) { | 
 | 873 |         // nothing to do | 
 | 874 |         return ret_value; | 
 | 875 |     } | 
 | 876 |  | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 877 |     stream_lock(&in->lock); | 
 | 878 |     device_lock(in->adev); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 879 |  | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 880 |     struct alsa_device_info* device_info = stream_get_first_alsa_device(&in->alsa_devices); | 
 | 881 |  | 
 | 882 |     if (card >= 0 && device >= 0 && | 
 | 883 |             device_info != NULL && !profile_is_cached_for(&device_info->profile, card, device)) { | 
| Paul McLean | cf5310a | 2018-08-22 14:33:12 -0700 | [diff] [blame] | 884 |         /* cannot read pcm device info if capture is active, or more than one open stream */ | 
| Andy Hung | b10ce6d | 2017-10-27 19:37:58 -0700 | [diff] [blame] | 885 |         if (!in->standby || in->adev->inputs_open > 1) | 
| Eric Laurent | 05333d4 | 2014-08-04 20:29:17 -0700 | [diff] [blame] | 886 |             ret_value = -ENOSYS; | 
 | 887 |         else { | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 888 |             int saved_card = device_info->profile.card; | 
 | 889 |             int saved_device = device_info->profile.device; | 
 | 890 |             device_info->profile.card = card; | 
 | 891 |             device_info->profile.device = device; | 
 | 892 |             ret_value = profile_read_device_info(&device_info->profile) ? 0 : -EINVAL; | 
| Eric Laurent | 05333d4 | 2014-08-04 20:29:17 -0700 | [diff] [blame] | 893 |             if (ret_value != 0) { | 
| Paul McLean | cf5310a | 2018-08-22 14:33:12 -0700 | [diff] [blame] | 894 |                 ALOGE("Can't read device profile. card:%d, device:%d", card, device); | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 895 |                 device_info->profile.card = saved_card; | 
 | 896 |                 device_info->profile.device = saved_device; | 
| Eric Laurent | 05333d4 | 2014-08-04 20:29:17 -0700 | [diff] [blame] | 897 |             } | 
 | 898 |         } | 
| Paul McLean | 2c6196f | 2014-08-20 16:50:25 -0700 | [diff] [blame] | 899 |     } | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 900 |  | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 901 |     device_unlock(in->adev); | 
 | 902 |     stream_unlock(&in->lock); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 903 |  | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 904 |     return ret_value; | 
 | 905 | } | 
 | 906 |  | 
 | 907 | static char * in_get_parameters(const struct audio_stream *stream, const char *keys) | 
 | 908 | { | 
 | 909 |     struct stream_in *in = (struct stream_in *)stream; | 
 | 910 |  | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 911 |     stream_lock(&in->lock); | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 912 |     struct alsa_device_info *device_info = stream_get_first_alsa_device(&in->alsa_devices); | 
 | 913 |     char *params_str = NULL; | 
 | 914 |     if (device_info != NULL) { | 
 | 915 |         params_str =  device_get_parameters(&device_info->profile, keys); | 
 | 916 |     } | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 917 |     stream_unlock(&in->lock); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 918 |  | 
 | 919 |     return params_str; | 
 | 920 | } | 
 | 921 |  | 
 | 922 | static int in_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) | 
 | 923 | { | 
 | 924 |     return 0; | 
 | 925 | } | 
 | 926 |  | 
 | 927 | static int in_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) | 
 | 928 | { | 
 | 929 |     return 0; | 
 | 930 | } | 
 | 931 |  | 
 | 932 | static int in_set_gain(struct audio_stream_in *stream, float gain) | 
 | 933 | { | 
 | 934 |     return 0; | 
 | 935 | } | 
 | 936 |  | 
 | 937 | /* must be called with hw device and output stream mutexes locked */ | 
 | 938 | static int start_input_stream(struct stream_in *in) | 
 | 939 | { | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 940 |     // Only care about the first device as only one input device is allowed. | 
 | 941 |     struct alsa_device_info *device_info = stream_get_first_alsa_device(&in->alsa_devices); | 
 | 942 |     if (device_info == NULL) { | 
 | 943 |         return -ENODEV; | 
 | 944 |     } | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 945 |  | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 946 |     ALOGV("start_input_stream(card:%d device:%d)", | 
 | 947 |             device_info->profile.card, device_info->profile.device); | 
 | 948 |     return proxy_open(&device_info->proxy); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 949 | } | 
 | 950 |  | 
 | 951 | /* TODO mutex stuff here (see out_write) */ | 
 | 952 | static ssize_t in_read(struct audio_stream_in *stream, void* buffer, size_t bytes) | 
 | 953 | { | 
 | 954 |     size_t num_read_buff_bytes = 0; | 
 | 955 |     void * read_buff = buffer; | 
 | 956 |     void * out_buff = buffer; | 
| Pavan Chikkala | 83b47a6 | 2015-01-09 12:21:13 -0800 | [diff] [blame] | 957 |     int ret = 0; | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 958 |  | 
 | 959 |     struct stream_in * in = (struct stream_in *)stream; | 
 | 960 |  | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 961 |     stream_lock(&in->lock); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 962 |     if (in->standby) { | 
| Eric Laurent | 7031809 | 2015-06-19 17:49:17 -0700 | [diff] [blame] | 963 |         ret = start_input_stream(in); | 
| Eric Laurent | 7031809 | 2015-06-19 17:49:17 -0700 | [diff] [blame] | 964 |         if (ret != 0) { | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 965 |             goto err; | 
 | 966 |         } | 
 | 967 |         in->standby = false; | 
 | 968 |     } | 
 | 969 |  | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 970 |     // Only care about the first device as only one input device is allowed. | 
 | 971 |     struct alsa_device_info *device_info = stream_get_first_alsa_device(&in->alsa_devices); | 
 | 972 |     if (device_info == NULL) { | 
 | 973 |         return 0; | 
 | 974 |     } | 
 | 975 |  | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 976 |     /* | 
 | 977 |      * OK, we need to figure out how much data to read to be able to output the requested | 
 | 978 |      * number of bytes in the HAL format (16-bit, stereo). | 
 | 979 |      */ | 
 | 980 |     num_read_buff_bytes = bytes; | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 981 |     int num_device_channels = proxy_get_channel_count(&device_info->proxy); /* what we told Alsa */ | 
| Andy Hung | 780f1f8 | 2015-04-22 22:14:39 -0700 | [diff] [blame] | 982 |     int num_req_channels = in->hal_channel_count; /* what we told AudioFlinger */ | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 983 |  | 
 | 984 |     if (num_device_channels != num_req_channels) { | 
 | 985 |         num_read_buff_bytes = (num_device_channels * num_read_buff_bytes) / num_req_channels; | 
 | 986 |     } | 
 | 987 |  | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 988 |     /* Setup/Realloc the conversion buffer (if necessary). */ | 
 | 989 |     if (num_read_buff_bytes != bytes) { | 
 | 990 |         if (num_read_buff_bytes > in->conversion_buffer_size) { | 
 | 991 |             /*TODO Remove this when AudioPolicyManger/AudioFlinger support arbitrary formats | 
 | 992 |               (and do these conversions themselves) */ | 
 | 993 |             in->conversion_buffer_size = num_read_buff_bytes; | 
 | 994 |             in->conversion_buffer = realloc(in->conversion_buffer, in->conversion_buffer_size); | 
 | 995 |         } | 
 | 996 |         read_buff = in->conversion_buffer; | 
 | 997 |     } | 
 | 998 |  | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 999 |     ret = proxy_read(&device_info->proxy, read_buff, num_read_buff_bytes); | 
| Pavan Chikkala | 83b47a6 | 2015-01-09 12:21:13 -0800 | [diff] [blame] | 1000 |     if (ret == 0) { | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1001 |         if (num_device_channels != num_req_channels) { | 
 | 1002 |             // ALOGV("chans dev:%d req:%d", num_device_channels, num_req_channels); | 
 | 1003 |  | 
 | 1004 |             out_buff = buffer; | 
 | 1005 |             /* Num Channels conversion */ | 
 | 1006 |             if (num_device_channels != num_req_channels) { | 
 | 1007 |                 audio_format_t audio_format = in_get_format(&(in->stream.common)); | 
 | 1008 |                 unsigned sample_size_in_bytes = audio_bytes_per_sample(audio_format); | 
 | 1009 |  | 
 | 1010 |                 num_read_buff_bytes = | 
 | 1011 |                     adjust_channels(read_buff, num_device_channels, | 
 | 1012 |                                     out_buff, num_req_channels, | 
 | 1013 |                                     sample_size_in_bytes, num_read_buff_bytes); | 
 | 1014 |             } | 
 | 1015 |         } | 
| Eric Laurent | 253def9 | 2014-09-14 12:18:18 -0700 | [diff] [blame] | 1016 |  | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 1017 |         /* no need to acquire in->adev->lock to read mic_muted here as we don't change its state */ | 
 | 1018 |         if (num_read_buff_bytes > 0 && in->adev->mic_muted) | 
| Eric Laurent | 253def9 | 2014-09-14 12:18:18 -0700 | [diff] [blame] | 1019 |             memset(buffer, 0, num_read_buff_bytes); | 
| Viswanath L | 8c7e111 | 2014-10-31 13:07:39 +0530 | [diff] [blame] | 1020 |     } else { | 
| Eric Laurent | e649942 | 2015-01-09 17:03:27 -0800 | [diff] [blame] | 1021 |         num_read_buff_bytes = 0; // reset the value after USB headset is unplugged | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1022 |     } | 
 | 1023 |  | 
 | 1024 | err: | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 1025 |     stream_unlock(&in->lock); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1026 |     return num_read_buff_bytes; | 
 | 1027 | } | 
 | 1028 |  | 
 | 1029 | static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream) | 
 | 1030 | { | 
 | 1031 |     return 0; | 
 | 1032 | } | 
 | 1033 |  | 
| Andy Hung | fa6b4a6 | 2018-06-04 19:14:22 -0700 | [diff] [blame] | 1034 | static int in_get_capture_position(const struct audio_stream_in *stream, | 
 | 1035 |                                    int64_t *frames, int64_t *time) | 
 | 1036 | { | 
 | 1037 |     struct stream_in *in = (struct stream_in *)stream; // discard const qualifier | 
 | 1038 |     stream_lock(&in->lock); | 
 | 1039 |  | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 1040 |     struct alsa_device_info *device_info = stream_get_first_alsa_device(&in->alsa_devices); | 
 | 1041 |  | 
 | 1042 |     const int ret = device_info == NULL ? -ENODEV | 
 | 1043 |             : proxy_get_capture_position(&device_info->proxy, frames, time); | 
| Andy Hung | fa6b4a6 | 2018-06-04 19:14:22 -0700 | [diff] [blame] | 1044 |  | 
 | 1045 |     stream_unlock(&in->lock); | 
 | 1046 |     return ret; | 
 | 1047 | } | 
 | 1048 |  | 
| Paul McLean | fa3ae3e | 2018-12-12 09:57:02 -0800 | [diff] [blame] | 1049 | static int in_get_active_microphones(const struct audio_stream_in *stream, | 
 | 1050 |                                      struct audio_microphone_characteristic_t *mic_array, | 
 | 1051 |                                      size_t *mic_count) { | 
 | 1052 |     (void)stream; | 
 | 1053 |     (void)mic_array; | 
 | 1054 |     (void)mic_count; | 
 | 1055 |  | 
 | 1056 |     return -ENOSYS; | 
 | 1057 | } | 
 | 1058 |  | 
 | 1059 | static int in_set_microphone_direction(const struct audio_stream_in *stream, | 
 | 1060 |                                            audio_microphone_direction_t dir) { | 
 | 1061 |     (void)stream; | 
 | 1062 |     (void)dir; | 
 | 1063 |     ALOGV("---- in_set_microphone_direction()"); | 
 | 1064 |     return -ENOSYS; | 
 | 1065 | } | 
 | 1066 |  | 
 | 1067 | static int in_set_microphone_field_dimension(const struct audio_stream_in *stream, float zoom) { | 
 | 1068 |     (void)zoom; | 
 | 1069 |     ALOGV("---- in_set_microphone_field_dimension()"); | 
 | 1070 |     return -ENOSYS; | 
 | 1071 | } | 
 | 1072 |  | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 1073 | static int adev_open_input_stream(struct audio_hw_device *hw_dev, | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1074 |                                   audio_io_handle_t handle, | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 1075 |                                   audio_devices_t devicesSpec __unused, | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1076 |                                   struct audio_config *config, | 
 | 1077 |                                   struct audio_stream_in **stream_in, | 
| Eric Laurent | f5e2469 | 2014-07-27 16:14:57 -0700 | [diff] [blame] | 1078 |                                   audio_input_flags_t flags __unused, | 
| Eric Laurent | 981f774 | 2017-05-03 12:44:26 -0700 | [diff] [blame] | 1079 |                                   const char *address, | 
| Eric Laurent | f5e2469 | 2014-07-27 16:14:57 -0700 | [diff] [blame] | 1080 |                                   audio_source_t source __unused) | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1081 | { | 
| Paul McLean | 1d585cc | 2016-05-24 11:28:10 -0600 | [diff] [blame] | 1082 |     ALOGV("adev_open_input_stream() rate:%" PRIu32 ", chanMask:0x%" PRIX32 ", fmt:%" PRIu8, | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1083 |           config->sample_rate, config->channel_mask, config->format); | 
 | 1084 |  | 
| Andy Hung | b10ce6d | 2017-10-27 19:37:58 -0700 | [diff] [blame] | 1085 |     /* Pull out the card/device pair */ | 
 | 1086 |     int32_t card, device; | 
 | 1087 |     if (!parse_card_device_params(address, &card, &device)) { | 
 | 1088 |         ALOGW("%s fail - invalid address %s", __func__, address); | 
 | 1089 |         *stream_in = NULL; | 
 | 1090 |         return -EINVAL; | 
 | 1091 |     } | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1092 |  | 
| Andy Hung | b10ce6d | 2017-10-27 19:37:58 -0700 | [diff] [blame] | 1093 |     struct stream_in * const in = (struct stream_in *)calloc(1, sizeof(struct stream_in)); | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 1094 |     if (in == NULL) { | 
| Andy Hung | b10ce6d | 2017-10-27 19:37:58 -0700 | [diff] [blame] | 1095 |         *stream_in = NULL; | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1096 |         return -ENOMEM; | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 1097 |     } | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1098 |  | 
 | 1099 |     /* setup function pointers */ | 
 | 1100 |     in->stream.common.get_sample_rate = in_get_sample_rate; | 
 | 1101 |     in->stream.common.set_sample_rate = in_set_sample_rate; | 
 | 1102 |     in->stream.common.get_buffer_size = in_get_buffer_size; | 
 | 1103 |     in->stream.common.get_channels = in_get_channels; | 
 | 1104 |     in->stream.common.get_format = in_get_format; | 
 | 1105 |     in->stream.common.set_format = in_set_format; | 
 | 1106 |     in->stream.common.standby = in_standby; | 
 | 1107 |     in->stream.common.dump = in_dump; | 
 | 1108 |     in->stream.common.set_parameters = in_set_parameters; | 
 | 1109 |     in->stream.common.get_parameters = in_get_parameters; | 
 | 1110 |     in->stream.common.add_audio_effect = in_add_audio_effect; | 
 | 1111 |     in->stream.common.remove_audio_effect = in_remove_audio_effect; | 
 | 1112 |  | 
 | 1113 |     in->stream.set_gain = in_set_gain; | 
 | 1114 |     in->stream.read = in_read; | 
 | 1115 |     in->stream.get_input_frames_lost = in_get_input_frames_lost; | 
| Andy Hung | fa6b4a6 | 2018-06-04 19:14:22 -0700 | [diff] [blame] | 1116 |     in->stream.get_capture_position = in_get_capture_position; | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1117 |  | 
| Paul McLean | fa3ae3e | 2018-12-12 09:57:02 -0800 | [diff] [blame] | 1118 |     in->stream.get_active_microphones = in_get_active_microphones; | 
 | 1119 |     in->stream.set_microphone_direction = in_set_microphone_direction; | 
 | 1120 |     in->stream.set_microphone_field_dimension = in_set_microphone_field_dimension; | 
 | 1121 |  | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 1122 |     stream_lock_init(&in->lock); | 
| Eric Laurent | 7031809 | 2015-06-19 17:49:17 -0700 | [diff] [blame] | 1123 |  | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 1124 |     in->adev = (struct audio_device *)hw_dev; | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1125 |  | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 1126 |     list_init(&in->alsa_devices); | 
 | 1127 |     struct alsa_device_info *device_info = | 
 | 1128 |             (struct alsa_device_info *)calloc(1, sizeof(struct alsa_device_info)); | 
 | 1129 |     profile_init(&device_info->profile, PCM_IN); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1130 |  | 
 | 1131 |     struct pcm_config proxy_config; | 
| Paul McLean | 2c6196f | 2014-08-20 16:50:25 -0700 | [diff] [blame] | 1132 |     memset(&proxy_config, 0, sizeof(proxy_config)); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1133 |  | 
| Andy Hung | b10ce6d | 2017-10-27 19:37:58 -0700 | [diff] [blame] | 1134 |     int ret = 0; | 
| Paul McLean | cf5310a | 2018-08-22 14:33:12 -0700 | [diff] [blame] | 1135 |     device_lock(in->adev); | 
 | 1136 |     int num_open_inputs = in->adev->inputs_open; | 
 | 1137 |     device_unlock(in->adev); | 
 | 1138 |  | 
| Andy Hung | b10ce6d | 2017-10-27 19:37:58 -0700 | [diff] [blame] | 1139 |     /* Check if an input stream is already open */ | 
| Paul McLean | cf5310a | 2018-08-22 14:33:12 -0700 | [diff] [blame] | 1140 |     if (num_open_inputs > 0) { | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 1141 |         if (!profile_is_cached_for(&device_info->profile, card, device)) { | 
| Andy Hung | b10ce6d | 2017-10-27 19:37:58 -0700 | [diff] [blame] | 1142 |             ALOGW("%s fail - address card:%d device:%d doesn't match existing profile", | 
 | 1143 |                     __func__, card, device); | 
 | 1144 |             ret = -EINVAL; | 
 | 1145 |         } | 
 | 1146 |     } else { | 
 | 1147 |         /* Read input profile only if necessary */ | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 1148 |         device_info->profile.card = card; | 
 | 1149 |         device_info->profile.device = device; | 
 | 1150 |         if (!profile_read_device_info(&device_info->profile)) { | 
| Andy Hung | b10ce6d | 2017-10-27 19:37:58 -0700 | [diff] [blame] | 1151 |             ALOGW("%s fail - cannot read profile", __func__); | 
 | 1152 |             ret = -EINVAL; | 
 | 1153 |         } | 
 | 1154 |     } | 
 | 1155 |     if (ret != 0) { | 
| Andy Hung | b10ce6d | 2017-10-27 19:37:58 -0700 | [diff] [blame] | 1156 |         free(in); | 
 | 1157 |         *stream_in = NULL; | 
 | 1158 |         return ret; | 
 | 1159 |     } | 
| Paul McLean | 0f1753e | 2014-12-02 12:36:45 -0700 | [diff] [blame] | 1160 |  | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1161 |     /* Rate */ | 
| Paul McLean | cfdbd6b | 2018-07-27 11:16:02 -0600 | [diff] [blame] | 1162 |     int request_config_rate = config->sample_rate; | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1163 |     if (config->sample_rate == 0) { | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 1164 |         config->sample_rate = profile_get_default_sample_rate(&device_info->profile); | 
| Paul McLean | 9a1c305 | 2016-05-25 13:30:54 -0600 | [diff] [blame] | 1165 |     } | 
 | 1166 |  | 
| Paul McLean | cfdbd6b | 2018-07-27 11:16:02 -0600 | [diff] [blame] | 1167 |     if (in->adev->device_sample_rate != 0 &&   /* we are playing, so lock the rate if possible */ | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 1168 |         in->adev->device_sample_rate >= RATELOCK_THRESHOLD) {/* but only for high sample rates */ | 
| Paul McLean | cfdbd6b | 2018-07-27 11:16:02 -0600 | [diff] [blame] | 1169 |         if (config->sample_rate != in->adev->device_sample_rate) { | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 1170 |             unsigned highest_rate = profile_get_highest_sample_rate(&device_info->profile); | 
| Paul McLean | cfdbd6b | 2018-07-27 11:16:02 -0600 | [diff] [blame] | 1171 |             if (highest_rate == 0) { | 
 | 1172 |                 ret = -EINVAL; /* error with device */ | 
 | 1173 |             } else { | 
 | 1174 |                 proxy_config.rate = config->sample_rate = | 
 | 1175 |                         min(highest_rate, in->adev->device_sample_rate); | 
 | 1176 |                 if (request_config_rate != 0 && proxy_config.rate != config->sample_rate) { | 
 | 1177 |                     /* Changing the requested rate */ | 
 | 1178 |                     ret = -EINVAL; | 
 | 1179 |                 } else { | 
 | 1180 |                     /* Everything AOK! */ | 
 | 1181 |                     ret = 0; | 
 | 1182 |                 } | 
 | 1183 |             } | 
 | 1184 |         } | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 1185 |     } else if (profile_is_sample_rate_valid(&device_info->profile, config->sample_rate)) { | 
| Eric Laurent | 981f774 | 2017-05-03 12:44:26 -0700 | [diff] [blame] | 1186 |         proxy_config.rate = config->sample_rate; | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1187 |     } else { | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 1188 |         proxy_config.rate = config->sample_rate = | 
 | 1189 |                 profile_get_default_sample_rate(&device_info->profile); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1190 |         ret = -EINVAL; | 
 | 1191 |     } | 
| Paul McLean | cfdbd6b | 2018-07-27 11:16:02 -0600 | [diff] [blame] | 1192 |  | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1193 |     /* Format */ | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1194 |     if (config->format == AUDIO_FORMAT_DEFAULT) { | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 1195 |         proxy_config.format = profile_get_default_format(&device_info->profile); | 
| Andy Hung | 780f1f8 | 2015-04-22 22:14:39 -0700 | [diff] [blame] | 1196 |         config->format = audio_format_from_pcm_format(proxy_config.format); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1197 |     } else { | 
| Andy Hung | 780f1f8 | 2015-04-22 22:14:39 -0700 | [diff] [blame] | 1198 |         enum pcm_format fmt = pcm_format_from_audio_format(config->format); | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 1199 |         if (profile_is_format_valid(&device_info->profile, fmt)) { | 
| Andy Hung | 780f1f8 | 2015-04-22 22:14:39 -0700 | [diff] [blame] | 1200 |             proxy_config.format = fmt; | 
 | 1201 |         } else { | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 1202 |             proxy_config.format = profile_get_default_format(&device_info->profile); | 
| Andy Hung | 780f1f8 | 2015-04-22 22:14:39 -0700 | [diff] [blame] | 1203 |             config->format = audio_format_from_pcm_format(proxy_config.format); | 
 | 1204 |             ret = -EINVAL; | 
 | 1205 |         } | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1206 |     } | 
 | 1207 |  | 
| Paul McLean | 2cfd81b | 2014-09-15 12:32:23 -0700 | [diff] [blame] | 1208 |     /* Channels */ | 
| Paul McLean | 64345f8 | 2016-06-06 13:26:05 -0600 | [diff] [blame] | 1209 |     bool calc_mask = false; | 
 | 1210 |     if (config->channel_mask == AUDIO_CHANNEL_NONE) { | 
 | 1211 |         /* query case */ | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 1212 |         in->hal_channel_count = profile_get_default_channel_count(&device_info->profile); | 
| Paul McLean | 64345f8 | 2016-06-06 13:26:05 -0600 | [diff] [blame] | 1213 |         calc_mask = true; | 
| Andy Hung | 780f1f8 | 2015-04-22 22:14:39 -0700 | [diff] [blame] | 1214 |     } else { | 
| Paul McLean | 64345f8 | 2016-06-06 13:26:05 -0600 | [diff] [blame] | 1215 |         /* explicit case */ | 
| Andy Hung | 780f1f8 | 2015-04-22 22:14:39 -0700 | [diff] [blame] | 1216 |         in->hal_channel_count = audio_channel_count_from_in_mask(config->channel_mask); | 
 | 1217 |     } | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1218 |  | 
| Paul McLean | 64345f8 | 2016-06-06 13:26:05 -0600 | [diff] [blame] | 1219 |     /* The Framework is currently limited to no more than this number of channels */ | 
 | 1220 |     if (in->hal_channel_count > FCC_8) { | 
 | 1221 |         in->hal_channel_count = FCC_8; | 
 | 1222 |         calc_mask = true; | 
 | 1223 |     } | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1224 |  | 
| Paul McLean | 64345f8 | 2016-06-06 13:26:05 -0600 | [diff] [blame] | 1225 |     if (calc_mask) { | 
 | 1226 |         /* need to calculate the mask from channel count either because this is the query case | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 1227 |          * or the specified mask isn't valid for this device, or is more than the FW can handle */ | 
| Paul McLean | 64345f8 | 2016-06-06 13:26:05 -0600 | [diff] [blame] | 1228 |         in->hal_channel_mask = in->hal_channel_count <= FCC_2 | 
 | 1229 |             /* position mask for mono & stereo */ | 
 | 1230 |             ? audio_channel_in_mask_from_count(in->hal_channel_count) | 
 | 1231 |             /* otherwise indexed */ | 
 | 1232 |             : audio_channel_mask_for_index_assignment_from_count(in->hal_channel_count); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1233 |  | 
| Paul McLean | 64345f8 | 2016-06-06 13:26:05 -0600 | [diff] [blame] | 1234 |         // if we change the mask... | 
 | 1235 |         if (in->hal_channel_mask != config->channel_mask && | 
 | 1236 |             config->channel_mask != AUDIO_CHANNEL_NONE) { | 
 | 1237 |             config->channel_mask = in->hal_channel_mask; | 
 | 1238 |             ret = -EINVAL; | 
 | 1239 |         } | 
 | 1240 |     } else { | 
 | 1241 |         in->hal_channel_mask = config->channel_mask; | 
 | 1242 |     } | 
| Paul McLean | 6a75e4e | 2016-05-25 14:09:02 -0600 | [diff] [blame] | 1243 |  | 
| Paul McLean | 64345f8 | 2016-06-06 13:26:05 -0600 | [diff] [blame] | 1244 |     if (ret == 0) { | 
 | 1245 |         // Validate the "logical" channel count against support in the "actual" profile. | 
 | 1246 |         // if they differ, choose the "actual" number of channels *closest* to the "logical". | 
 | 1247 |         // and store THAT in proxy_config.channels | 
 | 1248 |         proxy_config.channels = | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 1249 |                 profile_get_closest_channel_count(&device_info->profile, in->hal_channel_count); | 
 | 1250 |         ret = proxy_prepare(&device_info->proxy, &device_info->profile, &proxy_config); | 
| Eric Laurent | 981f774 | 2017-05-03 12:44:26 -0700 | [diff] [blame] | 1251 |         if (ret == 0) { | 
 | 1252 |             in->standby = true; | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1253 |  | 
| Eric Laurent | 981f774 | 2017-05-03 12:44:26 -0700 | [diff] [blame] | 1254 |             in->conversion_buffer = NULL; | 
 | 1255 |             in->conversion_buffer_size = 0; | 
| Paul McLean | 64345f8 | 2016-06-06 13:26:05 -0600 | [diff] [blame] | 1256 |  | 
| Eric Laurent | 981f774 | 2017-05-03 12:44:26 -0700 | [diff] [blame] | 1257 |             *stream_in = &in->stream; | 
| Paul McLean | 64345f8 | 2016-06-06 13:26:05 -0600 | [diff] [blame] | 1258 |  | 
| Eric Laurent | 981f774 | 2017-05-03 12:44:26 -0700 | [diff] [blame] | 1259 |             /* Save this for adev_dump() */ | 
 | 1260 |             adev_add_stream_to_list(in->adev, &in->adev->input_stream_list, &in->list_node); | 
 | 1261 |         } else { | 
 | 1262 |             ALOGW("proxy_prepare error %d", ret); | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 1263 |             unsigned channel_count = proxy_get_channel_count(&device_info->proxy); | 
| Eric Laurent | 981f774 | 2017-05-03 12:44:26 -0700 | [diff] [blame] | 1264 |             config->channel_mask = channel_count <= FCC_2 | 
 | 1265 |                 ? audio_channel_in_mask_from_count(channel_count) | 
 | 1266 |                 : audio_channel_mask_for_index_assignment_from_count(channel_count); | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 1267 |             config->format = audio_format_from_pcm_format(proxy_get_format(&device_info->proxy)); | 
 | 1268 |             config->sample_rate = proxy_get_sample_rate(&device_info->proxy); | 
| Eric Laurent | 981f774 | 2017-05-03 12:44:26 -0700 | [diff] [blame] | 1269 |         } | 
 | 1270 |     } | 
| Paul McLean | 64345f8 | 2016-06-06 13:26:05 -0600 | [diff] [blame] | 1271 |  | 
| Eric Laurent | 981f774 | 2017-05-03 12:44:26 -0700 | [diff] [blame] | 1272 |     if (ret != 0) { | 
| Paul McLean | 64345f8 | 2016-06-06 13:26:05 -0600 | [diff] [blame] | 1273 |         // Deallocate this stream on error, because AudioFlinger won't call | 
 | 1274 |         // adev_close_input_stream() in this case. | 
 | 1275 |         *stream_in = NULL; | 
 | 1276 |         free(in); | 
| Mikhail Naganov | 1d08a56 | 2020-03-26 13:18:12 -0700 | [diff] [blame] | 1277 |         return ret; | 
| Paul McLean | 64345f8 | 2016-06-06 13:26:05 -0600 | [diff] [blame] | 1278 |     } | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1279 |  | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 1280 |     list_add_tail(&in->alsa_devices, &device_info->list_node); | 
 | 1281 |  | 
| Andy Hung | b10ce6d | 2017-10-27 19:37:58 -0700 | [diff] [blame] | 1282 |     device_lock(in->adev); | 
 | 1283 |     ++in->adev->inputs_open; | 
 | 1284 |     device_unlock(in->adev); | 
 | 1285 |  | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1286 |     return ret; | 
 | 1287 | } | 
 | 1288 |  | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 1289 | static void adev_close_input_stream(struct audio_hw_device *hw_dev, | 
 | 1290 |                                     struct audio_stream_in *stream) | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1291 | { | 
 | 1292 |     struct stream_in *in = (struct stream_in *)stream; | 
| Paul McLean | 6a75e4e | 2016-05-25 14:09:02 -0600 | [diff] [blame] | 1293 |  | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 1294 |     adev_remove_stream_from_list(in->adev, &in->list_node); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1295 |  | 
| Andy Hung | b10ce6d | 2017-10-27 19:37:58 -0700 | [diff] [blame] | 1296 |     device_lock(in->adev); | 
 | 1297 |     --in->adev->inputs_open; | 
| jiabin | 8b265c9 | 2020-10-21 15:16:40 -0700 | [diff] [blame] | 1298 |     struct alsa_device_info *device_info = stream_get_first_alsa_device(&in->alsa_devices); | 
 | 1299 |     if (device_info != NULL) { | 
 | 1300 |         ALOGV("adev_close_input_stream(c:%d d:%d)", | 
 | 1301 |                 device_info->profile.card, device_info->profile.device); | 
 | 1302 |     } | 
| Andy Hung | b10ce6d | 2017-10-27 19:37:58 -0700 | [diff] [blame] | 1303 |     LOG_ALWAYS_FATAL_IF(in->adev->inputs_open < 0, | 
 | 1304 |             "invalid inputs_open: %d", in->adev->inputs_open); | 
 | 1305 |     device_unlock(in->adev); | 
 | 1306 |  | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1307 |     /* Close the pcm device */ | 
 | 1308 |     in_standby(&stream->common); | 
 | 1309 |  | 
 | 1310 |     free(in->conversion_buffer); | 
 | 1311 |  | 
 | 1312 |     free(stream); | 
 | 1313 | } | 
 | 1314 |  | 
 | 1315 | /* | 
 | 1316 |  * ADEV Functions | 
 | 1317 |  */ | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 1318 | static int adev_set_parameters(struct audio_hw_device *hw_dev, const char *kvpairs) | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 1319 | { | 
 | 1320 |     return 0; | 
 | 1321 | } | 
 | 1322 |  | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 1323 | static char * adev_get_parameters(const struct audio_hw_device *hw_dev, const char *keys) | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 1324 | { | 
 | 1325 |     return strdup(""); | 
 | 1326 | } | 
 | 1327 |  | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 1328 | static int adev_init_check(const struct audio_hw_device *hw_dev) | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 1329 | { | 
 | 1330 |     return 0; | 
 | 1331 | } | 
 | 1332 |  | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 1333 | static int adev_set_voice_volume(struct audio_hw_device *hw_dev, float volume) | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 1334 | { | 
 | 1335 |     return -ENOSYS; | 
 | 1336 | } | 
 | 1337 |  | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 1338 | static int adev_set_master_volume(struct audio_hw_device *hw_dev, float volume) | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 1339 | { | 
 | 1340 |     return -ENOSYS; | 
 | 1341 | } | 
 | 1342 |  | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 1343 | static int adev_set_mode(struct audio_hw_device *hw_dev, audio_mode_t mode) | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 1344 | { | 
 | 1345 |     return 0; | 
 | 1346 | } | 
 | 1347 |  | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 1348 | static int adev_set_mic_mute(struct audio_hw_device *hw_dev, bool state) | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 1349 | { | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 1350 |     struct audio_device * adev = (struct audio_device *)hw_dev; | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 1351 |     device_lock(adev); | 
| Eric Laurent | 253def9 | 2014-09-14 12:18:18 -0700 | [diff] [blame] | 1352 |     adev->mic_muted = state; | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 1353 |     device_unlock(adev); | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 1354 |     return -ENOSYS; | 
 | 1355 | } | 
 | 1356 |  | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 1357 | static int adev_get_mic_mute(const struct audio_hw_device *hw_dev, bool *state) | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 1358 | { | 
 | 1359 |     return -ENOSYS; | 
 | 1360 | } | 
 | 1361 |  | 
| Paul McLean | 76dba68 | 2016-06-01 12:29:11 -0600 | [diff] [blame] | 1362 | static int adev_dump(const struct audio_hw_device *device, int fd) | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 1363 | { | 
| Paul McLean | 6a75e4e | 2016-05-25 14:09:02 -0600 | [diff] [blame] | 1364 |     dprintf(fd, "\nUSB audio module:\n"); | 
 | 1365 |  | 
 | 1366 |     struct audio_device* adev = (struct audio_device*)device; | 
 | 1367 |     const int kNumRetries = 3; | 
 | 1368 |     const int kSleepTimeMS = 500; | 
 | 1369 |  | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 1370 |     // use device_try_lock() in case we dumpsys during a deadlock | 
| Paul McLean | 6a75e4e | 2016-05-25 14:09:02 -0600 | [diff] [blame] | 1371 |     int retry = kNumRetries; | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 1372 |     while (retry > 0 && device_try_lock(adev) != 0) { | 
| Paul McLean | 6a75e4e | 2016-05-25 14:09:02 -0600 | [diff] [blame] | 1373 |       sleep(kSleepTimeMS); | 
 | 1374 |       retry--; | 
 | 1375 |     } | 
 | 1376 |  | 
 | 1377 |     if (retry > 0) { | 
 | 1378 |         if (list_empty(&adev->output_stream_list)) { | 
 | 1379 |             dprintf(fd, "  No output streams.\n"); | 
 | 1380 |         } else { | 
 | 1381 |             struct listnode* node; | 
 | 1382 |             list_for_each(node, &adev->output_stream_list) { | 
 | 1383 |                 struct audio_stream* stream = | 
 | 1384 |                         (struct audio_stream *)node_to_item(node, struct stream_out, list_node); | 
 | 1385 |                 out_dump(stream, fd); | 
 | 1386 |             } | 
 | 1387 |         } | 
 | 1388 |  | 
 | 1389 |         if (list_empty(&adev->input_stream_list)) { | 
 | 1390 |             dprintf(fd, "\n  No input streams.\n"); | 
 | 1391 |         } else { | 
 | 1392 |             struct listnode* node; | 
 | 1393 |             list_for_each(node, &adev->input_stream_list) { | 
 | 1394 |                 struct audio_stream* stream = | 
 | 1395 |                         (struct audio_stream *)node_to_item(node, struct stream_in, list_node); | 
 | 1396 |                 in_dump(stream, fd); | 
 | 1397 |             } | 
 | 1398 |         } | 
 | 1399 |  | 
| Paul McLean | 994ac07 | 2016-06-02 15:33:24 -0600 | [diff] [blame] | 1400 |         device_unlock(adev); | 
| Paul McLean | 6a75e4e | 2016-05-25 14:09:02 -0600 | [diff] [blame] | 1401 |     } else { | 
 | 1402 |         // Couldn't lock | 
 | 1403 |         dprintf(fd, "  Could not obtain device lock.\n"); | 
 | 1404 |     } | 
 | 1405 |  | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 1406 |     return 0; | 
 | 1407 | } | 
 | 1408 |  | 
 | 1409 | static int adev_close(hw_device_t *device) | 
 | 1410 | { | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 1411 |     free(device); | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 1412 |  | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 1413 |     return 0; | 
 | 1414 | } | 
 | 1415 |  | 
| Paul McLean | 30f4185 | 2014-04-16 15:44:20 -0700 | [diff] [blame] | 1416 | static int adev_open(const hw_module_t* module, const char* name, hw_device_t** device) | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 1417 | { | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 1418 |     if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) | 
 | 1419 |         return -EINVAL; | 
 | 1420 |  | 
| Paul McLean | eedc92e | 2013-12-19 15:46:15 -0800 | [diff] [blame] | 1421 |     struct audio_device *adev = calloc(1, sizeof(struct audio_device)); | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 1422 |     if (!adev) | 
 | 1423 |         return -ENOMEM; | 
 | 1424 |  | 
| Paul McLean | cf5310a | 2018-08-22 14:33:12 -0700 | [diff] [blame] | 1425 |     pthread_mutex_init(&adev->lock, (const pthread_mutexattr_t *) NULL); | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1426 |  | 
| Paul McLean | 6a75e4e | 2016-05-25 14:09:02 -0600 | [diff] [blame] | 1427 |     list_init(&adev->output_stream_list); | 
 | 1428 |     list_init(&adev->input_stream_list); | 
 | 1429 |  | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 1430 |     adev->hw_device.common.tag = HARDWARE_DEVICE_TAG; | 
| Eric Laurent | 85e08e2 | 2012-08-28 14:30:35 -0700 | [diff] [blame] | 1431 |     adev->hw_device.common.version = AUDIO_DEVICE_API_VERSION_2_0; | 
| Paul McLean | c88e6ae | 2014-07-16 09:48:34 -0700 | [diff] [blame] | 1432 |     adev->hw_device.common.module = (struct hw_module_t *)module; | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 1433 |     adev->hw_device.common.close = adev_close; | 
 | 1434 |  | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 1435 |     adev->hw_device.init_check = adev_init_check; | 
 | 1436 |     adev->hw_device.set_voice_volume = adev_set_voice_volume; | 
 | 1437 |     adev->hw_device.set_master_volume = adev_set_master_volume; | 
 | 1438 |     adev->hw_device.set_mode = adev_set_mode; | 
 | 1439 |     adev->hw_device.set_mic_mute = adev_set_mic_mute; | 
 | 1440 |     adev->hw_device.get_mic_mute = adev_get_mic_mute; | 
 | 1441 |     adev->hw_device.set_parameters = adev_set_parameters; | 
 | 1442 |     adev->hw_device.get_parameters = adev_get_parameters; | 
 | 1443 |     adev->hw_device.get_input_buffer_size = adev_get_input_buffer_size; | 
 | 1444 |     adev->hw_device.open_output_stream = adev_open_output_stream; | 
 | 1445 |     adev->hw_device.close_output_stream = adev_close_output_stream; | 
 | 1446 |     adev->hw_device.open_input_stream = adev_open_input_stream; | 
 | 1447 |     adev->hw_device.close_input_stream = adev_close_input_stream; | 
 | 1448 |     adev->hw_device.dump = adev_dump; | 
 | 1449 |  | 
 | 1450 |     *device = &adev->hw_device.common; | 
 | 1451 |  | 
 | 1452 |     return 0; | 
 | 1453 | } | 
 | 1454 |  | 
 | 1455 | static struct hw_module_methods_t hal_module_methods = { | 
 | 1456 |     .open = adev_open, | 
 | 1457 | }; | 
 | 1458 |  | 
 | 1459 | struct audio_module HAL_MODULE_INFO_SYM = { | 
 | 1460 |     .common = { | 
 | 1461 |         .tag = HARDWARE_MODULE_TAG, | 
| Mike Lockwood | 46a9809 | 2012-04-24 16:41:18 -0700 | [diff] [blame] | 1462 |         .module_api_version = AUDIO_MODULE_API_VERSION_0_1, | 
 | 1463 |         .hal_api_version = HARDWARE_HAL_API_VERSION, | 
| Simon Wilson | 19957a3 | 2012-04-06 16:17:12 -0700 | [diff] [blame] | 1464 |         .id = AUDIO_HARDWARE_MODULE_ID, | 
 | 1465 |         .name = "USB audio HW HAL", | 
 | 1466 |         .author = "The Android Open Source Project", | 
 | 1467 |         .methods = &hal_module_methods, | 
 | 1468 |     }, | 
 | 1469 | }; |