]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/audio.c
Add SPDX tags to bhyve(8) HD Audio device.
[FreeBSD/FreeBSD.git] / usr.sbin / bhyve / audio.c
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 }