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