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