revert between 56095 -> 55830 in arch
[AROS.git] / arch / m68k-amiga / devs / audio / audio.c
blob26a7c007c5d7f8a094bb10c03ea24021b5d9d80f
1 /*
2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Paula audio.device
6 Lang: English
7 */
9 /* TODO:
10 - Channel stealing not yet supported
11 - ADCMD_LOCK never returns
12 - DMA wait missing (CMD_STOP and immediate CMD_START may cause glitches)
13 - and lots bugs
15 NOTES:
16 - normally unused io->ioa_Request.io_Unit bits 4 to 7 are used to keep
17 track of pending SYNCCYCLE-style requests (all cleared = done)
21 #define DEBUG 0
22 #include <aros/debug.h>
24 #include <exec/resident.h>
25 #include <exec/errors.h>
26 #include <exec/memory.h>
27 #include <exec/lists.h>
28 #include <exec/alerts.h>
29 #include <exec/tasks.h>
30 #include <exec/interrupts.h>
31 #include <devices/audio.h>
32 #include <hardware/intbits.h>
33 #include <hardware/custom.h>
34 #include <clib/alib_protos.h>
35 #include <aros/symbolsets.h>
37 #include <proto/exec.h>
39 #include "audio_intern.h"
41 #include LC_LIBDEFS_FILE
43 static const UBYTE masktoch[16] = { 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 };
45 static BOOL isplaying(struct AudioBase *ab, struct IOAudio *io, UBYTE ch)
47 return (struct IOAudio*)ab->writelist[ch].mlh_Head->mln_Succ == io && !(ab->stopmask & (1 << ch));
50 struct IOAudio *getnextwrite(struct AudioBase *ab, UBYTE ch, BOOL second)
52 struct IOAudio *io;
53 if (ab->writelist[ch].mlh_Head->mln_Succ == NULL)
54 return NULL;
55 io = (struct IOAudio*)ab->writelist[ch].mlh_Head;
56 if (!second)
57 return io;
58 if(ab->writelist[ch].mlh_Head->mln_Succ->mln_Succ == NULL)
59 return NULL;
60 return (struct IOAudio*)io->ioa_Request.io_Message.mn_Node.ln_Succ;
63 #define HEADER \
64 UBYTE mask = (UBYTE)(ULONG)io->ioa_Request.io_Unit; \
65 WORD key = io->ioa_AllocKey; \
66 { io->ioa_Request.io_Unit = NULL; }
68 static void abort_io(struct AudioBase *ab, struct IOAudio *io)
70 UBYTE ch = masktoch[(UBYTE)(ULONG)io->ioa_Request.io_Unit & CH_MASK];
72 if (!io)
73 return;
74 if (isplaying(ab, io, ch)) {
75 D(bug("audio: ch %d aborted, io %p\n", ch, io));
76 audiohw_stop(ab, 1 << ch);
78 D(bug("abort_io(%p)\n", io));
79 REMOVE(io);
80 io->ioa_Request.io_Error = IOERR_ABORTED;
81 ReplyMsg(&io->ioa_Request.io_Message);
84 static void abort_ch(struct AudioBase *ab, UBYTE ch)
86 struct IOAudio *io;
88 Disable();
89 while ((io = (struct IOAudio*)GetHead(&ab->writelist[ch])))
90 abort_io(ab, io);
91 Enable();
94 static void abort_waitcycles(struct AudioBase *ab, UBYTE mask)
96 struct IOAudio *io, *next;
97 Disable();
98 ForeachNodeSafe(&ab->misclist, io, next) {
99 UWORD cmd = io->ioa_Request.io_Command;
100 UBYTE cmask = (UBYTE)(ULONG)io->ioa_Request.io_Unit;
101 if (cmd != ADCMD_FINISH && cmd != ADCMD_PERVOL && cmd != ADCMD_WAITCYCLE)
102 continue;
103 if (!(cmask & mask))
104 continue;
105 abort_io(ab, io);
107 Enable();
110 static void allocchannels(struct AudioBase *ab, struct IOAudio *io, UBYTE mask, BYTE pri)
112 UBYTE ch;
113 WORD key;
115 key = io->ioa_AllocKey;
116 while (!key)
117 key = ++ab->keygen;
118 for (ch = 0; ch < NR_CH; ch++) {
119 if (mask & (1 << ch)) {
120 ab->pri[ch] = pri;
121 ab->key[ch] = key;
124 io->ioa_AllocKey = key;
125 io->ioa_Request.io_Unit = (struct Unit*)(ULONG)mask;
126 audiohw_reset(ab, mask);
127 D(bug("audio: allocmask %02x, pri %d, %04x (%04x %04x %04x %04x)\n",
128 mask, pri, io->ioa_AllocKey, ab->key[0], ab->key[1], ab->key[2], ab->key[3]));
131 static BOOL allocaudio(struct AudioBase *ab, struct IOAudio *io)
133 UBYTE i, ch;
134 UBYTE freech;
136 io->ioa_Request.io_Error = 0;
137 freech = 0;
138 for (ch = 0; ch < NR_CH; ch++) {
139 if (ab->key[ch] == 0)
140 freech |= 1 << ch;
142 for (i = 0; i < io->ioa_Length && i < 16; i++) {
143 UBYTE mask = io->ioa_Data[i];
144 D(bug("%d: allocation mask %02x & %02x\n", i, mask, freech));
145 if (mask == 0 || (mask & freech) == mask) {
146 allocchannels(ab, io, mask, io->ioa_Request.io_Message.mn_Node.ln_Pri);
147 return TRUE;
150 io->ioa_Request.io_Error = ADIOERR_ALLOCFAILED;
151 return FALSE;
154 static void setunit(struct IOAudio *io, UBYTE num)
156 ULONG unit = (ULONG)io->ioa_Request.io_Unit;
157 unit |= 1 << num;
158 io->ioa_Request.io_Unit = (struct Unit*)unit;
161 static BOOL ADCMD_ALLOCATE_f(struct AudioBase *ab, struct IOAudio *io)
163 D(HEADER);
164 D(bug("ADCMD_ALLOCATE %02x %04x\n", mask, key));
165 allocaudio(ab, io);
166 if (io->ioa_Request.io_Error == 0)
167 return TRUE;
168 if (io->ioa_Request.io_Flags & ADIOF_NOWAIT)
169 return TRUE;
170 AddTail((struct List*)&ab->misclist, &io->ioa_Request.io_Message.mn_Node);
171 return FALSE;
174 static BOOL ADCMD_FREE_f(struct AudioBase *ab, struct IOAudio *io)
176 HEADER
177 UBYTE ch;
178 struct IOAudio *node, *next;
180 D(bug("ADCMD_FREE %02x %04x\n", mask, key));
181 for (ch = 0; ch < NR_CH; ch++) {
182 if ((mask & (1 << ch)) && key == ab->key[ch]) {
183 abort_waitcycles(ab, 1 << ch);
184 ab->key[ch] = 0;
185 audiohw_reset(ab, 1 << ch);
186 setunit(io, ch);
189 if (!io->ioa_Request.io_Unit) {
190 io->ioa_Request.io_Error = ADIOERR_NOALLOCATION;
191 return TRUE;
193 ForeachNodeSafe(&ab->misclist, node, next) {
194 if (node->ioa_Request.io_Command == ADCMD_ALLOCATE && allocaudio(ab, node)) {
195 REMOVE(node);
196 ReplyMsg(&io->ioa_Request.io_Message);
199 return TRUE;
202 static BOOL ADCMD_LOCK_f(struct AudioBase *ab, struct IOAudio *io)
204 HEADER
205 UBYTE ch;
207 D(bug("ADCMD_LOCK %02x %04x\n", mask, key));
208 for (ch = 0; ch < NR_CH; ch++) {
209 if ((mask & (1 << ch)) && key == ab->key[ch])
210 setunit(io, ch);
212 if (!io->ioa_Request.io_Unit) {
213 io->ioa_Request.io_Error = ADIOERR_NOALLOCATION;
214 return TRUE;
216 AddTail((struct List*)&ab->misclist, &io->ioa_Request.io_Message.mn_Node);
217 return FALSE;
220 static BOOL ADCMD_SETPREC_f(struct AudioBase *ab, struct IOAudio *io)
222 HEADER
223 UBYTE ch;
225 D(bug("ADCMD_SETPREC %02x %04x\n", mask, key));
226 for (ch = 0; ch < NR_CH; ch++) {
227 if ((mask & (1 << ch)) && key == ab->key[ch]) {
228 ab->pri[ch] = io->ioa_Request.io_Message.mn_Node.ln_Pri;
229 setunit(io, ch);
232 if (!io->ioa_Request.io_Unit)
233 io->ioa_Request.io_Error = ADIOERR_NOALLOCATION;
234 return TRUE;
237 static BOOL ADCMD_NULL_f(struct AudioBase *ab, struct IOAudio *io)
239 HEADER
240 UBYTE ch;
242 for (ch = 0; ch < NR_CH; ch++) {
243 if ((mask & (1 << ch)) && key == ab->key[ch])
244 setunit(io, ch);
246 if (!io->ioa_Request.io_Unit)
247 io->ioa_Request.io_Error = ADIOERR_NOALLOCATION;
248 return TRUE;
251 static BOOL ADCMD_FLUSH_f(struct AudioBase *ab, struct IOAudio *io)
253 HEADER
254 UBYTE ch;
256 D(bug("ADCMD_FLUSH %02x %04x\n", mask, key));
257 for (ch = 0; ch < NR_CH; ch++) {
258 if ((mask & (1 << ch)) && key == ab->key[ch]) {
259 abort_ch(ab, ch);
260 setunit(io, ch);
263 if (!io->ioa_Request.io_Unit)
264 io->ioa_Request.io_Error = ADIOERR_NOALLOCATION;
265 return TRUE;
268 static BOOL ADCMD_FINISH_f(struct AudioBase *ab, struct IOAudio *io)
270 HEADER
271 UBYTE ch;
272 BOOL ret, added;
274 D(bug("ADCMD_FINISH %02x %04x\n", mask, key));
275 ret = TRUE;
276 added = FALSE;
277 Disable();
278 for (ch = 0; ch < NR_CH; ch++) {
279 if ((mask & (1 << ch)) && key == ab->key[ch]) {
280 setunit(io, ch);
281 if (getnextwrite(ab, ch, FALSE) && (io->ioa_Request.io_Flags & ADIOF_SYNCCYCLE)) {
282 if (!added) {
283 AddTail((struct List*)&ab->misclist, &io->ioa_Request.io_Message.mn_Node);
284 setunit(io, ch + NR_CH);
285 ret = FALSE;
286 added = TRUE;
288 } else {
289 abort_ch(ab, ch);
293 Enable();
294 if (!io->ioa_Request.io_Unit)
295 io->ioa_Request.io_Error = ADIOERR_NOALLOCATION;
296 return ret;
299 static BOOL ADCMD_RESET_f(struct AudioBase *ab, struct IOAudio *io)
301 HEADER
302 UBYTE ch;
304 D(bug("ADCMD_RESET %02x %04x\n", mask, key));
305 for (ch = 0; ch < NR_CH; ch++) {
306 if ((mask & (1 << ch)) && key == ab->key[ch]) {
307 ADCMD_FLUSH_f(ab, io);
308 audiohw_reset(ab, 1 << ch);
309 setunit(io, ch);
310 ab->stopmask &= ~(1 << ch);
313 if (!io->ioa_Request.io_Unit)
314 io->ioa_Request.io_Error = ADIOERR_NOALLOCATION;
315 return TRUE;
318 static BOOL ADCMD_START_f(struct AudioBase *ab, struct IOAudio *io)
320 HEADER
321 UBYTE ch;
322 UWORD newmask;
324 D(bug("ADCMD_START %02x %04x\n", mask, key));
325 newmask = 0;
326 for (ch = 0; ch < NR_CH; ch++) {
327 if ((mask & (1 << ch)) && key == ab->key[ch] && (ab->stopmask & (1 << ch))) {
328 ab->stopmask &= ~(1 << ch);
329 newmask |= 1 << ch;
330 setunit(io, ch);
333 if (!io->ioa_Request.io_Unit)
334 io->ioa_Request.io_Error = ADIOERR_NOALLOCATION;
335 else
336 audiohw_start(ab, newmask);
337 return TRUE;
340 static BOOL ADCMD_STOP_f(struct AudioBase *ab, struct IOAudio *io)
342 HEADER
343 UBYTE ch;
344 UWORD newmask;
346 D(bug("ADCMD_STOP %02x %04x\n", mask, key));
347 newmask = 0;
348 for (ch = 0; ch < NR_CH; ch++) {
349 if ((mask & (1 << ch)) && key == ab->key[ch]) {
350 ab->stopmask |= 1 << ch;
351 newmask |= 1 << ch;
352 setunit(io, ch);
355 if (!io->ioa_Request.io_Unit) {
356 io->ioa_Request.io_Error = ADIOERR_NOALLOCATION;
357 } else {
358 audiohw_stop(ab, newmask);
359 abort_waitcycles(ab, newmask);
361 return TRUE;
364 static BOOL ADCMD_PERVOL_f(struct AudioBase *ab, struct IOAudio *io)
366 HEADER
367 UBYTE ch;
368 BOOL ret, added;
370 D(bug("ADCMD_PERVOL %02x %04x\n", mask, key));
371 ret = TRUE;
372 added = FALSE;
373 Disable();
374 for (ch = 0; ch < NR_CH; ch++) {
375 if ((mask & (1 << ch)) && key == ab->key[ch]) {
376 setunit(io, ch);
377 if (getnextwrite(ab, ch, FALSE) && (io->ioa_Request.io_Flags & ADIOF_SYNCCYCLE)) {
378 if (!added) {
379 AddTail((struct List*)&ab->misclist, &io->ioa_Request.io_Message.mn_Node);
380 setunit(io, ch + NR_CH);
381 ret = FALSE;
382 added = TRUE;
384 } else {
385 UBYTE flags = io->ioa_Request.io_Flags;
386 io->ioa_Request.io_Flags |= ADIOF_PERVOL;
387 audiohw_preparepervol(ab, io, ch);
388 io->ioa_Request.io_Flags = flags;
392 Enable();
393 if (!io->ioa_Request.io_Unit)
394 io->ioa_Request.io_Error = ADIOERR_NOALLOCATION;
395 return ret;
398 static BOOL ADCMD_WRITE_f(struct AudioBase *ab, struct IOAudio *io)
400 HEADER
401 UBYTE ch;
402 UWORD newmask;
403 BOOL firstempty, secondempty;
404 BOOL ret = TRUE;
406 D(bug("ADCMD_WRITE %02x %04x\n", mask, key));
407 Disable();
408 newmask = 0;
409 firstempty = secondempty = FALSE;
410 for (ch = 0; ch < NR_CH; ch++) {
411 if ((mask & (1 << ch)) && key == ab->key[ch]) {
412 setunit(io, ch);
413 firstempty = getnextwrite(ab, ch, FALSE) == NULL;
414 secondempty = getnextwrite(ab, ch, TRUE) == NULL;
415 AddTail((struct List*)&ab->writelist[ch], &io->ioa_Request.io_Message.mn_Node);
416 newmask = 1 << ch;
417 break;
420 D(bug("unit=%08x 1=%d 2=%d newmask=%02x stopmask=%02x cycles=%d\n",
421 io->ioa_Request.io_Unit, firstempty, secondempty, newmask,
422 ab->stopmask, io->ioa_Cycles));
423 if (!io->ioa_Request.io_Unit) {
424 io->ioa_Request.io_Error = ADIOERR_NOALLOCATION;
425 } else {
426 if (!(ab->stopmask & newmask)) {
427 if (firstempty) {
428 audiohw_start(ab, newmask);
429 } else if (secondempty) {
430 audiohw_prepareptlen(ab, getnextwrite(ab, ch, TRUE), ch);
433 ret = FALSE;
435 Enable();
436 return ret;
439 static BOOL ADCMD_READ_f(struct AudioBase *ab, struct IOAudio *io)
441 HEADER
442 UBYTE ch;
444 D(bug("ADCMD_READ %02x %04x\n", mask, key));
445 for (ch = 0; ch < NR_CH; ch++) {
446 if ((mask & (1 << ch)) && key == ab->key[ch]) {
447 setunit(io, ch);
448 if (!(ab->stopmask & (1 << ch)))
449 io->ioa_Data = (APTR)getnextwrite(ab, ch, FALSE);
450 break;
453 if (!io->ioa_Request.io_Unit)
454 io->ioa_Request.io_Error = ADIOERR_NOALLOCATION;
455 return TRUE;
458 static BOOL ADCMD_WAITCYCLE_f(struct AudioBase *ab, struct IOAudio *io)
460 HEADER
461 UBYTE ch;
462 BOOL ret;
464 D(bug("ADCMD_WAITCYCLE %02x %04x\n", mask, key));
465 ret = TRUE;
466 for (ch = 0; ch < NR_CH; ch++) {
467 if ((mask & (1 << ch)) && key == ab->key[ch]) {
468 setunit(io, ch);
469 if (!(ab->stopmask & (1 << ch)) && getnextwrite(ab, ch, FALSE)) {
470 AddTail((struct List*)&ab->misclist, &io->ioa_Request.io_Message.mn_Node);
471 setunit(io, ch + NR_CH);
472 ret = FALSE;
474 break;
477 if (!io->ioa_Request.io_Unit)
478 io->ioa_Request.io_Error = ADIOERR_NOALLOCATION;
479 return ret;
482 static BOOL processcommand(struct AudioBase *ab, struct IOAudio *io)
485 io->ioa_Request.io_Error = 0;
486 switch(io->ioa_Request.io_Command)
488 case CMD_CLEAR:
489 return ADCMD_NULL_f(ab, io);
490 case CMD_FLUSH:
491 return ADCMD_FLUSH_f(ab, io);
492 case CMD_RESET:
493 return ADCMD_RESET_f(ab, io);
494 case CMD_UPDATE:
495 return ADCMD_NULL_f(ab, io);
496 case CMD_START:
497 return ADCMD_START_f(ab, io);
498 case CMD_STOP:
499 return ADCMD_STOP_f(ab, io);
500 case CMD_READ:
501 return ADCMD_READ_f(ab, io);
502 case CMD_WRITE:
503 return ADCMD_WRITE_f(ab, io);
505 case ADCMD_ALLOCATE:
506 return ADCMD_ALLOCATE_f(ab, io);
507 case ADCMD_FREE:
508 return ADCMD_FREE_f(ab, io);
509 case ADCMD_LOCK:
510 return ADCMD_LOCK_f(ab, io);
511 case ADCMD_SETPREC:
512 return ADCMD_SETPREC_f(ab, io);
513 case ADCMD_WAITCYCLE:
514 return ADCMD_WAITCYCLE_f(ab, io);
515 case ADCMD_PERVOL:
516 return ADCMD_PERVOL_f(ab, io);
517 case ADCMD_FINISH:
518 return ADCMD_FINISH_f(ab, io);
519 default:
520 io->ioa_Request.io_Error = IOERR_NOCMD;
522 return TRUE;
525 AROS_LH1(void, beginio,
526 AROS_LHA(struct IOAudio *, io, A1),
527 struct AudioBase *, AudioBase, 5, Audio)
529 AROS_LIBFUNC_INIT
531 D(bug("audio beginio %p:%d\n", io, io->ioa_Request.io_Command));
533 io->ioa_Request.io_Message.mn_Node.ln_Type = NT_MESSAGE;
534 if (processcommand(AudioBase, io)) {
535 /* TRUE = finished immediately */
536 if (!(io->ioa_Request.io_Flags & IOF_QUICK))
537 ReplyMsg(&io->ioa_Request.io_Message);
538 } else {
539 /* FALSE = async */
540 io->ioa_Request.io_Flags &= ~IOF_QUICK;
543 AROS_LIBFUNC_EXIT
546 AROS_LH1(LONG, abortio,
547 AROS_LHA(struct IOAudio *, io, A1),
548 struct AudioBase *, AudioBase, 6, Audio)
550 AROS_LIBFUNC_INIT
552 struct IOAudio *node;
553 UBYTE ch;
555 D(bug("audio abortio %p\n", io));
557 io->ioa_Request.io_Error = 0;
559 Disable();
560 for (ch = 0; ch < NR_CH; ch++) {
561 ForeachNode(&AudioBase->writelist[ch], node) {
562 if (node == io) {
563 abort_io(AudioBase, io);
564 break;
568 ForeachNode(&AudioBase->misclist, node) {
569 if (node == io) {
570 abort_io(AudioBase, io);
571 break;
574 Enable();
576 return 0;
578 AROS_LIBFUNC_EXIT
582 static int GM_UNIQUENAME(init)(LIBBASETYPEPTR AudioBase)
584 NEWLIST(&AudioBase->writelist[0]);
585 NEWLIST(&AudioBase->writelist[1]);
586 NEWLIST(&AudioBase->writelist[2]);
587 NEWLIST(&AudioBase->writelist[3]);
588 NEWLIST(&AudioBase->misclist);
589 AudioBase->zerosample = AllocMem(2, MEMF_CHIP | MEMF_CLEAR);
590 AudioBase->keygen = 0x5a00;
591 audiohw_init(AudioBase);
592 return TRUE;
595 static int GM_UNIQUENAME(open)
597 LIBBASETYPEPTR AudioBase,
598 struct IOAudio *io,
599 ULONG unitnum,
600 ULONG flags
603 D(bug("Audio open: pri=%d key=%04x data=%p len=%d\n",
604 io->ioa_Request.io_Message.mn_Node.ln_Pri,
605 io->ioa_AllocKey,
606 io->ioa_Data,
607 io->ioa_Length));
609 io->ioa_Request.io_Error = 0;
610 io->ioa_Request.io_Unit = (struct Unit*) NULL;
612 if (io->ioa_Length) {
613 allocaudio(AudioBase, io);
614 D(bug("new key = %04x\n", io->ioa_AllocKey));
615 io->ioa_Request.io_Device = (struct Device *)AudioBase;
618 return io->ioa_Request.io_Error == 0;
621 static int GM_UNIQUENAME(close)
623 LIBBASETYPEPTR AudioBase,
624 struct IOAudio *io
627 UBYTE ch;
629 for (ch = 0; ch < NR_CH; ch++) {
630 if (io->ioa_AllocKey == AudioBase->key[ch]) {
631 abort_ch(AudioBase, ch);
632 AudioBase->key[ch] = 0;
633 audiohw_reset(AudioBase, 1 << ch);
636 io->ioa_Request.io_Unit = NULL;
637 io->ioa_Request.io_Device = (struct Device*)-1;
639 D(bug("Audio close: key = %04x\n", io->ioa_AllocKey));
641 return TRUE;
644 ADD2INITLIB(GM_UNIQUENAME(init), 0)
645 ADD2OPENDEV(GM_UNIQUENAME(open), 0)
646 ADD2CLOSEDEV(GM_UNIQUENAME(close), 0)