xref: /illumos-gate/usr/src/cmd/bhyve/audio.c (revision 32640292339b07090f10ce34d455f98711077343)
184659b24SMichael Zeller /*-
2*32640292SAndy Fiddaman  * SPDX-License-Identifier: BSD-2-Clause
384659b24SMichael Zeller  *
484659b24SMichael Zeller  * Copyright (c) 2016 Alex Teaca <iateaca@FreeBSD.org>
584659b24SMichael Zeller  * All rights reserved.
684659b24SMichael Zeller  *
784659b24SMichael Zeller  * Redistribution and use in source and binary forms, with or without
884659b24SMichael Zeller  * modification, are permitted provided that the following conditions
984659b24SMichael Zeller  * are met:
1084659b24SMichael Zeller  * 1. Redistributions of source code must retain the above copyright
1184659b24SMichael Zeller  *    notice, this list of conditions and the following disclaimer.
1284659b24SMichael Zeller  * 2. Redistributions in binary form must reproduce the above copyright
1384659b24SMichael Zeller  *    notice, this list of conditions and the following disclaimer in the
1484659b24SMichael Zeller  *    documentation and/or other materials provided with the distribution.
1584659b24SMichael Zeller  *
1684659b24SMichael Zeller  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
1784659b24SMichael Zeller  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1884659b24SMichael Zeller  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1984659b24SMichael Zeller  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2084659b24SMichael Zeller  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2184659b24SMichael Zeller  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2284659b24SMichael Zeller  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2384659b24SMichael Zeller  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2484659b24SMichael Zeller  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2584659b24SMichael Zeller  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2684659b24SMichael Zeller  * SUCH DAMAGE.
2784659b24SMichael Zeller  *
2884659b24SMichael Zeller  */
2984659b24SMichael Zeller 
3084659b24SMichael Zeller #include <sys/cdefs.h>
3184659b24SMichael Zeller 
3284659b24SMichael Zeller #ifndef WITHOUT_CAPSICUM
3384659b24SMichael Zeller #include <sys/capsicum.h>
3484659b24SMichael Zeller #include <capsicum_helpers.h>
3584659b24SMichael Zeller #endif
3684659b24SMichael Zeller 
3784659b24SMichael Zeller #include <stdio.h>
3884659b24SMichael Zeller #include <stdlib.h>
3984659b24SMichael Zeller #include <fcntl.h>
4084659b24SMichael Zeller #include <sys/ioctl.h>
4184659b24SMichael Zeller #include <unistd.h>
4284659b24SMichael Zeller #include <assert.h>
4384659b24SMichael Zeller #include <errno.h>
4484659b24SMichael Zeller #include <err.h>
4584659b24SMichael Zeller #include <sysexits.h>
4684659b24SMichael Zeller 
4784659b24SMichael Zeller #include "audio.h"
4884659b24SMichael Zeller #include "pci_hda.h"
4984659b24SMichael Zeller 
5084659b24SMichael Zeller /*
5184659b24SMichael Zeller  * Audio Player internal data structures
5284659b24SMichael Zeller  */
5384659b24SMichael Zeller 
5484659b24SMichael Zeller struct audio {
5584659b24SMichael Zeller 	int fd;
5684659b24SMichael Zeller 	uint8_t dir;
5784659b24SMichael Zeller 	uint8_t inited;
5884659b24SMichael Zeller 	char dev_name[64];
5984659b24SMichael Zeller };
6084659b24SMichael Zeller 
6184659b24SMichael Zeller /*
6284659b24SMichael Zeller  * Audio Player module function definitions
6384659b24SMichael Zeller  */
6484659b24SMichael Zeller 
6584659b24SMichael Zeller /*
6684659b24SMichael Zeller  * audio_init - initialize an instance of audio player
6784659b24SMichael Zeller  * @dev_name - the backend sound device used to play / capture
6884659b24SMichael Zeller  * @dir - dir = 1 for write mode, dir = 0 for read mode
6984659b24SMichael Zeller  */
7084659b24SMichael Zeller struct audio *
audio_init(const char * dev_name,uint8_t dir)7184659b24SMichael Zeller audio_init(const char *dev_name, uint8_t dir)
7284659b24SMichael Zeller {
7384659b24SMichael Zeller 	struct audio *aud = NULL;
7484659b24SMichael Zeller #ifndef WITHOUT_CAPSICUM
7584659b24SMichael Zeller 	cap_rights_t rights;
7684659b24SMichael Zeller 	cap_ioctl_t cmds[] = {
7784659b24SMichael Zeller 	    SNDCTL_DSP_RESET, SNDCTL_DSP_SETFMT, SNDCTL_DSP_CHANNELS,
7884659b24SMichael Zeller 	    SNDCTL_DSP_SPEED,
7984659b24SMichael Zeller #ifdef DEBUG_HDA
8084659b24SMichael Zeller 	    SNDCTL_DSP_GETOSPACE, SNDCTL_DSP_GETISPACE,
8184659b24SMichael Zeller #endif
8284659b24SMichael Zeller 	};
8384659b24SMichael Zeller #endif
8484659b24SMichael Zeller 
8584659b24SMichael Zeller 	assert(dev_name);
8684659b24SMichael Zeller 
8784659b24SMichael Zeller 	aud = calloc(1, sizeof(*aud));
8884659b24SMichael Zeller 	if (!aud)
8984659b24SMichael Zeller 		return NULL;
9084659b24SMichael Zeller 
9184659b24SMichael Zeller 	if (strlen(dev_name) < sizeof(aud->dev_name))
9284659b24SMichael Zeller 		memcpy(aud->dev_name, dev_name, strlen(dev_name) + 1);
9384659b24SMichael Zeller 	else {
94154972afSPatrick Mooney 		DPRINTF("dev_name too big");
9584659b24SMichael Zeller 		free(aud);
9684659b24SMichael Zeller 		return NULL;
9784659b24SMichael Zeller 	}
9884659b24SMichael Zeller 
9984659b24SMichael Zeller 	aud->dir = dir;
10084659b24SMichael Zeller 
10184659b24SMichael Zeller 	aud->fd = open(aud->dev_name, aud->dir ? O_WRONLY : O_RDONLY, 0);
10284659b24SMichael Zeller 	if (aud->fd == -1) {
103154972afSPatrick Mooney 		DPRINTF("Failed to open dev: %s, errno: %d",
10484659b24SMichael Zeller 		    aud->dev_name, errno);
10584659b24SMichael Zeller 		free(aud);
10684659b24SMichael Zeller 		return (NULL);
10784659b24SMichael Zeller 	}
10884659b24SMichael Zeller 
10984659b24SMichael Zeller #ifndef WITHOUT_CAPSICUM
11084659b24SMichael Zeller 	cap_rights_init(&rights, CAP_IOCTL, CAP_READ, CAP_WRITE);
11184659b24SMichael Zeller 	if (caph_rights_limit(aud->fd, &rights) == -1)
11284659b24SMichael Zeller 		errx(EX_OSERR, "Unable to apply rights for sandbox");
11384659b24SMichael Zeller 	if (caph_ioctls_limit(aud->fd, cmds, nitems(cmds)) == -1)
11484659b24SMichael Zeller 		errx(EX_OSERR, "Unable to limit ioctl rights for sandbox");
11584659b24SMichael Zeller #endif
11684659b24SMichael Zeller 
11784659b24SMichael Zeller 	return aud;
11884659b24SMichael Zeller }
11984659b24SMichael Zeller 
12084659b24SMichael Zeller /*
12184659b24SMichael Zeller  * audio_set_params - reset the sound device and set the audio params
12284659b24SMichael Zeller  * @aud - the audio player to be configured
12384659b24SMichael Zeller  * @params - the audio parameters to be set
12484659b24SMichael Zeller  */
12584659b24SMichael Zeller int
audio_set_params(struct audio * aud,struct audio_params * params)12684659b24SMichael Zeller audio_set_params(struct audio *aud, struct audio_params *params)
12784659b24SMichael Zeller {
12884659b24SMichael Zeller 	int audio_fd;
12984659b24SMichael Zeller 	int format, channels, rate;
13084659b24SMichael Zeller 	int err;
13184659b24SMichael Zeller #if DEBUG_HDA == 1
13284659b24SMichael Zeller 	audio_buf_info info;
13384659b24SMichael Zeller #endif
13484659b24SMichael Zeller 
13584659b24SMichael Zeller 	assert(aud);
13684659b24SMichael Zeller 	assert(params);
13784659b24SMichael Zeller 
13884659b24SMichael Zeller 	if ((audio_fd = aud->fd) < 0) {
139154972afSPatrick Mooney 		DPRINTF("Incorrect audio device descriptor for %s",
14084659b24SMichael Zeller 		    aud->dev_name);
14184659b24SMichael Zeller 		return (-1);
14284659b24SMichael Zeller 	}
14384659b24SMichael Zeller 
14484659b24SMichael Zeller 	/* Reset the device if it was previously opened */
14584659b24SMichael Zeller 	if (aud->inited) {
14684659b24SMichael Zeller 		err = ioctl(audio_fd, SNDCTL_DSP_RESET, NULL);
14784659b24SMichael Zeller 		if (err == -1) {
148154972afSPatrick Mooney 			DPRINTF("Failed to reset fd: %d, errno: %d",
14984659b24SMichael Zeller 			    aud->fd, errno);
15084659b24SMichael Zeller 			return (-1);
15184659b24SMichael Zeller 		}
15284659b24SMichael Zeller 	} else
15384659b24SMichael Zeller 		aud->inited = 1;
15484659b24SMichael Zeller 
15584659b24SMichael Zeller 	/* Set the Format (Bits per Sample) */
15684659b24SMichael Zeller 	format = params->format;
15784659b24SMichael Zeller 	err = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format);
15884659b24SMichael Zeller 	if (err == -1) {
159154972afSPatrick Mooney 		DPRINTF("Fail to set fmt: 0x%x errno: %d",
16084659b24SMichael Zeller 		    params->format, errno);
16184659b24SMichael Zeller 		return -1;
16284659b24SMichael Zeller 	}
16384659b24SMichael Zeller 
16484659b24SMichael Zeller 	/* The device does not support the requested audio format */
16584659b24SMichael Zeller 	if (format != params->format) {
166154972afSPatrick Mooney 		DPRINTF("Mismatch format: 0x%x params->format: 0x%x",
16784659b24SMichael Zeller 		    format, params->format);
16884659b24SMichael Zeller 		return -1;
16984659b24SMichael Zeller 	}
17084659b24SMichael Zeller 
17184659b24SMichael Zeller 	/* Set the Number of Channels */
17284659b24SMichael Zeller 	channels = params->channels;
17384659b24SMichael Zeller 	err = ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &channels);
17484659b24SMichael Zeller 	if (err == -1) {
175154972afSPatrick Mooney 		DPRINTF("Fail to set channels: %d errno: %d",
17684659b24SMichael Zeller 		    params->channels, errno);
17784659b24SMichael Zeller 		return -1;
17884659b24SMichael Zeller 	}
17984659b24SMichael Zeller 
18084659b24SMichael Zeller 	/* The device does not support the requested no. of channels */
18184659b24SMichael Zeller 	if (channels != params->channels) {
182154972afSPatrick Mooney 		DPRINTF("Mismatch channels: %d params->channels: %d",
18384659b24SMichael Zeller 		    channels, params->channels);
18484659b24SMichael Zeller 		return -1;
18584659b24SMichael Zeller 	}
18684659b24SMichael Zeller 
18784659b24SMichael Zeller 	/* Set the Sample Rate / Speed */
18884659b24SMichael Zeller 	rate = params->rate;
18984659b24SMichael Zeller 	err = ioctl(audio_fd, SNDCTL_DSP_SPEED, &rate);
19084659b24SMichael Zeller 	if (err == -1) {
191154972afSPatrick Mooney 		DPRINTF("Fail to set speed: %d errno: %d",
19284659b24SMichael Zeller 		    params->rate, errno);
19384659b24SMichael Zeller 		return -1;
19484659b24SMichael Zeller 	}
19584659b24SMichael Zeller 
19684659b24SMichael Zeller 	/* The device does not support the requested rate / speed */
19784659b24SMichael Zeller 	if (rate != params->rate) {
198154972afSPatrick Mooney 		DPRINTF("Mismatch rate: %d params->rate: %d",
19984659b24SMichael Zeller 		    rate, params->rate);
20084659b24SMichael Zeller 		return -1;
20184659b24SMichael Zeller 	}
20284659b24SMichael Zeller 
20384659b24SMichael Zeller #if DEBUG_HDA == 1
20484659b24SMichael Zeller 	err = ioctl(audio_fd, aud->dir ? SNDCTL_DSP_GETOSPACE :
20584659b24SMichael Zeller 	    SNDCTL_DSP_GETISPACE, &info);
20684659b24SMichael Zeller 	if (err == -1) {
207154972afSPatrick Mooney 		DPRINTF("Fail to get audio buf info errno: %d", errno);
20884659b24SMichael Zeller 		return -1;
20984659b24SMichael Zeller 	}
210154972afSPatrick Mooney 	DPRINTF("fragstotal: 0x%x fragsize: 0x%x",
21184659b24SMichael Zeller 	    info.fragstotal, info.fragsize);
21284659b24SMichael Zeller #endif
21384659b24SMichael Zeller 	return 0;
21484659b24SMichael Zeller }
21584659b24SMichael Zeller 
21684659b24SMichael Zeller /*
21784659b24SMichael Zeller  * audio_playback - plays samples to the sound device using blocking operations
21884659b24SMichael Zeller  * @aud - the audio player used to play the samples
21984659b24SMichael Zeller  * @buf - the buffer containing the samples
22084659b24SMichael Zeller  * @count - the number of bytes in buffer
22184659b24SMichael Zeller  */
22284659b24SMichael Zeller int
audio_playback(struct audio * aud,const uint8_t * buf,size_t count)22359d65d31SAndy Fiddaman audio_playback(struct audio *aud, const uint8_t *buf, size_t count)
22484659b24SMichael Zeller {
22559d65d31SAndy Fiddaman 	ssize_t len;
22659d65d31SAndy Fiddaman 	size_t total;
22759d65d31SAndy Fiddaman 	int audio_fd;
22884659b24SMichael Zeller 
22984659b24SMichael Zeller 	assert(aud);
23084659b24SMichael Zeller 	assert(aud->dir);
23184659b24SMichael Zeller 	assert(buf);
23284659b24SMichael Zeller 
23384659b24SMichael Zeller 	audio_fd = aud->fd;
23484659b24SMichael Zeller 	assert(audio_fd != -1);
23584659b24SMichael Zeller 
23659d65d31SAndy Fiddaman 	for (total = 0; total < count; total += len) {
23784659b24SMichael Zeller 		len = write(audio_fd, buf + total, count - total);
23859d65d31SAndy Fiddaman 		if (len < 0) {
239154972afSPatrick Mooney 			DPRINTF("Fail to write to fd: %d, errno: %d",
24084659b24SMichael Zeller 			    audio_fd, errno);
24184659b24SMichael Zeller 			return -1;
24284659b24SMichael Zeller 		}
24384659b24SMichael Zeller 	}
24484659b24SMichael Zeller 
24584659b24SMichael Zeller 	return 0;
24684659b24SMichael Zeller }
24784659b24SMichael Zeller 
24884659b24SMichael Zeller /*
24984659b24SMichael Zeller  * audio_record - records samples from the sound device using
25084659b24SMichael Zeller  * blocking operations.
25184659b24SMichael Zeller  * @aud - the audio player used to capture the samples
25284659b24SMichael Zeller  * @buf - the buffer to receive the samples
25384659b24SMichael Zeller  * @count - the number of bytes to capture in buffer
25484659b24SMichael Zeller  * Returns -1 on error and 0 on success
25584659b24SMichael Zeller  */
25684659b24SMichael Zeller int
audio_record(struct audio * aud,uint8_t * buf,size_t count)25759d65d31SAndy Fiddaman audio_record(struct audio *aud, uint8_t *buf, size_t count)
25884659b24SMichael Zeller {
25959d65d31SAndy Fiddaman 	ssize_t len;
26059d65d31SAndy Fiddaman 	size_t total;
26159d65d31SAndy Fiddaman 	int audio_fd;
26284659b24SMichael Zeller 
26384659b24SMichael Zeller 	assert(aud);
26484659b24SMichael Zeller 	assert(!aud->dir);
26584659b24SMichael Zeller 	assert(buf);
26684659b24SMichael Zeller 
26784659b24SMichael Zeller 	audio_fd = aud->fd;
26884659b24SMichael Zeller 	assert(audio_fd != -1);
26984659b24SMichael Zeller 
27059d65d31SAndy Fiddaman 	for (total = 0; total < count; total += len) {
27184659b24SMichael Zeller 		len = read(audio_fd, buf + total, count - total);
27259d65d31SAndy Fiddaman 		if (len < 0) {
273154972afSPatrick Mooney 			DPRINTF("Fail to write to fd: %d, errno: %d",
27484659b24SMichael Zeller 			    audio_fd, errno);
27584659b24SMichael Zeller 			return -1;
27684659b24SMichael Zeller 		}
27784659b24SMichael Zeller 	}
27884659b24SMichael Zeller 
27984659b24SMichael Zeller 	return 0;
28084659b24SMichael Zeller }
281