summaryrefslogtreecommitdiff
path: root/src/pcm/pcm_local.h
blob: 5c354ab42a6d2952f66fe0708286452865cb2cc5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
/*
 *  PCM Interface - local header file
 *  Copyright (c) 2000 by Jaroslav Kysela <perex@suse.cz>
 *                        Abramo Bagnara <abramo@alsa-project.org>
 *
 *
 *   This library is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Library General Public License as
 *   published by the Free Software Foundation; either version 2 of
 *   the License, or (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this library; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/uio.h>
#include <errno.h>
#include "asoundlib.h"

#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
#define ERR(...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, 0, __VA_ARGS__)
#define SYSERR(...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, errno, __VA_ARGS__)
#else
#define ERR(args...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, 0, ##args)
#define SYSERR(args...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, errno, ##args)
#endif

typedef struct _snd_pcm_channel_info {
	unsigned int channel;
	void *addr;			/* base address of channel samples */
	unsigned int first;		/* offset to first sample in bits */
	unsigned int step;		/* samples distance in bits */
	enum { SND_PCM_AREA_SHM, SND_PCM_AREA_MMAP } type;
	union {
		struct {
			int shmid;
		} shm;
		struct {
			int fd;
			off_t offset;
		} mmap;
	} u;
	char reserved[64];
} snd_pcm_channel_info_t;

typedef struct {
	int (*close)(snd_pcm_t *pcm);
	int (*card)(snd_pcm_t *pcm);
	int (*nonblock)(snd_pcm_t *pcm, int nonblock);
	int (*async)(snd_pcm_t *pcm, int sig, pid_t pid);
	int (*info)(snd_pcm_t *pcm, snd_pcm_info_t *info);
	int (*hw_refine)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
	int (*hw_params)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
	int (*sw_params)(snd_pcm_t *pcm, snd_pcm_sw_params_t *params);
	int (*channel_info)(snd_pcm_t *pcm, snd_pcm_channel_info_t *info);
	void (*dump)(snd_pcm_t *pcm, FILE *fp);
	int (*mmap)(snd_pcm_t *pcm);
	int (*munmap)(snd_pcm_t *pcm);
} snd_pcm_ops_t;

typedef struct {
	int (*status)(snd_pcm_t *pcm, snd_pcm_status_t *status);
	int (*prepare)(snd_pcm_t *pcm);
	int (*reset)(snd_pcm_t *pcm);
	int (*start)(snd_pcm_t *pcm);
	int (*drop)(snd_pcm_t *pcm);
	int (*drain)(snd_pcm_t *pcm);
	int (*pause)(snd_pcm_t *pcm, int enable);
	int (*state)(snd_pcm_t *pcm);
	int (*delay)(snd_pcm_t *pcm, ssize_t *delayp);
	ssize_t (*rewind)(snd_pcm_t *pcm, size_t frames);
	ssize_t (*writei)(snd_pcm_t *pcm, const void *buffer, size_t size);
	ssize_t (*writen)(snd_pcm_t *pcm, void **bufs, size_t size);
	ssize_t (*readi)(snd_pcm_t *pcm, void *buffer, size_t size);
	ssize_t (*readn)(snd_pcm_t *pcm, void **bufs, size_t size);
	ssize_t (*avail_update)(snd_pcm_t *pcm);
	ssize_t (*mmap_forward)(snd_pcm_t *pcm, size_t size);
	int (*set_avail_min)(snd_pcm_t *pcm, size_t frames);
} snd_pcm_fast_ops_t;

struct _snd_pcm {
	char *name;
	snd_pcm_type_t type;
	int stream;
	int mode;
	int poll_fd;
	int setup;
	unsigned int access;		/* access mode */
	unsigned int format;		/* SND_PCM_FORMAT_* */
	unsigned int subformat;		/* subformat */
	unsigned int channels;		/* channels */
	unsigned int rate;		/* rate in Hz */
	size_t fragment_size;		/* fragment size */
	unsigned int fragments;		/* fragments */
	unsigned int start_mode;	/* start mode */
	unsigned int ready_mode;	/* ready detection mode */
	unsigned int xrun_mode;		/* xrun detection mode */
	size_t avail_min;		/* min avail frames for wakeup */
	size_t xfer_min;		/* xfer min size */
	size_t xfer_align;		/* xfer size need to be a multiple */
	unsigned int time: 1;		/* timestamp switch */
	size_t boundary;		/* pointers wrap point */
	unsigned int info;		/* Info for returned setup */
	unsigned int msbits;		/* used most significant bits */
	unsigned int rate_num;		/* rate numerator */
	unsigned int rate_den;		/* rate denominator */
	size_t fifo_size;		/* chip FIFO size in frames */
	size_t buffer_size;
	size_t bits_per_sample;
	size_t bits_per_frame;
	size_t *appl_ptr;
	volatile size_t *hw_ptr;
	int mmap_rw;
	snd_pcm_channel_info_t *mmap_channels;
	snd_pcm_channel_area_t *running_areas;
	snd_pcm_channel_area_t *stopped_areas;
	void *stopped;
	snd_pcm_ops_t *ops;
	snd_pcm_fast_ops_t *fast_ops;
	snd_pcm_t *op_arg;
	snd_pcm_t *fast_op_arg;
	void *private;
};

int snd_pcm_hw_open(snd_pcm_t **pcm, char *name, int card, int device, int subdevice, int stream, int mode);
int snd_pcm_plug_open_hw(snd_pcm_t **pcm, char *name, int card, int device, int subdevice, int stream, int mode);
int snd_pcm_shm_open(snd_pcm_t **pcmp, char *name, char *socket, char *sname, int stream, int mode);
int snd_pcm_file_open(snd_pcm_t **pcmp, char *name, char *fname, int fd, char *fmt, snd_pcm_t *slave, int close_slave);
int snd_pcm_null_open(snd_pcm_t **pcmp, char *name, int stream, int mode);


void snd_pcm_areas_from_buf(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, void *buf);
void snd_pcm_areas_from_bufs(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, void **bufs);

int snd_pcm_mmap(snd_pcm_t *pcm);
int snd_pcm_munmap(snd_pcm_t *pcm);
int snd_pcm_mmap_ready(snd_pcm_t *pcm);
ssize_t snd_pcm_mmap_appl_ptr(snd_pcm_t *pcm, off_t offset);
void snd_pcm_mmap_appl_backward(snd_pcm_t *pcm, size_t frames);
void snd_pcm_mmap_appl_forward(snd_pcm_t *pcm, size_t frames);
void snd_pcm_mmap_hw_backward(snd_pcm_t *pcm, size_t frames);
void snd_pcm_mmap_hw_forward(snd_pcm_t *pcm, size_t frames);
size_t snd_pcm_mmap_hw_offset(snd_pcm_t *pcm);
size_t snd_pcm_mmap_playback_xfer(snd_pcm_t *pcm, size_t frames);
size_t snd_pcm_mmap_capture_xfer(snd_pcm_t *pcm, size_t frames);

typedef ssize_t (*snd_pcm_xfer_areas_func_t)(snd_pcm_t *pcm, 
					     const snd_pcm_channel_area_t *areas,
					     size_t offset, size_t size,
					     size_t *slave_sizep);

ssize_t snd_pcm_read_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_t *areas,
			   size_t offset, size_t size,
			   snd_pcm_xfer_areas_func_t func);
ssize_t snd_pcm_write_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_t *areas,
			    size_t offset, size_t size,
			    snd_pcm_xfer_areas_func_t func);
ssize_t snd_pcm_read_mmap(snd_pcm_t *pcm, size_t size);
ssize_t snd_pcm_write_mmap(snd_pcm_t *pcm, size_t size);
int snd_pcm_hw_info_complete(snd_pcm_hw_params_t *info);
int snd_pcm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info);
int snd_pcm_channel_info_shm(snd_pcm_t *pcm, snd_pcm_channel_info_t *info, int shmid);

static inline size_t snd_pcm_mmap_playback_avail(snd_pcm_t *pcm)
{
	ssize_t avail;
	avail = *pcm->hw_ptr + pcm->buffer_size - *pcm->appl_ptr;
	if (avail < 0)
		avail += pcm->boundary;
	return avail;
}

static inline size_t snd_pcm_mmap_capture_avail(snd_pcm_t *pcm)
{
	ssize_t avail;
	avail = *pcm->hw_ptr - *pcm->appl_ptr;
	if (avail < 0)
		avail += pcm->boundary;
	return avail;
}

static inline size_t snd_pcm_mmap_avail(snd_pcm_t *pcm)
{
	ssize_t avail;
	avail = *pcm->hw_ptr - *pcm->appl_ptr;
	if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
		avail += pcm->buffer_size;
	if (avail < 0)
		avail += pcm->boundary;
	return avail;
}

static inline ssize_t snd_pcm_mmap_playback_hw_avail(snd_pcm_t *pcm)
{
	ssize_t avail;
	avail = *pcm->hw_ptr + pcm->buffer_size - *pcm->appl_ptr;
	if (avail < 0)
		avail += pcm->boundary;
	return pcm->buffer_size - avail;
}

static inline ssize_t snd_pcm_mmap_capture_hw_avail(snd_pcm_t *pcm)
{
	ssize_t avail;
	avail = *pcm->hw_ptr - *pcm->appl_ptr;
	if (avail < 0)
		avail += pcm->boundary;
	return pcm->buffer_size - avail;
}

static inline ssize_t snd_pcm_mmap_hw_avail(snd_pcm_t *pcm)
{
	ssize_t avail;
	avail = *pcm->hw_ptr - *pcm->appl_ptr;
	if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
		avail += pcm->buffer_size;
	if (avail < 0)
		avail += pcm->boundary;
	return pcm->buffer_size - avail;
}

#define snd_pcm_mmap_playback_delay snd_pcm_mmap_playback_hw_avail
#define snd_pcm_mmap_capture_delay snd_pcm_mmap_capture_avail

static inline ssize_t snd_pcm_mmap_delay(snd_pcm_t *pcm)
{
	if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
		return snd_pcm_mmap_playback_delay(pcm);
	else
		return snd_pcm_mmap_capture_delay(pcm);
}

static inline void *snd_pcm_channel_area_addr(const snd_pcm_channel_area_t *area, size_t offset)
{
	size_t bitofs = area->first + area->step * offset;
	assert(bitofs % 8 == 0);
	return area->addr + bitofs / 8;
}

static inline size_t snd_pcm_channel_area_step(const snd_pcm_channel_area_t *area)
{
	assert(area->step % 8 == 0);
	return area->step / 8;
}

static inline ssize_t _snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, size_t size)
{
	return pcm->fast_ops->writei(pcm->fast_op_arg, buffer, size);
}

static inline ssize_t _snd_pcm_writen(snd_pcm_t *pcm, void **bufs, size_t size)
{
	return pcm->fast_ops->writen(pcm->fast_op_arg, bufs, size);
}

static inline ssize_t _snd_pcm_readi(snd_pcm_t *pcm, void *buffer, size_t size)
{
	return pcm->fast_ops->readi(pcm->fast_op_arg, buffer, size);
}

static inline ssize_t _snd_pcm_readn(snd_pcm_t *pcm, void **bufs, size_t size)
{
	return pcm->fast_ops->readn(pcm->fast_op_arg, bufs, size);
}

static inline int muldiv(int a, int b, int c, int *r)
{
	int64_t n = (int64_t)a * b;
	int64_t v = n / c;
	if (v > INT_MAX) {
		*r = 0;
		return INT_MAX;
	}
	if (v < INT_MIN) {
		*r = 0;
		return INT_MIN;
	}
	*r = n % c;
	return v;
}

static inline int muldiv_down(int a, int b, int c)
{
	int64_t v = (int64_t)a * b / c;
	if (v > INT_MAX) {
		return INT_MAX;
	}
	if (v < INT_MIN) {
		return INT_MIN;
	}
	return v;
}

static inline int muldiv_near(int a, int b, int c)
{
	int r;
	int n = muldiv(a, b, c, &r);
	if (r >= (c + 1) / 2)
		n++;
	return n;
}

int _snd_pcm_hw_refine(snd_pcm_hw_params_t *params);
void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params);
int _snd_pcm_hw_params_mask(snd_pcm_hw_params_t *params, int hw,
			    unsigned int var, const mask_t *mask);
int _snd_pcm_hw_params_first(snd_pcm_hw_params_t *params, int hw,
			     unsigned int var);
int _snd_pcm_hw_params_last(snd_pcm_hw_params_t *params, int hw,
			    unsigned int var);
int _snd_pcm_hw_params_set(snd_pcm_hw_params_t *params, int hw,
			   unsigned int var, unsigned int val);
int _snd_pcm_hw_params_min(snd_pcm_hw_params_t *params, int hw,
			   unsigned int var, unsigned int val);
int _snd_pcm_hw_params_max(snd_pcm_hw_params_t *params, int hw,
			   unsigned int var, unsigned int val);
int snd_pcm_hw_refine2(snd_pcm_hw_params_t *params,
		       snd_pcm_hw_params_t *sparams,
		       int (*func)(snd_pcm_t *slave,
				   snd_pcm_hw_params_t *params),
		       snd_pcm_t *slave,
		       unsigned int links);
int snd_pcm_hw_params2(snd_pcm_hw_params_t *params,
		       snd_pcm_hw_params_t *sparams,
		       int (*func)(snd_pcm_t *slave, 
				   snd_pcm_hw_params_t *sparams),
		       snd_pcm_t *slave,
		       unsigned int links);

#define SND_PCM_HW_PARBIT_ACCESS	(1 << SND_PCM_HW_PARAM_ACCESS)
#define SND_PCM_HW_PARBIT_FORMAT	(1 << SND_PCM_HW_PARAM_FORMAT)
#define SND_PCM_HW_PARBIT_SUBFORMAT	(1 << SND_PCM_HW_PARAM_SUBFORMAT)
#define SND_PCM_HW_PARBIT_CHANNELS	(1 << SND_PCM_HW_PARAM_CHANNELS)
#define SND_PCM_HW_PARBIT_RATE		(1 << SND_PCM_HW_PARAM_RATE)
#define SND_PCM_HW_PARBIT_FRAGMENT_LENGTH (1 << SND_PCM_HW_PARAM_FRAGMENT_LENGTH)
#define SND_PCM_HW_PARBIT_FRAGMENT_SIZE	(1 << SND_PCM_HW_PARAM_FRAGMENT_SIZE)
#define SND_PCM_HW_PARBIT_FRAGMENTS	(1 << SND_PCM_HW_PARAM_FRAGMENTS)
#define SND_PCM_HW_PARBIT_BUFFER_LENGTH	(1 << SND_PCM_HW_PARAM_BUFFER_LENGTH)
#define SND_PCM_HW_PARBIT_BUFFER_SIZE	(1 << SND_PCM_HW_PARAM_BUFFER_SIZE)
#define SND_PCM_HW_PARBIT_SAMPLE_BITS	(1 << SND_PCM_HW_PARAM_SAMPLE_BITS)
#define SND_PCM_HW_PARBIT_FRAME_BITS	(1 << SND_PCM_HW_PARAM_FRAME_BITS)
#define SND_PCM_HW_PARBIT_FRAGMENT_BYTES (1 << SND_PCM_HW_PARAM_FRAGMENT_BYTES)
#define SND_PCM_HW_PARBIT_BUFFER_BYTES	(1 << SND_PCM_HW_PARAM_BUFFER_BYTES)


#define SND_PCM_ACCBIT_MMAP ((1 << SND_PCM_ACCESS_MMAP_INTERLEAVED) | \
			     (1 << SND_PCM_ACCESS_MMAP_NONINTERLEAVED) | \
			     (1 << SND_PCM_ACCESS_MMAP_COMPLEX))