Home | History | Annotate | Line # | Download | only in audiocfg
audiodev.c revision 1.13
      1 /* $NetBSD: audiodev.c,v 1.13 2019/08/24 06:16:27 isaki Exp $ */
      2 
      3 /*
      4  * Copyright (c) 2010 Jared D. McNeill <jmcneill (at) invisible.ca>
      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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/queue.h>
     30 #include <sys/ioctl.h>
     31 #include <sys/stat.h>
     32 #include <sys/drvctlio.h>
     33 
     34 #include <err.h>
     35 #include <errno.h>
     36 #include <fcntl.h>
     37 #include <paths.h>
     38 #include <stdio.h>
     39 #include <stdlib.h>
     40 #include <string.h>
     41 #include <unistd.h>
     42 
     43 #include "audiodev.h"
     44 #include "drvctl.h"
     45 #include "dtmf.h"
     46 
     47 static int audiodev_test_chmask(struct audiodev *, unsigned int,
     48 	audio_info_t *);
     49 
     50 static TAILQ_HEAD(audiodevhead, audiodev) audiodevlist =
     51     TAILQ_HEAD_INITIALIZER(audiodevlist);
     52 static unsigned int maxunit;
     53 
     54 #define AUDIODEV_SAMPLE_RATE	44100
     55 
     56 static int
     57 audiodev_getinfo(struct audiodev *adev)
     58 {
     59 	struct stat st;
     60 	struct audiofmt *f;
     61 	audio_format_query_t query;
     62 	int i;
     63 
     64 	if (stat(adev->ctlpath, &st) == -1)
     65 		return -1;
     66 	adev->dev = st.st_rdev;
     67 
     68 	if (stat(_PATH_AUDIOCTL, &st) != -1 && st.st_rdev == adev->dev)
     69 		adev->defaultdev = true;
     70 
     71 	adev->ctlfd = open(adev->ctlpath, O_RDONLY);
     72 	if (adev->ctlfd == -1) {
     73 			return -1;
     74 	}
     75 	if (ioctl(adev->ctlfd, AUDIO_GETDEV, &adev->audio_device) == -1) {
     76 		close(adev->ctlfd);
     77 		return -1;
     78 	}
     79 
     80 	for (i = 0; ;i++) {
     81 		memset(&query, 0, sizeof(query));
     82 		query.index = i;
     83 		if (ioctl(adev->ctlfd, AUDIO_QUERYFORMAT, &query) == -1) {
     84 			if (errno == ENODEV) {
     85 				/* QUERYFORMAT not supported. */
     86 				break;
     87 			}
     88 			if (errno == EINVAL)
     89 				break;
     90 			close(adev->ctlfd);
     91 			return -1;
     92 		}
     93 
     94 		f = calloc(1, sizeof(*f));
     95 		f->fmt = query.fmt;
     96 		TAILQ_INSERT_TAIL(&adev->formats, f, next);
     97 	}
     98 
     99 	if (ioctl(adev->ctlfd, AUDIO_GETFORMAT, &adev->hwinfo) == -1) {
    100 		close(adev->ctlfd);
    101 		return -1;
    102 	}
    103 
    104 	return 0;
    105 }
    106 
    107 static int
    108 audiodev_add(const char *pdev, const char *dev, unsigned int unit)
    109 {
    110 	struct audiodev *adev;
    111 
    112 	adev = calloc(1, sizeof(*adev));
    113 	if (adev == NULL)
    114 		return -1;
    115 
    116 	strlcpy(adev->pxname, pdev, sizeof(adev->pxname));
    117 	strlcpy(adev->xname, dev, sizeof(adev->xname));
    118 	snprintf(adev->path, sizeof(adev->path), "/dev/%s", dev);
    119 	snprintf(adev->ctlpath, sizeof(adev->ctlpath), "/dev/audioctl%d", unit);
    120 	adev->unit = unit;
    121 	TAILQ_INIT(&adev->formats);
    122 
    123 	if (audiodev_getinfo(adev) == -1) {
    124 		free(adev);
    125 		return -1;
    126 	}
    127 
    128 #ifdef DEBUG
    129 	printf("DEBUG: [%c] %s(%s): %s\n", adev->defaultdev ? '*' : ' ',
    130 	    adev->path, adev->ctlpath, adev->audio_device.name);
    131 	struct audiofmt *f;
    132 	TAILQ_FOREACH(f, &adev->formats, next) {
    133 		printf("DEBUG: enc%d, %d/%d, %dch\n",
    134 		    f->fmt.encoding,
    135 		    f->fmt.validbits,
    136 		    f->fmt.precision,
    137 		    f->fmt.channels);
    138 	}
    139 #endif
    140 
    141 	TAILQ_INSERT_TAIL(&audiodevlist, adev, next);
    142 
    143 	if (unit > maxunit)
    144 		maxunit = unit;
    145 
    146 	return 0;
    147 }
    148 
    149 static void
    150 audiodev_cb(void *args, const char *pdev, const char *dev, unsigned int unit)
    151 {
    152 	audiodev_add(pdev, dev, unit);
    153 }
    154 
    155 int
    156 audiodev_refresh(void)
    157 {
    158 	struct audiodev *adev;
    159 	int fd, error;
    160 
    161 	fd = open(DRVCTLDEV, O_RDONLY);
    162 	if (fd == -1) {
    163 		warn("open %s", DRVCTLDEV);
    164 		return -1;
    165 	}
    166 
    167 	while (!TAILQ_EMPTY(&audiodevlist)) {
    168 		adev = TAILQ_FIRST(&audiodevlist);
    169 		if (adev->ctlfd != -1)
    170 			close(adev->ctlfd);
    171 		TAILQ_REMOVE(&audiodevlist, adev, next);
    172 		free(adev);
    173 	}
    174 
    175 	error = drvctl_foreach(fd, "audio", audiodev_cb, NULL);
    176 	if (error == -1) {
    177 		warnx("drvctl failed");
    178 		return -1;
    179 	}
    180 
    181 	close(fd);
    182 
    183 	return 0;
    184 }
    185 
    186 unsigned int
    187 audiodev_maxunit(void)
    188 {
    189 	return maxunit;
    190 }
    191 
    192 /*
    193  * Get audiodev corresponding to audio<i> device.
    194  */
    195 struct audiodev *
    196 audiodev_get(unsigned int i)
    197 {
    198 	struct audiodev *adev;
    199 
    200 	TAILQ_FOREACH(adev, &audiodevlist, next) {
    201 		if (i == adev->unit)
    202 			return adev;
    203 	}
    204 
    205 	return NULL;
    206 }
    207 
    208 int
    209 audiodev_set_default(struct audiodev *adev)
    210 {
    211 	char audiopath[PATH_MAX+1];
    212 	char soundpath[PATH_MAX+1];
    213 	char audioctlpath[PATH_MAX+1];
    214 	char mixerpath[PATH_MAX+1];
    215 
    216 	snprintf(audiopath, sizeof(audiopath),
    217 	    _PATH_AUDIO "%u", adev->unit);
    218 	snprintf(soundpath, sizeof(soundpath),
    219 	    _PATH_SOUND "%u", adev->unit);
    220 	snprintf(audioctlpath, sizeof(audioctlpath),
    221 	    _PATH_AUDIOCTL "%u", adev->unit);
    222 	snprintf(mixerpath, sizeof(mixerpath),
    223 	    _PATH_MIXER "%u", adev->unit);
    224 
    225 	unlink(_PATH_AUDIO);
    226 	unlink(_PATH_SOUND);
    227 	unlink(_PATH_AUDIOCTL);
    228 	unlink(_PATH_MIXER);
    229 
    230 	if (symlink(audiopath, _PATH_AUDIO) == -1) {
    231 		warn("symlink %s", _PATH_AUDIO);
    232 		return -1;
    233 	}
    234 	if (symlink(soundpath, _PATH_SOUND) == -1) {
    235 		warn("symlink %s", _PATH_SOUND);
    236 		return -1;
    237 	}
    238 	if (symlink(audioctlpath, _PATH_AUDIOCTL) == -1) {
    239 		warn("symlink %s", _PATH_AUDIOCTL);
    240 		return -1;
    241 	}
    242 	if (symlink(mixerpath, _PATH_MIXER) == -1) {
    243 		warn("symlink %s", _PATH_MIXER);
    244 		return -1;
    245 	}
    246 
    247 	return 0;
    248 }
    249 
    250 int
    251 audiodev_set_param(struct audiodev *adev, int mode,
    252 	const char *encname, unsigned int prec, unsigned int ch, unsigned int freq)
    253 {
    254 	audio_info_t ai;
    255 	int setmode;
    256 	u_int enc;
    257 
    258 	setmode = 0;
    259 	ai = adev->hwinfo;
    260 
    261 	for (enc = 0; enc < encoding_max; enc++) {
    262 		if (strcmp(encname, encoding_names[enc]) == 0)
    263 			break;
    264 	}
    265 	if (enc >= encoding_max) {
    266 		warnx("unknown encoding name: %s", encname);
    267 		return -1;
    268 	}
    269 
    270 	if ((ai.mode & mode & AUMODE_PLAY)) {
    271 		setmode |= AUMODE_PLAY;
    272 		ai.play.encoding = enc;
    273 		ai.play.precision = prec;
    274 		ai.play.channels = ch;
    275 		ai.play.sample_rate = freq;
    276 	}
    277 	if ((ai.mode & mode & AUMODE_RECORD)) {
    278 		setmode |= AUMODE_RECORD;
    279 		ai.record.encoding = enc;
    280 		ai.record.precision = prec;
    281 		ai.record.channels = ch;
    282 		ai.record.sample_rate = freq;
    283 	}
    284 
    285 	ai.mode = setmode;
    286 	printf("setting %s to %s:%u, %uch, %uHz\n",
    287 	    adev->xname, encname, prec, ch, freq);
    288 	if (ioctl(adev->ctlfd, AUDIO_SETFORMAT, &ai) == -1) {
    289 		warn("ioctl AUDIO_SETFORMAT");
    290 		return -1;
    291 	}
    292 	return 0;
    293 }
    294 
    295 int
    296 audiodev_test(struct audiodev *adev)
    297 {
    298 	audio_info_t info;
    299 	unsigned int i;
    300 	int rv;
    301 
    302 	rv = -1;
    303 
    304 	adev->fd = open(adev->path, O_WRONLY);
    305 	if (adev->fd == -1) {
    306 		warn("open %s", adev->path);
    307 		return -1;
    308 	}
    309 
    310 	AUDIO_INITINFO(&info);
    311 	info.play.sample_rate = AUDIODEV_SAMPLE_RATE;
    312 	info.play.channels = adev->hwinfo.play.channels;
    313 	info.play.precision = 16;
    314 	info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
    315 	info.mode = AUMODE_PLAY;
    316 	if (ioctl(adev->fd, AUDIO_SETINFO, &info) == -1) {
    317 		warn("ioctl AUDIO_SETINFO");
    318 		goto done;
    319 	}
    320 	if (ioctl(adev->fd, AUDIO_GETBUFINFO, &info) == -1) {
    321 		warn("ioctl AUDIO_GETBUFINFO");
    322 		goto done;
    323 	}
    324 
    325 	for (i = 0; i < adev->hwinfo.play.channels; i++) {
    326 		printf("  testing channel %u...", i);
    327 		fflush(stdout);
    328 		if (audiodev_test_chmask(adev, 1 << i, &info) == -1)
    329 			goto done;
    330 		printf(" done\n");
    331 	}
    332 
    333 	rv = 0;
    334 done:
    335 	close(adev->fd);
    336 	return rv;
    337 }
    338 
    339 static int
    340 audiodev_test_chmask(struct audiodev *adev, unsigned int chanmask,
    341 	audio_info_t *info)
    342 {
    343 	int16_t *buf;
    344 	size_t buflen;
    345 	off_t off;
    346 	int rv;
    347 
    348 	rv = -1;
    349 
    350 	dtmf_new(&buf, &buflen, info->play.sample_rate, 2,
    351 	    adev->hwinfo.play.channels, chanmask, 350.0, 440.0);
    352 	if (buf == NULL) {
    353 		return -1;
    354 	}
    355 
    356 	off = 0;
    357 	while (buflen > 0) {
    358 		size_t wlen;
    359 		ssize_t ret;
    360 
    361 		wlen = info->play.buffer_size;
    362 		if (wlen > buflen)
    363 			wlen = buflen;
    364 		ret = write(adev->fd, (char *)buf + off, wlen);
    365 		if (ret == -1) {
    366 			warn("write");
    367 			goto done;
    368 		}
    369 		wlen = ret;
    370 		off += wlen;
    371 		buflen -= wlen;
    372 	}
    373 
    374 	if (ioctl(adev->fd, AUDIO_DRAIN) == -1) {
    375 		warn("ioctl AUDIO_DRAIN");
    376 		goto done;
    377 	}
    378 
    379 	rv = 0;
    380 done:
    381 	free(buf);
    382 	return rv;
    383 }
    384