1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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 32 #ifndef WITHOUT_CAPSICUM 33 #include <sys/capsicum.h> 34 #include <capsicum_helpers.h> 35 #endif 36 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <fcntl.h> 40 #include <sys/ioctl.h> 41 #include <unistd.h> 42 #include <assert.h> 43 #include <errno.h> 44 #include <err.h> 45 #include <sysexits.h> 46 47 #include "audio.h" 48 #include "pci_hda.h" 49 50 /* 51 * Audio Player internal data structures 52 */ 53 54 struct audio { 55 int fd; 56 uint8_t dir; 57 uint8_t inited; 58 char dev_name[64]; 59 }; 60 61 /* 62 * Audio Player module function definitions 63 */ 64 65 /* 66 * audio_init - initialize an instance of audio player 67 * @dev_name - the backend sound device used to play / capture 68 * @dir - dir = 1 for write mode, dir = 0 for read mode 69 */ 70 struct audio * 71 audio_init(const char *dev_name, uint8_t dir) 72 { 73 struct audio *aud = NULL; 74 #ifndef WITHOUT_CAPSICUM 75 cap_rights_t rights; 76 cap_ioctl_t cmds[] = { 77 SNDCTL_DSP_RESET, SNDCTL_DSP_SETFMT, SNDCTL_DSP_CHANNELS, 78 SNDCTL_DSP_SPEED, 79 #ifdef DEBUG_HDA 80 SNDCTL_DSP_GETOSPACE, SNDCTL_DSP_GETISPACE, 81 #endif 82 }; 83 #endif 84 85 assert(dev_name); 86 87 aud = calloc(1, sizeof(*aud)); 88 if (!aud) 89 return NULL; 90 91 if (strlen(dev_name) < sizeof(aud->dev_name)) 92 memcpy(aud->dev_name, dev_name, strlen(dev_name) + 1); 93 else { 94 DPRINTF("dev_name too big"); 95 free(aud); 96 return NULL; 97 } 98 99 aud->dir = dir; 100 101 aud->fd = open(aud->dev_name, aud->dir ? O_WRONLY : O_RDONLY, 0); 102 if (aud->fd == -1) { 103 DPRINTF("Failed to open dev: %s, errno: %d", 104 aud->dev_name, errno); 105 free(aud); 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", 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", 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", 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", 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", 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", 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", 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", 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", errno); 208 return -1; 209 } 210 DPRINTF("fragstotal: 0x%x fragsize: 0x%x", 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 uint8_t *buf, size_t count) 224 { 225 ssize_t len; 226 size_t total; 227 int audio_fd; 228 229 assert(aud); 230 assert(aud->dir); 231 assert(buf); 232 233 audio_fd = aud->fd; 234 assert(audio_fd != -1); 235 236 for (total = 0; total < count; total += len) { 237 len = write(audio_fd, buf + total, count - total); 238 if (len < 0) { 239 DPRINTF("Fail to write to fd: %d, errno: %d", 240 audio_fd, errno); 241 return -1; 242 } 243 } 244 245 return 0; 246 } 247 248 /* 249 * audio_record - records samples from the sound device using 250 * blocking operations. 251 * @aud - the audio player used to capture the samples 252 * @buf - the buffer to receive the samples 253 * @count - the number of bytes to capture in buffer 254 * Returns -1 on error and 0 on success 255 */ 256 int 257 audio_record(struct audio *aud, uint8_t *buf, size_t count) 258 { 259 ssize_t len; 260 size_t total; 261 int audio_fd; 262 263 assert(aud); 264 assert(!aud->dir); 265 assert(buf); 266 267 audio_fd = aud->fd; 268 assert(audio_fd != -1); 269 270 for (total = 0; total < count; total += len) { 271 len = read(audio_fd, buf + total, count - total); 272 if (len < 0) { 273 DPRINTF("Fail to write to fd: %d, errno: %d", 274 audio_fd, errno); 275 return -1; 276 } 277 } 278 279 return 0; 280 } 281