xref: /third_party/alsa-lib/src/pcm/pcm_null.c (revision d5ac70f0)
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 "pcm_local.h"
30#include "pcm_plugin.h"
31#include "bswap.h"
32#include <limits.h>
33
34#ifndef PIC
35/* entry for static linking */
36const char *_snd_module_pcm_null = "";
37#endif
38
39#ifndef DOC_HIDDEN
40typedef 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
50static 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
58static int snd_pcm_null_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED)
59{
60	return 0;
61}
62
63static 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
68static 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
82static 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
93static 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
107static 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
113static int snd_pcm_null_hwsync(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
114{
115	return 0;
116}
117
118static 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
124static 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
131static 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
138static 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
150static 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
158static 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
166static 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
181static snd_pcm_sframes_t snd_pcm_null_rewindable(snd_pcm_t *pcm)
182{
183	return pcm->buffer_size;
184}
185
186static snd_pcm_sframes_t snd_pcm_null_forwardable(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
187{
188	return 0;
189}
190
191
192static 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
207static 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
222static int snd_pcm_null_resume(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
223{
224	return 0;
225}
226
227static 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
237static 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
242static 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
247static 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
252static 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
257static 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
264static 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
283static 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
288static int snd_pcm_null_hw_free(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
289{
290	return 0;
291}
292
293static 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
298static 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
307static 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
316static 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
325static 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
343static 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 */
379int 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
429This plugin discards contents of a PCM stream or creates a stream with zero
430samples.
431
432Note: This implementation uses devices /dev/null (playback, must be writable)
433and /dev/full (capture, must be readable).
434
435\code
436pcm.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 */
464int _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
504SND_DLSYM_BUILD_VERSION(_snd_pcm_null_open, SND_PCM_DLSYM_VERSION);
505#endif
506