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