xref: /haiku/src/add-ons/kernel/drivers/audio/ac97/auvia/auvia.c (revision 344ded80d400028c8f561b4b876257b94c12db4a)
1 /*
2  * Auvia BeOS Driver for Via VT82xx Southbridge audio
3  *
4  * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr)
5 
6  * This code is derived from software contributed to The NetBSD Foundation
7  * by Tyler C. Sarna
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed by the NetBSD
20  *	Foundation, Inc. and its contributors.
21  * 4. Neither the name of The NetBSD Foundation nor the names of its
22  *    contributors may be used to endorse or promote products derived
23  *    from this software without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
26  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
29  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  * POSSIBILITY OF SUCH DAMAGE.
36  */
37 
38 #include <KernelExport.h>
39 #include <PCI.h>
40 #include <string.h>
41 #include <stdio.h>
42 #include "auvia.h"
43 #include "debug.h"
44 #include "config.h"
45 #include "util.h"
46 #include "io.h"
47 #include <fcntl.h>
48 #include <unistd.h>
49 #include "ac97.h"
50 
51 status_t init_hardware(void);
52 status_t init_driver(void);
53 void uninit_driver(void);
54 const char ** publish_devices(void);
55 device_hooks * find_device(const char *);
56 
57 pci_module_info	*pci;
58 
59 int32 num_cards;
60 auvia_dev cards[NUM_CARDS];
61 int32 num_names;
62 char * names[NUM_CARDS*20+1];
63 
64 extern device_hooks multi_hooks;
65 
66 /* Auvia Memory management */
67 
68 static auvia_mem *
69 auvia_mem_new(auvia_dev *card, size_t size)
70 {
71 	auvia_mem *mem;
72 
73 	if ((mem = malloc(sizeof(*mem))) == NULL)
74 		return (NULL);
75 
76 	mem->area = alloc_mem(&mem->phy_base, &mem->log_base, size, "auvia buffer", true);
77 	mem->size = size;
78 	if (mem->area < B_OK) {
79 		free(mem);
80 		return NULL;
81 	}
82 	return mem;
83 }
84 
85 
86 static void
87 auvia_mem_delete(auvia_mem *mem)
88 {
89 	if(mem->area > B_OK)
90 		delete_area(mem->area);
91 	free(mem);
92 }
93 
94 
95 static void *
96 auvia_mem_alloc(auvia_dev *card, size_t size)
97 {
98 	auvia_mem *mem;
99 
100 	mem = auvia_mem_new(card, size);
101 	if (mem == NULL)
102 		return (NULL);
103 
104 	LIST_INSERT_HEAD(&(card->mems), mem, next);
105 
106 	return mem;
107 }
108 
109 
110 static void
111 auvia_mem_free(auvia_dev *card, void *ptr)
112 {
113 	auvia_mem 		*mem;
114 
115 	LIST_FOREACH(mem, &card->mems, next) {
116 		if (mem->log_base != ptr)
117 			continue;
118 		LIST_REMOVE(mem, next);
119 
120 		auvia_mem_delete(mem);
121 		break;
122 	}
123 }
124 
125 /*	Auvia stream functions */
126 
127 status_t
128 auvia_stream_set_audioparms(auvia_stream *stream, uint8 channels,
129      uint8 b16, uint32 sample_rate)
130 {
131 	uint8 			sample_size, frame_size;
132 	LOG(("auvia_stream_set_audioparms\n"));
133 
134 	if ((stream->channels == channels) &&
135 		(stream->b16 == b16) &&
136 		(stream->sample_rate == sample_rate))
137 		return B_OK;
138 
139 	if(stream->buffer)
140 		auvia_mem_free(stream->card, stream->buffer->log_base);
141 
142 	stream->b16 = b16;
143 	stream->sample_rate = sample_rate;
144 	stream->channels = channels;
145 
146 	sample_size = stream->b16 + 1;
147 	frame_size = sample_size * stream->channels;
148 
149 	stream->buffer = auvia_mem_alloc(stream->card, stream->bufframes
150 		* frame_size * stream->bufcount);
151 
152 	stream->trigblk = 0;	/* This shouldn't be needed */
153 	stream->blkmod = stream->bufcount;
154 	stream->blksize = stream->bufframes * frame_size;
155 
156 	return B_OK;
157 }
158 
159 
160 status_t
161 auvia_stream_commit_parms(auvia_stream *stream)
162 {
163 	int i;
164 	uint32* page;
165 	uint32 value;
166 	LOG(("auvia_stream_commit_parms\n"));
167 
168 	page = stream->dmaops_log_base;
169 
170 	for(i = 0; i < stream->bufcount; i++) {
171 		page[2 * i] = stream->buffer->phy_base + i * stream->blksize;
172 		page[2 * i + 1] = AUVIA_DMAOP_FLAG | stream->blksize;
173 	}
174 
175 	page[2 * stream->bufcount - 1] &= ~AUVIA_DMAOP_FLAG;
176 	page[2 * stream->bufcount - 1] |= AUVIA_DMAOP_EOL;
177 
178 	auvia_reg_write_32(&stream->card->config, stream->base + AUVIA_RP_DMAOPS_BASE,
179 		stream->dmaops_phy_base);
180 
181 	if(stream->use & AUVIA_USE_RECORD)
182 		auvia_codec_write(&stream->card->config, AC97_PCM_L_R_ADC_RATE,
183 			(uint16)stream->sample_rate);
184 	else
185 		auvia_codec_write(&stream->card->config, AC97_PCM_FRONT_DAC_RATE,
186 			(uint16)stream->sample_rate);
187 
188 	if(IS_8233(&stream->card->config)) {
189 		if(stream->base != AUVIA_8233_MP_BASE) {
190 			value = auvia_reg_read_32(&stream->card->config, stream->base
191 				+ AUVIA_8233_RP_RATEFMT);
192 			value &= ~(AUVIA_8233_RATEFMT_48K | AUVIA_8233_RATEFMT_STEREO
193 				| AUVIA_8233_RATEFMT_16BIT);
194 			if(stream->use & AUVIA_USE_PLAY)
195 				value |= AUVIA_8233_RATEFMT_48K * (stream->sample_rate / 20)
196 					/ (48000 / 20);
197 			value |= (stream->channels == 2 ? AUVIA_8233_RATEFMT_STEREO : 0)
198 				| (stream->b16 ? AUVIA_8233_RATEFMT_16BIT : 0);
199 			auvia_reg_write_32(&stream->card->config, stream->base
200 				+ AUVIA_8233_RP_RATEFMT, value);
201 		} else {
202 			static const uint32 slottab[7] = {0, 0xff000011, 0xff000021,
203 				0xff000521, 0xff004321, 0xff054321, 0xff654321};
204 			value = (stream->b16 ? AUVIA_8233_MP_FORMAT_16BIT : AUVIA_8233_MP_FORMAT_8BIT)
205 				| ((stream->channels << 4) & AUVIA_8233_MP_FORMAT_CHANNEL_MASK);
206 			auvia_reg_write_8(&stream->card->config, stream->base
207 				+ AUVIA_8233_OFF_MP_FORMAT, value);
208 			auvia_reg_write_32(&stream->card->config, stream->base
209 				+ AUVIA_8233_OFF_MP_STOP, slottab[stream->channels]);
210 		}
211 	}
212 	//auvia_codec_write(&stream->card->config, AC97_SPDIF_CONTROL, (uint16)stream->sample_rate);
213 
214 	return B_OK;
215 }
216 
217 
218 status_t
219 auvia_stream_get_nth_buffer(auvia_stream *stream, uint8 chan, uint8 buf,
220 					char** buffer, size_t *stride)
221 {
222 	uint8 			sample_size, frame_size;
223 	LOG(("auvia_stream_get_nth_buffer\n"));
224 
225 	sample_size = stream->b16 + 1;
226 	frame_size = sample_size * stream->channels;
227 
228 	*buffer =(char *)((addr_t)stream->buffer->log_base + (uintptr_t)(buf * stream->bufframes * frame_size))
229 		+ chan * sample_size;
230 	*stride = frame_size;
231 
232 	return B_OK;
233 }
234 
235 
236 static uint32
237 auvia_stream_curaddr(auvia_stream *stream)
238 {
239 	uint32 addr;
240 	if(IS_8233(&stream->card->config)) {
241 		addr = auvia_reg_read_32(&stream->card->config, stream->base + AUVIA_RP_DMAOPS_BASE);
242 		TRACE(("stream_curaddr %p, phy_base %p\n", addr, stream->dmaops_phy_base));
243 		return (addr - stream->dmaops_phy_base - 4) / 8;
244 	} else {
245 		addr = auvia_reg_read_32(&stream->card->config, stream->base + AUVIA_RP_DMAOPS_BASE);
246 		TRACE(("stream_curaddr %p, phy_base %p\n", addr, stream->dmaops_phy_base));
247 		return (addr - stream->dmaops_phy_base - 8) / 8;
248 	}
249 }
250 
251 
252 void
253 auvia_stream_start(auvia_stream *stream, void (*inth) (void *), void *inthparam)
254 {
255 	LOG(("auvia_stream_start\n"));
256 
257 	stream->inth = inth;
258 	stream->inthparam = inthparam;
259 
260 	stream->state |= AUVIA_STATE_STARTED;
261 
262 	if(IS_8233(&stream->card->config)) {
263 		if(stream->base != AUVIA_8233_MP_BASE) {
264 			auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_8233_RP_DXS_LVOL, 0);
265 			auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_8233_RP_DXS_RVOL, 0);
266 		}
267 		auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_RP_CONTROL,
268 			AUVIA_RPCTRL_START | AUVIA_RPCTRL_AUTOSTART | AUVIA_RPCTRL_STOP
269 			| AUVIA_RPCTRL_EOL | AUVIA_RPCTRL_FLAG);
270 	} else {
271 		uint8 regvalue = (stream->channels > 1 ? AUVIA_RPMODE_STEREO : 0)
272 			| (stream->b16 == 1 ? AUVIA_RPMODE_16BIT : 0)
273 			| AUVIA_RPMODE_INTR_FLAG | AUVIA_RPMODE_INTR_EOL | AUVIA_RPMODE_AUTOSTART;
274 		auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_RP_MODE, regvalue);
275 		auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_RP_CONTROL,
276 			AUVIA_RPCTRL_START);
277 	}
278 }
279 
280 
281 void
282 auvia_stream_halt(auvia_stream *stream)
283 {
284 	LOG(("auvia_stream_halt\n"));
285 
286 	stream->state &= ~AUVIA_STATE_STARTED;
287 
288 	auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_RP_CONTROL,
289 		AUVIA_RPCTRL_TERMINATE);
290 }
291 
292 
293 auvia_stream *
294 auvia_stream_new(auvia_dev *card, uint8 use, uint32 bufframes, uint8 bufcount)
295 {
296 	auvia_stream *stream;
297 	cpu_status status;
298 	LOG(("auvia_stream_new\n"));
299 
300 	stream = malloc(sizeof(auvia_stream));
301 	if (stream == NULL)
302 		return (NULL);
303 	stream->card = card;
304 	stream->use = use;
305 	stream->state = !AUVIA_STATE_STARTED;
306 	stream->b16 = 0;
307 	stream->sample_rate = 0;
308 	stream->channels = 0;
309 	stream->bufframes = bufframes;
310 	stream->bufcount = bufcount;
311 	stream->inth = NULL;
312 	stream->inthparam = NULL;
313 	stream->buffer = NULL;
314 	stream->blksize = 0;
315 	stream->trigblk = 0;
316 	stream->blkmod = 0;
317 
318 	if(use & AUVIA_USE_PLAY) {
319 		if(IS_8233(&card->config))
320 			stream->base = AUVIA_8233_MP_BASE;
321 			//stream->base = AUVIA_PLAY_BASE;
322 		else
323 			stream->base = AUVIA_PLAY_BASE;
324 	} else {
325 		if(IS_8233(&card->config))
326 			stream->base = AUVIA_8233_RECORD_BASE;
327 		else
328 			stream->base = AUVIA_RECORD_BASE;
329 	}
330 
331 	stream->frames_count = 0;
332 	stream->real_time = 0;
333 	stream->buffer_cycle = 0;
334 	stream->update_needed = false;
335 
336 	/* allocate memory for our dma ops */
337 	stream->dmaops_area = alloc_mem(&stream->dmaops_phy_base, &stream->dmaops_log_base,
338 		VIA_TABLE_SIZE, "auvia dmaops", false);
339 
340 	if (stream->dmaops_area < B_OK) {
341 		PRINT(("couldn't allocate memory\n"));
342 		free(stream);
343 		return NULL;
344 	}
345 
346 	status = lock();
347 	LIST_INSERT_HEAD((&card->streams), stream, next);
348 	unlock(status);
349 
350 	return stream;
351 }
352 
353 
354 void
355 auvia_stream_delete(auvia_stream *stream)
356 {
357 	cpu_status status;
358 	LOG(("auvia_stream_delete\n"));
359 
360 	auvia_stream_halt(stream);
361 
362 	auvia_reg_write_32(&stream->card->config, stream->base + AUVIA_RP_DMAOPS_BASE, 0);
363 
364 	if (stream->dmaops_area > B_OK)
365 		delete_area(stream->dmaops_area);
366 
367 	if(stream->buffer)
368 		auvia_mem_free(stream->card, stream->buffer->log_base);
369 
370 	status = lock();
371 	LIST_REMOVE(stream, next);
372 	unlock(status);
373 
374 	free(stream);
375 }
376 
377 /* Auvia interrupt */
378 
379 static int32
380 auvia_int(void *arg)
381 {
382 	auvia_dev	 *card = arg;
383 	bool gotone = false;
384 	uint32       curblk;
385 	auvia_stream *stream;
386 
387 	if(auvia_reg_read_32(&card->config, AUVIA_SGD_SHADOW)
388 		& card->interrupt_mask) {
389 
390 		LIST_FOREACH(stream, &card->streams, next)
391 			if(auvia_reg_read_8(&card->config, stream->base + AUVIA_RP_STAT) & AUVIA_RPSTAT_INTR) {
392 				gotone = true;
393 				//TRACE(("interrupt\n"));
394 
395 				curblk = auvia_stream_curaddr(stream);
396 				TRACE(("RPSTAT_INTR at trigblk %lu, stream->trigblk %lu\n", curblk, stream->trigblk));
397 				if (curblk == stream->trigblk) {
398 					//TRACE(("AUVIA_RPSTAT_INTR at trigblk %lu\n", curblk));
399 
400 					if(stream->inth)
401 						stream->inth(stream->inthparam);
402 
403 					stream->trigblk++;
404 					stream->trigblk %= stream->blkmod;
405 				}
406 
407 				auvia_reg_write_8(&card->config, stream->base + AUVIA_RP_STAT, AUVIA_RPSTAT_INTR);
408 			}
409 	} else {
410 		TRACE(("SGD_SHADOW %x %x\n", card->interrupt_mask,
411 			auvia_reg_read_32(&card->config, AUVIA_SGD_SHADOW)));
412 	}
413 
414 	if(gotone)
415 		return B_INVOKE_SCHEDULER;
416 
417 	TRACE(("Got unhandled interrupt\n"));
418 	return B_UNHANDLED_INTERRUPT;
419 }
420 
421 /*	Auvia driver functions */
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(B_PCI_MODULE_NAME, (module_info **)&pci))
436 		return ENOSYS;
437 
438 	while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) {
439 		if (info.vendor_id == VIATECH_VENDOR_ID &&
440 			(info.device_id == VIATECH_82C686_AC97_DEVICE_ID
441 			|| info.device_id == VIATECH_8233_AC97_DEVICE_ID
442 			)) {
443 			err = B_OK;
444 		}
445 		ix++;
446 	}
447 
448 	put_module(B_PCI_MODULE_NAME);
449 
450 	return err;
451 }
452 
453 static void
454 make_device_names(
455 	auvia_dev * card)
456 {
457 	sprintf(card->name, "audio/hmulti/auvia/%ld", card-cards+1);
458 	names[num_names++] = card->name;
459 
460 	names[num_names] = NULL;
461 }
462 
463 
464 static status_t
465 auvia_init(auvia_dev * card)
466 {
467 	uint32 pr;
468 
469 	pr = (*pci->read_pci_config)(card->info.bus, card->info.device,
470 		card->info.function, AUVIA_PCICONF_JUNK, 4);
471 	PRINT(("AUVIA_PCICONF_JUNK before: %" B_PRIx32 "\n", pr));
472 	pr &= ~AUVIA_PCICONF_ENABLES;
473 	pr |= AUVIA_PCICONF_ACLINKENAB | AUVIA_PCICONF_ACNOTRST
474 		| AUVIA_PCICONF_ACVSR | AUVIA_PCICONF_ACSGD;
475 	pr &= ~(AUVIA_PCICONF_ACFM | AUVIA_PCICONF_ACSB);
476 	(*pci->write_pci_config)(card->info.bus, card->info.device,
477 		card->info.function, AUVIA_PCICONF_JUNK, 4, pr );
478 	snooze(100);
479 	pr = (*pci->read_pci_config)(card->info.bus, card->info.device,
480 		card->info.function, AUVIA_PCICONF_JUNK, 4);
481 	PRINT(("AUVIA_PCICONF_JUNK after: %" B_PRIx32 "\n", pr));
482 
483 	if(IS_8233(&card->config)) {
484 		card->interrupt_mask =
485 			AUVIA_8233_SGD_STAT_FLAG_EOL |
486 			AUVIA_8233_SGD_STAT_FLAG_EOL << 4 |
487 			AUVIA_8233_SGD_STAT_FLAG_EOL << 8 |
488 			AUVIA_8233_SGD_STAT_FLAG_EOL << 12 |
489 			AUVIA_8233_SGD_STAT_FLAG_EOL << 16 |
490 			AUVIA_8233_SGD_STAT_FLAG_EOL << 24 |
491 			AUVIA_8233_SGD_STAT_FLAG_EOL << 28;
492 	} else {
493 		card->interrupt_mask = AUVIA_SGD_STAT_ALL | (AUVIA_SGD_STAT_ALL << 4);
494 	}
495 
496 
497 	/* Init streams list */
498 	LIST_INIT(&(card->streams));
499 
500 	/* Init mems list */
501 	LIST_INIT(&(card->mems));
502 
503 	return B_OK;
504 }
505 
506 
507 static void
508 auvia_shutdown(auvia_dev *card)
509 {
510 	PRINT(("shutdown(%p)\n", card));
511 	ac97_detach(card->config.ac97);
512 	remove_io_interrupt_handler(card->config.irq, auvia_int, card);
513 }
514 
515 
516 static status_t
517 auvia_setup(auvia_dev * card)
518 {
519 	status_t err = B_OK;
520 	unsigned char cmd;
521 
522 	PRINT(("auvia_setup(%p)\n", card));
523 
524 	make_device_names(card);
525 
526 	card->config.subvendor_id = card->info.u.h0.subsystem_vendor_id;
527 	card->config.subsystem_id = card->info.u.h0.subsystem_id;
528 	card->config.nabmbar = card->info.u.h0.base_registers[0];
529 	card->config.irq = card->info.u.h0.interrupt_line;
530 	card->config.type = 0;
531 	if(card->info.device_id == VIATECH_82C686_AC97_DEVICE_ID)
532 		card->config.type |= TYPE_686;
533 	if(card->info.device_id == VIATECH_8233_AC97_DEVICE_ID)
534 		card->config.type |= TYPE_8233;
535 
536 	PRINT(("%s deviceid = %#04x chiprev = %x model = %x enhanced "
537 		"at %" B_PRIx32 "\n",
538 		card->name, card->info.device_id, card->info.revision,
539 		card->info.u.h0.subsystem_id, card->config.nabmbar));
540 
541 	cmd = (*pci->read_pci_config)(card->info.bus, card->info.device,
542 		card->info.function, PCI_command, 2);
543 	PRINT(("PCI command before: %x\n", cmd));
544 	(*pci->write_pci_config)(card->info.bus, card->info.device,
545 		card->info.function, PCI_command, 2, cmd | PCI_command_io);
546 	cmd = (*pci->read_pci_config)(card->info.bus, card->info.device,
547 		card->info.function, PCI_command, 2);
548 	PRINT(("PCI command after: %x\n", cmd));
549 
550 	/* attach the codec */
551 	PRINT(("codec attach\n"));
552 	ac97_attach(&card->config.ac97, (codec_reg_read)auvia_codec_read,
553 		(codec_reg_write)auvia_codec_write, &card->config,
554 		card->config.subvendor_id, card->config.subsystem_id);
555 
556 	PRINT(("installing interrupt : %" B_PRIx32 "\n", card->config.irq));
557 	err = install_io_interrupt_handler(card->config.irq, auvia_int, card, 0);
558 	if (err != B_OK) {
559 		PRINT(("failed to install interrupt\n"));
560 		ac97_detach(card->config.ac97);
561 		return err;
562 	}
563 
564 	if ((err = auvia_init(card))) {
565 		auvia_shutdown(card);
566 		return err;
567 	}
568 
569 	PRINT(("init_driver done\n"));
570 
571 	return err;
572 }
573 
574 
575 status_t
576 init_driver(void)
577 {
578 	pci_info info;
579 	status_t err;
580 	int ix = 0;
581 	num_cards = 0;
582 
583 	PRINT(("init_driver()\n"));
584 
585 	if (get_module(B_PCI_MODULE_NAME, (module_info **)&pci))
586 		return ENOSYS;
587 
588 	while ((*pci->get_nth_pci_info)(ix++, &info) == B_OK) {
589 		if (info.vendor_id == VIATECH_VENDOR_ID &&
590 			(info.device_id == VIATECH_82C686_AC97_DEVICE_ID
591 			|| info.device_id == VIATECH_8233_AC97_DEVICE_ID
592 			)) {
593 			if (num_cards == NUM_CARDS) {
594 				PRINT(("Too many auvia cards installed!\n"));
595 				break;
596 			}
597 			memset(&cards[num_cards], 0, sizeof(auvia_dev));
598 			cards[num_cards].info = info;
599 #ifdef __HAIKU__
600 			if ((err = (*pci->reserve_device)(info.bus, info.device, info.function,
601 				DRIVER_NAME, &cards[num_cards])) < B_OK) {
602 				dprintf("%s: failed to reserve_device(%d, %d, %d,): %s\n",
603 					DRIVER_NAME, info.bus, info.device, info.function,
604 					strerror(err));
605 				continue;
606 			}
607 #endif
608 			if (auvia_setup(&cards[num_cards])) {
609 				PRINT(("Setup of auvia %" B_PRId32 " failed\n", num_cards + 1));
610 #ifdef __HAIKU__
611 				(*pci->unreserve_device)(info.bus, info.device, info.function,
612 					DRIVER_NAME, &cards[num_cards]);
613 #endif
614 			}
615 			else {
616 				num_cards++;
617 			}
618 		}
619 	}
620 	if (!num_cards) {
621 		PRINT(("no cards\n"));
622 		put_module(B_PCI_MODULE_NAME);
623 		PRINT(("no suitable cards found\n"));
624 		return ENODEV;
625 	}
626 
627 
628 #if DEBUG
629 	//add_debugger_command("auvia", auvia_debug, "auvia [card# (1-n)]");
630 #endif
631 	return B_OK;
632 }
633 
634 
635 void
636 uninit_driver(void)
637 {
638 	int ix, cnt = num_cards;
639 	num_cards = 0;
640 
641 	PRINT(("uninit_driver()\n"));
642 	//remove_debugger_command("auvia", auvia_debug);
643 
644 	for (ix=0; ix<cnt; ix++) {
645 		auvia_shutdown(&cards[ix]);
646 #ifdef __HAIKU__
647 		(*pci->unreserve_device)(cards[ix].info.bus,
648 			cards[ix].info.device, cards[ix].info.function,
649 			DRIVER_NAME, &cards[ix]);
650 #endif
651 	}
652 	memset(&cards, 0, sizeof(cards));
653 	put_module(B_PCI_MODULE_NAME);
654 }
655 
656 
657 const char **
658 publish_devices(void)
659 {
660 	int ix = 0;
661 	PRINT(("publish_devices()\n"));
662 
663 	for (ix=0; names[ix]; ix++) {
664 		PRINT(("publish %s\n", names[ix]));
665 	}
666 	return (const char **)names;
667 }
668 
669 
670 device_hooks *
671 find_device(const char * name)
672 {
673 	int ix;
674 
675 	PRINT(("find_device(%s)\n", name));
676 
677 	for (ix=0; ix<num_cards; ix++) {
678 		if (!strcmp(cards[ix].name, name)) {
679 			return &multi_hooks;
680 		}
681 	}
682 	PRINT(("find_device(%s) failed\n", name));
683 	return NULL;
684 }
685 
686 int32	api_version = B_CUR_DRIVER_API_VERSION;
687