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