xref: /openbsd-src/usr.bin/sndiod/siofile.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: siofile.c,v 1.4 2014/03/17 17:16:06 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 "file.h"
31 #include "siofile.h"
32 #include "utils.h"
33 
34 #define WATCHDOG_USEC	2000000		/* 2 seconds */
35 
36 void dev_sio_onmove(void *, int);
37 void dev_sio_timeout(void *);
38 int dev_sio_pollfd(void *, struct pollfd *);
39 int dev_sio_revents(void *, struct pollfd *);
40 void dev_sio_run(void *);
41 void dev_sio_hup(void *);
42 
43 struct fileops dev_sio_ops = {
44 	"sio",
45 	dev_sio_pollfd,
46 	dev_sio_revents,
47 	dev_sio_run,
48 	dev_sio_run,
49 	dev_sio_hup
50 };
51 
52 void
53 dev_sio_onmove(void *arg, int delta)
54 {
55 	struct dev *d = arg;
56 
57 #ifdef DEBUG
58 	if (log_level >= 4) {
59 		dev_log(d);
60 		log_puts(": tick, delta = ");
61 		log_puti(delta);
62 		log_puts("\n");
63 	}
64 	d->sio.sum_utime += file_utime - d->sio.utime;
65 	d->sio.sum_wtime += file_wtime - d->sio.wtime;
66 	d->sio.wtime = file_wtime;
67 	d->sio.utime = file_utime;
68 	if (d->mode & MODE_PLAY)
69 		d->sio.pused -= delta;
70 	if (d->mode & MODE_REC)
71 		d->sio.rused += delta;
72 #endif
73 	dev_onmove(d, delta);
74 }
75 
76 void
77 dev_sio_timeout(void *arg)
78 {
79 	struct dev *d = arg;
80 
81 	dev_log(d);
82 	log_puts(": watchdog timeout\n");
83 	dev_close(d);
84 }
85 
86 /*
87  * open the device.
88  */
89 int
90 dev_sio_open(struct dev *d)
91 {
92 	struct sio_par par;
93 	unsigned int mode = d->mode & (MODE_PLAY | MODE_REC);
94 
95 	d->sio.hdl = sio_open(d->path, mode, 1);
96 	if (d->sio.hdl == NULL) {
97 		if (mode != (SIO_PLAY | SIO_REC))
98 			return 0;
99 		d->sio.hdl = sio_open(d->path, SIO_PLAY, 1);
100 		if (d->sio.hdl != NULL)
101 			mode = SIO_PLAY;
102 		else {
103 			d->sio.hdl = sio_open(d->path, SIO_REC, 1);
104 			if (d->sio.hdl != NULL)
105 				mode = SIO_REC;
106 			else
107 				return 0;
108 		}
109 		if (log_level >= 1) {
110 			log_puts("warning, device opened in ");
111 			log_puts(mode == SIO_PLAY ? "play-only" : "rec-only");
112 			log_puts(" mode\n");
113 		}
114 	}
115 	sio_initpar(&par);
116 	par.bits = d->par.bits;
117 	par.bps = d->par.bps;
118 	par.sig = d->par.sig;
119 	par.le = d->par.le;
120 	par.msb = d->par.msb;
121 	if (mode & SIO_PLAY)
122 		par.pchan = d->pchan;
123 	if (mode & SIO_REC)
124 		par.rchan = d->rchan;
125 	if (d->bufsz)
126 		par.appbufsz = d->bufsz;
127 	if (d->round)
128 		par.round = d->round;
129 	if (d->rate)
130 		par.rate = d->rate;
131 	if (!sio_setpar(d->sio.hdl, &par))
132 		goto bad_close;
133 	if (!sio_getpar(d->sio.hdl, &par))
134 		goto bad_close;
135 	d->par.bits = par.bits;
136 	d->par.bps = par.bps;
137 	d->par.sig = par.sig;
138 	d->par.le = par.le;
139 	d->par.msb = par.msb;
140 	if (mode & SIO_PLAY)
141 		d->pchan = par.pchan;
142 	if (mode & SIO_REC)
143 		d->rchan = par.rchan;
144 	d->bufsz = par.bufsz;
145 	d->round = par.round;
146 	d->rate = par.rate;
147 	if (!(mode & MODE_PLAY))
148 		d->mode &= ~(MODE_PLAY | MODE_MON);
149 	if (!(mode & MODE_REC))
150 		d->mode &= ~MODE_REC;
151 	sio_onmove(d->sio.hdl, dev_sio_onmove, d);
152 	d->sio.file = file_new(&dev_sio_ops, d, d->path, sio_nfds(d->sio.hdl));
153 	timo_set(&d->sio.watchdog, dev_sio_timeout, d);
154 	return 1;
155  bad_close:
156 	sio_close(d->sio.hdl);
157 	return 0;
158 }
159 
160 void
161 dev_sio_close(struct dev *d)
162 {
163 #ifdef DEBUG
164 	if (log_level >= 3) {
165 		dev_log(d);
166 		log_puts(": closed\n");
167 	}
168 #endif
169 	file_del(d->sio.file);
170 	sio_close(d->sio.hdl);
171 }
172 
173 void
174 dev_sio_start(struct dev *d)
175 {
176 	if (!sio_start(d->sio.hdl)) {
177 		if (log_level >= 1) {
178 			dev_log(d);
179 			log_puts(": failed to start device\n");
180 		}
181 		return;
182 	}
183 	if (d->mode & MODE_PLAY) {
184 		d->sio.cstate = DEV_SIO_CYCLE;
185 		d->sio.todo = 0;
186 	} else {
187 		d->sio.cstate = DEV_SIO_READ;
188 		d->sio.todo = d->round * d->rchan * d->par.bps;
189 	}
190 #ifdef DEBUG
191 	d->sio.pused = 0;
192 	d->sio.rused = 0;
193 	d->sio.sum_utime = 0;
194 	d->sio.sum_wtime = 0;
195 	d->sio.wtime = file_wtime;
196 	d->sio.utime = file_utime;
197 	if (log_level >= 3) {
198 		dev_log(d);
199 		log_puts(": started\n");
200 	}
201 #endif
202 	timo_add(&d->sio.watchdog, WATCHDOG_USEC);
203 }
204 
205 void
206 dev_sio_stop(struct dev *d)
207 {
208 	if (!sio_eof(d->sio.hdl) && !sio_stop(d->sio.hdl)) {
209 		if (log_level >= 1) {
210 			dev_log(d);
211 			log_puts(": failed to stop device\n");
212 		}
213 		return;
214 	}
215 #ifdef DEBUG
216 	if (log_level >= 3) {
217 		dev_log(d);
218 		log_puts(": stopped, load avg = ");
219 		log_puti(d->sio.sum_utime / 1000);
220 		log_puts(" / ");
221 		log_puti(d->sio.sum_wtime / 1000);
222 		log_puts("\n");
223 	}
224 #endif
225 	timo_del(&d->sio.watchdog);
226 }
227 
228 int
229 dev_sio_pollfd(void *arg, struct pollfd *pfd)
230 {
231 	struct dev *d = arg;
232 	int events;
233 
234 	events = (d->sio.cstate == DEV_SIO_READ) ? POLLIN : POLLOUT;
235 	return sio_pollfd(d->sio.hdl, pfd, events);
236 }
237 
238 int
239 dev_sio_revents(void *arg, struct pollfd *pfd)
240 {
241 	struct dev *d = arg;
242 	int events;
243 
244 	events = sio_revents(d->sio.hdl, pfd);
245 #ifdef DEBUG
246 	d->sio.events = events;
247 #endif
248 	return events;
249 }
250 
251 void
252 dev_sio_run(void *arg)
253 {
254 	struct dev *d = arg;
255 	unsigned char *data, *base;
256 	unsigned int n;
257 
258 	/*
259 	 * sio_read() and sio_write() would block at the end of the
260 	 * cycle so we *must* return and restart poll()'ing. Otherwise
261 	 * we may trigger dev_cycle() which would make all clients
262 	 * underrun (ex, on a play-only device)
263 	 */
264 	for (;;) {
265 		if (d->pstate != DEV_RUN)
266 			return;
267 		switch (d->sio.cstate) {
268 		case DEV_SIO_READ:
269 #ifdef DEBUG
270 			if (!(d->sio.events & POLLIN)) {
271 				dev_log(d);
272 				log_puts(": recording, but POLLIN not set\n");
273 				panic();
274 			}
275 			if (d->sio.todo == 0) {
276 				dev_log(d);
277 				log_puts(": can't read data\n");
278 				panic();
279 			}
280 			if (d->prime > 0) {
281 				dev_log(d);
282 				log_puts(": unexpected data\n");
283 				panic();
284 			}
285 #endif
286 			base = d->decbuf ? d->decbuf : (unsigned char *)d->rbuf;
287 			data = base +
288 			    d->rchan * d->round * d->par.bps -
289 			    d->sio.todo;
290 			n = sio_read(d->sio.hdl, data, d->sio.todo);
291 			d->sio.todo -= n;
292 #ifdef DEBUG
293 			if (n == 0 && data == base && !sio_eof(d->sio.hdl)) {
294 				dev_log(d);
295 				log_puts(": read blocked at cycle start\n");
296 			}
297 			if (log_level >= 4) {
298 				dev_log(d);
299 				log_puts(": read ");
300 				log_putu(n);
301 				log_puts(": bytes, todo ");
302 				log_putu(d->sio.todo);
303 				log_puts("/");
304 				log_putu(d->round * d->rchan * d->par.bps);
305 				log_puts("\n");
306 			}
307 #endif
308 			if (d->sio.todo > 0)
309 				return;
310 #ifdef DEBUG
311 			d->sio.rused -= d->round;
312 			if (log_level >= 2) {
313 				if (d->sio.rused >= d->round) {
314 					dev_log(d);
315 					log_puts(": rec hw xrun, rused = ");
316 					log_puti(d->sio.rused);
317 					log_puts("/");
318 					log_puti(d->bufsz);
319 					log_puts("\n");
320 				}
321 				if (d->sio.rused < 0 ||
322 				    d->sio.rused >= d->bufsz) {
323 					dev_log(d);
324 					log_puts(": out of bounds rused = ");
325 					log_puti(d->sio.rused);
326 					log_puts("/");
327 					log_puti(d->bufsz);
328 					log_puts("\n");
329 				}
330 			}
331 #endif
332 			d->sio.cstate = DEV_SIO_CYCLE;
333 			break;
334 		case DEV_SIO_CYCLE:
335 			timo_del(&d->sio.watchdog);
336 			timo_add(&d->sio.watchdog, WATCHDOG_USEC);
337 
338 #ifdef DEBUG
339 			/*
340 			 * check that we're called at cycle boundary:
341 			 * either after a recorded block, or when POLLOUT is
342 			 * raised
343 			 */
344 			if (!((d->mode & MODE_REC) && d->prime == 0) &&
345 			    !(d->sio.events & POLLOUT)) {
346 				dev_log(d);
347 				log_puts(": cycle not at block boundary\n");
348 				panic();
349 			}
350 #endif
351 			dev_cycle(d);
352 			if (d->mode & MODE_PLAY) {
353 				d->sio.cstate = DEV_SIO_WRITE;
354 				d->sio.todo = d->round * d->pchan * d->par.bps;
355 				break;
356 			} else {
357 				d->sio.cstate = DEV_SIO_READ;
358 				d->sio.todo = d->round * d->rchan * d->par.bps;
359 				return;
360 			}
361 		case DEV_SIO_WRITE:
362 #ifdef DEBUG
363 			if (d->sio.todo == 0) {
364 				dev_log(d);
365 				log_puts(": can't write data\n");
366 				panic();
367 			}
368 #endif
369 			base = d->encbuf ? d->encbuf : (unsigned char *)DEV_PBUF(d);
370 			data = base +
371 			    d->pchan * d->round * d->par.bps -
372 			    d->sio.todo;
373 			n = sio_write(d->sio.hdl, data, d->sio.todo);
374 			d->sio.todo -= n;
375 #ifdef DEBUG
376 			if (n == 0 && data == base && !sio_eof(d->sio.hdl)) {
377 				dev_log(d);
378 				log_puts(": write blocked at cycle start\n");
379 			}
380 			if (log_level >= 4) {
381 				dev_log(d);
382 				log_puts(": wrote ");
383 				log_putu(n);
384 				log_puts(" bytes, todo ");
385 				log_putu(d->sio.todo);
386 				log_puts("/");
387 				log_putu(d->round * d->pchan * d->par.bps);
388 				log_puts("\n");
389 			}
390 #endif
391 			if (d->sio.todo > 0)
392 				return;
393 #ifdef DEBUG
394 			d->sio.pused += d->round;
395 			if (log_level >= 2) {
396 				if (d->prime == 0 &&
397 				    d->sio.pused <= d->bufsz - d->round) {
398 					dev_log(d);
399 					log_puts(": play hw xrun, pused = ");
400 					log_puti(d->sio.pused);
401 					log_puts("/");
402 					log_puti(d->bufsz);
403 					log_puts("\n");
404 				}
405 				if (d->sio.pused < 0 ||
406 				    d->sio.pused > d->bufsz) {
407 					/* device driver or libsndio bug */
408 					dev_log(d);
409 					log_puts(": out of bounds pused = ");
410 					log_puti(d->sio.pused);
411 					log_puts("/");
412 					log_puti(d->bufsz);
413 					log_puts("\n");
414 				}
415 			}
416 #endif
417 			d->poffs += d->round;
418 			if (d->poffs == d->psize)
419 				d->poffs = 0;
420 			if ((d->mode & MODE_REC) && d->prime == 0) {
421 				d->sio.cstate = DEV_SIO_READ;
422 				d->sio.todo = d->round * d->rchan * d->par.bps;
423 			} else
424 				d->sio.cstate = DEV_SIO_CYCLE;
425 			return;
426 		}
427 	}
428 }
429 
430 void
431 dev_sio_hup(void *arg)
432 {
433 	struct dev *d = arg;
434 
435 	dev_close(d);
436 }
437