1 /* Unified NAS dda for Sun Sparc architecture
2 
3 Copyright 1995, 1997, 1998, 2000 Charles Levert
4 
5 Permission is hereby granted, free of charge, to any person obtaining
6 a copy of this software and associated documentation files (the
7 "Software"), to deal in the Software without restriction, including
8 without limitation the rights to use, copy, modify, merge, publish,
9 distribute, sublicense, and/or sell copies of the Software, and to
10 permit persons to whom the Software is furnished to do so, subject to
11 the following conditions:
12 
13 The above copyright notice and this permission notice shall be included
14 in all copies or substantial portions of the Software.
15 
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 OTHER DEALINGS IN THE SOFTWARE.
23 
24 Except as contained in this notice, the name of the copyright holder shall
25 not be used in advertising or otherwise to promote the sale, use or
26 other dealings in this Software without prior written authorization
27 from the copyright holder.
28 
29  $Id: ausuni.c 288 2013-08-11 21:28:33Z auerswald $
30 
31 
32  HISTORY:
33 
34  John Wehle 12/10/2000:
35         * ausuni.c: (errno.h): Include.
36                     (devAudioMode): New static variable.
37                     (openDevice): If open for O_RDWR fails
38                                   then retry open using O_WRONLY.
39                     (AuInitPhysicalDevices): Likewise.
40                     (writeOutput): Check devAudioMode and
41                                    reopen device if necessary.
42                     (readPhysicalInputs): Likewise.
43 
44 
45 
46 
47 */
48 
49 /**
50  *
51  * Copyright 1993 Network Computing Devices, Inc.
52  *
53  * Permission to use, copy, modify, distribute, and sell this software and its
54  * documentation for any purpose is hereby granted without fee, provided that
55  * the above copyright notice appear in all copies and that both that
56  * copyright notice and this permission notice appear in supporting
57  * documentation, and that the name Network Computing Devices, Inc. not be
58  * used in advertising or publicity pertaining to distribution of this
59  * software without specific, written prior permission.
60  *
61  * THIS SOFTWARE IS PROVIDED `AS-IS'.  NETWORK COMPUTING DEVICES, INC.,
62  * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT
63  * LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
64  * PARTICULAR PURPOSE, OR NONINFRINGEMENT.  IN NO EVENT SHALL NETWORK
65  * COMPUTING DEVICES, INC., BE LIABLE FOR ANY DAMAGES WHATSOEVER, INCLUDING
66  * SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, INCLUDING LOSS OF USE, DATA,
67  * OR PROFITS, EVEN IF ADVISED OF THE POSSIBILITY THEREOF, AND REGARDLESS OF
68  * WHETHER IN AN ACTION IN CONTRACT, TORT OR NEGLIGENCE, ARISING OUT OF OR IN
69  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
70  *
71  * $NCDId$
72  */
73 
74 #define _AUSUN_C_ 1
75 
76 
77 #include <stdio.h>              /* for sprintf */
78 #include <stdlib.h>             /* for getenv */
79 #include <errno.h>
80 #include "misc.h"
81 #include "dixstruct.h"          /* for RESTYPE */
82 #include "os.h"                 /* for xalloc/xfree and NULL */
83 #include "nasconf.h"
84 #include <fcntl.h>
85 #include <stropts.h>
86 #include <string.h>             /* for strcmp */
87 #ifndef SVR4
88 #include <sun/audioio.h>
89 #else /* SVR4 */
90 #include <sys/audioio.h>
91 #endif /* SVR4 */
92 #include <audio/audio.h>
93 #include <audio/Aproto.h>
94 #include "au.h"
95 
96 /* These constants are for SS5 and the like (SS4, ultra?) with a
97    CS4231.  With SunOS 4.1.x on a SS5, make sure you have at least
98    4.1.4 + patch 102387-06.  Solaris 2.5 is reported to work.  Can't
99    speak for other CS4231 configurations.  */
100 
101 #ifndef AUDIO_INTERNAL_CD_IN
102 #define AUDIO_INTERNAL_CD_IN 0x04
103 #endif /* not defined AUDIO_INTERNAL_CD_IN */
104 #ifndef SVR4
105 #ifndef AUDIO_DEV_CS4231
106 #define AUDIO_DEV_CS4231 5
107 #endif /* not defined AUDIO_DEV_CS4231 */
108 #endif /* not defined SVR4 */
109 
110 #if defined(SYSV) || defined(SVR4)
111 #define signal sigset
112 #else
113 #define BSD_SIGNALS
114 #endif
115 
116 static char name_am79c30[] = "SUNW,am79c30";
117 static char name_CS4231[] = "SUNW,CS4231";
118 static char name_dbri[] = "SUNW,dbri";
119 static char name_unknown[] = "unknown audio device";
120 static char *name_of_physical_device;   /* must point to one of the above */
121 
122 /* pebl: on some solaris (for example sun rays) have pseudo audio devices as
123    there are used for multiply users. The pseudo device is create by 'utadem'
124    in /tmp/SUNWut/dev/utaudio/<literal>. It seems that sun uses/encourage to
125    use the env AUDIODEV to point to the pseudo devide, so it should be checked
126    before trying the default devices. If there is a pseodu device the ctl is
127    /tmp/SUNWut/dev/utaudio/<literal>ctl.
128    http://www.sun.com/desktop/products/software/sunforum/sunforumnotes.html*/
129 
130 static const char *const default_device = "/dev/audio";
131 
132 
133 extern int errno;
134 
135 #ifndef SVR4
136 typedef int audio_device_t;
137 #define IS_AMD(_t)              ((_t) == AUDIO_DEV_AMD)
138 #define IS_CS4231(_t)           ((_t) == AUDIO_DEV_CS4231)
139 #define IS_DBRI(_t) \
140     ((_t) == AUDIO_DEV_SPEAKERBOX || (_t) == AUDIO_DEV_CODEC)
141 #else /* defined SVR4 */
142 #define IS_AMD(_t)      (!strcmp((_t).name, name_am79c30))
143 #define IS_CS4231(_t)   (!strcmp((_t).name, name_CS4231))
144 #define IS_DBRI(_t)     (!strcmp((_t).name, name_dbri))
145 #endif /* defined SVR4 */
146 
147 /**********************************************************************/
148 
149 /* Local compilation options. */
150 #define ADD_OUTPUTS
151 #undef DEBUGLOG
152 #define REVERSE_WRITE
153 #undef DELAYED_TRIGGER
154 #define WRITE_EMPTY_ENABLE
155 #undef SEPARATE_CTLS
156 #define SYNC_PROCESS_ENABLED
157 
158 /**********************************************************************/
159 
160 extern void AuNativeToULAW8();
161 extern void AuULAW8ToNative();
162 extern void AuProcessData();
163 static AuFixedPoint getPhysicalFeedbackGain();
164 static AuFixedPoint getPhysicalInputGain();
165 static AuUint8 getPhysicalInputMode();
166 
167 /**********************************************************************/
168 
169 static AuBool is_cs4231_or_dbri;
170 static AuBool relinquish_device = 0;
171 static int devAudio = -1, devAudioCtl = -1,
172 #ifdef SEPARATE_CTLS
173         devAudioCtl2 = -1,
174 #endif
175         devAudioMode = O_RDWR, bufSize;
176 static volatile AuBool signalEnabled = 0;
177 #ifdef DELAYED_TRIGGER
178 static AuBool delayedTrigger = 0;
179 #endif
180 static AuBool pendingTrigger = AuFalse;
181 static AuInt32 chunksPerSignal;
182 static volatile audio_info_t syncAudioInfo;
183 static audio_info_t audioInfo;
184 static AuUint8 *auOutputMono, *auOutputStereo, *auOutputLeft, *auOutputRight, *auInput, /* mono or stereo */
185    *emptyOutput;
186 static volatile AuBool updateSampleRate;
187 #ifdef SYNC_PROCESS_ENABLED
188 static volatile AuBool updateSyncAudioInfo;
189 #endif
190 static AuUint32 sampleRate,
191 #ifndef ADD_OUTPUTS
192     theAverage,
193 #endif
194 
195 
196         *leftSamples,
197         *rightSamples,
198         *monoSamples, *stereoSamples, availInputModes, availOutputModes;
199 
200 extern AuInt32 auMinibufSamples;
201 
202 char *VENDOR_STRING;
203 #define SUN_VENDOR              "Sun unified dda (running on %s)"
204 #define SERVER_CLIENT           0
205 #define MINIBUF_SAMPLES         800
206 #define SIGNAL_RATE             10
207 
208 #define auMinSampleRate         8000
209 static AuUint32 auMaxSampleRate;
210 
211 #define auPhysicalOutputChangableMask \
212         (AuCompDeviceGainMask | AuCompDeviceOutputModeMask)
213 
214 #define auPhysicalOutputValueMask \
215         (AuCompCommonAllMasks | \
216          AuCompDeviceMinSampleRateMask | \
217          AuCompDeviceMaxSampleRateMask | \
218          AuCompDeviceOutputModeMask | \
219          AuCompDeviceGainMask | \
220          AuCompDeviceLocationMask | \
221          AuCompDeviceChildrenMask)
222 
223 #define auPhysicalInputValueMask \
224         (AuCompCommonAllMasks | \
225          AuCompDeviceMinSampleRateMask | \
226          AuCompDeviceMaxSampleRateMask | \
227          AuCompDeviceLocationMask | \
228          AuCompDeviceGainMask)
229 
230 #define auPhysicalFeedbackValueMask \
231         (AuCompCommonAllMasks | \
232          AuCompDeviceGainMask)
233 
234 /**********************************************************************/
235 
236 /* callback */
237 static AuStatus
createServerComponents(auServerDeviceListSize,auServerBucketListSize,auServerRadioListSize,auServerMinRate,auServerMaxRate)238 createServerComponents(auServerDeviceListSize, auServerBucketListSize,
239                        auServerRadioListSize, auServerMinRate,
240                        auServerMaxRate)
241 AuUint32 *auServerDeviceListSize,
242         *auServerBucketListSize,
243         *auServerRadioListSize, *auServerMinRate, *auServerMaxRate;
244 {
245     AuDeviceID stereo, mono, left, right;
246     ComponentPtr d, *p;
247     AuUint8 num;
248     AuUint32 location;
249     extern RESTYPE auComponentType;
250     extern ComponentPtr *auServerDevices,       /* array of devices */
251         auDevices;              /* list of all devices */
252     extern AuUint32 auNumServerDevices; /* number of devices */
253 
254 #ifdef DEBUGLOG
255     fprintf(stderr, "createServerComponents()\n");
256     fflush(stderr);
257 #endif
258 
259     *auServerMinRate = auMinSampleRate;
260     *auServerMaxRate = auMaxSampleRate;
261 
262     auNumServerDevices
263             = *auServerDeviceListSize
264             = *auServerBucketListSize = *auServerRadioListSize = 0;
265 
266     if (is_cs4231_or_dbri) {
267         AU_ALLOC_DEVICE(d, 1, 0);
268         d->id = left = FakeClientID(SERVER_CLIENT);
269         d->changableMask = auPhysicalOutputChangableMask;
270         d->valueMask = auPhysicalOutputValueMask;
271         d->kind = AuComponentKindPhysicalOutput;
272         d->use = AuComponentUseExportMask;
273         d->access = AuAccessExportMask | AuAccessListMask;
274         d->format = auNativeFormat;
275         d->numTracks = 1;
276         d->description.type = AuStringLatin1;
277         d->description.string = "Left Channel Output";
278         d->description.len = strlen(d->description.string);
279         d->minSampleRate = auMinSampleRate;
280         d->maxSampleRate = auMaxSampleRate;
281         d->location = AuDeviceLocationLeftMask;
282         d->numChildren = 0;
283         d->minibuf = auOutputLeft;
284         d->minibufSize = auMinibufSamples * auNativeBytesPerSample *
285                 d->numTracks;
286         d->physicalDeviceMask = PhysicalOutputLeft;
287         leftSamples = &d->minibufSamples;
288         AU_ADD_DEVICE(d);
289 
290         AU_ALLOC_DEVICE(d, 1, 0);
291         d->id = right = FakeClientID(SERVER_CLIENT);
292         d->changableMask = auPhysicalOutputChangableMask;
293         d->valueMask = auPhysicalOutputValueMask;
294         d->kind = AuComponentKindPhysicalOutput;
295         d->use = AuComponentUseExportMask;
296         d->access = AuAccessExportMask | AuAccessListMask;
297         d->format = auNativeFormat;
298         d->numTracks = 1;
299         d->description.type = AuStringLatin1;
300         d->description.string = "Right Channel Output";
301         d->description.len = strlen(d->description.string);
302         d->minSampleRate = auMinSampleRate;
303         d->maxSampleRate = auMaxSampleRate;
304         d->location = AuDeviceLocationRightMask;
305         d->numChildren = 0;
306         d->minibuf = auOutputRight;
307         d->minibufSize = auMinibufSamples * auNativeBytesPerSample *
308                 d->numTracks;
309         d->physicalDeviceMask = PhysicalOutputRight;
310         rightSamples = &d->minibufSamples;
311         AU_ADD_DEVICE(d);
312     }
313 
314     if (is_cs4231_or_dbri) {
315         num = 2;
316         location = AuDeviceLocationLeftMask | AuDeviceLocationRightMask;
317     } else {
318         num = 0;
319         location = AuDeviceLocationCenterMask |
320                 AuDeviceLocationInternalMask;
321     }
322     AU_ALLOC_DEVICE(d, 1, num);
323     d->id = mono = FakeClientID(SERVER_CLIENT);
324     d->changableMask = auPhysicalOutputChangableMask;
325     d->valueMask = auPhysicalOutputValueMask;
326     d->kind = AuComponentKindPhysicalOutput;
327     d->use = AuComponentUseExportMask;
328     d->access = AuAccessExportMask | AuAccessListMask;
329     d->format = auNativeFormat;
330     d->numTracks = 1;
331     d->description.type = AuStringLatin1;
332     d->description.string = "Mono Channel Output";
333     d->description.len = strlen(d->description.string);
334     d->minSampleRate = auMinSampleRate;
335     d->maxSampleRate = auMaxSampleRate;
336     d->location = location;
337     d->numChildren = num;
338     if (is_cs4231_or_dbri) {
339         d->children = (AuID *) ((AuUint8 *) d +
340                                 PAD4(sizeof(ComponentRec)));
341         d->childSwap = (char *) (d->children + d->numChildren);
342         d->children[0] = left;
343         d->children[1] = right;
344     }
345     d->minibuf = auOutputMono;
346     d->minibufSize = auMinibufSamples * auNativeBytesPerSample *
347             d->numTracks;
348     d->physicalDeviceMask = PhysicalOutputMono;
349     monoSamples = &d->minibufSamples;
350     AU_ADD_DEVICE(d);
351 
352     num = is_cs4231_or_dbri ? 2 : 1;
353     AU_ALLOC_DEVICE(d, 2, num);
354     d->id = stereo = FakeClientID(SERVER_CLIENT);
355     d->changableMask = auPhysicalOutputChangableMask;
356     d->valueMask = auPhysicalOutputValueMask;
357     d->kind = AuComponentKindPhysicalOutput;
358     d->use = AuComponentUseExportMask;
359     d->access = AuAccessExportMask | AuAccessListMask;
360     d->format = auNativeFormat;
361     d->numTracks = 2;
362     d->description.type = AuStringLatin1;
363     d->description.string = "Stereo Channel Output";
364     d->description.len = strlen(d->description.string);
365     d->minSampleRate = auMinSampleRate;
366     d->maxSampleRate = auMaxSampleRate;
367     d->location = location;
368     d->numChildren = num;
369     d->children = (AuID *) ((AuUint8 *) d + PAD4(sizeof(ComponentRec)));
370     d->childSwap = (char *) (d->children + num);
371     if (is_cs4231_or_dbri) {
372         d->children[0] = left;
373         d->children[1] = right;
374     } else
375         d->children[0] = mono;
376     d->minibuf = auOutputStereo;
377     d->minibufSize = auMinibufSamples * auNativeBytesPerSample *
378             d->numTracks;
379     d->physicalDeviceMask = PhysicalOutputStereo;
380     stereoSamples = &d->minibufSamples;
381     AU_ADD_DEVICE(d);
382 
383     /* same value; don't recompute */
384     /* num = is_cs4231_or_dbri ? 2 : 1; */
385     AU_ALLOC_DEVICE(d, num, 0);
386     d->id = FakeClientID(SERVER_CLIENT);
387     d->changableMask = AuCompDeviceGainMask;
388     d->valueMask = auPhysicalFeedbackValueMask;
389     d->kind = AuComponentKindPhysicalFeedback;
390     d->use = 0;
391     d->access = AuAccessListMask;
392     d->format = 0;
393     d->numTracks = num;
394     d->description.type = AuStringLatin1;
395     d->description.string = "Feedback";
396     d->description.len = strlen(d->description.string);
397     d->numChildren = 0;
398     d->gain = getPhysicalFeedbackGain();
399     d->lineMode = AuDeviceLineModeNone;
400     d->physicalDeviceMask = (is_cs4231_or_dbri
401                              ? PhysicalFeedbackStereo
402                              : PhysicalFeedbackMono);
403     d->minibuf = NULL;
404     d->minibufSize = 0;
405     AU_ADD_DEVICE(d);
406 
407     /* same value; don't recompute */
408     /* num = is_cs4231_or_dbri ? 2 : 1; */
409     AU_ALLOC_DEVICE(d, num, 0);
410     d->id = FakeClientID(SERVER_CLIENT);
411     if (is_cs4231_or_dbri) {
412         d->changableMask = (AuCompDeviceGainMask |
413                             AuCompDeviceLineModeMask);
414         d->valueMask = (auPhysicalInputValueMask |
415                         AuCompDeviceLineModeMask);
416     } else {
417         d->changableMask = AuCompDeviceGainMask;
418         d->valueMask = auPhysicalInputValueMask;
419     }
420     d->kind = AuComponentKindPhysicalInput;
421     d->use = AuComponentUseImportMask;
422     d->access = AuAccessImportMask | AuAccessListMask;
423     d->format = auNativeFormat;
424     d->numTracks = num;
425     d->description.type = AuStringLatin1;
426     d->description.string = (is_cs4231_or_dbri
427                              ? "Stereo Channel Input"
428                              : "Mono Channel Input");
429     d->description.len = strlen(d->description.string);
430     d->minSampleRate = auMinSampleRate;
431     d->maxSampleRate = auMaxSampleRate;
432     /* External on most machines now */
433     d->location = (AuDeviceLocationRightMask | AuDeviceLocationLeftMask |
434                    AuDeviceLocationExternalMask);
435     d->numChildren = 0;
436     d->gain = getPhysicalInputGain();
437     d->lineMode = getPhysicalInputMode();
438     d->minibuf = auInput;
439     d->minibufSize = auMinibufSamples * auNativeBytesPerSample *
440             d->numTracks;
441     d->physicalDeviceMask = (is_cs4231_or_dbri
442                              ? PhysicalInputStereo : PhysicalInputMono);
443     AU_ADD_DEVICE(d);
444 
445     /* set the array of server devices */
446     if (!(auServerDevices =
447           (ComponentPtr *) aualloc(sizeof(ComponentPtr) *
448                                    auNumServerDevices)))
449         return AuBadAlloc;
450 
451     p = auServerDevices;
452     for (d = auDevices; d; d = d->next)
453         *p++ = d;
454 
455     return AuSuccess;
456 }
457 
458 static AuBool
openDevice(wait)459 openDevice(wait)
460 AuBool wait;
461 {
462     audio_info_t info;
463     const char *device = NULL;
464 
465     /* pebl: Check whether a pseudo device is used, else try the normal ones */
466     if ((device = getenv("AUDIODEV")) == NULL)
467         device = default_device;
468 
469 
470     if (devAudio == -1)
471         while ((devAudio = open(device, devAudioMode)) == -1 && wait) {
472             if (errno == EINVAL && devAudioMode == O_RDWR)
473                 devAudioMode = O_WRONLY;
474             sleep(5);
475         }
476 
477     if (devAudio != -1) {
478         if (is_cs4231_or_dbri) {
479             audioInfo.play.sample_rate
480                     = audioInfo.record.sample_rate = sampleRate;
481             aucopy(&audioInfo, &info, sizeof(audio_info_t));
482             ioctl(devAudio, AUDIO_SETINFO, &info);
483         }
484 
485         return AuTrue;
486     }
487 
488     return AuFalse;
489 }
490 
491 static void
closeDevice()492 closeDevice()
493 {
494     if (devAudio != -1) {
495         close(devAudio);
496         devAudio = -1;
497     }
498 }
499 
500 /* cleanup function at server reset */
501 static void
serverReset()502 serverReset()
503 {
504 #ifdef DEBUGLOG
505     fprintf(stderr, "serverReset()\n");
506     fflush(stderr);
507 #endif
508 
509     if (!relinquish_device || openDevice(AuFalse)) {
510         signal(SIGPOLL, SIG_IGN);       /* discard pending signals */
511         ioctl(devAudio, AUDIO_DRAIN, 0);        /* drain everything out */
512         if (relinquish_device)
513             closeDevice();
514     }
515 }
516 
517 static void
updateHardware()518 updateHardware()
519 {
520     /* We don't delay updating anything that can be changed via
521        audioCtl, possibly by some other application, anymore.
522        Those values aren't cached in static variables either.
523        This way, we can control the audio device through the
524        server for immediate feedback only operations, which don't
525        involve software manipulated audio data, without resorting
526        to a non-NAS application. */
527 
528     if (updateSampleRate
529 #ifdef SYNC_PROCESS_ENABLED
530         || updateSyncAudioInfo
531 #endif
532             ) {
533         audio_info_t info;
534 
535         ioctl(devAudioCtl, I_SETSIG, 0);        /* disable signal */
536 
537 #ifdef SYNC_PROCESS_ENABLED
538         if (updateSyncAudioInfo) {
539             ioctl(devAudioCtl, AUDIO_SETINFO, &syncAudioInfo);
540             AUDIO_INITINFO(&syncAudioInfo);
541             updateSyncAudioInfo = AuFalse;
542         }
543 
544         if (updateSampleRate) {
545 #endif
546             AUDIO_INITINFO(&info);
547             info.play.sample_rate = info.record.sample_rate = sampleRate;
548             ioctl(devAudio, AUDIO_SETINFO, &info);
549             updateSampleRate = AuFalse;
550 #ifdef SYNC_PROCESS_ENABLED
551         }
552 #endif
553 
554         if (signalEnabled)
555             ioctl(devAudioCtl, I_SETSIG, S_MSG);
556     }
557 }
558 
559 static void
setAudioCtlInfo()560 setAudioCtlInfo()
561 {
562 #ifndef SEPARATE_CTLS
563 #ifdef SYNC_PROCESS_ENABLED
564     if (signalEnabled)
565         updateSyncAudioInfo = AuTrue;
566     else {
567 #endif
568         ioctl(devAudioCtl, I_SETSIG, 0);
569         ioctl(devAudioCtl, AUDIO_SETINFO, &syncAudioInfo);
570         AUDIO_INITINFO(&syncAudioInfo);
571         if (signalEnabled)
572             ioctl(devAudioCtl, I_SETSIG, S_MSG);
573 #ifdef SYNC_PROCESS_ENABLED
574     }
575 #endif
576 #else
577     ioctl(devAudioCtl2, AUDIO_SETINFO, infoPtr);
578 #endif
579 }
580 
581 /**
582   * Gains are mapped thusly:
583   *
584   *   Software   0 - 49     50 - 100
585   *   Hardware   0 - 49     50 - 255
586   */
587 /* g in FixedPoint */
588 #define gain100togain255(g) (((g) < 0x320000) \
589                              ? ((g) >> 16) \
590                              : ((0x41999 * ((g) >> 9) - 0x4d77f100) >> 23))
591         /* (g - 50) * (205 / 50) + 50 */
592 #define gain255togain100(g) (((g) < 50) \
593                              ? AuFixedPointFromSum((g), 0) \
594                              : ((((g) * 0x3e7064) >> 8) + 0x25ce0d))
595         /* (g - 50) * (50 / 205) + 50 */
596 
597 
598 #ifdef SYNC_PROCESS_ENABLED
599         /* We assume that AUDIO_INITINFO uses ~0 as a filler. This is
600            because clients like aupanel do a get right after a set.  It's
601            not a good thing to waste CPU like that, but it should not
602            happen that often (usually in response to user action).  */
603 
604 #define getCtl(info, field) \
605         do { \
606                 while (~syncAudioInfo.field) /*  */ ; \
607                 ioctl(devAudioCtl, AUDIO_GETINFO, &(info)); \
608         } while (0)
609 #else
610 #define getCtl(info, field) \
611         ioctl(devAudioCtl, AUDIO_GETINFO, &(info))
612 #endif
613 
614 /* callback */
615 static AuFixedPoint
getPhysicalOutputGain()616 getPhysicalOutputGain()
617 {
618     audio_info_t info;
619 
620 #ifdef DEBUGLOG
621     fprintf(stderr, "getPhysicalOutputGain()\n");
622     fflush(stderr);
623 #endif
624 
625     getCtl(info, play.gain);
626 
627     return gain255togain100(info.play.gain);
628 }
629 
630 /* callback */
631 static void
setPhysicalOutputGain(gain)632 setPhysicalOutputGain(gain)
633 AuFixedPoint gain;
634 {
635 #ifdef DEBUGLOG
636     fprintf(stderr, "setPhysicalOutputGain(gain=%08d)\n", gain);
637     fflush(stderr);
638 #endif
639 
640     syncAudioInfo.play.gain = gain100togain255(gain);
641     setAudioCtlInfo();
642 }
643 
644 /* callback */
645 static AuFixedPoint
getPhysicalFeedbackGain()646 getPhysicalFeedbackGain()
647 {
648     audio_info_t info;
649 
650 #ifdef DEBUGLOG
651     fprintf(stderr, "getPhysicalFeedbackGain()\n");
652     fflush(stderr);
653 #endif
654 
655     getCtl(info, monitor_gain);
656 
657     return gain255togain100(info.monitor_gain);
658 }
659 
660 /* callback */
661 static void
setPhysicalFeedbackGain(gain)662 setPhysicalFeedbackGain(gain)
663 AuFixedPoint gain;
664 {
665 #ifdef DEBUGLOG
666     fprintf(stderr, "setPhysicalFeedbackGain(gain=%08d)\n", gain);
667     fflush(stderr);
668 #endif
669 
670     syncAudioInfo.monitor_gain = gain100togain255(gain);
671     setAudioCtlInfo();
672 }
673 
674 /* callback */
675 static AuUint8
getPhysicalOutputMode()676 getPhysicalOutputMode()
677 {
678     AuUint8 mode = 0;
679     audio_info_t info;
680 
681 #ifdef DEBUGLOG
682     fprintf(stderr, "getPhysicalOutputMode()\n");
683     fflush(stderr);
684 #endif
685 
686     getCtl(info, play.port);
687 
688     if (info.play.port & AUDIO_HEADPHONE)
689         mode |= AuDeviceOutputModeHeadphone;
690 
691     if (info.play.port & AUDIO_SPEAKER)
692         mode |= AuDeviceOutputModeSpeaker;
693 
694     if (info.play.port & AUDIO_LINE_OUT)
695         mode |= AuDeviceOutputModeLineOut;
696 
697     return mode;
698 }
699 
700 /* callback */
701 static void
setPhysicalOutputMode(lineMode)702 setPhysicalOutputMode(lineMode)
703 AuUint8 lineMode;
704 {
705 #ifdef DEBUGLOG
706     fprintf(stderr, "setPhysicalOutputMode(=%d)\n", lineMode);
707     fflush(stderr);
708 #endif
709 
710     if (name_of_physical_device == name_am79c30) {
711         /* Must implement some kind of a toggle in this case.
712            (One and only one behavior.) */
713 
714         /* We absolutely must do this before changing
715            syncAudioInfo.  */
716         AuUint8 oldLineMode = getPhysicalOutputMode();
717 
718         if (lineMode & ~oldLineMode)
719             lineMode &= ~oldLineMode;
720     }
721 #ifdef SYNC_PROCESS_ENABLED
722     {
723         AuBlock l = AuBlockAudio();
724 #endif
725 
726         /* A final value of zero is allowed for output (play).  */
727         /* Any number of bits can be set.  */
728         syncAudioInfo.play.port = 0;
729 
730         if (lineMode & AuDeviceOutputModeHeadphone)
731             syncAudioInfo.play.port |= AUDIO_HEADPHONE;
732 
733         if (lineMode & AuDeviceOutputModeSpeaker)
734             syncAudioInfo.play.port |= AUDIO_SPEAKER;
735 
736         if (lineMode & AuDeviceOutputModeLineOut)
737             syncAudioInfo.play.port |= AUDIO_LINE_OUT;
738 
739         syncAudioInfo.play.port &= availOutputModes;
740 
741 #ifdef SYNC_PROCESS_ENABLED
742         AuUnBlockAudio(l);
743     }
744 #endif
745 
746     setAudioCtlInfo();
747 }
748 
749 /* callback */
750 static AuFixedPoint
getPhysicalInputGain()751 getPhysicalInputGain()
752 {
753     audio_info_t info;
754     unsigned int g;
755 
756 #ifdef DEBUGLOG
757     fprintf(stderr, "getPhysicalInputGain()\n");
758     fflush(stderr);
759 #endif
760 
761     getCtl(info, record.gain);
762     g = info.record.gain;
763 
764     /* This little hack helps get/map/round up/map/set stability.
765        Don't ask.  */
766     if (is_cs4231_or_dbri && (g >= 50) && (g < 255))
767         g -= 5;
768 
769     return gain255togain100(g);
770 }
771 
772 /* callback */
773 static AuUint8
getPhysicalInputMode()774 getPhysicalInputMode()
775 {
776     AuUint8 mode = 0;
777     audio_info_t info;
778 
779 #ifdef DEBUGLOG
780     fprintf(stderr, "getPhysicalInputMode()\n");
781     fflush(stderr);
782 #endif
783 
784     getCtl(info, record.port);
785 
786     if (info.record.port & AUDIO_MICROPHONE)
787         mode |= AuDeviceLineModeHigh;
788 
789     if (info.record.port & AUDIO_LINE_IN)
790         mode |= AuDeviceLineModeLow;
791 
792 #ifdef UNCOMMENT_THIS_WHEN_NAS_SUPPORTS_IT
793     if (info.record.port & AUDIO_INTERNAL_CD_IN)
794         mode |= AuDeviceLineModeInternalCD;
795 #endif
796 
797     return mode;
798 }
799 
800 /* callback */
801 static void
setPhysicalInputGainAndLineMode(gain,lineMode)802 setPhysicalInputGainAndLineMode(gain, lineMode)
803 AuFixedPoint gain;
804 AuUint8 lineMode;
805 {
806     AuUint32 newInputLineMode;
807 
808 #ifdef DEBUGLOG
809     fprintf(stderr,
810             "setPhysicalInputGainAndLineMode(gain=%04x.%04x, lineMode=%u)\n",
811             gain >> 16, gain & 0xffff, lineMode);
812     fflush(stderr);
813 #endif
814 
815     syncAudioInfo.record.gain = gain100togain255(gain);
816 
817     /* one and only one bit must be set */
818     if (lineMode == AuDeviceLineModeHigh)
819         newInputLineMode = AUDIO_MICROPHONE;
820     else if (lineMode == AuDeviceLineModeLow)
821         newInputLineMode = AUDIO_LINE_IN;
822 #ifdef UNCOMMENT_THIS_WHEN_NAS_SUPPORTS_IT
823     else if (lineMode == AuDeviceLineModeInternalCD)
824         newInputLineMode = AUDIO_INTERNAL_CD_IN;
825 #endif
826     else
827         newInputLineMode = 0;
828 
829 #if 0
830     /* A value of zero is rejected by some drivers, so don't even try it. */
831     /* XXX - Really?  Doesn't matter since zero does not even get here!  */
832     if (newInputLineMode &= availInputModes)
833 #endif
834         syncAudioInfo.record.port = newInputLineMode;
835 
836     setAudioCtlInfo();
837 }
838 
839 /* A zero-length write() triggers a SIGPOLL. */
840 #define trigger() \
841         write(devAudio, emptyOutput, 0)
842 #define triggerIfPending() \
843         do { \
844                 if (pendingTrigger) { \
845                         trigger(); \
846                         pendingTrigger = AuFalse; \
847                 } \
848         } while (0)
849 #ifdef REVERSE_WRITE
850 #define write0(buf, n) \
851         do { triggerIfPending(); write(devAudio, buf, n); } while (0)
852 #else
853 #ifndef DELAYED_TRIGGER
854 #define write0(buf, n) \
855         do { write(devAudio, buf, n); triggerIfPending(); } while (0)
856 #else
857 #define write0(buf, n) \
858         do { write(devAudio, buf, n); delayedTrigger = AuTrue; } while (0)
859 #endif
860 #endif
861 
862 /* Warning:  this function must be called between Au{Block,UnBlock}Audio,
863  *   which is already the case in a BSD signal handler.  */
864 static void
writeEmptyOutput()865 writeEmptyOutput()
866 {
867 #ifndef BSD_SIGNALS
868     AuBlock l = AuBlockAudio();
869 #endif
870 
871 #ifdef DEBUGLOG
872     fprintf(stderr, "writeEmptyOutput(bufSize=%d)\n", bufSize);
873     fflush(stderr);
874 #endif
875 
876     write0(emptyOutput, bufSize);
877 
878 #ifndef BSD_SIGNALS
879     AuUnBlockAudio(l);
880 #endif
881 }
882 
883 /* callback */
884 static void
enableProcessFlow()885 enableProcessFlow()
886 {
887     AuBlock l;
888 
889 #ifdef DEBUGLOG
890     fprintf(stderr, "\nenableProcessFlow()\n");
891     fflush(stderr);
892 #endif
893 
894     if (relinquish_device)
895         openDevice(AuTrue);
896     ioctl(devAudio, I_FLUSH, FLUSHRW);  /* flush pending io */
897     signalEnabled = AuTrue;
898     ioctl(devAudioCtl, I_SETSIG, S_MSG);        /* enable signal */
899     updateHardware();
900     /* XXX - Shouldn't this be done only one time when audioctl is
901        opened? */
902     l = AuBlockAudio();
903 #if defined(REVERSE_WRITE) && defined(WRITE_EMPTY_ENABLE)
904     pendingTrigger = AuTrue;
905     writeEmptyOutput();
906 #else
907 #ifdef WRITE_EMPTY_ENABLE
908     write(devAudio, emptyOutput,
909           getenv("NAS_EMPTY_SIZE") ? atoi(getenv("NAS_EMPTY_SIZE")) :
910           bufSize);
911 #endif
912     trigger();                  /* a SIGPOLL. */
913 #endif
914     AuUnBlockAudio(l);
915 }
916 
917 /* callback */
918 static void
disableProcessFlow()919 disableProcessFlow()
920 {
921 #ifdef DEBUGLOG
922     fprintf(stderr, "disableProcessFlow()\n");
923     fflush(stderr);
924 #endif
925 
926     signalEnabled = AuFalse;
927     ioctl(devAudioCtl, I_SETSIG, 0);    /* disable signal */
928 
929 #if 0                           /* this seems to cause problems on some
930                                    sun kernels/sound drivers */
931     ioctl(devAudio, AUDIO_DRAIN, 0);    /* drain everything out */
932 #endif
933 
934     if (relinquish_device)
935         closeDevice();
936 }
937 
938 /* for CS4231 and dbri */
939 /* Warning:  this function must be called between Au{Block,UnBlock}Audio,
940  *   which is already the case in a BSD signal handler.  */
941 static void
writeOutput(p,n)942 writeOutput(p, n)
943 AuInt16 *p;
944 unsigned int n;
945 {
946 #ifndef BSD_SIGNALS
947     AuBlock l = AuBlockAudio();
948 #endif
949 
950 #ifdef DEBUGLOG
951     fprintf(stderr, "writeOutput(n=%d, n*4=%d)\n", n, n << 2);
952     fflush(stderr);
953 #endif
954 
955     /*
956      * The sbpro can only be opened for reading or for writing, not both.
957      */
958 
959     if (!(devAudioMode == O_RDWR || devAudioMode == O_WRONLY)) {
960         closeDevice();
961         devAudioMode = O_WRONLY;
962         openDevice(1);
963     }
964 
965 
966     write0(p, n << 2);
967 
968 #ifndef BSD_SIGNALS
969     AuUnBlockAudio(l);
970 #endif
971 }
972 
973 /* for am79c30 */
974 /* Warning:  this function must be called between Au{Block,UnBlock}Audio,
975  *   which is already the case in a BSD signal handler.  */
976 static void
writePhysicalOutput(p,n)977 writePhysicalOutput(p, n)
978 AuInt16 *p;
979 unsigned int n;
980 {
981 #ifndef BSD_SIGNALS
982     AuBlock l;
983 #endif
984 
985 #ifdef DEBUGLOG
986     fprintf(stderr, "writePhysicalOutput(n=%d)\n", n);
987     fflush(stderr);
988 #endif
989 
990     AuNativeToULAW8(p, 1, n);
991 #ifndef BSD_SIGNALS
992     l = AuBlockAudio();
993 #endif
994 
995     write0(p, n);
996 
997 #ifndef BSD_SIGNALS
998     AuUnBlockAudio(l);
999 #endif
1000 }
1001 
1002 /* for CS4231 and dbri */
1003 static void
writeStereoOutput()1004 writeStereoOutput()
1005 {
1006     writeOutput(auOutputStereo, *stereoSamples);
1007 }
1008 
1009 /* for am79c30 */
1010 static void
writePhysicalOutputsStereo()1011 writePhysicalOutputsStereo()
1012 {
1013     AuInt32 i;
1014     AuInt16 *s, *m;
1015 
1016     s = (AuInt16 *) auOutputStereo;
1017     m = (AuInt16 *) auOutputMono;
1018 
1019     for (i = 0; i < *stereoSamples; i++, s += 2)
1020         *m++ = (s[0] + s[1]) >> 1;
1021 
1022     writePhysicalOutput(auOutputMono, *stereoSamples);
1023 }
1024 
1025 /* for CS4231 and dbri */
1026 static void
writeMonoOutput()1027 writeMonoOutput()
1028 {
1029     AuInt16 *m, *p;
1030     int i;
1031 
1032     m = (AuInt16 *) auOutputMono;
1033     p = (AuInt16 *) auOutputStereo;
1034 
1035     for (i = 0; i < *monoSamples; i++) {
1036         *p++ = *m;
1037         *p++ = *m++;
1038     }
1039 
1040     writeOutput(auOutputStereo, *monoSamples);
1041 }
1042 
1043 /* for am79c30 */
1044 static void
writePhysicalOutputsMono()1045 writePhysicalOutputsMono()
1046 {
1047     writePhysicalOutput(auOutputMono, *monoSamples);
1048 }
1049 
1050 /* for CS4231 and dbri */
1051 static void
writeAllOutputs()1052 writeAllOutputs()
1053 {
1054     AuInt16 *l, *r, *m, *s, *p;
1055     int i;
1056     unsigned int n;
1057 
1058     l = (AuInt16 *) auOutputLeft;
1059     r = (AuInt16 *) auOutputRight;
1060     m = (AuInt16 *) auOutputMono;
1061     s = p = (AuInt16 *) auOutputStereo;
1062     n = aumax(aumax(*monoSamples, *stereoSamples),
1063               aumax(*leftSamples, *rightSamples));
1064 
1065     /* XXX - This assumes that any non participating flow has null value;
1066        true? */
1067     for (i = 0; i < n; i++) {
1068 #ifndef ADD_OUTPUTS
1069         *p++ = ((*l++ + *m + *s++) * theAverage) >> 16;
1070         *p++ = ((*r++ + *m++ + *s++) * theAverage) >> 16;
1071 #else
1072         *p++ = (*l++ + *m + *s++);
1073         *p++ = (*r++ + *m++ + *s++);
1074 #endif
1075     }
1076 
1077     writeOutput(auOutputStereo, n);
1078 }
1079 
1080 /* for am79c30 */
1081 static void
writePhysicalOutputsBoth()1082 writePhysicalOutputsBoth()
1083 {
1084     AuInt32 i;
1085     AuInt16 *s, *m;
1086     AuUint32 n;
1087 
1088     s = (AuInt16 *) auOutputStereo;
1089     m = (AuInt16 *) auOutputMono;
1090     n = aumax(*monoSamples, *stereoSamples);
1091 
1092 #ifndef ADD_OUTPUTS
1093     for (i = 0; i < n; i++, s += 2, m++)
1094         /* XXX - That's not right, conceptually. */
1095         /* beware: can't put m++ on this line because of
1096            unknown order of eval */
1097         *m = ((s[0] + s[1] + *m) * 0x5555) >> 16;
1098 #else
1099     for (i = 0; i < n; i++, s += 2)
1100         *m++ += (s[0] + s[1]) >> 1;
1101 #endif
1102 
1103     writeOutput(auOutputMono, n);
1104 }
1105 
1106 /* callback */
1107 static void
readPhysicalInputs()1108 readPhysicalInputs()
1109 {
1110 #ifdef DEBUGLOG
1111     fprintf(stderr, "readPhysicalInputs()\n");
1112     fflush(stderr);
1113 #endif
1114 
1115 
1116     /*
1117      * The sbpro can only be opened for reading or for writing, not both.
1118      */
1119 
1120     if (!(devAudioMode == O_RDWR || devAudioMode == O_RDONLY)) {
1121         closeDevice();
1122         devAudioMode = O_RDONLY;
1123         openDevice(1);
1124     }
1125 
1126     read(devAudio, auInput, bufSize);
1127 
1128     if (!is_cs4231_or_dbri)
1129         AuULAW8ToNative(auInput, 1, auMinibufSamples);
1130 }
1131 
1132 /* callback */
1133 /* for am79c30 */
1134 static void
setWritePhysicalOutputFunction(flow,funct)1135 setWritePhysicalOutputFunction(flow, funct)
1136 CompiledFlowPtr flow;
1137 void (**funct) ();
1138 {
1139     AuUint32 mask = flow->physicalDeviceMask;
1140 
1141 #ifdef DEBUGLOG
1142     fprintf(stderr, "setWritePhysicalOutputFunction()\n");
1143     fflush(stderr);
1144 #endif
1145 
1146     if ((mask & (PhysicalOutputMono | PhysicalOutputStereo)) ==
1147         (PhysicalOutputMono | PhysicalOutputStereo))
1148         *funct = writePhysicalOutputsBoth;
1149     else if (mask & PhysicalOutputMono)
1150         *funct = writePhysicalOutputsMono;
1151     else if (mask & PhysicalOutputStereo)
1152         *funct = writePhysicalOutputsStereo;
1153     else
1154         *funct = writeEmptyOutput;
1155 }
1156 
1157 /* callback */
1158 /* for CS4231 and dbri */
1159 static void
setWriteOutputFunction(flow,funct)1160 setWriteOutputFunction(flow, funct)
1161 CompiledFlowPtr flow;
1162 void (**funct) ();
1163 {
1164     AuUint32 mask = flow->physicalDeviceMask & AllPhysicalOutputs;
1165 
1166 #ifdef DEBUGLOG
1167     fprintf(stderr, "setWriteOutputFunction()\n");
1168     fflush(stderr);
1169 #endif
1170 
1171     if (mask)
1172         if (mask == PhysicalOutputMono)
1173             *funct = writeMonoOutput;
1174         else if (mask == PhysicalOutputStereo)
1175             *funct = writeStereoOutput;
1176         else {
1177 #ifndef ADD_OUTPUTS
1178             int both;
1179 
1180             theAverage = 0x10000;
1181 
1182             both = (mask & (PhysicalOutputLeft |
1183                             PhysicalOutputRight)) ? 1 : 0;
1184 
1185             if (mask & PhysicalOutputMono)
1186                 both++;
1187 
1188             if (mask & PhysicalOutputStereo)
1189                 both++;
1190 
1191             if (both > 1)
1192                 theAverage /= both;
1193 #endif
1194 
1195             *funct = writeAllOutputs;
1196     } else
1197         *funct = writeEmptyOutput;
1198 }
1199 
1200 /* signal handler */
1201 #ifdef DEBUGLOG
1202 #include <sys/time.h>
1203 #endif
1204 static void
processAudioSignal(sig)1205 processAudioSignal(sig)
1206 int sig;
1207 {
1208     int i;
1209 #ifdef DEBUGLOG
1210     static struct timeval tv0;
1211     struct timeval tv1, tv2, tv3;
1212     long ds1, du1, ds2, du2, ds3, du3;
1213 
1214     fprintf(stderr, "processAudioSignal...\n");
1215     fflush(stderr);
1216 
1217     gettimeofday(&tv1, 0);
1218 #endif /* DEBUGLOG */
1219 
1220     updateHardware();
1221 
1222 #ifdef DEBUGLOG
1223     gettimeofday(&tv2, 0);
1224 #endif /* DEBUGLOG */
1225 
1226     pendingTrigger = AuFalse;
1227     for (i = 0; i < chunksPerSignal - 1; i++)
1228         AuProcessData();
1229 
1230     pendingTrigger = AuTrue;
1231     AuProcessData();
1232 
1233 #if defined(DELAYED_TRIGGER) && !defined(REVERSE_WRITE)
1234     if (delayedTrigger) {
1235         /* Here's the very reason delaying this is useful.
1236            Don't do it if AuProcessData() called
1237            disableProcessFlow() between the write and now. */
1238         if (signalEnabled)
1239             trigger();          /* a SIGPOLL. */
1240 
1241         delayedTrigger = AuFalse;
1242     }
1243 #endif /* DELAYED_TRIGGER && !REVERSE_WRITE */
1244 
1245 #ifdef DEBUGLOG
1246     gettimeofday(&tv3, 0);
1247 
1248 #define tv_diff(tv_a, tv_b, ds, du) \
1249         do { \
1250                 ds = tv_a.tv_sec - tv_b.tv_sec; \
1251                 du = tv_a.tv_usec - tv_b.tv_usec; \
1252                 if (du < 0) { \
1253                         --ds; \
1254                         du += 1000000L; \
1255                 } \
1256         } while (0)
1257 
1258     tv_diff(tv1, tv0, ds1, du1);
1259     tv_diff(tv2, tv1, ds2, du2);
1260     tv_diff(tv3, tv2, ds3, du3);
1261 
1262 #undef tv_diff
1263 
1264     tv0 = tv1;
1265 
1266     fprintf(stderr,
1267             "...processAudioSignal [%ld.%06ld %ld.%06ld %ld.%06ld]\n",
1268             ds1, du1, ds2, du2, ds3, du3);
1269     fflush(stderr);
1270 #endif /* DEBUGLOG */
1271 }
1272 
1273 /* callback */
1274 /* for CS4231 and dbri */
1275 static AuUint32
setSampleRate(rate)1276 setSampleRate(rate)
1277 AuUint32 rate;
1278 {
1279     int i;
1280     AuUint32 closestRate;
1281     static AuUint32 rates[] = {
1282         /* 5510, 6620, */
1283         8000, 9600, 11025, 16000, 18900, 22050,
1284         /* 27420, */
1285         32000,
1286         /* 33075, */
1287         37800, 44100, 48000
1288     };
1289 
1290     closestRate = 48000;
1291 
1292     for (i = 0; i < sizeof(rates) / sizeof(rates[0]); i++)
1293         if ((rates[i] >= rate) && (rates[i] < closestRate))
1294             closestRate = rates[i];
1295 
1296     if (closestRate != sampleRate) {
1297         sampleRate = closestRate;
1298         updateSampleRate = AuTrue;
1299     }
1300 
1301     chunksPerSignal = sampleRate / SIGNAL_RATE / MINIBUF_SAMPLES;
1302 
1303 #ifdef DEBUGLOG
1304     fprintf(stderr, "setSampleRate(rate=%d) --> %d\n", rate, closestRate);
1305     fflush(stderr);
1306 #endif
1307 
1308     return closestRate;
1309 }
1310 
1311 #define PhysicalOneTrackBufferSize \
1312         PAD4(auMinibufSamples * auNativeBytesPerSample * 1)
1313 #define PhysicalTwoTrackBufferSize \
1314         PAD4(auMinibufSamples * auNativeBytesPerSample * 2)
1315 
1316 AuBool
AuInitPhysicalDevices()1317 AuInitPhysicalDevices()
1318 {
1319     int open_for_business;
1320     static AuUint8 *physicalBuffers;
1321     AuUint32 physicalBuffersSize;
1322     audio_info_t info;
1323     extern AuUint32 auPhysicalOutputBuffersSize;
1324     extern AuUint8 *auPhysicalOutputBuffers;
1325     char *nas_device_policy;
1326 
1327 #ifdef DEBUGLOG
1328     fprintf(stderr, "AuInitPhysicalDevices()\n");
1329     fflush(stderr);
1330 #endif
1331 
1332     if (VENDOR_STRING) {
1333         aufree(VENDOR_STRING);
1334         VENDOR_STRING = (char *) 0;
1335     }
1336 
1337     if (NasConfig.DoDeviceRelease) {
1338         relinquish_device = AuTrue;
1339         if (NasConfig.DoDebug)
1340             osLogMsg("Init: will close device when finished with stream.\n");
1341     } else {
1342         relinquish_device = AuFalse;
1343         if (NasConfig.DoDebug)
1344             osLogMsg("Init: will open device exclusively.\n");
1345     }
1346 
1347     if (devAudio == -1) {
1348         audio_device_t type;
1349         const char *device = NULL;
1350         char *devicectl = NULL;
1351 
1352         /* pebl: Check whether a pseudo device is used, else try the normal one */
1353         if ((device = getenv("AUDIODEV")) == NULL)
1354             device = default_device;
1355 
1356 #ifdef DEBUGLOG
1357         fprintf(stderr, "Trying device %s\n", device);
1358         fflush(stderr);
1359 #endif
1360 
1361         devAudio = open(device, devAudioMode);
1362         if (devAudio == -1 && errno == EINVAL && devAudioMode == O_RDWR) {
1363             devAudioMode = O_WRONLY;
1364             devAudio = open(device, devAudioMode);
1365         }
1366 
1367 
1368         /* pebl: We cannot just concat "ctl" on variable device, so
1369            make a copy and concat "ctl".  (free it again) */
1370         if (!(devicectl = (char *) aualloc(strlen(device) +
1371                                            strlen("ctl") + 1)))
1372             return AuFalse;
1373         sprintf(devicectl, "%sctl", device);
1374 
1375         open_for_business = (devAudio != -1 &&
1376 #ifdef SEPARATE_CTLS
1377                              (devAudioCtl2 = open(devicectl, O_RDWR)) != -1
1378                              &&
1379 #endif
1380                              (devAudioCtl =
1381                               open(devicectl, O_RDWR)) != -1);
1382 
1383         aufree(devicectl);
1384 
1385         if (open_for_business) {
1386 #ifndef AUDIO_GETDEV
1387             name_of_physical_device = name_unknown;
1388 #else /* defined AUDIO_GETDEV */
1389             if ((open_for_business =
1390                  (ioctl(devAudio, AUDIO_GETDEV, &type) != -1))) {
1391                 if (IS_AMD(type))
1392                     name_of_physical_device = name_am79c30;
1393                 else if (IS_CS4231(type))
1394                     name_of_physical_device = name_CS4231;
1395                 else if (IS_DBRI(type))
1396                     name_of_physical_device = name_dbri;
1397                 else
1398                     name_of_physical_device = name_unknown;
1399             }
1400 #endif /* defined AUDIO_GETDEV */
1401         }
1402 
1403         if (!open_for_business) {
1404             if (devAudio != -1)
1405                 close(devAudio);
1406             if (devAudioCtl != -1)
1407                 close(devAudioCtl);
1408 #ifdef SEPARATE_CTLS
1409             if (devAudioCtl2 != -1)
1410                 close(devAudioCtl2);
1411 #endif
1412             devAudio = devAudioCtl = -1;
1413             name_of_physical_device = 0;
1414             return AuFalse;
1415         }
1416     }
1417 
1418     if (!(VENDOR_STRING = (char *) aualloc(strlen(SUN_VENDOR) +
1419                                            strlen(name_of_physical_device)
1420                                            - 1)))
1421         return AuFalse;
1422 
1423     sprintf(VENDOR_STRING, SUN_VENDOR, name_of_physical_device);
1424 
1425     if (physicalBuffers) {
1426         aufree(physicalBuffers);
1427         physicalBuffers = 0;
1428     }
1429 
1430     if (emptyOutput) {
1431         aufree(emptyOutput);
1432         emptyOutput = 0;
1433     }
1434 
1435     is_cs4231_or_dbri =
1436             (name_of_physical_device == name_CS4231 ||
1437              name_of_physical_device == name_dbri);
1438 
1439     auMaxSampleRate = is_cs4231_or_dbri ? 48000 : 8000;
1440 
1441     auMinibufSamples = MINIBUF_SAMPLES;
1442 
1443     /* the output buffers need to be twice as large for output
1444        range checking */
1445     physicalBuffersSize = (PhysicalOneTrackBufferSize * 2 +     /* mono output */
1446                            PhysicalTwoTrackBufferSize * 2);     /* stereo output */
1447 
1448     if (is_cs4231_or_dbri) {
1449         physicalBuffersSize += (PhysicalTwoTrackBufferSize +    /* stereo input */
1450                                 PhysicalOneTrackBufferSize * 2 +        /* left output */
1451                                 PhysicalOneTrackBufferSize * 2);        /* right output */
1452         bufSize = MINIBUF_SAMPLES * 2 * 2;      /* stereo, 16 bits */
1453     } else {
1454         physicalBuffersSize += PhysicalTwoTrackBufferSize;      /* mono input */
1455         bufSize = MINIBUF_SAMPLES;      /* mono, 8 bits */
1456     }
1457 
1458     if (!(emptyOutput = (AuUint8 *) aualloc(bufSize)))
1459         return AuFalse;
1460 
1461     auset(emptyOutput, is_cs4231_or_dbri ? 0 : 0xff, bufSize);
1462 
1463     if (!(physicalBuffers = (AuUint8 *) aualloc(physicalBuffersSize)))
1464         return AuFalse;
1465 
1466     auInput = physicalBuffers;
1467 
1468     if (is_cs4231_or_dbri) {
1469         auOutputMono = auInput + PhysicalTwoTrackBufferSize;
1470         auOutputLeft = auOutputMono + 2 * PhysicalOneTrackBufferSize;
1471         auOutputRight = auOutputLeft + 2 * PhysicalOneTrackBufferSize;
1472         auOutputStereo = auOutputRight + 2 * PhysicalOneTrackBufferSize;
1473 
1474         auPhysicalOutputBuffersSize =
1475                 physicalBuffersSize - PhysicalTwoTrackBufferSize;
1476     } else {
1477         auOutputMono = auInput + PhysicalOneTrackBufferSize;
1478         auOutputStereo = auOutputMono + 2 * PhysicalOneTrackBufferSize;
1479 
1480         auPhysicalOutputBuffersSize =
1481                 physicalBuffersSize - PhysicalTwoTrackBufferSize;
1482     }
1483 
1484     auPhysicalOutputBuffers = auOutputMono;
1485 
1486     signal(SIGPOLL, processAudioSignal);
1487 
1488     AuRegisterCallback(AuCreateServerComponentsCB, createServerComponents);
1489     AuRegisterCallback(AuSetPhysicalOutputGainCB, setPhysicalOutputGain);
1490     AuRegisterCallback(AuGetPhysicalOutputGainCB, getPhysicalOutputGain);
1491     AuRegisterCallback(AuGetPhysicalOutputModeCB, getPhysicalOutputMode);
1492     AuRegisterCallback(AuSetPhysicalOutputModeCB, setPhysicalOutputMode);
1493     AuRegisterCallback(AuGetPhysicalFeedbackGainCB,
1494                        getPhysicalFeedbackGain);
1495     AuRegisterCallback(AuSetPhysicalFeedbackGainCB,
1496                        setPhysicalFeedbackGain);
1497     AuRegisterCallback(AuSetPhysicalInputGainAndLineModeCB,
1498                        setPhysicalInputGainAndLineMode);
1499     AuRegisterCallback(AuGetPhysicalInputGainCB, getPhysicalInputGain);
1500     AuRegisterCallback(AuGetPhysicalInputModeCB, getPhysicalInputMode);
1501     AuRegisterCallback(AuEnableProcessFlowCB, enableProcessFlow);
1502     AuRegisterCallback(AuDisableProcessFlowCB, disableProcessFlow);
1503     AuRegisterCallback(AuReadPhysicalInputsCB, readPhysicalInputs);
1504 
1505     if (is_cs4231_or_dbri) {
1506         AuRegisterCallback(AuSetWritePhysicalOutputFunctionCB,
1507                            setWriteOutputFunction);
1508         AuRegisterCallback(AuSetSampleRateCB, setSampleRate);
1509     } else {
1510         AuRegisterCallback(AuSetWritePhysicalOutputFunctionCB,
1511                            setWritePhysicalOutputFunction);
1512     }
1513 
1514     ioctl(devAudioCtl, AUDIO_GETINFO, &info);
1515     availInputModes = info.record.avail_ports;
1516     availOutputModes = info.play.avail_ports;
1517     sampleRate = info.play.sample_rate; /* XXX */
1518     chunksPerSignal = sampleRate / SIGNAL_RATE / MINIBUF_SAMPLES;
1519 
1520     AUDIO_INITINFO(&audioInfo);
1521     /* We only need to setup fields that differ from the open()
1522        defaults.  */
1523     if (is_cs4231_or_dbri) {
1524         audioInfo.play.encoding
1525                 = audioInfo.record.encoding = AUDIO_ENCODING_LINEAR;
1526         audioInfo.play.precision = audioInfo.record.precision = 16;
1527         audioInfo.play.channels = audioInfo.record.channels = 2;
1528 
1529         aucopy(&audioInfo, &info, sizeof(audio_info_t));
1530         ioctl(devAudio, AUDIO_SETINFO, &info);
1531     }
1532 
1533     AUDIO_INITINFO(&syncAudioInfo);
1534 
1535     /* bogus resource so we can have a cleanup function at server reset */
1536     AddResource(FakeClientID(SERVER_CLIENT),
1537                 CreateNewResourceType(serverReset), 0);
1538 
1539     if (relinquish_device)
1540         closeDevice();
1541 
1542     return AuTrue;
1543 }
1544