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