xref: /haiku/src/add-ons/kernel/drivers/audio/ac97/auvia/auvia.c (revision adb0d19d561947362090081e81d90dde59142026)
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");
77 	mem->size = size;
78 	if (mem->area < B_OK) {
79 		free(mem);
80 		return NULL;
81 	}
82 	return mem;
83 }
84 
85 static void
86 auvia_mem_delete(auvia_mem *mem)
87 {
88 	if(mem->area > B_OK)
89 		delete_area(mem->area);
90 	free(mem);
91 }
92 
93 static void *
94 auvia_mem_alloc(auvia_dev *card, size_t size)
95 {
96 	auvia_mem *mem;
97 
98 	mem = auvia_mem_new(card, size);
99 	if (mem == NULL)
100 		return (NULL);
101 
102 	LIST_INSERT_HEAD(&(card->mems), mem, next);
103 
104 	return mem;
105 }
106 
107 static void
108 auvia_mem_free(auvia_dev *card, void *ptr)
109 {
110 	auvia_mem 		*mem;
111 
112 	LIST_FOREACH(mem, &card->mems, next) {
113 		if (mem->log_base != ptr)
114 			continue;
115 		LIST_REMOVE(mem, next);
116 
117 		auvia_mem_delete(mem);
118 		break;
119 	}
120 }
121 
122 /*	Auvia stream functions */
123 
124 status_t
125 auvia_stream_set_audioparms(auvia_stream *stream, uint8 channels,
126      uint8 b16, uint32 sample_rate)
127 {
128 	uint8 			sample_size, frame_size;
129 	LOG(("auvia_stream_set_audioparms\n"));
130 
131 	if ((stream->channels == channels) &&
132 		(stream->b16 == b16) &&
133 		(stream->sample_rate == sample_rate))
134 		return B_OK;
135 
136 	if(stream->buffer)
137 		auvia_mem_free(stream->card, stream->buffer->log_base);
138 
139 	stream->b16 = b16;
140 	stream->sample_rate = sample_rate;
141 	stream->channels = channels;
142 
143 	sample_size = stream->b16 + 1;
144 	frame_size = sample_size * stream->channels;
145 
146 	stream->buffer = auvia_mem_alloc(stream->card, stream->bufframes * frame_size * stream->bufcount);
147 
148 	stream->trigblk = 0;	/* This shouldn't be needed */
149 	stream->blkmod = stream->bufcount;
150 	stream->blksize = stream->bufframes * frame_size;
151 
152 	return B_OK;
153 }
154 
155 status_t
156 auvia_stream_commit_parms(auvia_stream *stream)
157 {
158 	int             i;
159 	uint32      	*page;
160 	uint32			value;
161 	LOG(("auvia_stream_commit_parms\n"));
162 
163 	page = stream->dmaops_log_base;
164 
165 	for(i = 0; i < stream->bufcount; i++) {
166 		page[2*i] = ((uint32)stream->buffer->phy_base) +
167 			i * stream->blksize;
168 		page[2*i + 1] = AUVIA_DMAOP_FLAG | stream->blksize;
169 	}
170 
171 	page[2*stream->bufcount - 1] &= ~AUVIA_DMAOP_FLAG;
172 	page[2*stream->bufcount - 1] |= AUVIA_DMAOP_EOL;
173 
174 	auvia_reg_write_32(&stream->card->config, stream->base + AUVIA_RP_DMAOPS_BASE,
175 		(uint32)stream->dmaops_phy_base);
176 
177 	if(stream->use & AUVIA_USE_RECORD)
178 		auvia_codec_write(&stream->card->config, AC97_PCM_L_R_ADC_RATE, (uint16)stream->sample_rate);
179 	else
180 		auvia_codec_write(&stream->card->config, AC97_PCM_FRONT_DAC_RATE, (uint16)stream->sample_rate);
181 
182 	if(IS_8233(&stream->card->config)) {
183 		if(stream->base != AUVIA_8233_MP_BASE) {
184 			value = auvia_reg_read_32(&stream->card->config, stream->base + AUVIA_8233_RP_RATEFMT);
185 			value &= ~(AUVIA_8233_RATEFMT_48K | AUVIA_8233_RATEFMT_STEREO | AUVIA_8233_RATEFMT_16BIT);
186 			if(stream->use & AUVIA_USE_PLAY)
187 				value |= AUVIA_8233_RATEFMT_48K * (stream->sample_rate / 20) / (48000 / 20);
188 			value |= (stream->channels == 2 ? AUVIA_8233_RATEFMT_STEREO : 0)
189 				| (stream->b16 ? AUVIA_8233_RATEFMT_16BIT : 0);
190 			auvia_reg_write_32(&stream->card->config, stream->base + AUVIA_8233_RP_RATEFMT, value);
191 		} else {
192 			static const uint32 slottab[7] = {0, 0xff000011, 0xff000021, 0xff000521,
193 											0xff004321, 0xff054321, 0xff654321};
194 			value = (stream->b16 ? AUVIA_8233_MP_FORMAT_16BIT : AUVIA_8233_MP_FORMAT_8BIT)
195 				| ((stream->channels << 4) & AUVIA_8233_MP_FORMAT_CHANNEL_MASK) ;
196 			auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_8233_OFF_MP_FORMAT, value);
197 			auvia_reg_write_32(&stream->card->config, stream->base + AUVIA_8233_OFF_MP_STOP,
198 				slottab[stream->channels]);
199 		}
200 	}
201 	//auvia_codec_write(&stream->card->config, AC97_SPDIF_CONTROL, (uint16)stream->sample_rate);
202 
203 	return B_OK;
204 }
205 
206 status_t
207 auvia_stream_get_nth_buffer(auvia_stream *stream, uint8 chan, uint8 buf,
208 					char** buffer, size_t *stride)
209 {
210 	uint8 			sample_size, frame_size;
211 	LOG(("auvia_stream_get_nth_buffer\n"));
212 
213 	sample_size = stream->b16 + 1;
214 	frame_size = sample_size * stream->channels;
215 
216 	*buffer = stream->buffer->log_base + (buf * stream->bufframes * frame_size)
217 		+ chan * sample_size;
218 	*stride = frame_size;
219 
220 	return B_OK;
221 }
222 
223 static uint32
224 auvia_stream_curaddr(auvia_stream *stream)
225 {
226 	uint32 addr;
227 	if(IS_8233(&stream->card->config)) {
228 		addr = auvia_reg_read_32(&stream->card->config, stream->base + AUVIA_RP_DMAOPS_BASE);
229 		TRACE(("stream_curaddr %p, phy_base %p\n", addr, (uint32)stream->dmaops_phy_base));
230 		return (addr - (uint32)stream->dmaops_phy_base - 4) / 8;
231 	} else {
232 		addr = auvia_reg_read_32(&stream->card->config, stream->base + AUVIA_RP_DMAOPS_BASE);
233 		TRACE(("stream_curaddr %p, phy_base %p\n", addr, (uint32)stream->dmaops_phy_base));
234 		return (addr - (uint32)stream->dmaops_phy_base - 8) / 8;
235 	}
236 }
237 
238 void
239 auvia_stream_start(auvia_stream *stream, void (*inth) (void *), void *inthparam)
240 {
241 	LOG(("auvia_stream_start\n"));
242 
243 	stream->inth = inth;
244 	stream->inthparam = inthparam;
245 
246 	stream->state |= AUVIA_STATE_STARTED;
247 
248 	if(IS_8233(&stream->card->config)) {
249 		if(stream->base != AUVIA_8233_MP_BASE) {
250 			auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_8233_RP_DXS_LVOL, 0);
251 			auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_8233_RP_DXS_RVOL, 0);
252 		}
253 		auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_RP_CONTROL,
254 			AUVIA_RPCTRL_START | AUVIA_RPCTRL_AUTOSTART | AUVIA_RPCTRL_STOP
255 			| AUVIA_RPCTRL_EOL | AUVIA_RPCTRL_FLAG);
256 	} else {
257 		uint8 regvalue = (stream->channels > 1 ? AUVIA_RPMODE_STEREO : 0)
258 			| (stream->b16 == 1 ? AUVIA_RPMODE_16BIT : 0)
259 			| AUVIA_RPMODE_INTR_FLAG | AUVIA_RPMODE_INTR_EOL | AUVIA_RPMODE_AUTOSTART;
260 		auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_RP_MODE, regvalue);
261 		auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_RP_CONTROL, AUVIA_RPCTRL_START);
262 	}
263 }
264 
265 void
266 auvia_stream_halt(auvia_stream *stream)
267 {
268 	LOG(("auvia_stream_halt\n"));
269 
270 	stream->state &= ~AUVIA_STATE_STARTED;
271 
272 	auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_RP_CONTROL, AUVIA_RPCTRL_TERMINATE);
273 }
274 
275 auvia_stream *
276 auvia_stream_new(auvia_dev *card, uint8 use, uint32 bufframes, uint8 bufcount)
277 {
278 	auvia_stream *stream;
279 	cpu_status status;
280 	LOG(("auvia_stream_new\n"));
281 
282 	stream = malloc(sizeof(auvia_stream));
283 	if (stream == NULL)
284 		return (NULL);
285 	stream->card = card;
286 	stream->use = use;
287 	stream->state = !AUVIA_STATE_STARTED;
288 	stream->b16 = 0;
289 	stream->sample_rate = 0;
290 	stream->channels = 0;
291 	stream->bufframes = bufframes;
292 	stream->bufcount = bufcount;
293 	stream->inth = NULL;
294 	stream->inthparam = NULL;
295 	stream->buffer = NULL;
296 	stream->blksize = 0;
297 	stream->trigblk = 0;
298 	stream->blkmod = 0;
299 
300 	if(use & AUVIA_USE_PLAY) {
301 		if(IS_8233(&card->config))
302 			stream->base = AUVIA_8233_MP_BASE;
303 			//stream->base = AUVIA_PLAY_BASE;
304 		else
305 			stream->base = AUVIA_PLAY_BASE;
306 	} else {
307 		if(IS_8233(&card->config))
308 			stream->base = AUVIA_8233_RECORD_BASE;
309 		else
310 			stream->base = AUVIA_RECORD_BASE;
311 	}
312 
313 	stream->frames_count = 0;
314 	stream->real_time = 0;
315 	stream->buffer_cycle = 0;
316 	stream->update_needed = false;
317 
318 	/* allocate memory for our dma ops */
319 	stream->dmaops_area = alloc_mem(&stream->dmaops_phy_base, &stream->dmaops_log_base,
320 		VIA_TABLE_SIZE, "auvia dmaops");
321 
322 	if (stream->dmaops_area < B_OK) {
323 		PRINT(("couldn't allocate memory\n"));
324 		free(stream);
325 		return NULL;
326 	}
327 
328 	status = lock();
329 	LIST_INSERT_HEAD((&card->streams), stream, next);
330 	unlock(status);
331 
332 	return stream;
333 }
334 
335 void
336 auvia_stream_delete(auvia_stream *stream)
337 {
338 	cpu_status status;
339 	LOG(("auvia_stream_delete\n"));
340 
341 	auvia_stream_halt(stream);
342 
343 	auvia_reg_write_32(&stream->card->config, stream->base + AUVIA_RP_DMAOPS_BASE, 0);
344 
345 	if (stream->dmaops_area > B_OK)
346 		delete_area(stream->dmaops_area);
347 
348 	if(stream->buffer)
349 		auvia_mem_free(stream->card, stream->buffer->log_base);
350 
351 	status = lock();
352 	LIST_REMOVE(stream, next);
353 	unlock(status);
354 
355 	free(stream);
356 }
357 
358 /* Auvia interrupt */
359 
360 static int32
361 auvia_int(void *arg)
362 {
363 	auvia_dev	 *card = arg;
364 	bool gotone = false;
365 	uint32       curblk;
366 	auvia_stream *stream;
367 
368 	if(auvia_reg_read_32(&card->config, AUVIA_SGD_SHADOW)
369 		& card->interrupt_mask) {
370 
371 		LIST_FOREACH(stream, &card->streams, next)
372 			if(auvia_reg_read_8(&card->config, stream->base + AUVIA_RP_STAT) & AUVIA_RPSTAT_INTR) {
373 				gotone = true;
374 				//TRACE(("interrupt\n"));
375 
376 				curblk = auvia_stream_curaddr(stream);
377 				TRACE(("RPSTAT_INTR at trigblk %lu, stream->trigblk %lu\n", curblk, stream->trigblk));
378 				if (curblk == stream->trigblk) {
379 					//TRACE(("AUVIA_RPSTAT_INTR at trigblk %lu\n", curblk));
380 
381 					if(stream->inth)
382 						stream->inth(stream->inthparam);
383 
384 					stream->trigblk++;
385 					stream->trigblk %= stream->blkmod;
386 				}
387 
388 				auvia_reg_write_8(&card->config, stream->base + AUVIA_RP_STAT, AUVIA_RPSTAT_INTR);
389 			}
390 	} else {
391 		TRACE(("SGD_SHADOW %x %x\n", card->interrupt_mask, auvia_reg_read_32(&card->config, AUVIA_SGD_SHADOW)));
392 	}
393 
394 	if(gotone)
395 		return B_INVOKE_SCHEDULER;
396 
397 	TRACE(("Got unhandled interrupt\n"));
398 	return B_UNHANDLED_INTERRUPT;
399 }
400 
401 /*	Auvia driver functions */
402 
403 /* detect presence of our hardware */
404 status_t
405 init_hardware(void)
406 {
407 	int ix=0;
408 	pci_info info;
409 	status_t err = ENODEV;
410 
411 	LOG_CREATE();
412 
413 	PRINT(("init_hardware()\n"));
414 
415 	if (get_module(B_PCI_MODULE_NAME, (module_info **)&pci))
416 		return ENOSYS;
417 
418 	while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) {
419 		if (info.vendor_id == VIATECH_VENDOR_ID &&
420 			(info.device_id == VIATECH_82C686_AC97_DEVICE_ID
421 			|| info.device_id == VIATECH_8233_AC97_DEVICE_ID
422 			)) {
423 			err = B_OK;
424 		}
425 		ix++;
426 	}
427 
428 	put_module(B_PCI_MODULE_NAME);
429 
430 	return err;
431 }
432 
433 static void
434 make_device_names(
435 	auvia_dev * card)
436 {
437 	sprintf(card->name, "audio/hmulti/auvia/%ld", card-cards+1);
438 	names[num_names++] = card->name;
439 
440 	names[num_names] = NULL;
441 }
442 
443 
444 static status_t
445 auvia_init(auvia_dev * card)
446 {
447 	uint32 pr;
448 
449 	pr = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, AUVIA_PCICONF_JUNK, 4);
450 	PRINT(("AUVIA_PCICONF_JUNK before: %lx\n", pr));
451 	pr &= ~AUVIA_PCICONF_ENABLES;
452 	pr |= AUVIA_PCICONF_ACLINKENAB | AUVIA_PCICONF_ACNOTRST | AUVIA_PCICONF_ACVSR | AUVIA_PCICONF_ACSGD;
453 	pr &= ~(AUVIA_PCICONF_ACFM | AUVIA_PCICONF_ACSB);
454 	(*pci->write_pci_config)(card->info.bus, card->info.device, card->info.function, AUVIA_PCICONF_JUNK, 4, pr );
455 	snooze(100);
456 	pr = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, AUVIA_PCICONF_JUNK, 4);
457 	PRINT(("AUVIA_PCICONF_JUNK after: %lx\n", pr));
458 
459 	if(IS_8233(&card->config)) {
460 		card->interrupt_mask =
461 			AUVIA_8233_SGD_STAT_FLAG_EOL |
462 			AUVIA_8233_SGD_STAT_FLAG_EOL << 4 |
463 			AUVIA_8233_SGD_STAT_FLAG_EOL << 8 |
464 			AUVIA_8233_SGD_STAT_FLAG_EOL << 12 |
465 			AUVIA_8233_SGD_STAT_FLAG_EOL << 16 |
466 			AUVIA_8233_SGD_STAT_FLAG_EOL << 24 |
467 			AUVIA_8233_SGD_STAT_FLAG_EOL << 28;
468 	} else {
469 		card->interrupt_mask = AUVIA_SGD_STAT_ALL | (AUVIA_SGD_STAT_ALL << 4);
470 	}
471 
472 
473 	/* Init streams list */
474 	LIST_INIT(&(card->streams));
475 
476 	/* Init mems list */
477 	LIST_INIT(&(card->mems));
478 
479 	return B_OK;
480 }
481 
482 static status_t
483 auvia_setup(auvia_dev * card)
484 {
485 	status_t err = B_OK;
486 	unsigned char cmd;
487 
488 	PRINT(("auvia_setup(%p)\n", card));
489 
490 	make_device_names(card);
491 
492 	card->config.subvendor_id = card->info.u.h0.subsystem_vendor_id;
493 	card->config.subsystem_id = card->info.u.h0.subsystem_id;
494 	card->config.nabmbar = card->info.u.h0.base_registers[0];
495 	card->config.irq = card->info.u.h0.interrupt_line;
496 	card->config.type = 0;
497 	if(card->info.device_id == VIATECH_82C686_AC97_DEVICE_ID)
498 		card->config.type |= TYPE_686;
499 	if(card->info.device_id == VIATECH_8233_AC97_DEVICE_ID)
500 		card->config.type |= TYPE_8233;
501 
502 	PRINT(("%s deviceid = %#04x chiprev = %x model = %x enhanced at %lx\n", card->name, card->info.device_id,
503 		card->info.revision, card->info.u.h0.subsystem_id, card->config.nabmbar));
504 
505 	cmd = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, PCI_command, 2);
506 	PRINT(("PCI command before: %x\n", cmd));
507 	(*pci->write_pci_config)(card->info.bus, card->info.device, card->info.function, PCI_command, 2, cmd | PCI_command_io);
508 	cmd = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, PCI_command, 2);
509 	PRINT(("PCI command after: %x\n", cmd));
510 
511 	/* attach the codec */
512 	PRINT(("codec attach\n"));
513 	ac97_attach(&card->config.ac97, (codec_reg_read)auvia_codec_read,
514 		(codec_reg_write)auvia_codec_write, &card->config,
515 		card->config.subvendor_id, card->config.subsystem_id);
516 
517 	PRINT(("installing interrupt : %lx\n", card->config.irq));
518 	install_io_interrupt_handler(card->config.irq, auvia_int, card, 0);
519 
520 	if ((err = auvia_init(card)))
521 		return (err);
522 
523 	PRINT(("init_driver done\n"));
524 
525 	return err;
526 }
527 
528 
529 status_t
530 init_driver(void)
531 {
532 	pci_info info;
533 	int ix = 0;
534 
535 	num_cards = 0;
536 
537 	PRINT(("init_driver()\n"));
538 
539 	if (get_module(B_PCI_MODULE_NAME, (module_info **)&pci))
540 		return ENOSYS;
541 
542 	while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) {
543 		if (info.vendor_id == VIATECH_VENDOR_ID &&
544 			(info.device_id == VIATECH_82C686_AC97_DEVICE_ID
545 			|| info.device_id == VIATECH_8233_AC97_DEVICE_ID
546 			)) {
547 			if (num_cards == NUM_CARDS) {
548 				PRINT(("Too many auvia cards installed!\n"));
549 				break;
550 			}
551 			memset(&cards[num_cards], 0, sizeof(auvia_dev));
552 			cards[num_cards].info = info;
553 			if (auvia_setup(&cards[num_cards])) {
554 				PRINT(("Setup of auvia %ld failed\n", num_cards+1));
555 			}
556 			else {
557 				num_cards++;
558 			}
559 		}
560 		ix++;
561 	}
562 	if (!num_cards) {
563 		PRINT(("no cards\n"));
564 		put_module(B_PCI_MODULE_NAME);
565 		PRINT(("no suitable cards found\n"));
566 		return ENODEV;
567 	}
568 
569 
570 #if DEBUG
571 	//add_debugger_command("auvia", auvia_debug, "auvia [card# (1-n)]");
572 #endif
573 	return B_OK;
574 }
575 
576 
577 static void
578 auvia_shutdown(auvia_dev *card)
579 {
580 	PRINT(("shutdown(%p)\n", card));
581 	ac97_detach(card->config.ac97);
582 	remove_io_interrupt_handler(card->config.irq, auvia_int, card);
583 }
584 
585 
586 void
587 uninit_driver(void)
588 {
589 	int ix, cnt = num_cards;
590 	num_cards = 0;
591 
592 	PRINT(("uninit_driver()\n"));
593 	//remove_debugger_command("auvia", auvia_debug);
594 
595 	for (ix=0; ix<cnt; ix++) {
596 		auvia_shutdown(&cards[ix]);
597 	}
598 	memset(&cards, 0, sizeof(cards));
599 	put_module(B_PCI_MODULE_NAME);
600 }
601 
602 
603 const char **
604 publish_devices(void)
605 {
606 	int ix = 0;
607 	PRINT(("publish_devices()\n"));
608 
609 	for (ix=0; names[ix]; ix++) {
610 		PRINT(("publish %s\n", names[ix]));
611 	}
612 	return (const char **)names;
613 }
614 
615 
616 device_hooks *
617 find_device(const char * name)
618 {
619 	int ix;
620 
621 	PRINT(("find_device(%s)\n", name));
622 
623 	for (ix=0; ix<num_cards; ix++) {
624 		if (!strcmp(cards[ix].name, name)) {
625 			return &multi_hooks;
626 		}
627 	}
628 	PRINT(("find_device(%s) failed\n", name));
629 	return NULL;
630 }
631 
632 int32	api_version = B_CUR_DRIVER_API_VERSION;
633