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