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