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