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