xref: /haiku/src/add-ons/kernel/drivers/audio/echo/echo.cpp (revision 5412911f7f8ca41340b0f5cb928ed9726322ab44)
1 //------------------------------------------------------------------------------
2 //
3 //  EchoGals/Echo24 BeOS Driver for Echo audio cards
4 //
5 //	Copyright (c) 2003, Jérôme Duval
6 //
7 //	Permission is hereby granted, free of charge, to any person obtaining a
8 //	copy of this software and associated documentation files (the "Software"),
9 //	to deal in the Software without restriction, including without limitation
10 //	the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 //	and/or sell copies of the Software, and to permit persons to whom the
12 //	Software is furnished to do so, subject to the following conditions:
13 //
14 //	The above copyright notice and this permission notice shall be included in
15 //	all copies or substantial portions of the Software.
16 //
17 //	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 //	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 //	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 //	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 //	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 //	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 //	DEALINGS IN THE SOFTWARE.
24 
25 #include <KernelExport.h>
26 #include <Drivers.h>
27 #include <malloc.h>
28 #include <unistd.h>
29 #include "OsSupportBeOS.h"
30 #include "EchoGalsXface.h"
31 #include "C3g.h"
32 #include "CDarla24.h"
33 #include "CDarla.h"
34 #include "CGina.h"
35 #include "CGina24.h"
36 #include "CIndigo.h"
37 #include "CIndigoDJ.h"
38 #include "CIndigoIO.h"
39 #include "CLayla.h"
40 #include "CLayla24.h"
41 #include "CMia.h"
42 #include "CMona.h"
43 #include "echo.h"
44 #include "debug.h"
45 #include "util.h"
46 
47 static char pci_name[] = B_PCI_MODULE_NAME;
48 static pci_module_info	*pci;
49 int32 num_cards;
50 echo_dev cards[NUM_CARDS];
51 int32 num_names;
52 char * names[NUM_CARDS*20+1];
53 
54 extern device_hooks multi_hooks;
55 #ifdef MIDI_SUPPORT
56 extern device_hooks midi_hooks;
57 #endif
58 
59 int32 echo_int(void *arg);
60 status_t init_hardware(void);
61 status_t init_driver(void);
62 static void make_device_names(echo_dev * card);
63 
64 static status_t echo_setup(echo_dev * card);
65 static void echo_shutdown(echo_dev *card);
66 
67 void uninit_driver(void);
68 const char ** publish_devices(void);
69 device_hooks * find_device(const char * name);
70 
71 
72 /* Echo Memory management */
73 
74 echo_mem *
75 echo_mem_new(echo_dev *card, size_t size)
76 {
77 	echo_mem *mem;
78 
79 	if ((mem = (echo_mem *) malloc(sizeof(*mem))) == NULL)
80 		return (NULL);
81 
82 	mem->area = alloc_mem(&mem->phy_base, &mem->log_base, size, "echo buffer");
83 	mem->size = size;
84 	if (mem->area < B_OK) {
85 		free(mem);
86 		return NULL;
87 	}
88 	return mem;
89 }
90 
91 void
92 echo_mem_delete(echo_mem *mem)
93 {
94 	if(mem->area > B_OK)
95 		delete_area(mem->area);
96 	free(mem);
97 }
98 
99 echo_mem *
100 echo_mem_alloc(echo_dev *card, size_t size)
101 {
102 	echo_mem *mem;
103 
104 	mem = echo_mem_new(card, size);
105 	if (mem == NULL)
106 		return (NULL);
107 
108 	LIST_INSERT_HEAD(&(card->mems), mem, next);
109 
110 	return mem;
111 }
112 
113 void
114 echo_mem_free(echo_dev *card, void *ptr)
115 {
116 	echo_mem 		*mem;
117 
118 	LIST_FOREACH(mem, &card->mems, next) {
119 		if (mem->log_base != ptr)
120 			continue;
121 		LIST_REMOVE(mem, next);
122 
123 		echo_mem_delete(mem);
124 		break;
125 	}
126 }
127 
128 /*	Echo stream functions */
129 
130 extern char *      pStatusStrs[ECHOSTATUS_LAST];
131 
132 status_t
133 echo_stream_set_audioparms(echo_stream *stream, uint8 channels,
134      uint8 bitsPerSample, uint32 sample_rate, uint8 index)
135 {
136 	int32 			i;
137 	uint8 			sample_size, frame_size;
138 	ECHOGALS_OPENAUDIOPARAMETERS	open_params;
139 	ECHOGALS_CLOSEAUDIOPARAMETERS  close_params;
140 	ECHOGALS_AUDIOFORMAT			format_params;
141 	ECHOSTATUS status;
142 
143 	LOG(("echo_stream_set_audioparms\n"));
144 
145 	close_params.wPipeIndex = stream->pipe;
146 	status = stream->card->pEG->CloseAudio(&close_params);
147 	if(status!=ECHOSTATUS_OK && status!=ECHOSTATUS_CHANNEL_NOT_OPEN) {
148 		PRINT(("echo_stream_set_audioparms : CloseAudio failed\n"));
149 		PRINT((" status: %s \n", pStatusStrs[status]));
150 		return B_ERROR;
151 	}
152 
153 	open_params.bIsCyclic = TRUE;
154 	open_params.Pipe.nPipe = index;
155 	open_params.Pipe.bIsInput = stream->use == ECHO_USE_RECORD ? TRUE : FALSE;
156 	open_params.Pipe.wInterleave = channels;
157 	open_params.ProcessId = NULL;
158 
159 	status = stream->card->pEG->OpenAudio(&open_params, &stream->pipe);
160 	if(status!=ECHOSTATUS_OK) {
161 		PRINT(("echo_stream_set_audioparms : OpenAudio failed\n"));
162 		PRINT((" status: %s \n", pStatusStrs[status]));
163 		return B_ERROR;
164 	}
165 
166 	//PRINT(("VerifyAudioOpen\n"));
167 	status = stream->card->pEG->VerifyAudioOpen(stream->pipe);
168 	if(status!=ECHOSTATUS_OK) {
169 		PRINT(("echo_stream_set_audioparms : VerifyAudioOpen failed\n"));
170 		PRINT(("  status: %s \n", pStatusStrs[status]));
171 		return B_ERROR;
172 	}
173 
174 	if ((stream->channels == channels) &&
175 		(stream->bitsPerSample == bitsPerSample) &&
176 		(stream->sample_rate == sample_rate))
177 		return B_OK;
178 
179 	format_params.wBitsPerSample = bitsPerSample;
180 	format_params.byDataAreBigEndian = 0;
181 	format_params.byMonoToStereo = 0;
182 	format_params.wDataInterleave = channels == 1 ? 1 : 2;
183 
184 	status = stream->card->pEG->QueryAudioFormat(stream->pipe, &format_params);
185 	if(status!=ECHOSTATUS_OK) {
186 		PRINT(("echo_stream_set_audioparms : bad format when querying\n"));
187 		PRINT(("  status: %s \n", pStatusStrs[status]));
188 		return B_ERROR;
189 	}
190 
191 	status = stream->card->pEG->SetAudioFormat(stream->pipe, &format_params);
192 	if(status!=ECHOSTATUS_OK) {
193 		PRINT(("echo_stream_set_audioparms : bad format when setting\n"));
194 		PRINT(("  status: %s \n", pStatusStrs[status]));
195 		return B_ERROR;
196 	}
197 
198 	/* XXXX : setting sample rate is global in this driver */
199 	status = stream->card->pEG->QueryAudioSampleRate(sample_rate);
200 	if(status!=ECHOSTATUS_OK) {
201 		PRINT(("echo_stream_set_audioparms : bad sample rate when querying\n"));
202 		PRINT(("  status: %s \n", pStatusStrs[status]));
203 		return B_ERROR;
204 	}
205 
206 	/* XXXX : setting sample rate is global in this driver */
207 	status = stream->card->pEG->SetAudioSampleRate(sample_rate);
208 	if(status!=ECHOSTATUS_OK) {
209 		PRINT(("echo_stream_set_audioparms : bad sample rate when setting\n"));
210 		PRINT(("  status: %s \n", pStatusStrs[status]));
211 		return B_ERROR;
212 	}
213 
214 	if(stream->buffer)
215 		echo_mem_free(stream->card, stream->buffer->log_base);
216 
217 	stream->bitsPerSample = bitsPerSample;
218 	stream->sample_rate = sample_rate;
219 	stream->channels = channels;
220 
221 	sample_size = stream->bitsPerSample / 8;
222 	frame_size = sample_size * stream->channels;
223 
224 	stream->buffer = echo_mem_alloc(stream->card, stream->bufframes * frame_size * stream->bufcount);
225 
226 	stream->trigblk = 1;
227 	stream->blkmod = stream->bufcount;
228 	stream->blksize = stream->bufframes * frame_size;
229 
230 	CDaffyDuck *duck = stream->card->pEG->GetDaffyDuck(stream->pipe);
231 	if(duck == NULL) {
232 		PRINT(("echo_stream_set_audioparms : Could not get daffy duck pointer\n"));
233 		return B_ERROR;
234 	}
235 
236 	uint32 dwNumFreeEntries = 0;
237 
238 	for(i=0; i<stream->bufcount; i++) {
239 		duck->AddMapping(((uint32)stream->buffer->phy_base) +
240 			i * stream->blksize, stream->blksize, 0, TRUE, dwNumFreeEntries);
241 	}
242 
243 	duck->Wrap();
244 
245 	if(stream->card->pEG->GetAudioPositionPtr(stream->pipe, stream->position)!=ECHOSTATUS_OK) {
246 		PRINT(("echo_stream_set_audioparms : Could not get audio position ptr\n"));
247 		return B_ERROR;
248 	}
249 
250 	return B_OK;
251 }
252 
253 
254 status_t
255 echo_stream_get_nth_buffer(echo_stream *stream, uint8 chan, uint8 buf,
256 					char** buffer, size_t *stride)
257 {
258 	uint8 			sample_size, frame_size;
259 	LOG(("echo_stream_get_nth_buffer\n"));
260 
261 	sample_size = stream->bitsPerSample / 8;
262 	frame_size = sample_size * stream->channels;
263 
264 	*buffer = (char*)stream->buffer->log_base + (buf * stream->bufframes * frame_size)
265 		+ chan * sample_size;
266 	*stride = frame_size;
267 
268 	return B_OK;
269 }
270 
271 
272 static uint32
273 echo_stream_curaddr(echo_stream *stream)
274 {
275 	uint32 addr = B_LENDIAN_TO_HOST_INT32(*stream->position);
276 //	TRACE(("stream_curaddr %p, phy_base %p\n", addr));
277 	return (addr / stream->blksize) % stream->blkmod;
278 }
279 
280 
281 void
282 echo_stream_start(echo_stream *stream, void (*inth) (void *), void *inthparam)
283 {
284 	LOG(("echo_stream_start\n"));
285 	ECHOSTATUS status;
286 
287 	stream->inth = inth;
288 	stream->inthparam = inthparam;
289 
290 	stream->state |= ECHO_STATE_STARTED;
291 
292 	status = stream->card->pEG->Start(stream->pipe);
293 	if (status!=ECHOSTATUS_OK) {
294 		PRINT(("echo_stream_start : Could not start the pipe %s\n", pStatusStrs[status]));
295 	}
296 }
297 
298 void
299 echo_stream_halt(echo_stream *stream)
300 {
301 	LOG(("echo_stream_halt\n"));
302 	ECHOSTATUS status;
303 
304 	stream->state &= ~ECHO_STATE_STARTED;
305 
306 	status = stream->card->pEG->Stop(stream->pipe);
307 	if (status!=ECHOSTATUS_OK) {
308 		PRINT(("echo_stream_halt : Could not stop the pipe %s\n", pStatusStrs[status]));
309 	}
310 }
311 
312 echo_stream *
313 echo_stream_new(echo_dev *card, uint8 use, uint32 bufframes, uint8 bufcount)
314 {
315 	echo_stream *stream;
316 	cpu_status status;
317 	LOG(("echo_stream_new\n"));
318 
319 	stream = (echo_stream *) malloc(sizeof(echo_stream));
320 	if (stream == NULL)
321 		return (NULL);
322 	stream->card = card;
323 	stream->use = use;
324 	stream->state = !ECHO_STATE_STARTED;
325 	stream->bitsPerSample = 0;
326 	stream->sample_rate = 0;
327 	stream->channels = 0;
328 	stream->bufframes = bufframes;
329 	stream->bufcount = bufcount;
330 	stream->inth = NULL;
331 	stream->inthparam = NULL;
332 	stream->buffer = NULL;
333 	stream->blksize = 0;
334 	stream->trigblk = 0;
335 	stream->blkmod = 0;
336 
337 	stream->pipe = -1;
338 
339 	stream->frames_count = 0;
340 	stream->real_time = 0;
341 	stream->update_needed = false;
342 
343 	status = lock();
344 	LIST_INSERT_HEAD((&card->streams), stream, next);
345 	unlock(status);
346 
347 	return stream;
348 }
349 
350 void
351 echo_stream_delete(echo_stream *stream)
352 {
353 	cpu_status status;
354 	LOG(("echo_stream_delete\n"));
355 
356 	echo_stream_halt(stream);
357 
358 	if(stream->buffer)
359 		echo_mem_free(stream->card, stream->buffer->log_base);
360 
361 	status = lock();
362 	LIST_REMOVE(stream, next);
363 	unlock(status);
364 
365 	free(stream);
366 }
367 
368 
369 /* Echo interrupt */
370 
371 int32 echo_int(void *arg)
372 {
373 	echo_dev	 	*card = (echo_dev*)arg;
374 	BOOL			midiReceived;
375 	ECHOSTATUS 		err;
376 
377 	echo_stream *stream;
378 	uint32       curblk;
379 
380 	err = card->pEG->ServiceIrq(midiReceived);
381 
382 	if(err != ECHOSTATUS_OK) {
383 		return B_UNHANDLED_INTERRUPT;
384 	}
385 
386 #ifdef MIDI_SUPPORT
387 	if (midiReceived)
388 		release_sem(card->midi.midi_ready_sem);
389 #endif
390 
391 	LIST_FOREACH(stream, &card->streams, next) {
392 		if ((stream->state & ECHO_STATE_STARTED) == 0 ||
393 			(stream->inth == NULL))
394 				continue;
395 
396 		curblk = echo_stream_curaddr(stream);
397 		//TRACE(("echo_int stream %p at trigblk %lu at stream->trigblk %lu\n",
398 		//	   stream, curblk, stream->trigblk));
399 		if (curblk == stream->trigblk) {
400 			if(stream->inth)
401 				stream->inth(stream->inthparam);
402 
403 			stream->trigblk++;
404 			stream->trigblk %= stream->blkmod;
405 		}
406 	}
407 
408 	return B_INVOKE_SCHEDULER;
409 }
410 
411 /* dumps card capabilities */
412 void
413 echo_dump_caps(echo_dev *card)
414 {
415 	PECHOGALS_CAPS caps = &card->caps;
416 	PRINT(("name: %s\n", caps->szName));
417 	PRINT(("out pipes: %d, in pipes: %d, out busses: %d, in busses: %d, out midi: %d, in midi: %d\n",
418 		caps->wNumPipesOut, caps->wNumPipesIn, caps->wNumBussesOut, caps->wNumBussesIn, caps->wNumMidiOut, caps->wNumMidiIn));
419 
420 }
421 
422 
423 /* detect presence of our hardware */
424 status_t
425 init_hardware(void)
426 {
427 	int ix=0;
428 	pci_info info;
429 	status_t err = ENODEV;
430 
431 	LOG_CREATE();
432 
433 	PRINT(("init_hardware()\n"));
434 
435 	if (get_module(pci_name, (module_info **)&pci))
436 		return ENOSYS;
437 
438 	while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) {
439 
440 		ushort card_type = info.u.h0.subsystem_id & 0xfff0;
441 
442 		if (info.vendor_id == VENDOR_ID &&
443 			((info.device_id == DEVICE_ID_56301)
444 			|| (info.device_id == DEVICE_ID_56361)) &&
445 			(info.u.h0.subsystem_vendor_id == SUBVENDOR_ID) &&
446 			(
447 #ifdef ECHOGALS_FAMILY
448 			(card_type == DARLA)
449 			|| (card_type == GINA)
450 			|| (card_type == LAYLA)
451 			|| (card_type == DARLA24)
452 #endif
453 #ifdef ECHO24_FAMILY
454 			(card_type == GINA24)
455 			|| (card_type == LAYLA24)
456 			|| (card_type == MONA)
457 			|| (card_type == MIA)
458 #endif
459 #ifdef INDIGO_FAMILY
460 			(card_type == INDIGO)
461 			|| (card_type == INDIGO_IO)
462 			|| (card_type == INDIGO_DJ)
463 #endif
464 #ifdef ECHO3G_FAMILY
465 			(card_type == ECHO3G)
466 #endif
467 			 )) {
468 			err = B_OK;
469 		}
470 		ix++;
471 	}
472 
473 	put_module(pci_name);
474 
475 	if(err!=B_OK) {
476 		PRINT(("no card found\n"));
477 	}
478 
479 	return err;
480 }
481 
482 
483 status_t
484 init_driver(void)
485 {
486 	int ix=0;
487 
488 	pci_info info;
489 	num_cards = 0;
490 
491 	PRINT(("init_driver()\n"));
492 	load_driver_symbols(DRIVER_NAME);
493 
494 	if (get_module(pci_name, (module_info **) &pci))
495 		return ENOSYS;
496 
497 	while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) {
498 		ushort card_type = info.u.h0.subsystem_id & 0xfff0;
499 
500 		if (info.vendor_id == VENDOR_ID &&
501 			((info.device_id == DEVICE_ID_56301)
502 			|| (info.device_id == DEVICE_ID_56361)) &&
503 			(info.u.h0.subsystem_vendor_id == SUBVENDOR_ID) &&
504 			(
505 #ifdef ECHOGALS_FAMILY
506 			(card_type == DARLA)
507 			|| (card_type == GINA)
508 			|| (card_type == LAYLA)
509 			|| (card_type == DARLA24)
510 #endif
511 #ifdef ECHO24_FAMILY
512 			(card_type == GINA24)
513 			|| (card_type == LAYLA24)
514 			|| (card_type == MONA)
515 			|| (card_type == MIA)
516 #endif
517 #ifdef INDIGO_FAMILY
518 			(card_type == INDIGO)
519 			|| (card_type == INDIGO_IO)
520 			|| (card_type == INDIGO_DJ)
521 #endif
522 #ifdef ECHO3G_FAMILY
523 			(card_type == ECHO3G)
524 #endif
525 			)) {
526 
527 			if (num_cards == NUM_CARDS) {
528 				PRINT(("Too many "DRIVER_NAME" cards installed!\n"));
529 				break;
530 			}
531 			memset(&cards[num_cards], 0, sizeof(echo_dev));
532 			cards[num_cards].info = info;
533 			cards[num_cards].type = card_type;
534 			if (echo_setup(&cards[num_cards])) {
535 				PRINT(("Setup of "DRIVER_NAME" %ld failed\n", num_cards+1));
536 			}
537 			else {
538 				num_cards++;
539 			}
540 		}
541 		ix++;
542 	}
543 	if (!num_cards) {
544 		PRINT(("no cards\n"));
545 		put_module(pci_name);
546 		PRINT(("no suitable cards found\n"));
547 		return ENODEV;
548 	}
549 
550 	return B_OK;
551 }
552 
553 
554 static void
555 make_device_names(
556 	echo_dev * card)
557 {
558 
559 #ifdef MIDI_SUPPORT
560 	sprintf(card->midi.name, "midi/"DRIVER_NAME"/%ld", card-cards+1);
561 	names[num_names++] = card->midi.name;
562 #endif
563 	sprintf(card->name, "audio/multi/"DRIVER_NAME"/%ld", card-cards+1);
564 	names[num_names++] = card->name;
565 
566 	names[num_names] = NULL;
567 }
568 
569 
570 static status_t
571 echo_setup(echo_dev * card)
572 {
573 	status_t err = B_OK;
574 	unsigned char cmd;
575 	char *name;
576 
577 	PRINT(("echo_setup(%p)\n", card));
578 
579 	(*pci->write_pci_config)(card->info.bus, card->info.device, card->info.function,
580 		PCI_latency, 1, 0xc0 );
581 
582 	make_device_names(card);
583 
584 	card->bmbar = card->info.u.h0.base_registers[0];
585 	card->irq = card->info.u.h0.interrupt_line;
586 
587 	card->pOSS = new COsSupport(card->info.device_id, card->info.revision);
588 	if(card->pOSS == NULL)
589 		return B_ERROR;
590 
591 	switch (card->type) {
592 #ifdef ECHOGALS_FAMILY
593 		case DARLA:
594 			card->pEG = new CDarla(card->pOSS);
595 			name = "Echo Darla";
596 			break;
597 		case GINA:
598 			card->pEG = new CGina(card->pOSS);
599 			name = "Echo Gina";
600 			break;
601 		case LAYLA:
602 			card->pEG = new CLayla(card->pOSS);
603 			name = "Echo Layla";
604 			break;
605 		case DARLA24:
606 			card->pEG = new CDarla24(card->pOSS);
607 			name = "Echo Darla24";
608 			break;
609 #endif
610 #ifdef ECHO24_FAMILY
611 		case GINA24:
612 			card->pEG = new CGina24(card->pOSS);
613 			name = "Echo Gina24";
614 			break;
615 		case LAYLA24:
616 			card->pEG = new CLayla24(card->pOSS);
617 			name = "Echo Gina24";
618 			break;
619 		case MONA:
620 			card->pEG = new CMona(card->pOSS);
621 			name = "Echo Mona";
622 			break;
623 		case MIA:
624 			card->pEG = new CMia(card->pOSS);
625 			name = "Echo Mia";
626 			break;
627 #endif
628 #ifdef INDIGO_FAMILY
629 		case INDIGO:
630 			card->pEG = new CIndigo(card->pOSS);
631 			name = "Echo Mia";
632 			break;
633 		case INDIGO_IO:
634 			card->pEG = new CIndigoIO(card->pOSS);
635 			name = "Echo Mia";
636 			break;
637 		case INDIGO_DJ:
638 			card->pEG = new CIndigoDJ(card->pOSS);
639 			name = "Echo Mia";
640 			break;
641 #endif
642 #ifdef ECHO3G_FAMILY
643 		case ECHO3G:
644 			card->pEG = new C3g(card->pOSS);
645 			name = "Echo 3g";
646 			break;
647 #endif
648 		default:
649 			PRINT(("card type 0x%x not supported by "DRIVER_NAME"\n", card->type));
650 			delete card->pOSS;
651 			return B_ERROR;
652 	}
653 
654 	if (card->pEG == NULL)
655 		return B_ERROR;
656 
657 	card->area_bmbar = map_mem(&card->log_bmbar, (void *)card->bmbar,
658 		card->info.u.h0.base_register_sizes[0], DRIVER_NAME" bmbar io");
659 	if (card->area_bmbar <= B_OK) {
660 		LOG(("mapping of bmbar io failed, error = %#x\n",card->area_bmbar));
661 		return B_ERROR;
662 	}
663 	LOG(("mapping of bmbar: area %#x, phys %#x, log %#x\n", card->area_bmbar, card->bmbar, card->log_bmbar));
664 
665 	cmd = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, PCI_command, 2);
666 	PRINT(("PCI command before: %x\n", cmd));
667 	(*pci->write_pci_config)(card->info.bus, card->info.device, card->info.function, PCI_command, 2, cmd | PCI_command_io);
668 	cmd = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, PCI_command, 2);
669 	PRINT(("PCI command after: %x\n", cmd));
670 
671 	card->pEG->AssignResources(card->log_bmbar, name);
672 
673 	ECHOSTATUS status;
674 	status = card->pEG->InitHw();
675 	if(status != ECHOSTATUS_OK)
676 		return B_ERROR;
677 
678 	card->pEG->GetCapabilities(&card->caps);
679 
680 	/* Init streams list */
681 	LIST_INIT(&(card->streams));
682 
683 	/* Init mems list */
684 	LIST_INIT(&(card->mems));
685 
686 #ifdef MIDI_SUPPORT
687 	card->midi.midi_ready_sem = create_sem(0, "midi sem");
688 #endif
689 
690 	PRINT(("installing interrupt : %x\n", card->irq));
691 	install_io_interrupt_handler(card->irq, echo_int, card, 0);
692 
693 	PRINT(("echo_setup done\n"));
694 
695 	echo_dump_caps(card);
696 
697 #ifdef ECHO3G_FAMILY
698 	if (card->type == ECHO3G) {
699 		strncpy(card->caps.szName, ((C3g*)card->pEG)->Get3gBoxName(), ECHO_MAXNAMELEN);
700 	}
701 #endif
702 
703 	status = card->pEG->OpenMixer(card->mixer);
704 	if (status != ECHOSTATUS_OK)
705 		return B_ERROR;
706 
707 	return err;
708 }
709 
710 static void
711 echo_shutdown(echo_dev *card)
712 {
713 	ECHOSTATUS status;
714 
715 	PRINT(("shutdown(%p)\n", card));
716 	status = card->pEG->CloseMixer(card->mixer);
717 	if (status != ECHOSTATUS_OK)
718 		PRINT(("echo_shutdown: error when CloseMixer\n"));
719 
720 	remove_io_interrupt_handler(card->irq, echo_int, card);
721 
722 #ifdef MIDI_SUPPORT
723         delete_sem(card->midi.midi_ready_sem);
724 #endif
725 
726 	delete card->pEG;
727 	delete card->pOSS;
728 
729 	delete_area(card->area_bmbar);
730 }
731 
732 
733 
734 void
735 uninit_driver(void)
736 {
737 	int ix, cnt = num_cards;
738 	num_cards = 0;
739 
740 	PRINT(("uninit_driver()\n"));
741 
742 	for (ix=0; ix<cnt; ix++) {
743 		echo_shutdown(&cards[ix]);
744 	}
745 	memset(&cards, 0, sizeof(cards));
746 	put_module(pci_name);
747 }
748 
749 
750 const char **
751 publish_devices(void)
752 {
753 	int ix = 0;
754 	PRINT(("publish_devices()\n"));
755 
756 	for (ix=0; names[ix]; ix++) {
757 		PRINT(("publish %s\n", names[ix]));
758 	}
759 	return (const char **)names;
760 }
761 
762 
763 device_hooks *
764 find_device(const char * name)
765 {
766 	int ix;
767 
768 	PRINT(("find_device(%s)\n", name));
769 
770 	for (ix=0; ix<num_cards; ix++) {
771 #ifdef MIDI_SUPPORT
772 		if (!strcmp(cards[ix].midi.name, name)) {
773 			return &midi_hooks;
774 		}
775 #endif
776 		if (!strcmp(cards[ix].name, name)) {
777 			return &multi_hooks;
778 		}
779 	}
780 	PRINT(("find_device(%s) failed\n", name));
781 	return NULL;
782 }
783 
784 int32	api_version = B_CUR_DRIVER_API_VERSION;
785