blob: 4f55437646045236b93b9d7b00f9f85fb18637b9 [file] [log] [blame]
Iliyan Malchev4765c432012-06-11 14:36:16 -07001/*
2** Copyright 2010, The Android Open-Source Project
3** Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
Ajay Dudani9746c472012-06-18 16:01:16 -070018#define LOG_TAG "alsa_pcm"
19#define LOG_NDEBUG 1
Iliyan Malchev4765c432012-06-11 14:36:16 -070020#ifdef ANDROID
21/* definitions for Android logging */
22#include <utils/Log.h>
23#include <cutils/properties.h>
24#else /* ANDROID */
25#define strlcat g_strlcat
26#define strlcpy g_strlcpy
27#define ALOGI(...) fprintf(stdout, __VA_ARGS__)
28#define ALOGE(...) fprintf(stderr, __VA_ARGS__)
29#define ALOGV(...) fprintf(stderr, __VA_ARGS__)
30#endif /* ANDROID */
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <fcntl.h>
35#include <stdarg.h>
36#include <string.h>
37#include <errno.h>
38#include <unistd.h>
39#include <stdint.h>
40#include <sys/ioctl.h>
41#include <sys/mman.h>
42#include <sys/time.h>
43#include <sys/poll.h>
44#include <linux/ioctl.h>
45#include <linux/types.h>
Mingming Yinb0f0fce2012-11-29 20:04:36 -080046#ifdef QCOM_COMPRESSED_AUDIO_ENABLED
47#include <sound/compress_params.h>
48#endif
Iliyan Malchev4765c432012-06-11 14:36:16 -070049#include "alsa_audio.h"
50
51#define __force
52#define __bitwise
53#define __user
54
55#define DEBUG 1
56
57enum format_alias {
58 S8 = 0,
59 U8,
60 S16_LE,
61 S16_BE,
62 U16_LE,
63 U16_BE,
64 S24_LE,
65 S24_BE,
66 U24_LE,
67 U24_BE,
68 S32_LE,
69 S32_BE,
70 U32_LE,
71 U32_BE,
72 FLOAT_LE,
73 FLOAT_BE,
74 FLOAT64_LE,
75 FLOAT64_BE,
76 IEC958_SUBFRAME_LE,
77 IEC958_SUBFRAME_BE,
78 MU_LAW,
79 A_LAW,
80 IMA_ADPCM,
81 MPEG,
82 GSM,
Mingming Yinb0f0fce2012-11-29 20:04:36 -080083 SPECIAL = 31,
Iliyan Malchev4765c432012-06-11 14:36:16 -070084 S24_3LE,
85 S24_3BE,
86 U24_3LE,
87 U24_3BE,
88 S20_3LE,
89 S20_3BE,
90 U20_3LE,
91 U20_3BE,
92 S18_3LE,
93 S18_3BE,
94 U18_3LE,
95 U18_3BE,
96 FORMAT_LAST,
97};
98const char *formats_list[][2] = {
99 {"S8", "Signed 8 bit"},
100 {"U8", "Unsigned 8 bit"},
101 {"S16_LE", "Signed 16 bit Little Endian"},
102 {"S16_BE", "Signed 16 bit Big Endian"},
103 {"U16_LE", "Unsigned 16 bit Little Endian"},
104 {"U16_BE", "Unsigned 16 bit Big Endian"},
105 {"S24_LE", "Signed 24 bit Little Endian"},
106 {"S24_BE", "Signed 24 bit Big Endian"},
107 {"U24_LE", "Unsigned 24 bit Little Endian"},
108 {"U24_BE", "Unsigned 24 bit Big Endian"},
109 {"S32_LE", "Signed 32 bit Little Endian"},
110 {"S32_BE", "Signed 32 bit Big Endian"},
111 {"U32_LE", "Unsigned 32 bit Little Endian"},
112 {"U32_BE", "Unsigned 32 bit Big Endian"},
113 {"FLOAT_LE", "Float 32 bit Little Endian"},
114 {"FLOAT_BE", "Float 32 bit Big Endian"},
115 {"FLOAT64_LE", "Float 64 bit Little Endian"},
116 {"FLOAT64_BE", "Float 64 bit Big Endian"},
117 {"IEC958_SUBFRAME_LE", "IEC-958 Little Endian"},
118 {"IEC958_SUBFRAME_BE", "IEC-958 Big Endian"},
119 {"MU_LAW", "Mu-Law"},
120 {"A_LAW", "A-Law"},
121 {"IMA_ADPCM", "Ima-ADPCM"},
122 {"MPEG", "MPEG"},
Mingming Yinb0f0fce2012-11-29 20:04:36 -0800123 {"GSM", "GSM"},
Iliyan Malchev4765c432012-06-11 14:36:16 -0700124 [31] = {"SPECIAL", "Special"},
125 {"S24_3LE", "Signed 24 bit Little Endian in 3bytes"},
126 {"S24_3BE", "Signed 24 bit Big Endian in 3bytes"},
127 {"U24_3LE", "Unsigned 24 bit Little Endian in 3bytes"},
128 {"U24_3BE", "Unsigned 24 bit Big Endian in 3bytes"},
129 {"S20_3LE", "Signed 20 bit Little Endian in 3bytes"},
130 {"S20_3BE", "Signed 20 bit Big Endian in 3bytes"},
131 {"U20_3LE", "Unsigned 20 bit Little Endian in 3bytes"},
132 {"U20_3BE", "Unsigned 20 bit Big Endian in 3bytes"},
133 {"S18_3LE", "Signed 18 bit Little Endian in 3bytes"},
134 {"S18_3BE", "Signed 18 bit Big Endian in 3bytes"},
135 {"U18_3LE", "Unsigned 18 bit Little Endian in 3bytes"},
136 {"U18_3BE", "Unsigned 18 bit Big Endian in 3bytes"},
137};
Mingming Yinb0f0fce2012-11-29 20:04:36 -0800138enum decoder_alias {
139 FORMAT_MP3 = SND_AUDIOCODEC_MP3,
140 FORMAT_AAC = SND_AUDIOCODEC_AAC,
141 FORMAT_AC3_PASS_THROUGH = SND_AUDIOCODEC_AC3_PASS_THROUGH,
142 FORMAT_WMA = SND_AUDIOCODEC_WMA,
143 FORMAT_WMA_PRO = SND_AUDIOCODEC_WMA_PRO,
144 FORMAT_DTS = SND_AUDIOCODEC_DTS,
145 FORMAT_DTS_LBR = SND_AUDIOCODEC_DTS_LBR,
146 FORMAT_DTS_PASS_THROUGH = SND_AUDIOCODEC_DTS_PASS_THROUGH,
147 FORMAT_AMRWB = SND_AUDIOCODEC_AMRWB,
148 FORMAT_AMRWB_PLUS = SND_AUDIOCODEC_AMRWBPLUS
149};
Iliyan Malchev4765c432012-06-11 14:36:16 -0700150
151int get_compressed_format(const char *format)
152{
153 const char *ch = format;
154 if (strcmp(ch, "MP3") == 0) {
155 printf("MP3 is selected\n");
156 return FORMAT_MP3;
157 } else if (strcmp(ch, "AC3_PASS_THROUGH") == 0) {
158 printf("AC3 PASS THROUGH is selected\n");
159 return FORMAT_AC3_PASS_THROUGH;
Mingming Yinb0f0fce2012-11-29 20:04:36 -0800160 } else if (strcmp(ch, "AAC") == 0) {
161 printf("AAC is selected\n");
162 return FORMAT_AAC;
163 } else if (strcmp(ch, "AC3_PASS_THROUGH") == 0) {
164 printf("AC3_PASS_THROUGH is selected\n");
165 return FORMAT_AC3_PASS_THROUGH;
166 } else if (strcmp(ch, "WMA") == 0) {
167 printf("WMA is selected\n");
168 return FORMAT_WMA;
169 }else if (strcmp(ch, "WMA_PRO") == 0) {
170 printf("WMA_PRO is selected\n");
171 return FORMAT_WMA_PRO;
172 }else if (strcmp(ch, "DTS") == 0) {
173 printf("DTS is selected\n");
174 return FORMAT_DTS;
175 } else if (strcmp(ch, "DTS_LBR") == 0) {
176 printf("DTS_LBR is selected\n");
177 return FORMAT_DTS_LBR;
178 } else if (strcmp(ch, "AMR_WB") == 0) {
179 printf("AMR_WB is selected\n");
180 return FORMAT_AMRWB;
181 }else if (strcmp(ch, "AMR_WB_PLUS") == 0) {
182 printf("FORMAT_AMRWB_PLUS is selected\n");
183 return FORMAT_AMRWB_PLUS;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700184 } else {
185 printf("invalid format\n");
186 return -1;
187 }
188 return 0;
189}
190
191int get_format(const char* name)
192{
193 int format;
194 for (format = 0; format < FORMAT_LAST; format++) {
195 if (formats_list[format][0] &&
196 strcasecmp(name, formats_list[format][0]) == 0) {
197 ALOGV("format_names %s", name);
198 return format;
199 }
200 }
201 return -EINVAL;
202}
203
204const char *get_format_name(int format)
205{
206 if ((format < FORMAT_LAST) &&
207 formats_list[format][0])
208 return formats_list[format][0];
209 return NULL;
210}
211
212const char *get_format_desc(int format)
213{
214 if ((format < FORMAT_LAST) &&
215 formats_list[format][1])
216 return formats_list[format][1];
217 return NULL;
218}
219
220/* alsa parameter manipulation cruft */
221
222#define PARAM_MAX SNDRV_PCM_HW_PARAM_LAST_INTERVAL
223static int oops(struct pcm *pcm, int e, const char *fmt, ...);
224
225static inline int param_is_mask(int p)
226{
227 return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
228 (p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
229}
230
231static inline int param_is_interval(int p)
232{
233 return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) &&
234 (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL);
235}
236
237static inline struct snd_interval *param_to_interval(struct snd_pcm_hw_params *p, int n)
238{
239 return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]);
240}
241
242static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n)
243{
244 return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
245}
246
247void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned bit)
248{
249 if (bit >= SNDRV_MASK_MAX)
250 return;
251 if (param_is_mask(n)) {
252 struct snd_mask *m = param_to_mask(p, n);
253 m->bits[0] = 0;
254 m->bits[1] = 0;
255 m->bits[bit >> 5] |= (1 << (bit & 31));
256 }
257}
258
259void param_set_min(struct snd_pcm_hw_params *p, int n, unsigned val)
260{
261 if (param_is_interval(n)) {
262 struct snd_interval *i = param_to_interval(p, n);
263 i->min = val;
264 }
265}
266
267void param_set_max(struct snd_pcm_hw_params *p, int n, unsigned val)
268{
269 if (param_is_interval(n)) {
270 struct snd_interval *i = param_to_interval(p, n);
271 i->max = val;
272 }
273}
274
275void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned val)
276{
277 if (param_is_interval(n)) {
278 struct snd_interval *i = param_to_interval(p, n);
279 i->min = val;
280 i->max = val;
281 i->integer = 1;
282 }
283}
284
285void param_init(struct snd_pcm_hw_params *p)
286{
287 int n;
288 memset(p, 0, sizeof(*p));
289 for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK;
290 n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) {
291 struct snd_mask *m = param_to_mask(p, n);
292 m->bits[0] = ~0;
293 m->bits[1] = ~0;
294 }
295 for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL;
296 n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) {
297 struct snd_interval *i = param_to_interval(p, n);
298 i->min = 0;
299 i->max = ~0;
300 }
301}
302
303/* debugging gunk */
304
305#if DEBUG
306static const char *param_name[PARAM_MAX+1] = {
307 [SNDRV_PCM_HW_PARAM_ACCESS] = "access",
308 [SNDRV_PCM_HW_PARAM_FORMAT] = "format",
309 [SNDRV_PCM_HW_PARAM_SUBFORMAT] = "subformat",
310
311 [SNDRV_PCM_HW_PARAM_SAMPLE_BITS] = "sample_bits",
312 [SNDRV_PCM_HW_PARAM_FRAME_BITS] = "frame_bits",
313 [SNDRV_PCM_HW_PARAM_CHANNELS] = "channels",
314 [SNDRV_PCM_HW_PARAM_RATE] = "rate",
315 [SNDRV_PCM_HW_PARAM_PERIOD_TIME] = "period_time",
316 [SNDRV_PCM_HW_PARAM_PERIOD_SIZE] = "period_size",
317 [SNDRV_PCM_HW_PARAM_PERIOD_BYTES] = "period_bytes",
318 [SNDRV_PCM_HW_PARAM_PERIODS] = "periods",
319 [SNDRV_PCM_HW_PARAM_BUFFER_TIME] = "buffer_time",
320 [SNDRV_PCM_HW_PARAM_BUFFER_SIZE] = "buffer_size",
321 [SNDRV_PCM_HW_PARAM_BUFFER_BYTES] = "buffer_bytes",
322 [SNDRV_PCM_HW_PARAM_TICK_TIME] = "tick_time",
323};
324
325void param_dump(struct snd_pcm_hw_params *p)
326{
327 int n;
328
329 for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK;
330 n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) {
331 struct snd_mask *m = param_to_mask(p, n);
332 ALOGV("%s = %08x%08x\n", param_name[n],
333 m->bits[1], m->bits[0]);
334 }
335 for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL;
336 n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) {
337 struct snd_interval *i = param_to_interval(p, n);
338 ALOGV("%s = (%d,%d) omin=%d omax=%d int=%d empty=%d\n",
339 param_name[n], i->min, i->max, i->openmin,
340 i->openmax, i->integer, i->empty);
341 }
342 ALOGV("info = %08x\n", p->info);
343 ALOGV("msbits = %d\n", p->msbits);
344 ALOGV("rate = %d/%d\n", p->rate_num, p->rate_den);
345 ALOGV("fifo = %d\n", (int) p->fifo_size);
346}
347
348static void info_dump(struct snd_pcm_info *info)
349{
350 ALOGV("device = %d\n", info->device);
351 ALOGV("subdevice = %d\n", info->subdevice);
352 ALOGV("stream = %d\n", info->stream);
353 ALOGV("card = %d\n", info->card);
354 ALOGV("id = '%s'\n", info->id);
355 ALOGV("name = '%s'\n", info->name);
356 ALOGV("subname = '%s'\n", info->subname);
357 ALOGV("dev_class = %d\n", info->dev_class);
358 ALOGV("dev_subclass = %d\n", info->dev_subclass);
359 ALOGV("subdevices_count = %d\n", info->subdevices_count);
360 ALOGV("subdevices_avail = %d\n", info->subdevices_avail);
361}
362#else
363void param_dump(struct snd_pcm_hw_params *p) {}
364static void info_dump(struct snd_pcm_info *info) {}
365#endif
366
367int param_set_hw_refine(struct pcm *pcm, struct snd_pcm_hw_params *params)
368{
369 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_REFINE, params)) {
370 ALOGE("SNDRV_PCM_IOCTL_HW_REFINE failed");
371 return -EPERM;
372 }
373 return 0;
374}
375
376int param_set_hw_params(struct pcm *pcm, struct snd_pcm_hw_params *params)
377{
378 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, params)) {
379 return -EPERM;
380 }
381 pcm->hw_p = params;
382 return 0;
383}
384
385int param_set_sw_params(struct pcm *pcm, struct snd_pcm_sw_params *sparams)
386{
387 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, sparams)) {
388 return -EPERM;
389 }
390 pcm->sw_p = sparams;
391 return 0;
392}
393
394int pcm_buffer_size(struct snd_pcm_hw_params *params)
395{
396 struct snd_interval *i = param_to_interval(params, SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
397 ALOGV("%s = (%d,%d) omin=%d omax=%d int=%d empty=%d\n",
398 param_name[SNDRV_PCM_HW_PARAM_BUFFER_BYTES],
399 i->min, i->max, i->openmin,
400 i->openmax, i->integer, i->empty);
401 return i->min;
402}
403
404int pcm_period_size(struct snd_pcm_hw_params *params)
405{
406 struct snd_interval *i = param_to_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_BYTES);
407 ALOGV("%s = (%d,%d) omin=%d omax=%d int=%d empty=%d\n",
408 param_name[SNDRV_PCM_HW_PARAM_PERIOD_BYTES],
409 i->min, i->max, i->openmin,
410 i->openmax, i->integer, i->empty);
411 return i->min;
412}
413
414const char* pcm_error(struct pcm *pcm)
415{
416 return pcm->error;
417}
418
419static int oops(struct pcm *pcm, int e, const char *fmt, ...)
420{
421 va_list ap;
422 int sz;
423
424 va_start(ap, fmt);
425 vsnprintf(pcm->error, PCM_ERROR_MAX, fmt, ap);
426 va_end(ap);
427 sz = strnlen(pcm->error, PCM_ERROR_MAX);
428
429 if (errno)
430 snprintf(pcm->error + sz, PCM_ERROR_MAX - sz,
431 ": %s", strerror(e));
432 return -1;
433}
434
435long pcm_avail(struct pcm *pcm)
436{
437 struct snd_pcm_sync_ptr *sync_ptr = pcm->sync_ptr;
438 if (pcm->flags & DEBUG_ON) {
439 ALOGV("hw_ptr = %d buf_size = %d appl_ptr = %d\n",
440 sync_ptr->s.status.hw_ptr,
441 pcm->buffer_size,
442 sync_ptr->c.control.appl_ptr);
443 }
444 if (pcm->flags & PCM_IN) {
445 long avail = sync_ptr->s.status.hw_ptr - sync_ptr->c.control.appl_ptr;
446 if (avail < 0)
447 avail += pcm->sw_p->boundary;
448 return avail;
449 } else {
Mingming Yinb0f0fce2012-11-29 20:04:36 -0800450 int buffer_size = 0;
451 long avail;
452 if(pcm->flags & PCM_MONO)
453 buffer_size = pcm->buffer_size/2;
454 else if(pcm->flags & PCM_QUAD)
455 buffer_size = pcm->buffer_size/8;
456 else if(pcm->flags & PCM_5POINT1)
457 buffer_size = pcm->buffer_size/12;
458 else if(pcm->flags & PCM_7POINT1)
459 buffer_size = pcm->buffer_size/16;
460 else
461 buffer_size = pcm->buffer_size/4;
462
463 avail = sync_ptr->s.status.hw_ptr - sync_ptr->c.control.appl_ptr + buffer_size;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700464 if (avail < 0)
465 avail += pcm->sw_p->boundary;
466 else if ((unsigned long) avail >= pcm->sw_p->boundary)
467 avail -= pcm->sw_p->boundary;
468 return avail;
469 }
470}
471
472int sync_ptr(struct pcm *pcm)
473{
474 int err;
475 err = ioctl(pcm->fd, SNDRV_PCM_IOCTL_SYNC_PTR, pcm->sync_ptr);
476 if (err < 0) {
477 err = errno;
478 ALOGE("SNDRV_PCM_IOCTL_SYNC_PTR failed %d \n", err);
479 return err;
480 }
481
482 return 0;
483}
484
485int mmap_buffer(struct pcm *pcm)
486{
487 int err, i;
488 char *ptr;
489 unsigned size;
490 struct snd_pcm_channel_info ch;
Mingming Yinb0f0fce2012-11-29 20:04:36 -0800491 int channels;
492
493 if(pcm->flags & PCM_MONO)
494 channels = 1;
495 else if(pcm->flags & PCM_QUAD)
496 channels = 4;
497 else if(pcm->flags & PCM_5POINT1)
498 channels = 6;
499 else if(pcm->flags & PCM_7POINT1)
500 channels = 8;
501 else
502 channels = 2;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700503
504 size = pcm->buffer_size;
505 if (pcm->flags & DEBUG_ON)
506 ALOGV("size = %d\n", size);
507 pcm->addr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED,
508 pcm->fd, 0);
509 if (pcm->addr)
510 return 0;
511 else
512 return -errno;
513}
514
515/*
516 * Destination offset would be mod of total data written
517 * (application pointer) and the buffer size of the driver.
518 * Hence destination address would be base address(pcm->addr) +
519 * destination offset.
520 */
521u_int8_t *dst_address(struct pcm *pcm)
522{
523 unsigned long pcm_offset = 0;
524 struct snd_pcm_sync_ptr *sync_ptr = pcm->sync_ptr;
525 unsigned int appl_ptr = 0;
Mingming Yinb0f0fce2012-11-29 20:04:36 -0800526 int channels;
527 if(pcm->flags & PCM_MONO)
528 channels = 1;
529 else if(pcm->flags & PCM_QUAD)
530 channels = 4;
531 else if(pcm->flags & PCM_5POINT1)
532 channels = 6;
533 else if(pcm->flags & PCM_7POINT1)
534 channels = 8;
535 else
536 channels = 2;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700537
Mingming Yinb0f0fce2012-11-29 20:04:36 -0800538 appl_ptr = sync_ptr->c.control.appl_ptr*2*channels;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700539 pcm_offset = (appl_ptr % (unsigned long)pcm->buffer_size);
540 return pcm->addr + pcm_offset;
541
542}
543
544int mmap_transfer(struct pcm *pcm, void *data, unsigned offset,
545 long frames)
546{
547 struct snd_pcm_sync_ptr *sync_ptr = pcm->sync_ptr;
548 unsigned size;
549 u_int8_t *dst_addr, *mmaped_addr;
550 u_int8_t *src_addr = data;
Mingming Yinb0f0fce2012-11-29 20:04:36 -0800551 int channels;
552 if(pcm->flags & PCM_MONO)
553 channels = 1;
554 else if(pcm->flags & PCM_QUAD)
555 channels = 4;
556 else if(pcm->flags & PCM_5POINT1)
557 channels = 6;
558 else if(pcm->flags & PCM_7POINT1)
559 channels = 8;
560 else
561 channels = 2;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700562
563 dst_addr = dst_address(pcm);
564
565 frames = frames * channels *2 ;
566
567 while (frames-- > 0) {
568 *(u_int8_t*)dst_addr = *(const u_int8_t*)src_addr;
569 src_addr++;
570 dst_addr++;
571 }
572 return 0;
573}
574
575int mmap_transfer_capture(struct pcm *pcm, void *data, unsigned offset,
576 long frames)
577{
578 struct snd_pcm_sync_ptr *sync_ptr = pcm->sync_ptr;
579 unsigned long pcm_offset = 0;
580 unsigned size;
581 u_int8_t *dst_addr, *mmaped_addr;
582 u_int8_t *src_addr;
Mingming Yinb0f0fce2012-11-29 20:04:36 -0800583 int channels;
584 unsigned int tmp;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700585
Mingming Yinb0f0fce2012-11-29 20:04:36 -0800586 if(pcm->flags & PCM_MONO)
587 channels = 1;
588 else if(pcm->flags & PCM_QUAD)
589 channels = 4;
590 else if(pcm->flags & PCM_5POINT1)
591 channels = 6;
592 else if(pcm->flags & PCM_7POINT1)
593 channels = 8;
594 else
595 channels = 2;
596 tmp = sync_ptr->c.control.appl_ptr*2*channels;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700597 pcm_offset = (tmp % (unsigned long)pcm->buffer_size);
598 dst_addr = data;
599 src_addr = pcm->addr + pcm_offset;
600 frames = frames * channels *2 ;
601
602 while (frames-- > 0) {
603 *(u_int8_t*)dst_addr = *(const u_int8_t*)src_addr;
604 src_addr++;
605 dst_addr++;
606 }
607 return 0;
608}
609
610int pcm_prepare(struct pcm *pcm)
611{
612 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE)) {
613 ALOGE("cannot prepare channel: errno =%d\n", -errno);
614 return -errno;
615 }
616 pcm->running = 1;
617 return 0;
618}
619
620static int pcm_write_mmap(struct pcm *pcm, void *data, unsigned count)
621{
622 long frames;
623 int err;
624 int bytes_written;
Mingming Yinb0f0fce2012-11-29 20:04:36 -0800625 int channels;
626 if(pcm->flags & PCM_MONO)
627 channels = 1;
628 else if(pcm->flags & PCM_QUAD)
629 channels = 4;
630 else if(pcm->flags & PCM_5POINT1)
631 channels = 6;
632 else if(pcm->flags & PCM_7POINT1)
633 channels = 8;
634 else
635 channels = 2;
636 frames = count / (2*channels);
Iliyan Malchev4765c432012-06-11 14:36:16 -0700637
638 pcm->sync_ptr->flags = SNDRV_PCM_SYNC_PTR_APPL | SNDRV_PCM_SYNC_PTR_AVAIL_MIN;
639 err = sync_ptr(pcm);
640 if (err == EPIPE) {
641 ALOGE("Failed in sync_ptr\n");
642 /* we failed to make our window -- try to restart */
643 pcm->underruns++;
644 pcm->running = 0;
645 pcm_prepare(pcm);
646 }
647 pcm->sync_ptr->c.control.appl_ptr += frames;
648 pcm->sync_ptr->flags = 0;
649
650 err = sync_ptr(pcm);
651 if (err == EPIPE) {
652 ALOGE("Failed in sync_ptr 2 \n");
653 /* we failed to make our window -- try to restart */
654 pcm->underruns++;
655 pcm->running = 0;
656 pcm_prepare(pcm);
657 }
658 bytes_written = pcm->sync_ptr->c.control.appl_ptr - pcm->sync_ptr->s.status.hw_ptr;
659 if ((bytes_written >= pcm->sw_p->start_threshold) && (!pcm->start)) {
660 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START)) {
661 err = -errno;
662 if (errno == EPIPE) {
663 ALOGE("Failed in SNDRV_PCM_IOCTL_START\n");
664 /* we failed to make our window -- try to restart */
665 pcm->underruns++;
666 pcm->running = 0;
667 pcm_prepare(pcm);
668 } else {
669 ALOGE("Error no %d \n", errno);
670 return -errno;
671 }
672 } else {
SathishKumar Mani0a019912012-09-11 12:33:11 -0700673 ALOGD(" start\n");
Iliyan Malchev4765c432012-06-11 14:36:16 -0700674 pcm->start = 1;
675 }
676 }
677 return 0;
678}
679
680static int pcm_write_nmmap(struct pcm *pcm, void *data, unsigned count)
681{
682 struct snd_xferi x;
Mingming Yinb0f0fce2012-11-29 20:04:36 -0800683 int channels;
684 if(pcm->flags & PCM_MONO)
685 channels = 1;
686 else if(pcm->flags & PCM_QUAD)
687 channels = 4;
688 else if(pcm->flags & PCM_5POINT1)
689 channels = 6;
690 else if(pcm->flags & PCM_7POINT1)
691 channels = 8;
692 else
693 channels = 2;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700694
695 if (pcm->flags & PCM_IN)
696 return -EINVAL;
697 x.buf = data;
698 x.frames = (count / (channels * 2)) ;
699
700 for (;;) {
701 if (!pcm->running) {
702 if (pcm_prepare(pcm))
703 return -errno;
704 }
705 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) {
706 if (errno == EPIPE) {
707 /* we failed to make our window -- try to restart */
708 ALOGE("Underrun Error\n");
709 pcm->underruns++;
710 pcm->running = 0;
711 continue;
712 }
713 return -errno;
714 }
715 if (pcm->flags & DEBUG_ON)
716 ALOGV("Sent frame\n");
717 return 0;
718 }
719}
720
721int pcm_write(struct pcm *pcm, void *data, unsigned count)
722{
723 if (pcm->flags & PCM_MMAP)
724 return pcm_write_mmap(pcm, data, count);
725 else
726 return pcm_write_nmmap(pcm, data, count);
727}
728
729int pcm_read(struct pcm *pcm, void *data, unsigned count)
730{
731 struct snd_xferi x;
732
733 if (!(pcm->flags & PCM_IN))
734 return -EINVAL;
735
736 x.buf = data;
737 if (pcm->flags & PCM_MONO) {
738 x.frames = (count / 2);
739 } else if (pcm->flags & PCM_QUAD) {
740 x.frames = (count / 8);
741 } else if (pcm->flags & PCM_5POINT1) {
742 x.frames = (count / 12);
Mingming Yinb0f0fce2012-11-29 20:04:36 -0800743 } else if (pcm->flags & PCM_7POINT1) {
744 x.frames = (count / 16);
Iliyan Malchev4765c432012-06-11 14:36:16 -0700745 } else {
746 x.frames = (count / 4);
747 }
748
749 for (;;) {
750 if (!pcm->running) {
751 if (pcm_prepare(pcm))
752 return -errno;
753 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START)) {
754 ALOGE("Arec:SNDRV_PCM_IOCTL_START failed\n");
755 return -errno;
756 }
757 pcm->running = 1;
758 }
759 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) {
760 if (errno == EPIPE) {
761 /* we failed to make our window -- try to restart */
762 ALOGE("Arec:Overrun Error\n");
763 pcm->underruns++;
764 pcm->running = 0;
765 continue;
Mingming Yinb0f0fce2012-11-29 20:04:36 -0800766 } else if (errno == ESTRPIPE) {
767 ALOGV("Resume from suspended\n");
768 for (;;) {
769 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_RESUME)) {
770 if (errno == EAGAIN ) {
771 if (pcm_prepare(pcm))
772 return -errno;
773 }
774 /* send resume command again */
775 continue;
776 } else
777 break;
778 }
779 continue;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700780 }
781 ALOGE("Arec: error%d\n", errno);
782 return -errno;
783 }
784 return 0;
785 }
786}
787
788static struct pcm bad_pcm = {
789 .fd = -1,
790};
791
792static int enable_timer(struct pcm *pcm) {
793
794 pcm->timer_fd = open("/dev/snd/timer", O_RDWR | O_NONBLOCK);
795 if (pcm->timer_fd < 0) {
796 close(pcm->fd);
797 ALOGE("cannot open timer device 'timer'");
798 return &bad_pcm;
799 }
800 int arg = 1;
801 struct snd_timer_params timer_param;
802 struct snd_timer_select sel;
803 if (ioctl(pcm->timer_fd, SNDRV_TIMER_IOCTL_TREAD, &arg) < 0) {
804 ALOGE("extended read is not supported (SNDRV_TIMER_IOCTL_TREAD)\n");
805 }
806 memset(&sel, 0, sizeof(sel));
807 sel.id.dev_class = SNDRV_TIMER_CLASS_PCM;
808 sel.id.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
809 sel.id.card = pcm->card_no;
810 sel.id.device = pcm->device_no;
811 if (pcm->flags & PCM_IN)
812 sel.id.subdevice = 1;
813 else
814 sel.id.subdevice = 0;
815
816 if (pcm->flags & DEBUG_ON) {
SathishKumar Mani0a019912012-09-11 12:33:11 -0700817 ALOGD("sel.id.dev_class= %d\n", sel.id.dev_class);
818 ALOGD("sel.id.dev_sclass = %d\n", sel.id.dev_sclass);
819 ALOGD("sel.id.card = %d\n", sel.id.card);
820 ALOGD("sel.id.device = %d\n", sel.id.device);
821 ALOGD("sel.id.subdevice = %d\n", sel.id.subdevice);
Iliyan Malchev4765c432012-06-11 14:36:16 -0700822 }
823 if (ioctl(pcm->timer_fd, SNDRV_TIMER_IOCTL_SELECT, &sel) < 0) {
824 ALOGE("SNDRV_TIMER_IOCTL_SELECT failed.\n");
825 close(pcm->timer_fd);
826 close(pcm->fd);
827 return &bad_pcm;
828 }
829 memset(&timer_param, 0, sizeof(struct snd_timer_params));
830 timer_param.flags |= SNDRV_TIMER_PSFLG_AUTO;
831 timer_param.ticks = 1;
832 timer_param.filter = (1<<SNDRV_TIMER_EVENT_MSUSPEND) | (1<<SNDRV_TIMER_EVENT_MRESUME) | (1<<SNDRV_TIMER_EVENT_TICK);
833
834 if (ioctl(pcm->timer_fd, SNDRV_TIMER_IOCTL_PARAMS, &timer_param)< 0) {
835 ALOGE("SNDRV_TIMER_IOCTL_PARAMS failed\n");
836 }
837 if (ioctl(pcm->timer_fd, SNDRV_TIMER_IOCTL_START) < 0) {
838 close(pcm->timer_fd);
839 ALOGE("SNDRV_TIMER_IOCTL_START failed\n");
840 }
841 return 0;
842}
843
844static int disable_timer(struct pcm *pcm) {
845 if (pcm == &bad_pcm)
846 return 0;
847 if (ioctl(pcm->timer_fd, SNDRV_TIMER_IOCTL_STOP) < 0)
848 ALOGE("SNDRV_TIMER_IOCTL_STOP failed\n");
849 return close(pcm->timer_fd);
850}
851
852int pcm_close(struct pcm *pcm)
853{
854 if (pcm == &bad_pcm)
855 return 0;
856
857 if (pcm->flags & PCM_MMAP) {
858 disable_timer(pcm);
859 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DROP) < 0) {
860 ALOGE("Reset failed");
861 }
862
863 if (munmap(pcm->addr, pcm->buffer_size))
864 ALOGE("munmap failed");
865
866 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_FREE) < 0) {
867 ALOGE("HW_FREE failed");
868 }
869 }
870
871 if (pcm->fd >= 0)
872 close(pcm->fd);
873 pcm->running = 0;
874 pcm->buffer_size = 0;
875 pcm->fd = -1;
876 if (pcm->sw_p)
877 free(pcm->sw_p);
878 if (pcm->hw_p)
879 free(pcm->hw_p);
880 if (pcm->sync_ptr)
881 free(pcm->sync_ptr);
882 free(pcm);
883 return 0;
884}
885
886struct pcm *pcm_open(unsigned flags, char *device)
887{
888 char dname[19];
889 struct pcm *pcm;
890 struct snd_pcm_info info;
891 struct snd_pcm_hw_params params;
892 struct snd_pcm_sw_params sparams;
893 unsigned period_sz;
894 unsigned period_cnt;
895 char *tmp;
896
897 if (flags & DEBUG_ON) {
898 ALOGV("pcm_open(0x%08x)",flags);
899 ALOGV("device %s\n",device);
900 }
901
902 pcm = calloc(1, sizeof(struct pcm));
903 if (!pcm)
904 return &bad_pcm;
905
906 tmp = device+4;
907 if ((strncmp(device, "hw:",3) != 0) || (strncmp(tmp, ",",1) != 0)){
908 ALOGE("Wrong device fromat\n");
909 free(pcm);
910 return -EINVAL;
911 }
912
913 if (flags & PCM_IN) {
914 strlcpy(dname, "/dev/snd/pcmC", sizeof(dname));
915 tmp = device+3;
916 strlcat(dname, tmp, (2+strlen(dname))) ;
917 pcm->card_no = atoi(tmp);
918 strlcat(dname, "D", (sizeof("D")+strlen(dname)));
919 tmp = device+5;
920 pcm->device_no = atoi(tmp);
921 /* should be safe to assume pcm dev ID never exceed 99 */
922 if (pcm->device_no > 9)
923 strlcat(dname, tmp, (3+strlen(dname)));
924 else
925 strlcat(dname, tmp, (2+strlen(dname)));
926 strlcat(dname, "c", (sizeof("c")+strlen(dname)));
927 } else {
928 strlcpy(dname, "/dev/snd/pcmC", sizeof(dname));
929 tmp = device+3;
930 strlcat(dname, tmp, (2+strlen(dname))) ;
931 pcm->card_no = atoi(tmp);
932 strlcat(dname, "D", (sizeof("D")+strlen(dname)));
933 tmp = device+5;
934 pcm->device_no = atoi(tmp);
935 /* should be safe to assume pcm dev ID never exceed 99 */
936 if (pcm->device_no > 9)
937 strlcat(dname, tmp, (3+strlen(dname)));
938 else
939 strlcat(dname, tmp, (2+strlen(dname)));
940 strlcat(dname, "p", (sizeof("p")+strlen(dname)));
941 }
942 if (pcm->flags & DEBUG_ON)
943 ALOGV("Device name %s\n", dname);
944
945 pcm->sync_ptr = calloc(1, sizeof(struct snd_pcm_sync_ptr));
946 if (!pcm->sync_ptr) {
947 free(pcm);
948 return &bad_pcm;
949 }
950 pcm->flags = flags;
951
952 pcm->fd = open(dname, O_RDWR|O_NONBLOCK);
953 if (pcm->fd < 0) {
954 free(pcm->sync_ptr);
955 free(pcm);
956 ALOGE("cannot open device '%s', errno %d", dname, errno);
957 return &bad_pcm;
958 }
959
960 if (fcntl(pcm->fd, F_SETFL, fcntl(pcm->fd, F_GETFL) &
961 ~O_NONBLOCK) < 0) {
962 close(pcm->fd);
963 free(pcm->sync_ptr);
964 free(pcm);
965 ALOGE("failed to change the flag, errno %d", errno);
966 return &bad_pcm;
967 }
968
969 if (pcm->flags & PCM_MMAP)
970 enable_timer(pcm);
971
972 if (pcm->flags & DEBUG_ON)
973 ALOGV("pcm_open() %s\n", dname);
974 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) {
975 ALOGE("cannot get info - %s", dname);
976 }
977 if (pcm->flags & DEBUG_ON)
978 info_dump(&info);
979
980 return pcm;
981}
982
983int pcm_ready(struct pcm *pcm)
984{
985 return pcm->fd >= 0;
986}
Mingming Yinb0f0fce2012-11-29 20:04:36 -0800987
988int pcm_set_channel_map(struct pcm *pcm, struct mixer *mixer,
989 int max_channels, char *chmap)
990{
991 struct mixer_ctl *ctl;
992 char control_name[44]; // max length of name is 44 as defined
993 char device_num[3]; // device number upto 2 digit
994 char **set_values;
995 int i;
996
997 ALOGV("pcm_set_channel_map");
998 set_values = (char**)malloc(max_channels*sizeof(char*));
999 if(set_values) {
1000 for(i=0; i< max_channels; i++) {
1001 set_values[i] = (char*)malloc(4*sizeof(char));
1002 if(set_values[i]) {
1003 sprintf(set_values[i],"%d",chmap[i]);
1004 } else {
1005 ALOGE("memory allocation for set channel map failed");
1006 return -1;
1007 }
1008 }
1009 } else {
1010 ALOGE("memory allocation for set channel map failed");
1011 return -1;
1012 }
1013 strlcpy(control_name, "Playback Channel Map", sizeof(control_name));
1014 sprintf(device_num, "%d", pcm->device_no);
1015 strcat(control_name, device_num);
1016 ALOGV("pcm_set_channel_map: control name:%s", control_name);
1017 ctl = mixer_get_control(mixer, control_name, 0);
1018 if(ctl == NULL) {
1019 ALOGE(stderr, "Could not get the mixer control\n");
1020 return -1;
1021 }
1022 mixer_ctl_set_value(ctl, max_channels, set_values);
1023 for(i=0; i< max_channels; i++)
1024 if(set_values[i])
1025 free(set_values[i]);
1026 if(set_values)
1027 free(set_values);
1028 return 0;
1029}