OSDN Git Service

fixed dmix crashes by Clemens Ladisch <clemens@ladisch.de>:
[android-x86/external-alsa-lib.git] / src / pcm / pcm_null.c
1 /**
2  * \file pcm/pcm_null.c
3  * \ingroup PCM_Plugins
4  * \brief PCM Null Plugin Interface
5  * \author Abramo Bagnara <abramo@alsa-project.org>
6  * \date 2000-2001
7  */
8 /*
9  *  PCM - Null plugin
10  *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
11  *
12  *
13  *   This library is free software; you can redistribute it and/or modify
14  *   it under the terms of the GNU Lesser General Public License as
15  *   published by the Free Software Foundation; either version 2.1 of
16  *   the License, or (at your option) any later version.
17  *
18  *   This program is distributed in the hope that it will be useful,
19  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
20  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  *   GNU Lesser General Public License for more details.
22  *
23  *   You should have received a copy of the GNU Lesser General Public
24  *   License along with this library; if not, write to the Free Software
25  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
26  *
27  */
28   
29 #include <byteswap.h>
30 #include <limits.h>
31 #include <sys/shm.h>
32 #include "pcm_local.h"
33 #include "pcm_plugin.h"
34
35 #ifndef PIC
36 /* entry for static linking */
37 const char *_snd_module_pcm_null = "";
38 #endif
39
40 #ifndef DOC_HIDDEN
41 typedef struct {
42         snd_htimestamp_t trigger_tstamp;
43         snd_pcm_state_t state;
44         snd_pcm_uframes_t appl_ptr;
45         snd_pcm_uframes_t hw_ptr;
46         int poll_fd;
47 } snd_pcm_null_t;
48 #endif
49
50 static int snd_pcm_null_close(snd_pcm_t *pcm)
51 {
52         snd_pcm_null_t *null = pcm->private_data;
53         close(null->poll_fd);
54         free(null);
55         return 0;
56 }
57
58 static int snd_pcm_null_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED)
59 {
60         return 0;
61 }
62
63 static int snd_pcm_null_async(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int sig ATTRIBUTE_UNUSED, pid_t pid ATTRIBUTE_UNUSED)
64 {
65         return -ENOSYS;
66 }
67
68 static int snd_pcm_null_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
69 {
70         memset(info, 0, sizeof(*info));
71         info->stream = pcm->stream;
72         info->card = -1;
73         if (pcm->name) {
74                 strncpy(info->id, pcm->name, sizeof(info->id));
75                 strncpy(info->name, pcm->name, sizeof(info->name));
76                 strncpy(info->subname, pcm->name, sizeof(info->subname));
77         }
78         info->subdevices_count = 1;
79         return 0;
80 }
81
82 static int snd_pcm_null_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
83 {
84         return snd_pcm_channel_info_shm(pcm, info, -1);
85 }
86
87 static int snd_pcm_null_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
88 {
89         snd_pcm_null_t *null = pcm->private_data;
90         struct timeval tv;
91         memset(status, 0, sizeof(*status));
92         status->state = null->state;
93         status->trigger_tstamp = null->trigger_tstamp;
94         gettimeofday(&tv, 0);
95         status->tstamp.tv_sec = tv.tv_sec;
96         status->tstamp.tv_nsec = tv.tv_usec * 1000L;
97         status->avail = pcm->buffer_size;
98         status->avail_max = status->avail;
99         return 0;
100 }
101
102 static snd_pcm_state_t snd_pcm_null_state(snd_pcm_t *pcm)
103 {
104         snd_pcm_null_t *null = pcm->private_data;
105         return null->state;
106 }
107
108 static int snd_pcm_null_hwsync(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
109 {
110         return 0;
111 }
112
113 static int snd_pcm_null_delay(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_sframes_t *delayp)
114 {
115         *delayp = 0;
116         return 0;
117 }
118
119 static int snd_pcm_null_prepare(snd_pcm_t *pcm)
120 {
121         snd_pcm_null_t *null = pcm->private_data;
122         null->state = SND_PCM_STATE_PREPARED;
123         *pcm->appl.ptr = 0;
124         *pcm->hw.ptr = 0;
125         return 0;
126 }
127
128 static int snd_pcm_null_reset(snd_pcm_t *pcm)
129 {
130         *pcm->appl.ptr = 0;
131         *pcm->hw.ptr = 0;
132         return 0;
133 }
134
135 static int snd_pcm_null_start(snd_pcm_t *pcm)
136 {
137         snd_pcm_null_t *null = pcm->private_data;
138         assert(null->state == SND_PCM_STATE_PREPARED);
139         null->state = SND_PCM_STATE_RUNNING;
140         if (pcm->stream == SND_PCM_STREAM_CAPTURE)
141                 *pcm->hw.ptr = *pcm->appl.ptr + pcm->buffer_size;
142         else
143                 *pcm->hw.ptr = *pcm->appl.ptr;
144         return 0;
145 }
146
147 static int snd_pcm_null_drop(snd_pcm_t *pcm)
148 {
149         snd_pcm_null_t *null = pcm->private_data;
150         assert(null->state != SND_PCM_STATE_OPEN);
151         null->state = SND_PCM_STATE_SETUP;
152         return 0;
153 }
154
155 static int snd_pcm_null_drain(snd_pcm_t *pcm)
156 {
157         snd_pcm_null_t *null = pcm->private_data;
158         assert(null->state != SND_PCM_STATE_OPEN);
159         null->state = SND_PCM_STATE_SETUP;
160         return 0;
161 }
162
163 static int snd_pcm_null_pause(snd_pcm_t *pcm, int enable)
164 {
165         snd_pcm_null_t *null = pcm->private_data;
166         if (enable) {
167                 if (null->state != SND_PCM_STATE_RUNNING)
168                         return -EBADFD;
169                 null->state = SND_PCM_STATE_PAUSED;
170         } else {
171                 if (null->state != SND_PCM_STATE_PAUSED)
172                         return -EBADFD;
173                 null->state = SND_PCM_STATE_RUNNING;
174         }
175         return 0;
176 }
177
178 static snd_pcm_sframes_t snd_pcm_null_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
179 {
180         snd_pcm_null_t *null = pcm->private_data;
181         switch (null->state) {
182         case SND_PCM_STATE_RUNNING:
183                 snd_pcm_mmap_hw_backward(pcm, frames);
184                 /* Fall through */
185         case SND_PCM_STATE_PREPARED:
186                 snd_pcm_mmap_appl_backward(pcm, frames);
187                 return frames;
188         default:
189                 return -EBADFD;
190         }
191 }
192
193 static snd_pcm_sframes_t snd_pcm_null_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
194 {
195         snd_pcm_null_t *null = pcm->private_data;
196         switch (null->state) {
197         case SND_PCM_STATE_RUNNING:
198                 snd_pcm_mmap_hw_forward(pcm, frames);
199                 /* Fall through */
200         case SND_PCM_STATE_PREPARED:
201                 snd_pcm_mmap_appl_forward(pcm, frames);
202                 return frames;
203         default:
204                 return -EBADFD;
205         }
206 }
207
208 static int snd_pcm_null_resume(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
209 {
210         return 0;
211 }
212
213 static snd_pcm_sframes_t snd_pcm_null_fwd(snd_pcm_t *pcm, snd_pcm_uframes_t size)
214 {
215         snd_pcm_null_t *null = pcm->private_data;
216         switch (null->state) {
217         case SND_PCM_STATE_RUNNING:
218                 snd_pcm_mmap_hw_forward(pcm, size);
219                 /* Fall through */
220         case SND_PCM_STATE_PREPARED:
221                 snd_pcm_mmap_appl_forward(pcm, size);
222                 return size;
223         default:
224                 return -EBADFD;
225         }
226 }
227
228 static snd_pcm_sframes_t snd_pcm_null_xfer_areas(snd_pcm_t *pcm,
229                                                  const snd_pcm_channel_area_t *areas ATTRIBUTE_UNUSED,
230                                                  snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
231                                                  snd_pcm_uframes_t size)
232 {
233         snd_pcm_mmap_appl_forward(pcm, size);
234         snd_pcm_mmap_hw_forward(pcm, size);
235         return size;
236 }
237
238 static snd_pcm_sframes_t snd_pcm_null_writei(snd_pcm_t *pcm, const void *buffer ATTRIBUTE_UNUSED, snd_pcm_uframes_t size)
239 {
240         return snd_pcm_write_areas(pcm, NULL, 0, size, snd_pcm_null_xfer_areas);
241 }
242
243 static snd_pcm_sframes_t snd_pcm_null_writen(snd_pcm_t *pcm, void **bufs ATTRIBUTE_UNUSED, snd_pcm_uframes_t size)
244 {
245         return snd_pcm_write_areas(pcm, NULL, 0, size, snd_pcm_null_xfer_areas);
246 }
247
248 static snd_pcm_sframes_t snd_pcm_null_readi(snd_pcm_t *pcm, void *buffer ATTRIBUTE_UNUSED, snd_pcm_uframes_t size)
249 {
250         return snd_pcm_read_areas(pcm, NULL, 0, size, snd_pcm_null_xfer_areas);
251 }
252
253 static snd_pcm_sframes_t snd_pcm_null_readn(snd_pcm_t *pcm, void **bufs ATTRIBUTE_UNUSED, snd_pcm_uframes_t size)
254 {
255         return snd_pcm_read_areas(pcm, NULL, 0, size, snd_pcm_null_xfer_areas);
256 }
257
258 static snd_pcm_sframes_t snd_pcm_null_mmap_commit(snd_pcm_t *pcm,
259                                                   snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
260                                                   snd_pcm_uframes_t size)
261 {
262         snd_pcm_sframes_t res;
263         
264         res = snd_pcm_null_fwd(pcm, size);
265         if (res < 0)
266                 return res;
267         return res;
268 }
269
270 static snd_pcm_sframes_t snd_pcm_null_avail_update(snd_pcm_t *pcm)
271 {
272         return pcm->buffer_size;
273 }
274
275 static int snd_pcm_null_hw_refine(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
276 {
277         int err = snd_pcm_hw_refine_soft(pcm, params);
278         params->fifo_size = 0;
279         return err;
280 }
281
282 static int snd_pcm_null_hw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t * params ATTRIBUTE_UNUSED)
283 {
284         return 0;
285 }
286
287 static int snd_pcm_null_hw_free(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
288 {
289         return 0;
290 }
291
292 static int snd_pcm_null_sw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_sw_params_t * params ATTRIBUTE_UNUSED)
293 {
294         return 0;
295 }
296
297 static int snd_pcm_null_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
298 {
299         return 0;
300 }
301
302 static int snd_pcm_null_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
303 {
304         return 0;
305 }
306
307 static void snd_pcm_null_dump(snd_pcm_t *pcm, snd_output_t *out)
308 {
309         snd_output_printf(out, "Null PCM\n");
310         if (pcm->setup) {
311                 snd_output_printf(out, "Its setup is:\n");
312                 snd_pcm_dump_setup(pcm, out);
313         }
314 }
315
316 static snd_pcm_ops_t snd_pcm_null_ops = {
317         .close = snd_pcm_null_close,
318         .info = snd_pcm_null_info,
319         .hw_refine = snd_pcm_null_hw_refine,
320         .hw_params = snd_pcm_null_hw_params,
321         .hw_free = snd_pcm_null_hw_free,
322         .sw_params = snd_pcm_null_sw_params,
323         .channel_info = snd_pcm_null_channel_info,
324         .dump = snd_pcm_null_dump,
325         .nonblock = snd_pcm_null_nonblock,
326         .async = snd_pcm_null_async,
327         .mmap = snd_pcm_null_mmap,
328         .munmap = snd_pcm_null_munmap,
329 };
330
331 static snd_pcm_fast_ops_t snd_pcm_null_fast_ops = {
332         .status = snd_pcm_null_status,
333         .state = snd_pcm_null_state,
334         .hwsync = snd_pcm_null_hwsync,
335         .delay = snd_pcm_null_delay,
336         .prepare = snd_pcm_null_prepare,
337         .reset = snd_pcm_null_reset,
338         .start = snd_pcm_null_start,
339         .drop = snd_pcm_null_drop,
340         .drain = snd_pcm_null_drain,
341         .pause = snd_pcm_null_pause,
342         .rewind = snd_pcm_null_rewind,
343         .forward = snd_pcm_null_forward,
344         .resume = snd_pcm_null_resume,
345         .writei = snd_pcm_null_writei,
346         .writen = snd_pcm_null_writen,
347         .readi = snd_pcm_null_readi,
348         .readn = snd_pcm_null_readn,
349         .avail_update = snd_pcm_null_avail_update,
350         .mmap_commit = snd_pcm_null_mmap_commit,
351 };
352
353 /**
354  * \brief Creates a new null PCM
355  * \param pcmp Returns created PCM handle
356  * \param name Name of PCM
357  * \param stream Stream type
358  * \param mode Stream mode
359  * \retval zero on success otherwise a negative error code
360  * \warning Using of this function might be dangerous in the sense
361  *          of compatibility reasons. The prototype might be freely
362  *          changed in future.
363  */
364 int snd_pcm_null_open(snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode)
365 {
366         snd_pcm_t *pcm;
367         snd_pcm_null_t *null;
368         int fd;
369         int err;
370         assert(pcmp);
371         if (stream == SND_PCM_STREAM_PLAYBACK) {
372                 fd = open("/dev/null", O_WRONLY);
373                 if (fd < 0) {
374                         SYSERR("Cannot open /dev/null");
375                         return -errno;
376                 }
377         } else {
378                 fd = open("/dev/full", O_RDONLY);
379                 if (fd < 0) {
380                         SYSERR("Cannot open /dev/full");
381                         return -errno;
382                 }
383         }
384         null = calloc(1, sizeof(snd_pcm_null_t));
385         if (!null) {
386                 close(fd);
387                 return -ENOMEM;
388         }
389         null->poll_fd = fd;
390         null->state = SND_PCM_STATE_OPEN;
391         
392         err = snd_pcm_new(&pcm, SND_PCM_TYPE_NULL, name, stream, mode);
393         if (err < 0) {
394                 close(fd);
395                 free(null);
396                 return err;
397         }
398         pcm->ops = &snd_pcm_null_ops;
399         pcm->fast_ops = &snd_pcm_null_fast_ops;
400         pcm->private_data = null;
401         pcm->poll_fd = fd;
402         pcm->poll_events = stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN;
403         snd_pcm_set_hw_ptr(pcm, &null->hw_ptr, -1, 0);
404         snd_pcm_set_appl_ptr(pcm, &null->appl_ptr, -1, 0);
405         *pcmp = pcm;
406
407         return 0;
408 }
409
410 /*! \page pcm_plugins
411
412 \section pcm_plugins_null Plugin: Null
413
414 This plugin discards contents of a PCM stream or creates a stream with zero
415 samples.
416
417 Note: This implementation uses devices /dev/null (playback, must be writable)
418 and /dev/full (capture, must be readable).
419
420 \code
421 pcm.name {
422         type null               # Null PCM
423 }
424 \endcode
425
426 \subsection pcm_plugins_null_funcref Function reference
427
428 <UL>
429   <LI>snd_pcm_null_open()
430   <LI>_snd_pcm_null_open()
431 </UL>
432
433 */
434
435 /**
436  * \brief Creates a new Null PCM
437  * \param pcmp Returns created PCM handle
438  * \param name Name of PCM
439  * \param root Root configuration node
440  * \param conf Configuration node with Null PCM description
441  * \param stream Stream type
442  * \param mode Stream mode
443  * \retval zero on success otherwise a negative error code
444  * \warning Using of this function might be dangerous in the sense
445  *          of compatibility reasons. The prototype might be freely
446  *          changed in future.
447  */
448 int _snd_pcm_null_open(snd_pcm_t **pcmp, const char *name,
449                        snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf, 
450                        snd_pcm_stream_t stream, int mode)
451 {
452         snd_config_iterator_t i, next;
453         snd_config_for_each(i, next, conf) {
454                 snd_config_t *n = snd_config_iterator_entry(i);
455                 const char *id;
456                 if (snd_config_get_id(n, &id) < 0)
457                         continue;
458                 if (snd_pcm_conf_generic_id(id))
459                         continue;
460                 SNDERR("Unknown field %s", id);
461                 return -EINVAL;
462         }
463         return snd_pcm_null_open(pcmp, name, stream, mode);
464 }
465 #ifndef DOC_HIDDEN
466 SND_DLSYM_BUILD_VERSION(_snd_pcm_null_open, SND_PCM_DLSYM_VERSION);
467 #endif