xref: /openbsd-src/lib/libsndio/sio_aucat.c (revision 07e4e28e462a424a68709ae76796de42a5e40f5c)
1 /*	$OpenBSD: sio_aucat.c,v 1.14 2012/11/23 06:40:26 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 		DPRINTF("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(2, "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 	struct sio_par par;
184 
185 	/*
186 	 * save bpf
187 	 */
188 	if (!sio_getpar(&hdl->sio, &par))
189 		return 0;
190 	hdl->wbpf = par.bps * par.pchan;
191 	hdl->rbpf = par.bps * par.rchan;
192 	hdl->aucat.maxwrite = 0;
193 	hdl->round = par.round;
194 	hdl->delta = 0;
195 	DPRINTF("aucat: start, maxwrite = %d\n", hdl->aucat.maxwrite);
196 
197 	AMSG_INIT(&hdl->aucat.wmsg);
198 	hdl->aucat.wmsg.cmd = htonl(AMSG_START);
199 	hdl->aucat.wtodo = sizeof(struct amsg);
200 	if (!aucat_wmsg(&hdl->aucat, &hdl->sio.eof))
201 		return 0;
202 	hdl->aucat.rstate = RSTATE_MSG;
203 	hdl->aucat.rtodo = sizeof(struct amsg);
204 	if (!aucat_setfl(&hdl->aucat, 1, &hdl->sio.eof))
205 		return 0;
206 	hdl->walign = hdl->round * hdl->wbpf;
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 int 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->aucat.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 int i, bps, le, sig, chan, rindex, rmult;
331 	static unsigned int 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 int)) {
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->aucat.maxwrite <= 0)
424 		return 0;
425 	if (len > hdl->aucat.maxwrite)
426 		len = hdl->aucat.maxwrite;
427 	if (len > hdl->walign)
428 		len = hdl->walign;
429 	n = aucat_wdata(&hdl->aucat, buf, len, hdl->wbpf, &hdl->sio.eof);
430 	hdl->aucat.maxwrite -= n;
431 	hdl->walign -= n;
432 	if (hdl->walign == 0)
433 		hdl->walign = hdl->round * hdl->wbpf;
434 	return n;
435 }
436 
437 static int
438 sio_aucat_nfds(struct sio_hdl *hdl)
439 {
440 	return 1;
441  }
442 
443 static int
444 sio_aucat_pollfd(struct sio_hdl *sh, struct pollfd *pfd, int events)
445 {
446 	struct sio_aucat_hdl *hdl = (struct sio_aucat_hdl *)sh;
447 
448 	hdl->events = events;
449 	if (hdl->aucat.maxwrite <= 0)
450 		events &= ~POLLOUT;
451 	return aucat_pollfd(&hdl->aucat, pfd, events);
452 }
453 
454 static int
455 sio_aucat_revents(struct sio_hdl *sh, struct pollfd *pfd)
456 {
457 	struct sio_aucat_hdl *hdl = (struct sio_aucat_hdl *)sh;
458 	int revents = pfd->revents;
459 
460 	if (revents & POLLIN) {
461 		while (hdl->aucat.rstate == RSTATE_MSG) {
462 			if (!sio_aucat_runmsg(hdl))
463 				break;
464 		}
465 		if (hdl->aucat.rstate != RSTATE_DATA)
466 			revents &= ~POLLIN;
467 	}
468 	if (revents & POLLOUT) {
469 		if (hdl->aucat.maxwrite <= 0)
470 			revents &= ~POLLOUT;
471 	}
472 	if (hdl->sio.eof)
473 		return POLLHUP;
474 	DPRINTFN(2, "sio_aucat_revents: %x\n", revents & hdl->events);
475 	return revents & (hdl->events | POLLHUP);
476 }
477 
478 static int
479 sio_aucat_setvol(struct sio_hdl *sh, unsigned int vol)
480 {
481 	struct sio_aucat_hdl *hdl = (struct sio_aucat_hdl *)sh;
482 
483 	hdl->reqvol = vol;
484 	return 1;
485 }
486 
487 static void
488 sio_aucat_getvol(struct sio_hdl *sh)
489 {
490 	struct sio_aucat_hdl *hdl = (struct sio_aucat_hdl *)sh;
491 
492 	sio_onvol_cb(&hdl->sio, hdl->reqvol);
493 	return;
494 }
495