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