xref: /freebsd/lib/virtual_oss/sndio/sndio.c (revision 9cab9fde5edad9b409dd2317a2aec7815e6d6bed)
1*9cab9fdeSChristos Margiolis /*-
2*9cab9fdeSChristos Margiolis  * Copyright (c) 2021 Tim Creech <tcreech@tcreech.com>
3*9cab9fdeSChristos Margiolis  *
4*9cab9fdeSChristos Margiolis  * Redistribution and use in source and binary forms, with or without
5*9cab9fdeSChristos Margiolis  * modification, are permitted provided that the following conditions
6*9cab9fdeSChristos Margiolis  * are met:
7*9cab9fdeSChristos Margiolis  * 1. Redistributions of source code must retain the above copyright
8*9cab9fdeSChristos Margiolis  *    notice, this list of conditions and the following disclaimer.
9*9cab9fdeSChristos Margiolis  * 2. Redistributions in binary form must reproduce the above copyright
10*9cab9fdeSChristos Margiolis  *    notice, this list of conditions and the following disclaimer in the
11*9cab9fdeSChristos Margiolis  *    documentation and/or other materials provided with the distribution.
12*9cab9fdeSChristos Margiolis  *
13*9cab9fdeSChristos Margiolis  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14*9cab9fdeSChristos Margiolis  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15*9cab9fdeSChristos Margiolis  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16*9cab9fdeSChristos Margiolis  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17*9cab9fdeSChristos Margiolis  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18*9cab9fdeSChristos Margiolis  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19*9cab9fdeSChristos Margiolis  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20*9cab9fdeSChristos Margiolis  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21*9cab9fdeSChristos Margiolis  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22*9cab9fdeSChristos Margiolis  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23*9cab9fdeSChristos Margiolis  * SUCH DAMAGE.
24*9cab9fdeSChristos Margiolis  */
25*9cab9fdeSChristos Margiolis 
26*9cab9fdeSChristos Margiolis #include <sys/queue.h>
27*9cab9fdeSChristos Margiolis #include <sys/soundcard.h>
28*9cab9fdeSChristos Margiolis 
29*9cab9fdeSChristos Margiolis #include <err.h>
30*9cab9fdeSChristos Margiolis #include <stdlib.h>
31*9cab9fdeSChristos Margiolis #include <string.h>
32*9cab9fdeSChristos Margiolis 
33*9cab9fdeSChristos Margiolis #include <sndio.h>
34*9cab9fdeSChristos Margiolis 
35*9cab9fdeSChristos Margiolis #include "backend.h"
36*9cab9fdeSChristos Margiolis #include "int.h"
37*9cab9fdeSChristos Margiolis 
38*9cab9fdeSChristos Margiolis static struct sio_hdl *
get_sio_hdl(struct voss_backend * pbe)39*9cab9fdeSChristos Margiolis get_sio_hdl(struct voss_backend *pbe)
40*9cab9fdeSChristos Margiolis {
41*9cab9fdeSChristos Margiolis 	if (pbe)
42*9cab9fdeSChristos Margiolis 		return (pbe->arg);
43*9cab9fdeSChristos Margiolis 
44*9cab9fdeSChristos Margiolis 	return (NULL);
45*9cab9fdeSChristos Margiolis }
46*9cab9fdeSChristos Margiolis 
47*9cab9fdeSChristos Margiolis static void
sndio_close(struct voss_backend * pbe)48*9cab9fdeSChristos Margiolis sndio_close(struct voss_backend *pbe)
49*9cab9fdeSChristos Margiolis {
50*9cab9fdeSChristos Margiolis 	if (!pbe)
51*9cab9fdeSChristos Margiolis 		return;
52*9cab9fdeSChristos Margiolis 
53*9cab9fdeSChristos Margiolis 	if (get_sio_hdl(pbe))
54*9cab9fdeSChristos Margiolis 		sio_close(get_sio_hdl(pbe));
55*9cab9fdeSChristos Margiolis }
56*9cab9fdeSChristos Margiolis 
57*9cab9fdeSChristos Margiolis static int
sndio_get_signedness(int * fmt)58*9cab9fdeSChristos Margiolis sndio_get_signedness(int *fmt)
59*9cab9fdeSChristos Margiolis {
60*9cab9fdeSChristos Margiolis 	int s_fmt = *fmt & (VPREFERRED_SLE_AFMT | VPREFERRED_SBE_AFMT);
61*9cab9fdeSChristos Margiolis 
62*9cab9fdeSChristos Margiolis 	if (s_fmt) {
63*9cab9fdeSChristos Margiolis 		*fmt = s_fmt;
64*9cab9fdeSChristos Margiolis 		return (1);
65*9cab9fdeSChristos Margiolis 	}
66*9cab9fdeSChristos Margiolis 	*fmt = *fmt & (VPREFERRED_ULE_AFMT | VPREFERRED_UBE_AFMT);
67*9cab9fdeSChristos Margiolis 	return (0);
68*9cab9fdeSChristos Margiolis }
69*9cab9fdeSChristos Margiolis 
70*9cab9fdeSChristos Margiolis static int
sndio_get_endianness_is_le(int * fmt)71*9cab9fdeSChristos Margiolis sndio_get_endianness_is_le(int *fmt)
72*9cab9fdeSChristos Margiolis {
73*9cab9fdeSChristos Margiolis 	int le_fmt = *fmt & (VPREFERRED_SLE_AFMT | VPREFERRED_ULE_AFMT);
74*9cab9fdeSChristos Margiolis 
75*9cab9fdeSChristos Margiolis 	if (le_fmt) {
76*9cab9fdeSChristos Margiolis 		*fmt = le_fmt;
77*9cab9fdeSChristos Margiolis 		return (1);
78*9cab9fdeSChristos Margiolis 	}
79*9cab9fdeSChristos Margiolis 	*fmt = *fmt & (VPREFERRED_SBE_AFMT | VPREFERRED_UBE_AFMT);
80*9cab9fdeSChristos Margiolis 	return (0);
81*9cab9fdeSChristos Margiolis }
82*9cab9fdeSChristos Margiolis 
83*9cab9fdeSChristos Margiolis static int
sndio_get_bits(int * fmt)84*9cab9fdeSChristos Margiolis sndio_get_bits(int *fmt)
85*9cab9fdeSChristos Margiolis {
86*9cab9fdeSChristos Margiolis 	if (*fmt & AFMT_16BIT)
87*9cab9fdeSChristos Margiolis 		return (16);
88*9cab9fdeSChristos Margiolis 	if (*fmt & AFMT_24BIT)
89*9cab9fdeSChristos Margiolis 		return (24);
90*9cab9fdeSChristos Margiolis 	if (*fmt & AFMT_32BIT)
91*9cab9fdeSChristos Margiolis 		return (32);
92*9cab9fdeSChristos Margiolis 	if (*fmt & AFMT_8BIT)
93*9cab9fdeSChristos Margiolis 		return (8);
94*9cab9fdeSChristos Margiolis 	return (-1);
95*9cab9fdeSChristos Margiolis 	/* TODO AFMT_BIT */
96*9cab9fdeSChristos Margiolis }
97*9cab9fdeSChristos Margiolis 
98*9cab9fdeSChristos Margiolis static int
sndio_open(struct voss_backend * pbe,const char * devname,int samplerate,int bufsize,int * pchannels,int * pformat,int direction)99*9cab9fdeSChristos Margiolis sndio_open(struct voss_backend *pbe, const char *devname,
100*9cab9fdeSChristos Margiolis     int samplerate, int bufsize, int *pchannels, int *pformat, int direction)
101*9cab9fdeSChristos Margiolis {
102*9cab9fdeSChristos Margiolis 	const char *sndio_name = devname + strlen("/dev/sndio/");
103*9cab9fdeSChristos Margiolis 
104*9cab9fdeSChristos Margiolis 	int sig = sndio_get_signedness(pformat);
105*9cab9fdeSChristos Margiolis 	int le = sndio_get_endianness_is_le(pformat);
106*9cab9fdeSChristos Margiolis 	int bits = sndio_get_bits(pformat);
107*9cab9fdeSChristos Margiolis 
108*9cab9fdeSChristos Margiolis 	if (bits == -1) {
109*9cab9fdeSChristos Margiolis 		warn("unsupported format precision");
110*9cab9fdeSChristos Margiolis 		return (-1);
111*9cab9fdeSChristos Margiolis 	}
112*9cab9fdeSChristos Margiolis 
113*9cab9fdeSChristos Margiolis 	struct sio_hdl *hdl = sio_open(sndio_name, direction, 0);
114*9cab9fdeSChristos Margiolis 
115*9cab9fdeSChristos Margiolis 	if (hdl == 0) {
116*9cab9fdeSChristos Margiolis 		warn("sndio: failed to open device");
117*9cab9fdeSChristos Margiolis 		return (-1);
118*9cab9fdeSChristos Margiolis 	}
119*9cab9fdeSChristos Margiolis 
120*9cab9fdeSChristos Margiolis 	struct sio_par par;
121*9cab9fdeSChristos Margiolis 
122*9cab9fdeSChristos Margiolis 	sio_initpar(&par);
123*9cab9fdeSChristos Margiolis 	par.pchan = *pchannels;
124*9cab9fdeSChristos Margiolis 	par.sig = sig;
125*9cab9fdeSChristos Margiolis 	par.bits = bits;
126*9cab9fdeSChristos Margiolis 	par.bps = SIO_BPS(bits);
127*9cab9fdeSChristos Margiolis 	par.le = le;
128*9cab9fdeSChristos Margiolis 	par.rate = samplerate;
129*9cab9fdeSChristos Margiolis 	par.appbufsz = bufsize;
130*9cab9fdeSChristos Margiolis 	par.xrun = SIO_SYNC;
131*9cab9fdeSChristos Margiolis 	if (!sio_setpar(hdl, &par))
132*9cab9fdeSChristos Margiolis 		errx(1, "internal error, sio_setpar() failed");
133*9cab9fdeSChristos Margiolis 	if (!sio_getpar(hdl, &par))
134*9cab9fdeSChristos Margiolis 		errx(1, "internal error, sio_getpar() failed");
135*9cab9fdeSChristos Margiolis 	if ((int)par.pchan != *pchannels)
136*9cab9fdeSChristos Margiolis 		errx(1, "couldn't set number of channels");
137*9cab9fdeSChristos Margiolis 	if ((int)par.sig != sig || (int)par.bits != bits || (int)par.le != le)
138*9cab9fdeSChristos Margiolis 		errx(1, "couldn't set format");
139*9cab9fdeSChristos Margiolis 	if ((int)par.bits != bits)
140*9cab9fdeSChristos Margiolis 		errx(1, "couldn't set precision");
141*9cab9fdeSChristos Margiolis 	if ((int)par.rate < samplerate * 995 / 1000 ||
142*9cab9fdeSChristos Margiolis 	    (int)par.rate > samplerate * 1005 / 1000)
143*9cab9fdeSChristos Margiolis 		errx(1, "couldn't set rate");
144*9cab9fdeSChristos Margiolis 	if (par.xrun != SIO_SYNC)
145*9cab9fdeSChristos Margiolis 		errx(1, "couldn't set xun policy");
146*9cab9fdeSChristos Margiolis 
147*9cab9fdeSChristos Margiolis 	/* Save the device handle with the backend */
148*9cab9fdeSChristos Margiolis 	pbe->arg = hdl;
149*9cab9fdeSChristos Margiolis 
150*9cab9fdeSChristos Margiolis 	/* Start the device. */
151*9cab9fdeSChristos Margiolis 	if (!sio_start(hdl))
152*9cab9fdeSChristos Margiolis 		errx(1, "couldn't start device");
153*9cab9fdeSChristos Margiolis 
154*9cab9fdeSChristos Margiolis 	return (0);
155*9cab9fdeSChristos Margiolis }
156*9cab9fdeSChristos Margiolis 
157*9cab9fdeSChristos Margiolis static int
sndio_open_play(struct voss_backend * pbe,const char * devname,int samplerate,int bufsize,int * pchannels,int * pformat)158*9cab9fdeSChristos Margiolis sndio_open_play(struct voss_backend *pbe, const char *devname,
159*9cab9fdeSChristos Margiolis     int samplerate, int bufsize, int *pchannels, int *pformat)
160*9cab9fdeSChristos Margiolis {
161*9cab9fdeSChristos Margiolis 	return (sndio_open(pbe, devname, samplerate, bufsize, pchannels, pformat, SIO_PLAY));
162*9cab9fdeSChristos Margiolis }
163*9cab9fdeSChristos Margiolis 
164*9cab9fdeSChristos Margiolis static int
sndio_open_rec(struct voss_backend * pbe,const char * devname,int samplerate,int bufsize,int * pchannels,int * pformat)165*9cab9fdeSChristos Margiolis sndio_open_rec(struct voss_backend *pbe, const char *devname,
166*9cab9fdeSChristos Margiolis     int samplerate, int bufsize, int *pchannels, int *pformat)
167*9cab9fdeSChristos Margiolis {
168*9cab9fdeSChristos Margiolis 	return (sndio_open(pbe, devname, samplerate, bufsize, pchannels, pformat, SIO_REC));
169*9cab9fdeSChristos Margiolis }
170*9cab9fdeSChristos Margiolis 
171*9cab9fdeSChristos Margiolis static int
sndio_play_transfer(struct voss_backend * pbe,void * ptr,int len)172*9cab9fdeSChristos Margiolis sndio_play_transfer(struct voss_backend *pbe, void *ptr, int len)
173*9cab9fdeSChristos Margiolis {
174*9cab9fdeSChristos Margiolis 	return (sio_write(get_sio_hdl(pbe), ptr, len));
175*9cab9fdeSChristos Margiolis }
176*9cab9fdeSChristos Margiolis 
177*9cab9fdeSChristos Margiolis static int
sndio_rec_transfer(struct voss_backend * pbe,void * ptr,int len)178*9cab9fdeSChristos Margiolis sndio_rec_transfer(struct voss_backend *pbe, void *ptr, int len)
179*9cab9fdeSChristos Margiolis {
180*9cab9fdeSChristos Margiolis 	return (sio_read(get_sio_hdl(pbe), ptr, len));
181*9cab9fdeSChristos Margiolis }
182*9cab9fdeSChristos Margiolis 
183*9cab9fdeSChristos Margiolis static void
sndio_delay(struct voss_backend * pbe __unused,int * pdelay)184*9cab9fdeSChristos Margiolis sndio_delay(struct voss_backend *pbe __unused, int *pdelay)
185*9cab9fdeSChristos Margiolis {
186*9cab9fdeSChristos Margiolis 	*pdelay = -1;
187*9cab9fdeSChristos Margiolis }
188*9cab9fdeSChristos Margiolis 
189*9cab9fdeSChristos Margiolis struct voss_backend voss_backend_sndio_rec = {
190*9cab9fdeSChristos Margiolis 	.open = sndio_open_rec,
191*9cab9fdeSChristos Margiolis 	.close = sndio_close,
192*9cab9fdeSChristos Margiolis 	.transfer = sndio_rec_transfer,
193*9cab9fdeSChristos Margiolis 	.delay = sndio_delay,
194*9cab9fdeSChristos Margiolis 	.fd = -1,
195*9cab9fdeSChristos Margiolis };
196*9cab9fdeSChristos Margiolis 
197*9cab9fdeSChristos Margiolis struct voss_backend voss_backend_sndio_play = {
198*9cab9fdeSChristos Margiolis 	.open = sndio_open_play,
199*9cab9fdeSChristos Margiolis 	.close = sndio_close,
200*9cab9fdeSChristos Margiolis 	.transfer = sndio_play_transfer,
201*9cab9fdeSChristos Margiolis 	.delay = sndio_delay,
202*9cab9fdeSChristos Margiolis 	.fd = -1,
203*9cab9fdeSChristos Margiolis };
204