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