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", false); 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