revert between 56095 -> 55830 in arch
[AROS.git] / workbench / devs / AHI / Drivers / Aura / aura.c
blobd3480898be476694994489c0c1fd21a58dfb2e5d
1 /*
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!
7 **
8 ** We use Timer A of CIA-B.
9 **
13 #include <exec/exec.h>
14 #include <dos/dos.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>
27 #include "aura.h"
29 #define EQ ==
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[] =
59 DummyInt,
60 PlayIntMono,
61 RecordIntMono,
62 PlayAndRecordIntMono,
64 DummyInt,
65 PlayIntStereo,
66 RecordIntStereo,
67 PlayAndRecordIntStereo
71 #define FREQUENCIES 23
72 const static ULONG frequency[FREQUENCIES] =
74 4410, // CD/10
75 4800, // DAT/10
76 5513, // CD/8
77 6000, // DAT/8
78 7350, // CD/6
79 8000, // ยต- and A-Law, DAT/6
80 9600, // DAT/5
81 11025, // CD/4
82 12000, // DAT/4
83 14700, // CD/3
84 16000, // DAT/3
85 17640, // CD/2.5
86 18900,
87 19200, // DAT/2.5
88 22050, // CD/2
89 24000, // DAT/2
90 27429,
91 29400, // CD/1.5
92 32000, // DAT/1.5
93 33075,
94 37800,
95 44100, // CD
96 48000 // DAT
99 struct Library *UtilityBase = NULL;
100 struct Library *AHIsubBase = NULL;
101 struct CIA *ciab = (struct CIA *)0xbfd000;
102 struct CIABase *ciabbase = NULL;
104 BOOL InUse = FALSE;
106 int __saveds __asm __UserLibInit (register __a6 struct Library *libbase)
108 SysBase = *(struct ExecBase **)4;
109 AHIsubBase=libbase;
111 if( ! (UtilityBase=OpenLibrary("utility.library",37)))
113 Alert(AN_Unknown|AG_OpenLib|AO_UtilityLib);
114 return 1;
117 if( ! (ciabbase = OpenResource(CIABNAME)))
119 return 1;
122 return 0;
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!
137 Forbid();
138 if(InUse)
140 Permit();
141 return AHISF_ERROR;
143 InUse = TRUE;
144 Permit();
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;
161 // Allocate Timer A
162 if( ! (AddICRVector((struct Library *) ciabbase,CIAICRB_TA,dd->a_AuraInt)))
164 dd->a_GotTimerA = TRUE;
166 // Allocate Timer B
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
172 Disable();
173 ciab->ciacra &= STOPA_AND;
174 ciab->ciacrb &= STOPB_AND;
175 Enable();
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;
196 return AHISF_ERROR;
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);
208 if(dd->a_GotTimerA)
210 RemICRVector((struct Library *)ciabbase,CIAICRB_TA,dd->a_AuraInt);
212 if(dd->a_GotTimerB)
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;
223 InUse = FALSE;
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)))
238 return AHIE_NOMEM;
240 if(!(dd->a_MixBuffer2=AllocVec(AudioCtrl->ahiac_BuffSize,MEMF_PUBLIC|MEMF_CLEAR|MEMF_ANY)))
241 return AHIE_NOMEM;
243 dd->a_Status |= STATUS_PLAY;
245 switch(AudioCtrl->ahiac_BuffType)
247 case AHIST_M16S:
248 dd->a_Status &= ~STATUS_STEREO;
249 break;
250 case AHIST_S16S:
251 dd->a_Status |= STATUS_STEREO;
252 break;
253 default:
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;
278 // Turn on start bit
280 Disable();
281 ciab->ciacra |= STARTA_OR;
282 Enable();
285 if(Flags & AHISF_RECORD)
287 if(!(dd->a_RecBuffer1=AllocVec(RECBUFFERSIZE<<2,MEMF_ANY)))
288 return AHIE_NOMEM;
289 if(!(dd->a_RecBuffer2=AllocVec(RECBUFFERSIZE<<2,MEMF_ANY)))
290 return AHIE_NOMEM;
291 if(!(dd->a_RecMessage=AllocVec(sizeof(struct AHIRecordMessage),MEMF_ANY|MEMF_CLEAR)))
292 return AHIE_NOMEM;
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;
306 // Turn on start bit
308 Disable();
309 ciab->ciacra |= STARTA_OR;
310 Enable();
313 return AHIE_OK;
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
356 Disable();
357 ciab->ciacra &= STOPA_AND;
358 Enable();
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)
371 switch(Attribute)
373 case AHIDB_Bits:
374 return 12;
375 case AHIDB_Frequencies:
376 return FREQUENCIES;
377 case AHIDB_Frequency: // Index->Frequency
378 return (LONG) frequency[Argument];
379 case AHIDB_Index: // Frequency->Index
381 int i;
383 if(Argument<=frequency[0])
384 return 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) )
391 return i-1;
392 else
393 return i;
395 return 0; // Will not happen
397 case AHIDB_Author:
398 return (IPTR) "Martin 'Leviticus' Blom";
399 case AHIDB_Copyright:
400 return (IPTR) "Public Domain";
401 case AHIDB_Version:
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.";
407 break;
408 case AHIDB_Record:
409 return TRUE;
410 case AHIDB_FullDuplex:
411 return TRUE;
412 case AHIDB_Realtime:
413 return TRUE;
414 case AHIDB_MaxRecordSamples:
415 return RECBUFFERSIZE;
416 case AHIDB_MinMonitorVolume:
417 case AHIDB_MaxMonitorVolume:
418 return 0x00000;
419 case AHIDB_MinInputGain:
420 case AHIDB_MaxInputGain:
421 case AHIDB_MinOutputVolume:
422 case AHIDB_MaxOutputVolume:
423 return 0x10000;
424 case AHIDB_Inputs:
425 case AHIDB_Outputs:
426 return 1;
427 case AHIDB_Input:
428 case AHIDB_Output:
429 return (IPTR) "Line";
430 default:
431 return Default;
435 LONG __asm __saveds __interrupt intAHIsub_HardwareControl(
436 register __d0 ULONG attribute,
437 register __d1 LONG argument,
438 register __a2 struct AHIAudioCtrlDrv *AudioCtrl )
440 switch (attribute)
442 case AHIC_MonitorVolume_Query:
443 return 0;
444 case AHIC_InputGain_Query:
445 case AHIC_OutputVolume_Query:
446 return 0x10000;
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
452 return FALSE;