| lh | 9ed821d | 2023-04-07 01:36:19 -0700 | [diff] [blame] | 1 | /* pcm.c | 
 | 2 | ** | 
 | 3 | ** Copyright 2011, The Android Open Source Project | 
 | 4 | ** | 
 | 5 | ** Redistribution and use in source and binary forms, with or without | 
 | 6 | ** modification, are permitted provided that the following conditions are met: | 
 | 7 | **     * Redistributions of source code must retain the above copyright | 
 | 8 | **       notice, this list of conditions and the following disclaimer. | 
 | 9 | **     * Redistributions in binary form must reproduce the above copyright | 
 | 10 | **       notice, this list of conditions and the following disclaimer in the | 
 | 11 | **       documentation and/or other materials provided with the distribution. | 
 | 12 | **     * Neither the name of The Android Open Source Project nor the names of | 
 | 13 | **       its contributors may be used to endorse or promote products derived | 
 | 14 | **       from this software without specific prior written permission. | 
 | 15 | ** | 
 | 16 | ** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND | 
 | 17 | ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
 | 18 | ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
 | 19 | ** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE | 
 | 20 | ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
 | 21 | ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | 
 | 22 | ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | 
 | 23 | ** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 
 | 24 | ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 
 | 25 | ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | 
 | 26 | ** DAMAGE. | 
 | 27 | */ | 
 | 28 |  | 
 | 29 | #include <stdio.h> | 
 | 30 | #include <stdlib.h> | 
 | 31 | #include <fcntl.h> | 
 | 32 | #include <stdarg.h> | 
 | 33 | #include <string.h> | 
 | 34 | #include <errno.h> | 
 | 35 | #include <unistd.h> | 
 | 36 | #include <poll.h> | 
 | 37 |  | 
 | 38 | #include <sys/ioctl.h> | 
 | 39 | #include <sys/mman.h> | 
 | 40 | #include <sys/time.h> | 
 | 41 | #include <limits.h> | 
 | 42 |  | 
 | 43 | #include <linux/ioctl.h> | 
 | 44 | #define __force | 
 | 45 | #define __bitwise | 
 | 46 | #define __user | 
 | 47 | #include <sound/asound.h> | 
 | 48 |  | 
 | 49 | #include <tinyalsa/asoundlib.h> | 
 | 50 |  | 
 | 51 | #define PARAM_MAX SNDRV_PCM_HW_PARAM_LAST_INTERVAL | 
 | 52 |  | 
 | 53 | /* Logs information into a string; follows snprintf() in that | 
 | 54 |  * offset may be greater than size, and though no characters are copied | 
 | 55 |  * into string, characters are still counted into offset. */ | 
 | 56 | #define STRLOG(string, offset, size, ...) \ | 
 | 57 |     do { int temp, clipoffset = offset > size ? size : offset; \ | 
 | 58 |          temp = snprintf(string + clipoffset, size - clipoffset, __VA_ARGS__); \ | 
 | 59 |          if (temp > 0) offset += temp; } while (0) | 
 | 60 |  | 
 | 61 | #ifndef ARRAY_SIZE | 
 | 62 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) | 
 | 63 | #endif | 
 | 64 |  | 
 | 65 | /* refer to SNDRV_PCM_ACCESS_##index in sound/asound.h. */ | 
 | 66 | static const char * const access_lookup[] = { | 
 | 67 |         "MMAP_INTERLEAVED", | 
 | 68 |         "MMAP_NONINTERLEAVED", | 
 | 69 |         "MMAP_COMPLEX", | 
 | 70 |         "RW_INTERLEAVED", | 
 | 71 |         "RW_NONINTERLEAVED", | 
 | 72 | }; | 
 | 73 |  | 
 | 74 | /* refer to SNDRV_PCM_FORMAT_##index in sound/asound.h. */ | 
 | 75 | static const char * const format_lookup[] = { | 
 | 76 |         /*[0] =*/ "S8", | 
 | 77 |         "U8", | 
 | 78 |         "S16_LE", | 
 | 79 |         "S16_BE", | 
 | 80 |         "U16_LE", | 
 | 81 |         "U16_BE", | 
 | 82 |         "S24_LE", | 
 | 83 |         "S24_BE", | 
 | 84 |         "U24_LE", | 
 | 85 |         "U24_BE", | 
 | 86 |         "S32_LE", | 
 | 87 |         "S32_BE", | 
 | 88 |         "U32_LE", | 
 | 89 |         "U32_BE", | 
 | 90 |         "FLOAT_LE", | 
 | 91 |         "FLOAT_BE", | 
 | 92 |         "FLOAT64_LE", | 
 | 93 |         "FLOAT64_BE", | 
 | 94 |         "IEC958_SUBFRAME_LE", | 
 | 95 |         "IEC958_SUBFRAME_BE", | 
 | 96 |         "MU_LAW", | 
 | 97 |         "A_LAW", | 
 | 98 |         "IMA_ADPCM", | 
 | 99 |         "MPEG", | 
 | 100 |         /*[24] =*/ "GSM", | 
 | 101 |         /* gap */ | 
 | 102 |         [31] = "SPECIAL", | 
 | 103 |         "S24_3LE", | 
 | 104 |         "S24_3BE", | 
 | 105 |         "U24_3LE", | 
 | 106 |         "U24_3BE", | 
 | 107 |         "S20_3LE", | 
 | 108 |         "S20_3BE", | 
 | 109 |         "U20_3LE", | 
 | 110 |         "U20_3BE", | 
 | 111 |         "S18_3LE", | 
 | 112 |         "S18_3BE", | 
 | 113 |         "U18_3LE", | 
 | 114 |         /*[43] =*/ "U18_3BE", | 
 | 115 | #if 0 | 
 | 116 |         /* recent additions, may not be present on local asound.h */ | 
 | 117 |         "G723_24", | 
 | 118 |         "G723_24_1B", | 
 | 119 |         "G723_40", | 
 | 120 |         "G723_40_1B", | 
 | 121 |         "DSD_U8", | 
 | 122 |         "DSD_U16_LE", | 
 | 123 | #endif | 
 | 124 | }; | 
 | 125 |  | 
 | 126 | /* refer to SNDRV_PCM_SUBFORMAT_##index in sound/asound.h. */ | 
 | 127 | static const char * const subformat_lookup[] = { | 
 | 128 |         "STD", | 
 | 129 | }; | 
 | 130 |  | 
 | 131 | static inline int param_is_mask(int p) | 
 | 132 | { | 
 | 133 |     return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && | 
 | 134 |         (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); | 
 | 135 | } | 
 | 136 |  | 
 | 137 | static inline int param_is_interval(int p) | 
 | 138 | { | 
 | 139 |     return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) && | 
 | 140 |         (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL); | 
 | 141 | } | 
 | 142 |  | 
 | 143 | static inline struct snd_interval *param_to_interval(struct snd_pcm_hw_params *p, int n) | 
 | 144 | { | 
 | 145 |     return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]); | 
 | 146 | } | 
 | 147 |  | 
 | 148 | static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n) | 
 | 149 | { | 
 | 150 |     return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); | 
 | 151 | } | 
 | 152 |  | 
 | 153 | static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit) | 
 | 154 | { | 
 | 155 |     if (bit >= SNDRV_MASK_MAX) | 
 | 156 |         return; | 
 | 157 |     if (param_is_mask(n)) { | 
 | 158 |         struct snd_mask *m = param_to_mask(p, n); | 
 | 159 |         m->bits[0] = 0; | 
 | 160 |         m->bits[1] = 0; | 
 | 161 |         m->bits[bit >> 5] |= (1 << (bit & 31)); | 
 | 162 |     } | 
 | 163 | } | 
 | 164 |  | 
 | 165 | static void param_set_min(struct snd_pcm_hw_params *p, int n, unsigned int val) | 
 | 166 | { | 
 | 167 |     if (param_is_interval(n)) { | 
 | 168 |         struct snd_interval *i = param_to_interval(p, n); | 
 | 169 |         i->min = val; | 
 | 170 |     } | 
 | 171 | } | 
 | 172 |  | 
 | 173 | static unsigned int param_get_min(struct snd_pcm_hw_params *p, int n) | 
 | 174 | { | 
 | 175 |     if (param_is_interval(n)) { | 
 | 176 |         struct snd_interval *i = param_to_interval(p, n); | 
 | 177 |         return i->min; | 
 | 178 |     } | 
 | 179 |     return 0; | 
 | 180 | } | 
 | 181 |  | 
 | 182 | static void param_set_max(struct snd_pcm_hw_params *p, int n, unsigned int val) | 
 | 183 | { | 
 | 184 |     if (param_is_interval(n)) { | 
 | 185 |         struct snd_interval *i = param_to_interval(p, n); | 
 | 186 |         i->max = val; | 
 | 187 |     } | 
 | 188 | } | 
 | 189 |  | 
 | 190 | static unsigned int param_get_max(struct snd_pcm_hw_params *p, int n) | 
 | 191 | { | 
 | 192 |     if (param_is_interval(n)) { | 
 | 193 |         struct snd_interval *i = param_to_interval(p, n); | 
 | 194 |         return i->max; | 
 | 195 |     } | 
 | 196 |     return 0; | 
 | 197 | } | 
 | 198 |  | 
 | 199 | static void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned int val) | 
 | 200 | { | 
 | 201 |     if (param_is_interval(n)) { | 
 | 202 |         struct snd_interval *i = param_to_interval(p, n); | 
 | 203 |         i->min = val; | 
 | 204 |         i->max = val; | 
 | 205 |         i->integer = 1; | 
 | 206 |     } | 
 | 207 | } | 
 | 208 |  | 
 | 209 | static unsigned int param_get_int(struct snd_pcm_hw_params *p, int n) | 
 | 210 | { | 
 | 211 |     if (param_is_interval(n)) { | 
 | 212 |         struct snd_interval *i = param_to_interval(p, n); | 
 | 213 |         if (i->integer) | 
 | 214 |             return i->max; | 
 | 215 |     } | 
 | 216 |     return 0; | 
 | 217 | } | 
 | 218 |  | 
 | 219 | static void param_init(struct snd_pcm_hw_params *p) | 
 | 220 | { | 
 | 221 |     int n; | 
 | 222 |  | 
 | 223 |     memset(p, 0, sizeof(*p)); | 
 | 224 |     for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK; | 
 | 225 |          n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) { | 
 | 226 |             struct snd_mask *m = param_to_mask(p, n); | 
 | 227 |             m->bits[0] = ~0; | 
 | 228 |             m->bits[1] = ~0; | 
 | 229 |     } | 
 | 230 |     for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; | 
 | 231 |          n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) { | 
 | 232 |             struct snd_interval *i = param_to_interval(p, n); | 
 | 233 |             i->min = 0; | 
 | 234 |             i->max = ~0; | 
 | 235 |     } | 
 | 236 |     p->rmask = ~0U; | 
 | 237 |     p->cmask = 0; | 
 | 238 |     p->info = ~0U; | 
 | 239 | } | 
 | 240 |  | 
 | 241 | #define PCM_ERROR_MAX 128 | 
 | 242 |  | 
 | 243 | struct pcm { | 
 | 244 |     int fd; | 
 | 245 |     unsigned int flags; | 
 | 246 |     int running:1; | 
 | 247 |     int prepared:1; | 
 | 248 |     int underruns; | 
 | 249 |     unsigned int buffer_size; | 
 | 250 |     unsigned int boundary; | 
 | 251 |     char error[PCM_ERROR_MAX]; | 
 | 252 |     struct pcm_config config; | 
 | 253 |     struct snd_pcm_mmap_status *mmap_status; | 
 | 254 |     struct snd_pcm_mmap_control *mmap_control; | 
 | 255 |     struct snd_pcm_sync_ptr *sync_ptr; | 
 | 256 |     void *mmap_buffer; | 
 | 257 |     unsigned int noirq_frames_per_msec; | 
 | 258 |     int wait_for_avail_min; | 
 | 259 | }; | 
 | 260 |  | 
 | 261 | unsigned int pcm_get_buffer_size(struct pcm *pcm) | 
 | 262 | { | 
 | 263 |     return pcm->buffer_size; | 
 | 264 | } | 
 | 265 |  | 
 | 266 | const char* pcm_get_error(struct pcm *pcm) | 
 | 267 | { | 
 | 268 |     return pcm->error; | 
 | 269 | } | 
 | 270 |  | 
 | 271 | static int oops(struct pcm *pcm, int e, const char *fmt, ...) | 
 | 272 | { | 
 | 273 |     va_list ap; | 
 | 274 |     int sz; | 
 | 275 |  | 
 | 276 |     va_start(ap, fmt); | 
 | 277 |     vsnprintf(pcm->error, PCM_ERROR_MAX, fmt, ap); | 
 | 278 |     va_end(ap); | 
 | 279 |     sz = strlen(pcm->error); | 
 | 280 |  | 
 | 281 |     if (errno) | 
 | 282 |         snprintf(pcm->error + sz, PCM_ERROR_MAX - sz, | 
 | 283 |                  ": %s", strerror(e)); | 
 | 284 |     return -1; | 
 | 285 | } | 
 | 286 |  | 
 | 287 | static unsigned int pcm_format_to_alsa(enum pcm_format format) | 
 | 288 | { | 
 | 289 |     switch (format) { | 
 | 290 |     case PCM_FORMAT_S32_LE: | 
 | 291 |         return SNDRV_PCM_FORMAT_S32_LE; | 
 | 292 |     case PCM_FORMAT_S8: | 
 | 293 |         return SNDRV_PCM_FORMAT_S8; | 
 | 294 |     case PCM_FORMAT_S24_3LE: | 
 | 295 |         return SNDRV_PCM_FORMAT_S24_3LE; | 
 | 296 |     case PCM_FORMAT_S24_LE: | 
 | 297 |         return SNDRV_PCM_FORMAT_S24_LE; | 
 | 298 |     default: | 
 | 299 |     case PCM_FORMAT_S16_LE: | 
 | 300 |         return SNDRV_PCM_FORMAT_S16_LE; | 
 | 301 |     }; | 
 | 302 | } | 
 | 303 |  | 
 | 304 | unsigned int pcm_format_to_bits(enum pcm_format format) | 
 | 305 | { | 
 | 306 |     switch (format) { | 
 | 307 |     case PCM_FORMAT_S32_LE: | 
 | 308 |     case PCM_FORMAT_S24_LE: | 
 | 309 |         return 32; | 
 | 310 |     case PCM_FORMAT_S24_3LE: | 
 | 311 |         return 24; | 
 | 312 |     default: | 
 | 313 |     case PCM_FORMAT_S16_LE: | 
 | 314 |         return 16; | 
 | 315 |     }; | 
 | 316 | } | 
 | 317 |  | 
 | 318 | unsigned int pcm_bytes_to_frames(struct pcm *pcm, unsigned int bytes) | 
 | 319 | { | 
 | 320 |     return bytes / (pcm->config.channels * | 
 | 321 |         (pcm_format_to_bits(pcm->config.format) >> 3)); | 
 | 322 | } | 
 | 323 |  | 
 | 324 | unsigned int pcm_frames_to_bytes(struct pcm *pcm, unsigned int frames) | 
 | 325 | { | 
 | 326 |     return frames * pcm->config.channels * | 
 | 327 |         (pcm_format_to_bits(pcm->config.format) >> 3); | 
 | 328 | } | 
 | 329 |  | 
 | 330 | static int pcm_sync_ptr(struct pcm *pcm, int flags) { | 
 | 331 |     if (pcm->sync_ptr) { | 
 | 332 |         pcm->sync_ptr->flags = flags; | 
 | 333 |         if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SYNC_PTR, pcm->sync_ptr) < 0) | 
 | 334 |             return -1; | 
 | 335 |     } | 
 | 336 |     return 0; | 
 | 337 | } | 
 | 338 |  | 
 | 339 | static int pcm_hw_mmap_status(struct pcm *pcm) { | 
 | 340 |  | 
 | 341 |     if (pcm->sync_ptr) | 
 | 342 |         return 0; | 
 | 343 |  | 
 | 344 |     int page_size = sysconf(_SC_PAGE_SIZE); | 
 | 345 |     pcm->mmap_status = mmap(NULL, page_size, PROT_READ, MAP_FILE | MAP_SHARED, | 
 | 346 |                             pcm->fd, SNDRV_PCM_MMAP_OFFSET_STATUS); | 
 | 347 |     if (pcm->mmap_status == MAP_FAILED) | 
 | 348 |         pcm->mmap_status = NULL; | 
 | 349 |     if (!pcm->mmap_status) | 
 | 350 |         goto mmap_error; | 
 | 351 |  | 
 | 352 |     pcm->mmap_control = mmap(NULL, page_size, PROT_READ | PROT_WRITE, | 
 | 353 |                              MAP_FILE | MAP_SHARED, pcm->fd, SNDRV_PCM_MMAP_OFFSET_CONTROL); | 
 | 354 |     if (pcm->mmap_control == MAP_FAILED) | 
 | 355 |         pcm->mmap_control = NULL; | 
 | 356 |     if (!pcm->mmap_control) { | 
 | 357 |         munmap(pcm->mmap_status, page_size); | 
 | 358 |         pcm->mmap_status = NULL; | 
 | 359 |         goto mmap_error; | 
 | 360 |     } | 
 | 361 |     if (pcm->flags & PCM_MMAP) | 
 | 362 |         pcm->mmap_control->avail_min = pcm->config.avail_min; | 
 | 363 |     else | 
 | 364 |         pcm->mmap_control->avail_min = 1; | 
 | 365 |  | 
 | 366 |     return 0; | 
 | 367 |  | 
 | 368 | mmap_error: | 
 | 369 |  | 
 | 370 |     pcm->sync_ptr = calloc(1, sizeof(*pcm->sync_ptr)); | 
 | 371 |     if (!pcm->sync_ptr) | 
 | 372 |         return -ENOMEM; | 
 | 373 |     pcm->mmap_status = &pcm->sync_ptr->s.status; | 
 | 374 |     pcm->mmap_control = &pcm->sync_ptr->c.control; | 
 | 375 |     if (pcm->flags & PCM_MMAP) | 
 | 376 |         pcm->mmap_control->avail_min = pcm->config.avail_min; | 
 | 377 |     else | 
 | 378 |         pcm->mmap_control->avail_min = 1; | 
 | 379 |  | 
 | 380 |     pcm_sync_ptr(pcm, 0); | 
 | 381 |  | 
 | 382 |     return 0; | 
 | 383 | } | 
 | 384 |  | 
 | 385 | static void pcm_hw_munmap_status(struct pcm *pcm) { | 
 | 386 |     if (pcm->sync_ptr) { | 
 | 387 |         free(pcm->sync_ptr); | 
 | 388 |         pcm->sync_ptr = NULL; | 
 | 389 |     } else { | 
 | 390 |         int page_size = sysconf(_SC_PAGE_SIZE); | 
 | 391 |         if (pcm->mmap_status) | 
 | 392 |             munmap(pcm->mmap_status, page_size); | 
 | 393 |         if (pcm->mmap_control) | 
 | 394 |             munmap(pcm->mmap_control, page_size); | 
 | 395 |     } | 
 | 396 |     pcm->mmap_status = NULL; | 
 | 397 |     pcm->mmap_control = NULL; | 
 | 398 | } | 
 | 399 |  | 
 | 400 | static int pcm_areas_copy(struct pcm *pcm, unsigned int pcm_offset, | 
 | 401 |                           char *buf, unsigned int src_offset, | 
 | 402 |                           unsigned int frames) | 
 | 403 | { | 
 | 404 |     int size_bytes = pcm_frames_to_bytes(pcm, frames); | 
 | 405 |     int pcm_offset_bytes = pcm_frames_to_bytes(pcm, pcm_offset); | 
 | 406 |     int src_offset_bytes = pcm_frames_to_bytes(pcm, src_offset); | 
 | 407 |  | 
 | 408 |     /* interleaved only atm */ | 
 | 409 |     if (pcm->flags & PCM_IN) | 
 | 410 |         memcpy(buf + src_offset_bytes, | 
 | 411 |                (char*)pcm->mmap_buffer + pcm_offset_bytes, | 
 | 412 |                size_bytes); | 
 | 413 |     else | 
 | 414 |         memcpy((char*)pcm->mmap_buffer + pcm_offset_bytes, | 
 | 415 |                buf + src_offset_bytes, | 
 | 416 |                size_bytes); | 
 | 417 |     return 0; | 
 | 418 | } | 
 | 419 |  | 
 | 420 | static int pcm_mmap_transfer_areas(struct pcm *pcm, char *buf, | 
 | 421 |                                 unsigned int offset, unsigned int size) | 
 | 422 | { | 
 | 423 |     void *pcm_areas; | 
 | 424 |     int commit; | 
 | 425 |     unsigned int pcm_offset, frames, count = 0; | 
 | 426 |  | 
 | 427 |     while (size > 0) { | 
 | 428 |         frames = size; | 
 | 429 |         pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames); | 
 | 430 |         pcm_areas_copy(pcm, pcm_offset, buf, offset, frames); | 
 | 431 |         commit = pcm_mmap_commit(pcm, pcm_offset, frames); | 
 | 432 |         if (commit < 0) { | 
 | 433 |             oops(pcm, commit, "failed to commit %d frames\n", frames); | 
 | 434 |             return commit; | 
 | 435 |         } | 
 | 436 |  | 
 | 437 |         offset += commit; | 
 | 438 |         count += commit; | 
 | 439 |         size -= commit; | 
 | 440 |     } | 
 | 441 |     return count; | 
 | 442 | } | 
 | 443 |  | 
 | 444 | int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail, | 
 | 445 |                        struct timespec *tstamp) | 
 | 446 | { | 
 | 447 |     int frames; | 
 | 448 |     int rc; | 
 | 449 |     snd_pcm_uframes_t hw_ptr; | 
 | 450 |  | 
 | 451 |     if (!pcm_is_ready(pcm)) | 
 | 452 |         return -1; | 
 | 453 |  | 
 | 454 |     rc = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_HWSYNC); | 
 | 455 |     if (rc < 0) | 
 | 456 |         return -1; | 
 | 457 |  | 
 | 458 |     if ((pcm->mmap_status->state != PCM_STATE_RUNNING) && | 
 | 459 |             (pcm->mmap_status->state != PCM_STATE_DRAINING)) | 
 | 460 |         return -1; | 
 | 461 |  | 
 | 462 |     *tstamp = pcm->mmap_status->tstamp; | 
 | 463 |     if (tstamp->tv_sec == 0 && tstamp->tv_nsec == 0) | 
 | 464 |         return -1; | 
 | 465 |  | 
 | 466 |     hw_ptr = pcm->mmap_status->hw_ptr; | 
 | 467 |     if (pcm->flags & PCM_IN) | 
 | 468 |         frames = hw_ptr - pcm->mmap_control->appl_ptr; | 
 | 469 |     else | 
 | 470 |         frames = hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr; | 
 | 471 |  | 
 | 472 |     if (frames < 0) | 
 | 473 |         frames += pcm->boundary; | 
 | 474 |     else if (frames > (int)pcm->boundary) | 
 | 475 |         frames -= pcm->boundary; | 
 | 476 |  | 
 | 477 |     *avail = (unsigned int)frames; | 
 | 478 |  | 
 | 479 |     return 0; | 
 | 480 | } | 
 | 481 |  | 
 | 482 | int pcm_write(struct pcm *pcm, const void *data, unsigned int count) | 
 | 483 | { | 
 | 484 |     struct snd_xferi x; | 
 | 485 |  | 
 | 486 |     if (pcm->flags & PCM_IN) | 
 | 487 |         return -EINVAL; | 
 | 488 |  | 
 | 489 |     x.buf = (void*)data; | 
 | 490 |     x.frames = count / (pcm->config.channels * | 
 | 491 |                         pcm_format_to_bits(pcm->config.format) / 8); | 
 | 492 |  | 
 | 493 |     for (;;) { | 
 | 494 |         if (!pcm->running) { | 
 | 495 |             int prepare_error = pcm_prepare(pcm); | 
 | 496 |             if (prepare_error) | 
 | 497 |                 return prepare_error; | 
 | 498 |             if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) | 
 | 499 |                 return oops(pcm, errno, "cannot write initial data"); | 
 | 500 |             pcm->running = 1; | 
 | 501 |             return 0; | 
 | 502 |         } | 
 | 503 |         if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) { | 
 | 504 |             pcm->prepared = 0; | 
 | 505 |             pcm->running = 0; | 
 | 506 |             if (errno == EPIPE) { | 
 | 507 |                 /* we failed to make our window -- try to restart if we are | 
 | 508 |                  * allowed to do so.  Otherwise, simply allow the EPIPE error to | 
 | 509 |                  * propagate up to the app level */ | 
 | 510 |                 pcm->underruns++; | 
 | 511 |                 if (pcm->flags & PCM_NORESTART) | 
 | 512 |                     return -EPIPE; | 
 | 513 |                 continue; | 
 | 514 |             } | 
 | 515 |             return oops(pcm, errno, "cannot write stream data"); | 
 | 516 |         } | 
 | 517 |         return 0; | 
 | 518 |     } | 
 | 519 | } | 
 | 520 |  | 
 | 521 | int pcm_read(struct pcm *pcm, void *data, unsigned int count) | 
 | 522 | { | 
 | 523 |     struct snd_xferi x; | 
 | 524 |  | 
 | 525 |     if (!(pcm->flags & PCM_IN)) | 
 | 526 |         return -EINVAL; | 
 | 527 |  | 
 | 528 |     x.buf = data; | 
 | 529 |     x.frames = count / (pcm->config.channels * | 
 | 530 |                         pcm_format_to_bits(pcm->config.format) / 8); | 
 | 531 |  | 
 | 532 |     for (;;) { | 
 | 533 |         if (!pcm->running) { | 
 | 534 |             if (pcm_start(pcm) < 0) { | 
 | 535 |                 fprintf(stderr, "start error"); | 
 | 536 |                 return -errno; | 
 | 537 |             } | 
 | 538 |         } | 
 | 539 |         if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) { | 
 | 540 |             pcm->prepared = 0; | 
 | 541 |             pcm->running = 0; | 
 | 542 |             if (errno == EPIPE) { | 
 | 543 |                     /* we failed to make our window -- try to restart */ | 
 | 544 |                 pcm->underruns++; | 
 | 545 |                 continue; | 
 | 546 |             } | 
 | 547 |             return oops(pcm, errno, "cannot read stream data"); | 
 | 548 |         } | 
 | 549 |         return 0; | 
 | 550 |     } | 
 | 551 | } | 
 | 552 |  | 
 | 553 | static struct pcm bad_pcm = { | 
 | 554 |     .fd = -1, | 
 | 555 | }; | 
 | 556 |  | 
 | 557 | struct pcm_params *pcm_params_get(unsigned int card, unsigned int device, | 
 | 558 |                                   unsigned int flags) | 
 | 559 | { | 
 | 560 |     struct snd_pcm_hw_params *params; | 
 | 561 |     char fn[256]; | 
 | 562 |     int fd; | 
 | 563 |  | 
 | 564 |     snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device, | 
 | 565 |              flags & PCM_IN ? 'c' : 'p'); | 
 | 566 |  | 
 | 567 |     fd = open(fn, O_RDWR); | 
 | 568 |     if (fd < 0) { | 
 | 569 |         fprintf(stderr, "cannot open device '%s'\n", fn); | 
 | 570 |         goto err_open; | 
 | 571 |     } | 
 | 572 |  | 
 | 573 |     params = calloc(1, sizeof(struct snd_pcm_hw_params)); | 
 | 574 |     if (!params) | 
 | 575 |         goto err_calloc; | 
 | 576 |  | 
 | 577 |     param_init(params); | 
 | 578 |     if (ioctl(fd, SNDRV_PCM_IOCTL_HW_REFINE, params)) { | 
 | 579 |         fprintf(stderr, "SNDRV_PCM_IOCTL_HW_REFINE error (%d)\n", errno); | 
 | 580 |         goto err_hw_refine; | 
 | 581 |     } | 
 | 582 |  | 
 | 583 |     close(fd); | 
 | 584 |  | 
 | 585 |     return (struct pcm_params *)params; | 
 | 586 |  | 
 | 587 | err_hw_refine: | 
 | 588 |     free(params); | 
 | 589 | err_calloc: | 
 | 590 |     close(fd); | 
 | 591 | err_open: | 
 | 592 |     return NULL; | 
 | 593 | } | 
 | 594 |  | 
 | 595 | void pcm_params_free(struct pcm_params *pcm_params) | 
 | 596 | { | 
 | 597 |     struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params; | 
 | 598 |  | 
 | 599 |     if (params) | 
 | 600 |         free(params); | 
 | 601 | } | 
 | 602 |  | 
 | 603 | static int pcm_param_to_alsa(enum pcm_param param) | 
 | 604 | { | 
 | 605 |     switch (param) { | 
 | 606 |     case PCM_PARAM_ACCESS: | 
 | 607 |         return SNDRV_PCM_HW_PARAM_ACCESS; | 
 | 608 |     case PCM_PARAM_FORMAT: | 
 | 609 |         return SNDRV_PCM_HW_PARAM_FORMAT; | 
 | 610 |     case PCM_PARAM_SUBFORMAT: | 
 | 611 |         return SNDRV_PCM_HW_PARAM_SUBFORMAT; | 
 | 612 |     case PCM_PARAM_SAMPLE_BITS: | 
 | 613 |         return SNDRV_PCM_HW_PARAM_SAMPLE_BITS; | 
 | 614 |         break; | 
 | 615 |     case PCM_PARAM_FRAME_BITS: | 
 | 616 |         return SNDRV_PCM_HW_PARAM_FRAME_BITS; | 
 | 617 |         break; | 
 | 618 |     case PCM_PARAM_CHANNELS: | 
 | 619 |         return SNDRV_PCM_HW_PARAM_CHANNELS; | 
 | 620 |         break; | 
 | 621 |     case PCM_PARAM_RATE: | 
 | 622 |         return SNDRV_PCM_HW_PARAM_RATE; | 
 | 623 |         break; | 
 | 624 |     case PCM_PARAM_PERIOD_TIME: | 
 | 625 |         return SNDRV_PCM_HW_PARAM_PERIOD_TIME; | 
 | 626 |         break; | 
 | 627 |     case PCM_PARAM_PERIOD_SIZE: | 
 | 628 |         return SNDRV_PCM_HW_PARAM_PERIOD_SIZE; | 
 | 629 |         break; | 
 | 630 |     case PCM_PARAM_PERIOD_BYTES: | 
 | 631 |         return SNDRV_PCM_HW_PARAM_PERIOD_BYTES; | 
 | 632 |         break; | 
 | 633 |     case PCM_PARAM_PERIODS: | 
 | 634 |         return SNDRV_PCM_HW_PARAM_PERIODS; | 
 | 635 |         break; | 
 | 636 |     case PCM_PARAM_BUFFER_TIME: | 
 | 637 |         return SNDRV_PCM_HW_PARAM_BUFFER_TIME; | 
 | 638 |         break; | 
 | 639 |     case PCM_PARAM_BUFFER_SIZE: | 
 | 640 |         return SNDRV_PCM_HW_PARAM_BUFFER_SIZE; | 
 | 641 |         break; | 
 | 642 |     case PCM_PARAM_BUFFER_BYTES: | 
 | 643 |         return SNDRV_PCM_HW_PARAM_BUFFER_BYTES; | 
 | 644 |         break; | 
 | 645 |     case PCM_PARAM_TICK_TIME: | 
 | 646 |         return SNDRV_PCM_HW_PARAM_TICK_TIME; | 
 | 647 |         break; | 
 | 648 |  | 
 | 649 |     default: | 
 | 650 |         return -1; | 
 | 651 |     } | 
 | 652 | } | 
 | 653 |  | 
 | 654 | struct pcm_mask *pcm_params_get_mask(struct pcm_params *pcm_params, | 
 | 655 |                                      enum pcm_param param) | 
 | 656 | { | 
 | 657 |     int p; | 
 | 658 |     struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params; | 
 | 659 |     if (params == NULL) { | 
 | 660 |         return NULL; | 
 | 661 |     } | 
 | 662 |  | 
 | 663 |     p = pcm_param_to_alsa(param); | 
 | 664 |     if (p < 0 || !param_is_mask(p)) { | 
 | 665 |         return NULL; | 
 | 666 |     } | 
 | 667 |  | 
 | 668 |     return (struct pcm_mask *)param_to_mask(params, p); | 
 | 669 | } | 
 | 670 |  | 
 | 671 | unsigned int pcm_params_get_min(struct pcm_params *pcm_params, | 
 | 672 |                                 enum pcm_param param) | 
 | 673 | { | 
 | 674 |     struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params; | 
 | 675 |     int p; | 
 | 676 |  | 
 | 677 |     if (!params) | 
 | 678 |         return 0; | 
 | 679 |  | 
 | 680 |     p = pcm_param_to_alsa(param); | 
 | 681 |     if (p < 0) | 
 | 682 |         return 0; | 
 | 683 |  | 
 | 684 |     return param_get_min(params, p); | 
 | 685 | } | 
 | 686 |  | 
 | 687 | void pcm_params_set_min(struct pcm_params *pcm_params, | 
 | 688 |                                 enum pcm_param param, unsigned int val) | 
 | 689 | { | 
 | 690 |     struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params; | 
 | 691 |     int p; | 
 | 692 |  | 
 | 693 |     if (!params) | 
 | 694 |         return; | 
 | 695 |  | 
 | 696 |     p = pcm_param_to_alsa(param); | 
 | 697 |     if (p < 0) | 
 | 698 |         return; | 
 | 699 |  | 
 | 700 |     param_set_min(params, p, val); | 
 | 701 | } | 
 | 702 |  | 
 | 703 | unsigned int pcm_params_get_max(struct pcm_params *pcm_params, | 
 | 704 |                                 enum pcm_param param) | 
 | 705 | { | 
 | 706 |     struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params; | 
 | 707 |     int p; | 
 | 708 |  | 
 | 709 |     if (!params) | 
 | 710 |         return 0; | 
 | 711 |  | 
 | 712 |     p = pcm_param_to_alsa(param); | 
 | 713 |     if (p < 0) | 
 | 714 |         return 0; | 
 | 715 |  | 
 | 716 |     return param_get_max(params, p); | 
 | 717 | } | 
 | 718 |  | 
 | 719 | void pcm_params_set_max(struct pcm_params *pcm_params, | 
 | 720 |                                 enum pcm_param param, unsigned int val) | 
 | 721 | { | 
 | 722 |     struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params; | 
 | 723 |     int p; | 
 | 724 |  | 
 | 725 |     if (!params) | 
 | 726 |         return; | 
 | 727 |  | 
 | 728 |     p = pcm_param_to_alsa(param); | 
 | 729 |     if (p < 0) | 
 | 730 |         return; | 
 | 731 |  | 
 | 732 |     param_set_max(params, p, val); | 
 | 733 | } | 
 | 734 |  | 
 | 735 | static int pcm_mask_test(struct pcm_mask *m, unsigned int index) | 
 | 736 | { | 
 | 737 |     const unsigned int bitshift = 5; /* for 32 bit integer */ | 
 | 738 |     const unsigned int bitmask = (1 << bitshift) - 1; | 
 | 739 |     unsigned int element; | 
 | 740 |  | 
 | 741 |     element = index >> bitshift; | 
 | 742 |     if (element >= ARRAY_SIZE(m->bits)) | 
 | 743 |         return 0; /* for safety, but should never occur */ | 
 | 744 |     return (m->bits[element] >> (index & bitmask)) & 1; | 
 | 745 | } | 
 | 746 |  | 
 | 747 | static int pcm_mask_to_string(struct pcm_mask *m, char *string, unsigned int size, | 
 | 748 |                               char *mask_name, | 
 | 749 |                               const char * const *bit_array_name, size_t bit_array_size) | 
 | 750 | { | 
 | 751 |     unsigned int i; | 
 | 752 |     unsigned int offset = 0; | 
 | 753 |  | 
 | 754 |     if (m == NULL) | 
 | 755 |         return 0; | 
 | 756 |     if (bit_array_size < 32) { | 
 | 757 |         STRLOG(string, offset, size, "%12s:\t%#08x\n", mask_name, m->bits[0]); | 
 | 758 |     } else { /* spans two or more bitfields, print with an array index */ | 
 | 759 |         for (i = 0; i < (bit_array_size + 31) >> 5; ++i) { | 
 | 760 |             STRLOG(string, offset, size, "%9s[%d]:\t%#08x\n", | 
 | 761 |                    mask_name, i, m->bits[i]); | 
 | 762 |         } | 
 | 763 |     } | 
 | 764 |     for (i = 0; i < bit_array_size; ++i) { | 
 | 765 |         if (pcm_mask_test(m, i)) { | 
 | 766 |             STRLOG(string, offset, size, "%12s \t%s\n", "", bit_array_name[i]); | 
 | 767 |         } | 
 | 768 |     } | 
 | 769 |     return offset; | 
 | 770 | } | 
 | 771 |  | 
 | 772 | int pcm_params_to_string(struct pcm_params *params, char *string, unsigned int size) | 
 | 773 | { | 
 | 774 |     struct pcm_mask *m; | 
 | 775 |     unsigned int min, max; | 
 | 776 |     unsigned int clipoffset, offset; | 
 | 777 |  | 
 | 778 |     m = pcm_params_get_mask(params, PCM_PARAM_ACCESS); | 
 | 779 |     offset = pcm_mask_to_string(m, string, size, | 
 | 780 |                                  "Access", access_lookup, ARRAY_SIZE(access_lookup)); | 
 | 781 |     m = pcm_params_get_mask(params, PCM_PARAM_FORMAT); | 
 | 782 |     clipoffset = offset > size ? size : offset; | 
 | 783 |     offset += pcm_mask_to_string(m, string + clipoffset, size - clipoffset, | 
 | 784 |                                  "Format", format_lookup, ARRAY_SIZE(format_lookup)); | 
 | 785 |     m = pcm_params_get_mask(params, PCM_PARAM_SUBFORMAT); | 
 | 786 |     clipoffset = offset > size ? size : offset; | 
 | 787 |     offset += pcm_mask_to_string(m, string + clipoffset, size - clipoffset, | 
 | 788 |                                  "Subformat", subformat_lookup, ARRAY_SIZE(subformat_lookup)); | 
 | 789 |     min = pcm_params_get_min(params, PCM_PARAM_RATE); | 
 | 790 |     max = pcm_params_get_max(params, PCM_PARAM_RATE); | 
 | 791 |     STRLOG(string, offset, size, "        Rate:\tmin=%uHz\tmax=%uHz\n", min, max); | 
 | 792 |     min = pcm_params_get_min(params, PCM_PARAM_CHANNELS); | 
 | 793 |     max = pcm_params_get_max(params, PCM_PARAM_CHANNELS); | 
 | 794 |     STRLOG(string, offset, size, "    Channels:\tmin=%u\t\tmax=%u\n", min, max); | 
 | 795 |     min = pcm_params_get_min(params, PCM_PARAM_SAMPLE_BITS); | 
 | 796 |     max = pcm_params_get_max(params, PCM_PARAM_SAMPLE_BITS); | 
 | 797 |     STRLOG(string, offset, size, " Sample bits:\tmin=%u\t\tmax=%u\n", min, max); | 
 | 798 |     min = pcm_params_get_min(params, PCM_PARAM_PERIOD_SIZE); | 
 | 799 |     max = pcm_params_get_max(params, PCM_PARAM_PERIOD_SIZE); | 
 | 800 |     STRLOG(string, offset, size, " Period size:\tmin=%u\t\tmax=%u\n", min, max); | 
 | 801 |     min = pcm_params_get_min(params, PCM_PARAM_PERIODS); | 
 | 802 |     max = pcm_params_get_max(params, PCM_PARAM_PERIODS); | 
 | 803 |     STRLOG(string, offset, size, "Period count:\tmin=%u\t\tmax=%u\n", min, max); | 
 | 804 |     return offset; | 
 | 805 | } | 
 | 806 |  | 
 | 807 | int pcm_params_format_test(struct pcm_params *params, enum pcm_format format) | 
 | 808 | { | 
 | 809 |     unsigned int alsa_format = pcm_format_to_alsa(format); | 
 | 810 |  | 
 | 811 |     if (alsa_format == SNDRV_PCM_FORMAT_S16_LE && format != PCM_FORMAT_S16_LE) | 
 | 812 |         return 0; /* caution: format not recognized is equivalent to S16_LE */ | 
 | 813 |     return pcm_mask_test(pcm_params_get_mask(params, PCM_PARAM_FORMAT), alsa_format); | 
 | 814 | } | 
 | 815 |  | 
 | 816 | int pcm_close(struct pcm *pcm) | 
 | 817 | { | 
 | 818 |     if (pcm == &bad_pcm) | 
 | 819 |         return 0; | 
 | 820 |  | 
 | 821 |     pcm_hw_munmap_status(pcm); | 
 | 822 |  | 
 | 823 |     if (pcm->flags & PCM_MMAP) { | 
 | 824 |         pcm_stop(pcm); | 
 | 825 |         munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size)); | 
 | 826 |     } | 
 | 827 |  | 
 | 828 |     if (pcm->fd >= 0) | 
 | 829 |         close(pcm->fd); | 
 | 830 |     pcm->prepared = 0; | 
 | 831 |     pcm->running = 0; | 
 | 832 |     pcm->buffer_size = 0; | 
 | 833 |     pcm->fd = -1; | 
 | 834 |     free(pcm); | 
 | 835 |     return 0; | 
 | 836 | } | 
 | 837 |  | 
 | 838 | struct pcm *pcm_open(unsigned int card, unsigned int device, | 
 | 839 |                      unsigned int flags, struct pcm_config *config) | 
 | 840 | { | 
 | 841 |     struct pcm *pcm; | 
 | 842 |     struct snd_pcm_info info; | 
 | 843 |     struct snd_pcm_hw_params params; | 
 | 844 |     struct snd_pcm_sw_params sparams; | 
 | 845 |     char fn[256]; | 
 | 846 |     int rc; | 
 | 847 |  | 
 | 848 |     pcm = calloc(1, sizeof(struct pcm)); | 
 | 849 |     if (!pcm || !config) | 
 | 850 |         return &bad_pcm; /* TODO: could support default config here */ | 
 | 851 |  | 
 | 852 |     pcm->config = *config; | 
 | 853 |  | 
 | 854 |     snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device, | 
 | 855 |              flags & PCM_IN ? 'c' : 'p'); | 
 | 856 |  | 
 | 857 |     pcm->flags = flags; | 
 | 858 |     pcm->fd = open(fn, O_RDWR|O_NONBLOCK); | 
 | 859 |     if (pcm->fd < 0) { | 
 | 860 |         oops(pcm, errno, "cannot open device '%s'", fn); | 
 | 861 |         return pcm; | 
 | 862 |     } | 
 | 863 |  | 
 | 864 |     if (fcntl(pcm->fd, F_SETFL, fcntl(pcm->fd, F_GETFL) & | 
 | 865 |               ~O_NONBLOCK) < 0) { | 
 | 866 |         oops(pcm, errno, "failed to reset blocking mode '%s'", fn); | 
 | 867 |         goto fail_close; | 
 | 868 |     } | 
 | 869 |  | 
 | 870 |     if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) { | 
 | 871 |         oops(pcm, errno, "cannot get info"); | 
 | 872 |         goto fail_close; | 
 | 873 |     } | 
 | 874 |  | 
 | 875 |     param_init(¶ms); | 
 | 876 |     param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT, | 
 | 877 |                    pcm_format_to_alsa(config->format)); | 
 | 878 |     param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_SUBFORMAT, | 
 | 879 |                    SNDRV_PCM_SUBFORMAT_STD); | 
 | 880 |     param_set_min(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size); | 
 | 881 |     param_set_int(¶ms, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, | 
 | 882 |                   pcm_format_to_bits(config->format)); | 
 | 883 |     param_set_int(¶ms, SNDRV_PCM_HW_PARAM_FRAME_BITS, | 
 | 884 |                   pcm_format_to_bits(config->format) * config->channels); | 
 | 885 |     param_set_int(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS, | 
 | 886 |                   config->channels); | 
 | 887 |     param_set_int(¶ms, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count); | 
 | 888 |     param_set_int(¶ms, SNDRV_PCM_HW_PARAM_RATE, config->rate); | 
 | 889 |  | 
 | 890 |     if (flags & PCM_NOIRQ) { | 
 | 891 |         if (!(flags & PCM_MMAP)) { | 
 | 892 |             oops(pcm, -EINVAL, "noirq only currently supported with mmap()."); | 
 | 893 |             goto fail_close; | 
 | 894 |         } | 
 | 895 |  | 
 | 896 |         params.flags |= SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP; | 
 | 897 |         pcm->noirq_frames_per_msec = config->rate / 1000; | 
 | 898 |     } | 
 | 899 |  | 
 | 900 |     if (flags & PCM_MMAP) | 
 | 901 |         param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS, | 
 | 902 |                        SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); | 
 | 903 |     else | 
 | 904 |         param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS, | 
 | 905 |                        SNDRV_PCM_ACCESS_RW_INTERLEAVED); | 
 | 906 |  | 
 | 907 |     if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, ¶ms)) { | 
 | 908 |         oops(pcm, errno, "cannot set hw params"); | 
 | 909 |         goto fail_close; | 
 | 910 |     } | 
 | 911 |  | 
 | 912 |     /* get our refined hw_params */ | 
 | 913 |     config->period_size = param_get_int(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); | 
 | 914 |     config->period_count = param_get_int(¶ms, SNDRV_PCM_HW_PARAM_PERIODS); | 
 | 915 |     pcm->buffer_size = config->period_count * config->period_size; | 
 | 916 |  | 
 | 917 |     if (flags & PCM_MMAP) { | 
 | 918 |         pcm->mmap_buffer = mmap(NULL, pcm_frames_to_bytes(pcm, pcm->buffer_size), | 
 | 919 |                                 PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, pcm->fd, 0); | 
 | 920 |         if (pcm->mmap_buffer == MAP_FAILED) { | 
 | 921 |             oops(pcm, -errno, "failed to mmap buffer %d bytes\n", | 
 | 922 |                  pcm_frames_to_bytes(pcm, pcm->buffer_size)); | 
 | 923 |             goto fail_close; | 
 | 924 |         } | 
 | 925 |     } | 
 | 926 |  | 
 | 927 |     memset(&sparams, 0, sizeof(sparams)); | 
 | 928 |     sparams.tstamp_mode = SNDRV_PCM_TSTAMP_ENABLE; | 
 | 929 |     sparams.period_step = 1; | 
 | 930 |  | 
 | 931 |     if (!config->start_threshold) { | 
 | 932 |         if (pcm->flags & PCM_IN) | 
 | 933 |             pcm->config.start_threshold = sparams.start_threshold = 1; | 
 | 934 |         else | 
 | 935 |             pcm->config.start_threshold = sparams.start_threshold = | 
 | 936 |                 config->period_count * config->period_size / 2; | 
 | 937 |     } else | 
 | 938 |         sparams.start_threshold = config->start_threshold; | 
 | 939 |  | 
 | 940 |     /* pick a high stop threshold - todo: does this need further tuning */ | 
 | 941 |     if (!config->stop_threshold) { | 
 | 942 |         if (pcm->flags & PCM_IN) | 
 | 943 |             pcm->config.stop_threshold = sparams.stop_threshold = | 
 | 944 |                 config->period_count * config->period_size * 10; | 
 | 945 |         else | 
 | 946 |             pcm->config.stop_threshold = sparams.stop_threshold = | 
 | 947 |                 config->period_count * config->period_size; | 
 | 948 |     } | 
 | 949 |     else | 
 | 950 |         sparams.stop_threshold = config->stop_threshold; | 
 | 951 |  | 
 | 952 |     if (!pcm->config.avail_min) { | 
 | 953 |         if (pcm->flags & PCM_MMAP) | 
 | 954 |             pcm->config.avail_min = sparams.avail_min = pcm->config.period_size; | 
 | 955 |         else | 
 | 956 |             pcm->config.avail_min = sparams.avail_min = 1; | 
 | 957 |     } else | 
 | 958 |         sparams.avail_min = config->avail_min; | 
 | 959 |  | 
 | 960 |     sparams.xfer_align = config->period_size / 2; /* needed for old kernels */ | 
 | 961 |     sparams.silence_threshold = config->silence_threshold; | 
 | 962 |     sparams.silence_size = config->silence_size; | 
 | 963 |     pcm->boundary = sparams.boundary = pcm->buffer_size; | 
 | 964 |  | 
 | 965 |     while (pcm->boundary * 2 <= INT_MAX - pcm->buffer_size) | 
 | 966 |         pcm->boundary *= 2; | 
 | 967 |  | 
 | 968 |     if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) { | 
 | 969 |         oops(pcm, errno, "cannot set sw params"); | 
 | 970 |         goto fail; | 
 | 971 |     } | 
 | 972 |  | 
 | 973 |     rc = pcm_hw_mmap_status(pcm); | 
 | 974 |     if (rc < 0) { | 
 | 975 |         oops(pcm, rc, "mmap status failed"); | 
 | 976 |         goto fail; | 
 | 977 |     } | 
 | 978 |  | 
 | 979 | #ifdef SNDRV_PCM_IOCTL_TTSTAMP | 
 | 980 |     if (pcm->flags & PCM_MONOTONIC) { | 
 | 981 |         int arg = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC; | 
 | 982 |         rc = ioctl(pcm->fd, SNDRV_PCM_IOCTL_TTSTAMP, &arg); | 
 | 983 |         if (rc < 0) { | 
 | 984 |             oops(pcm, rc, "cannot set timestamp type"); | 
 | 985 |             goto fail; | 
 | 986 |         } | 
 | 987 |     } | 
 | 988 | #endif | 
 | 989 |  | 
 | 990 |     pcm->underruns = 0; | 
 | 991 |     return pcm; | 
 | 992 |  | 
 | 993 | fail: | 
 | 994 |     if (flags & PCM_MMAP) | 
 | 995 |         munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size)); | 
 | 996 | fail_close: | 
 | 997 |     close(pcm->fd); | 
 | 998 |     pcm->fd = -1; | 
 | 999 |     return pcm; | 
 | 1000 | } | 
 | 1001 |  | 
 | 1002 | int pcm_is_ready(struct pcm *pcm) | 
 | 1003 | { | 
 | 1004 |     return pcm->fd >= 0; | 
 | 1005 | } | 
 | 1006 |  | 
 | 1007 | int pcm_prepare(struct pcm *pcm) | 
 | 1008 | { | 
 | 1009 |     if (pcm->prepared) | 
 | 1010 |         return 0; | 
 | 1011 |  | 
 | 1012 |     if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE) < 0) | 
 | 1013 |         return oops(pcm, errno, "cannot prepare channel"); | 
 | 1014 |  | 
 | 1015 |     pcm->prepared = 1; | 
 | 1016 |     return 0; | 
 | 1017 | } | 
 | 1018 |  | 
 | 1019 | int pcm_start(struct pcm *pcm) | 
 | 1020 | { | 
 | 1021 |     int prepare_error = pcm_prepare(pcm); | 
 | 1022 |     if (prepare_error) | 
 | 1023 |         return prepare_error; | 
 | 1024 |  | 
 | 1025 |     if (pcm->flags & PCM_MMAP) | 
 | 1026 | 	    pcm_sync_ptr(pcm, 0); | 
 | 1027 |  | 
 | 1028 |     if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START) < 0) | 
 | 1029 |         return oops(pcm, errno, "cannot start channel"); | 
 | 1030 |  | 
 | 1031 |     pcm->running = 1; | 
 | 1032 |     return 0; | 
 | 1033 | } | 
 | 1034 |  | 
 | 1035 | int pcm_stop(struct pcm *pcm) | 
 | 1036 | { | 
 | 1037 |     if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DROP) < 0) | 
 | 1038 |         return oops(pcm, errno, "cannot stop channel"); | 
 | 1039 |  | 
 | 1040 |     pcm->prepared = 0; | 
 | 1041 |     pcm->running = 0; | 
 | 1042 |     return 0; | 
 | 1043 | } | 
 | 1044 |  | 
 | 1045 | static inline int pcm_mmap_playback_avail(struct pcm *pcm) | 
 | 1046 | { | 
 | 1047 |     int avail; | 
 | 1048 |  | 
 | 1049 |     avail = pcm->mmap_status->hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr; | 
 | 1050 |  | 
 | 1051 |     if (avail < 0) | 
 | 1052 |         avail += pcm->boundary; | 
 | 1053 |     else if (avail > (int)pcm->boundary) | 
 | 1054 |         avail -= pcm->boundary; | 
 | 1055 |  | 
 | 1056 |     return avail; | 
 | 1057 | } | 
 | 1058 |  | 
 | 1059 | static inline int pcm_mmap_capture_avail(struct pcm *pcm) | 
 | 1060 | { | 
 | 1061 |     int avail = pcm->mmap_status->hw_ptr - pcm->mmap_control->appl_ptr; | 
 | 1062 |     if (avail < 0) | 
 | 1063 |         avail += pcm->boundary; | 
 | 1064 |     return avail; | 
 | 1065 | } | 
 | 1066 |  | 
 | 1067 | int pcm_mmap_avail(struct pcm *pcm) | 
 | 1068 | { | 
 | 1069 |     pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC); | 
 | 1070 |     if (pcm->flags & PCM_IN) | 
 | 1071 |         return pcm_mmap_capture_avail(pcm); | 
 | 1072 |     else | 
 | 1073 |         return pcm_mmap_playback_avail(pcm); | 
 | 1074 | } | 
 | 1075 |  | 
 | 1076 | static void pcm_mmap_appl_forward(struct pcm *pcm, int frames) | 
 | 1077 | { | 
 | 1078 |     unsigned int appl_ptr = pcm->mmap_control->appl_ptr; | 
 | 1079 |     appl_ptr += frames; | 
 | 1080 |  | 
 | 1081 |     /* check for boundary wrap */ | 
 | 1082 |     if (appl_ptr > pcm->boundary) | 
 | 1083 |          appl_ptr -= pcm->boundary; | 
 | 1084 |     pcm->mmap_control->appl_ptr = appl_ptr; | 
 | 1085 | } | 
 | 1086 |  | 
 | 1087 | int pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset, | 
 | 1088 |                    unsigned int *frames) | 
 | 1089 | { | 
 | 1090 |     unsigned int continuous, copy_frames, avail; | 
 | 1091 |  | 
 | 1092 |     /* return the mmap buffer */ | 
 | 1093 |     *areas = pcm->mmap_buffer; | 
 | 1094 |  | 
 | 1095 |     /* and the application offset in frames */ | 
 | 1096 |     *offset = pcm->mmap_control->appl_ptr % pcm->buffer_size; | 
 | 1097 |  | 
 | 1098 |     avail = pcm_mmap_avail(pcm); | 
 | 1099 |     if (avail > pcm->buffer_size) | 
 | 1100 |         avail = pcm->buffer_size; | 
 | 1101 |     continuous = pcm->buffer_size - *offset; | 
 | 1102 |  | 
 | 1103 |     /* we can only copy frames if the are availabale and continuos */ | 
 | 1104 |     copy_frames = *frames; | 
 | 1105 |     if (copy_frames > avail) | 
 | 1106 |         copy_frames = avail; | 
 | 1107 |     if (copy_frames > continuous) | 
 | 1108 |         copy_frames = continuous; | 
 | 1109 |     *frames = copy_frames; | 
 | 1110 |  | 
 | 1111 |     return 0; | 
 | 1112 | } | 
 | 1113 |  | 
 | 1114 | int pcm_mmap_commit(struct pcm *pcm, unsigned int offset __attribute__((unused)), unsigned int frames) | 
 | 1115 | { | 
 | 1116 |     /* update the application pointer in userspace and kernel */ | 
 | 1117 |     pcm_mmap_appl_forward(pcm, frames); | 
 | 1118 |     pcm_sync_ptr(pcm, 0); | 
 | 1119 |  | 
 | 1120 |     return frames; | 
 | 1121 | } | 
 | 1122 |  | 
 | 1123 | int pcm_avail_update(struct pcm *pcm) | 
 | 1124 | { | 
 | 1125 |     pcm_sync_ptr(pcm, 0); | 
 | 1126 |     return pcm_mmap_avail(pcm); | 
 | 1127 | } | 
 | 1128 |  | 
 | 1129 | int pcm_state(struct pcm *pcm) | 
 | 1130 | { | 
 | 1131 |     int err = pcm_sync_ptr(pcm, 0); | 
 | 1132 |     if (err < 0) | 
 | 1133 |         return err; | 
 | 1134 |  | 
 | 1135 |     return pcm->mmap_status->state; | 
 | 1136 | } | 
 | 1137 |  | 
 | 1138 | int pcm_set_avail_min(struct pcm *pcm, int avail_min) | 
 | 1139 | { | 
 | 1140 |     if ((~pcm->flags) & (PCM_MMAP | PCM_NOIRQ)) | 
 | 1141 |         return -ENOSYS; | 
 | 1142 |  | 
 | 1143 |     pcm->config.avail_min = avail_min; | 
 | 1144 |     return 0; | 
 | 1145 | } | 
 | 1146 |  | 
 | 1147 | int pcm_wait(struct pcm *pcm, int timeout) | 
 | 1148 | { | 
 | 1149 |     struct pollfd pfd; | 
 | 1150 |     int err; | 
 | 1151 |  | 
 | 1152 |     pfd.fd = pcm->fd; | 
 | 1153 |     pfd.events = POLLOUT | POLLERR | POLLNVAL; | 
 | 1154 |  | 
 | 1155 |     do { | 
 | 1156 |         /* let's wait for avail or timeout */ | 
 | 1157 |         err = poll(&pfd, 1, timeout); | 
 | 1158 |         if (err < 0) | 
 | 1159 |             return -errno; | 
 | 1160 |  | 
 | 1161 |         /* timeout ? */ | 
 | 1162 |         if (err == 0) | 
 | 1163 |             return 0; | 
 | 1164 |  | 
 | 1165 |         /* have we been interrupted ? */ | 
 | 1166 |         if (errno == -EINTR) | 
 | 1167 |             continue; | 
 | 1168 |  | 
 | 1169 |         /* check for any errors */ | 
 | 1170 |         if (pfd.revents & (POLLERR | POLLNVAL)) { | 
 | 1171 |             switch (pcm_state(pcm)) { | 
 | 1172 |             case PCM_STATE_XRUN: | 
 | 1173 |                 return -EPIPE; | 
 | 1174 |             case PCM_STATE_SUSPENDED: | 
 | 1175 |                 return -ESTRPIPE; | 
 | 1176 |             case PCM_STATE_DISCONNECTED: | 
 | 1177 |                 return -ENODEV; | 
 | 1178 |             default: | 
 | 1179 |                 return -EIO; | 
 | 1180 |             } | 
 | 1181 |         } | 
 | 1182 |     /* poll again if fd not ready for IO */ | 
 | 1183 |     } while (!(pfd.revents & (POLLIN | POLLOUT))); | 
 | 1184 |  | 
 | 1185 |     return 1; | 
 | 1186 | } | 
 | 1187 |  | 
 | 1188 | int pcm_get_poll_fd(struct pcm *pcm) | 
 | 1189 | { | 
 | 1190 |     return pcm->fd; | 
 | 1191 | } | 
 | 1192 |  | 
 | 1193 | int pcm_mmap_transfer(struct pcm *pcm, const void *buffer, unsigned int bytes) | 
 | 1194 | { | 
 | 1195 |     int err = 0, frames, avail; | 
 | 1196 |     unsigned int offset = 0, count; | 
 | 1197 |  | 
 | 1198 |     if (bytes == 0) | 
 | 1199 |         return 0; | 
 | 1200 |  | 
 | 1201 |     count = pcm_bytes_to_frames(pcm, bytes); | 
 | 1202 |  | 
 | 1203 |     while (count > 0) { | 
 | 1204 |  | 
 | 1205 |         /* get the available space for writing new frames */ | 
 | 1206 |         avail = pcm_avail_update(pcm); | 
 | 1207 |         if (avail < 0) { | 
 | 1208 |             fprintf(stderr, "cannot determine available mmap frames"); | 
 | 1209 |             return err; | 
 | 1210 |         } | 
 | 1211 |  | 
 | 1212 |         /* start the audio if we reach the threshold */ | 
 | 1213 | 	    if (!pcm->running && | 
 | 1214 |             (pcm->buffer_size - avail) >= pcm->config.start_threshold) { | 
 | 1215 |             if (pcm_start(pcm) < 0) { | 
 | 1216 |                fprintf(stderr, "start error: hw 0x%x app 0x%x avail 0x%x\n", | 
 | 1217 |                     (unsigned int)pcm->mmap_status->hw_ptr, | 
 | 1218 |                     (unsigned int)pcm->mmap_control->appl_ptr, | 
 | 1219 |                     avail); | 
 | 1220 |                 return -errno; | 
 | 1221 |             } | 
 | 1222 |             pcm->wait_for_avail_min = 0; | 
 | 1223 |         } | 
 | 1224 |  | 
 | 1225 |         /* sleep until we have space to write new frames */ | 
 | 1226 |         if (pcm->running) { | 
 | 1227 |             /* enable waiting for avail_min threshold when less frames than we have to write | 
 | 1228 |              * are available. */ | 
 | 1229 |             if (!pcm->wait_for_avail_min && (count > (unsigned int)avail)) | 
 | 1230 |                 pcm->wait_for_avail_min = 1; | 
 | 1231 |  | 
 | 1232 |             if (pcm->wait_for_avail_min && (avail < pcm->config.avail_min)) { | 
 | 1233 |                 int time = -1; | 
 | 1234 |  | 
 | 1235 |                 /* disable waiting for avail_min threshold to allow small amounts of data to be | 
 | 1236 |                  * written without waiting as long as there is enough room in buffer. */ | 
 | 1237 |                 pcm->wait_for_avail_min = 0; | 
 | 1238 |  | 
 | 1239 |                 if (pcm->flags & PCM_NOIRQ) | 
 | 1240 |                     time = (pcm->config.avail_min - avail) / pcm->noirq_frames_per_msec; | 
 | 1241 |  | 
 | 1242 |                 err = pcm_wait(pcm, time); | 
 | 1243 |                 if (err < 0) { | 
 | 1244 |                     pcm->prepared = 0; | 
 | 1245 |                     pcm->running = 0; | 
 | 1246 |                     oops(pcm, err, "wait error: hw 0x%x app 0x%x avail 0x%x\n", | 
 | 1247 |                         (unsigned int)pcm->mmap_status->hw_ptr, | 
 | 1248 |                         (unsigned int)pcm->mmap_control->appl_ptr, | 
 | 1249 |                         avail); | 
 | 1250 |                     pcm->mmap_control->appl_ptr = 0; | 
 | 1251 |                     return err; | 
 | 1252 |                 } | 
 | 1253 |                 continue; | 
 | 1254 |             } | 
 | 1255 |         } | 
 | 1256 |  | 
 | 1257 |         frames = count; | 
 | 1258 |         if (frames > avail) | 
 | 1259 |             frames = avail; | 
 | 1260 |  | 
 | 1261 |         if (!frames) | 
 | 1262 |             break; | 
 | 1263 |  | 
 | 1264 |         /* copy frames from buffer */ | 
 | 1265 |         frames = pcm_mmap_transfer_areas(pcm, (void *)buffer, offset, frames); | 
 | 1266 |         if (frames < 0) { | 
 | 1267 |             fprintf(stderr, "write error: hw 0x%x app 0x%x avail 0x%x\n", | 
 | 1268 |                     (unsigned int)pcm->mmap_status->hw_ptr, | 
 | 1269 |                     (unsigned int)pcm->mmap_control->appl_ptr, | 
 | 1270 |                     avail); | 
 | 1271 |             return frames; | 
 | 1272 |         } | 
 | 1273 |  | 
 | 1274 |         offset += frames; | 
 | 1275 |         count -= frames; | 
 | 1276 |     } | 
 | 1277 |  | 
 | 1278 |     return 0; | 
 | 1279 | } | 
 | 1280 |  | 
 | 1281 | int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count) | 
 | 1282 | { | 
 | 1283 |     if ((~pcm->flags) & (PCM_OUT | PCM_MMAP)) | 
 | 1284 |         return -ENOSYS; | 
 | 1285 |  | 
 | 1286 |     return pcm_mmap_transfer(pcm, (void *)data, count); | 
 | 1287 | } | 
 | 1288 |  | 
 | 1289 | int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count) | 
 | 1290 | { | 
 | 1291 |     if ((~pcm->flags) & (PCM_IN | PCM_MMAP)) | 
 | 1292 |         return -ENOSYS; | 
 | 1293 |  | 
 | 1294 |     return pcm_mmap_transfer(pcm, data, count); | 
 | 1295 | } | 
 | 1296 |  | 
 | 1297 | int pcm_ioctl(struct pcm *pcm, int request, ...) | 
 | 1298 | { | 
 | 1299 |     va_list ap; | 
 | 1300 |     void * arg; | 
 | 1301 |  | 
 | 1302 |     if (!pcm_is_ready(pcm)) | 
 | 1303 |         return -1; | 
 | 1304 |  | 
 | 1305 |     va_start(ap, request); | 
 | 1306 |     arg = va_arg(ap, void *); | 
 | 1307 |     va_end(ap); | 
 | 1308 |  | 
 | 1309 |     return ioctl(pcm->fd, request, arg); | 
 | 1310 | } |