OSDN Git Service

Replaced snd_pcm_avail() with snd_pcm_hwsync()
[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_timestamp_t trigger_tstamp;
43         snd_pcm_state_t state;
44         int shmid;
45         snd_pcm_uframes_t appl_ptr;
46         snd_pcm_uframes_t hw_ptr;
47         int poll_fd;
48 } snd_pcm_null_t;
49 #endif
50
51 static int snd_pcm_null_close(snd_pcm_t *pcm)
52 {
53         snd_pcm_null_t *null = pcm->private_data;
54         close(null->poll_fd);
55         free(null);
56         return 0;
57 }
58
59 static int snd_pcm_null_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED)
60 {
61         return 0;
62 }
63
64 static int snd_pcm_null_async(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int sig ATTRIBUTE_UNUSED, pid_t pid ATTRIBUTE_UNUSED)
65 {
66         return -ENOSYS;
67 }
68
69 static int snd_pcm_null_info(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_info_t * info)
70 {
71         memset(info, 0, sizeof(*info));
72         info->stream = pcm->stream;
73         info->card = -1;
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         info->subdevices_count = 1;
78         return 0;
79 }
80
81 static int snd_pcm_null_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
82 {
83         snd_pcm_null_t *null = pcm->private_data;
84         return snd_pcm_channel_info_shm(pcm, info, null->shmid);
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         memset(status, 0, sizeof(*status));
91         status->state = null->state;
92         status->trigger_tstamp = null->trigger_tstamp;
93         gettimeofday(&status->tstamp, 0);
94         status->avail = pcm->buffer_size;
95         status->avail_max = status->avail;
96         return 0;
97 }
98
99 static snd_pcm_state_t snd_pcm_null_state(snd_pcm_t *pcm)
100 {
101         snd_pcm_null_t *null = pcm->private_data;
102         return null->state;
103 }
104
105 static int snd_pcm_null_hwsync(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
106 {
107         return 0;
108 }
109
110 static int snd_pcm_null_delay(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_sframes_t *delayp)
111 {
112         *delayp = 0;
113         return 0;
114 }
115
116 static int snd_pcm_null_prepare(snd_pcm_t *pcm)
117 {
118         snd_pcm_null_t *null = pcm->private_data;
119         null->state = SND_PCM_STATE_PREPARED;
120         *pcm->appl.ptr = 0;
121         *pcm->hw.ptr = 0;
122         return 0;
123 }
124
125 static int snd_pcm_null_reset(snd_pcm_t *pcm)
126 {
127         *pcm->appl.ptr = 0;
128         *pcm->hw.ptr = 0;
129         return 0;
130 }
131
132 static int snd_pcm_null_start(snd_pcm_t *pcm)
133 {
134         snd_pcm_null_t *null = pcm->private_data;
135         assert(null->state == SND_PCM_STATE_PREPARED);
136         null->state = SND_PCM_STATE_RUNNING;
137         if (pcm->stream == SND_PCM_STREAM_CAPTURE)
138                 *pcm->hw.ptr = *pcm->appl.ptr + pcm->buffer_size;
139         else
140                 *pcm->hw.ptr = *pcm->appl.ptr;
141         return 0;
142 }
143
144 static int snd_pcm_null_drop(snd_pcm_t *pcm)
145 {
146         snd_pcm_null_t *null = pcm->private_data;
147         assert(null->state != SND_PCM_STATE_OPEN);
148         null->state = SND_PCM_STATE_SETUP;
149         return 0;
150 }
151
152 static int snd_pcm_null_drain(snd_pcm_t *pcm)
153 {
154         snd_pcm_null_t *null = pcm->private_data;
155         assert(null->state != SND_PCM_STATE_OPEN);
156         null->state = SND_PCM_STATE_SETUP;
157         return 0;
158 }
159
160 static int snd_pcm_null_pause(snd_pcm_t *pcm, int enable)
161 {
162         snd_pcm_null_t *null = pcm->private_data;
163         if (enable) {
164                 if (null->state != SND_PCM_STATE_RUNNING)
165                         return -EBADFD;
166         } else if (null->state != SND_PCM_STATE_PAUSED)
167                 return -EBADFD;
168         null->state = SND_PCM_STATE_PAUSED;
169         return 0;
170 }
171
172 static snd_pcm_sframes_t snd_pcm_null_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
173 {
174         snd_pcm_null_t *null = pcm->private_data;
175         switch (null->state) {
176         case SND_PCM_STATE_RUNNING:
177                 snd_pcm_mmap_hw_backward(pcm, frames);
178                 /* Fall through */
179         case SND_PCM_STATE_PREPARED:
180                 snd_pcm_mmap_appl_backward(pcm, frames);
181                 return frames;
182         default:
183                 return -EBADFD;
184         }
185 }
186
187 static int snd_pcm_null_resume(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
188 {
189         return 0;
190 }
191
192 static snd_pcm_sframes_t snd_pcm_null_fwd(snd_pcm_t *pcm, snd_pcm_uframes_t size)
193 {
194         snd_pcm_null_t *null = pcm->private_data;
195         switch (null->state) {
196         case SND_PCM_STATE_RUNNING:
197                 snd_pcm_mmap_hw_forward(pcm, size);
198                 /* Fall through */
199         case SND_PCM_STATE_PREPARED:
200                 snd_pcm_mmap_appl_forward(pcm, size);
201                 return size;
202         default:
203                 return -EBADFD;
204         }
205 }
206
207 static snd_pcm_sframes_t snd_pcm_null_xfer_areas(snd_pcm_t *pcm,
208                                                  const snd_pcm_channel_area_t *areas ATTRIBUTE_UNUSED,
209                                                  snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
210                                                  snd_pcm_uframes_t size)
211 {
212         snd_pcm_mmap_appl_forward(pcm, size);
213         snd_pcm_mmap_hw_forward(pcm, size);
214         return size;
215 }
216
217 static snd_pcm_sframes_t snd_pcm_null_writei(snd_pcm_t *pcm, const void *buffer ATTRIBUTE_UNUSED, snd_pcm_uframes_t size)
218 {
219         return snd_pcm_write_areas(pcm, NULL, 0, size, snd_pcm_null_xfer_areas);
220 }
221
222 static snd_pcm_sframes_t snd_pcm_null_writen(snd_pcm_t *pcm, void **bufs ATTRIBUTE_UNUSED, snd_pcm_uframes_t size)
223 {
224         return snd_pcm_write_areas(pcm, NULL, 0, size, snd_pcm_null_xfer_areas);
225 }
226
227 static snd_pcm_sframes_t snd_pcm_null_readi(snd_pcm_t *pcm, void *buffer ATTRIBUTE_UNUSED, snd_pcm_uframes_t size)
228 {
229         return snd_pcm_read_areas(pcm, NULL, 0, size, snd_pcm_null_xfer_areas);
230 }
231
232 static snd_pcm_sframes_t snd_pcm_null_readn(snd_pcm_t *pcm, void **bufs ATTRIBUTE_UNUSED, snd_pcm_uframes_t size)
233 {
234         return snd_pcm_read_areas(pcm, NULL, 0, size, snd_pcm_null_xfer_areas);
235 }
236
237 static snd_pcm_sframes_t snd_pcm_null_mmap_commit(snd_pcm_t *pcm,
238                                                   snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
239                                                   snd_pcm_uframes_t size)
240 {
241         snd_pcm_sframes_t res;
242         
243         res = snd_pcm_null_fwd(pcm, size);
244         if (res < 0)
245                 return res;
246         return res;
247 }
248
249 static snd_pcm_sframes_t snd_pcm_null_avail_update(snd_pcm_t *pcm)
250 {
251         return pcm->buffer_size;
252 }
253
254 static int snd_pcm_null_hw_refine(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
255 {
256         int err = snd_pcm_hw_refine_soft(pcm, params);
257         params->fifo_size = 0;
258         return err;
259 }
260
261 static int snd_pcm_null_hw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t * params ATTRIBUTE_UNUSED)
262 {
263         return 0;
264 }
265
266 static int snd_pcm_null_hw_free(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
267 {
268         return 0;
269 }
270
271 static int snd_pcm_null_sw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_sw_params_t * params ATTRIBUTE_UNUSED)
272 {
273         return 0;
274 }
275
276 static int snd_pcm_null_mmap(snd_pcm_t *pcm)
277 {
278         snd_pcm_null_t *null = pcm->private_data;
279         if (!(pcm->info & SND_PCM_INFO_MMAP)) {
280                 size_t size = snd_pcm_frames_to_bytes(pcm, pcm->buffer_size);
281                 int id = shmget(IPC_PRIVATE, size, 0666);
282                 if (id < 0) {
283                         SYSERR("shmget failed");
284                         return -errno;
285                 }
286                 null->shmid = id;
287         }
288         return 0;
289 }
290
291 static int snd_pcm_null_munmap(snd_pcm_t *pcm)
292 {
293         snd_pcm_null_t *null = pcm->private_data;
294         if (shmctl(null->shmid, IPC_RMID, 0) < 0) {
295                 SYSERR("shmctl IPC_RMID failed");
296                         return -errno;
297         }
298         return 0;
299 }
300
301 static void snd_pcm_null_dump(snd_pcm_t *pcm, snd_output_t *out)
302 {
303         snd_output_printf(out, "Null PCM\n");
304         if (pcm->setup) {
305                 snd_output_printf(out, "Its setup is:\n");
306                 snd_pcm_dump_setup(pcm, out);
307         }
308 }
309
310 static snd_pcm_ops_t snd_pcm_null_ops = {
311         close: snd_pcm_null_close,
312         info: snd_pcm_null_info,
313         hw_refine: snd_pcm_null_hw_refine,
314         hw_params: snd_pcm_null_hw_params,
315         hw_free: snd_pcm_null_hw_free,
316         sw_params: snd_pcm_null_sw_params,
317         channel_info: snd_pcm_null_channel_info,
318         dump: snd_pcm_null_dump,
319         nonblock: snd_pcm_null_nonblock,
320         async: snd_pcm_null_async,
321         mmap: snd_pcm_null_mmap,
322         munmap: snd_pcm_null_munmap,
323 };
324
325 static snd_pcm_fast_ops_t snd_pcm_null_fast_ops = {
326         status: snd_pcm_null_status,
327         state: snd_pcm_null_state,
328         hwsync: snd_pcm_null_hwsync,
329         delay: snd_pcm_null_delay,
330         prepare: snd_pcm_null_prepare,
331         reset: snd_pcm_null_reset,
332         start: snd_pcm_null_start,
333         drop: snd_pcm_null_drop,
334         drain: snd_pcm_null_drain,
335         pause: snd_pcm_null_pause,
336         rewind: snd_pcm_null_rewind,
337         resume: snd_pcm_null_resume,
338         writei: snd_pcm_null_writei,
339         writen: snd_pcm_null_writen,
340         readi: snd_pcm_null_readi,
341         readn: snd_pcm_null_readn,
342         avail_update: snd_pcm_null_avail_update,
343         mmap_commit: snd_pcm_null_mmap_commit,
344 };
345
346 /**
347  * \brief Creates a new null PCM
348  * \param pcmp Returns created PCM handle
349  * \param name Name of PCM
350  * \param stream Stream type
351  * \param mode Stream mode
352  * \retval zero on success otherwise a negative error code
353  * \warning Using of this function might be dangerous in the sense
354  *          of compatibility reasons. The prototype might be freely
355  *          changed in future.
356  */
357 int snd_pcm_null_open(snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode)
358 {
359         snd_pcm_t *pcm;
360         snd_pcm_null_t *null;
361         int fd;
362         int err;
363         assert(pcmp);
364         if (stream == SND_PCM_STREAM_PLAYBACK) {
365                 fd = open("/dev/null", O_WRONLY);
366                 if (fd < 0) {
367                         SYSERR("Cannot open /dev/null");
368                         return -errno;
369                 }
370         } else {
371                 fd = open("/dev/full", O_RDONLY);
372                 if (fd < 0) {
373                         SYSERR("Cannot open /dev/full");
374                         return -errno;
375                 }
376         }
377         null = calloc(1, sizeof(snd_pcm_null_t));
378         if (!null) {
379                 close(fd);
380                 return -ENOMEM;
381         }
382         null->poll_fd = fd;
383         null->state = SND_PCM_STATE_OPEN;
384         
385         err = snd_pcm_new(&pcm, SND_PCM_TYPE_NULL, name, stream, mode);
386         if (err < 0) {
387                 close(fd);
388                 free(null);
389                 return err;
390         }
391         pcm->ops = &snd_pcm_null_ops;
392         pcm->fast_ops = &snd_pcm_null_fast_ops;
393         pcm->private_data = null;
394         pcm->poll_fd = fd;
395         snd_pcm_set_hw_ptr(pcm, &null->hw_ptr, -1, 0);
396         snd_pcm_set_appl_ptr(pcm, &null->appl_ptr, -1, 0);
397         *pcmp = pcm;
398
399         return 0;
400 }
401
402 /*! \page pcm_plugins
403
404 \section pcm_plugins_null Plugin: Null
405
406 This plugin discards contents of a PCM stream or creates a stream with zero
407 samples.
408
409 Note: This implementation uses devices /dev/null (playback, must be writable)
410 and /dev/full (capture, must be readable).
411
412 \code
413 pcm.name {
414         type null               # Null PCM
415 }
416 \endcode
417
418 \subsection pcm_plugins_null_funcref Function reference
419
420 <UL>
421   <LI>snd_pcm_null_open()
422   <LI>_snd_pcm_null_open()
423 </UL>
424
425 */
426
427 /**
428  * \brief Creates a new Null PCM
429  * \param pcmp Returns created PCM handle
430  * \param name Name of PCM
431  * \param root Root configuration node
432  * \param conf Configuration node with Null PCM description
433  * \param stream Stream type
434  * \param mode Stream mode
435  * \retval zero on success otherwise a negative error code
436  * \warning Using of this function might be dangerous in the sense
437  *          of compatibility reasons. The prototype might be freely
438  *          changed in future.
439  */
440 int _snd_pcm_null_open(snd_pcm_t **pcmp, const char *name,
441                        snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf, 
442                        snd_pcm_stream_t stream, int mode)
443 {
444         snd_config_iterator_t i, next;
445         snd_config_for_each(i, next, conf) {
446                 snd_config_t *n = snd_config_iterator_entry(i);
447                 const char *id;
448                 if (snd_config_get_id(n, &id) < 0)
449                         continue;
450                 if (snd_pcm_conf_generic_id(id))
451                         continue;
452                 SNDERR("Unknown field %s", id);
453                 return -EINVAL;
454         }
455         return snd_pcm_null_open(pcmp, name, stream, mode);
456 }
457 #ifndef DOC_HIDDEN
458 SND_DLSYM_BUILD_VERSION(_snd_pcm_null_open, SND_PCM_DLSYM_VERSION);
459 #endif