xref: /openbsd-src/usr.bin/sndiod/siofile.c (revision 7b6392009e6e5a7f8e494c162a4d259ea5e13a62)
1*7b639200Sratchov /*	$OpenBSD: siofile.c,v 1.28 2024/12/20 07:35:56 ratchov Exp $	*/
287bc9f6aSratchov /*
387bc9f6aSratchov  * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
487bc9f6aSratchov  *
587bc9f6aSratchov  * Permission to use, copy, modify, and distribute this software for any
687bc9f6aSratchov  * purpose with or without fee is hereby granted, provided that the above
787bc9f6aSratchov  * copyright notice and this permission notice appear in all copies.
887bc9f6aSratchov  *
987bc9f6aSratchov  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1087bc9f6aSratchov  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1187bc9f6aSratchov  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1287bc9f6aSratchov  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1387bc9f6aSratchov  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1487bc9f6aSratchov  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1587bc9f6aSratchov  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1687bc9f6aSratchov  */
1787bc9f6aSratchov #include <sys/time.h>
1887bc9f6aSratchov #include <sys/types.h>
1987bc9f6aSratchov 
2087bc9f6aSratchov #include <poll.h>
2187bc9f6aSratchov #include <sndio.h>
2287bc9f6aSratchov #include <stdio.h>
2387bc9f6aSratchov #include <stdlib.h>
2487bc9f6aSratchov #include <string.h>
2587bc9f6aSratchov 
2687bc9f6aSratchov #include "abuf.h"
2787bc9f6aSratchov #include "defs.h"
2887bc9f6aSratchov #include "dev.h"
29d07fece6Sratchov #include "dev_sioctl.h"
3087bc9f6aSratchov #include "dsp.h"
31395f8c55Sratchov #include "fdpass.h"
3287bc9f6aSratchov #include "file.h"
3387bc9f6aSratchov #include "siofile.h"
3487bc9f6aSratchov #include "utils.h"
3587bc9f6aSratchov 
36b4234b19Sratchov #define WATCHDOG_USEC	4000000		/* 4 seconds */
37d135469bSratchov 
38fcda7a7eSratchov void dev_sio_onmove(void *, int);
39fcda7a7eSratchov void dev_sio_timeout(void *);
4087bc9f6aSratchov int dev_sio_pollfd(void *, struct pollfd *);
4187bc9f6aSratchov int dev_sio_revents(void *, struct pollfd *);
4287bc9f6aSratchov void dev_sio_run(void *);
4387bc9f6aSratchov void dev_sio_hup(void *);
4487bc9f6aSratchov 
45bbbe65dfSratchov extern struct fileops dev_sioctl_ops;
46bbbe65dfSratchov 
4787bc9f6aSratchov struct fileops dev_sio_ops = {
4887bc9f6aSratchov 	"sio",
4987bc9f6aSratchov 	dev_sio_pollfd,
5087bc9f6aSratchov 	dev_sio_revents,
5187bc9f6aSratchov 	dev_sio_run,
5287bc9f6aSratchov 	dev_sio_run,
5387bc9f6aSratchov 	dev_sio_hup
5487bc9f6aSratchov };
5587bc9f6aSratchov 
5687bc9f6aSratchov void
5787bc9f6aSratchov dev_sio_onmove(void *arg, int delta)
5887bc9f6aSratchov {
5987bc9f6aSratchov 	struct dev *d = arg;
6087bc9f6aSratchov 
6187bc9f6aSratchov #ifdef DEBUG
62*7b639200Sratchov 	logx(4, "%s: tick, delta = %d", d->path, delta);
63*7b639200Sratchov 
6487bc9f6aSratchov 	d->sio.sum_utime += file_utime - d->sio.utime;
6587bc9f6aSratchov 	d->sio.sum_wtime += file_wtime - d->sio.wtime;
6687bc9f6aSratchov 	d->sio.wtime = file_wtime;
6787bc9f6aSratchov 	d->sio.utime = file_utime;
6887bc9f6aSratchov 	if (d->mode & MODE_PLAY)
6987bc9f6aSratchov 		d->sio.pused -= delta;
7087bc9f6aSratchov 	if (d->mode & MODE_REC)
7187bc9f6aSratchov 		d->sio.rused += delta;
7287bc9f6aSratchov #endif
7387bc9f6aSratchov 	dev_onmove(d, delta);
7487bc9f6aSratchov }
7587bc9f6aSratchov 
76d135469bSratchov void
77d135469bSratchov dev_sio_timeout(void *arg)
78d135469bSratchov {
79d135469bSratchov 	struct dev *d = arg;
80d135469bSratchov 
81*7b639200Sratchov 	logx(1, "%s: watchdog timeout", d->path);
822435013dSratchov 	dev_migrate(d);
834a2af8d6Sratchov 	dev_abort(d);
84d135469bSratchov }
85d135469bSratchov 
8636355b88Sratchov /*
8736355b88Sratchov  * open the device.
8836355b88Sratchov  */
8936355b88Sratchov int
9036355b88Sratchov dev_sio_open(struct dev *d)
91bd94a039Sedd {
9236355b88Sratchov 	struct sio_par par;
9336355b88Sratchov 	unsigned int rate, mode = d->reqmode & (SIO_PLAY | SIO_REC);
94bd94a039Sedd 
9536355b88Sratchov 	d->sio.hdl = fdpass_sio_open(d->num, mode);
9636355b88Sratchov 	if (d->sio.hdl == NULL) {
97bd94a039Sedd 		if (mode != (SIO_PLAY | SIO_REC))
98bd94a039Sedd 			return 0;
9936355b88Sratchov 		d->sio.hdl = fdpass_sio_open(d->num, SIO_PLAY);
10036355b88Sratchov 		if (d->sio.hdl != NULL)
101bd94a039Sedd 			mode = SIO_PLAY;
102bd94a039Sedd 		else {
10336355b88Sratchov 			d->sio.hdl = fdpass_sio_open(d->num, SIO_REC);
10436355b88Sratchov 			if (d->sio.hdl != NULL)
105bd94a039Sedd 				mode = SIO_REC;
106bd94a039Sedd 			else
107bd94a039Sedd 				return 0;
108bd94a039Sedd 		}
109*7b639200Sratchov 		logx(1, "%s: warning, device opened in %s mode",
110*7b639200Sratchov 		    d->path, mode == SIO_PLAY ? "play-only" : "rec-only");
111bd94a039Sedd 	}
11236355b88Sratchov 	d->mode = mode;
113bd94a039Sedd 
11436355b88Sratchov 	d->sioctl.hdl = fdpass_sioctl_open(d->num, SIOCTL_READ | SIOCTL_WRITE);
115*7b639200Sratchov 	if (d->sioctl.hdl == NULL)
116*7b639200Sratchov 		logx(1, "%s: no control device", d->path);
117bd94a039Sedd 
11887bc9f6aSratchov 	sio_initpar(&par);
11987bc9f6aSratchov 	par.bits = d->par.bits;
12087bc9f6aSratchov 	par.bps = d->par.bps;
12187bc9f6aSratchov 	par.sig = d->par.sig;
12287bc9f6aSratchov 	par.le = d->par.le;
12387bc9f6aSratchov 	par.msb = d->par.msb;
124bd94a039Sedd 	if (d->mode & SIO_PLAY)
12587bc9f6aSratchov 		par.pchan = d->pchan;
126bd94a039Sedd 	if (d->mode & SIO_REC)
12787bc9f6aSratchov 		par.rchan = d->rchan;
12887bc9f6aSratchov 	par.appbufsz = d->bufsz;
12987bc9f6aSratchov 	par.round = d->round;
13087bc9f6aSratchov 	par.rate = d->rate;
13187bc9f6aSratchov 	if (!sio_setpar(d->sio.hdl, &par))
13287bc9f6aSratchov 		goto bad_close;
13387bc9f6aSratchov 	if (!sio_getpar(d->sio.hdl, &par))
13487bc9f6aSratchov 		goto bad_close;
1354285a83aSratchov 
13636355b88Sratchov 	/*
13736355b88Sratchov 	 * If the requested rate is not supported by the device,
13836355b88Sratchov 	 * use the new one, but retry using a block size that would
13936355b88Sratchov 	 * match the requested one
14036355b88Sratchov 	 */
14136355b88Sratchov 	rate = par.rate;
14236355b88Sratchov 	if (rate != d->rate) {
14336355b88Sratchov 		sio_initpar(&par);
14436355b88Sratchov 		par.bits = d->par.bits;
14536355b88Sratchov 		par.bps = d->par.bps;
14636355b88Sratchov 		par.sig = d->par.sig;
14736355b88Sratchov 		par.le = d->par.le;
14836355b88Sratchov 		par.msb = d->par.msb;
14936355b88Sratchov 		if (mode & SIO_PLAY)
15036355b88Sratchov 			par.pchan = d->reqpchan;
15136355b88Sratchov 		if (mode & SIO_REC)
15236355b88Sratchov 			par.rchan = d->reqrchan;
15336355b88Sratchov 		par.appbufsz = d->bufsz * rate / d->rate;
15436355b88Sratchov 		par.round = d->round * rate / d->rate;
15536355b88Sratchov 		par.rate = rate;
15636355b88Sratchov 		if (!sio_setpar(d->sio.hdl, &par))
15736355b88Sratchov 			goto bad_close;
15836355b88Sratchov 		if (!sio_getpar(d->sio.hdl, &par))
15936355b88Sratchov 			goto bad_close;
16036355b88Sratchov 	}
16136355b88Sratchov 
1624285a83aSratchov #ifdef DEBUG
1634285a83aSratchov 	/*
1647c71888cSratchov 	 * We support any parameter combination exposed by the kernel,
1654285a83aSratchov 	 * and we have no other choice than trusting the kernel for
1664285a83aSratchov 	 * returning correct parameters. But let's check parameters
1674285a83aSratchov 	 * early and nicely report kernel bugs rather than crashing
1684285a83aSratchov 	 * later in memset(), malloc() or alike.
1694285a83aSratchov 	 */
1704285a83aSratchov 
1714285a83aSratchov 	if (par.bits > BITS_MAX) {
172*7b639200Sratchov 		logx(0, "%s: %u: unsupported number of bits", d->path, par.bits);
1734285a83aSratchov 		goto bad_close;
1744285a83aSratchov 	}
1754285a83aSratchov 	if (par.bps > SIO_BPS(BITS_MAX)) {
176*7b639200Sratchov 		logx(0, "%s: %u: unsupported sample size", d->path, par.bps);
1774285a83aSratchov 		goto bad_close;
1784285a83aSratchov 	}
179bd94a039Sedd 	if ((d->mode & SIO_PLAY) && par.pchan > NCHAN_MAX) {
180*7b639200Sratchov 		logx(0, "%s: %u: unsupported number of play channels", d->path, par.pchan);
1814285a83aSratchov 		goto bad_close;
1824285a83aSratchov 	}
183bd94a039Sedd 	if ((d->mode & SIO_REC) && par.rchan > NCHAN_MAX) {
184*7b639200Sratchov 		logx(0, "%s: %u: unsupported number of rec channels", d->path, par.rchan);
1854285a83aSratchov 		goto bad_close;
1864285a83aSratchov 	}
1874285a83aSratchov 	if (par.bufsz == 0 || par.bufsz > RATE_MAX) {
188*7b639200Sratchov 		logx(0, "%s: %u: unsupported buffer size", d->path, par.bufsz);
1894285a83aSratchov 		goto bad_close;
1904285a83aSratchov 	}
1914285a83aSratchov 	if (par.round == 0 || par.round > par.bufsz ||
1924285a83aSratchov 	    par.bufsz % par.round != 0) {
193*7b639200Sratchov 		logx(0, "%s: %u: unsupported block size", d->path, par.round);
1944285a83aSratchov 		goto bad_close;
1954285a83aSratchov 	}
1964285a83aSratchov 	if (par.rate == 0 || par.rate > RATE_MAX) {
197*7b639200Sratchov 		logx(0, "%s: %u: unsupported rate", d->path, par.rate);
1984285a83aSratchov 		goto bad_close;
1994285a83aSratchov 	}
2004285a83aSratchov #endif
20187bc9f6aSratchov 	d->par.bits = par.bits;
20287bc9f6aSratchov 	d->par.bps = par.bps;
20387bc9f6aSratchov 	d->par.sig = par.sig;
20487bc9f6aSratchov 	d->par.le = par.le;
20587bc9f6aSratchov 	d->par.msb = par.msb;
206bd94a039Sedd 	if (d->mode & SIO_PLAY)
20787bc9f6aSratchov 		d->pchan = par.pchan;
208bd94a039Sedd 	if (d->mode & SIO_REC)
20987bc9f6aSratchov 		d->rchan = par.rchan;
21087bc9f6aSratchov 	d->bufsz = par.bufsz;
21187bc9f6aSratchov 	d->round = par.round;
21287bc9f6aSratchov 	d->rate = par.rate;
213bd94a039Sedd 	if (d->mode & MODE_PLAY)
214bd94a039Sedd 		d->mode |= MODE_MON;
21587bc9f6aSratchov 	sio_onmove(d->sio.hdl, dev_sio_onmove, d);
216731605d7Sratchov 	d->sio.file = file_new(&dev_sio_ops, d, "dev", sio_nfds(d->sio.hdl));
217bbbe65dfSratchov 	if (d->sioctl.hdl) {
218bbbe65dfSratchov 		d->sioctl.file = file_new(&dev_sioctl_ops, d, "mix",
219bbbe65dfSratchov 		    sioctl_nfds(d->sioctl.hdl));
220bbbe65dfSratchov 	}
221d135469bSratchov 	timo_set(&d->sio.watchdog, dev_sio_timeout, d);
222d07fece6Sratchov 	dev_sioctl_open(d);
22387bc9f6aSratchov 	return 1;
22487bc9f6aSratchov  bad_close:
22587bc9f6aSratchov 	sio_close(d->sio.hdl);
226d07fece6Sratchov 	if (d->sioctl.hdl) {
227d07fece6Sratchov 		sioctl_close(d->sioctl.hdl);
228d07fece6Sratchov 		d->sioctl.hdl = NULL;
229d07fece6Sratchov 	}
23087bc9f6aSratchov 	return 0;
23187bc9f6aSratchov }
23287bc9f6aSratchov 
23387bc9f6aSratchov void
23487bc9f6aSratchov dev_sio_close(struct dev *d)
23587bc9f6aSratchov {
236d07fece6Sratchov 	dev_sioctl_close(d);
23787bc9f6aSratchov #ifdef DEBUG
238*7b639200Sratchov 	logx(3, "%s: closed", d->path);
23987bc9f6aSratchov #endif
24059613df7Sratchov 	timo_del(&d->sio.watchdog);
24187bc9f6aSratchov 	file_del(d->sio.file);
24287bc9f6aSratchov 	sio_close(d->sio.hdl);
243d07fece6Sratchov 	if (d->sioctl.hdl) {
244bbbe65dfSratchov 		file_del(d->sioctl.file);
245d07fece6Sratchov 		sioctl_close(d->sioctl.hdl);
246d07fece6Sratchov 		d->sioctl.hdl = NULL;
247d07fece6Sratchov 	}
24887bc9f6aSratchov }
24987bc9f6aSratchov 
25087bc9f6aSratchov void
25187bc9f6aSratchov dev_sio_start(struct dev *d)
25287bc9f6aSratchov {
25387bc9f6aSratchov 	if (!sio_start(d->sio.hdl)) {
254*7b639200Sratchov 		logx(1, "%s: failed to start device", d->path);
25587bc9f6aSratchov 		return;
25687bc9f6aSratchov 	}
25787bc9f6aSratchov 	if (d->mode & MODE_PLAY) {
25887bc9f6aSratchov 		d->sio.cstate = DEV_SIO_CYCLE;
25987bc9f6aSratchov 		d->sio.todo = 0;
26087bc9f6aSratchov 	} else {
26187bc9f6aSratchov 		d->sio.cstate = DEV_SIO_READ;
26287bc9f6aSratchov 		d->sio.todo = d->round * d->rchan * d->par.bps;
26387bc9f6aSratchov 	}
26487bc9f6aSratchov #ifdef DEBUG
26587bc9f6aSratchov 	d->sio.pused = 0;
26687bc9f6aSratchov 	d->sio.rused = 0;
26787bc9f6aSratchov 	d->sio.sum_utime = 0;
26887bc9f6aSratchov 	d->sio.sum_wtime = 0;
26987bc9f6aSratchov 	d->sio.wtime = file_wtime;
27087bc9f6aSratchov 	d->sio.utime = file_utime;
271*7b639200Sratchov 	logx(3, "%s: started", d->path);
27287bc9f6aSratchov #endif
273d135469bSratchov 	timo_add(&d->sio.watchdog, WATCHDOG_USEC);
27487bc9f6aSratchov }
27587bc9f6aSratchov 
27687bc9f6aSratchov void
27787bc9f6aSratchov dev_sio_stop(struct dev *d)
27887bc9f6aSratchov {
279ec8a3410Sratchov 	if (!sio_eof(d->sio.hdl) && !sio_flush(d->sio.hdl)) {
280*7b639200Sratchov 		logx(1, "%s: failed to stop device", d->path);
28187bc9f6aSratchov 		return;
28287bc9f6aSratchov 	}
28387bc9f6aSratchov #ifdef DEBUG
284*7b639200Sratchov 	logx(3, "%s: stopped, load avg = %lld / %lld",
285*7b639200Sratchov 	    d->path, d->sio.sum_utime / 1000, d->sio.sum_wtime / 1000);
28687bc9f6aSratchov #endif
287d135469bSratchov 	timo_del(&d->sio.watchdog);
28887bc9f6aSratchov }
28987bc9f6aSratchov 
29087bc9f6aSratchov int
29187bc9f6aSratchov dev_sio_pollfd(void *arg, struct pollfd *pfd)
29287bc9f6aSratchov {
29387bc9f6aSratchov 	struct dev *d = arg;
29487bc9f6aSratchov 	int events;
29587bc9f6aSratchov 
29687bc9f6aSratchov 	events = (d->sio.cstate == DEV_SIO_READ) ? POLLIN : POLLOUT;
29787bc9f6aSratchov 	return sio_pollfd(d->sio.hdl, pfd, events);
29887bc9f6aSratchov }
29987bc9f6aSratchov 
30087bc9f6aSratchov int
30187bc9f6aSratchov dev_sio_revents(void *arg, struct pollfd *pfd)
30287bc9f6aSratchov {
30387bc9f6aSratchov 	struct dev *d = arg;
30487bc9f6aSratchov 	int events;
30587bc9f6aSratchov 
30687bc9f6aSratchov 	events = sio_revents(d->sio.hdl, pfd);
30787bc9f6aSratchov #ifdef DEBUG
30887bc9f6aSratchov 	d->sio.events = events;
30987bc9f6aSratchov #endif
31087bc9f6aSratchov 	return events;
31187bc9f6aSratchov }
31287bc9f6aSratchov 
31387bc9f6aSratchov void
31487bc9f6aSratchov dev_sio_run(void *arg)
31587bc9f6aSratchov {
31687bc9f6aSratchov 	struct dev *d = arg;
31787bc9f6aSratchov 	unsigned char *data, *base;
31887bc9f6aSratchov 	unsigned int n;
31987bc9f6aSratchov 
32087bc9f6aSratchov 	/*
32187bc9f6aSratchov 	 * sio_read() and sio_write() would block at the end of the
32287bc9f6aSratchov 	 * cycle so we *must* return and restart poll()'ing. Otherwise
32387bc9f6aSratchov 	 * we may trigger dev_cycle() which would make all clients
32487bc9f6aSratchov 	 * underrun (ex, on a play-only device)
32587bc9f6aSratchov 	 */
32687bc9f6aSratchov 	for (;;) {
32787bc9f6aSratchov 		if (d->pstate != DEV_RUN)
32887bc9f6aSratchov 			return;
32987bc9f6aSratchov 		switch (d->sio.cstate) {
33087bc9f6aSratchov 		case DEV_SIO_READ:
33187bc9f6aSratchov #ifdef DEBUG
33287bc9f6aSratchov 			if (!(d->sio.events & POLLIN)) {
333*7b639200Sratchov 				logx(0, "%s: recording, but POLLIN not set", d->path);
33487bc9f6aSratchov 				panic();
33587bc9f6aSratchov 			}
33687bc9f6aSratchov 			if (d->sio.todo == 0) {
337*7b639200Sratchov 				logx(0, "%s: can't read data", d->path);
33887bc9f6aSratchov 				panic();
33987bc9f6aSratchov 			}
34087bc9f6aSratchov 			if (d->prime > 0) {
341*7b639200Sratchov 				logx(0, "%s: unexpected data", d->path);
34287bc9f6aSratchov 				panic();
34387bc9f6aSratchov 			}
34487bc9f6aSratchov #endif
34587bc9f6aSratchov 			base = d->decbuf ? d->decbuf : (unsigned char *)d->rbuf;
34687bc9f6aSratchov 			data = base +
34787bc9f6aSratchov 			    d->rchan * d->round * d->par.bps -
34887bc9f6aSratchov 			    d->sio.todo;
34987bc9f6aSratchov 			n = sio_read(d->sio.hdl, data, d->sio.todo);
35087bc9f6aSratchov 			d->sio.todo -= n;
35187bc9f6aSratchov #ifdef DEBUG
352*7b639200Sratchov 			logx(4, "%s: read %u bytes, todo %u / %u", d->path,
353*7b639200Sratchov 			    n, d->sio.todo, d->round * d->rchan * d->par.bps);
35487bc9f6aSratchov #endif
35587bc9f6aSratchov 			if (d->sio.todo > 0)
35687bc9f6aSratchov 				return;
35787bc9f6aSratchov #ifdef DEBUG
35887bc9f6aSratchov 			d->sio.rused -= d->round;
35987bc9f6aSratchov 			if (d->sio.rused >= d->round) {
360*7b639200Sratchov 				logx(2, "%s: rec hw xrun, rused = %d / %d",
361*7b639200Sratchov 				    d->path, d->sio.rused, d->bufsz);
36287bc9f6aSratchov 			}
36387bc9f6aSratchov #endif
36487bc9f6aSratchov 			d->sio.cstate = DEV_SIO_CYCLE;
36587bc9f6aSratchov 			break;
36687bc9f6aSratchov 		case DEV_SIO_CYCLE:
367d135469bSratchov 			timo_del(&d->sio.watchdog);
368d135469bSratchov 			timo_add(&d->sio.watchdog, WATCHDOG_USEC);
369d135469bSratchov 
37087bc9f6aSratchov #ifdef DEBUG
37187bc9f6aSratchov 			/*
37287bc9f6aSratchov 			 * check that we're called at cycle boundary:
37387bc9f6aSratchov 			 * either after a recorded block, or when POLLOUT is
37487bc9f6aSratchov 			 * raised
37587bc9f6aSratchov 			 */
37687bc9f6aSratchov 			if (!((d->mode & MODE_REC) && d->prime == 0) &&
37787bc9f6aSratchov 			    !(d->sio.events & POLLOUT)) {
378*7b639200Sratchov 				logx(0, "%s: cycle not at block boundary", d->path);
37987bc9f6aSratchov 				panic();
38087bc9f6aSratchov 			}
38187bc9f6aSratchov #endif
38287bc9f6aSratchov 			dev_cycle(d);
38387bc9f6aSratchov 			if (d->mode & MODE_PLAY) {
38487bc9f6aSratchov 				d->sio.cstate = DEV_SIO_WRITE;
38587bc9f6aSratchov 				d->sio.todo = d->round * d->pchan * d->par.bps;
38687bc9f6aSratchov 				break;
38787bc9f6aSratchov 			} else {
38887bc9f6aSratchov 				d->sio.cstate = DEV_SIO_READ;
38987bc9f6aSratchov 				d->sio.todo = d->round * d->rchan * d->par.bps;
39087bc9f6aSratchov 				return;
39187bc9f6aSratchov 			}
39287bc9f6aSratchov 		case DEV_SIO_WRITE:
39387bc9f6aSratchov #ifdef DEBUG
39487bc9f6aSratchov 			if (d->sio.todo == 0) {
395*7b639200Sratchov 				logx(0, "%s: can't write data", d->path);
39687bc9f6aSratchov 				panic();
39787bc9f6aSratchov 			}
39887bc9f6aSratchov #endif
39987bc9f6aSratchov 			base = d->encbuf ? d->encbuf : (unsigned char *)DEV_PBUF(d);
40087bc9f6aSratchov 			data = base +
40187bc9f6aSratchov 			    d->pchan * d->round * d->par.bps -
40287bc9f6aSratchov 			    d->sio.todo;
40387bc9f6aSratchov 			n = sio_write(d->sio.hdl, data, d->sio.todo);
40487bc9f6aSratchov 			d->sio.todo -= n;
40587bc9f6aSratchov #ifdef DEBUG
406*7b639200Sratchov 			logx(4, "%s: wrote %u bytes, todo %u / %u",
407*7b639200Sratchov 			    d->path, n, d->sio.todo, d->round * d->pchan * d->par.bps);
40887bc9f6aSratchov #endif
40987bc9f6aSratchov 			if (d->sio.todo > 0)
41087bc9f6aSratchov 				return;
41187bc9f6aSratchov #ifdef DEBUG
41287bc9f6aSratchov 			d->sio.pused += d->round;
41387bc9f6aSratchov 			if (d->prime == 0 &&
41487bc9f6aSratchov 			    d->sio.pused <= d->bufsz - d->round) {
415*7b639200Sratchov 				logx(2, "%s: play hw xrun, pused = %d / %d",
416*7b639200Sratchov 				    d->path, d->sio.pused, d->bufsz);
41787bc9f6aSratchov 			}
41887bc9f6aSratchov 			if (d->sio.pused < 0 ||
41987bc9f6aSratchov 			    d->sio.pused > d->bufsz) {
42087bc9f6aSratchov 				/* device driver or libsndio bug */
421*7b639200Sratchov 				logx(2, "%s: out of bounds pused = %d / %d",
422*7b639200Sratchov 				    d->path, d->sio.pused, d->bufsz);
42387bc9f6aSratchov 			}
42487bc9f6aSratchov #endif
42587bc9f6aSratchov 			d->poffs += d->round;
4268aadda71Sratchov 			if (d->poffs == d->psize)
42787bc9f6aSratchov 				d->poffs = 0;
42887bc9f6aSratchov 			if ((d->mode & MODE_REC) && d->prime == 0) {
42987bc9f6aSratchov 				d->sio.cstate = DEV_SIO_READ;
43087bc9f6aSratchov 				d->sio.todo = d->round * d->rchan * d->par.bps;
43187bc9f6aSratchov 			} else
43287bc9f6aSratchov 				d->sio.cstate = DEV_SIO_CYCLE;
43387bc9f6aSratchov 			return;
43487bc9f6aSratchov 		}
43587bc9f6aSratchov 	}
43687bc9f6aSratchov }
43787bc9f6aSratchov 
43887bc9f6aSratchov void
43987bc9f6aSratchov dev_sio_hup(void *arg)
44087bc9f6aSratchov {
44187bc9f6aSratchov 	struct dev *d = arg;
44287bc9f6aSratchov 
44346f2f343Sratchov #ifdef DEBUG
444*7b639200Sratchov 	logx(2, "%s: disconnected", d->path);
44546f2f343Sratchov #endif
44636355b88Sratchov 	dev_migrate(d);
4474a2af8d6Sratchov 	dev_abort(d);
44887bc9f6aSratchov }
449