2 ** Because of the massive number of interrupts the Aura "soundcard" needs,
3 ** this driver does some *very* ugly things:
4 ** 1) It requires both CIA-B timers.
5 ** 2) It takes complete control over the level 6 interrupt. Make sure
6 ** nobody else is using it!
8 ** We use Timer A of CIA-B.
13 #include <exec/exec.h>
15 #include <exec/interrupts.h>
16 #include <hardware/cia.h>
17 #include <resources/cia.h>
19 #include <proto/cia.h>
20 #include <proto/exec.h>
21 #include <proto/utility.h>
22 #include <proto/ahi_sub.h>
24 #include <devices/ahi.h>
25 #include <libraries/ahi_sub.h>
30 #define dd ((struct aura *) AudioCtrl->ahiac_DriverData)
32 #define RECBUFFERSIZE 2048 // in samples
34 #define STARTA_OR CIACRAF_START
35 #define STOPA_AND CIACRAF_TODIN | CIACRAF_PBON | CIACRAF_OUTMODE | CIACRAF_SPMODE
36 #define STOPB_AND CIACRBF_ALARM | CIACRBF_PBON | CIACRBF_OUTMODE
38 extern void KPrintF(char *fmt
,...);
40 extern char __far _LibID
[];
41 extern char __far _LibName
[];
43 extern void __asm
InstallUglyInterruptHack(register __a1
void (* )(), register __a2
struct aura
*);
44 extern void __asm
UninstallUglyInterruptHack(register __a3
struct aura
*);
45 extern void __asm
DummyFunc(void);
46 extern void __asm
DummyInt(void);
47 extern void __asm
PlayIntMono(void);
48 extern void __asm
RecordIntMono(void);
49 extern void __asm
PlayAndRecordIntMono(void);
50 extern void __asm
PlayIntStereo(void);
51 extern void __asm
RecordIntStereo(void);
52 extern void __asm
PlayAndRecordIntStereo(void);
53 extern void __asm
SoftFunc(void);
55 struct ExecBase
*SysBase
;
57 const static void *IntCodeTable
[] =
67 PlayAndRecordIntStereo
71 #define FREQUENCIES 23
72 const static ULONG frequency
[FREQUENCIES
] =
79 8000, // ยต- and A-Law, DAT/6
99 struct Library
*UtilityBase
= NULL
;
100 struct Library
*AHIsubBase
= NULL
;
101 struct CIA
*ciab
= (struct CIA
*)0xbfd000;
102 struct CIABase
*ciabbase
= NULL
;
106 int __saveds __asm
__UserLibInit (register __a6
struct Library
*libbase
)
108 SysBase
= *(struct ExecBase
**)4;
111 if( ! (UtilityBase
=OpenLibrary("utility.library",37)))
113 Alert(AN_Unknown
|AG_OpenLib
|AO_UtilityLib
);
117 if( ! (ciabbase
= OpenResource(CIABNAME
)))
125 void __saveds __asm
__UserLibCleanup (register __a6
struct Library
*libbase
)
127 if(UtilityBase
) { CloseLibrary(UtilityBase
); UtilityBase
=NULL
; }
130 ULONG __asm __saveds
intAHIsub_AllocAudio(
131 register __a1
struct TagItem
*tagList
,
132 register __a2
struct AHIAudioCtrlDrv
*AudioCtrl
)
135 // Only one user at the time!
147 if(AudioCtrl
->ahiac_DriverData
=AllocVec(sizeof(struct aura
),MEMF_PUBLIC
|MEMF_CLEAR
))
150 dd
->a_CIAperiod
=SysBase
->ex_EClockFrequency
*2/AudioCtrl
->ahiac_MixFreq
;
151 AudioCtrl
->ahiac_MixFreq
=SysBase
->ex_EClockFrequency
/dd
->a_CIAperiod
/2;
153 if(dd
->a_AuraInt
=AllocVec(sizeof(struct Interrupt
),MEMF_PUBLIC
|MEMF_ANY
|MEMF_CLEAR
))
155 dd
->a_AuraInt
->is_Node
.ln_Type
=NT_INTERRUPT
;
156 dd
->a_AuraInt
->is_Node
.ln_Name
=_LibName
;
157 dd
->a_AuraInt
->is_Data
=AudioCtrl
;
158 // It will never be called anyway...
159 dd
->a_AuraInt
->is_Code
=(void (* )()) DummyFunc
;
162 if( ! (AddICRVector((struct Library
*) ciabbase
,CIAICRB_TA
,dd
->a_AuraInt
)))
164 dd
->a_GotTimerA
= TRUE
;
167 if ( ! (AddICRVector((struct Library
*)ciabbase
,CIAICRB_TB
,dd
->a_AuraInt
)))
169 dd
->a_GotTimerB
= TRUE
;
171 // Stop both timers, set 02 pulse count-down mode, set continuous mode
173 ciab
->ciacra
&= STOPA_AND
;
174 ciab
->ciacrb
&= STOPB_AND
;
177 InstallUglyInterruptHack(DummyInt
, (struct aura
*)AudioCtrl
->ahiac_DriverData
);
179 if(dd
->a_SoftInt
= AllocVec(
180 sizeof(struct Interrupt
),MEMF_PUBLIC
|MEMF_ANY
|MEMF_CLEAR
))
182 dd
->a_SoftInt
->is_Node
.ln_Type
=NT_INTERRUPT
;
183 dd
->a_SoftInt
->is_Node
.ln_Name
=_LibName
;
184 dd
->a_SoftInt
->is_Data
=AudioCtrl
;
185 dd
->a_SoftInt
->is_Code
=(void (* )()) SoftFunc
;
187 if(TRUE
) // FIXIT: Check if hardware is present...
189 return AHISF_KNOWSTEREO
|AHISF_CANRECORD
|AHISF_MIXING
|AHISF_TIMING
;
199 void __asm __saveds
intAHIsub_FreeAudio(
200 register __a2
struct AHIAudioCtrlDrv
*AudioCtrl
)
202 if(AudioCtrl
->ahiac_DriverData
)
204 if(dd
->a_GotTimerA
&& dd
->a_GotTimerB
)
206 UninstallUglyInterruptHack((struct aura
*)AudioCtrl
->ahiac_DriverData
);
210 RemICRVector((struct Library
*)ciabbase
,CIAICRB_TA
,dd
->a_AuraInt
);
214 RemICRVector((struct Library
*)ciabbase
,CIAICRB_TB
,dd
->a_AuraInt
);
217 FreeVec(dd
->a_SoftInt
);
218 FreeVec(dd
->a_AuraInt
);
219 FreeVec(AudioCtrl
->ahiac_DriverData
);
220 AudioCtrl
->ahiac_DriverData
=NULL
;
228 ULONG __asm __saveds
intAHIsub_Start(
229 register __d0 ULONG Flags
,
230 register __a2
struct AHIAudioCtrlDrv
*AudioCtrl
)
233 if(Flags
& AHISF_PLAY
)
235 AHIsub_Stop(AHISF_PLAY
,AudioCtrl
);
237 if(!(dd
->a_MixBuffer1
=AllocVec(AudioCtrl
->ahiac_BuffSize
,MEMF_PUBLIC
|MEMF_CLEAR
|MEMF_ANY
)))
240 if(!(dd
->a_MixBuffer2
=AllocVec(AudioCtrl
->ahiac_BuffSize
,MEMF_PUBLIC
|MEMF_CLEAR
|MEMF_ANY
)))
243 dd
->a_Status
|= STATUS_PLAY
;
245 switch(AudioCtrl
->ahiac_BuffType
)
248 dd
->a_Status
&= ~STATUS_STEREO
;
251 dd
->a_Status
|= STATUS_STEREO
;
254 return AHIE_BADSAMPLETYPE
;
256 InstallUglyInterruptHack((void (* )())IntCodeTable
[dd
->a_Status
],
257 (struct aura
*)AudioCtrl
->ahiac_DriverData
);
260 KPrintF("DummyInt: 0x%08lx\n",DummyInt);
261 KPrintF("PlayIntMono: 0x%08lx\n",PlayIntMono);
262 KPrintF("RecordIntMono: 0x%08lx\n",RecordIntMono);
263 KPrintF("PlayAndRecordIntMono: 0x%08lx\n",PlayAndRecordIntMono);
265 KPrintF("DummyInt: 0x%08lx\n",DummyInt);
266 KPrintF("PlayIntStereo: 0x%08lx\n",PlayIntStereo);
267 KPrintF("RecordIntStereo: 0x%08lx\n",RecordIntStereo);
268 KPrintF("PlayAndRecordIntStereo: 0x%08lx\n",PlayAndRecordIntStereo);
270 KPrintF("Code: 0x%08lx\n",IntCodeTable[dd->a_Status]);
272 // Start the interval timer - we will start the counter after
273 // writing the low, and high byte counter values
275 ciab
->ciatalo
= dd
->a_CIAperiod
& 0xff;
276 ciab
->ciatahi
= dd
->a_CIAperiod
>>8;
281 ciab
->ciacra
|= STARTA_OR
;
285 if(Flags
& AHISF_RECORD
)
287 if(!(dd
->a_RecBuffer1
=AllocVec(RECBUFFERSIZE
<<2,MEMF_ANY
)))
289 if(!(dd
->a_RecBuffer2
=AllocVec(RECBUFFERSIZE
<<2,MEMF_ANY
)))
291 if(!(dd
->a_RecMessage
=AllocVec(sizeof(struct AHIRecordMessage
),MEMF_ANY
|MEMF_CLEAR
)))
294 dd
->a_RecMessage
->ahirm_Type
=AHIST_S16S
;
296 dd
->a_Status
|= STATUS_RECORD
;
297 InstallUglyInterruptHack((void (* )())IntCodeTable
[dd
->a_Status
],
298 (struct aura
*)AudioCtrl
->ahiac_DriverData
);
300 // Start the interval timer - we will start the counter after
301 // writing the low, and high byte counter values
303 ciab
->ciatalo
= dd
->a_CIAperiod
& 0xff;
304 ciab
->ciatahi
= dd
->a_CIAperiod
>>8;
309 ciab
->ciacra
|= STARTA_OR
;
316 void __asm __saveds __interrupt
intAHIsub_Update(
317 register __d0 ULONG Flags
,
318 register __a2
struct AHIAudioCtrlDrv
*AudioCtrl
)
323 void __asm __saveds
intAHIsub_Stop(
324 register __d0 ULONG Flags
,
325 register __a2
struct AHIAudioCtrlDrv
*AudioCtrl
)
327 if(Flags
& AHISF_PLAY
)
329 dd
->a_Status
&= ~STATUS_PLAY
;
330 InstallUglyInterruptHack((void (* )())IntCodeTable
[dd
->a_Status
],
331 (struct aura
*)AudioCtrl
->ahiac_DriverData
);
333 FreeVec(dd
->a_MixBuffer1
);
334 dd
->a_MixBuffer1
=NULL
;
335 FreeVec(dd
->a_MixBuffer2
);
336 dd
->a_MixBuffer2
=NULL
;
339 if(Flags
& AHISF_RECORD
)
341 dd
->a_Status
&= ~STATUS_RECORD
;
342 InstallUglyInterruptHack((void (* )())IntCodeTable
[dd
->a_Status
],
343 (struct aura
*)AudioCtrl
->ahiac_DriverData
);
345 FreeVec(dd
->a_RecBuffer1
);
346 dd
->a_RecBuffer1
=NULL
;
347 FreeVec(dd
->a_RecBuffer2
);
348 dd
->a_RecBuffer2
=NULL
;
349 FreeVec(dd
->a_RecMessage
);
350 dd
->a_RecMessage
=NULL
;
353 if(!(dd
->a_Status
& (STATUS_PLAY
| STATUS_RECORD
)))
355 // Stop timer, set 02 pulse count-down mode, set continuous mode
357 ciab
->ciacra
&= STOPA_AND
;
364 IPTR __asm __saveds
intAHIsub_GetAttr(
365 register __d0 ULONG Attribute
,
366 register __d1 LONG Argument
,
367 register __d2 IPTR Default
,
368 register __a1
struct TagItem
*tagList
,
369 register __a2
struct AHIAudioCtrlDrv
*AudioCtrl
)
375 case AHIDB_Frequencies
:
377 case AHIDB_Frequency
: // Index->Frequency
378 return (LONG
) frequency
[Argument
];
379 case AHIDB_Index
: // Frequency->Index
383 if(Argument
<=frequency
[0])
385 if(Argument
>=frequency
[FREQUENCIES
-1])
386 return FREQUENCIES
-1;
387 for(i
=1;i
<FREQUENCIES
;i
++)
388 if(frequency
[i
]>Argument
)
390 if( (Argument
-frequency
[i
-1]) < (frequency
[i
]-Argument
) )
395 return 0; // Will not happen
398 return (IPTR
) "Martin 'Leviticus' Blom";
399 case AHIDB_Copyright
:
400 return (IPTR
) "Public Domain";
402 return (IPTR
) _LibID
;
403 case AHIDB_Annotation
:
404 return (IPTR
) "This driver plays some very nasty tricks with the\n"
405 "Level 6 interrupt, and will not work unless it can\n"
406 "get full control over both timer A and B.";
410 case AHIDB_FullDuplex
:
414 case AHIDB_MaxRecordSamples
:
415 return RECBUFFERSIZE
;
416 case AHIDB_MinMonitorVolume
:
417 case AHIDB_MaxMonitorVolume
:
419 case AHIDB_MinInputGain
:
420 case AHIDB_MaxInputGain
:
421 case AHIDB_MinOutputVolume
:
422 case AHIDB_MaxOutputVolume
:
429 return (IPTR
) "Line";
435 LONG __asm __saveds __interrupt
intAHIsub_HardwareControl(
436 register __d0 ULONG attribute
,
437 register __d1 LONG argument
,
438 register __a2
struct AHIAudioCtrlDrv
*AudioCtrl
)
442 case AHIC_MonitorVolume_Query
:
444 case AHIC_InputGain_Query
:
445 case AHIC_OutputVolume_Query
:
447 case AHIC_Input_Query
:
448 return 0; // There is only one input
449 case AHIC_Output_Query
:
450 return 0; // There is only one output