]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - android-sdk/platform-external-tinyalsa.git/blob - pcm.c
Update tinyalsa for pcm_start/stop() support
[android-sdk/platform-external-tinyalsa.git] / pcm.c
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 */
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>
37 #include <sys/ioctl.h>
38 #include <sys/mman.h>
39 #include <sys/time.h>
41 #include <linux/ioctl.h>
42 #define __force
43 #define __bitwise
44 #define __user
45 #include <sound/asound.h>
47 #include <tinyalsa/asoundlib.h>
49 #define PARAM_MAX SNDRV_PCM_HW_PARAM_LAST_INTERVAL
51 static inline int param_is_mask(int p)
52 {
53     return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
54         (p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
55 }
57 static inline int param_is_interval(int p)
58 {
59     return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) &&
60         (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL);
61 }
63 static inline struct snd_interval *param_to_interval(struct snd_pcm_hw_params *p, int n)
64 {
65     return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]);
66 }
68 static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n)
69 {
70     return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
71 }
73 static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit)
74 {
75     if (bit >= SNDRV_MASK_MAX)
76         return;
77     if (param_is_mask(n)) {
78         struct snd_mask *m = param_to_mask(p, n);
79         m->bits[0] = 0;
80         m->bits[1] = 0;
81         m->bits[bit >> 5] |= (1 << (bit & 31));
82     }
83 }
85 static void param_set_min(struct snd_pcm_hw_params *p, int n, unsigned int val)
86 {
87     if (param_is_interval(n)) {
88         struct snd_interval *i = param_to_interval(p, n);
89         i->min = val;
90     }
91 }
93 static void param_set_max(struct snd_pcm_hw_params *p, int n, unsigned int val)
94 {
95     if (param_is_interval(n)) {
96         struct snd_interval *i = param_to_interval(p, n);
97         i->max = val;
98     }
99 }
101 static void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned int val)
103     if (param_is_interval(n)) {
104         struct snd_interval *i = param_to_interval(p, n);
105         i->min = val;
106         i->max = val;
107         i->integer = 1;
108     }
111 static void param_init(struct snd_pcm_hw_params *p)
113     int n;
115     memset(p, 0, sizeof(*p));
116     for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK;
117          n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) {
118             struct snd_mask *m = param_to_mask(p, n);
119             m->bits[0] = ~0;
120             m->bits[1] = ~0;
121     }
122     for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL;
123          n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) {
124             struct snd_interval *i = param_to_interval(p, n);
125             i->min = 0;
126             i->max = ~0;
127     }
130 #define PCM_ERROR_MAX 128
132 struct pcm {
133     int fd;
134     unsigned int flags;
135     int running:1;
136     int underruns;
137     unsigned int buffer_size;
138     char error[PCM_ERROR_MAX];
139     struct pcm_config config;
140 };
142 unsigned int pcm_get_buffer_size(struct pcm *pcm)
144     return pcm->buffer_size;
147 const char* pcm_get_error(struct pcm *pcm)
149     return pcm->error;
152 static int oops(struct pcm *pcm, int e, const char *fmt, ...)
154     va_list ap;
155     int sz;
157     va_start(ap, fmt);
158     vsnprintf(pcm->error, PCM_ERROR_MAX, fmt, ap);
159     va_end(ap);
160     sz = strlen(pcm->error);
162     if (errno)
163         snprintf(pcm->error + sz, PCM_ERROR_MAX - sz,
164                  ": %s", strerror(e));
165     return -1;
168 static unsigned int pcm_format_to_alsa(enum pcm_format format)
170     switch (format) {
171     case PCM_FORMAT_S32_LE:
172         return SNDRV_PCM_FORMAT_S32_LE;
173     default:
174     case PCM_FORMAT_S16_LE:
175         return SNDRV_PCM_FORMAT_S16_LE;
176     };
179 static unsigned int pcm_format_to_bits(enum pcm_format format)
181     switch (format) {
182     case PCM_FORMAT_S32_LE:
183         return 32;
184     default:
185     case PCM_FORMAT_S16_LE:
186         return 16;
187     };
190 int pcm_write(struct pcm *pcm, void *data, unsigned int count)
192     struct snd_xferi x;
194     if (pcm->flags & PCM_IN)
195         return -EINVAL;
197     x.buf = data;
198     x.frames = count / (pcm->config.channels *
199                         pcm_format_to_bits(pcm->config.format) / 8);
201     for (;;) {
202         if (!pcm->running) {
203             if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE))
204                 return oops(pcm, errno, "cannot prepare channel");
205             if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x))
206                 return oops(pcm, errno, "cannot write initial data");
207             pcm->running = 1;
208             return 0;
209         }
210         if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) {
211             pcm->running = 0;
212             if (errno == EPIPE) {
213                     /* we failed to make our window -- try to restart */
214                 pcm->underruns++;
215                 continue;
216             }
217             return oops(pcm, errno, "cannot write stream data");
218         }
219         return 0;
220     }
223 int pcm_read(struct pcm *pcm, void *data, unsigned int count)
225     struct snd_xferi x;
227     if (!(pcm->flags & PCM_IN))
228         return -EINVAL;
230     x.buf = data;
231     x.frames = count / (pcm->config.channels *
232                         pcm_format_to_bits(pcm->config.format) / 8);
234     for (;;) {
235         if (!pcm->running) {
236             if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE))
237                 return oops(pcm, errno, "cannot prepare channel");
238             if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START))
239                 return oops(pcm, errno, "cannot start channel");
240             pcm->running = 1;
241         }
242         if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) {
243             pcm->running = 0;
244             if (errno == EPIPE) {
245                     /* we failed to make our window -- try to restart */
246                 pcm->underruns++;
247                 continue;
248             }
249             return oops(pcm, errno, "cannot read stream data");
250         }
251         return 0;
252     }
255 static struct pcm bad_pcm = {
256     .fd = -1,
257 };
259 int pcm_close(struct pcm *pcm)
261     if (pcm == &bad_pcm)
262         return 0;
264     if (pcm->fd >= 0)
265         close(pcm->fd);
266     pcm->running = 0;
267     pcm->buffer_size = 0;
268     pcm->fd = -1;
269     return 0;
272 struct pcm *pcm_open(unsigned int card, unsigned int device,
273                      unsigned int flags, struct pcm_config *config)
275     struct pcm *pcm;
276     struct snd_pcm_info info;
277     struct snd_pcm_hw_params params;
278     struct snd_pcm_sw_params sparams;
279     char fn[256];
281     pcm = calloc(1, sizeof(struct pcm));
282     if (!pcm || !config)
283         return &bad_pcm; /* TODO: could support default config here */
285     pcm->config = *config;
287     snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
288              flags & PCM_IN ? 'c' : 'p');
290     pcm->flags = flags;
291     pcm->fd = open(fn, O_RDWR);
292     if (pcm->fd < 0) {
293         oops(pcm, errno, "cannot open device '%s'", fn);
294         return pcm;
295     }
297     if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) {
298         oops(pcm, errno, "cannot get info");
299         goto fail;
300     }
302     param_init(&params);
303     param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,
304                    SNDRV_PCM_ACCESS_RW_INTERLEAVED);
305     param_set_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT,
306                    pcm_format_to_alsa(config->format));
307     param_set_mask(&params, SNDRV_PCM_HW_PARAM_SUBFORMAT,
308                    SNDRV_PCM_SUBFORMAT_STD);
309     param_set_min(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size);
310     param_set_int(&params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
311                   pcm_format_to_bits(config->format));
312     param_set_int(&params, SNDRV_PCM_HW_PARAM_FRAME_BITS,
313                   pcm_format_to_bits(config->format) * config->channels);
314     param_set_int(&params, SNDRV_PCM_HW_PARAM_CHANNELS,
315                   config->channels);
316     param_set_int(&params, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count);
317     param_set_int(&params, SNDRV_PCM_HW_PARAM_RATE, config->rate);
319     if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, &params)) {
320         oops(pcm, errno, "cannot set hw params");
321         goto fail;
322     }
324     memset(&sparams, 0, sizeof(sparams));
325     sparams.tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
326     sparams.period_step = 1;
327     sparams.avail_min = 1;
328     sparams.start_threshold = config->period_count * config->period_size;
329     sparams.stop_threshold = config->period_count * config->period_size;
330     sparams.xfer_align = config->period_size / 2; /* needed for old kernels */
331     sparams.silence_size = 0;
332     sparams.silence_threshold = 0;
334     if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) {
335         oops(pcm, errno, "cannot set sw params");
336         goto fail;
337     }
339     pcm->buffer_size = config->period_count * config->period_size;
340     pcm->underruns = 0;
341     return pcm;
343 fail:
344     close(pcm->fd);
345     pcm->fd = -1;
346     return pcm;
349 int pcm_is_ready(struct pcm *pcm)
351     return pcm->fd >= 0;
354 int pcm_start(struct pcm *pcm)
356     if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE) < 0)
357         return oops(pcm, errno, "cannot prepare channel");
358     if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START) < 0)
359         return oops(pcm, errno, "cannot start channel");
361     return 0;
364 int pcm_stop(struct pcm *pcm)
366     if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DROP) < 0)
367         return oops(pcm, errno, "cannot stop channel");
369     return 0;