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