1 /* 2 * Copyright 2006-2010, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler, axeld@pinc-software.de 7 */ 8 9 10 #include "device_interfaces.h" 11 #include "domains.h" 12 #include "interfaces.h" 13 #include "stack_private.h" 14 #include "utility.h" 15 16 #include <net_device.h> 17 18 #include <lock.h> 19 #include <util/AutoLock.h> 20 21 #include <KernelExport.h> 22 23 #include <net/if_dl.h> 24 #include <netinet/in.h> 25 #include <new> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 30 31 //#define TRACE_DEVICE_INTERFACES 32 #ifdef TRACE_DEVICE_INTERFACES 33 # define TRACE(x) dprintf x 34 #else 35 # define TRACE(x) ; 36 #endif 37 38 39 static mutex sLock; 40 static DeviceInterfaceList sInterfaces; 41 static uint32 sDeviceIndex; 42 43 44 /*! A service thread for each device interface. It just reads as many packets 45 as availabe, deframes them, and puts them into the receive queue of the 46 device interface. 47 */ 48 static status_t 49 device_reader_thread(void* _interface) 50 { 51 net_device_interface* interface = (net_device_interface*)_interface; 52 net_device* device = interface->device; 53 status_t status = B_OK; 54 55 while ((device->flags & IFF_UP) != 0) { 56 net_buffer* buffer; 57 status = device->module->receive_data(device, &buffer); 58 if (status == B_OK) { 59 // feed device monitors 60 if (atomic_get(&interface->monitor_count) > 0) 61 device_interface_monitor_receive(interface, buffer); 62 63 ASSERT(buffer->interface_address == NULL); 64 65 if (interface->deframe_func(interface->device, buffer) != B_OK) { 66 gNetBufferModule.free(buffer); 67 continue; 68 } 69 70 fifo_enqueue_buffer(&interface->receive_queue, buffer); 71 } else if (status == B_DEVICE_NOT_FOUND) { 72 device_removed(device); 73 } else { 74 // In case of error, give the other threads some 75 // time to run since this is a high priority time thread. 76 snooze(10000); 77 } 78 } 79 80 return status; 81 } 82 83 84 static status_t 85 device_consumer_thread(void* _interface) 86 { 87 net_device_interface* interface = (net_device_interface*)_interface; 88 net_device* device = interface->device; 89 net_buffer* buffer; 90 91 while (true) { 92 ssize_t status = fifo_dequeue_buffer(&interface->receive_queue, 0, 93 B_INFINITE_TIMEOUT, &buffer); 94 if (status != B_OK) { 95 if (status == B_INTERRUPTED) 96 continue; 97 break; 98 } 99 100 if (buffer->interface_address != NULL) { 101 // If the interface is already specified, this buffer was 102 // delivered locally. 103 if (buffer->interface_address->domain->module->receive_data(buffer) 104 == B_OK) 105 buffer = NULL; 106 } else { 107 sockaddr_dl& linkAddress = *(sockaddr_dl*)buffer->source; 108 int32 genericType = buffer->type; 109 int32 specificType = B_NET_FRAME_TYPE(linkAddress.sdl_type, 110 ntohs(linkAddress.sdl_e_type)); 111 112 buffer->index = interface->device->index; 113 114 // Find handler for this packet 115 116 RecursiveLocker locker(interface->receive_lock); 117 118 DeviceHandlerList::Iterator iterator 119 = interface->receive_funcs.GetIterator(); 120 while (buffer != NULL && iterator.HasNext()) { 121 net_device_handler* handler = iterator.Next(); 122 123 // If the handler returns B_OK, it consumed the buffer - first 124 // handler wins. 125 if ((handler->type == genericType 126 || handler->type == specificType) 127 && handler->func(handler->cookie, device, buffer) == B_OK) 128 buffer = NULL; 129 } 130 } 131 132 if (buffer != NULL) 133 gNetBufferModule.free(buffer); 134 } 135 136 return B_OK; 137 } 138 139 140 /*! The domain's device receive handler - this will inject the net_buffers into 141 the protocol layer (the domain's registered receive handler). 142 */ 143 static status_t 144 domain_receive_adapter(void* cookie, net_device* device, net_buffer* buffer) 145 { 146 net_domain_private* domain = (net_domain_private*)cookie; 147 148 return domain->module->receive_data(buffer); 149 } 150 151 152 static net_device_interface* 153 find_device_interface(const char* name) 154 { 155 DeviceInterfaceList::Iterator iterator = sInterfaces.GetIterator(); 156 157 while (net_device_interface* interface = iterator.Next()) { 158 if (!strcmp(interface->device->name, name)) 159 return interface; 160 } 161 162 return NULL; 163 } 164 165 166 static net_device_interface* 167 allocate_device_interface(net_device* device, net_device_module_info* module) 168 { 169 net_device_interface* interface = new(std::nothrow) net_device_interface; 170 if (interface == NULL) 171 return NULL; 172 173 recursive_lock_init(&interface->receive_lock, "device interface receive"); 174 recursive_lock_init(&interface->monitor_lock, "device interface monitors"); 175 176 char name[128]; 177 snprintf(name, sizeof(name), "%s receive queue", device->name); 178 179 if (init_fifo(&interface->receive_queue, name, 16 * 1024 * 1024) < B_OK) 180 goto error1; 181 182 interface->device = device; 183 interface->up_count = 0; 184 interface->ref_count = 1; 185 interface->monitor_count = 0; 186 interface->deframe_func = NULL; 187 interface->deframe_ref_count = 0; 188 189 snprintf(name, sizeof(name), "%s consumer", device->name); 190 191 interface->reader_thread = -1; 192 interface->consumer_thread = spawn_kernel_thread(device_consumer_thread, 193 name, B_DISPLAY_PRIORITY, interface); 194 if (interface->consumer_thread < B_OK) 195 goto error2; 196 resume_thread(interface->consumer_thread); 197 198 // TODO: proper interface index allocation 199 device->index = ++sDeviceIndex; 200 device->module = module; 201 202 sInterfaces.Add(interface); 203 return interface; 204 205 error2: 206 uninit_fifo(&interface->receive_queue); 207 error1: 208 recursive_lock_destroy(&interface->receive_lock); 209 recursive_lock_destroy(&interface->monitor_lock); 210 delete interface; 211 212 return NULL; 213 } 214 215 216 static void 217 notify_device_monitors(net_device_interface* interface, int32 event) 218 { 219 RecursiveLocker locker(interface->monitor_lock); 220 221 DeviceMonitorList::Iterator iterator 222 = interface->monitor_funcs.GetIterator(); 223 while (net_device_monitor* monitor = iterator.Next()) { 224 // it's safe for the "current" item to remove itself. 225 monitor->event(monitor, event); 226 } 227 } 228 229 230 #if ENABLE_DEBUGGER_COMMANDS 231 232 233 static int 234 dump_device_interface(int argc, char** argv) 235 { 236 if (argc != 2) { 237 kprintf("usage: %s [address]\n", argv[0]); 238 return 0; 239 } 240 241 net_device_interface* interface 242 = (net_device_interface*)parse_expression(argv[1]); 243 244 kprintf("device: %p\n", interface->device); 245 kprintf("reader_thread: %" B_PRId32 "\n", interface->reader_thread); 246 kprintf("up_count: %" B_PRIu32 "\n", interface->up_count); 247 kprintf("ref_count: %" B_PRId32 "\n", interface->ref_count); 248 kprintf("deframe_func: %p\n", interface->deframe_func); 249 kprintf("deframe_ref_count: %" B_PRId32 "\n", interface->ref_count); 250 kprintf("consumer_thread: %" B_PRId32 "\n", interface->consumer_thread); 251 252 kprintf("monitor_count: %" B_PRId32 "\n", interface->monitor_count); 253 kprintf("monitor_lock: %p\n", &interface->monitor_lock); 254 kprintf("monitor_funcs:\n"); 255 DeviceMonitorList::Iterator monitorIterator 256 = interface->monitor_funcs.GetIterator(); 257 while (monitorIterator.HasNext()) 258 kprintf(" %p\n", monitorIterator.Next()); 259 260 kprintf("receive_lock: %p\n", &interface->receive_lock); 261 kprintf("receive_queue: %p\n", &interface->receive_queue); 262 kprintf("receive_funcs:\n"); 263 DeviceHandlerList::Iterator handlerIterator 264 = interface->receive_funcs.GetIterator(); 265 while (handlerIterator.HasNext()) 266 kprintf(" %p\n", handlerIterator.Next()); 267 268 return 0; 269 } 270 271 272 static int 273 dump_device_interfaces(int argc, char** argv) 274 { 275 DeviceInterfaceList::Iterator iterator = sInterfaces.GetIterator(); 276 while (net_device_interface* interface = iterator.Next()) { 277 kprintf(" %p %s\n", interface, interface->device->name); 278 } 279 280 return 0; 281 } 282 283 284 #endif // ENABLE_DEBUGGER_COMMANDS 285 286 287 // #pragma mark - device interfaces 288 289 290 net_device_interface* 291 acquire_device_interface(net_device_interface* interface) 292 { 293 if (interface == NULL || atomic_add(&interface->ref_count, 1) == 0) 294 return NULL; 295 296 return interface; 297 } 298 299 300 void 301 get_device_interface_address(net_device_interface* interface, 302 sockaddr* _address) 303 { 304 sockaddr_dl &address = *(sockaddr_dl*)_address; 305 306 address.sdl_family = AF_LINK; 307 address.sdl_index = interface->device->index; 308 address.sdl_type = interface->device->type; 309 address.sdl_nlen = strlen(interface->device->name); 310 address.sdl_slen = 0; 311 memcpy(address.sdl_data, interface->device->name, address.sdl_nlen); 312 313 address.sdl_alen = interface->device->address.length; 314 memcpy(LLADDR(&address), interface->device->address.data, address.sdl_alen); 315 316 address.sdl_len = sizeof(sockaddr_dl) - sizeof(address.sdl_data) 317 + address.sdl_nlen + address.sdl_alen; 318 } 319 320 321 uint32 322 count_device_interfaces() 323 { 324 MutexLocker locker(sLock); 325 326 DeviceInterfaceList::Iterator iterator = sInterfaces.GetIterator(); 327 uint32 count = 0; 328 329 while (iterator.HasNext()) { 330 iterator.Next(); 331 count++; 332 } 333 334 return count; 335 } 336 337 338 /*! Dumps a list of all interfaces into the supplied userland buffer. 339 If the interfaces don't fit into the buffer, an error (\c ENOBUFS) is 340 returned. 341 */ 342 status_t 343 list_device_interfaces(void* _buffer, size_t* bufferSize) 344 { 345 MutexLocker locker(sLock); 346 347 DeviceInterfaceList::Iterator iterator = sInterfaces.GetIterator(); 348 UserBuffer buffer(_buffer, *bufferSize); 349 350 while (net_device_interface* interface = iterator.Next()) { 351 buffer.Push(interface->device->name, IF_NAMESIZE); 352 353 sockaddr_storage address; 354 get_device_interface_address(interface, (sockaddr*)&address); 355 356 buffer.Push(&address, address.ss_len); 357 if (IF_NAMESIZE + address.ss_len < (int)sizeof(ifreq)) 358 buffer.Pad(sizeof(ifreq) - IF_NAMESIZE - address.ss_len); 359 360 if (buffer.Status() != B_OK) 361 return buffer.Status(); 362 } 363 364 *bufferSize = buffer.BytesConsumed(); 365 return B_OK; 366 } 367 368 369 /*! Releases the reference for the interface. When all references are 370 released, the interface is removed. 371 */ 372 void 373 put_device_interface(struct net_device_interface* interface) 374 { 375 if (interface == NULL) 376 return; 377 378 if (atomic_add(&interface->ref_count, -1) != 1) 379 return; 380 381 MutexLocker locker(sLock); 382 sInterfaces.Remove(interface); 383 locker.Unlock(); 384 385 uninit_fifo(&interface->receive_queue); 386 status_t status; 387 wait_for_thread(interface->consumer_thread, &status); 388 389 net_device* device = interface->device; 390 const char* moduleName = device->module->info.name; 391 392 device->module->uninit_device(device); 393 put_module(moduleName); 394 395 recursive_lock_destroy(&interface->monitor_lock); 396 recursive_lock_destroy(&interface->receive_lock); 397 delete interface; 398 } 399 400 401 /*! Finds an interface by the specified index and acquires a reference to it. 402 */ 403 struct net_device_interface* 404 get_device_interface(uint32 index) 405 { 406 MutexLocker locker(sLock); 407 408 // TODO: maintain an array of all device interfaces instead 409 DeviceInterfaceList::Iterator iterator = sInterfaces.GetIterator(); 410 while (net_device_interface* interface = iterator.Next()) { 411 if (interface->device->index == index) { 412 if (atomic_add(&interface->ref_count, 1) != 0) 413 return interface; 414 } 415 } 416 417 return NULL; 418 } 419 420 421 /*! Finds an interface by the specified name and grabs a reference to it. 422 If the interface does not yet exist, a new one is created. 423 */ 424 struct net_device_interface* 425 get_device_interface(const char* name, bool create) 426 { 427 MutexLocker locker(sLock); 428 429 net_device_interface* interface = find_device_interface(name); 430 if (interface != NULL) { 431 if (atomic_add(&interface->ref_count, 1) != 0) 432 return interface; 433 434 // try to recreate interface - it just got removed 435 } 436 437 if (!create) 438 return NULL; 439 440 void* cookie = open_module_list("network/devices"); 441 if (cookie == NULL) 442 return NULL; 443 444 while (true) { 445 char moduleName[B_FILE_NAME_LENGTH]; 446 size_t length = sizeof(moduleName); 447 if (read_next_module_name(cookie, moduleName, &length) != B_OK) 448 break; 449 450 TRACE(("get_device_interface: ask \"%s\" for %s\n", moduleName, name)); 451 452 net_device_module_info* module; 453 if (get_module(moduleName, (module_info**)&module) == B_OK) { 454 net_device* device; 455 status_t status = module->init_device(name, &device); 456 if (status == B_OK) { 457 interface = allocate_device_interface(device, module); 458 if (interface != NULL) 459 return interface; 460 461 module->uninit_device(device); 462 } 463 put_module(moduleName); 464 } 465 } 466 467 close_module_list(cookie); 468 469 return NULL; 470 } 471 472 473 /*! Feeds the device monitors of the \a interface with the specified \a buffer. 474 You might want to check interface::monitor_count before calling this 475 function for optimization. 476 */ 477 void 478 device_interface_monitor_receive(net_device_interface* interface, 479 net_buffer* buffer) 480 { 481 RecursiveLocker locker(interface->monitor_lock); 482 483 DeviceMonitorList::Iterator iterator 484 = interface->monitor_funcs.GetIterator(); 485 while (iterator.HasNext()) { 486 net_device_monitor* monitor = iterator.Next(); 487 monitor->receive(monitor, buffer); 488 } 489 } 490 491 492 status_t 493 up_device_interface(net_device_interface* interface) 494 { 495 net_device* device = interface->device; 496 497 RecursiveLocker locker(interface->receive_lock); 498 499 if (interface->up_count != 0) { 500 interface->up_count++; 501 return B_OK; 502 } 503 504 status_t status = device->module->up(device); 505 if (status != B_OK) 506 return status; 507 508 if (device->module->receive_data != NULL) { 509 // give the thread a nice name 510 char name[B_OS_NAME_LENGTH]; 511 snprintf(name, sizeof(name), "%s reader", device->name); 512 513 interface->reader_thread = spawn_kernel_thread(device_reader_thread, 514 name, B_REAL_TIME_DISPLAY_PRIORITY - 10, interface); 515 if (interface->reader_thread < B_OK) 516 return interface->reader_thread; 517 } 518 519 device->flags |= IFF_UP; 520 521 if (device->module->receive_data != NULL) 522 resume_thread(interface->reader_thread); 523 524 interface->up_count = 1; 525 return B_OK; 526 } 527 528 529 void 530 down_device_interface(net_device_interface* interface) 531 { 532 // Receive lock must be held when calling down_device_interface. 533 // Known callers are `interface_protocol_down' which gets 534 // here via one of the following paths: 535 // 536 // - domain_interface_control() [rx lock held, domain lock held] 537 // interface_set_down() 538 // interface_protocol_down() 539 // 540 // - domain_interface_control() [rx lock held, domain lock held] 541 // remove_interface_from_domain() 542 // delete_interface() 543 // interface_set_down() 544 545 net_device* device = interface->device; 546 547 device->flags &= ~IFF_UP; 548 device->module->down(device); 549 550 notify_device_monitors(interface, B_DEVICE_GOING_DOWN); 551 552 if (device->module->receive_data != NULL) { 553 thread_id readerThread = interface->reader_thread; 554 555 // make sure the reader thread is gone before shutting down the interface 556 status_t status; 557 wait_for_thread(readerThread, &status); 558 } 559 } 560 561 562 // #pragma mark - devices stack API 563 564 565 /*! Unregisters a previously registered deframer function. */ 566 status_t 567 unregister_device_deframer(net_device* device) 568 { 569 MutexLocker locker(sLock); 570 571 // find device interface for this device 572 net_device_interface* interface = find_device_interface(device->name); 573 if (interface == NULL) 574 return B_DEVICE_NOT_FOUND; 575 576 RecursiveLocker _(interface->receive_lock); 577 578 if (--interface->deframe_ref_count == 0) 579 interface->deframe_func = NULL; 580 581 return B_OK; 582 } 583 584 585 /*! Registers the deframer function for the specified \a device. 586 Note, however, that right now, you can only register one single 587 deframer function per device. 588 589 If the need arises, we might want to lift that limitation at a 590 later time (which would require a slight API change, though). 591 */ 592 status_t 593 register_device_deframer(net_device* device, net_deframe_func deframeFunc) 594 { 595 MutexLocker locker(sLock); 596 597 // find device interface for this device 598 net_device_interface* interface = find_device_interface(device->name); 599 if (interface == NULL) 600 return B_DEVICE_NOT_FOUND; 601 602 RecursiveLocker _(interface->receive_lock); 603 604 if (interface->deframe_func != NULL 605 && interface->deframe_func != deframeFunc) 606 return B_ERROR; 607 608 interface->deframe_func = deframeFunc; 609 interface->deframe_ref_count++; 610 return B_OK; 611 } 612 613 614 /*! Registers a domain to receive net_buffers from the specified \a device. */ 615 status_t 616 register_domain_device_handler(struct net_device* device, int32 type, 617 struct net_domain* _domain) 618 { 619 net_domain_private* domain = (net_domain_private*)_domain; 620 if (domain->module == NULL || domain->module->receive_data == NULL) 621 return B_BAD_VALUE; 622 623 return register_device_handler(device, type, &domain_receive_adapter, 624 domain); 625 } 626 627 628 /*! Registers a receiving function callback for the specified \a device. */ 629 status_t 630 register_device_handler(struct net_device* device, int32 type, 631 net_receive_func receiveFunc, void* cookie) 632 { 633 MutexLocker locker(sLock); 634 635 // find device interface for this device 636 net_device_interface* interface = find_device_interface(device->name); 637 if (interface == NULL) 638 return B_DEVICE_NOT_FOUND; 639 640 RecursiveLocker _(interface->receive_lock); 641 642 // see if such a handler already for this device 643 644 DeviceHandlerList::Iterator iterator 645 = interface->receive_funcs.GetIterator(); 646 while (net_device_handler* handler = iterator.Next()) { 647 if (handler->type == type) 648 return B_ERROR; 649 } 650 651 // Add new handler 652 653 net_device_handler* handler = new(std::nothrow) net_device_handler; 654 if (handler == NULL) 655 return B_NO_MEMORY; 656 657 handler->func = receiveFunc; 658 handler->type = type; 659 handler->cookie = cookie; 660 interface->receive_funcs.Add(handler); 661 return B_OK; 662 } 663 664 665 /*! Unregisters a previously registered device handler. */ 666 status_t 667 unregister_device_handler(struct net_device* device, int32 type) 668 { 669 MutexLocker locker(sLock); 670 671 // find device interface for this device 672 net_device_interface* interface = find_device_interface(device->name); 673 if (interface == NULL) 674 return B_DEVICE_NOT_FOUND; 675 676 RecursiveLocker _(interface->receive_lock); 677 678 // search for the handler 679 680 DeviceHandlerList::Iterator iterator 681 = interface->receive_funcs.GetIterator(); 682 while (net_device_handler* handler = iterator.Next()) { 683 if (handler->type == type) { 684 // found it 685 iterator.Remove(); 686 delete handler; 687 return B_OK; 688 } 689 } 690 691 return B_BAD_VALUE; 692 } 693 694 695 /*! Registers a device monitor for the specified device. */ 696 status_t 697 register_device_monitor(net_device* device, net_device_monitor* monitor) 698 { 699 if (monitor->receive == NULL || monitor->event == NULL) 700 return B_BAD_VALUE; 701 702 MutexLocker locker(sLock); 703 704 // find device interface for this device 705 net_device_interface* interface = find_device_interface(device->name); 706 if (interface == NULL) 707 return B_DEVICE_NOT_FOUND; 708 709 RecursiveLocker monitorLocker(interface->monitor_lock); 710 interface->monitor_funcs.Add(monitor); 711 atomic_add(&interface->monitor_count, 1); 712 713 return B_OK; 714 } 715 716 717 /*! Unregisters a previously registered device monitor. */ 718 status_t 719 unregister_device_monitor(net_device* device, net_device_monitor* monitor) 720 { 721 MutexLocker locker(sLock); 722 723 // find device interface for this device 724 net_device_interface* interface = find_device_interface(device->name); 725 if (interface == NULL) 726 return B_DEVICE_NOT_FOUND; 727 728 RecursiveLocker monitorLocker(interface->monitor_lock); 729 730 // search for the monitor 731 732 DeviceMonitorList::Iterator iterator 733 = interface->monitor_funcs.GetIterator(); 734 while (iterator.HasNext()) { 735 if (iterator.Next() == monitor) { 736 iterator.Remove(); 737 atomic_add(&interface->monitor_count, -1); 738 return B_OK; 739 } 740 } 741 742 return B_BAD_VALUE; 743 } 744 745 746 /*! This function is called by device modules in case their link 747 state changed, ie. if an ethernet cable was plugged in or 748 removed. 749 */ 750 status_t 751 device_link_changed(net_device* device) 752 { 753 notify_link_changed(device); 754 return B_OK; 755 } 756 757 758 /*! This function is called by device modules once their device got 759 physically removed, ie. a USB networking card is unplugged. 760 */ 761 status_t 762 device_removed(net_device* device) 763 { 764 MutexLocker locker(sLock); 765 766 // hold a reference to the device interface being removed 767 // so our put_() will (eventually) do the final cleanup 768 net_device_interface* interface = get_device_interface(device->name, false); 769 if (interface == NULL) 770 return B_DEVICE_NOT_FOUND; 771 772 // Propagate the loss of the device throughout the stack. 773 774 interface_removed_device_interface(interface); 775 notify_device_monitors(interface, B_DEVICE_BEING_REMOVED); 776 777 // By now all of the monitors must have removed themselves. If they 778 // didn't, they'll probably wait forever to be callback'ed again. 779 recursive_lock_lock(&interface->monitor_lock); 780 interface->monitor_funcs.RemoveAll(); 781 782 // All of the readers should be gone as well since we are out of 783 // interfaces and put_domain_datalink_protocols() is called for 784 // each delete_interface(). 785 786 put_device_interface(interface); 787 788 return B_OK; 789 } 790 791 792 status_t 793 device_enqueue_buffer(net_device* device, net_buffer* buffer) 794 { 795 net_device_interface* interface = get_device_interface(device->index); 796 if (interface == NULL) 797 return B_DEVICE_NOT_FOUND; 798 799 status_t status = fifo_enqueue_buffer(&interface->receive_queue, buffer); 800 801 put_device_interface(interface); 802 return status; 803 } 804 805 806 // #pragma mark - 807 808 809 status_t 810 init_device_interfaces() 811 { 812 mutex_init(&sLock, "net device interfaces"); 813 814 new (&sInterfaces) DeviceInterfaceList; 815 // static C++ objects are not initialized in the module startup 816 817 #if ENABLE_DEBUGGER_COMMANDS 818 add_debugger_command("net_device_interface", &dump_device_interface, 819 "Dump the given network device interface"); 820 add_debugger_command("net_device_interfaces", &dump_device_interfaces, 821 "Dump network device interfaces"); 822 #endif 823 return B_OK; 824 } 825 826 827 status_t 828 uninit_device_interfaces() 829 { 830 #if ENABLE_DEBUGGER_COMMANDS 831 remove_debugger_command("net_device_interface", &dump_device_interface); 832 remove_debugger_command("net_device_interfaces", &dump_device_interfaces); 833 #endif 834 835 mutex_destroy(&sLock); 836 return B_OK; 837 } 838 839