Home | History | Annotate | Download | only in pcm
      1 /**
      2  * \file pcm/pcm_file.c
      3  * \ingroup PCM_Plugins
      4  * \brief PCM File Plugin Interface
      5  * \author Abramo Bagnara <abramo (at) alsa-project.org>
      6  * \date 2000-2001
      7  */
      8 /*
      9  *  PCM - File 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 <endian.h>
     30 #include <byteswap.h>
     31 #include <ctype.h>
     32 #include "pcm_local.h"
     33 #include "pcm_plugin.h"
     34 
     35 #ifndef PIC
     36 /* entry for static linking */
     37 const char *_snd_module_pcm_file = "";
     38 #endif
     39 
     40 #ifndef DOC_HIDDEN
     41 
     42 typedef enum _snd_pcm_file_format {
     43 	SND_PCM_FILE_FORMAT_RAW,
     44 	SND_PCM_FILE_FORMAT_WAV
     45 } snd_pcm_file_format_t;
     46 
     47 /* WAV format chunk */
     48 struct wav_fmt {
     49 	short fmt;
     50 	short chan;
     51 	int rate;
     52 	int bps;
     53 	short bwidth;
     54 	short bits;
     55 };
     56 
     57 typedef struct {
     58 	snd_pcm_generic_t gen;
     59 	char *fname;
     60 	int fd;
     61 	char *ifname;
     62 	int ifd;
     63 	int format;
     64 	snd_pcm_uframes_t appl_ptr;
     65 	snd_pcm_uframes_t file_ptr_bytes;
     66 	snd_pcm_uframes_t wbuf_size;
     67 	size_t wbuf_size_bytes;
     68 	size_t wbuf_used_bytes;
     69 	char *wbuf;
     70 	size_t rbuf_size_bytes;
     71 	size_t rbuf_used_bytes;
     72 	char *rbuf;
     73 	snd_pcm_channel_area_t *wbuf_areas;
     74 	size_t buffer_bytes;
     75 	struct wav_fmt wav_header;
     76 	size_t filelen;
     77 } snd_pcm_file_t;
     78 
     79 #if __BYTE_ORDER == __LITTLE_ENDIAN
     80 #define TO_LE32(x)	(x)
     81 #define TO_LE16(x)	(x)
     82 #else
     83 #define TO_LE32(x)	bswap_32(x)
     84 #define TO_LE16(x)	bswap_16(x)
     85 #endif
     86 
     87 static void setup_wav_header(snd_pcm_t *pcm, struct wav_fmt *fmt)
     88 {
     89 	fmt->fmt = TO_LE16(0x01);
     90 	fmt->chan = TO_LE16(pcm->channels);
     91 	fmt->rate = TO_LE32(pcm->rate);
     92 	fmt->bwidth = pcm->frame_bits / 8;
     93 	fmt->bps = fmt->bwidth * pcm->rate;
     94 	fmt->bits = snd_pcm_format_width(pcm->format);
     95 	fmt->bps = TO_LE32(fmt->bps);
     96 	fmt->bwidth = TO_LE16(fmt->bwidth);
     97 	fmt->bits = TO_LE16(fmt->bits);
     98 }
     99 
    100 static int write_wav_header(snd_pcm_t *pcm)
    101 {
    102 	snd_pcm_file_t *file = pcm->private_data;
    103 	static const char header[] = {
    104 		'R', 'I', 'F', 'F',
    105 		0x24, 0, 0, 0,
    106 		'W', 'A', 'V', 'E',
    107 		'f', 'm', 't', ' ',
    108 		0x10, 0, 0, 0,
    109 	};
    110 	static const char header2[] = {
    111 		'd', 'a', 't', 'a',
    112 		0, 0, 0, 0
    113 	};
    114 
    115 	setup_wav_header(pcm, &file->wav_header);
    116 
    117 	if (write(file->fd, header, sizeof(header)) != sizeof(header) ||
    118 	    write(file->fd, &file->wav_header, sizeof(file->wav_header)) !=
    119 	    sizeof(file->wav_header) ||
    120 	    write(file->fd, header2, sizeof(header2)) != sizeof(header2)) {
    121 		int err = errno;
    122 		SYSERR("Write error.\n");
    123 		return -err;
    124 	}
    125 	return 0;
    126 }
    127 
    128 /* fix up the length fields in WAV header */
    129 static void fixup_wav_header(snd_pcm_t *pcm)
    130 {
    131 	snd_pcm_file_t *file = pcm->private_data;
    132 	int len, ret;
    133 
    134 	/* RIFF length */
    135 	if (lseek(file->fd, 4, SEEK_SET) == 4) {
    136 		len = (file->filelen + 0x24) > 0x7fffffff ?
    137 			0x7fffffff : (int)(file->filelen + 0x24);
    138 		len = TO_LE32(len);
    139 		ret = write(file->fd, &len, 4);
    140 		if (ret < 0)
    141 			return;
    142 	}
    143 	/* data length */
    144 	if (lseek(file->fd, 0x28, SEEK_SET) == 0x28) {
    145 		len = file->filelen > 0x7fffffff ?
    146 			0x7fffffff : (int)file->filelen;
    147 		len = TO_LE32(len);
    148 		ret = write(file->fd, &len, 4);
    149 		if (ret < 0)
    150 			return;
    151 	}
    152 }
    153 #endif /* DOC_HIDDEN */
    154 
    155 static void snd_pcm_file_write_bytes(snd_pcm_t *pcm, size_t bytes)
    156 {
    157 	snd_pcm_file_t *file = pcm->private_data;
    158 	assert(bytes <= file->wbuf_used_bytes);
    159 
    160 	if (file->format == SND_PCM_FILE_FORMAT_WAV &&
    161 	    !file->wav_header.fmt) {
    162 		if (write_wav_header(pcm) < 0)
    163 			return;
    164 	}
    165 
    166 	while (bytes > 0) {
    167 		snd_pcm_sframes_t err;
    168 		size_t n = bytes;
    169 		size_t cont = file->wbuf_size_bytes - file->file_ptr_bytes;
    170 		if (n > cont)
    171 			n = cont;
    172 		err = write(file->fd, file->wbuf + file->file_ptr_bytes, n);
    173 		if (err < 0) {
    174 			SYSERR("write failed");
    175 			break;
    176 		}
    177 		bytes -= err;
    178 		file->wbuf_used_bytes -= err;
    179 		file->file_ptr_bytes += err;
    180 		if (file->file_ptr_bytes == file->wbuf_size_bytes)
    181 			file->file_ptr_bytes = 0;
    182 		file->filelen += err;
    183 		if ((snd_pcm_uframes_t)err != n)
    184 			break;
    185 	}
    186 }
    187 
    188 static void snd_pcm_file_add_frames(snd_pcm_t *pcm,
    189 				    const snd_pcm_channel_area_t *areas,
    190 				    snd_pcm_uframes_t offset,
    191 				    snd_pcm_uframes_t frames)
    192 {
    193 	snd_pcm_file_t *file = pcm->private_data;
    194 	while (frames > 0) {
    195 		snd_pcm_uframes_t n = frames;
    196 		snd_pcm_uframes_t cont = file->wbuf_size - file->appl_ptr;
    197 		snd_pcm_uframes_t avail = file->wbuf_size - snd_pcm_bytes_to_frames(pcm, file->wbuf_used_bytes);
    198 		if (n > cont)
    199 			n = cont;
    200 		if (n > avail)
    201 			n = avail;
    202 		snd_pcm_areas_copy(file->wbuf_areas, file->appl_ptr,
    203 				   areas, offset,
    204 				   pcm->channels, n, pcm->format);
    205 		frames -= n;
    206 		offset += n;
    207 		file->appl_ptr += n;
    208 		if (file->appl_ptr == file->wbuf_size)
    209 			file->appl_ptr = 0;
    210 		file->wbuf_used_bytes += snd_pcm_frames_to_bytes(pcm, n);
    211 		if (file->wbuf_used_bytes > file->buffer_bytes)
    212 			snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes - file->buffer_bytes);
    213 		assert(file->wbuf_used_bytes < file->wbuf_size_bytes);
    214 	}
    215 }
    216 
    217 static int snd_pcm_file_close(snd_pcm_t *pcm)
    218 {
    219 	snd_pcm_file_t *file = pcm->private_data;
    220 	if (file->fname) {
    221 		if (file->wav_header.fmt)
    222 			fixup_wav_header(pcm);
    223 		free((void *)file->fname);
    224 		close(file->fd);
    225 	}
    226 	if (file->ifname) {
    227 		free((void *)file->ifname);
    228 		close(file->ifd);
    229 	}
    230 	return snd_pcm_generic_close(pcm);
    231 }
    232 
    233 static int snd_pcm_file_reset(snd_pcm_t *pcm)
    234 {
    235 	snd_pcm_file_t *file = pcm->private_data;
    236 	int err = snd_pcm_reset(file->gen.slave);
    237 	if (err >= 0) {
    238 		/* FIXME: Questionable here */
    239 		snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes);
    240 		assert(file->wbuf_used_bytes == 0);
    241 	}
    242 	return err;
    243 }
    244 
    245 static int snd_pcm_file_drop(snd_pcm_t *pcm)
    246 {
    247 	snd_pcm_file_t *file = pcm->private_data;
    248 	int err = snd_pcm_drop(file->gen.slave);
    249 	if (err >= 0) {
    250 		/* FIXME: Questionable here */
    251 		snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes);
    252 		assert(file->wbuf_used_bytes == 0);
    253 	}
    254 	return err;
    255 }
    256 
    257 static int snd_pcm_file_drain(snd_pcm_t *pcm)
    258 {
    259 	snd_pcm_file_t *file = pcm->private_data;
    260 	int err = snd_pcm_drain(file->gen.slave);
    261 	if (err >= 0) {
    262 		snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes);
    263 		assert(file->wbuf_used_bytes == 0);
    264 	}
    265 	return err;
    266 }
    267 
    268 static snd_pcm_sframes_t snd_pcm_file_rewindable(snd_pcm_t *pcm)
    269 {
    270 	snd_pcm_file_t *file = pcm->private_data;
    271 	snd_pcm_sframes_t res = snd_pcm_rewindable(pcm);
    272 	snd_pcm_sframes_t n = snd_pcm_bytes_to_frames(pcm, file->wbuf_used_bytes);
    273 	if (res > n)
    274 		res = n;
    275 	return res;
    276 }
    277 
    278 static snd_pcm_sframes_t snd_pcm_file_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
    279 {
    280 	snd_pcm_file_t *file = pcm->private_data;
    281 	snd_pcm_sframes_t err;
    282 	snd_pcm_uframes_t n;
    283 
    284 	n = snd_pcm_frames_to_bytes(pcm, frames);
    285 	if (n > file->wbuf_used_bytes)
    286 		frames = snd_pcm_bytes_to_frames(pcm, file->wbuf_used_bytes);
    287 	err = snd_pcm_rewind(file->gen.slave, frames);
    288 	if (err > 0) {
    289 		file->appl_ptr = (file->appl_ptr - err + file->wbuf_size) % file->wbuf_size;
    290 		n = snd_pcm_frames_to_bytes(pcm, err);
    291 		file->wbuf_used_bytes -= n;
    292 	}
    293 	return err;
    294 }
    295 
    296 static snd_pcm_sframes_t snd_pcm_file_forwardable(snd_pcm_t *pcm)
    297 {
    298 	snd_pcm_file_t *file = pcm->private_data;
    299 	snd_pcm_sframes_t res = snd_pcm_forwardable(pcm);
    300 	snd_pcm_sframes_t n = snd_pcm_bytes_to_frames(pcm, file->wbuf_size_bytes - file->wbuf_used_bytes);
    301 	if (res > n)
    302 		res = n;
    303 	return res;
    304 }
    305 
    306 static snd_pcm_sframes_t snd_pcm_file_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
    307 {
    308 	snd_pcm_file_t *file = pcm->private_data;
    309 	snd_pcm_sframes_t err;
    310 	snd_pcm_uframes_t n;
    311 
    312 	n = snd_pcm_frames_to_bytes(pcm, frames);
    313 	if (file->wbuf_used_bytes + n > file->wbuf_size_bytes)
    314 		frames = snd_pcm_bytes_to_frames(pcm, file->wbuf_size_bytes - file->wbuf_used_bytes);
    315 	err = INTERNAL(snd_pcm_forward)(file->gen.slave, frames);
    316 	if (err > 0) {
    317 		file->appl_ptr = (file->appl_ptr + err) % file->wbuf_size;
    318 		n = snd_pcm_frames_to_bytes(pcm, err);
    319 		file->wbuf_used_bytes += n;
    320 	}
    321 	return err;
    322 }
    323 
    324 static snd_pcm_sframes_t snd_pcm_file_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
    325 {
    326 	snd_pcm_file_t *file = pcm->private_data;
    327 	snd_pcm_channel_area_t areas[pcm->channels];
    328 	snd_pcm_sframes_t n = snd_pcm_writei(file->gen.slave, buffer, size);
    329 	if (n > 0) {
    330 		snd_pcm_areas_from_buf(pcm, areas, (void*) buffer);
    331 		snd_pcm_file_add_frames(pcm, areas, 0, n);
    332 	}
    333 	return n;
    334 }
    335 
    336 static snd_pcm_sframes_t snd_pcm_file_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
    337 {
    338 	snd_pcm_file_t *file = pcm->private_data;
    339 	snd_pcm_channel_area_t areas[pcm->channels];
    340 	snd_pcm_sframes_t n = snd_pcm_writen(file->gen.slave, bufs, size);
    341 	if (n > 0) {
    342 		snd_pcm_areas_from_bufs(pcm, areas, bufs);
    343 		snd_pcm_file_add_frames(pcm, areas, 0, n);
    344 	}
    345 	return n;
    346 }
    347 
    348 static snd_pcm_sframes_t snd_pcm_file_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
    349 {
    350 	snd_pcm_file_t *file = pcm->private_data;
    351 	snd_pcm_channel_area_t areas[pcm->channels];
    352 	snd_pcm_sframes_t n;
    353 
    354 	n = snd_pcm_readi(file->gen.slave, buffer, size);
    355 	if (n <= 0)
    356 		return n;
    357 	if (file->ifd >= 0) {
    358 		n = read(file->ifd, buffer, n * pcm->frame_bits / 8);
    359 		if (n < 0)
    360 			return n;
    361 		return n * 8 / pcm->frame_bits;
    362 	}
    363 	snd_pcm_areas_from_buf(pcm, areas, buffer);
    364 	snd_pcm_file_add_frames(pcm, areas, 0, n);
    365 	return n;
    366 }
    367 
    368 static snd_pcm_sframes_t snd_pcm_file_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
    369 {
    370 	snd_pcm_file_t *file = pcm->private_data;
    371 	snd_pcm_channel_area_t areas[pcm->channels];
    372 	snd_pcm_sframes_t n;
    373 
    374 	if (file->ifd >= 0) {
    375 		SNDERR("DEBUG: Noninterleaved read not yet implemented.\n");
    376 		return 0;	/* TODO: Noninterleaved read */
    377 	}
    378 
    379 	n = snd_pcm_readn(file->gen.slave, bufs, size);
    380 	if (n > 0) {
    381 		snd_pcm_areas_from_bufs(pcm, areas, bufs);
    382 		snd_pcm_file_add_frames(pcm, areas, 0, n);
    383 	}
    384 	return n;
    385 }
    386 
    387 static snd_pcm_sframes_t snd_pcm_file_mmap_commit(snd_pcm_t *pcm,
    388 					          snd_pcm_uframes_t offset,
    389 						  snd_pcm_uframes_t size)
    390 {
    391 	snd_pcm_file_t *file = pcm->private_data;
    392 	snd_pcm_uframes_t ofs;
    393 	snd_pcm_uframes_t siz = size;
    394 	const snd_pcm_channel_area_t *areas;
    395 	snd_pcm_sframes_t result;
    396 
    397 	snd_pcm_mmap_begin(file->gen.slave, &areas, &ofs, &siz);
    398 	assert(ofs == offset && siz == size);
    399 	result = snd_pcm_mmap_commit(file->gen.slave, ofs, siz);
    400 	if (result > 0)
    401 		snd_pcm_file_add_frames(pcm, areas, ofs, result);
    402 	return result;
    403 }
    404 
    405 static int snd_pcm_file_hw_free(snd_pcm_t *pcm)
    406 {
    407 	snd_pcm_file_t *file = pcm->private_data;
    408 	free(file->wbuf);
    409 	free(file->wbuf_areas);
    410 	file->wbuf = NULL;
    411 	file->wbuf_areas = NULL;
    412 	return snd_pcm_hw_free(file->gen.slave);
    413 }
    414 
    415 static int snd_pcm_file_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
    416 {
    417 	snd_pcm_file_t *file = pcm->private_data;
    418 	unsigned int channel;
    419 	snd_pcm_t *slave = file->gen.slave;
    420 	int err = _snd_pcm_hw_params(slave, params);
    421 	if (err < 0)
    422 		return err;
    423 	file->buffer_bytes = snd_pcm_frames_to_bytes(slave, slave->buffer_size);
    424 	file->wbuf_size = slave->buffer_size * 2;
    425 	file->wbuf_size_bytes = snd_pcm_frames_to_bytes(slave, file->wbuf_size);
    426 	file->wbuf_used_bytes = 0;
    427 	assert(!file->wbuf);
    428 	file->wbuf = malloc(file->wbuf_size_bytes);
    429 	if (file->wbuf == NULL) {
    430 		snd_pcm_file_hw_free(pcm);
    431 		return -ENOMEM;
    432 	}
    433 	file->wbuf_areas = malloc(sizeof(*file->wbuf_areas) * slave->channels);
    434 	if (file->wbuf_areas == NULL) {
    435 		snd_pcm_file_hw_free(pcm);
    436 		return -ENOMEM;
    437 	}
    438 	file->appl_ptr = file->file_ptr_bytes = 0;
    439 	for (channel = 0; channel < slave->channels; ++channel) {
    440 		snd_pcm_channel_area_t *a = &file->wbuf_areas[channel];
    441 		a->addr = file->wbuf;
    442 		a->first = slave->sample_bits * channel;
    443 		a->step = slave->frame_bits;
    444 	}
    445 	return 0;
    446 }
    447 
    448 static void snd_pcm_file_dump(snd_pcm_t *pcm, snd_output_t *out)
    449 {
    450 	snd_pcm_file_t *file = pcm->private_data;
    451 	if (file->fname)
    452 		snd_output_printf(out, "File PCM (file=%s)\n", file->fname);
    453 	else
    454 		snd_output_printf(out, "File PCM (fd=%d)\n", file->fd);
    455 	if (pcm->setup) {
    456 		snd_output_printf(out, "Its setup is:\n");
    457 		snd_pcm_dump_setup(pcm, out);
    458 	}
    459 	snd_output_printf(out, "Slave: ");
    460 	snd_pcm_dump(file->gen.slave, out);
    461 }
    462 
    463 static const snd_pcm_ops_t snd_pcm_file_ops = {
    464 	.close = snd_pcm_file_close,
    465 	.info = snd_pcm_generic_info,
    466 	.hw_refine = snd_pcm_generic_hw_refine,
    467 	.hw_params = snd_pcm_file_hw_params,
    468 	.hw_free = snd_pcm_file_hw_free,
    469 	.sw_params = snd_pcm_generic_sw_params,
    470 	.channel_info = snd_pcm_generic_channel_info,
    471 	.dump = snd_pcm_file_dump,
    472 	.nonblock = snd_pcm_generic_nonblock,
    473 	.async = snd_pcm_generic_async,
    474 	.mmap = snd_pcm_generic_mmap,
    475 	.munmap = snd_pcm_generic_munmap,
    476 };
    477 
    478 static const snd_pcm_fast_ops_t snd_pcm_file_fast_ops = {
    479 	.status = snd_pcm_generic_status,
    480 	.state = snd_pcm_generic_state,
    481 	.hwsync = snd_pcm_generic_hwsync,
    482 	.delay = snd_pcm_generic_delay,
    483 	.prepare = snd_pcm_generic_prepare,
    484 	.reset = snd_pcm_file_reset,
    485 	.start = snd_pcm_generic_start,
    486 	.drop = snd_pcm_file_drop,
    487 	.drain = snd_pcm_file_drain,
    488 	.pause = snd_pcm_generic_pause,
    489 	.rewindable = snd_pcm_file_rewindable,
    490 	.rewind = snd_pcm_file_rewind,
    491 	.forwardable = snd_pcm_file_forwardable,
    492 	.forward = snd_pcm_file_forward,
    493 	.resume = snd_pcm_generic_resume,
    494 	.link = snd_pcm_generic_link,
    495 	.link_slaves = snd_pcm_generic_link_slaves,
    496 	.unlink = snd_pcm_generic_unlink,
    497 	.writei = snd_pcm_file_writei,
    498 	.writen = snd_pcm_file_writen,
    499 	.readi = snd_pcm_file_readi,
    500 	.readn = snd_pcm_file_readn,
    501 	.avail_update = snd_pcm_generic_avail_update,
    502 	.mmap_commit = snd_pcm_file_mmap_commit,
    503 	.poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
    504 	.poll_descriptors = snd_pcm_generic_poll_descriptors,
    505 	.poll_revents = snd_pcm_generic_poll_revents,
    506 };
    507 
    508 /**
    509  * \brief Creates a new File PCM
    510  * \param pcmp Returns created PCM handle
    511  * \param name Name of PCM
    512  * \param fname Output filename (or NULL if file descriptor fd is available)
    513  * \param fd Output file descriptor
    514  * \param ifname Input filename (or NULL if file descriptor ifd is available)
    515  * \param ifd Input file descriptor (if (ifd < 0) && (ifname == NULL), no input
    516  *            redirection will be performed)
    517  * \param trunc Truncate the file if it already exists
    518  * \param fmt File format ("raw" or "wav" are available)
    519  * \param perm File permission
    520  * \param slave Slave PCM handle
    521  * \param close_slave When set, the slave PCM handle is closed with copy PCM
    522  * \retval zero on success otherwise a negative error code
    523  * \warning Using of this function might be dangerous in the sense
    524  *          of compatibility reasons. The prototype might be freely
    525  *          changed in future.
    526  */
    527 int snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
    528 		      const char *fname, int fd, const char *ifname, int ifd,
    529 		      int trunc,
    530 		      const char *fmt, int perm, snd_pcm_t *slave, int close_slave)
    531 {
    532 	snd_pcm_t *pcm;
    533 	snd_pcm_file_t *file;
    534 	snd_pcm_file_format_t format;
    535 	struct timespec timespec;
    536 	char *tmpname = NULL;
    537 	int err;
    538 
    539 	assert(pcmp);
    540 	if (fmt == NULL ||
    541 	    strcmp(fmt, "raw") == 0)
    542 		format = SND_PCM_FILE_FORMAT_RAW;
    543 	else if (!strcmp(fmt, "wav"))
    544 		format = SND_PCM_FILE_FORMAT_WAV;
    545 	else {
    546 		SNDERR("file format %s is unknown", fmt);
    547 		return -EINVAL;
    548 	}
    549 	if (fname) {
    550 		if (trunc)
    551 			fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, perm);
    552 		else {
    553 			fd = open(fname, O_WRONLY|O_CREAT|O_EXCL, perm);
    554 			if (fd < 0) {
    555 				int idx, len;
    556 				len = strlen(fname) + 6;
    557 				tmpname = malloc(len);
    558 				if (!tmpname)
    559 					return -ENOMEM;
    560 				for (idx = 1; idx < 10000; idx++) {
    561 					snprintf(tmpname, len,
    562 						 "%s.%04d", fname, idx);
    563 					fd = open(tmpname, O_WRONLY|O_CREAT|O_EXCL, perm);
    564 					if (fd >= 0) {
    565 						fname = tmpname;
    566 						break;
    567 					}
    568 				}
    569 			}
    570 		}
    571 		if (fd < 0) {
    572 			SYSERR("open %s for writing failed", fname);
    573 			free(tmpname);
    574 			return -errno;
    575 		}
    576 	}
    577 	file = calloc(1, sizeof(snd_pcm_file_t));
    578 	if (!file) {
    579 		if (fname)
    580 			close(fd);
    581 		free(tmpname);
    582 		return -ENOMEM;
    583 	}
    584 
    585 	if (ifname) {
    586 		ifd = open(ifname, O_RDONLY);	/* TODO: mind blocking mode */
    587 		if (ifd < 0) {
    588 			SYSERR("open %s for reading failed", ifname);
    589 			if (fname)
    590 				close(fd);
    591 			free(file);
    592 			free(tmpname);
    593 			return -errno;
    594 		}
    595 	}
    596 
    597 	if (fname)
    598 		file->fname = strdup(fname);
    599 	if (ifname)
    600 		file->ifname = strdup(ifname);
    601 	file->fd = fd;
    602 	file->ifd = ifd;
    603 	file->format = format;
    604 	file->gen.slave = slave;
    605 	file->gen.close_slave = close_slave;
    606 
    607 	err = snd_pcm_new(&pcm, SND_PCM_TYPE_FILE, name, slave->stream, slave->mode);
    608 	if (err < 0) {
    609 		free(file->fname);
    610 		free(file);
    611 		free(tmpname);
    612 		return err;
    613 	}
    614 	pcm->ops = &snd_pcm_file_ops;
    615 	pcm->fast_ops = &snd_pcm_file_fast_ops;
    616 	pcm->private_data = file;
    617 	pcm->poll_fd = slave->poll_fd;
    618 	pcm->poll_events = slave->poll_events;
    619 	pcm->mmap_shadow = 1;
    620 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
    621 	pcm->monotonic = clock_gettime(CLOCK_MONOTONIC, &timespec) == 0;
    622 #else
    623 	pcm->monotonic = 0;
    624 #endif
    625 	snd_pcm_link_hw_ptr(pcm, slave);
    626 	snd_pcm_link_appl_ptr(pcm, slave);
    627 	*pcmp = pcm;
    628 
    629 	free(tmpname);
    630 	return 0;
    631 }
    632 
    633 /*! \page pcm_plugins
    634 
    635 \section pcm_plugins_file Plugin: File
    636 
    637 This plugin stores contents of a PCM stream to file, and optionally
    638 uses an existing file as an input data source (i.e., "virtual mic")
    639 
    640 \code
    641 pcm.name {
    642         type file               # File PCM
    643         slave STR               # Slave name
    644         # or
    645         slave {                 # Slave definition
    646                 pcm STR         # Slave PCM name
    647                 # or
    648                 pcm { }         # Slave PCM definition
    649         }
    650 	file STR		# Output filename
    651 	or
    652 	file INT		# Output file descriptor number
    653 	infile STR		# Input filename - only raw format
    654 	or
    655 	infile INT		# Input file descriptor number
    656 	[format STR]		# File format ("raw" or "wav")
    657 	[perm INT]		# Output file permission (octal, def. 0600)
    658 }
    659 \endcode
    660 
    661 \subsection pcm_plugins_file_funcref Function reference
    662 
    663 <UL>
    664   <LI>snd_pcm_file_open()
    665   <LI>_snd_pcm_file_open()
    666 </UL>
    667 
    668 */
    669 
    670 /**
    671  * \brief Creates a new File PCM
    672  * \param pcmp Returns created PCM handle
    673  * \param name Name of PCM
    674  * \param root Root configuration node
    675  * \param conf Configuration node with File PCM description
    676  * \param stream Stream type
    677  * \param mode Stream mode
    678  * \retval zero on success otherwise a negative error code
    679  * \warning Using of this function might be dangerous in the sense
    680  *          of compatibility reasons. The prototype might be freely
    681  *          changed in future.
    682  */
    683 int _snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
    684 		       snd_config_t *root, snd_config_t *conf,
    685 		       snd_pcm_stream_t stream, int mode)
    686 {
    687 	snd_config_iterator_t i, next;
    688 	int err;
    689 	snd_pcm_t *spcm;
    690 	snd_config_t *slave = NULL, *sconf;
    691 	const char *fname = NULL, *ifname = NULL;
    692 	const char *format = NULL;
    693 	long fd = -1, ifd = -1, trunc = 1;
    694 	long perm = 0600;
    695 	snd_config_for_each(i, next, conf) {
    696 		snd_config_t *n = snd_config_iterator_entry(i);
    697 		const char *id;
    698 		if (snd_config_get_id(n, &id) < 0)
    699 			continue;
    700 		if (snd_pcm_conf_generic_id(id))
    701 			continue;
    702 		if (strcmp(id, "slave") == 0) {
    703 			slave = n;
    704 			continue;
    705 		}
    706 		if (strcmp(id, "format") == 0) {
    707 			err = snd_config_get_string(n, &format);
    708 			if (err < 0) {
    709 				SNDERR("Invalid type for %s", id);
    710 				return -EINVAL;
    711 			}
    712 			continue;
    713 		}
    714 		if (strcmp(id, "file") == 0) {
    715 			err = snd_config_get_string(n, &fname);
    716 			if (err < 0) {
    717 				err = snd_config_get_integer(n, &fd);
    718 				if (err < 0) {
    719 					SNDERR("Invalid type for %s", id);
    720 					return -EINVAL;
    721 				}
    722 			}
    723 			continue;
    724 		}
    725 		if (strcmp(id, "infile") == 0) {
    726 			err = snd_config_get_string(n, &ifname);
    727 			if (err < 0) {
    728 				err = snd_config_get_integer(n, &ifd);
    729 				if (err < 0) {
    730 					SNDERR("Invalid type for %s", id);
    731 					return -EINVAL;
    732 				}
    733 			}
    734 			continue;
    735 		}
    736 		if (strcmp(id, "perm") == 0) {
    737 			err = snd_config_get_integer(n, &perm);
    738 			if (err < 0) {
    739 				SNDERR("Invalid type for %s", id);
    740 				return err;
    741 			}
    742 			if ((perm & ~0777) != 0) {
    743 				SNDERR("The field perm must be a valid file permission");
    744 				return -EINVAL;
    745 			}
    746 			continue;
    747 		}
    748 		if (strcmp(id, "truncate") == 0) {
    749 			err = snd_config_get_bool(n);
    750 			if (err < 0)
    751 				return -EINVAL;
    752 			trunc = err;
    753 			continue;
    754 		}
    755 		SNDERR("Unknown field %s", id);
    756 		return -EINVAL;
    757 	}
    758 	if (!format) {
    759 		snd_config_t *n;
    760 		/* read defaults */
    761 		if (snd_config_search(root, "defaults.pcm.file_format", &n) >= 0) {
    762 			err = snd_config_get_string(n, &format);
    763 			if (err < 0) {
    764 				SNDERR("Invalid file format");
    765 				return -EINVAL;
    766 			}
    767 		}
    768 	}
    769 	if (!slave) {
    770 		SNDERR("slave is not defined");
    771 		return -EINVAL;
    772 	}
    773 	err = snd_pcm_slave_conf(root, slave, &sconf, 0);
    774 	if (err < 0)
    775 		return err;
    776 	if (!fname && fd < 0 && !ifname) {
    777 		snd_config_delete(sconf);
    778 		SNDERR("file is not defined");
    779 		return -EINVAL;
    780 	}
    781 	err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
    782 	snd_config_delete(sconf);
    783 	if (err < 0)
    784 		return err;
    785 	err = snd_pcm_file_open(pcmp, name, fname, fd, ifname, ifd,
    786 				trunc, format, perm, spcm, 1);
    787 	if (err < 0)
    788 		snd_pcm_close(spcm);
    789 	return err;
    790 }
    791 #ifndef DOC_HIDDEN
    792 SND_DLSYM_BUILD_VERSION(_snd_pcm_file_open, SND_PCM_DLSYM_VERSION);
    793 #endif
    794