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"); 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", 105 aud->dev_name, errno); 106 free(aud); 107 return (NULL); 108 } 109 110 #ifndef WITHOUT_CAPSICUM 111 cap_rights_init(&rights, CAP_IOCTL, CAP_READ, CAP_WRITE); 112 if (caph_rights_limit(aud->fd, &rights) == -1) 113 errx(EX_OSERR, "Unable to apply rights for sandbox"); 114 if (caph_ioctls_limit(aud->fd, cmds, nitems(cmds)) == -1) 115 errx(EX_OSERR, "Unable to limit ioctl rights for sandbox"); 116 #endif 117 118 return aud; 119 } 120 121 /* 122 * audio_set_params - reset the sound device and set the audio params 123 * @aud - the audio player to be configured 124 * @params - the audio parameters to be set 125 */ 126 int 127 audio_set_params(struct audio *aud, struct audio_params *params) 128 { 129 int audio_fd; 130 int format, channels, rate; 131 int err; 132 #if DEBUG_HDA == 1 133 audio_buf_info info; 134 #endif 135 136 assert(aud); 137 assert(params); 138 139 if ((audio_fd = aud->fd) < 0) { 140 DPRINTF("Incorrect audio device descriptor for %s", 141 aud->dev_name); 142 return (-1); 143 } 144 145 /* Reset the device if it was previously opened */ 146 if (aud->inited) { 147 err = ioctl(audio_fd, SNDCTL_DSP_RESET, NULL); 148 if (err == -1) { 149 DPRINTF("Failed to reset fd: %d, errno: %d", 150 aud->fd, errno); 151 return (-1); 152 } 153 } else 154 aud->inited = 1; 155 156 /* Set the Format (Bits per Sample) */ 157 format = params->format; 158 err = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format); 159 if (err == -1) { 160 DPRINTF("Fail to set fmt: 0x%x errno: %d", 161 params->format, errno); 162 return -1; 163 } 164 165 /* The device does not support the requested audio format */ 166 if (format != params->format) { 167 DPRINTF("Mismatch format: 0x%x params->format: 0x%x", 168 format, params->format); 169 return -1; 170 } 171 172 /* Set the Number of Channels */ 173 channels = params->channels; 174 err = ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &channels); 175 if (err == -1) { 176 DPRINTF("Fail to set channels: %d errno: %d", 177 params->channels, errno); 178 return -1; 179 } 180 181 /* The device does not support the requested no. of channels */ 182 if (channels != params->channels) { 183 DPRINTF("Mismatch channels: %d params->channels: %d", 184 channels, params->channels); 185 return -1; 186 } 187 188 /* Set the Sample Rate / Speed */ 189 rate = params->rate; 190 err = ioctl(audio_fd, SNDCTL_DSP_SPEED, &rate); 191 if (err == -1) { 192 DPRINTF("Fail to set speed: %d errno: %d", 193 params->rate, errno); 194 return -1; 195 } 196 197 /* The device does not support the requested rate / speed */ 198 if (rate != params->rate) { 199 DPRINTF("Mismatch rate: %d params->rate: %d", 200 rate, params->rate); 201 return -1; 202 } 203 204 #if DEBUG_HDA == 1 205 err = ioctl(audio_fd, aud->dir ? SNDCTL_DSP_GETOSPACE : 206 SNDCTL_DSP_GETISPACE, &info); 207 if (err == -1) { 208 DPRINTF("Fail to get audio buf info errno: %d", errno); 209 return -1; 210 } 211 DPRINTF("fragstotal: 0x%x fragsize: 0x%x", 212 info.fragstotal, info.fragsize); 213 #endif 214 return 0; 215 } 216 217 /* 218 * audio_playback - plays samples to the sound device using blocking operations 219 * @aud - the audio player used to play the samples 220 * @buf - the buffer containing the samples 221 * @count - the number of bytes in buffer 222 */ 223 int 224 audio_playback(struct audio *aud, const void *buf, size_t count) 225 { 226 int audio_fd = -1; 227 ssize_t len = 0, total = 0; 228 229 assert(aud); 230 assert(aud->dir); 231 assert(buf); 232 233 audio_fd = aud->fd; 234 assert(audio_fd != -1); 235 236 total = 0; 237 while (total < count) { 238 len = write(audio_fd, buf + total, count - total); 239 if (len == -1) { 240 DPRINTF("Fail to write to fd: %d, errno: %d", 241 audio_fd, errno); 242 return -1; 243 } 244 245 total += len; 246 } 247 248 return 0; 249 } 250 251 /* 252 * audio_record - records samples from the sound device using 253 * blocking operations. 254 * @aud - the audio player used to capture the samples 255 * @buf - the buffer to receive the samples 256 * @count - the number of bytes to capture in buffer 257 * Returns -1 on error and 0 on success 258 */ 259 int 260 audio_record(struct audio *aud, void *buf, size_t count) 261 { 262 int audio_fd = -1; 263 ssize_t len = 0, total = 0; 264 265 assert(aud); 266 assert(!aud->dir); 267 assert(buf); 268 269 audio_fd = aud->fd; 270 assert(audio_fd != -1); 271 272 total = 0; 273 while (total < count) { 274 len = read(audio_fd, buf + total, count - total); 275 if (len == -1) { 276 DPRINTF("Fail to write to fd: %d, errno: %d", 277 audio_fd, errno); 278 return -1; 279 } 280 281 total += len; 282 } 283 284 return 0; 285 } 286