Home | History | Annotate | Line # | Download | only in audiocfg
audiodev.c revision 1.4
      1 /* $NetBSD: audiodev.c,v 1.4 2010/09/03 19:20:37 jmcneill 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 <fcntl.h>
     35 #include <paths.h>
     36 #include <stdio.h>
     37 #include <stdlib.h>
     38 #include <string.h>
     39 #include <unistd.h>
     40 
     41 #include "audiodev.h"
     42 #include "drvctl.h"
     43 #include "dtmf.h"
     44 
     45 static TAILQ_HEAD(audiodevhead, audiodev) audiodevlist =
     46     TAILQ_HEAD_INITIALIZER(audiodevlist);
     47 
     48 #define AUDIODEV_SAMPLE_RATE	44100
     49 
     50 static unsigned int
     51 audiodev_probe_pchans(struct audiodev *adev)
     52 {
     53 	audio_info_t info;
     54 	unsigned int nchans = 0, n;
     55 	int error;
     56 
     57 	AUDIO_INITINFO(&info);
     58 	info.play.sample_rate = AUDIODEV_SAMPLE_RATE;
     59 	info.play.precision = 16;
     60 	info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
     61 	info.play.channels = 1;
     62 	info.mode = AUMODE_PLAY;
     63 	error = ioctl(adev->fd, AUDIO_SETINFO, &info);
     64 	if (error == -1)
     65 		return 0;
     66 	nchans = 1;
     67 
     68 	for (n = 2; n <= 16; n += 2) {
     69 		info.play.channels = n;
     70 		error = ioctl(adev->fd, AUDIO_SETINFO, &info);
     71 		if (error == -1)
     72 			break;
     73 		nchans = info.play.channels;
     74 	}
     75 
     76 	return nchans;
     77 }
     78 
     79 static int
     80 audiodev_getinfo(struct audiodev *adev)
     81 {
     82 	struct stat st;
     83 
     84 	if (stat(adev->path, &st) == -1)
     85 		return -1;
     86 	adev->dev = st.st_rdev;
     87 
     88 	if (stat(_PATH_AUDIO, &st) != -1 && st.st_rdev == adev->dev)
     89 		adev->defaultdev = true;
     90 
     91 	adev->fd = open(adev->path, O_RDWR);
     92 	if (adev->fd == -1)
     93 		return -1;
     94 	if (ioctl(adev->fd, AUDIO_GETDEV, &adev->audio_device) == -1) {
     95 		close(adev->fd);
     96 		return -1;
     97 	}
     98 
     99 	adev->pchan = audiodev_probe_pchans(adev);
    100 
    101 	return 0;
    102 }
    103 
    104 static int
    105 audiodev_add(const char *pdev, const char *dev, unsigned int unit)
    106 {
    107 	struct audiodev *adev;
    108 
    109 	adev = calloc(1, sizeof(*adev));
    110 	if (adev == NULL)
    111 		return -1;
    112 
    113 	strlcpy(adev->pxname, pdev, sizeof(adev->pxname));
    114 	strlcpy(adev->xname, dev, sizeof(adev->xname));
    115 	snprintf(adev->path, sizeof(adev->path) - 1, "/dev/%s", dev);
    116 	adev->unit = unit;
    117 
    118 	if (audiodev_getinfo(adev) == -1) {
    119 		free(adev);
    120 		return -1;
    121 	}
    122 
    123 #ifdef DEBUG
    124 	printf("[%c] %s: %s\n", adev->defaultdev ? '*' : ' ',
    125 	    adev->path, adev->audio_device.name);
    126 #endif
    127 
    128 	TAILQ_INSERT_TAIL(&audiodevlist, adev, next);
    129 
    130 	return 0;
    131 }
    132 
    133 static void
    134 audiodev_cb(void *args, const char *pdev, const char *dev, unsigned int unit)
    135 {
    136 	audiodev_add(pdev, dev, unit);
    137 }
    138 
    139 int
    140 audiodev_refresh(void)
    141 {
    142 	struct audiodev *adev;
    143 	int fd, error;
    144 
    145 	fd = open(DRVCTLDEV, O_RDONLY);
    146 	if (fd == -1) {
    147 		perror("open " DRVCTLDEV);
    148 		return -1;
    149 	}
    150 
    151 	while (!TAILQ_EMPTY(&audiodevlist)) {
    152 		adev = TAILQ_FIRST(&audiodevlist);
    153 		if (adev->fd != -1)
    154 			close(adev->fd);
    155 		TAILQ_REMOVE(&audiodevlist, adev, next);
    156 		free(adev);
    157 	}
    158 
    159 	error = drvctl_foreach(fd, "audio", audiodev_cb, NULL);
    160 	if (error == -1) {
    161 		perror("drvctl");
    162 		return -1;
    163 	}
    164 
    165 	close(fd);
    166 
    167 	return 0;
    168 }
    169 
    170 unsigned int
    171 audiodev_count(void)
    172 {
    173 	struct audiodev *adev;
    174 	unsigned int n;
    175 
    176 	n = 0;
    177 	TAILQ_FOREACH(adev, &audiodevlist, next)
    178 		++n;
    179 
    180 	return n;
    181 }
    182 
    183 struct audiodev *
    184 audiodev_get(unsigned int i)
    185 {
    186 	struct audiodev *adev;
    187 	unsigned int n;
    188 
    189 	n = 0;
    190 	TAILQ_FOREACH(adev, &audiodevlist, next) {
    191 		if (n == i)
    192 			return adev;
    193 		++n;
    194 	}
    195 
    196 	return NULL;
    197 }
    198 
    199 int
    200 audiodev_set_default(struct audiodev *adev)
    201 {
    202 	char audiopath[PATH_MAX+1];
    203 	char soundpath[PATH_MAX+1];
    204 	char audioctlpath[PATH_MAX+1];
    205 	char mixerpath[PATH_MAX+1];
    206 
    207 	snprintf(audiopath, sizeof(audiopath) - 1,
    208 	    _PATH_AUDIO "%u", adev->unit);
    209 	snprintf(soundpath, sizeof(soundpath) - 1,
    210 	    _PATH_SOUND "%u", adev->unit);
    211 	snprintf(audioctlpath, sizeof(audioctlpath) - 1,
    212 	    _PATH_AUDIOCTL "%u", adev->unit);
    213 	snprintf(mixerpath, sizeof(mixerpath) - 1,
    214 	    _PATH_MIXER "%u", adev->unit);
    215 
    216 	unlink(_PATH_AUDIO);
    217 	unlink(_PATH_SOUND);
    218 	unlink(_PATH_AUDIOCTL);
    219 	unlink(_PATH_MIXER);
    220 
    221 	if (symlink(audiopath, _PATH_AUDIO) == -1) {
    222 		perror("symlink " _PATH_AUDIO);
    223 		return -1;
    224 	}
    225 	if (symlink(soundpath, _PATH_SOUND) == -1) {
    226 		perror("symlink " _PATH_SOUND);
    227 		return -1;
    228 	}
    229 	if (symlink(audioctlpath, _PATH_AUDIOCTL) == -1) {
    230 		perror("symlink " _PATH_AUDIOCTL);
    231 		return -1;
    232 	}
    233 	if (symlink(mixerpath, _PATH_MIXER) == -1) {
    234 		perror("symlink " _PATH_MIXER);
    235 		return -1;
    236 	}
    237 
    238 	return 0;
    239 }
    240 
    241 int
    242 audiodev_test(struct audiodev *adev, unsigned int chanmask)
    243 {
    244 	audio_info_t info;
    245 	int16_t *buf;
    246 	size_t buflen;
    247 	off_t off;
    248 	int rv = 0;
    249 
    250 	AUDIO_INITINFO(&info);
    251 	info.play.sample_rate = AUDIODEV_SAMPLE_RATE;
    252 	info.play.channels = adev->pchan;
    253 	info.play.precision = 16;
    254 	info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
    255 	info.mode = AUMODE_PLAY;
    256 	if (ioctl(adev->fd, AUDIO_SETINFO, &info) == -1) {
    257 		perror("ioctl AUDIO_SETINFO");
    258 		return -1;
    259 	}
    260 	if (ioctl(adev->fd, AUDIO_GETINFO, &info) == -1) {
    261 		perror("ioctl AUDIO_GETINFO");
    262 		return -1;
    263 	}
    264 
    265 	dtmf_new(&buf, &buflen, info.play.sample_rate, 2,
    266 	    adev->pchan, chanmask, 350.0, 440.0);
    267 	if (buf == NULL)
    268 		return -1;
    269 
    270 	off = 0;
    271 	while (buflen > 0) {
    272 		size_t wlen = info.play.buffer_size;
    273 		if (wlen > buflen)
    274 			wlen = buflen;
    275 		wlen = write(adev->fd, (char *)buf + off, wlen);
    276 		if (wlen == -1) {
    277 			perror("write");
    278 			rv = -1;
    279 			goto done;
    280 		}
    281 		off += wlen;
    282 		buflen -= wlen;
    283 	}
    284 
    285 	if (ioctl(adev->fd, AUDIO_DRAIN) == -1)
    286 		perror("ioctl AUDIO_DRAIN");
    287 
    288 done:
    289 	free(buf);
    290 
    291 	return rv;
    292 }
    293