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 51status_t init_hardware(void); 52status_t init_driver(void); 53void uninit_driver(void); 54const char ** publish_devices(void); 55device_hooks * find_device(const char *); 56 57pci_module_info *pci; 58 59int32 num_cards; 60auvia_dev cards[NUM_CARDS]; 61int32 num_names; 62char * names[NUM_CARDS*20+1]; 63 64extern device_hooks multi_hooks; 65 66/* Auvia Memory management */ 67 68static auvia_mem * 69auvia_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", false); 77 mem->size = size; 78 if (mem->area < B_OK) { 79 free(mem); 80 return NULL; 81 } 82 return mem; 83} 84 85 86static void 87auvia_mem_delete(auvia_mem *mem) 88{ 89 if(mem->area > B_OK) 90 delete_area(mem->area); 91 free(mem); 92} 93 94 95static void * 96auvia_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 110static void 111auvia_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 127status_t 128auvia_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 160status_t 161auvia_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 218status_t 219auvia_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 236static uint32 237auvia_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 252void 253auvia_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 281void 282auvia_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 293auvia_stream * 294auvia_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 354void 355auvia_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 379static int32 380auvia_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 */ 424status_t 425init_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 453static void 454make_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 464static status_t 465auvia_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 507static void 508auvia_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 516static status_t 517auvia_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 575status_t 576init_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 635void 636uninit_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 657const char ** 658publish_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 670device_hooks * 671find_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 686int32 api_version = B_CUR_DRIVER_API_VERSION; 687