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>