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