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