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