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
21extern "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
45extern void* gFDT;
46
47device_manager_info* gDeviceManager;
48
49extern fdt_bus_module_info gBusModule;
50extern fdt_device_module_info gDeviceModule;
51
52
53//#pragma mark -
54
55
56struct fdt_bus {
57	device_node* node;
58	HashMap<HashKey32<int32>, device_node*> phandles;
59};
60
61
62struct fdt_device {
63	device_node* node;
64	device_node* bus;
65};
66
67
68struct fdt_interrupt_map_entry {
69	uint32_t childAddr;
70	uint32_t childIrq;
71	uint32_t parentIrqCtrl;
72	uint32_t parentIrq;
73};
74
75
76struct fdt_interrupt_map {
77	uint32_t childAddrMask;
78	uint32_t childIrqMask;
79
80	Vector<fdt_interrupt_map_entry> fInterruptMap;
81};
82
83
84static status_t
85fdt_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
141static void
142fdt_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
161static int32
162fdt_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
178static float
179fdt_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
195static status_t
196fdt_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
214static status_t
215fdt_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	// gFDT is stored in kernel_args and will be freed, so copy it to kernel heap.
229	size_t size = fdt_totalsize(gFDT);
230	void* newFDT = malloc(size);
231	if (newFDT == NULL)
232		return B_NO_MEMORY;
233
234	memcpy(newFDT, gFDT, size);
235	gFDT = newFDT;
236
237	bus->node = node;
238	*cookie = bus.Detach();
239	return B_OK;
240}
241
242
243static void
244fdt_bus_uninit(void* cookie)
245{
246	TRACE("fdt_bus_uninit\n");
247
248	ObjectDeleter<fdt_bus> bus((fdt_bus*)cookie);
249}
250
251
252static status_t
253fdt_bus_register_child_devices(void* cookie)
254{
255	TRACE("fdt_bus_register_child_devices\n");
256
257	fdt_bus* bus = (fdt_bus*)cookie;
258
259	status_t res = gDeviceManager->publish_device(bus->node, "bus/fdt/blob",
260		"bus_managers/fdt/device/v1");
261	if (res < B_OK)
262		return res;
263
264	int node = -1, depth = -1;
265	node = fdt_next_node(gFDT, node, &depth);
266	fdt_traverse(bus, node, depth, bus->node);
267
268	return B_OK;
269}
270
271
272device_node*
273fdt_bus_node_by_phandle(fdt_bus* bus, int phandle)
274{
275	ASSERT(bus != NULL);
276
277	device_node** devNode;
278	if (!bus->phandles.Get(phandle, devNode))
279		return NULL;
280
281	return *devNode;
282}
283
284
285//#pragma mark device
286
287
288static status_t
289fdt_device_std_ops(int32 op, ...)
290{
291	switch (op) {
292		case B_MODULE_INIT:
293		case B_MODULE_UNINIT:
294			return B_OK;
295	}
296
297	return B_BAD_VALUE;
298}
299
300
301static status_t
302fdt_device_init_driver(device_node* node, void** cookie)
303{
304	TRACE("fdt_device_init_driver()\n");
305
306	ObjectDeleter<fdt_device> dev(new(std::nothrow) fdt_device());
307	if (!dev.IsSet())
308		return B_NO_MEMORY;
309
310	dev->node = node;
311
312	// get bus from parent node
313	DeviceNodePutter<&gDeviceManager> parent(
314		gDeviceManager->get_parent_node(node));
315	driver_module_info* parentModule;
316	void* parentDev;
317	ASSERT(gDeviceManager->get_driver(
318		parent.Get(), &parentModule, &parentDev) >= B_OK);
319	if (parentModule == (driver_module_info*)&gDeviceModule)
320		dev->bus = ((fdt_device*)parentDev)->bus;
321	else if (parentModule == (driver_module_info*)&gBusModule)
322		dev->bus = parent.Get();
323	else
324		panic("bad parent node");
325
326	*cookie = dev.Detach();
327	return B_OK;
328}
329
330
331static void
332fdt_device_uninit_driver(void* cookie)
333{
334	TRACE("fdt_device_uninit_driver()\n");
335	ObjectDeleter<fdt_device> dev((fdt_device*)cookie);
336}
337
338
339static status_t
340fdt_device_register_child_devices(void* cookie)
341{
342	TRACE("fdt_device_register_child_devices()\n");
343	return B_OK;
344}
345
346
347static device_node*
348fdt_device_get_bus(fdt_device* dev)
349{
350	ASSERT(dev != NULL);
351	return dev->bus;
352}
353
354
355static const char*
356fdt_device_get_name(fdt_device* dev)
357{
358	ASSERT(dev != NULL);
359
360	uint32 fdtNode;
361	ASSERT(gDeviceManager->get_attr_uint32(
362		dev->node, "fdt/node", &fdtNode, false) >= B_OK);
363
364	return fdt_get_name(gFDT, (int)fdtNode, NULL);
365}
366
367
368static const void*
369fdt_device_get_prop(fdt_device* dev, const char* name, int* len)
370{
371	ASSERT(dev != NULL);
372
373	uint32 fdtNode;
374	ASSERT(gDeviceManager->get_attr_uint32(
375		dev->node, "fdt/node", &fdtNode, false) >= B_OK);
376
377	return fdt_getprop(gFDT, (int)fdtNode, name, len);
378}
379
380
381static uint32
382fdt_get_address_cells(const void* fdt, int node)
383{
384	uint32 res = 2;
385
386	int parent = fdt_parent_offset(fdt, node);
387	if (parent < 0)
388		return res;
389
390	uint32 *prop = (uint32*)fdt_getprop(fdt, parent, "#address-cells", NULL);
391	if (prop == NULL)
392		return res;
393
394	res = fdt32_to_cpu(*prop);
395	return res;
396}
397
398
399static uint32
400fdt_get_size_cells(const void* fdt, int node)
401{
402	uint32 res = 1;
403
404	int parent = fdt_parent_offset(fdt, node);
405	if (parent < 0)
406		return res;
407
408	uint32 *prop = (uint32*)fdt_getprop(fdt, parent, "#size-cells", NULL);
409	if (prop == NULL)
410		return res;
411
412	res = fdt32_to_cpu(*prop);
413	return res;
414}
415
416
417static bool
418fdt_device_get_reg(fdt_device* dev, uint32 ord, uint64* regs, uint64* len)
419{
420	ASSERT(dev != NULL);
421
422	uint32 fdtNode;
423	ASSERT(gDeviceManager->get_attr_uint32(
424		dev->node, "fdt/node", &fdtNode, false) >= B_OK);
425
426	int propLen;
427	const void* prop = fdt_getprop(gFDT, (int)fdtNode, "reg", &propLen);
428	if (prop == NULL)
429		return false;
430
431	uint32 addressCells = fdt_get_address_cells(gFDT, fdtNode);
432	uint32 sizeCells = fdt_get_size_cells(gFDT, fdtNode);
433	size_t entrySize = 4 * (addressCells + sizeCells);
434
435	if ((ord + 1) * entrySize > (uint32)propLen)
436		return false;
437
438	const void* addressPtr = (const uint8*)prop + ord * entrySize;
439	const void* sizePtr = (const uint32*)addressPtr + addressCells;
440
441	switch (addressCells) {
442		case 1:
443			*regs = fdt32_to_cpu(*(const uint32*)addressPtr);
444			break;
445		case 2:
446			*regs = fdt64_to_cpu(*(const uint64*)addressPtr);
447			break;
448		default:
449			return false;
450	}
451	switch (sizeCells) {
452		case 1:
453			*len = fdt32_to_cpu(*(const uint32*)sizePtr);
454			break;
455		case 2:
456			*len = fdt64_to_cpu(*(const uint64*)sizePtr);
457			break;
458		default:
459			return false;
460	}
461
462	return true;
463}
464
465
466static uint32
467fdt_get_interrupt_parent(fdt_device* dev, int node)
468{
469	while (node >= 0) {
470		uint32* prop;
471		int propLen;
472		prop = (uint32*)fdt_getprop(gFDT, node, "interrupt-parent", &propLen);
473		if (prop != NULL && propLen == 4) {
474			return fdt32_to_cpu(*prop);
475		}
476
477		node = fdt_parent_offset(gFDT, node);
478	}
479
480	return 0;
481}
482
483
484static uint32
485fdt_get_interrupt_cells(uint32 interrupt_parent_phandle)
486{
487	if (interrupt_parent_phandle > 0) {
488		int node = fdt_node_offset_by_phandle(gFDT, interrupt_parent_phandle);
489		if (node >= 0) {
490			uint32* prop;
491			int propLen;
492			prop  = (uint32*)fdt_getprop(gFDT, node, "#interrupt-cells", &propLen);
493			if (prop != NULL && propLen == 4) {
494				return fdt32_to_cpu(*prop);
495			}
496		}
497	}
498	return 1;
499}
500
501
502static bool
503fdt_device_get_interrupt(fdt_device* dev, uint32 index,
504	device_node** interruptController, uint64* interrupt)
505{
506	ASSERT(dev != NULL);
507
508	uint32 fdtNode;
509	ASSERT(gDeviceManager->get_attr_uint32(
510		dev->node, "fdt/node", &fdtNode, false) >= B_OK);
511
512	int propLen;
513	const uint32 *prop = (uint32*)fdt_getprop(gFDT, (int)fdtNode, "interrupts-extended",
514		&propLen);
515	if (prop == NULL) {
516		uint32 interruptParent = fdt_get_interrupt_parent(dev, fdtNode);
517		uint32 interruptCells = fdt_get_interrupt_cells(interruptParent);
518
519		prop = (uint32*)fdt_getprop(gFDT, (int)fdtNode, "interrupts",
520			&propLen);
521		if (prop == NULL)
522			return false;
523
524		if ((index + 1) * interruptCells * sizeof(uint32) > (uint32)propLen)
525			return false;
526
527		uint32 offset = interruptCells * index;
528		uint32 interruptNumber = 0;
529
530		if ((interruptCells == 1) || (interruptCells == 2)) {
531			 interruptNumber = fdt32_to_cpu(*(prop + offset));
532		} else if (interruptCells == 3) {
533			uint32 interruptType = fdt32_to_cpu(prop[offset + GIC_INTERRUPT_CELL_TYPE]);
534			interruptNumber = fdt32_to_cpu(prop[offset + GIC_INTERRUPT_CELL_ID]);
535
536			if (interruptType == GIC_INTERRUPT_TYPE_SPI)
537				interruptNumber += GIC_INTERRUPT_BASE_SPI;
538			else if (interruptType == GIC_INTERRUPT_TYPE_PPI)
539				interruptNumber += GIC_INTERRUPT_BASE_PPI;
540		} else {
541			panic("unsupported interruptCells");
542		}
543
544		if (interrupt != NULL)
545			*interrupt = interruptNumber;
546
547		if (interruptController != NULL && interruptParent != 0) {
548			fdt_bus* bus;
549			ASSERT(gDeviceManager->get_driver(dev->bus, NULL, (void**)&bus) >= B_OK);
550			*interruptController = fdt_bus_node_by_phandle(bus, interruptParent);
551		}
552
553		return true;
554	}
555
556	if ((index + 1) * 8 > (uint32)propLen)
557		return false;
558
559	if (interruptController != NULL) {
560		uint32 phandle = fdt32_to_cpu(*(prop + 2 * index));
561
562		fdt_bus* bus;
563		ASSERT(gDeviceManager->get_driver(
564			dev->bus, NULL, (void**)&bus) >= B_OK);
565
566		*interruptController = fdt_bus_node_by_phandle(bus, phandle);
567	}
568
569	if (interrupt != NULL)
570		*interrupt = fdt32_to_cpu(*(prop + 2 * index + 1));
571
572	return true;
573}
574
575
576static struct fdt_interrupt_map *
577fdt_device_get_interrupt_map(struct fdt_device* dev)
578{
579	int fdtNode;
580	ASSERT(gDeviceManager->get_attr_uint32(
581		dev->node, "fdt/node", (uint32*)&fdtNode, false) >= B_OK);
582
583	ObjectDeleter<struct fdt_interrupt_map> interrupt_map(new struct fdt_interrupt_map());
584
585	int intMapMaskLen;
586	const void* intMapMask = fdt_getprop(gFDT, fdtNode, "interrupt-map-mask",
587		&intMapMaskLen);
588
589	if (intMapMask == NULL || intMapMaskLen != 4 * 4) {
590		dprintf("  interrupt-map-mask property not found or invalid\n");
591		return NULL;
592	}
593
594	interrupt_map->childAddrMask = B_BENDIAN_TO_HOST_INT32(*((uint32*)intMapMask + 0));
595	interrupt_map->childIrqMask = B_BENDIAN_TO_HOST_INT32(*((uint32*)intMapMask + 3));
596
597	int intMapLen;
598	const void* intMapAddr = fdt_getprop(gFDT, fdtNode, "interrupt-map", &intMapLen);
599	if (intMapAddr == NULL) {
600		dprintf("  interrupt-map property not found\n");
601		return NULL;
602	}
603
604	int addressCells = 3;
605	int interruptCells = 1;
606	int phandleCells = 1;
607
608	const void *property;
609
610	property = fdt_getprop(gFDT, fdtNode, "#address-cells", NULL);
611	if (property != NULL)
612		addressCells = B_BENDIAN_TO_HOST_INT32(*(uint32*)property);
613
614	property = fdt_getprop(gFDT, fdtNode, "#interrupt-cells", NULL);
615	if (property != NULL)
616		interruptCells = B_BENDIAN_TO_HOST_INT32(*(uint32*)property);
617
618	uint32_t *it = (uint32_t*)intMapAddr;
619	while ((uint8_t*)it - (uint8_t*)intMapAddr < intMapLen) {
620		struct fdt_interrupt_map_entry irqEntry;
621
622		irqEntry.childAddr = B_BENDIAN_TO_HOST_INT32(*it);
623		it += addressCells;
624
625		irqEntry.childIrq = B_BENDIAN_TO_HOST_INT32(*it);
626		it += interruptCells;
627
628		irqEntry.parentIrqCtrl = B_BENDIAN_TO_HOST_INT32(*it);
629		it += phandleCells;
630
631		int parentAddressCells = 0;
632		int parentInterruptCells = 1;
633
634		int interruptParent = fdt_node_offset_by_phandle(gFDT, irqEntry.parentIrqCtrl);
635		if (interruptParent >= 0) {
636			property = fdt_getprop(gFDT, interruptParent, "#address-cells", NULL);
637			if (property != NULL)
638				parentAddressCells = B_BENDIAN_TO_HOST_INT32(*(uint32*)property);
639
640			property = fdt_getprop(gFDT, interruptParent, "#interrupt-cells", NULL);
641			if (property != NULL)
642				parentInterruptCells = B_BENDIAN_TO_HOST_INT32(*(uint32*)property);
643		}
644
645		it += parentAddressCells;
646
647		if ((parentInterruptCells == 1) || (parentInterruptCells == 2)) {
648			irqEntry.parentIrq = B_BENDIAN_TO_HOST_INT32(*it);
649		} else if (parentInterruptCells == 3) {
650			uint32 interruptType = fdt32_to_cpu(it[GIC_INTERRUPT_CELL_TYPE]);
651			uint32 interruptNumber = fdt32_to_cpu(it[GIC_INTERRUPT_CELL_ID]);
652
653			if (interruptType == GIC_INTERRUPT_TYPE_SPI)
654				irqEntry.parentIrq = interruptNumber + GIC_INTERRUPT_BASE_SPI;
655			else if (interruptType == GIC_INTERRUPT_TYPE_PPI)
656				irqEntry.parentIrq = interruptNumber + GIC_INTERRUPT_BASE_PPI;
657			else
658				irqEntry.parentIrq = interruptNumber;
659		}
660		it += parentInterruptCells;
661
662		interrupt_map->fInterruptMap.PushBack(irqEntry);
663	}
664
665	return interrupt_map.Detach();
666}
667
668
669static void
670fdt_device_print_interrupt_map(struct fdt_interrupt_map* interruptMap)
671{
672	if (interruptMap == NULL)
673		return;
674
675	dprintf("interrupt_map_mask: 0x%08" PRIx32 ", 0x%08" PRIx32 "\n",
676		interruptMap->childAddrMask, interruptMap->childIrqMask);
677	dprintf("interrupt_map:\n");
678
679	for (Vector<struct fdt_interrupt_map_entry>::Iterator it = interruptMap->fInterruptMap.Begin();
680		it != interruptMap->fInterruptMap.End();
681		it++) {
682
683		dprintf("childAddr=0x%08" PRIx32 ", childIrq=%" PRIu32 ", parentIrqCtrl=%" PRIu32 ", parentIrq=%" PRIu32 "\n",
684			it->childAddr, it->childIrq, it->parentIrqCtrl, it->parentIrq);
685	}
686}
687
688
689static uint32
690fdt_device_lookup_interrupt_map(struct fdt_interrupt_map* interruptMap, uint32 childAddr, uint32 childIrq)
691{
692	if (interruptMap == NULL)
693		return 0xffffffff;
694
695	childAddr &= interruptMap->childAddrMask;
696	childIrq &= interruptMap->childIrqMask;
697
698	for (Vector<struct fdt_interrupt_map_entry>::Iterator it = interruptMap->fInterruptMap.Begin();
699			it != interruptMap->fInterruptMap.End(); it++) {
700		if ((it->childAddr == childAddr) && (it->childIrq == childIrq))
701			return it->parentIrq;
702	}
703
704	return 0xffffffff;
705}
706
707
708//#pragma mark devfs node
709
710
711static status_t
712fdt_devfs_node_read(void *cookie, off_t pos, void *buffer, size_t *_length)
713{
714	if (pos < 0)
715		return B_BAD_VALUE;
716
717	size_t size = fdt_totalsize(gFDT);
718	if ((uint64)pos >= size) {
719		*_length = 0;
720		return B_OK;
721	}
722	size_t readSize = *_length;
723	if (pos + readSize > size)
724		readSize = size - pos;
725
726	status_t res = user_memcpy(buffer, (uint8*)gFDT + pos, readSize);
727	if (res < B_OK)
728		return res;
729
730	*_length = readSize;
731	return B_OK;
732}
733
734
735//#pragma mark -
736
737fdt_bus_module_info gBusModule = {
738	{
739		{
740			"bus_managers/fdt/root/driver_v1",
741			0,
742			fdt_bus_std_ops
743		},
744		fdt_bus_supports_device,
745		fdt_bus_register_device,
746		fdt_bus_init,
747		fdt_bus_uninit,
748		fdt_bus_register_child_devices,
749		NULL,	// rescan devices
750		NULL,	// device removed
751	},
752	fdt_bus_node_by_phandle,
753};
754
755
756fdt_device_module_info gDeviceModule = {
757	{
758		{
759			"bus_managers/fdt/driver_v1",
760			0,
761			fdt_device_std_ops
762		},
763
764		NULL,		// supports device
765		NULL,		// register device (our parent registered us)
766		fdt_device_init_driver,
767		fdt_device_uninit_driver,
768		fdt_device_register_child_devices,
769		NULL,		// rescan devices
770		NULL,		// device removed
771	},
772	fdt_device_get_bus,
773	fdt_device_get_name,
774	fdt_device_get_prop,
775	fdt_device_get_reg,
776	fdt_device_get_interrupt,
777	fdt_device_get_interrupt_map,
778	fdt_device_print_interrupt_map,
779	fdt_device_lookup_interrupt_map,
780};
781
782
783device_module_info gDevfsNodeModule = {
784	.info = {
785			.name = "bus_managers/fdt/device/v1"
786	},
787	.init_device = [](void *driverCookie, void **_deviceCookie) {
788		*_deviceCookie = NULL;
789		return B_OK;
790	},
791	.uninit_device = [](void *deviceCookie) {},
792	.open = [](void *deviceCookie, const char *path, int openMode, void **_cookie) {
793		return B_OK;
794	},
795	.close = [](void *cookie) {
796		return B_OK;
797	},
798	.free = [](void *cookie) {
799		return B_OK;
800	},
801	.read = fdt_devfs_node_read,
802	.control = [](void *cookie, uint32 op, void *buffer, size_t length) {
803		return B_DEV_INVALID_IOCTL;
804	},
805};
806
807
808module_info* modules[] = {
809	(module_info*)&gBusModule,
810	(module_info*)&gDeviceModule,
811	(module_info*)&gDevfsNodeModule,
812	NULL
813};
814
815module_dependency module_dependencies[] = {
816	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager },
817	{ NULL }
818};
819