blob: 8dd873f045debd41a28967d9bbc42029d8cda575 [file] [log] [blame]
Iliyan Malchev4765c432012-06-11 14:36:16 -07001/*
2** Copyright 2010, The Android Open-Source Project
Subhash Chandra Bose Naripeddy6000eb52013-01-05 18:02:44 -08003** Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
Iliyan Malchev4765c432012-06-11 14:36:16 -07004**
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>
dhacker2994db5552013-04-26 21:32:42 -050046#ifdef QCOM_COMPRESSED_AUDIO_ENABLED
Mingming Yinbbd94ad2012-11-29 20:04:36 -080047#include <sound/compress_params.h>
dhacker2994db5552013-04-26 21:32:42 -050048#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 Yinbbd94ad2012-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 Yinbbd94ad2012-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 Yinbbd94ad2012-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 Yinbbd94ad2012-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 Yinbbd94ad2012-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
Leela Kameswari Yellamraju1333c582013-04-19 00:15:11 +0530472void appl_pt_forward(struct pcm *pcm)
473{
474 struct snd_pcm_sync_ptr *sync_ptr = pcm->sync_ptr;
475 snd_pcm_uframes_t appl_ptr = sync_ptr->c.control.appl_ptr;
476
477 if (appl_ptr >= pcm->sw_p->boundary) {
478 appl_ptr -= pcm->sw_p->boundary;
479 ALOGE("appl_ptr %ld set to ld",
480 sync_ptr->c.control.appl_ptr, appl_ptr);
481 sync_ptr->c.control.appl_ptr = appl_ptr;
482 }
483}
484
Iliyan Malchev4765c432012-06-11 14:36:16 -0700485int sync_ptr(struct pcm *pcm)
486{
487 int err;
Leela Kameswari Yellamraju1333c582013-04-19 00:15:11 +0530488
489 if (pcm->flags & PCM_MMAP)
490 appl_pt_forward(pcm);
491
Iliyan Malchev4765c432012-06-11 14:36:16 -0700492 err = ioctl(pcm->fd, SNDRV_PCM_IOCTL_SYNC_PTR, pcm->sync_ptr);
493 if (err < 0) {
494 err = errno;
495 ALOGE("SNDRV_PCM_IOCTL_SYNC_PTR failed %d \n", err);
496 return err;
497 }
498
499 return 0;
500}
501
502int mmap_buffer(struct pcm *pcm)
503{
504 int err, i;
505 char *ptr;
506 unsigned size;
507 struct snd_pcm_channel_info ch;
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800508 int channels;
509
510 if(pcm->flags & PCM_MONO)
511 channels = 1;
512 else if(pcm->flags & PCM_QUAD)
513 channels = 4;
514 else if(pcm->flags & PCM_5POINT1)
515 channels = 6;
516 else if(pcm->flags & PCM_7POINT1)
517 channels = 8;
518 else
519 channels = 2;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700520
521 size = pcm->buffer_size;
522 if (pcm->flags & DEBUG_ON)
523 ALOGV("size = %d\n", size);
524 pcm->addr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED,
525 pcm->fd, 0);
526 if (pcm->addr)
527 return 0;
528 else
529 return -errno;
530}
531
532/*
533 * Destination offset would be mod of total data written
534 * (application pointer) and the buffer size of the driver.
535 * Hence destination address would be base address(pcm->addr) +
536 * destination offset.
537 */
538u_int8_t *dst_address(struct pcm *pcm)
539{
540 unsigned long pcm_offset = 0;
541 struct snd_pcm_sync_ptr *sync_ptr = pcm->sync_ptr;
542 unsigned int appl_ptr = 0;
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800543 int channels;
544 if(pcm->flags & PCM_MONO)
545 channels = 1;
546 else if(pcm->flags & PCM_QUAD)
547 channels = 4;
548 else if(pcm->flags & PCM_5POINT1)
549 channels = 6;
550 else if(pcm->flags & PCM_7POINT1)
551 channels = 8;
552 else
553 channels = 2;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700554
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800555 appl_ptr = sync_ptr->c.control.appl_ptr*2*channels;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700556 pcm_offset = (appl_ptr % (unsigned long)pcm->buffer_size);
557 return pcm->addr + pcm_offset;
558
559}
560
561int mmap_transfer(struct pcm *pcm, void *data, unsigned offset,
562 long frames)
563{
564 struct snd_pcm_sync_ptr *sync_ptr = pcm->sync_ptr;
565 unsigned size;
566 u_int8_t *dst_addr, *mmaped_addr;
567 u_int8_t *src_addr = data;
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800568 int channels;
569 if(pcm->flags & PCM_MONO)
570 channels = 1;
571 else if(pcm->flags & PCM_QUAD)
572 channels = 4;
573 else if(pcm->flags & PCM_5POINT1)
574 channels = 6;
575 else if(pcm->flags & PCM_7POINT1)
576 channels = 8;
577 else
578 channels = 2;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700579
580 dst_addr = dst_address(pcm);
581
582 frames = frames * channels *2 ;
583
584 while (frames-- > 0) {
585 *(u_int8_t*)dst_addr = *(const u_int8_t*)src_addr;
586 src_addr++;
587 dst_addr++;
588 }
589 return 0;
590}
591
592int mmap_transfer_capture(struct pcm *pcm, void *data, unsigned offset,
593 long frames)
594{
595 struct snd_pcm_sync_ptr *sync_ptr = pcm->sync_ptr;
596 unsigned long pcm_offset = 0;
597 unsigned size;
598 u_int8_t *dst_addr, *mmaped_addr;
599 u_int8_t *src_addr;
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800600 int channels;
601 unsigned int tmp;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700602
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800603 if(pcm->flags & PCM_MONO)
604 channels = 1;
605 else if(pcm->flags & PCM_QUAD)
606 channels = 4;
607 else if(pcm->flags & PCM_5POINT1)
608 channels = 6;
609 else if(pcm->flags & PCM_7POINT1)
610 channels = 8;
611 else
612 channels = 2;
613 tmp = sync_ptr->c.control.appl_ptr*2*channels;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700614 pcm_offset = (tmp % (unsigned long)pcm->buffer_size);
615 dst_addr = data;
616 src_addr = pcm->addr + pcm_offset;
617 frames = frames * channels *2 ;
618
619 while (frames-- > 0) {
620 *(u_int8_t*)dst_addr = *(const u_int8_t*)src_addr;
621 src_addr++;
622 dst_addr++;
623 }
624 return 0;
625}
626
627int pcm_prepare(struct pcm *pcm)
628{
629 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE)) {
630 ALOGE("cannot prepare channel: errno =%d\n", -errno);
631 return -errno;
632 }
633 pcm->running = 1;
634 return 0;
635}
636
637static int pcm_write_mmap(struct pcm *pcm, void *data, unsigned count)
638{
639 long frames;
640 int err;
641 int bytes_written;
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800642 int channels;
643 if(pcm->flags & PCM_MONO)
644 channels = 1;
645 else if(pcm->flags & PCM_QUAD)
646 channels = 4;
647 else if(pcm->flags & PCM_5POINT1)
648 channels = 6;
649 else if(pcm->flags & PCM_7POINT1)
650 channels = 8;
651 else
652 channels = 2;
653 frames = count / (2*channels);
Iliyan Malchev4765c432012-06-11 14:36:16 -0700654
655 pcm->sync_ptr->flags = SNDRV_PCM_SYNC_PTR_APPL | SNDRV_PCM_SYNC_PTR_AVAIL_MIN;
656 err = sync_ptr(pcm);
657 if (err == EPIPE) {
658 ALOGE("Failed in sync_ptr\n");
659 /* we failed to make our window -- try to restart */
660 pcm->underruns++;
661 pcm->running = 0;
662 pcm_prepare(pcm);
663 }
664 pcm->sync_ptr->c.control.appl_ptr += frames;
665 pcm->sync_ptr->flags = 0;
666
667 err = sync_ptr(pcm);
668 if (err == EPIPE) {
669 ALOGE("Failed in sync_ptr 2 \n");
670 /* we failed to make our window -- try to restart */
671 pcm->underruns++;
672 pcm->running = 0;
673 pcm_prepare(pcm);
674 }
675 bytes_written = pcm->sync_ptr->c.control.appl_ptr - pcm->sync_ptr->s.status.hw_ptr;
676 if ((bytes_written >= pcm->sw_p->start_threshold) && (!pcm->start)) {
677 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START)) {
678 err = -errno;
679 if (errno == EPIPE) {
680 ALOGE("Failed in SNDRV_PCM_IOCTL_START\n");
681 /* we failed to make our window -- try to restart */
682 pcm->underruns++;
683 pcm->running = 0;
684 pcm_prepare(pcm);
685 } else {
686 ALOGE("Error no %d \n", errno);
687 return -errno;
688 }
689 } else {
SathishKumar Mani0a019912012-09-11 12:33:11 -0700690 ALOGD(" start\n");
Iliyan Malchev4765c432012-06-11 14:36:16 -0700691 pcm->start = 1;
692 }
693 }
694 return 0;
695}
696
697static int pcm_write_nmmap(struct pcm *pcm, void *data, unsigned count)
698{
699 struct snd_xferi x;
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800700 int channels;
701 if(pcm->flags & PCM_MONO)
702 channels = 1;
703 else if(pcm->flags & PCM_QUAD)
704 channels = 4;
705 else if(pcm->flags & PCM_5POINT1)
706 channels = 6;
707 else if(pcm->flags & PCM_7POINT1)
708 channels = 8;
709 else
710 channels = 2;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700711
712 if (pcm->flags & PCM_IN)
713 return -EINVAL;
714 x.buf = data;
715 x.frames = (count / (channels * 2)) ;
716
717 for (;;) {
718 if (!pcm->running) {
719 if (pcm_prepare(pcm))
720 return -errno;
721 }
722 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) {
723 if (errno == EPIPE) {
724 /* we failed to make our window -- try to restart */
725 ALOGE("Underrun Error\n");
726 pcm->underruns++;
727 pcm->running = 0;
728 continue;
729 }
730 return -errno;
731 }
732 if (pcm->flags & DEBUG_ON)
733 ALOGV("Sent frame\n");
734 return 0;
735 }
736}
737
738int pcm_write(struct pcm *pcm, void *data, unsigned count)
739{
740 if (pcm->flags & PCM_MMAP)
741 return pcm_write_mmap(pcm, data, count);
742 else
743 return pcm_write_nmmap(pcm, data, count);
744}
745
746int pcm_read(struct pcm *pcm, void *data, unsigned count)
747{
748 struct snd_xferi x;
749
750 if (!(pcm->flags & PCM_IN))
751 return -EINVAL;
752
753 x.buf = data;
754 if (pcm->flags & PCM_MONO) {
755 x.frames = (count / 2);
756 } else if (pcm->flags & PCM_QUAD) {
757 x.frames = (count / 8);
758 } else if (pcm->flags & PCM_5POINT1) {
759 x.frames = (count / 12);
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800760 } else if (pcm->flags & PCM_7POINT1) {
761 x.frames = (count / 16);
Iliyan Malchev4765c432012-06-11 14:36:16 -0700762 } else {
763 x.frames = (count / 4);
764 }
765
766 for (;;) {
767 if (!pcm->running) {
768 if (pcm_prepare(pcm))
769 return -errno;
770 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START)) {
771 ALOGE("Arec:SNDRV_PCM_IOCTL_START failed\n");
772 return -errno;
773 }
774 pcm->running = 1;
775 }
776 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) {
777 if (errno == EPIPE) {
778 /* we failed to make our window -- try to restart */
779 ALOGE("Arec:Overrun Error\n");
780 pcm->underruns++;
781 pcm->running = 0;
782 continue;
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800783 } else if (errno == ESTRPIPE) {
784 ALOGV("Resume from suspended\n");
785 for (;;) {
786 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_RESUME)) {
787 if (errno == EAGAIN ) {
788 if (pcm_prepare(pcm))
789 return -errno;
790 }
791 /* send resume command again */
792 continue;
793 } else
794 break;
795 }
796 continue;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700797 }
798 ALOGE("Arec: error%d\n", errno);
799 return -errno;
800 }
801 return 0;
802 }
803}
804
805static struct pcm bad_pcm = {
806 .fd = -1,
807};
808
809static int enable_timer(struct pcm *pcm) {
810
811 pcm->timer_fd = open("/dev/snd/timer", O_RDWR | O_NONBLOCK);
812 if (pcm->timer_fd < 0) {
813 close(pcm->fd);
814 ALOGE("cannot open timer device 'timer'");
815 return &bad_pcm;
816 }
817 int arg = 1;
818 struct snd_timer_params timer_param;
819 struct snd_timer_select sel;
820 if (ioctl(pcm->timer_fd, SNDRV_TIMER_IOCTL_TREAD, &arg) < 0) {
821 ALOGE("extended read is not supported (SNDRV_TIMER_IOCTL_TREAD)\n");
822 }
823 memset(&sel, 0, sizeof(sel));
824 sel.id.dev_class = SNDRV_TIMER_CLASS_PCM;
825 sel.id.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
826 sel.id.card = pcm->card_no;
827 sel.id.device = pcm->device_no;
828 if (pcm->flags & PCM_IN)
829 sel.id.subdevice = 1;
830 else
831 sel.id.subdevice = 0;
832
833 if (pcm->flags & DEBUG_ON) {
SathishKumar Mani0a019912012-09-11 12:33:11 -0700834 ALOGD("sel.id.dev_class= %d\n", sel.id.dev_class);
835 ALOGD("sel.id.dev_sclass = %d\n", sel.id.dev_sclass);
836 ALOGD("sel.id.card = %d\n", sel.id.card);
837 ALOGD("sel.id.device = %d\n", sel.id.device);
838 ALOGD("sel.id.subdevice = %d\n", sel.id.subdevice);
Iliyan Malchev4765c432012-06-11 14:36:16 -0700839 }
840 if (ioctl(pcm->timer_fd, SNDRV_TIMER_IOCTL_SELECT, &sel) < 0) {
841 ALOGE("SNDRV_TIMER_IOCTL_SELECT failed.\n");
842 close(pcm->timer_fd);
843 close(pcm->fd);
844 return &bad_pcm;
845 }
846 memset(&timer_param, 0, sizeof(struct snd_timer_params));
847 timer_param.flags |= SNDRV_TIMER_PSFLG_AUTO;
848 timer_param.ticks = 1;
849 timer_param.filter = (1<<SNDRV_TIMER_EVENT_MSUSPEND) | (1<<SNDRV_TIMER_EVENT_MRESUME) | (1<<SNDRV_TIMER_EVENT_TICK);
850
851 if (ioctl(pcm->timer_fd, SNDRV_TIMER_IOCTL_PARAMS, &timer_param)< 0) {
852 ALOGE("SNDRV_TIMER_IOCTL_PARAMS failed\n");
853 }
854 if (ioctl(pcm->timer_fd, SNDRV_TIMER_IOCTL_START) < 0) {
855 close(pcm->timer_fd);
856 ALOGE("SNDRV_TIMER_IOCTL_START failed\n");
857 }
858 return 0;
859}
860
861static int disable_timer(struct pcm *pcm) {
862 if (pcm == &bad_pcm)
863 return 0;
864 if (ioctl(pcm->timer_fd, SNDRV_TIMER_IOCTL_STOP) < 0)
865 ALOGE("SNDRV_TIMER_IOCTL_STOP failed\n");
866 return close(pcm->timer_fd);
867}
868
869int pcm_close(struct pcm *pcm)
870{
871 if (pcm == &bad_pcm)
872 return 0;
873
874 if (pcm->flags & PCM_MMAP) {
875 disable_timer(pcm);
876 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DROP) < 0) {
877 ALOGE("Reset failed");
878 }
879
880 if (munmap(pcm->addr, pcm->buffer_size))
881 ALOGE("munmap failed");
882
883 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_FREE) < 0) {
884 ALOGE("HW_FREE failed");
885 }
886 }
887
888 if (pcm->fd >= 0)
889 close(pcm->fd);
890 pcm->running = 0;
891 pcm->buffer_size = 0;
892 pcm->fd = -1;
893 if (pcm->sw_p)
894 free(pcm->sw_p);
895 if (pcm->hw_p)
896 free(pcm->hw_p);
897 if (pcm->sync_ptr)
898 free(pcm->sync_ptr);
899 free(pcm);
900 return 0;
901}
902
903struct pcm *pcm_open(unsigned flags, char *device)
904{
905 char dname[19];
906 struct pcm *pcm;
907 struct snd_pcm_info info;
908 struct snd_pcm_hw_params params;
909 struct snd_pcm_sw_params sparams;
910 unsigned period_sz;
911 unsigned period_cnt;
912 char *tmp;
913
914 if (flags & DEBUG_ON) {
915 ALOGV("pcm_open(0x%08x)",flags);
916 ALOGV("device %s\n",device);
917 }
918
919 pcm = calloc(1, sizeof(struct pcm));
920 if (!pcm)
921 return &bad_pcm;
922
923 tmp = device+4;
924 if ((strncmp(device, "hw:",3) != 0) || (strncmp(tmp, ",",1) != 0)){
925 ALOGE("Wrong device fromat\n");
926 free(pcm);
927 return -EINVAL;
928 }
929
930 if (flags & PCM_IN) {
931 strlcpy(dname, "/dev/snd/pcmC", sizeof(dname));
932 tmp = device+3;
933 strlcat(dname, tmp, (2+strlen(dname))) ;
934 pcm->card_no = atoi(tmp);
935 strlcat(dname, "D", (sizeof("D")+strlen(dname)));
936 tmp = device+5;
937 pcm->device_no = atoi(tmp);
938 /* should be safe to assume pcm dev ID never exceed 99 */
939 if (pcm->device_no > 9)
940 strlcat(dname, tmp, (3+strlen(dname)));
941 else
942 strlcat(dname, tmp, (2+strlen(dname)));
943 strlcat(dname, "c", (sizeof("c")+strlen(dname)));
944 } else {
945 strlcpy(dname, "/dev/snd/pcmC", sizeof(dname));
946 tmp = device+3;
947 strlcat(dname, tmp, (2+strlen(dname))) ;
948 pcm->card_no = atoi(tmp);
949 strlcat(dname, "D", (sizeof("D")+strlen(dname)));
950 tmp = device+5;
951 pcm->device_no = atoi(tmp);
952 /* should be safe to assume pcm dev ID never exceed 99 */
953 if (pcm->device_no > 9)
954 strlcat(dname, tmp, (3+strlen(dname)));
955 else
956 strlcat(dname, tmp, (2+strlen(dname)));
957 strlcat(dname, "p", (sizeof("p")+strlen(dname)));
958 }
959 if (pcm->flags & DEBUG_ON)
960 ALOGV("Device name %s\n", dname);
961
962 pcm->sync_ptr = calloc(1, sizeof(struct snd_pcm_sync_ptr));
963 if (!pcm->sync_ptr) {
964 free(pcm);
965 return &bad_pcm;
966 }
967 pcm->flags = flags;
968
969 pcm->fd = open(dname, O_RDWR|O_NONBLOCK);
970 if (pcm->fd < 0) {
971 free(pcm->sync_ptr);
972 free(pcm);
973 ALOGE("cannot open device '%s', errno %d", dname, errno);
974 return &bad_pcm;
975 }
976
977 if (fcntl(pcm->fd, F_SETFL, fcntl(pcm->fd, F_GETFL) &
978 ~O_NONBLOCK) < 0) {
979 close(pcm->fd);
980 free(pcm->sync_ptr);
981 free(pcm);
982 ALOGE("failed to change the flag, errno %d", errno);
983 return &bad_pcm;
984 }
985
986 if (pcm->flags & PCM_MMAP)
987 enable_timer(pcm);
988
989 if (pcm->flags & DEBUG_ON)
990 ALOGV("pcm_open() %s\n", dname);
991 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) {
992 ALOGE("cannot get info - %s", dname);
993 }
994 if (pcm->flags & DEBUG_ON)
995 info_dump(&info);
996
997 return pcm;
998}
999
1000int pcm_ready(struct pcm *pcm)
1001{
1002 return pcm->fd >= 0;
1003}
Mingming Yinbbd94ad2012-11-29 20:04:36 -08001004
1005int pcm_set_channel_map(struct pcm *pcm, struct mixer *mixer,
1006 int max_channels, char *chmap)
1007{
1008 struct mixer_ctl *ctl;
1009 char control_name[44]; // max length of name is 44 as defined
1010 char device_num[3]; // device number upto 2 digit
1011 char **set_values;
1012 int i;
1013
1014 ALOGV("pcm_set_channel_map");
1015 set_values = (char**)malloc(max_channels*sizeof(char*));
1016 if(set_values) {
1017 for(i=0; i< max_channels; i++) {
1018 set_values[i] = (char*)malloc(4*sizeof(char));
1019 if(set_values[i]) {
1020 sprintf(set_values[i],"%d",chmap[i]);
1021 } else {
1022 ALOGE("memory allocation for set channel map failed");
1023 return -1;
1024 }
1025 }
1026 } else {
1027 ALOGE("memory allocation for set channel map failed");
1028 return -1;
1029 }
1030 strlcpy(control_name, "Playback Channel Map", sizeof(control_name));
Subhash Chandra Bose Naripeddy6000eb52013-01-05 18:02:44 -08001031 if(pcm != NULL) {
1032 sprintf(device_num, "%d", pcm->device_no);
1033 strcat(control_name, device_num);
1034 }
Mingming Yinbbd94ad2012-11-29 20:04:36 -08001035 ALOGV("pcm_set_channel_map: control name:%s", control_name);
1036 ctl = mixer_get_control(mixer, control_name, 0);
1037 if(ctl == NULL) {
Phani Kumar Uppalapati1cfa1ac2013-02-21 18:01:15 -08001038 ALOGE("Could not get the mixer control\n");
Mingming Yinbbd94ad2012-11-29 20:04:36 -08001039 return -1;
1040 }
1041 mixer_ctl_set_value(ctl, max_channels, set_values);
1042 for(i=0; i< max_channels; i++)
1043 if(set_values[i])
1044 free(set_values[i]);
1045 if(set_values)
1046 free(set_values);
1047 return 0;
1048}