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