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