1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2016 Alex Teaca <iateaca@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #ifndef WITHOUT_CAPSICUM 34 #include <sys/capsicum.h> 35 #include <capsicum_helpers.h> 36 #endif 37 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <fcntl.h> 41 #include <sys/ioctl.h> 42 #include <unistd.h> 43 #include <assert.h> 44 #include <errno.h> 45 #include <err.h> 46 #include <sysexits.h> 47 48 #include "audio.h" 49 #include "pci_hda.h" 50 51 /* 52 * Audio Player internal data structures 53 */ 54 55 struct audio { 56 int fd; 57 uint8_t dir; 58 uint8_t inited; 59 char dev_name[64]; 60 }; 61 62 /* 63 * Audio Player module function definitions 64 */ 65 66 /* 67 * audio_init - initialize an instance of audio player 68 * @dev_name - the backend sound device used to play / capture 69 * @dir - dir = 1 for write mode, dir = 0 for read mode 70 */ 71 struct audio * 72 audio_init(const char *dev_name, uint8_t dir) 73 { 74 struct audio *aud = NULL; 75 #ifndef WITHOUT_CAPSICUM 76 cap_rights_t rights; 77 cap_ioctl_t cmds[] = { 78 SNDCTL_DSP_RESET, SNDCTL_DSP_SETFMT, SNDCTL_DSP_CHANNELS, 79 SNDCTL_DSP_SPEED, 80 #ifdef DEBUG_HDA 81 SNDCTL_DSP_GETOSPACE, SNDCTL_DSP_GETISPACE, 82 #endif 83 }; 84 #endif 85 86 assert(dev_name); 87 88 aud = calloc(1, sizeof(*aud)); 89 if (!aud) 90 return NULL; 91 92 if (strlen(dev_name) < sizeof(aud->dev_name)) 93 memcpy(aud->dev_name, dev_name, strlen(dev_name) + 1); 94 else { 95 DPRINTF("dev_name too big\n"); 96 free(aud); 97 return NULL; 98 } 99 100 aud->dir = dir; 101 102 aud->fd = open(aud->dev_name, aud->dir ? O_WRONLY : O_RDONLY, 0); 103 if (aud->fd == -1) { 104 DPRINTF("Failed to open dev: %s, errno: %d\n", 105 aud->dev_name, errno); 106 return (NULL); 107 } 108 109 #ifndef WITHOUT_CAPSICUM 110 cap_rights_init(&rights, CAP_IOCTL, CAP_READ, CAP_WRITE); 111 if (caph_rights_limit(aud->fd, &rights) == -1) 112 errx(EX_OSERR, "Unable to apply rights for sandbox"); 113 if (caph_ioctls_limit(aud->fd, cmds, nitems(cmds)) == -1) 114 errx(EX_OSERR, "Unable to limit ioctl rights for sandbox"); 115 #endif 116 117 return aud; 118 } 119 120 /* 121 * audio_set_params - reset the sound device and set the audio params 122 * @aud - the audio player to be configured 123 * @params - the audio parameters to be set 124 */ 125 int 126 audio_set_params(struct audio *aud, struct audio_params *params) 127 { 128 int audio_fd; 129 int format, channels, rate; 130 int err; 131 #if DEBUG_HDA == 1 132 audio_buf_info info; 133 #endif 134 135 assert(aud); 136 assert(params); 137 138 if ((audio_fd = aud->fd) < 0) { 139 DPRINTF("Incorrect audio device descriptor for %s\n", 140 aud->dev_name); 141 return (-1); 142 } 143 144 /* Reset the device if it was previously opened */ 145 if (aud->inited) { 146 err = ioctl(audio_fd, SNDCTL_DSP_RESET, NULL); 147 if (err == -1) { 148 DPRINTF("Failed to reset fd: %d, errno: %d\n", 149 aud->fd, errno); 150 return (-1); 151 } 152 } else 153 aud->inited = 1; 154 155 /* Set the Format (Bits per Sample) */ 156 format = params->format; 157 err = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format); 158 if (err == -1) { 159 DPRINTF("Fail to set fmt: 0x%x errno: %d\n", 160 params->format, errno); 161 return -1; 162 } 163 164 /* The device does not support the requested audio format */ 165 if (format != params->format) { 166 DPRINTF("Mismatch format: 0x%x params->format: 0x%x\n", 167 format, params->format); 168 return -1; 169 } 170 171 /* Set the Number of Channels */ 172 channels = params->channels; 173 err = ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &channels); 174 if (err == -1) { 175 DPRINTF("Fail to set channels: %d errno: %d\n", 176 params->channels, errno); 177 return -1; 178 } 179 180 /* The device does not support the requested no. of channels */ 181 if (channels != params->channels) { 182 DPRINTF("Mismatch channels: %d params->channels: %d\n", 183 channels, params->channels); 184 return -1; 185 } 186 187 /* Set the Sample Rate / Speed */ 188 rate = params->rate; 189 err = ioctl(audio_fd, SNDCTL_DSP_SPEED, &rate); 190 if (err == -1) { 191 DPRINTF("Fail to set speed: %d errno: %d\n", 192 params->rate, errno); 193 return -1; 194 } 195 196 /* The device does not support the requested rate / speed */ 197 if (rate != params->rate) { 198 DPRINTF("Mismatch rate: %d params->rate: %d\n", 199 rate, params->rate); 200 return -1; 201 } 202 203 #if DEBUG_HDA == 1 204 err = ioctl(audio_fd, aud->dir ? SNDCTL_DSP_GETOSPACE : 205 SNDCTL_DSP_GETISPACE, &info); 206 if (err == -1) { 207 DPRINTF("Fail to get audio buf info errno: %d\n", errno); 208 return -1; 209 } 210 DPRINTF("fragstotal: 0x%x fragsize: 0x%x\n", 211 info.fragstotal, info.fragsize); 212 #endif 213 return 0; 214 } 215 216 /* 217 * audio_playback - plays samples to the sound device using blocking operations 218 * @aud - the audio player used to play the samples 219 * @buf - the buffer containing the samples 220 * @count - the number of bytes in buffer 221 */ 222 int 223 audio_playback(struct audio *aud, const void *buf, size_t count) 224 { 225 int audio_fd = -1; 226 ssize_t len = 0, total = 0; 227 228 assert(aud); 229 assert(aud->dir); 230 assert(buf); 231 232 audio_fd = aud->fd; 233 assert(audio_fd != -1); 234 235 total = 0; 236 while (total < count) { 237 len = write(audio_fd, buf + total, count - total); 238 if (len == -1) { 239 DPRINTF("Fail to write to fd: %d, errno: %d\n", 240 audio_fd, errno); 241 return -1; 242 } 243 244 total += len; 245 } 246 247 return 0; 248 } 249 250 /* 251 * audio_record - records samples from the sound device using 252 * blocking operations. 253 * @aud - the audio player used to capture the samples 254 * @buf - the buffer to receive the samples 255 * @count - the number of bytes to capture in buffer 256 * Returns -1 on error and 0 on success 257 */ 258 int 259 audio_record(struct audio *aud, void *buf, size_t count) 260 { 261 int audio_fd = -1; 262 ssize_t len = 0, total = 0; 263 264 assert(aud); 265 assert(!aud->dir); 266 assert(buf); 267 268 audio_fd = aud->fd; 269 assert(audio_fd != -1); 270 271 total = 0; 272 while (total < count) { 273 len = read(audio_fd, buf + total, count - total); 274 if (len == -1) { 275 DPRINTF("Fail to write to fd: %d, errno: %d\n", 276 audio_fd, errno); 277 return -1; 278 } 279 280 total += len; 281 } 282 283 return 0; 284 } 285