1 /* 2 * Copyright 2014, Ithamar R. Adema <ithamar@upgrade-android.com> 3 * All rights reserved. Distributed under the terms of the MIT License. 4 * 5 * Copyright 2015-2022, Haiku, Inc. All rights reserved. 6 * Distributed under the terms of the MIT License. 7 */ 8 9 10 #include <drivers/bus/FDT.h> 11 #include <KernelExport.h> 12 #include <util/kernel_cpp.h> 13 #include <util/Vector.h> 14 #include <device_manager.h> 15 16 #include <AutoDeleter.h> 17 #include <AutoDeleterDrivers.h> 18 #include <HashMap.h> 19 #include <debug.h> 20 21 extern "C" { 22 #include <libfdt_env.h> 23 #include <fdt.h> 24 #include <libfdt.h> 25 }; 26 27 28 //#define TRACE_FDT 29 #ifdef TRACE_FDT 30 #define TRACE(x...) dprintf(x) 31 #else 32 #define TRACE(x...) 33 #endif 34 35 36 #define GIC_INTERRUPT_CELL_TYPE 0 37 #define GIC_INTERRUPT_CELL_ID 1 38 #define GIC_INTERRUPT_CELL_FLAGS 2 39 #define GIC_INTERRUPT_TYPE_SPI 0 40 #define GIC_INTERRUPT_TYPE_PPI 1 41 #define GIC_INTERRUPT_BASE_SPI 32 42 #define GIC_INTERRUPT_BASE_PPI 16 43 44 45 extern void* gFDT; 46 47 device_manager_info* gDeviceManager; 48 49 extern fdt_bus_module_info gBusModule; 50 extern fdt_device_module_info gDeviceModule; 51 52 53 //#pragma mark - 54 55 56 struct fdt_bus { 57 device_node* node; 58 HashMap<HashKey32<int32>, device_node*> phandles; 59 }; 60 61 62 struct fdt_device { 63 device_node* node; 64 device_node* bus; 65 }; 66 67 68 struct fdt_interrupt_map_entry { 69 uint32_t childAddr; 70 uint32_t childIrq; 71 uint32_t parentIrqCtrl; 72 uint32_t parentIrq; 73 }; 74 75 76 struct fdt_interrupt_map { 77 uint32_t childAddrMask; 78 uint32_t childIrqMask; 79 80 Vector<fdt_interrupt_map_entry> fInterruptMap; 81 }; 82 83 84 static status_t 85 fdt_register_node(fdt_bus* bus, int node, device_node* parentDev, 86 device_node*& curDev) 87 { 88 TRACE("%s('%s', %p)\n", __func__, fdt_get_name(gFDT, node, NULL), 89 parentDev); 90 91 const void* prop; int propLen; 92 Vector<device_attr> attrs; 93 int nameLen = 0; 94 const char *name = fdt_get_name(gFDT, node, &nameLen); 95 96 if (name == NULL) { 97 dprintf("%s ERROR: fdt_get_name: %s\n", __func__, 98 fdt_strerror(nameLen)); 99 return B_ERROR; 100 } 101 102 attrs.Add({ B_DEVICE_BUS, B_STRING_TYPE, {.string = "fdt"}}); 103 attrs.Add({ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, 104 { .string = (strcmp(name, "") != 0) ? name : "Root" }}); 105 attrs.Add({ "fdt/node", B_UINT32_TYPE, {.ui32 = (uint32)node}}); 106 attrs.Add({ "fdt/name", B_STRING_TYPE, {.string = name}}); 107 108 prop = fdt_getprop(gFDT, node, "device_type", &propLen); 109 if (prop != NULL) 110 attrs.Add({ "fdt/device_type", B_STRING_TYPE, { .string = (const char*)prop }}); 111 112 prop = fdt_getprop(gFDT, node, "compatible", &propLen); 113 114 if (prop != NULL) { 115 const char* propStr = (const char*)prop; 116 const char* propEnd = propStr + propLen; 117 while (propEnd - propStr > 0) { 118 int curLen = strlen(propStr); 119 attrs.Add({ "fdt/compatible", B_STRING_TYPE, { .string = propStr }}); 120 propStr += curLen + 1; 121 } 122 } 123 124 attrs.Add({}); 125 126 status_t res = gDeviceManager->register_node(parentDev, 127 "bus_managers/fdt/driver_v1", &attrs[0], NULL, &curDev); 128 129 if (res < B_OK) 130 return res; 131 132 prop = fdt_getprop(gFDT, node, "phandle", &propLen); 133 134 if (prop != NULL) 135 bus->phandles.Put(fdt32_to_cpu(*(uint32_t*)prop), curDev); 136 137 return B_OK; 138 } 139 140 141 static void 142 fdt_traverse(fdt_bus* bus, int &node, int &depth, device_node* parentDev) 143 { 144 int curDepth = depth; 145 #if 0 146 for (int i = 0; i < depth; i++) dprintf(" "); 147 dprintf("node('%s')\n", fdt_get_name(gFDT, node, NULL)); 148 #endif 149 device_node* curDev; 150 fdt_register_node(bus, node, parentDev, curDev); 151 152 node = fdt_next_node(gFDT, node, &depth); 153 while (node >= 0 && depth == curDepth + 1) { 154 fdt_traverse(bus, node, depth, curDev); 155 } 156 } 157 158 159 //#pragma mark bus 160 161 static int32 162 fdt_bus_std_ops(int32 op, ...) 163 { 164 switch (op) { 165 case B_MODULE_INIT: 166 TRACE("fdt root init\n"); 167 return B_OK; 168 169 case B_MODULE_UNINIT: 170 TRACE("fdt root uninit\n"); 171 return B_OK; 172 } 173 174 return B_BAD_VALUE; 175 } 176 177 178 static float 179 fdt_bus_supports_device(device_node* parent) 180 { 181 TRACE("fdt_bus_supports_device\n"); 182 183 // make sure parent is really device root 184 const char* bus; 185 if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) 186 return B_ERROR; 187 188 if (strcmp(bus, "root")) 189 return 0.0; 190 191 return 1.0; 192 } 193 194 195 static status_t 196 fdt_bus_register_device(device_node* parent) 197 { 198 TRACE("+fdt_bus_register_device\n"); 199 struct ScopeExit { 200 ScopeExit() {TRACE("-fdt_bus_register_device\n");} 201 } scopeExit; 202 203 device_attr attrs[] = { 204 {B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {.string = "FDT"}}, 205 {B_DEVICE_FLAGS, B_UINT32_TYPE, {.ui32 = B_KEEP_DRIVER_LOADED}}, 206 {} 207 }; 208 209 return gDeviceManager->register_node( 210 parent, "bus_managers/fdt/root/driver_v1", attrs, NULL, NULL); 211 } 212 213 214 static status_t 215 fdt_bus_init(device_node* node, void** cookie) 216 { 217 TRACE("fdt_bus_init\n"); 218 219 if (gFDT == NULL) { 220 TRACE("FDT is NULL!\n"); 221 return B_DEVICE_NOT_FOUND; 222 } 223 224 ObjectDeleter<fdt_bus> bus(new(std::nothrow) fdt_bus()); 225 if (!bus.IsSet()) 226 return B_NO_MEMORY; 227 228 bus->node = node; 229 *cookie = bus.Detach(); 230 return B_OK; 231 } 232 233 234 static void 235 fdt_bus_uninit(void* cookie) 236 { 237 TRACE("fdt_bus_uninit\n"); 238 239 ObjectDeleter<fdt_bus> bus((fdt_bus*)cookie); 240 } 241 242 243 static status_t 244 fdt_bus_register_child_devices(void* cookie) 245 { 246 TRACE("fdt_bus_register_child_devices\n"); 247 248 fdt_bus* bus = (fdt_bus*)cookie; 249 250 int node = -1, depth = -1; 251 node = fdt_next_node(gFDT, node, &depth); 252 fdt_traverse(bus, node, depth, bus->node); 253 254 return B_OK; 255 } 256 257 258 device_node* 259 fdt_bus_node_by_phandle(fdt_bus* bus, int phandle) 260 { 261 ASSERT(bus != NULL); 262 263 device_node** devNode; 264 if (!bus->phandles.Get(phandle, devNode)) 265 return NULL; 266 267 return *devNode; 268 } 269 270 271 //#pragma mark device 272 273 274 static status_t 275 fdt_device_std_ops(int32 op, ...) 276 { 277 switch (op) { 278 case B_MODULE_INIT: 279 case B_MODULE_UNINIT: 280 return B_OK; 281 } 282 283 return B_BAD_VALUE; 284 } 285 286 287 static status_t 288 fdt_device_init_driver(device_node* node, void** cookie) 289 { 290 TRACE("fdt_device_init_driver()\n"); 291 292 ObjectDeleter<fdt_device> dev(new(std::nothrow) fdt_device()); 293 if (!dev.IsSet()) 294 return B_NO_MEMORY; 295 296 dev->node = node; 297 298 // get bus from parent node 299 DeviceNodePutter<&gDeviceManager> parent( 300 gDeviceManager->get_parent_node(node)); 301 driver_module_info* parentModule; 302 void* parentDev; 303 ASSERT(gDeviceManager->get_driver( 304 parent.Get(), &parentModule, &parentDev) >= B_OK); 305 if (parentModule == (driver_module_info*)&gDeviceModule) 306 dev->bus = ((fdt_device*)parentDev)->bus; 307 else if (parentModule == (driver_module_info*)&gBusModule) 308 dev->bus = parent.Get(); 309 else 310 panic("bad parent node"); 311 312 *cookie = dev.Detach(); 313 return B_OK; 314 } 315 316 317 static void 318 fdt_device_uninit_driver(void* cookie) 319 { 320 TRACE("fdt_device_uninit_driver()\n"); 321 ObjectDeleter<fdt_device> dev((fdt_device*)cookie); 322 } 323 324 325 static status_t 326 fdt_device_register_child_devices(void* cookie) 327 { 328 TRACE("fdt_device_register_child_devices()\n"); 329 return B_OK; 330 } 331 332 333 static device_node* 334 fdt_device_get_bus(fdt_device* dev) 335 { 336 ASSERT(dev != NULL); 337 return dev->bus; 338 } 339 340 341 static const char* 342 fdt_device_get_name(fdt_device* dev) 343 { 344 ASSERT(dev != NULL); 345 346 uint32 fdtNode; 347 ASSERT(gDeviceManager->get_attr_uint32( 348 dev->node, "fdt/node", &fdtNode, false) >= B_OK); 349 350 return fdt_get_name(gFDT, (int)fdtNode, NULL); 351 } 352 353 354 static const void* 355 fdt_device_get_prop(fdt_device* dev, const char* name, int* len) 356 { 357 ASSERT(dev != NULL); 358 359 uint32 fdtNode; 360 ASSERT(gDeviceManager->get_attr_uint32( 361 dev->node, "fdt/node", &fdtNode, false) >= B_OK); 362 363 return fdt_getprop(gFDT, (int)fdtNode, name, len); 364 } 365 366 367 static uint32 368 fdt_get_address_cells(const void* fdt, int node) 369 { 370 uint32 res = 2; 371 372 int parent = fdt_parent_offset(fdt, node); 373 if (parent < 0) 374 return res; 375 376 uint32 *prop = (uint32*)fdt_getprop(fdt, parent, "#address-cells", NULL); 377 if (prop == NULL) 378 return res; 379 380 res = fdt32_to_cpu(*prop); 381 return res; 382 } 383 384 385 static uint32 386 fdt_get_size_cells(const void* fdt, int node) 387 { 388 uint32 res = 1; 389 390 int parent = fdt_parent_offset(fdt, node); 391 if (parent < 0) 392 return res; 393 394 uint32 *prop = (uint32*)fdt_getprop(fdt, parent, "#size-cells", NULL); 395 if (prop == NULL) 396 return res; 397 398 res = fdt32_to_cpu(*prop); 399 return res; 400 } 401 402 403 static bool 404 fdt_device_get_reg(fdt_device* dev, uint32 ord, uint64* regs, uint64* len) 405 { 406 ASSERT(dev != NULL); 407 408 uint32 fdtNode; 409 ASSERT(gDeviceManager->get_attr_uint32( 410 dev->node, "fdt/node", &fdtNode, false) >= B_OK); 411 412 int propLen; 413 const void* prop = fdt_getprop(gFDT, (int)fdtNode, "reg", &propLen); 414 if (prop == NULL) 415 return false; 416 417 uint32 addressCells = fdt_get_address_cells(gFDT, fdtNode); 418 uint32 sizeCells = fdt_get_size_cells(gFDT, fdtNode); 419 size_t entrySize = 4 * (addressCells + sizeCells); 420 421 if ((ord + 1) * entrySize > (uint32)propLen) 422 return false; 423 424 const void* addressPtr = (const uint8*)prop + ord * entrySize; 425 const void* sizePtr = (const uint32*)addressPtr + addressCells; 426 427 switch (addressCells) { 428 case 1: 429 *regs = fdt32_to_cpu(*(const uint32*)addressPtr); 430 break; 431 case 2: 432 *regs = fdt64_to_cpu(*(const uint64*)addressPtr); 433 break; 434 default: 435 return false; 436 } 437 switch (sizeCells) { 438 case 1: 439 *len = fdt32_to_cpu(*(const uint32*)sizePtr); 440 break; 441 case 2: 442 *len = fdt64_to_cpu(*(const uint64*)sizePtr); 443 break; 444 default: 445 return false; 446 } 447 448 return true; 449 } 450 451 452 static uint32 453 fdt_get_interrupt_parent(fdt_device* dev, int node) 454 { 455 while (node >= 0) { 456 uint32* prop; 457 int propLen; 458 prop = (uint32*)fdt_getprop(gFDT, node, "interrupt-parent", &propLen); 459 if (prop != NULL && propLen == 4) { 460 return fdt32_to_cpu(*prop); 461 } 462 463 node = fdt_parent_offset(gFDT, node); 464 } 465 466 return 0; 467 } 468 469 470 static uint32 471 fdt_get_interrupt_cells(uint32 interrupt_parent_phandle) 472 { 473 if (interrupt_parent_phandle > 0) { 474 int node = fdt_node_offset_by_phandle(gFDT, interrupt_parent_phandle); 475 if (node >= 0) { 476 uint32* prop; 477 int propLen; 478 prop = (uint32*)fdt_getprop(gFDT, node, "#interrupt-cells", &propLen); 479 if (prop != NULL && propLen == 4) { 480 return fdt32_to_cpu(*prop); 481 } 482 } 483 } 484 return 1; 485 } 486 487 488 static bool 489 fdt_device_get_interrupt(fdt_device* dev, uint32 index, 490 device_node** interruptController, uint64* interrupt) 491 { 492 ASSERT(dev != NULL); 493 494 uint32 fdtNode; 495 ASSERT(gDeviceManager->get_attr_uint32( 496 dev->node, "fdt/node", &fdtNode, false) >= B_OK); 497 498 int propLen; 499 const uint32 *prop = (uint32*)fdt_getprop(gFDT, (int)fdtNode, "interrupts-extended", 500 &propLen); 501 if (prop == NULL) { 502 uint32 interruptParent = fdt_get_interrupt_parent(dev, fdtNode); 503 uint32 interruptCells = fdt_get_interrupt_cells(interruptParent); 504 505 prop = (uint32*)fdt_getprop(gFDT, (int)fdtNode, "interrupts", 506 &propLen); 507 if (prop == NULL) 508 return false; 509 510 if ((index + 1) * interruptCells * sizeof(uint32) > (uint32)propLen) 511 return false; 512 513 uint32 offset = interruptCells * index; 514 uint32 interruptNumber = 0; 515 516 if ((interruptCells == 1) || (interruptCells == 2)) { 517 interruptNumber = fdt32_to_cpu(*(prop + offset)); 518 } else if (interruptCells == 3) { 519 uint32 interruptType = fdt32_to_cpu(prop[offset + GIC_INTERRUPT_CELL_TYPE]); 520 interruptNumber = fdt32_to_cpu(prop[offset + GIC_INTERRUPT_CELL_ID]); 521 522 if (interruptType == GIC_INTERRUPT_TYPE_SPI) 523 interruptNumber += GIC_INTERRUPT_BASE_SPI; 524 else if (interruptType == GIC_INTERRUPT_TYPE_PPI) 525 interruptNumber += GIC_INTERRUPT_BASE_PPI; 526 } else { 527 panic("unsupported interruptCells"); 528 } 529 530 if (interrupt != NULL) 531 *interrupt = interruptNumber; 532 533 if (interruptController != NULL && interruptParent != 0) { 534 fdt_bus* bus; 535 ASSERT(gDeviceManager->get_driver(dev->bus, NULL, (void**)&bus) >= B_OK); 536 *interruptController = fdt_bus_node_by_phandle(bus, interruptParent); 537 } 538 539 return true; 540 } 541 542 if ((index + 1) * 8 > (uint32)propLen) 543 return false; 544 545 if (interruptController != NULL) { 546 uint32 phandle = fdt32_to_cpu(*(prop + 2 * index)); 547 548 fdt_bus* bus; 549 ASSERT(gDeviceManager->get_driver( 550 dev->bus, NULL, (void**)&bus) >= B_OK); 551 552 *interruptController = fdt_bus_node_by_phandle(bus, phandle); 553 } 554 555 if (interrupt != NULL) 556 *interrupt = fdt32_to_cpu(*(prop + 2 * index + 1)); 557 558 return true; 559 } 560 561 562 static struct fdt_interrupt_map * 563 fdt_device_get_interrupt_map(struct fdt_device* dev) 564 { 565 int fdtNode; 566 ASSERT(gDeviceManager->get_attr_uint32( 567 dev->node, "fdt/node", (uint32*)&fdtNode, false) >= B_OK); 568 569 ObjectDeleter<struct fdt_interrupt_map> interrupt_map(new struct fdt_interrupt_map()); 570 571 int intMapMaskLen; 572 const void* intMapMask = fdt_getprop(gFDT, fdtNode, "interrupt-map-mask", 573 &intMapMaskLen); 574 575 if (intMapMask == NULL || intMapMaskLen != 4 * 4) { 576 dprintf(" interrupt-map-mask property not found or invalid\n"); 577 return NULL; 578 } 579 580 interrupt_map->childAddrMask = B_BENDIAN_TO_HOST_INT32(*((uint32*)intMapMask + 0)); 581 interrupt_map->childIrqMask = B_BENDIAN_TO_HOST_INT32(*((uint32*)intMapMask + 3)); 582 583 int intMapLen; 584 const void* intMapAddr = fdt_getprop(gFDT, fdtNode, "interrupt-map", &intMapLen); 585 if (intMapAddr == NULL) { 586 dprintf(" interrupt-map property not found\n"); 587 return NULL; 588 } 589 590 int addressCells = 3; 591 int interruptCells = 1; 592 int phandleCells = 1; 593 594 const void *property; 595 596 property = fdt_getprop(gFDT, fdtNode, "#address-cells", NULL); 597 if (property != NULL) 598 addressCells = B_BENDIAN_TO_HOST_INT32(*(uint32*)property); 599 600 property = fdt_getprop(gFDT, fdtNode, "#interrupt-cells", NULL); 601 if (property != NULL) 602 interruptCells = B_BENDIAN_TO_HOST_INT32(*(uint32*)property); 603 604 uint32_t *it = (uint32_t*)intMapAddr; 605 while ((uint8_t*)it - (uint8_t*)intMapAddr < intMapLen) { 606 struct fdt_interrupt_map_entry irqEntry; 607 608 irqEntry.childAddr = B_BENDIAN_TO_HOST_INT32(*it); 609 it += addressCells; 610 611 irqEntry.childIrq = B_BENDIAN_TO_HOST_INT32(*it); 612 it += interruptCells; 613 614 irqEntry.parentIrqCtrl = B_BENDIAN_TO_HOST_INT32(*it); 615 it += phandleCells; 616 617 int parentAddressCells = 0; 618 int parentInterruptCells = 1; 619 620 int interruptParent = fdt_node_offset_by_phandle(gFDT, irqEntry.parentIrqCtrl); 621 if (interruptParent >= 0) { 622 property = fdt_getprop(gFDT, interruptParent, "#address-cells", NULL); 623 if (property != NULL) 624 parentAddressCells = B_BENDIAN_TO_HOST_INT32(*(uint32*)property); 625 626 property = fdt_getprop(gFDT, interruptParent, "#interrupt-cells", NULL); 627 if (property != NULL) 628 parentInterruptCells = B_BENDIAN_TO_HOST_INT32(*(uint32*)property); 629 } 630 631 it += parentAddressCells; 632 633 if ((parentInterruptCells == 1) || (parentInterruptCells == 2)) { 634 irqEntry.parentIrq = B_BENDIAN_TO_HOST_INT32(*it); 635 } else if (parentInterruptCells == 3) { 636 uint32 interruptType = fdt32_to_cpu(it[GIC_INTERRUPT_CELL_TYPE]); 637 uint32 interruptNumber = fdt32_to_cpu(it[GIC_INTERRUPT_CELL_ID]); 638 639 if (interruptType == GIC_INTERRUPT_TYPE_SPI) 640 irqEntry.parentIrq = interruptNumber + GIC_INTERRUPT_BASE_SPI; 641 else if (interruptType == GIC_INTERRUPT_TYPE_PPI) 642 irqEntry.parentIrq = interruptNumber + GIC_INTERRUPT_BASE_PPI; 643 else 644 irqEntry.parentIrq = interruptNumber; 645 } 646 it += parentInterruptCells; 647 648 interrupt_map->fInterruptMap.PushBack(irqEntry); 649 } 650 651 return interrupt_map.Detach(); 652 } 653 654 655 static void 656 fdt_device_print_interrupt_map(struct fdt_interrupt_map* interruptMap) 657 { 658 if (interruptMap == NULL) 659 return; 660 661 dprintf("interrupt_map_mask: 0x%08" PRIx32 ", 0x%08" PRIx32 "\n", 662 interruptMap->childAddrMask, interruptMap->childIrqMask); 663 dprintf("interrupt_map:\n"); 664 665 for (Vector<struct fdt_interrupt_map_entry>::Iterator it = interruptMap->fInterruptMap.Begin(); 666 it != interruptMap->fInterruptMap.End(); 667 it++) { 668 669 dprintf("childAddr=0x%08" PRIx32 ", childIrq=%" PRIu32 ", parentIrqCtrl=%" PRIu32 ", parentIrq=%" PRIu32 "\n", 670 it->childAddr, it->childIrq, it->parentIrqCtrl, it->parentIrq); 671 } 672 } 673 674 675 static uint32 676 fdt_device_lookup_interrupt_map(struct fdt_interrupt_map* interruptMap, uint32 childAddr, uint32 childIrq) 677 { 678 if (interruptMap == NULL) 679 return 0xffffffff; 680 681 childAddr &= interruptMap->childAddrMask; 682 childIrq &= interruptMap->childIrqMask; 683 684 for (Vector<struct fdt_interrupt_map_entry>::Iterator it = interruptMap->fInterruptMap.Begin(); 685 it != interruptMap->fInterruptMap.End(); it++) { 686 if ((it->childAddr == childAddr) && (it->childIrq == childIrq)) 687 return it->parentIrq; 688 } 689 690 return 0xffffffff; 691 } 692 693 694 //#pragma mark - 695 696 fdt_bus_module_info gBusModule = { 697 { 698 { 699 "bus_managers/fdt/root/driver_v1", 700 0, 701 fdt_bus_std_ops 702 }, 703 fdt_bus_supports_device, 704 fdt_bus_register_device, 705 fdt_bus_init, 706 fdt_bus_uninit, 707 fdt_bus_register_child_devices, 708 NULL, // rescan devices 709 NULL, // device removed 710 }, 711 fdt_bus_node_by_phandle, 712 }; 713 714 715 fdt_device_module_info gDeviceModule = { 716 { 717 { 718 "bus_managers/fdt/driver_v1", 719 0, 720 fdt_device_std_ops 721 }, 722 723 NULL, // supports device 724 NULL, // register device (our parent registered us) 725 fdt_device_init_driver, 726 fdt_device_uninit_driver, 727 fdt_device_register_child_devices, 728 NULL, // rescan devices 729 NULL, // device removed 730 }, 731 fdt_device_get_bus, 732 fdt_device_get_name, 733 fdt_device_get_prop, 734 fdt_device_get_reg, 735 fdt_device_get_interrupt, 736 fdt_device_get_interrupt_map, 737 fdt_device_print_interrupt_map, 738 fdt_device_lookup_interrupt_map, 739 }; 740 741 742 module_info* modules[] = { 743 (module_info*)&gBusModule, 744 (module_info*)&gDeviceModule, 745 NULL 746 }; 747 748 module_dependency module_dependencies[] = { 749 { B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager }, 750 { NULL } 751 }; 752