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