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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 *
27 */
28
29 #include "bswap.h"
30 #include <limits.h>
31 #include "pcm_local.h"
32 #include "pcm_plugin.h"
33
34 #ifndef PIC
35 /* entry for static linking */
36 const char *_snd_module_pcm_null = "";
37 #endif
38
39 #ifndef DOC_HIDDEN
40 typedef struct {
41 snd_htimestamp_t trigger_tstamp;
42 snd_pcm_state_t state;
43 snd_pcm_uframes_t appl_ptr;
44 snd_pcm_uframes_t hw_ptr;
45 int poll_fd;
46 snd_pcm_chmap_query_t **chmap;
47 } snd_pcm_null_t;
48 #endif
49
snd_pcm_null_close(snd_pcm_t * pcm)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
snd_pcm_null_nonblock(snd_pcm_t * pcm ATTRIBUTE_UNUSED,int nonblock ATTRIBUTE_UNUSED)58 static int snd_pcm_null_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED)
59 {
60 return 0;
61 }
62
snd_pcm_null_async(snd_pcm_t * pcm ATTRIBUTE_UNUSED,int sig ATTRIBUTE_UNUSED,pid_t pid ATTRIBUTE_UNUSED)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
snd_pcm_null_info(snd_pcm_t * pcm,snd_pcm_info_t * info)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 snd_strlcpy((char *)info->id, pcm->name, sizeof(info->id));
75 snd_strlcpy((char *)info->name, pcm->name, sizeof(info->name));
76 snd_strlcpy((char *)info->subname, pcm->name, sizeof(info->subname));
77 }
78 info->subdevices_count = 1;
79 return 0;
80 }
81
snd_pcm_null_avail_update(snd_pcm_t * pcm)82 static snd_pcm_sframes_t snd_pcm_null_avail_update(snd_pcm_t *pcm)
83 {
84 snd_pcm_null_t *null = pcm->private_data;
85 if (null->state == SND_PCM_STATE_PREPARED) {
86 /* it is required to return the correct avail count for */
87 /* the prepared stream, otherwise the start is not called */
88 return snd_pcm_mmap_avail(pcm);
89 }
90 return pcm->buffer_size;
91 }
92
snd_pcm_null_status(snd_pcm_t * pcm,snd_pcm_status_t * status)93 static int snd_pcm_null_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
94 {
95 snd_pcm_null_t *null = pcm->private_data;
96 memset(status, 0, sizeof(*status));
97 status->state = null->state;
98 status->trigger_tstamp = null->trigger_tstamp;
99 status->appl_ptr = *pcm->appl.ptr;
100 status->hw_ptr = *pcm->hw.ptr;
101 gettimestamp(&status->tstamp, pcm->tstamp_type);
102 status->avail = snd_pcm_null_avail_update(pcm);
103 status->avail_max = pcm->buffer_size;
104 return 0;
105 }
106
snd_pcm_null_state(snd_pcm_t * pcm)107 static snd_pcm_state_t snd_pcm_null_state(snd_pcm_t *pcm)
108 {
109 snd_pcm_null_t *null = pcm->private_data;
110 return null->state;
111 }
112
snd_pcm_null_hwsync(snd_pcm_t * pcm ATTRIBUTE_UNUSED)113 static int snd_pcm_null_hwsync(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
114 {
115 return 0;
116 }
117
snd_pcm_null_delay(snd_pcm_t * pcm ATTRIBUTE_UNUSED,snd_pcm_sframes_t * delayp)118 static int snd_pcm_null_delay(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_sframes_t *delayp)
119 {
120 *delayp = 0;
121 return 0;
122 }
123
snd_pcm_null_reset(snd_pcm_t * pcm)124 static int snd_pcm_null_reset(snd_pcm_t *pcm)
125 {
126 *pcm->appl.ptr = 0;
127 *pcm->hw.ptr = 0;
128 return 0;
129 }
130
snd_pcm_null_prepare(snd_pcm_t * pcm)131 static int snd_pcm_null_prepare(snd_pcm_t *pcm)
132 {
133 snd_pcm_null_t *null = pcm->private_data;
134 null->state = SND_PCM_STATE_PREPARED;
135 return snd_pcm_null_reset(pcm);
136 }
137
snd_pcm_null_start(snd_pcm_t * pcm)138 static int snd_pcm_null_start(snd_pcm_t *pcm)
139 {
140 snd_pcm_null_t *null = pcm->private_data;
141 assert(null->state == SND_PCM_STATE_PREPARED);
142 null->state = SND_PCM_STATE_RUNNING;
143 if (pcm->stream == SND_PCM_STREAM_CAPTURE)
144 *pcm->hw.ptr = *pcm->appl.ptr + pcm->buffer_size;
145 else
146 *pcm->hw.ptr = *pcm->appl.ptr;
147 return 0;
148 }
149
snd_pcm_null_drop(snd_pcm_t * pcm)150 static int snd_pcm_null_drop(snd_pcm_t *pcm)
151 {
152 snd_pcm_null_t *null = pcm->private_data;
153 assert(null->state != SND_PCM_STATE_OPEN);
154 null->state = SND_PCM_STATE_SETUP;
155 return 0;
156 }
157
snd_pcm_null_drain(snd_pcm_t * pcm)158 static int snd_pcm_null_drain(snd_pcm_t *pcm)
159 {
160 snd_pcm_null_t *null = pcm->private_data;
161 assert(null->state != SND_PCM_STATE_OPEN);
162 null->state = SND_PCM_STATE_SETUP;
163 return 0;
164 }
165
snd_pcm_null_pause(snd_pcm_t * pcm,int enable)166 static int snd_pcm_null_pause(snd_pcm_t *pcm, int enable)
167 {
168 snd_pcm_null_t *null = pcm->private_data;
169 if (enable) {
170 if (null->state != SND_PCM_STATE_RUNNING)
171 return -EBADFD;
172 null->state = SND_PCM_STATE_PAUSED;
173 } else {
174 if (null->state != SND_PCM_STATE_PAUSED)
175 return -EBADFD;
176 null->state = SND_PCM_STATE_RUNNING;
177 }
178 return 0;
179 }
180
snd_pcm_null_rewindable(snd_pcm_t * pcm)181 static snd_pcm_sframes_t snd_pcm_null_rewindable(snd_pcm_t *pcm)
182 {
183 return pcm->buffer_size;
184 }
185
snd_pcm_null_forwardable(snd_pcm_t * pcm ATTRIBUTE_UNUSED)186 static snd_pcm_sframes_t snd_pcm_null_forwardable(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
187 {
188 return 0;
189 }
190
191
snd_pcm_null_rewind(snd_pcm_t * pcm,snd_pcm_uframes_t frames)192 static snd_pcm_sframes_t snd_pcm_null_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
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_backward(pcm, frames);
198 /* Fall through */
199 case SND_PCM_STATE_PREPARED:
200 snd_pcm_mmap_appl_backward(pcm, frames);
201 return frames;
202 default:
203 return -EBADFD;
204 }
205 }
206
snd_pcm_null_forward(snd_pcm_t * pcm,snd_pcm_uframes_t frames)207 static snd_pcm_sframes_t snd_pcm_null_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
208 {
209 snd_pcm_null_t *null = pcm->private_data;
210 switch (null->state) {
211 case SND_PCM_STATE_RUNNING:
212 snd_pcm_mmap_hw_forward(pcm, frames);
213 /* Fall through */
214 case SND_PCM_STATE_PREPARED:
215 snd_pcm_mmap_appl_forward(pcm, frames);
216 return frames;
217 default:
218 return -EBADFD;
219 }
220 }
221
snd_pcm_null_resume(snd_pcm_t * pcm ATTRIBUTE_UNUSED)222 static int snd_pcm_null_resume(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
223 {
224 return 0;
225 }
226
snd_pcm_null_xfer_areas(snd_pcm_t * pcm,const snd_pcm_channel_area_t * areas ATTRIBUTE_UNUSED,snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,snd_pcm_uframes_t size)227 static snd_pcm_sframes_t snd_pcm_null_xfer_areas(snd_pcm_t *pcm,
228 const snd_pcm_channel_area_t *areas ATTRIBUTE_UNUSED,
229 snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
230 snd_pcm_uframes_t size)
231 {
232 snd_pcm_mmap_appl_forward(pcm, size);
233 snd_pcm_mmap_hw_forward(pcm, size);
234 return size;
235 }
236
snd_pcm_null_writei(snd_pcm_t * pcm,const void * buffer ATTRIBUTE_UNUSED,snd_pcm_uframes_t size)237 static snd_pcm_sframes_t snd_pcm_null_writei(snd_pcm_t *pcm, const void *buffer ATTRIBUTE_UNUSED, snd_pcm_uframes_t size)
238 {
239 return snd_pcm_write_areas(pcm, NULL, 0, size, snd_pcm_null_xfer_areas);
240 }
241
snd_pcm_null_writen(snd_pcm_t * pcm,void ** bufs ATTRIBUTE_UNUSED,snd_pcm_uframes_t size)242 static snd_pcm_sframes_t snd_pcm_null_writen(snd_pcm_t *pcm, void **bufs ATTRIBUTE_UNUSED, snd_pcm_uframes_t size)
243 {
244 return snd_pcm_write_areas(pcm, NULL, 0, size, snd_pcm_null_xfer_areas);
245 }
246
snd_pcm_null_readi(snd_pcm_t * pcm,void * buffer ATTRIBUTE_UNUSED,snd_pcm_uframes_t size)247 static snd_pcm_sframes_t snd_pcm_null_readi(snd_pcm_t *pcm, void *buffer ATTRIBUTE_UNUSED, snd_pcm_uframes_t size)
248 {
249 return snd_pcm_read_areas(pcm, NULL, 0, size, snd_pcm_null_xfer_areas);
250 }
251
snd_pcm_null_readn(snd_pcm_t * pcm,void ** bufs ATTRIBUTE_UNUSED,snd_pcm_uframes_t size)252 static snd_pcm_sframes_t snd_pcm_null_readn(snd_pcm_t *pcm, void **bufs ATTRIBUTE_UNUSED, snd_pcm_uframes_t size)
253 {
254 return snd_pcm_read_areas(pcm, NULL, 0, size, snd_pcm_null_xfer_areas);
255 }
256
snd_pcm_null_mmap_commit(snd_pcm_t * pcm,snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,snd_pcm_uframes_t size)257 static snd_pcm_sframes_t snd_pcm_null_mmap_commit(snd_pcm_t *pcm,
258 snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
259 snd_pcm_uframes_t size)
260 {
261 return snd_pcm_null_forward(pcm, size);
262 }
263
snd_pcm_null_hw_refine(snd_pcm_t * pcm ATTRIBUTE_UNUSED,snd_pcm_hw_params_t * params)264 static int snd_pcm_null_hw_refine(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
265 {
266 int err;
267
268 /* Do not return a period size of 0 because for example portaudio cannot
269 * handle it.
270 */
271 err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_PERIOD_SIZE, 1,
272 0);
273 if (err < 0)
274 return err;
275
276 err = snd_pcm_hw_refine_soft(pcm, params);
277 params->info = SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID |
278 SND_PCM_INFO_RESUME | SND_PCM_INFO_PAUSE;
279 params->fifo_size = 0;
280 return err;
281 }
282
snd_pcm_null_hw_params(snd_pcm_t * pcm ATTRIBUTE_UNUSED,snd_pcm_hw_params_t * params ATTRIBUTE_UNUSED)283 static int snd_pcm_null_hw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t * params ATTRIBUTE_UNUSED)
284 {
285 return 0;
286 }
287
snd_pcm_null_hw_free(snd_pcm_t * pcm ATTRIBUTE_UNUSED)288 static int snd_pcm_null_hw_free(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
289 {
290 return 0;
291 }
292
snd_pcm_null_sw_params(snd_pcm_t * pcm ATTRIBUTE_UNUSED,snd_pcm_sw_params_t * params ATTRIBUTE_UNUSED)293 static int snd_pcm_null_sw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_sw_params_t * params ATTRIBUTE_UNUSED)
294 {
295 return 0;
296 }
297
snd_pcm_null_query_chmaps(snd_pcm_t * pcm)298 static snd_pcm_chmap_query_t **snd_pcm_null_query_chmaps(snd_pcm_t *pcm)
299 {
300 snd_pcm_null_t *null = pcm->private_data;
301
302 if (null->chmap)
303 return _snd_pcm_copy_chmap_query(null->chmap);
304 return NULL;
305 }
306
snd_pcm_null_get_chmap(snd_pcm_t * pcm)307 static snd_pcm_chmap_t *snd_pcm_null_get_chmap(snd_pcm_t *pcm)
308 {
309 snd_pcm_null_t *null = pcm->private_data;
310
311 if (null->chmap)
312 return _snd_pcm_choose_fixed_chmap(pcm, null->chmap);
313 return NULL;
314 }
315
snd_pcm_null_dump(snd_pcm_t * pcm,snd_output_t * out)316 static void snd_pcm_null_dump(snd_pcm_t *pcm, snd_output_t *out)
317 {
318 snd_output_printf(out, "Null PCM\n");
319 if (pcm->setup) {
320 snd_output_printf(out, "Its setup is:\n");
321 snd_pcm_dump_setup(pcm, out);
322 }
323 }
324
325 static const snd_pcm_ops_t snd_pcm_null_ops = {
326 .close = snd_pcm_null_close,
327 .info = snd_pcm_null_info,
328 .hw_refine = snd_pcm_null_hw_refine,
329 .hw_params = snd_pcm_null_hw_params,
330 .hw_free = snd_pcm_null_hw_free,
331 .sw_params = snd_pcm_null_sw_params,
332 .channel_info = snd_pcm_generic_channel_info,
333 .dump = snd_pcm_null_dump,
334 .nonblock = snd_pcm_null_nonblock,
335 .async = snd_pcm_null_async,
336 .mmap = snd_pcm_generic_mmap,
337 .munmap = snd_pcm_generic_munmap,
338 .query_chmaps = snd_pcm_null_query_chmaps,
339 .get_chmap = snd_pcm_null_get_chmap,
340 .set_chmap = NULL,
341 };
342
343 static const snd_pcm_fast_ops_t snd_pcm_null_fast_ops = {
344 .status = snd_pcm_null_status,
345 .state = snd_pcm_null_state,
346 .hwsync = snd_pcm_null_hwsync,
347 .delay = snd_pcm_null_delay,
348 .prepare = snd_pcm_null_prepare,
349 .reset = snd_pcm_null_reset,
350 .start = snd_pcm_null_start,
351 .drop = snd_pcm_null_drop,
352 .drain = snd_pcm_null_drain,
353 .pause = snd_pcm_null_pause,
354 .rewindable = snd_pcm_null_rewindable,
355 .rewind = snd_pcm_null_rewind,
356 .forwardable = snd_pcm_null_forwardable,
357 .forward = snd_pcm_null_forward,
358 .resume = snd_pcm_null_resume,
359 .writei = snd_pcm_null_writei,
360 .writen = snd_pcm_null_writen,
361 .readi = snd_pcm_null_readi,
362 .readn = snd_pcm_null_readn,
363 .avail_update = snd_pcm_null_avail_update,
364 .mmap_commit = snd_pcm_null_mmap_commit,
365 .htimestamp = snd_pcm_generic_real_htimestamp,
366 };
367
368 /**
369 * \brief Creates a new null PCM
370 * \param pcmp Returns created PCM handle
371 * \param name Name of PCM
372 * \param stream Stream type
373 * \param mode Stream mode
374 * \retval zero on success otherwise a negative error code
375 * \warning Using of this function might be dangerous in the sense
376 * of compatibility reasons. The prototype might be freely
377 * changed in future.
378 */
snd_pcm_null_open(snd_pcm_t ** pcmp,const char * name,snd_pcm_stream_t stream,int mode)379 int snd_pcm_null_open(snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode)
380 {
381 snd_pcm_t *pcm;
382 snd_pcm_null_t *null;
383 int fd;
384 int err;
385 assert(pcmp);
386 if (stream == SND_PCM_STREAM_PLAYBACK) {
387 fd = open("/dev/null", O_WRONLY);
388 if (fd < 0) {
389 SYSERR("Cannot open /dev/null");
390 return -errno;
391 }
392 } else {
393 fd = open("/dev/full", O_RDONLY);
394 if (fd < 0) {
395 SYSERR("Cannot open /dev/full");
396 return -errno;
397 }
398 }
399 null = calloc(1, sizeof(snd_pcm_null_t));
400 if (!null) {
401 close(fd);
402 return -ENOMEM;
403 }
404 null->poll_fd = fd;
405 null->state = SND_PCM_STATE_OPEN;
406
407 err = snd_pcm_new(&pcm, SND_PCM_TYPE_NULL, name, stream, mode);
408 if (err < 0) {
409 close(fd);
410 free(null);
411 return err;
412 }
413 pcm->ops = &snd_pcm_null_ops;
414 pcm->fast_ops = &snd_pcm_null_fast_ops;
415 pcm->private_data = null;
416 pcm->poll_fd = fd;
417 pcm->poll_events = stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN;
418 snd_pcm_set_hw_ptr(pcm, &null->hw_ptr, -1, 0);
419 snd_pcm_set_appl_ptr(pcm, &null->appl_ptr, -1, 0);
420 *pcmp = pcm;
421
422 return 0;
423 }
424
425 /*! \page pcm_plugins
426
427 \section pcm_plugins_null Plugin: Null
428
429 This plugin discards contents of a PCM stream or creates a stream with zero
430 samples.
431
432 Note: This implementation uses devices /dev/null (playback, must be writable)
433 and /dev/full (capture, must be readable).
434
435 \code
436 pcm.name {
437 type null # Null PCM
438 [chmap MAP] # Provide channel maps; MAP is a string array
439 }
440 \endcode
441
442 \subsection pcm_plugins_null_funcref Function reference
443
444 <UL>
445 <LI>snd_pcm_null_open()
446 <LI>_snd_pcm_null_open()
447 </UL>
448
449 */
450
451 /**
452 * \brief Creates a new Null PCM
453 * \param pcmp Returns created PCM handle
454 * \param name Name of PCM
455 * \param root Root configuration node
456 * \param conf Configuration node with Null PCM description
457 * \param stream Stream type
458 * \param mode Stream mode
459 * \retval zero on success otherwise a negative error code
460 * \warning Using of this function might be dangerous in the sense
461 * of compatibility reasons. The prototype might be freely
462 * changed in future.
463 */
_snd_pcm_null_open(snd_pcm_t ** pcmp,const char * name,snd_config_t * root ATTRIBUTE_UNUSED,snd_config_t * conf,snd_pcm_stream_t stream,int mode)464 int _snd_pcm_null_open(snd_pcm_t **pcmp, const char *name,
465 snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf,
466 snd_pcm_stream_t stream, int mode)
467 {
468 snd_config_iterator_t i, next;
469 snd_pcm_null_t *null;
470 snd_pcm_chmap_query_t **chmap = NULL;
471 int err;
472
473 snd_config_for_each(i, next, conf) {
474 snd_config_t *n = snd_config_iterator_entry(i);
475 const char *id;
476 if (snd_config_get_id(n, &id) < 0)
477 continue;
478 if (snd_pcm_conf_generic_id(id))
479 continue;
480 if (strcmp(id, "chmap") == 0) {
481 snd_pcm_free_chmaps(chmap);
482 chmap = _snd_pcm_parse_config_chmaps(n);
483 if (!chmap) {
484 SNDERR("Invalid channel map for %s", id);
485 return -EINVAL;
486 }
487 continue;
488 }
489 SNDERR("Unknown field %s", id);
490 snd_pcm_free_chmaps(chmap);
491 return -EINVAL;
492 }
493 err = snd_pcm_null_open(pcmp, name, stream, mode);
494 if (err < 0) {
495 snd_pcm_free_chmaps(chmap);
496 return err;
497 }
498
499 null = (*pcmp)->private_data;
500 null->chmap = chmap;
501 return 0;
502 }
503 #ifndef DOC_HIDDEN
504 SND_DLSYM_BUILD_VERSION(_snd_pcm_null_open, SND_PCM_DLSYM_VERSION);
505 #endif
506