Annotation of embedaddon/ntp/libntp/audio.c, revision 1.1
1.1 ! misho 1: /*
! 2: * audio.c - audio interface for reference clock audio drivers
! 3: */
! 4: #ifdef HAVE_CONFIG_H
! 5: # include <config.h>
! 6: #endif
! 7:
! 8: #if defined(HAVE_SYS_AUDIOIO_H) || defined(HAVE_SUN_AUDIOIO_H) || \
! 9: defined(HAVE_SYS_SOUNDCARD_H) || defined(HAVE_MACHINE_SOUNDCARD_H)
! 10:
! 11: #include "audio.h"
! 12: #include "ntp_stdlib.h"
! 13: #include "ntp_syslog.h"
! 14: #ifdef HAVE_UNISTD_H
! 15: # include <unistd.h>
! 16: #endif
! 17: #include <stdio.h>
! 18: #include "ntp_string.h"
! 19:
! 20: #ifdef HAVE_SYS_AUDIOIO_H
! 21: # include <sys/audioio.h>
! 22: #endif /* HAVE_SYS_AUDIOIO_H */
! 23:
! 24: #ifdef HAVE_SUN_AUDIOIO_H
! 25: # include <sys/ioccom.h>
! 26: # include <sun/audioio.h>
! 27: #endif /* HAVE_SUN_AUDIOIO_H */
! 28:
! 29: #ifdef HAVE_SYS_IOCTL_H
! 30: # include <sys/ioctl.h>
! 31: #endif /* HAVE_SYS_IOCTL_H */
! 32:
! 33: #include <fcntl.h>
! 34:
! 35: #ifdef HAVE_MACHINE_SOUNDCARD_H
! 36: # include <machine/soundcard.h>
! 37: # define PCM_STYLE_SOUND
! 38: #else
! 39: # ifdef HAVE_SYS_SOUNDCARD_H
! 40: # include <sys/soundcard.h>
! 41: # define PCM_STYLE_SOUND
! 42: # endif
! 43: #endif
! 44:
! 45: #ifdef PCM_STYLE_SOUND
! 46: # include <ctype.h>
! 47: #endif
! 48:
! 49: /*
! 50: * Global variables
! 51: */
! 52: #ifdef HAVE_SYS_AUDIOIO_H
! 53: static struct audio_device device; /* audio device ident */
! 54: #endif /* HAVE_SYS_AUDIOIO_H */
! 55: #ifdef PCM_STYLE_SOUND
! 56: # define INIT_FILE "/etc/ntp.audio"
! 57: int agc = SOUND_MIXER_WRITE_RECLEV; /* or IGAIN or LINE */
! 58: int monitor = SOUND_MIXER_WRITE_VOLUME; /* or OGAIN */
! 59: int devmask = 0;
! 60: int recmask = 0;
! 61: char cf_c_dev[100], cf_i_dev[100], cf_agc[100], cf_monitor[100];
! 62:
! 63: const char *m_names[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
! 64: #else /* not PCM_STYLE_SOUND */
! 65: static struct audio_info info; /* audio device info */
! 66: #endif /* not PCM_STYLE_SOUND */
! 67: static int ctl_fd; /* audio control file descriptor */
! 68:
! 69: #ifdef PCM_STYLE_SOUND
! 70: static void audio_config_read (int, char **, char **);
! 71: static int mixer_name (const char *, int);
! 72:
! 73:
! 74: int
! 75: mixer_name(
! 76: const char *m_name,
! 77: int m_mask
! 78: )
! 79: {
! 80: int i;
! 81:
! 82: for (i = 0; i < SOUND_MIXER_NRDEVICES; ++i)
! 83: if (((1 << i) & m_mask)
! 84: && !strcmp(m_names[i], m_name))
! 85: break;
! 86:
! 87: return (SOUND_MIXER_NRDEVICES == i)
! 88: ? -1
! 89: : i
! 90: ;
! 91: }
! 92:
! 93:
! 94: /*
! 95: * Check:
! 96: *
! 97: * /etc/ntp.audio# where # is the unit number
! 98: * /etc/ntp.audio.# where # is the unit number
! 99: * /etc/ntp.audio
! 100: *
! 101: * for contents of the form:
! 102: *
! 103: * idev /dev/input_device
! 104: * cdev /dev/control_device
! 105: * agc pcm_input_device {igain,line,line1,...}
! 106: * monitor pcm_monitor_device {ogain,...}
! 107: *
! 108: * The device names for the "agc" and "monitor" keywords
! 109: * can be found by running either the "mixer" program or the
! 110: * util/audio-pcm program.
! 111: *
! 112: * Great hunks of this subroutine were swiped from refclock_oncore.c
! 113: */
! 114: static void
! 115: audio_config_read(
! 116: int unit,
! 117: char **c_dev, /* Control device */
! 118: char **i_dev /* input device */
! 119: )
! 120: {
! 121: FILE *fd;
! 122: char device[20], line[100], ab[100];
! 123:
! 124: snprintf(device, sizeof(device), "%s%d", INIT_FILE, unit);
! 125: if ((fd = fopen(device, "r")) == NULL) {
! 126: printf("audio_config_read: <%s> NO\n", device);
! 127: snprintf(device, sizeof(device), "%s.%d", INIT_FILE,
! 128: unit);
! 129: if ((fd = fopen(device, "r")) == NULL) {
! 130: printf("audio_config_read: <%s> NO\n", device);
! 131: snprintf(device, sizeof(device), "%s",
! 132: INIT_FILE);
! 133: if ((fd = fopen(device, "r")) == NULL) {
! 134: printf("audio_config_read: <%s> NO\n",
! 135: device);
! 136: return;
! 137: }
! 138: }
! 139: }
! 140: printf("audio_config_read: reading <%s>\n", device);
! 141: while (fgets(line, sizeof line, fd)) {
! 142: char *cp, *cc, *ca;
! 143: int i;
! 144:
! 145: /* Remove comments */
! 146: if ((cp = strchr(line, '#')))
! 147: *cp = '\0';
! 148:
! 149: /* Remove any trailing spaces */
! 150: for (i = strlen(line);
! 151: i > 0 && isascii((int)line[i - 1]) && isspace((int)line[i - 1]);
! 152: )
! 153: line[--i] = '\0';
! 154:
! 155: /* Remove leading space */
! 156: for (cc = line; *cc && isascii((int)*cc) && isspace((int)*cc); cc++)
! 157: continue;
! 158:
! 159: /* Stop if nothing left */
! 160: if (!*cc)
! 161: continue;
! 162:
! 163: /* Uppercase the command and find the arg */
! 164: for (ca = cc; *ca; ca++) {
! 165: if (isascii((int)*ca)) {
! 166: if (islower((int)*ca)) {
! 167: *ca = toupper(*ca);
! 168: } else if (isspace((int)*ca) || (*ca == '='))
! 169: break;
! 170: }
! 171: }
! 172:
! 173: /* Remove space (and possible =) leading the arg */
! 174: for (; *ca && isascii((int)*ca) && (isspace((int)*ca) || (*ca == '=')); ca++)
! 175: continue;
! 176:
! 177: if (!strncmp(cc, "IDEV", 4) &&
! 178: 1 == sscanf(ca, "%99s", ab)) {
! 179: strncpy(cf_i_dev, ab, sizeof(cf_i_dev));
! 180: printf("idev <%s>\n", ab);
! 181: } else if (!strncmp(cc, "CDEV", 4) &&
! 182: 1 == sscanf(ca, "%99s", ab)) {
! 183: strncpy(cf_c_dev, ab, sizeof(cf_c_dev));
! 184: printf("cdev <%s>\n", ab);
! 185: } else if (!strncmp(cc, "AGC", 3) &&
! 186: 1 == sscanf(ca, "%99s", ab)) {
! 187: strncpy(cf_agc, ab, sizeof(cf_agc));
! 188: printf("agc <%s> %d\n", ab, i);
! 189: } else if (!strncmp(cc, "MONITOR", 7) &&
! 190: 1 == sscanf(ca, "%99s", ab)) {
! 191: strncpy(cf_monitor, ab, sizeof(cf_monitor));
! 192: printf("monitor <%s> %d\n", ab, mixer_name(ab, -1));
! 193: }
! 194: }
! 195: fclose(fd);
! 196: return;
! 197: }
! 198: #endif /* PCM_STYLE_SOUND */
! 199:
! 200: /*
! 201: * audio_init - open and initialize audio device
! 202: *
! 203: * This code works with SunOS 4.x, Solaris 2.x, and PCM; however, it is
! 204: * believed generic and applicable to other systems with a minor twid
! 205: * or two. All it does is open the device, set the buffer size (Solaris
! 206: * only), preset the gain and set the input port. It assumes that the
! 207: * codec sample rate (8000 Hz), precision (8 bits), number of channels
! 208: * (1) and encoding (ITU-T G.711 mu-law companded) have been set by
! 209: * default.
! 210: */
! 211: int
! 212: audio_init(
! 213: char *dname, /* device name */
! 214: int bufsiz, /* buffer size */
! 215: int unit /* device unit (0-3) */
! 216: )
! 217: {
! 218: #ifdef PCM_STYLE_SOUND
! 219: # define ACTL_DEV "/dev/mixer%d"
! 220: char actl_dev[30];
! 221: # ifdef HAVE_STRUCT_SND_SIZE
! 222: struct snd_size s_size;
! 223: # endif
! 224: # ifdef AIOGFMT
! 225: snd_chan_param s_c_p;
! 226: # endif
! 227: #endif
! 228: int fd;
! 229: int rval;
! 230: char *actl =
! 231: #ifdef PCM_STYLE_SOUND
! 232: actl_dev
! 233: #else
! 234: "/dev/audioctl"
! 235: #endif
! 236: ;
! 237:
! 238: #ifdef PCM_STYLE_SOUND
! 239: snprintf(actl_dev, sizeof(actl_dev), ACTL_DEV, unit);
! 240:
! 241: audio_config_read(unit, &actl, &dname);
! 242: /* If we have values for cf_c_dev or cf_i_dev, use them. */
! 243: if (*cf_c_dev)
! 244: actl = cf_c_dev;
! 245: if (*cf_i_dev)
! 246: dname = cf_i_dev;
! 247: #endif
! 248:
! 249: /*
! 250: * Open audio device
! 251: */
! 252: fd = open(dname, O_RDWR | O_NONBLOCK, 0777);
! 253: if (fd < 0) {
! 254: msyslog(LOG_ERR, "audio_init: %s %m\n", dname);
! 255: return (fd);
! 256: }
! 257:
! 258: /*
! 259: * Open audio control device.
! 260: */
! 261: ctl_fd = open(actl, O_RDWR);
! 262: if (ctl_fd < 0) {
! 263: msyslog(LOG_ERR, "audio_init: invalid control device <%s>\n",
! 264: actl);
! 265: close(fd);
! 266: return(ctl_fd);
! 267: }
! 268:
! 269: /*
! 270: * Set audio device parameters.
! 271: */
! 272: #ifdef PCM_STYLE_SOUND
! 273: printf("audio_init: <%s> bufsiz %d\n", dname, bufsiz);
! 274: rval = fd;
! 275:
! 276: # ifdef HAVE_STRUCT_SND_SIZE
! 277: if (ioctl(fd, AIOGSIZE, &s_size) == -1)
! 278: printf("audio_init: AIOGSIZE: %s\n", strerror(errno));
! 279: else
! 280: printf("audio_init: orig: play_size %d, rec_size %d\n",
! 281: s_size.play_size, s_size.rec_size);
! 282:
! 283: s_size.play_size = s_size.rec_size = bufsiz;
! 284: printf("audio_init: want: play_size %d, rec_size %d\n",
! 285: s_size.play_size, s_size.rec_size);
! 286:
! 287: if (ioctl(fd, AIOSSIZE, &s_size) == -1)
! 288: printf("audio_init: AIOSSIZE: %s\n", strerror(errno));
! 289: else
! 290: printf("audio_init: set: play_size %d, rec_size %d\n",
! 291: s_size.play_size, s_size.rec_size);
! 292: # endif /* HAVE_STRUCT_SND_SIZE */
! 293:
! 294: # ifdef SNDCTL_DSP_SETFRAGMENT
! 295: {
! 296: int tmp = (16 << 16) + 6; /* 16 fragments, each 2^6 bytes */
! 297: if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &tmp) == -1)
! 298: printf("audio_init: SNDCTL_DSP_SETFRAGMENT: %s\n",
! 299: strerror(errno));
! 300: }
! 301: # endif /* SNDCTL_DSP_SETFRAGMENT */
! 302:
! 303: # ifdef AIOGFMT
! 304: if (ioctl(fd, AIOGFMT, &s_c_p) == -1)
! 305: printf("audio_init: AIOGFMT: %s\n", strerror(errno));
! 306: else
! 307: printf("audio_init: play_rate %lu, rec_rate %lu, play_format %#lx, rec_format %#lx\n",
! 308: s_c_p.play_rate, s_c_p.rec_rate, s_c_p.play_format, s_c_p.rec_format);
! 309: # endif
! 310:
! 311: /* Grab the device and record masks */
! 312:
! 313: if (ioctl(ctl_fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1)
! 314: printf("SOUND_MIXER_READ_DEVMASK: %s\n", strerror(errno));
! 315: if (ioctl(ctl_fd, SOUND_MIXER_READ_RECMASK, &recmask) == -1)
! 316: printf("SOUND_MIXER_READ_RECMASK: %s\n", strerror(errno));
! 317:
! 318: /* validate and set any specified config file stuff */
! 319: if (cf_agc[0] != '\0') {
! 320: int i;
! 321:
! 322: i = mixer_name(cf_agc, devmask);
! 323: if (i >= 0)
! 324: agc = MIXER_WRITE(i);
! 325: else
! 326: printf("input %s not in recmask %#x\n",
! 327: cf_agc, recmask);
! 328: }
! 329:
! 330: if (cf_monitor[0] != '\0') {
! 331: int i;
! 332:
! 333: /* devmask */
! 334: i = mixer_name(cf_monitor, devmask);
! 335: if (i >= 0)
! 336: monitor = MIXER_WRITE(i);
! 337: else
! 338: printf("monitor %s not in devmask %#x\n",
! 339: cf_monitor, devmask);
! 340: }
! 341:
! 342: #else /* not PCM_STYLE_SOUND */
! 343: AUDIO_INITINFO(&info);
! 344: info.play.gain = AUDIO_MAX_GAIN;
! 345: info.play.port = AUDIO_SPEAKER;
! 346: # ifdef HAVE_SYS_AUDIOIO_H
! 347: info.record.buffer_size = bufsiz;
! 348: # endif /* HAVE_SYS_AUDIOIO_H */
! 349: rval = ioctl(ctl_fd, (int)AUDIO_SETINFO, (char *)&info);
! 350: if (rval < 0) {
! 351: msyslog(LOG_ERR, "audio: invalid control device parameters\n");
! 352: close(ctl_fd);
! 353: close(fd);
! 354: return(rval);
! 355: }
! 356: rval = fd;
! 357: #endif /* not PCM_STYLE_SOUND */
! 358: return (rval);
! 359: }
! 360:
! 361:
! 362: /*
! 363: * audio_gain - adjust codec gains and port
! 364: */
! 365: int
! 366: audio_gain(
! 367: int gain, /* volume level (gain) 0-255 */
! 368: int mongain, /* input to output mix (monitor gain) 0-255 */
! 369: int port /* selected I/O port: 1 mic/2 line in */
! 370: )
! 371: {
! 372: int rval;
! 373: static int o_mongain = -1;
! 374: static int o_port = -1;
! 375:
! 376: #ifdef PCM_STYLE_SOUND
! 377: int l, r;
! 378:
! 379: rval = 0;
! 380:
! 381: r = l = 100 * gain / 255; /* Normalize to 0-100 */
! 382: # ifdef DEBUG
! 383: if (debug > 1)
! 384: printf("audio_gain: gain %d/%d\n", gain, l);
! 385: # endif
! 386: #if 0 /* not a good idea to do this; connector wiring dependency */
! 387: /* figure out what channel(s) to use. just nuke right for now. */
! 388: r = 0 ; /* setting to zero nicely mutes the channel */
! 389: #endif
! 390: l |= r << 8;
! 391: if (cf_agc[0] != '\0')
! 392: rval = ioctl(ctl_fd, agc, &l);
! 393: else
! 394: if (2 == port)
! 395: rval = ioctl(ctl_fd, SOUND_MIXER_WRITE_LINE, &l);
! 396: else
! 397: rval = ioctl(ctl_fd, SOUND_MIXER_WRITE_MIC, &l);
! 398: if (-1 == rval) {
! 399: printf("audio_gain: agc write: %s\n", strerror(errno));
! 400: return rval;
! 401: }
! 402:
! 403: if (o_mongain != mongain) {
! 404: r = l = 100 * mongain / 255; /* Normalize to 0-100 */
! 405: # ifdef DEBUG
! 406: if (debug > 1)
! 407: printf("audio_gain: mongain %d/%d\n", mongain, l);
! 408: # endif
! 409: l |= r << 8;
! 410: if (cf_monitor[0] != '\0')
! 411: rval = ioctl(ctl_fd, monitor, &l );
! 412: else
! 413: rval = ioctl(ctl_fd, SOUND_MIXER_WRITE_VOLUME,
! 414: &l);
! 415: if (-1 == rval) {
! 416: printf("audio_gain: mongain write: %s\n",
! 417: strerror(errno));
! 418: return (rval);
! 419: }
! 420: o_mongain = mongain;
! 421: }
! 422:
! 423: if (o_port != port) {
! 424: # ifdef DEBUG
! 425: if (debug > 1)
! 426: printf("audio_gain: port %d\n", port);
! 427: # endif
! 428: l = (1 << ((port == 2) ? SOUND_MIXER_LINE : SOUND_MIXER_MIC));
! 429: rval = ioctl(ctl_fd, SOUND_MIXER_WRITE_RECSRC, &l);
! 430: if (rval == -1) {
! 431: printf("SOUND_MIXER_WRITE_RECSRC: %s\n",
! 432: strerror(errno));
! 433: return (rval);
! 434: }
! 435: # ifdef DEBUG
! 436: if (debug > 1) {
! 437: if (ioctl(ctl_fd, SOUND_MIXER_READ_RECSRC, &l) == -1)
! 438: printf("SOUND_MIXER_WRITE_RECSRC: %s\n",
! 439: strerror(errno));
! 440: else
! 441: printf("audio_gain: recsrc is %d\n", l);
! 442: }
! 443: # endif
! 444: o_port = port;
! 445: }
! 446: #else /* not PCM_STYLE_SOUND */
! 447: ioctl(ctl_fd, (int)AUDIO_GETINFO, (char *)&info);
! 448: info.record.encoding = AUDIO_ENCODING_ULAW;
! 449: info.record.error = 0;
! 450: info.record.gain = gain;
! 451: if (o_mongain != mongain)
! 452: o_mongain = info.monitor_gain = mongain;
! 453: if (o_port != port)
! 454: o_port = info.record.port = port;
! 455: rval = ioctl(ctl_fd, (int)AUDIO_SETINFO, (char *)&info);
! 456: if (rval < 0) {
! 457: msyslog(LOG_ERR, "audio_gain: %m");
! 458: return (rval);
! 459: }
! 460: rval = info.record.error;
! 461: #endif /* not PCM_STYLE_SOUND */
! 462: return (rval);
! 463: }
! 464:
! 465:
! 466: /*
! 467: * audio_show - display audio parameters
! 468: *
! 469: * This code doesn't really do anything, except satisfy curiousity and
! 470: * verify the ioctl's work.
! 471: */
! 472: void
! 473: audio_show(void)
! 474: {
! 475: #ifdef PCM_STYLE_SOUND
! 476: int recsrc = 0;
! 477:
! 478: printf("audio_show: ctl_fd %d\n", ctl_fd);
! 479: if (ioctl(ctl_fd, SOUND_MIXER_READ_RECSRC, &recsrc) == -1)
! 480: printf("SOUND_MIXER_READ_RECSRC: %s\n", strerror(errno));
! 481:
! 482: #else /* not PCM_STYLE_SOUND */
! 483: # ifdef HAVE_SYS_AUDIOIO_H
! 484: ioctl(ctl_fd, (int)AUDIO_GETDEV, &device);
! 485: printf("audio: name %s, version %s, config %s\n",
! 486: device.name, device.version, device.config);
! 487: # endif /* HAVE_SYS_AUDIOIO_H */
! 488: ioctl(ctl_fd, (int)AUDIO_GETINFO, (char *)&info);
! 489: printf(
! 490: "audio: rate %d, chan %d, prec %d, code %d, gain %d, mon %d, port %d\n",
! 491: info.record.sample_rate, info.record.channels,
! 492: info.record.precision, info.record.encoding,
! 493: info.record.gain, info.monitor_gain, info.record.port);
! 494: printf(
! 495: "audio: samples %d, eof %d, pause %d, error %d, waiting %d, balance %d\n",
! 496: info.record.samples, info.record.eof,
! 497: info.record.pause, info.record.error,
! 498: info.record.waiting, info.record.balance);
! 499: #endif /* not PCM_STYLE_SOUND */
! 500: }
! 501: #else
! 502: int audio_bs;
! 503: #endif /* HAVE_{SYS_AUDIOIO,SUN_AUDIOIO,MACHINE_SOUNDCARD,SYS_SOUNDCARD}_H */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>