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