xref: /openbsd-src/lib/libsndio/sio_aucat.c (revision b395609868171dda79778399742da4042c7d40db)
1 /*	$OpenBSD: sio_aucat.c,v 1.9 2011/11/15 08:05:22 ratchov Exp $	*/
2 /*
3  * Copyright (c) 2008 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 
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <sys/un.h>
21 
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <poll.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 
30 #include "aucat.h"
31 #include "debug.h"
32 #include "sio_priv.h"
33 
34 struct sio_aucat_hdl {
35 	struct sio_hdl sio;
36 	struct aucat aucat;
37 	unsigned rbpf, wbpf;		/* read and write bytes-per-frame */
38 	int maxwrite;			/* latency constraint */
39 	int events;			/* events the user requested */
40 	unsigned curvol, reqvol;	/* current and requested volume */
41 	int delta;			/* some of received deltas */
42 #define PSTATE_INIT	0
43 #define PSTATE_RUN	1
44 	int pstate;
45 };
46 
47 static void sio_aucat_close(struct sio_hdl *);
48 static int sio_aucat_start(struct sio_hdl *);
49 static int sio_aucat_stop(struct sio_hdl *);
50 static int sio_aucat_setpar(struct sio_hdl *, struct sio_par *);
51 static int sio_aucat_getpar(struct sio_hdl *, struct sio_par *);
52 static int sio_aucat_getcap(struct sio_hdl *, struct sio_cap *);
53 static size_t sio_aucat_read(struct sio_hdl *, void *, size_t);
54 static size_t sio_aucat_write(struct sio_hdl *, const void *, size_t);
55 static int sio_aucat_nfds(struct sio_hdl *);
56 static int sio_aucat_pollfd(struct sio_hdl *, struct pollfd *, int);
57 static int sio_aucat_revents(struct sio_hdl *, struct pollfd *);
58 static int sio_aucat_setvol(struct sio_hdl *, unsigned);
59 static void sio_aucat_getvol(struct sio_hdl *);
60 
61 static struct sio_ops sio_aucat_ops = {
62 	sio_aucat_close,
63 	sio_aucat_setpar,
64 	sio_aucat_getpar,
65 	sio_aucat_getcap,
66 	sio_aucat_write,
67 	sio_aucat_read,
68 	sio_aucat_start,
69 	sio_aucat_stop,
70 	sio_aucat_nfds,
71 	sio_aucat_pollfd,
72 	sio_aucat_revents,
73 	sio_aucat_setvol,
74 	sio_aucat_getvol
75 };
76 
77 /*
78  * execute the next message, return 0 if blocked
79  */
80 static int
81 sio_aucat_runmsg(struct sio_aucat_hdl *hdl)
82 {
83 	int delta;
84 	unsigned size, ctl;
85 
86 	if (!aucat_rmsg(&hdl->aucat, &hdl->sio.eof))
87 		return 0;
88 	switch (ntohl(hdl->aucat.rmsg.cmd)) {
89 	case AMSG_DATA:
90 		size = ntohl(hdl->aucat.rmsg.u.data.size);
91 		if (size == 0 || size % hdl->rbpf) {
92 			DPRINTF("sio_aucat_runmsg: bad data message\n");
93 			hdl->sio.eof = 1;
94 			return 0;
95 		}
96 		return 1;
97 	case AMSG_POS:
98 		delta = ntohl(hdl->aucat.rmsg.u.ts.delta);
99 		hdl->maxwrite += delta * (int)hdl->wbpf;
100 		DPRINTF("aucat: pos = %d, maxwrite = %d\n",
101 		    delta, hdl->maxwrite);
102 		hdl->delta = delta;
103 		if (hdl->delta >= 0) {
104 			sio_onmove_cb(&hdl->sio, hdl->delta);
105 			hdl->delta = 0;
106 		}
107 		break;
108 	case AMSG_MOVE:
109 		delta = ntohl(hdl->aucat.rmsg.u.ts.delta);
110 		hdl->maxwrite += delta * hdl->wbpf;
111 		hdl->delta += delta;
112 		DPRINTFN(2, "aucat: move = %d, delta = %d, maxwrite = %d\n",
113 		    delta, hdl->delta, hdl->maxwrite);
114 		if (hdl->delta >= 0) {
115 			sio_onmove_cb(&hdl->sio, hdl->delta);
116 			hdl->delta = 0;
117 		}
118 		break;
119 	case AMSG_SETVOL:
120 		ctl = ntohl(hdl->aucat.rmsg.u.vol.ctl);
121 		hdl->curvol = hdl->reqvol = ctl;
122 		sio_onvol_cb(&hdl->sio, ctl);
123 		break;
124 	case AMSG_STOP:
125 		hdl->pstate = PSTATE_INIT;
126 		break;
127 	default:
128 		DPRINTF("sio_aucat_runmsg: unhandled message %u\n", hdl->aucat.rmsg.cmd);
129 		hdl->sio.eof = 1;
130 		return 0;
131 	}
132 	hdl->aucat.rstate = RSTATE_MSG;
133 	hdl->aucat.rtodo = sizeof(struct amsg);
134 	return 1;
135 }
136 
137 static int
138 sio_aucat_buildmsg(struct sio_aucat_hdl *hdl)
139 {
140 	if (hdl->curvol != hdl->reqvol) {
141 		hdl->aucat.wstate = WSTATE_MSG;
142 		hdl->aucat.wtodo = sizeof(struct amsg);
143 		hdl->aucat.wmsg.cmd = htonl(AMSG_SETVOL);
144 		hdl->aucat.wmsg.u.vol.ctl = htonl(hdl->reqvol);
145 		hdl->curvol = hdl->reqvol;
146 		return aucat_wmsg(&hdl->aucat, &hdl->sio.eof);
147 	}
148 	return 0;
149 }
150 
151 struct sio_hdl *
152 sio_aucat_open(const char *str, unsigned mode, int nbio)
153 {
154 	struct sio_aucat_hdl *hdl;
155 
156 	hdl = malloc(sizeof(struct sio_aucat_hdl));
157 	if (hdl == NULL)
158 		return NULL;
159 	if (!aucat_open(&hdl->aucat, str, mode, 0)) {
160 		free(hdl);
161 		return NULL;
162 	}
163 	sio_create(&hdl->sio, &sio_aucat_ops, mode, nbio);
164 	hdl->curvol = SIO_MAXVOL;
165 	hdl->reqvol = SIO_MAXVOL;
166 	hdl->pstate = PSTATE_INIT;
167 	return (struct sio_hdl *)hdl;
168 }
169 
170 static void
171 sio_aucat_close(struct sio_hdl *sh)
172 {
173 	struct sio_aucat_hdl *hdl = (struct sio_aucat_hdl *)sh;
174 
175 	if (!hdl->sio.eof && hdl->sio.started)
176 		(void)sio_aucat_stop(&hdl->sio);
177 	aucat_close(&hdl->aucat, hdl->sio.eof);
178 	free(hdl);
179 }
180 
181 static int
182 sio_aucat_start(struct sio_hdl *sh)
183 {
184 	struct sio_aucat_hdl *hdl = (struct sio_aucat_hdl *)sh;
185 	struct sio_par par;
186 
187 	/*
188 	 * save bpf
189 	 */
190 	if (!sio_getpar(&hdl->sio, &par))
191 		return 0;
192 	hdl->wbpf = par.bps * par.pchan;
193 	hdl->rbpf = par.bps * par.rchan;
194 	hdl->maxwrite = hdl->wbpf * par.bufsz;
195 	hdl->delta = 0;
196 	DPRINTF("aucat: start, maxwrite = %d\n", hdl->maxwrite);
197 
198 	AMSG_INIT(&hdl->aucat.wmsg);
199 	hdl->aucat.wmsg.cmd = htonl(AMSG_START);
200 	hdl->aucat.wtodo = sizeof(struct amsg);
201 	if (!aucat_wmsg(&hdl->aucat, &hdl->sio.eof))
202 		return 0;
203 	hdl->aucat.rstate = RSTATE_MSG;
204 	hdl->aucat.rtodo = sizeof(struct amsg);
205 	if (!aucat_setfl(&hdl->aucat, 1, &hdl->sio.eof))
206 		return 0;
207 	hdl->pstate = PSTATE_RUN;
208 	return 1;
209 }
210 
211 static int
212 sio_aucat_stop(struct sio_hdl *sh)
213 {
214 #define ZERO_MAX 0x400
215 	static unsigned char zero[ZERO_MAX];
216 	struct sio_aucat_hdl *hdl = (struct sio_aucat_hdl *)sh;
217 	unsigned n, count;
218 
219 	if (!aucat_setfl(&hdl->aucat, 0, &hdl->sio.eof))
220 		return 0;
221 	/*
222 	 * complete message or data block in progress
223 	 */
224 	if (hdl->aucat.wstate == WSTATE_MSG) {
225 		if (!aucat_wmsg(&hdl->aucat, &hdl->sio.eof))
226 			return 0;
227 	}
228 	if (hdl->aucat.wstate == WSTATE_DATA) {
229 		hdl->maxwrite = hdl->aucat.wtodo;
230 		while (hdl->aucat.wstate != WSTATE_IDLE) {
231 			count = hdl->aucat.wtodo;
232 			if (count > ZERO_MAX)
233 				count = ZERO_MAX;
234 			n = sio_aucat_write(&hdl->sio, zero, count);
235 			if (n == 0)
236 				return 0;
237 		}
238 	}
239 
240 	/*
241 	 * send stop message
242 	 */
243 	AMSG_INIT(&hdl->aucat.wmsg);
244 	hdl->aucat.wmsg.cmd = htonl(AMSG_STOP);
245 	hdl->aucat.wtodo = sizeof(struct amsg);
246 	if (!aucat_wmsg(&hdl->aucat, &hdl->sio.eof))
247 		return 0;
248 
249 	/*
250 	 * wait for the STOP ACK
251 	 */
252 	while (hdl->pstate != PSTATE_INIT) {
253 		switch (hdl->aucat.rstate) {
254 		case RSTATE_MSG:
255 			if (!sio_aucat_runmsg(hdl))
256 				return 0;
257 			break;
258 		case RSTATE_DATA:
259 			if (!sio_aucat_read(&hdl->sio, zero, ZERO_MAX))
260 				return 0;
261 			break;
262 		}
263 	}
264 	return 1;
265 }
266 
267 static int
268 sio_aucat_setpar(struct sio_hdl *sh, struct sio_par *par)
269 {
270 	struct sio_aucat_hdl *hdl = (struct sio_aucat_hdl *)sh;
271 
272 	AMSG_INIT(&hdl->aucat.wmsg);
273 	hdl->aucat.wmsg.cmd = htonl(AMSG_SETPAR);
274 	hdl->aucat.wmsg.u.par.bits = par->bits;
275 	hdl->aucat.wmsg.u.par.bps = par->bps;
276 	hdl->aucat.wmsg.u.par.sig = par->sig;
277 	hdl->aucat.wmsg.u.par.le = par->le;
278 	hdl->aucat.wmsg.u.par.msb = par->msb;
279 	hdl->aucat.wmsg.u.par.rate = htonl(par->rate);
280 	hdl->aucat.wmsg.u.par.appbufsz = htonl(par->appbufsz);
281 	hdl->aucat.wmsg.u.par.xrun = par->xrun;
282 	if (hdl->sio.mode & SIO_REC)
283 		hdl->aucat.wmsg.u.par.rchan = htons(par->rchan);
284 	if (hdl->sio.mode & SIO_PLAY)
285 		hdl->aucat.wmsg.u.par.pchan = htons(par->pchan);
286 	hdl->aucat.wtodo = sizeof(struct amsg);
287 	if (!aucat_wmsg(&hdl->aucat, &hdl->sio.eof))
288 		return 0;
289 	return 1;
290 }
291 
292 static int
293 sio_aucat_getpar(struct sio_hdl *sh, struct sio_par *par)
294 {
295 	struct sio_aucat_hdl *hdl = (struct sio_aucat_hdl *)sh;
296 
297 	AMSG_INIT(&hdl->aucat.wmsg);
298 	hdl->aucat.wmsg.cmd = htonl(AMSG_GETPAR);
299 	hdl->aucat.wtodo = sizeof(struct amsg);
300 	if (!aucat_wmsg(&hdl->aucat, &hdl->sio.eof))
301 		return 0;
302 	hdl->aucat.rtodo = sizeof(struct amsg);
303 	if (!aucat_rmsg(&hdl->aucat, &hdl->sio.eof))
304 		return 0;
305 	if (ntohl(hdl->aucat.rmsg.cmd) != AMSG_GETPAR) {
306 		DPRINTF("sio_aucat_getpar: protocol err\n");
307 		hdl->sio.eof = 1;
308 		return 0;
309 	}
310 	par->bits = hdl->aucat.rmsg.u.par.bits;
311 	par->bps = hdl->aucat.rmsg.u.par.bps;
312 	par->sig = hdl->aucat.rmsg.u.par.sig;
313 	par->le = hdl->aucat.rmsg.u.par.le;
314 	par->msb = hdl->aucat.rmsg.u.par.msb;
315 	par->rate = ntohl(hdl->aucat.rmsg.u.par.rate);
316 	par->bufsz = ntohl(hdl->aucat.rmsg.u.par.bufsz);
317 	par->appbufsz = ntohl(hdl->aucat.rmsg.u.par.appbufsz);
318 	par->xrun = hdl->aucat.rmsg.u.par.xrun;
319 	par->round = ntohl(hdl->aucat.rmsg.u.par.round);
320 	if (hdl->sio.mode & SIO_PLAY)
321 		par->pchan = ntohs(hdl->aucat.rmsg.u.par.pchan);
322 	if (hdl->sio.mode & SIO_REC)
323 		par->rchan = ntohs(hdl->aucat.rmsg.u.par.rchan);
324 	return 1;
325 }
326 
327 static int
328 sio_aucat_getcap(struct sio_hdl *sh, struct sio_cap *cap)
329 {
330 	unsigned i, bps, le, sig, chan, rindex, rmult;
331 	static unsigned rates[] = { 8000, 11025, 12000 };
332 
333 	bps = 1;
334 	sig = le = 0;
335 	cap->confs[0].enc = 0;
336 	for (i = 0; i < SIO_NENC; i++) {
337 		if (bps > 4)
338 			break;
339 		cap->confs[0].enc |= 1 << i;
340 		cap->enc[i].bits = bps == 4 ? 24 : bps * 8;
341 		cap->enc[i].bps = bps;
342 		cap->enc[i].sig = sig ^ 1;
343 		cap->enc[i].le = bps > 1 ? le : SIO_LE_NATIVE;
344 		cap->enc[i].msb = 1;
345 		le++;
346 		if (le > 1 || bps == 1) {
347 			le = 0;
348 			sig++;
349 		}
350 		if (sig > 1 || (le == 0 && bps > 1)) {
351 			sig = 0;
352 			bps++;
353 		}
354 	}
355 	chan = 1;
356 	cap->confs[0].rchan = 0;
357 	for (i = 0; i < SIO_NCHAN; i++) {
358 		if (chan > 16)
359 			break;
360 		cap->confs[0].rchan |= 1 << i;
361 		cap->rchan[i] = chan;
362 		if (chan >= 12) {
363 			chan += 4;
364 		} else if (chan >= 2) {
365 			chan += 2;
366 		} else
367 			chan++;
368 	}
369 	chan = 1;
370 	cap->confs[0].pchan = 0;
371 	for (i = 0; i < SIO_NCHAN; i++) {
372 		if (chan > 16)
373 			break;
374 		cap->confs[0].pchan |= 1 << i;
375 		cap->pchan[i] = chan;
376 		if (chan >= 12) {
377 			chan += 4;
378 		} else if (chan >= 2) {
379 			chan += 2;
380 		} else
381 			chan++;
382 	}
383 	rindex = 0;
384 	rmult = 1;
385 	cap->confs[0].rate = 0;
386 	for (i = 0; i < SIO_NRATE; i++) {
387 		if (rmult >= 32)
388 			break;
389 		cap->rate[i] = rates[rindex] * rmult;
390 		cap->confs[0].rate |= 1 << i;
391 		rindex++;
392 		if (rindex == sizeof(rates) / sizeof(unsigned)) {
393 			rindex = 0;
394 			rmult *= 2;
395 		}
396 	}
397 	cap->nconf = 1;
398 	return 1;
399 }
400 
401 static size_t
402 sio_aucat_read(struct sio_hdl *sh, void *buf, size_t len)
403 {
404 	struct sio_aucat_hdl *hdl = (struct sio_aucat_hdl *)sh;
405 
406 	while (hdl->aucat.rstate == RSTATE_MSG) {
407 		if (!sio_aucat_runmsg(hdl))
408 			return 0;
409 	}
410 	return aucat_rdata(&hdl->aucat, buf, len, &hdl->sio.eof);
411 }
412 
413 static size_t
414 sio_aucat_write(struct sio_hdl *sh, const void *buf, size_t len)
415 {
416 	struct sio_aucat_hdl *hdl = (struct sio_aucat_hdl *)sh;
417 	size_t n;
418 
419 	while (hdl->aucat.wstate == WSTATE_IDLE) {
420 		if (!sio_aucat_buildmsg(hdl))
421 			break;
422 	}
423 	if (len <= 0 || hdl->maxwrite <= 0)
424 		return 0;
425 	if (len > hdl->maxwrite)
426 		len = hdl->maxwrite;
427 	n = aucat_wdata(&hdl->aucat, buf, len, hdl->wbpf, &hdl->sio.eof);
428 	hdl->maxwrite -= n;
429 	return n;
430 }
431 
432 static int
433 sio_aucat_nfds(struct sio_hdl *hdl)
434 {
435 	return 1;
436  }
437 
438 static int
439 sio_aucat_pollfd(struct sio_hdl *sh, struct pollfd *pfd, int events)
440 {
441 	struct sio_aucat_hdl *hdl = (struct sio_aucat_hdl *)sh;
442 
443 	hdl->events = events;
444 	if (hdl->maxwrite <= 0)
445 		events &= ~POLLOUT;
446 	return aucat_pollfd(&hdl->aucat, pfd, events);
447 }
448 
449 static int
450 sio_aucat_revents(struct sio_hdl *sh, struct pollfd *pfd)
451 {
452 	struct sio_aucat_hdl *hdl = (struct sio_aucat_hdl *)sh;
453 	int revents = pfd->revents;
454 
455 	if (revents & POLLIN) {
456 		while (hdl->aucat.rstate == RSTATE_MSG) {
457 			if (!sio_aucat_runmsg(hdl))
458 				break;
459 		}
460 		if (hdl->aucat.rstate != RSTATE_DATA)
461 			revents &= ~POLLIN;
462 	}
463 	if (revents & POLLOUT) {
464 		if (hdl->maxwrite <= 0)
465 			revents &= ~POLLOUT;
466 	}
467 	if (hdl->sio.eof)
468 		return POLLHUP;
469 	DPRINTFN(2, "sio_aucat_revents: %x\n", revents & hdl->events);
470 	return revents & (hdl->events | POLLHUP);
471 }
472 
473 static int
474 sio_aucat_setvol(struct sio_hdl *sh, unsigned vol)
475 {
476 	struct sio_aucat_hdl *hdl = (struct sio_aucat_hdl *)sh;
477 
478 	hdl->reqvol = vol;
479 	return 1;
480 }
481 
482 static void
483 sio_aucat_getvol(struct sio_hdl *sh)
484 {
485 	struct sio_aucat_hdl *hdl = (struct sio_aucat_hdl *)sh;
486 
487 	sio_onvol_cb(&hdl->sio, hdl->reqvol);
488 	return;
489 }
490