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 * 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 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 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 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 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 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 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 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 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 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 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