GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/sndiod/siofile.c Lines: 0 274 0.0 %
Date: 2016-12-06 Branches: 0 142 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: siofile.c,v 1.12 2016/05/25 10:24:24 ratchov Exp $	*/
2
/*
3
 * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
4
 *
5
 * Permission to use, copy, modify, and distribute this software for any
6
 * purpose with or without fee is hereby granted, provided that the above
7
 * copyright notice and this permission notice appear in all copies.
8
 *
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
 */
17
#include <sys/time.h>
18
#include <sys/types.h>
19
20
#include <poll.h>
21
#include <sndio.h>
22
#include <stdio.h>
23
#include <stdlib.h>
24
#include <string.h>
25
26
#include "abuf.h"
27
#include "defs.h"
28
#include "dev.h"
29
#include "dsp.h"
30
#include "fdpass.h"
31
#include "file.h"
32
#include "siofile.h"
33
#include "utils.h"
34
35
#define WATCHDOG_USEC	4000000		/* 4 seconds */
36
37
void dev_sio_onmove(void *, int);
38
void dev_sio_timeout(void *);
39
int dev_sio_pollfd(void *, struct pollfd *);
40
int dev_sio_revents(void *, struct pollfd *);
41
void dev_sio_run(void *);
42
void dev_sio_hup(void *);
43
44
struct fileops dev_sio_ops = {
45
	"sio",
46
	dev_sio_pollfd,
47
	dev_sio_revents,
48
	dev_sio_run,
49
	dev_sio_run,
50
	dev_sio_hup
51
};
52
53
void
54
dev_sio_onmove(void *arg, int delta)
55
{
56
	struct dev *d = arg;
57
58
#ifdef DEBUG
59
	if (log_level >= 4) {
60
		dev_log(d);
61
		log_puts(": tick, delta = ");
62
		log_puti(delta);
63
		log_puts("\n");
64
	}
65
	d->sio.sum_utime += file_utime - d->sio.utime;
66
	d->sio.sum_wtime += file_wtime - d->sio.wtime;
67
	d->sio.wtime = file_wtime;
68
	d->sio.utime = file_utime;
69
	if (d->mode & MODE_PLAY)
70
		d->sio.pused -= delta;
71
	if (d->mode & MODE_REC)
72
		d->sio.rused += delta;
73
#endif
74
	dev_onmove(d, delta);
75
}
76
77
void
78
dev_sio_timeout(void *arg)
79
{
80
	struct dev *d = arg;
81
82
	dev_log(d);
83
	log_puts(": watchdog timeout\n");
84
	dev_close(d);
85
}
86
87
/*
88
 * open the device.
89
 */
90
int
91
dev_sio_open(struct dev *d)
92
{
93
	struct sio_par par;
94
	unsigned int mode = d->mode & (MODE_PLAY | MODE_REC);
95
96
	d->sio.hdl = fdpass_sio_open(d->num, mode);
97
	if (d->sio.hdl == NULL) {
98
		if (mode != (SIO_PLAY | SIO_REC))
99
			return 0;
100
		d->sio.hdl = fdpass_sio_open(d->num, SIO_PLAY);
101
		if (d->sio.hdl != NULL)
102
			mode = SIO_PLAY;
103
		else {
104
			d->sio.hdl = fdpass_sio_open(d->num, SIO_REC);
105
			if (d->sio.hdl != NULL)
106
				mode = SIO_REC;
107
			else
108
				return 0;
109
		}
110
		if (log_level >= 1) {
111
			log_puts("warning, device opened in ");
112
			log_puts(mode == SIO_PLAY ? "play-only" : "rec-only");
113
			log_puts(" mode\n");
114
		}
115
	}
116
	sio_initpar(&par);
117
	par.bits = d->par.bits;
118
	par.bps = d->par.bps;
119
	par.sig = d->par.sig;
120
	par.le = d->par.le;
121
	par.msb = d->par.msb;
122
	if (mode & SIO_PLAY)
123
		par.pchan = d->pchan;
124
	if (mode & SIO_REC)
125
		par.rchan = d->rchan;
126
	if (d->bufsz)
127
		par.appbufsz = d->bufsz;
128
	if (d->round)
129
		par.round = d->round;
130
	if (d->rate)
131
		par.rate = d->rate;
132
	if (!sio_setpar(d->sio.hdl, &par))
133
		goto bad_close;
134
	if (!sio_getpar(d->sio.hdl, &par))
135
		goto bad_close;
136
137
#ifdef DEBUG
138
	/*
139
	 * We support any parameters combination exposed by the kernel,
140
	 * and we have no other choice than trusting the kernel for
141
	 * returning correct parameters. But let's check parameters
142
	 * early and nicely report kernel bugs rather than crashing
143
	 * later in memset(), malloc() or alike.
144
	 */
145
146
	if (par.bits > BITS_MAX) {
147
		log_puts(d->path);
148
		log_puts(": ");
149
		log_putu(par.bits);
150
		log_puts(": unsupported number of bits\n");
151
		goto bad_close;
152
	}
153
	if (par.bps > SIO_BPS(BITS_MAX)) {
154
		log_puts(d->path);
155
		log_puts(": ");
156
		log_putu(par.bits);
157
		log_puts(": unsupported sample size\n");
158
		goto bad_close;
159
	}
160
	if ((mode & SIO_PLAY) && par.pchan > NCHAN_MAX) {
161
		log_puts(d->path);
162
		log_puts(": ");
163
		log_putu(par.pchan);
164
		log_puts(": unsupported number of play channels\n");
165
		goto bad_close;
166
	}
167
	if ((mode & SIO_REC) && par.rchan > NCHAN_MAX) {
168
		log_puts(d->path);
169
		log_puts(": ");
170
		log_putu(par.rchan);
171
		log_puts(": unsupported number of rec channels\n");
172
		goto bad_close;
173
	}
174
	if (par.bufsz == 0 || par.bufsz > RATE_MAX) {
175
		log_puts(d->path);
176
		log_puts(": ");
177
		log_putu(par.bufsz);
178
		log_puts(": unsupported buffer size\n");
179
		goto bad_close;
180
	}
181
	if (par.round == 0 || par.round > par.bufsz ||
182
	    par.bufsz % par.round != 0) {
183
		log_puts(d->path);
184
		log_puts(": ");
185
		log_putu(par.round);
186
		log_puts(": unsupported block size\n");
187
		goto bad_close;
188
	}
189
	if (par.rate == 0 || par.rate > RATE_MAX) {
190
		log_puts(d->path);
191
		log_puts(": ");
192
		log_putu(par.rate);
193
		log_puts(": unsupported rate\n");
194
		goto bad_close;
195
	}
196
#endif
197
198
	d->par.bits = par.bits;
199
	d->par.bps = par.bps;
200
	d->par.sig = par.sig;
201
	d->par.le = par.le;
202
	d->par.msb = par.msb;
203
	if (mode & SIO_PLAY)
204
		d->pchan = par.pchan;
205
	if (mode & SIO_REC)
206
		d->rchan = par.rchan;
207
	d->bufsz = par.bufsz;
208
	d->round = par.round;
209
	d->rate = par.rate;
210
	if (!(mode & MODE_PLAY))
211
		d->mode &= ~(MODE_PLAY | MODE_MON);
212
	if (!(mode & MODE_REC))
213
		d->mode &= ~MODE_REC;
214
	sio_onmove(d->sio.hdl, dev_sio_onmove, d);
215
	d->sio.file = file_new(&dev_sio_ops, d, d->path, sio_nfds(d->sio.hdl));
216
	timo_set(&d->sio.watchdog, dev_sio_timeout, d);
217
	return 1;
218
 bad_close:
219
	sio_close(d->sio.hdl);
220
	return 0;
221
}
222
223
void
224
dev_sio_close(struct dev *d)
225
{
226
#ifdef DEBUG
227
	if (log_level >= 3) {
228
		dev_log(d);
229
		log_puts(": closed\n");
230
	}
231
#endif
232
	timo_del(&d->sio.watchdog);
233
	file_del(d->sio.file);
234
	sio_close(d->sio.hdl);
235
}
236
237
void
238
dev_sio_start(struct dev *d)
239
{
240
	if (!sio_start(d->sio.hdl)) {
241
		if (log_level >= 1) {
242
			dev_log(d);
243
			log_puts(": failed to start device\n");
244
		}
245
		return;
246
	}
247
	if (d->mode & MODE_PLAY) {
248
		d->sio.cstate = DEV_SIO_CYCLE;
249
		d->sio.todo = 0;
250
	} else {
251
		d->sio.cstate = DEV_SIO_READ;
252
		d->sio.todo = d->round * d->rchan * d->par.bps;
253
	}
254
#ifdef DEBUG
255
	d->sio.pused = 0;
256
	d->sio.rused = 0;
257
	d->sio.sum_utime = 0;
258
	d->sio.sum_wtime = 0;
259
	d->sio.wtime = file_wtime;
260
	d->sio.utime = file_utime;
261
	if (log_level >= 3) {
262
		dev_log(d);
263
		log_puts(": started\n");
264
	}
265
#endif
266
	timo_add(&d->sio.watchdog, WATCHDOG_USEC);
267
}
268
269
void
270
dev_sio_stop(struct dev *d)
271
{
272
	if (!sio_eof(d->sio.hdl) && !sio_stop(d->sio.hdl)) {
273
		if (log_level >= 1) {
274
			dev_log(d);
275
			log_puts(": failed to stop device\n");
276
		}
277
		return;
278
	}
279
#ifdef DEBUG
280
	if (log_level >= 3) {
281
		dev_log(d);
282
		log_puts(": stopped, load avg = ");
283
		log_puti(d->sio.sum_utime / 1000);
284
		log_puts(" / ");
285
		log_puti(d->sio.sum_wtime / 1000);
286
		log_puts("\n");
287
	}
288
#endif
289
	timo_del(&d->sio.watchdog);
290
}
291
292
int
293
dev_sio_pollfd(void *arg, struct pollfd *pfd)
294
{
295
	struct dev *d = arg;
296
	int events;
297
298
	events = (d->sio.cstate == DEV_SIO_READ) ? POLLIN : POLLOUT;
299
	return sio_pollfd(d->sio.hdl, pfd, events);
300
}
301
302
int
303
dev_sio_revents(void *arg, struct pollfd *pfd)
304
{
305
	struct dev *d = arg;
306
	int events;
307
308
	events = sio_revents(d->sio.hdl, pfd);
309
#ifdef DEBUG
310
	d->sio.events = events;
311
#endif
312
	return events;
313
}
314
315
void
316
dev_sio_run(void *arg)
317
{
318
	struct dev *d = arg;
319
	unsigned char *data, *base;
320
	unsigned int n;
321
322
	/*
323
	 * sio_read() and sio_write() would block at the end of the
324
	 * cycle so we *must* return and restart poll()'ing. Otherwise
325
	 * we may trigger dev_cycle() which would make all clients
326
	 * underrun (ex, on a play-only device)
327
	 */
328
	for (;;) {
329
		if (d->pstate != DEV_RUN)
330
			return;
331
		switch (d->sio.cstate) {
332
		case DEV_SIO_READ:
333
#ifdef DEBUG
334
			if (!(d->sio.events & POLLIN)) {
335
				dev_log(d);
336
				log_puts(": recording, but POLLIN not set\n");
337
				panic();
338
			}
339
			if (d->sio.todo == 0) {
340
				dev_log(d);
341
				log_puts(": can't read data\n");
342
				panic();
343
			}
344
			if (d->prime > 0) {
345
				dev_log(d);
346
				log_puts(": unexpected data\n");
347
				panic();
348
			}
349
#endif
350
			base = d->decbuf ? d->decbuf : (unsigned char *)d->rbuf;
351
			data = base +
352
			    d->rchan * d->round * d->par.bps -
353
			    d->sio.todo;
354
			n = sio_read(d->sio.hdl, data, d->sio.todo);
355
			d->sio.todo -= n;
356
#ifdef DEBUG
357
			if (log_level >= 4) {
358
				dev_log(d);
359
				log_puts(": read ");
360
				log_putu(n);
361
				log_puts(": bytes, todo ");
362
				log_putu(d->sio.todo);
363
				log_puts("/");
364
				log_putu(d->round * d->rchan * d->par.bps);
365
				log_puts("\n");
366
			}
367
#endif
368
			if (d->sio.todo > 0)
369
				return;
370
#ifdef DEBUG
371
			d->sio.rused -= d->round;
372
			if (log_level >= 2) {
373
				if (d->sio.rused >= d->round) {
374
					dev_log(d);
375
					log_puts(": rec hw xrun, rused = ");
376
					log_puti(d->sio.rused);
377
					log_puts("/");
378
					log_puti(d->bufsz);
379
					log_puts("\n");
380
				}
381
				if (d->sio.rused < 0 ||
382
				    d->sio.rused >= d->bufsz) {
383
					dev_log(d);
384
					log_puts(": out of bounds rused = ");
385
					log_puti(d->sio.rused);
386
					log_puts("/");
387
					log_puti(d->bufsz);
388
					log_puts("\n");
389
				}
390
			}
391
#endif
392
			d->sio.cstate = DEV_SIO_CYCLE;
393
			break;
394
		case DEV_SIO_CYCLE:
395
			timo_del(&d->sio.watchdog);
396
			timo_add(&d->sio.watchdog, WATCHDOG_USEC);
397
398
#ifdef DEBUG
399
			/*
400
			 * check that we're called at cycle boundary:
401
			 * either after a recorded block, or when POLLOUT is
402
			 * raised
403
			 */
404
			if (!((d->mode & MODE_REC) && d->prime == 0) &&
405
			    !(d->sio.events & POLLOUT)) {
406
				dev_log(d);
407
				log_puts(": cycle not at block boundary\n");
408
				panic();
409
			}
410
#endif
411
			dev_cycle(d);
412
			if (d->mode & MODE_PLAY) {
413
				d->sio.cstate = DEV_SIO_WRITE;
414
				d->sio.todo = d->round * d->pchan * d->par.bps;
415
				break;
416
			} else {
417
				d->sio.cstate = DEV_SIO_READ;
418
				d->sio.todo = d->round * d->rchan * d->par.bps;
419
				return;
420
			}
421
		case DEV_SIO_WRITE:
422
#ifdef DEBUG
423
			if (d->sio.todo == 0) {
424
				dev_log(d);
425
				log_puts(": can't write data\n");
426
				panic();
427
			}
428
#endif
429
			base = d->encbuf ? d->encbuf : (unsigned char *)DEV_PBUF(d);
430
			data = base +
431
			    d->pchan * d->round * d->par.bps -
432
			    d->sio.todo;
433
			n = sio_write(d->sio.hdl, data, d->sio.todo);
434
			d->sio.todo -= n;
435
#ifdef DEBUG
436
			if (log_level >= 4) {
437
				dev_log(d);
438
				log_puts(": wrote ");
439
				log_putu(n);
440
				log_puts(" bytes, todo ");
441
				log_putu(d->sio.todo);
442
				log_puts("/");
443
				log_putu(d->round * d->pchan * d->par.bps);
444
				log_puts("\n");
445
			}
446
#endif
447
			if (d->sio.todo > 0)
448
				return;
449
#ifdef DEBUG
450
			d->sio.pused += d->round;
451
			if (log_level >= 2) {
452
				if (d->prime == 0 &&
453
				    d->sio.pused <= d->bufsz - d->round) {
454
					dev_log(d);
455
					log_puts(": play hw xrun, pused = ");
456
					log_puti(d->sio.pused);
457
					log_puts("/");
458
					log_puti(d->bufsz);
459
					log_puts("\n");
460
				}
461
				if (d->sio.pused < 0 ||
462
				    d->sio.pused > d->bufsz) {
463
					/* device driver or libsndio bug */
464
					dev_log(d);
465
					log_puts(": out of bounds pused = ");
466
					log_puti(d->sio.pused);
467
					log_puts("/");
468
					log_puti(d->bufsz);
469
					log_puts("\n");
470
				}
471
			}
472
#endif
473
			d->poffs += d->round;
474
			if (d->poffs == d->psize)
475
				d->poffs = 0;
476
			if ((d->mode & MODE_REC) && d->prime == 0) {
477
				d->sio.cstate = DEV_SIO_READ;
478
				d->sio.todo = d->round * d->rchan * d->par.bps;
479
			} else
480
				d->sio.cstate = DEV_SIO_CYCLE;
481
			return;
482
		}
483
	}
484
}
485
486
void
487
dev_sio_hup(void *arg)
488
{
489
	struct dev *d = arg;
490
491
#ifdef DEBUG
492
	if (log_level >= 2) {
493
		dev_log(d);
494
		log_puts(": disconnected\n");
495
	}
496
#endif
497
	dev_close(d);
498
}