1 /*- 2 * Copyright (c) 2015-2019 Hans Petter Selasky 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/filio.h> 28 #include <sys/soundcard.h> 29 30 #include <stdint.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <fcntl.h> 34 #include <unistd.h> 35 #include <err.h> 36 #include <poll.h> 37 38 #include "backend.h" 39 #include "int.h" 40 41 static int 42 oss_set_format(int fd, int *format) 43 { 44 int value[6]; 45 int error; 46 int fmt; 47 int i; 48 49 value[0] = *format & VPREFERRED_SNE_AFMT; 50 value[1] = *format & VPREFERRED_UNE_AFMT; 51 value[2] = *format & VPREFERRED_SLE_AFMT; 52 value[3] = *format & VPREFERRED_SBE_AFMT; 53 value[4] = *format & VPREFERRED_ULE_AFMT; 54 value[5] = *format & VPREFERRED_UBE_AFMT; 55 56 for (i = 0; i != 6; i++) { 57 fmt = value[i]; 58 if (fmt == 0) 59 continue; 60 error = ioctl(fd, SNDCTL_DSP_SETFMT, &fmt); 61 /* make sure we got the format we asked for */ 62 if (error == 0 && fmt == value[i]) { 63 *format = fmt; 64 return (0); 65 } 66 } 67 return (-1); 68 } 69 70 static void 71 oss_close(struct voss_backend *pbe) 72 { 73 if (pbe->fd > -1) { 74 close(pbe->fd); 75 pbe->fd = -1; 76 } 77 } 78 79 static int 80 oss_open(struct voss_backend *pbe, const char *devname, int samplerate, 81 int bufsize, int *pchannels, int *pformat, int attr, int fionbio) 82 { 83 int temp; 84 int err; 85 86 pbe->fd = open(devname, attr); 87 if (pbe->fd < 0) { 88 warn("Could not open DSP device '%s'", devname); 89 return (-1); 90 } 91 err = ioctl(pbe->fd, FIONBIO, &fionbio); 92 if (err < 0) { 93 warn("Could not set blocking mode on DSP"); 94 goto error; 95 } 96 err = oss_set_format(pbe->fd, pformat); 97 if (err < 0) { 98 warn("Could not set sample format 0x%08x", *pformat); 99 goto error; 100 } 101 temp = *pchannels; 102 bufsize /= temp; /* get buffer size per channel */ 103 do { 104 err = ioctl(pbe->fd, SOUND_PCM_WRITE_CHANNELS, &temp); 105 } while (err < 0 && --temp > 0); 106 107 err = ioctl(pbe->fd, SOUND_PCM_READ_CHANNELS, &temp); 108 if (err < 0 || temp <= 0 || temp > *pchannels) { 109 warn("Could not set DSP channels: %d / %d", temp, *pchannels); 110 goto error; 111 } 112 *pchannels = temp; 113 114 temp = samplerate; 115 err = ioctl(pbe->fd, SNDCTL_DSP_SPEED, &temp); 116 if (err < 0 || temp != samplerate) { 117 warn("Could not set sample rate to %d / %d Hz", temp, samplerate); 118 goto error; 119 } 120 121 temp = bufsize * (*pchannels); 122 err = ioctl(pbe->fd, SNDCTL_DSP_SETBLKSIZE, &temp); 123 if (err < 0) { 124 warn("Could not set block size to %d", temp); 125 goto error; 126 } 127 return (0); 128 error: 129 close(pbe->fd); 130 pbe->fd = -1; 131 return (-1); 132 } 133 134 static int 135 oss_rec_open(struct voss_backend *pbe, const char *devname, int samplerate, 136 int bufsize, int *pchannels, int *pformat) 137 { 138 return (oss_open(pbe, devname, samplerate, bufsize, pchannels, pformat, O_RDONLY, 0)); 139 } 140 141 static int 142 oss_play_open(struct voss_backend *pbe, const char *devname, int samplerate, 143 int bufsize, int *pchannels, int *pformat) 144 { 145 bufsize *= 4; /* XXX allow extra space for jitter */ 146 return (oss_open(pbe, devname, samplerate, bufsize, pchannels, pformat, O_WRONLY, 0)); 147 } 148 149 static int 150 oss_rec_transfer(struct voss_backend *pbe, void *ptr, int len) 151 { 152 struct pollfd fds = { .fd = pbe->fd, .events = POLLIN | POLLRDNORM }; 153 int err; 154 155 /* wait at maximum 2 seconds for data, else something is wrong */ 156 err = poll(&fds, 1, 2000); 157 if (err < 1) 158 return (-1); 159 return (read(pbe->fd, ptr, len)); 160 } 161 162 static int 163 oss_play_transfer(struct voss_backend *pbe, void *ptr, int len) 164 { 165 return (write(pbe->fd, ptr, len)); 166 } 167 168 static void 169 oss_rec_delay(struct voss_backend *pbe, int *pdelay) 170 { 171 if (ioctl(pbe->fd, FIONREAD, pdelay) != 0) 172 *pdelay = -1; 173 } 174 175 static void 176 oss_play_delay(struct voss_backend *pbe, int *pdelay) 177 { 178 if (voss_has_synchronization != 0 || 179 ioctl(pbe->fd, SNDCTL_DSP_GETODELAY, pdelay) != 0) 180 *pdelay = -1; 181 } 182 183 struct voss_backend voss_backend_oss_rec = { 184 .open = oss_rec_open, 185 .close = oss_close, 186 .transfer = oss_rec_transfer, 187 .delay = oss_rec_delay, 188 .fd = -1, 189 }; 190 191 struct voss_backend voss_backend_oss_play = { 192 .open = oss_play_open, 193 .close = oss_close, 194 .transfer = oss_play_transfer, 195 .delay = oss_play_delay, 196 .fd = -1, 197 }; 198