xref: /haiku/src/system/kernel/device_manager/legacy_drivers.cpp (revision 9d62be21bf56cc7f917059ebe274dfcef033d938)
1 /*
2  * Copyright 2002-2011, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "legacy_drivers.h"
8 
9 #include <dirent.h>
10 #include <errno.h>
11 #include <new>
12 #include <stdio.h>
13 
14 #include <FindDirectory.h>
15 #include <image.h>
16 #include <NodeMonitor.h>
17 
18 #include <boot_device.h>
19 #include <boot/kernel_args.h>
20 #include <elf.h>
21 #include <find_directory_private.h>
22 #include <fs/devfs.h>
23 #include <fs/KPath.h>
24 #include <fs/node_monitor.h>
25 #include <Notifications.h>
26 #include <safemode.h>
27 #include <util/DoublyLinkedList.h>
28 #include <util/OpenHashTable.h>
29 #include <util/Stack.h>
30 #include <vfs.h>
31 
32 #include "AbstractModuleDevice.h"
33 #include "devfs_private.h"
34 
35 
36 //#define TRACE_LEGACY_DRIVERS
37 #ifdef TRACE_LEGACY_DRIVERS
38 #	define TRACE(x) dprintf x
39 #else
40 #	define TRACE(x)
41 #endif
42 
43 #define DRIVER_HASH_SIZE 16
44 
45 
46 namespace {
47 
48 struct legacy_driver;
49 
50 class LegacyDevice : public AbstractModuleDevice,
51 	public DoublyLinkedListLinkImpl<LegacyDevice> {
52 public:
53 							LegacyDevice(legacy_driver* driver,
54 								const char* path, device_hooks* hooks);
55 	virtual					~LegacyDevice();
56 
57 			status_t		InitCheck() const;
58 
59 	virtual	status_t		InitDevice();
60 	virtual	void			UninitDevice();
61 
62 	virtual	void			Removed();
63 
64 			void			SetHooks(device_hooks* hooks);
65 
66 			legacy_driver*	Driver() const { return fDriver; }
67 			const char*		Path() const { return fPath; }
68 			device_hooks*	Hooks() const { return fHooks; }
69 
70 	virtual	status_t		Open(const char* path, int openMode,
71 								void** _cookie);
72 	virtual	status_t		Select(void* cookie, uint8 event, selectsync* sync);
73 
74 	virtual	status_t		Control(void* cookie, int32 op, void* buffer, size_t length);
75 
76 			bool			Republished() const { return fRepublished; }
77 			void			SetRepublished(bool republished)
78 								{ fRepublished = republished; }
79 
80 			void			SetRemovedFromParent(bool removed)
81 								{ fRemovedFromParent = removed; }
82 
83 private:
84 	legacy_driver*			fDriver;
85 	const char*				fPath;
86 	device_hooks*			fHooks;
87 	bool					fRepublished;
88 	bool					fRemovedFromParent;
89 };
90 
91 typedef DoublyLinkedList<LegacyDevice> DeviceList;
92 
93 struct legacy_driver {
94 	legacy_driver*	next;
95 	const char*		path;
96 	const char*		name;
97 	dev_t			device;
98 	ino_t			node;
99 	timespec		last_modified;
100 	image_id		image;
101 	uint32			devices_used;
102 	bool			binary_updated;
103 	int32			priority;
104 	DeviceList		devices;
105 
106 	// driver image information
107 	int32			api_version;
108 	device_hooks*	(*find_device)(const char *);
109 	const char**	(*publish_devices)(void);
110 	status_t		(*uninit_driver)(void);
111 	status_t		(*uninit_hardware)(void);
112 };
113 
114 
115 enum driver_event_type {
116 	kAddDriver,
117 	kRemoveDriver,
118 	kAddWatcher,
119 	kRemoveWatcher
120 };
121 
122 struct driver_event : DoublyLinkedListLinkImpl<driver_event> {
123 	driver_event(driver_event_type _type) : type(_type) {}
124 
125 	struct ref {
126 		dev_t		device;
127 		ino_t		node;
128 	};
129 
130 	driver_event_type	type;
131 	union {
132 		char			path[B_PATH_NAME_LENGTH];
133 		ref				node;
134 	};
135 };
136 
137 typedef DoublyLinkedList<driver_event> DriverEventList;
138 
139 
140 struct driver_entry : DoublyLinkedListLinkImpl<driver_entry> {
141 	char*			path;
142 	dev_t			device;
143 	ino_t			node;
144 	int32			busses;
145 };
146 
147 typedef DoublyLinkedList<driver_entry> DriverEntryList;
148 
149 
150 struct node_entry : DoublyLinkedListLinkImpl<node_entry> {
151 };
152 
153 typedef DoublyLinkedList<node_entry> NodeList;
154 
155 
156 struct directory_node_entry {
157 	directory_node_entry*	hash_link;
158 	ino_t					node;
159 };
160 
161 struct DirectoryNodeHashDefinition {
162 	typedef ino_t* KeyType;
163 	typedef directory_node_entry ValueType;
164 
165 	size_t HashKey(ino_t* key) const
166 		{ return _Hash(*key); }
167 	size_t Hash(directory_node_entry* entry) const
168 		{ return _Hash(entry->node); }
169 	bool Compare(ino_t* key, directory_node_entry* entry) const
170 		{ return *key == entry->node; }
171 	directory_node_entry*&
172 		GetLink(directory_node_entry* entry) const
173 		{ return entry->hash_link; }
174 
175 	uint32 _Hash(ino_t node) const
176 		{ return (uint32)(node >> 32) + (uint32)node; }
177 };
178 
179 typedef BOpenHashTable<DirectoryNodeHashDefinition> DirectoryNodeHash;
180 
181 class DirectoryIterator {
182 public:
183 						DirectoryIterator(const char *path,
184 							const char *subPath = NULL, bool recursive = false);
185 						~DirectoryIterator();
186 
187 			void		SetTo(const char *path, const char *subPath = NULL,
188 							bool recursive = false);
189 
190 			status_t	GetNext(KPath &path, struct stat &stat);
191 			const char*	CurrentName() const { return fCurrentName; }
192 
193 			void		Unset();
194 			void		AddPath(const char *path, const char *subPath = NULL);
195 
196 private:
197 	Stack<KPath*>		fPaths;
198 	bool				fRecursive;
199 	DIR*				fDirectory;
200 	KPath*				fBasePath;
201 	const char*			fCurrentName;
202 };
203 
204 
205 class DirectoryWatcher : public NotificationListener {
206 public:
207 						DirectoryWatcher();
208 	virtual				~DirectoryWatcher();
209 
210 	virtual void		EventOccurred(NotificationService& service,
211 							const KMessage* event);
212 };
213 
214 class DriverWatcher : public NotificationListener {
215 public:
216 						DriverWatcher();
217 	virtual				~DriverWatcher();
218 
219 	virtual void		EventOccurred(NotificationService& service,
220 							const KMessage* event);
221 };
222 
223 
224 struct DriverHash {
225 	typedef const char*			KeyType;
226 	typedef legacy_driver		ValueType;
227 
228 	size_t HashKey(KeyType key) const
229 	{
230 		return hash_hash_string(key);
231 	}
232 
233 	size_t Hash(ValueType* driver) const
234 	{
235 		return HashKey(driver->name);
236 	}
237 
238 	bool Compare(KeyType key, ValueType* driver) const
239 	{
240 		return strcmp(driver->name, key) == 0;
241 	}
242 
243 	ValueType*& GetLink(ValueType* value) const
244 	{
245 		return value->next;
246 	}
247 };
248 
249 typedef BOpenHashTable<DriverHash> DriverTable;
250 
251 
252 }	// unnamed namespace
253 
254 
255 static status_t unload_driver(legacy_driver *driver);
256 static status_t load_driver(legacy_driver *driver);
257 
258 
259 static const directory_which kDriverPaths[] = {
260 	B_USER_NONPACKAGED_ADDONS_DIRECTORY,
261 	B_USER_ADDONS_DIRECTORY,
262 	B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY,
263 	B_SYSTEM_ADDONS_DIRECTORY
264 };
265 
266 static DriverWatcher sDriverWatcher;
267 static int32 sDriverEventsPending;
268 static DriverEventList sDriverEvents;
269 static mutex sDriverEventsLock = MUTEX_INITIALIZER("driver events");
270 	// inner lock, protects the sDriverEvents list only
271 static DirectoryWatcher sDirectoryWatcher;
272 static DirectoryNodeHash sDirectoryNodeHash;
273 static recursive_lock sLock;
274 static bool sWatching;
275 
276 static DriverTable* sDriverHash;
277 
278 
279 //	#pragma mark - driver private
280 
281 
282 /*!	Collects all published devices of a driver, compares them to what the
283 	driver would publish now, and then publishes/unpublishes the devices
284 	as needed.
285 	If the driver does not publish any devices anymore, it is unloaded.
286 */
287 static status_t
288 republish_driver(legacy_driver* driver)
289 {
290 	if (driver->image < 0) {
291 		// The driver is not yet loaded - go through the normal load procedure
292 		return load_driver(driver);
293 	}
294 
295 	// mark all devices
296 	DeviceList::Iterator iterator = driver->devices.GetIterator();
297 	while (LegacyDevice* device = iterator.Next()) {
298 		device->SetRepublished(false);
299 	}
300 
301 	// now ask the driver for it's currently published devices
302 	const char** devicePaths = driver->publish_devices();
303 
304 	int32 exported = 0;
305 	for (; devicePaths != NULL && devicePaths[0]; devicePaths++) {
306 		LegacyDevice* device;
307 
308 		iterator = driver->devices.GetIterator();
309 		while ((device = iterator.Next()) != NULL) {
310 			if (!strncmp(device->Path(), devicePaths[0], B_PATH_NAME_LENGTH)) {
311 				// mark device as republished
312 				device->SetRepublished(true);
313 				exported++;
314 				break;
315 			}
316 		}
317 
318 		device_hooks* hooks = driver->find_device(devicePaths[0]);
319 		if (hooks == NULL)
320 			continue;
321 
322 		if (device != NULL) {
323 			// update hooks
324 			device->SetHooks(hooks);
325 			continue;
326 		}
327 
328 		// the device was not present before -> publish it now
329 		TRACE(("devfs: publishing new device \"%s\"\n", devicePaths[0]));
330 		device = new(std::nothrow) LegacyDevice(driver, devicePaths[0], hooks);
331 		if (device != NULL && device->InitCheck() == B_OK
332 			&& devfs_publish_device(devicePaths[0], device) == B_OK) {
333 			driver->devices.Add(device);
334 			exported++;
335 		} else
336 			delete device;
337 	}
338 
339 	// remove all devices that weren't republished
340 	iterator = driver->devices.GetIterator();
341 	while (LegacyDevice* device = iterator.Next()) {
342 		if (device->Republished())
343 			continue;
344 
345 		TRACE(("devfs: unpublishing no more present \"%s\"\n", device->Path()));
346 		iterator.Remove();
347 		device->SetRemovedFromParent(true);
348 
349 		devfs_unpublish_device(device, true);
350 	}
351 
352 	if (exported == 0 && driver->devices_used == 0 && gBootDevice >= 0) {
353 		TRACE(("devfs: driver \"%s\" does not publish any more nodes and is "
354 			"unloaded\n", driver->path));
355 		unload_driver(driver);
356 	}
357 
358 	return B_OK;
359 }
360 
361 
362 static status_t
363 load_driver(legacy_driver* driver)
364 {
365 	status_t (*init_hardware)(void);
366 	status_t (*init_driver)(void);
367 	status_t status;
368 
369 	driver->binary_updated = false;
370 
371 	// load the module
372 	image_id image = driver->image;
373 	if (image < 0) {
374 		image = load_kernel_add_on(driver->path);
375 		if (image < 0)
376 			return image;
377 	}
378 
379 	// For a valid device driver the following exports are required
380 
381 	int32* apiVersion;
382 	if (get_image_symbol(image, "api_version", B_SYMBOL_TYPE_DATA,
383 			(void**)&apiVersion) == B_OK) {
384 #if B_CUR_DRIVER_API_VERSION != 2
385 		// just in case someone decides to bump up the api version
386 #error Add checks here for new vs old api version!
387 #endif
388 		if (*apiVersion > B_CUR_DRIVER_API_VERSION) {
389 			dprintf("devfs: \"%s\" api_version %" B_PRId32 " not handled\n",
390 				driver->name, *apiVersion);
391 			status = B_BAD_VALUE;
392 			goto error1;
393 		}
394 		if (*apiVersion < 1) {
395 			dprintf("devfs: \"%s\" api_version invalid\n", driver->name);
396 			status = B_BAD_VALUE;
397 			goto error1;
398 		}
399 
400 		driver->api_version = *apiVersion;
401 	} else
402 		dprintf("devfs: \"%s\" api_version missing\n", driver->name);
403 
404 	if (get_image_symbol(image, "publish_devices", B_SYMBOL_TYPE_TEXT,
405 				(void**)&driver->publish_devices) != B_OK
406 		|| get_image_symbol(image, "find_device", B_SYMBOL_TYPE_TEXT,
407 				(void**)&driver->find_device) != B_OK) {
408 		dprintf("devfs: \"%s\" mandatory driver symbol(s) missing!\n",
409 			driver->name);
410 		status = B_BAD_VALUE;
411 		goto error1;
412 	}
413 
414 	// Init the driver
415 
416 	if (get_image_symbol(image, "init_hardware", B_SYMBOL_TYPE_TEXT,
417 			(void**)&init_hardware) == B_OK
418 		&& (status = init_hardware()) != B_OK) {
419 		TRACE(("%s: init_hardware() failed: %s\n", driver->name,
420 			strerror(status)));
421 		status = ENXIO;
422 		goto error1;
423 	}
424 
425 	if (get_image_symbol(image, "init_driver", B_SYMBOL_TYPE_TEXT,
426 			(void**)&init_driver) == B_OK
427 		&& (status = init_driver()) != B_OK) {
428 		TRACE(("%s: init_driver() failed: %s\n", driver->name,
429 			strerror(status)));
430 		status = ENXIO;
431 		goto error2;
432 	}
433 
434 	// resolve and cache those for the driver unload code
435 	if (get_image_symbol(image, "uninit_driver", B_SYMBOL_TYPE_TEXT,
436 		(void**)&driver->uninit_driver) != B_OK)
437 		driver->uninit_driver = NULL;
438 	if (get_image_symbol(image, "uninit_hardware", B_SYMBOL_TYPE_TEXT,
439 		(void**)&driver->uninit_hardware) != B_OK)
440 		driver->uninit_hardware = NULL;
441 
442 	// The driver has successfully been initialized, now we can
443 	// finally publish its device entries
444 
445 	driver->image = image;
446 	return republish_driver(driver);
447 
448 error2:
449 	if (driver->uninit_hardware)
450 		driver->uninit_hardware();
451 
452 error1:
453 	if (driver->image < 0) {
454 		unload_kernel_add_on(image);
455 		driver->image = status;
456 	}
457 
458 	return status;
459 }
460 
461 
462 static status_t
463 unload_driver(legacy_driver* driver)
464 {
465 	if (driver->image < 0) {
466 		// driver is not currently loaded
467 		return B_NO_INIT;
468 	}
469 
470 	if (driver->uninit_driver)
471 		driver->uninit_driver();
472 
473 	if (driver->uninit_hardware)
474 		driver->uninit_hardware();
475 
476 	unload_kernel_add_on(driver->image);
477 	driver->image = -1;
478 	driver->binary_updated = false;
479 	driver->find_device = NULL;
480 	driver->publish_devices = NULL;
481 	driver->uninit_driver = NULL;
482 	driver->uninit_hardware = NULL;
483 
484 	return B_OK;
485 }
486 
487 
488 /*!	Unpublishes all devices belonging to the \a driver. */
489 static void
490 unpublish_driver(legacy_driver* driver)
491 {
492 	while (LegacyDevice* device = driver->devices.RemoveHead()) {
493 		device->SetRemovedFromParent(true);
494 		devfs_unpublish_device(device, true);
495 	}
496 }
497 
498 
499 static void
500 change_driver_watcher(dev_t device, ino_t node, bool add)
501 {
502 	if (device == -1)
503 		return;
504 
505 	driver_event* event = new (std::nothrow) driver_event(
506 		add ? kAddWatcher : kRemoveWatcher);
507 	if (event == NULL)
508 		return;
509 
510 	event->node.device = device;
511 	event->node.node = node;
512 
513 	MutexLocker _(sDriverEventsLock);
514 	sDriverEvents.Add(event);
515 
516 	atomic_add(&sDriverEventsPending, 1);
517 }
518 
519 
520 static int32
521 get_priority(const char* path)
522 {
523 	// TODO: would it be better to initialize a static structure here
524 	// using find_directory()?
525 	const directory_which whichPath[] = {
526 		B_SYSTEM_DIRECTORY,
527 		B_SYSTEM_NONPACKAGED_DIRECTORY,
528 		B_USER_DIRECTORY
529 	};
530 	KPath pathBuffer;
531 
532 	for (uint32 index = 0; index < sizeof(whichPath) / sizeof(whichPath[0]);
533 			index++) {
534 		if (__find_directory(whichPath[index], gBootDevice, false,
535 			pathBuffer.LockBuffer(), pathBuffer.BufferSize()) == B_OK) {
536 			pathBuffer.UnlockBuffer();
537 			if (!strncmp(pathBuffer.Path(), path, pathBuffer.BufferSize()))
538 				return index;
539 		} else
540 			pathBuffer.UnlockBuffer();
541 	}
542 
543 	return -1;
544 }
545 
546 
547 static const char*
548 get_leaf(const char* path)
549 {
550 	const char* name = strrchr(path, '/');
551 	if (name == NULL)
552 		return path;
553 
554 	return name + 1;
555 }
556 
557 
558 static legacy_driver*
559 find_driver(dev_t device, ino_t node)
560 {
561 	DriverTable::Iterator iterator(sDriverHash);
562 	while (iterator.HasNext()) {
563 		legacy_driver* driver = iterator.Next();
564 		if (driver->device == device && driver->node == node)
565 			return driver;
566 	}
567 
568 	return NULL;
569 }
570 
571 
572 static status_t
573 add_driver(const char* path, image_id image)
574 {
575 	// Check if we already know this driver
576 
577 	struct stat stat;
578 	if (image >= 0) {
579 		// The image ID should be a small number and hopefully the boot FS
580 		// doesn't use small negative values -- if it is inode based, we should
581 		// be relatively safe.
582 		stat.st_dev = -1;
583 		stat.st_ino = -1;
584 	} else {
585 		if (::stat(path, &stat) != 0)
586 			return errno;
587 	}
588 
589 	int32 priority = get_priority(path);
590 
591 	RecursiveLocker _(sLock);
592 
593 	legacy_driver* driver = sDriverHash->Lookup(get_leaf(path));
594 	if (driver != NULL) {
595 		// we know this driver
596 		if (strcmp(driver->path, path) != 0) {
597 			// TODO: do properly, but for now we just update the path if it
598 			// isn't the same anymore so rescanning of drivers will work in
599 			// case this driver was loaded so early that it has a boot module
600 			// path and not a proper driver path
601 			free((char*)driver->path);
602 			driver->path = strdup(path);
603 			driver->name = get_leaf(driver->path);
604 			driver->binary_updated = true;
605 		}
606 
607 		// TODO: check if this driver is a different one and has precendence
608 		// (ie. common supersedes system).
609 		//dprintf("new driver has priority %ld, old %ld\n", priority, driver->priority);
610 		if (priority >= driver->priority) {
611 			driver->binary_updated = true;
612 			return B_OK;
613 		}
614 
615 		// TODO: test for changes here and/or via node monitoring and reload
616 		//	the driver if necessary
617 		if (driver->image < B_OK)
618 			return driver->image;
619 
620 		return B_OK;
621 	}
622 
623 	// we don't know this driver, create a new entry for it
624 
625 	driver = (legacy_driver*)malloc(sizeof(legacy_driver));
626 	if (driver == NULL)
627 		return B_NO_MEMORY;
628 
629 	driver->path = strdup(path);
630 	if (driver->path == NULL) {
631 		free(driver);
632 		return B_NO_MEMORY;
633 	}
634 
635 	driver->name = get_leaf(driver->path);
636 	driver->device = stat.st_dev;
637 	driver->node = stat.st_ino;
638 	driver->image = image;
639 	driver->last_modified = stat.st_mtim;
640 	driver->devices_used = 0;
641 	driver->binary_updated = false;
642 	driver->priority = priority;
643 
644 	driver->api_version = 1;
645 	driver->find_device = NULL;
646 	driver->publish_devices = NULL;
647 	driver->uninit_driver = NULL;
648 	driver->uninit_hardware = NULL;
649 	new(&driver->devices) DeviceList;
650 
651 	sDriverHash->Insert(driver);
652 	if (stat.st_dev > 0)
653 		change_driver_watcher(stat.st_dev, stat.st_ino, true);
654 
655 	// Even if loading the driver fails - its entry will stay with us
656 	// so that we don't have to go through it again
657 	return load_driver(driver);
658 }
659 
660 
661 /*!	This is no longer part of the public kernel API, so we just export the
662 	symbol
663 */
664 extern "C" status_t load_driver_symbols(const char* driverName);
665 status_t
666 load_driver_symbols(const char* driverName)
667 {
668 	// This is done globally for the whole kernel via the settings file.
669 	// We don't have to do anything here.
670 
671 	return B_OK;
672 }
673 
674 
675 static status_t
676 reload_driver(legacy_driver* driver)
677 {
678 	dprintf("devfs: reload driver \"%s\" (%" B_PRIdDEV ", %" B_PRIdINO ")\n",
679 		driver->name, driver->device, driver->node);
680 
681 	unload_driver(driver);
682 
683 	struct stat stat;
684 	if (::stat(driver->path, &stat) == 0
685 		&& (stat.st_dev != driver->device || stat.st_ino != driver->node)) {
686 		// The driver file has been changed, so we need to update its listener
687 		change_driver_watcher(driver->device, driver->node, false);
688 
689 		driver->device = stat.st_dev;
690 		driver->node = stat.st_ino;
691 
692 		change_driver_watcher(driver->device, driver->node, true);
693 	}
694 
695 	status_t status = load_driver(driver);
696 	if (status != B_OK)
697 		unpublish_driver(driver);
698 
699 	return status;
700 }
701 
702 
703 static void
704 handle_driver_events(void* /*_fs*/, int /*iteration*/)
705 {
706 	if (atomic_and(&sDriverEventsPending, 0) == 0)
707 		return;
708 
709 	// something happened, let's see what it was
710 
711 	while (true) {
712 		MutexLocker eventLocker(sDriverEventsLock);
713 
714 		driver_event* event = sDriverEvents.RemoveHead();
715 		if (event == NULL)
716 			break;
717 
718 		eventLocker.Unlock();
719 		TRACE(("driver event %p, type %d\n", event, event->type));
720 
721 		switch (event->type) {
722 			case kAddDriver:
723 			{
724 				// Add new drivers
725 				RecursiveLocker locker(sLock);
726 				TRACE(("  add driver %p\n", event->path));
727 
728 				legacy_driver* driver = sDriverHash->Lookup(
729 					get_leaf(event->path));
730 				if (driver == NULL)
731 					legacy_driver_add(event->path);
732 				else if (get_priority(event->path) >= driver->priority)
733 					driver->binary_updated = true;
734 				break;
735 			}
736 
737 			case kRemoveDriver:
738 			{
739 				// Mark removed drivers as updated
740 				RecursiveLocker locker(sLock);
741 				TRACE(("  remove driver %p\n", event->path));
742 
743 				legacy_driver* driver = sDriverHash->Lookup(
744 					get_leaf(event->path));
745 				if (driver != NULL
746 					&& get_priority(event->path) >= driver->priority)
747 					driver->binary_updated = true;
748 				break;
749 			}
750 
751 			case kAddWatcher:
752 				TRACE(("  add watcher %ld:%lld\n", event->node.device,
753 					event->node.node));
754 				add_node_listener(event->node.device, event->node.node,
755 					B_WATCH_STAT | B_WATCH_NAME, sDriverWatcher);
756 				break;
757 
758 			case kRemoveWatcher:
759 				TRACE(("  remove watcher %ld:%lld\n", event->node.device,
760 					event->node.node));
761 				remove_node_listener(event->node.device, event->node.node,
762 					sDriverWatcher);
763 				break;
764 		}
765 
766 		delete event;
767 	}
768 
769 	// Reload updated drivers
770 
771 	RecursiveLocker locker(sLock);
772 
773 	DriverTable::Iterator iterator(sDriverHash);
774 	while (iterator.HasNext()) {
775 		legacy_driver* driver = iterator.Next();
776 
777 		if (!driver->binary_updated || driver->devices_used != 0)
778 			continue;
779 
780 		// try to reload the driver
781 		reload_driver(driver);
782 	}
783 
784 	locker.Unlock();
785 }
786 
787 
788 //	#pragma mark - DriverWatcher
789 
790 
791 DriverWatcher::DriverWatcher()
792 {
793 }
794 
795 
796 DriverWatcher::~DriverWatcher()
797 {
798 }
799 
800 
801 void
802 DriverWatcher::EventOccurred(NotificationService& service,
803 	const KMessage* event)
804 {
805 	int32 opcode = event->GetInt32("opcode", -1);
806 	if (opcode != B_STAT_CHANGED
807 		|| (event->GetInt32("fields", 0) & B_STAT_MODIFICATION_TIME) == 0)
808 		return;
809 
810 	RecursiveLocker locker(sLock);
811 
812 	legacy_driver* driver = find_driver(event->GetInt32("device", -1),
813 		event->GetInt64("node", 0));
814 	if (driver == NULL)
815 		return;
816 
817 	driver->binary_updated = true;
818 
819 	if (driver->devices_used == 0) {
820 		// trigger a reload of the driver
821 		atomic_add(&sDriverEventsPending, 1);
822 	} else {
823 		// driver is in use right now
824 		dprintf("devfs: changed driver \"%s\" is still in use\n", driver->name);
825 	}
826 }
827 
828 
829 static void
830 dump_driver(legacy_driver* driver)
831 {
832 	kprintf("DEVFS DRIVER: %p\n", driver);
833 	kprintf(" name:           %s\n", driver->name);
834 	kprintf(" path:           %s\n", driver->path);
835 	kprintf(" image:          %" B_PRId32 "\n", driver->image);
836 	kprintf(" device:         %" B_PRIdDEV "\n", driver->device);
837 	kprintf(" node:           %" B_PRIdINO "\n", driver->node);
838 	kprintf(" last modified:  %" B_PRIdTIME ".%ld\n", driver->last_modified.tv_sec,
839 		driver->last_modified.tv_nsec);
840 	kprintf(" devs used:      %" B_PRIu32 "\n", driver->devices_used);
841 	kprintf(" devs published: %" B_PRId32 "\n", driver->devices.Count());
842 	kprintf(" binary updated: %d\n", driver->binary_updated);
843 	kprintf(" priority:       %" B_PRId32 "\n", driver->priority);
844 	kprintf(" api version:    %" B_PRId32 "\n", driver->api_version);
845 	kprintf(" hooks:          find_device %p, publish_devices %p\n"
846 		"                 uninit_driver %p, uninit_hardware %p\n",
847 		driver->find_device, driver->publish_devices, driver->uninit_driver,
848 		driver->uninit_hardware);
849 }
850 
851 
852 static int
853 dump_device(int argc, char** argv)
854 {
855 	if (argc < 2 || !strcmp(argv[1], "--help")) {
856 		kprintf("usage: %s [device]\n", argv[0]);
857 		return 0;
858 	}
859 
860 	LegacyDevice* device = (LegacyDevice*)parse_expression(argv[1]);
861 
862 	kprintf("LEGACY DEVICE: %p\n", device);
863 	kprintf(" path:     %s\n", device->Path());
864 	kprintf(" hooks:    %p\n", device->Hooks());
865 	device_hooks* hooks = device->Hooks();
866 	kprintf("  close()     %p\n", hooks->close);
867 	kprintf("  free()      %p\n", hooks->free);
868 	kprintf("  control()   %p\n", hooks->control);
869 	kprintf("  read()      %p\n", hooks->read);
870 	kprintf("  write()     %p\n", hooks->write);
871 	kprintf("  select()    %p\n", hooks->select);
872 	kprintf("  deselect()  %p\n", hooks->deselect);
873 	dump_driver(device->Driver());
874 
875 	return 0;
876 }
877 
878 
879 static int
880 dump_driver(int argc, char** argv)
881 {
882 	if (argc < 2) {
883 		// print list of all drivers
884 		kprintf("address    image used publ.   pri name\n");
885 		DriverTable::Iterator iterator(sDriverHash);
886 		while (iterator.HasNext()) {
887 			legacy_driver* driver = iterator.Next();
888 
889 			kprintf("%p  %5" B_PRId32 " %3" B_PRIu32 " %5" B_PRId32 " %c "
890 				"%3" B_PRId32 " %s\n", driver,
891 				driver->image < 0 ? -1 : driver->image,
892 				driver->devices_used, driver->devices.Count(),
893 				driver->binary_updated ? 'U' : ' ', driver->priority,
894 				driver->name);
895 		}
896 
897 		return 0;
898 	}
899 
900 	if (!strcmp(argv[1], "--help")) {
901 		kprintf("usage: %s [name]\n", argv[0]);
902 		return 0;
903 	}
904 
905 	legacy_driver* driver = sDriverHash->Lookup(argv[1]);
906 	if (driver == NULL) {
907 		kprintf("Driver named \"%s\" not found.\n", argv[1]);
908 		return 0;
909 	}
910 
911 	dump_driver(driver);
912 	return 0;
913 }
914 
915 
916 //	#pragma mark -
917 
918 
919 DirectoryIterator::DirectoryIterator(const char* path, const char* subPath,
920 		bool recursive)
921 	:
922 	fDirectory(NULL),
923 	fBasePath(NULL),
924 	fCurrentName(NULL)
925 {
926 	SetTo(path, subPath, recursive);
927 }
928 
929 
930 DirectoryIterator::~DirectoryIterator()
931 {
932 	Unset();
933 }
934 
935 
936 void
937 DirectoryIterator::SetTo(const char* path, const char* subPath, bool recursive)
938 {
939 	Unset();
940 	fRecursive = recursive;
941 
942 	if (path == NULL) {
943 		// add default paths
944 		KPath pathBuffer;
945 
946 		bool disableUserAddOns = get_safemode_boolean(
947 			B_SAFEMODE_DISABLE_USER_ADD_ONS, false);
948 
949 		for (uint32 i = 0; i < sizeof(kDriverPaths) / sizeof(kDriverPaths[0]); i++) {
950 			if (i < 3 && disableUserAddOns)
951 				continue;
952 
953 			if (__find_directory(kDriverPaths[i], gBootDevice, true,
954 					pathBuffer.LockBuffer(), pathBuffer.BufferSize()) == B_OK) {
955 				pathBuffer.UnlockBuffer();
956 				pathBuffer.Append("kernel");
957 				AddPath(pathBuffer.Path(), subPath);
958 			} else
959 				pathBuffer.UnlockBuffer();
960 		}
961 	} else
962 		AddPath(path, subPath);
963 }
964 
965 
966 status_t
967 DirectoryIterator::GetNext(KPath& path, struct stat& stat)
968 {
969 next_directory:
970 	while (fDirectory == NULL) {
971 		delete fBasePath;
972 		fBasePath = NULL;
973 
974 		if (!fPaths.Pop(&fBasePath))
975 			return B_ENTRY_NOT_FOUND;
976 
977 		fDirectory = opendir(fBasePath->Path());
978 	}
979 
980 next_entry:
981 	struct dirent* dirent = readdir(fDirectory);
982 	if (dirent == NULL) {
983 		// get over to next directory on the stack
984 		closedir(fDirectory);
985 		fDirectory = NULL;
986 
987 		goto next_directory;
988 	}
989 
990 	if (!strcmp(dirent->d_name, "..") || !strcmp(dirent->d_name, "."))
991 		goto next_entry;
992 
993 	fCurrentName = dirent->d_name;
994 
995 	path.SetTo(fBasePath->Path());
996 	path.Append(fCurrentName);
997 
998 	if (::stat(path.Path(), &stat) != 0)
999 		goto next_entry;
1000 
1001 	if (S_ISDIR(stat.st_mode) && fRecursive) {
1002 		KPath* nextPath = new(nothrow) KPath(path);
1003 		if (!nextPath)
1004 			return B_NO_MEMORY;
1005 		if (fPaths.Push(nextPath) != B_OK)
1006 			return B_NO_MEMORY;
1007 
1008 		goto next_entry;
1009 	}
1010 
1011 	return B_OK;
1012 }
1013 
1014 
1015 void
1016 DirectoryIterator::Unset()
1017 {
1018 	if (fDirectory != NULL) {
1019 		closedir(fDirectory);
1020 		fDirectory = NULL;
1021 	}
1022 
1023 	delete fBasePath;
1024 	fBasePath = NULL;
1025 
1026 	KPath* path;
1027 	while (fPaths.Pop(&path))
1028 		delete path;
1029 }
1030 
1031 
1032 void
1033 DirectoryIterator::AddPath(const char* basePath, const char* subPath)
1034 {
1035 	KPath* path = new(nothrow) KPath(basePath);
1036 	if (!path)
1037 		panic("out of memory");
1038 	if (subPath != NULL)
1039 		path->Append(subPath);
1040 
1041 	fPaths.Push(path);
1042 }
1043 
1044 
1045 //	#pragma mark -
1046 
1047 
1048 DirectoryWatcher::DirectoryWatcher()
1049 {
1050 }
1051 
1052 
1053 DirectoryWatcher::~DirectoryWatcher()
1054 {
1055 }
1056 
1057 
1058 void
1059 DirectoryWatcher::EventOccurred(NotificationService& service,
1060 	const KMessage* event)
1061 {
1062 	int32 opcode = event->GetInt32("opcode", -1);
1063 	dev_t device = event->GetInt32("device", -1);
1064 	ino_t directory = event->GetInt64("directory", -1);
1065 	const char* name = event->GetString("name", NULL);
1066 
1067 	if (opcode == B_ENTRY_MOVED) {
1068 		// Determine whether it's a move within, out of, or into one
1069 		// of our watched directories.
1070 		ino_t from = event->GetInt64("from directory", -1);
1071 		ino_t to = event->GetInt64("to directory", -1);
1072 		if (sDirectoryNodeHash.Lookup(&from) == NULL) {
1073 			directory = to;
1074 			opcode = B_ENTRY_CREATED;
1075 		} else if (sDirectoryNodeHash.Lookup(&to) == NULL) {
1076 			directory = from;
1077 			opcode = B_ENTRY_REMOVED;
1078 		} else {
1079 			// Move within, don't do anything for now
1080 			// TODO: adjust driver priority if necessary
1081 			return;
1082 		}
1083 	}
1084 
1085 	KPath path(B_PATH_NAME_LENGTH + 1);
1086 	if (path.InitCheck() != B_OK || vfs_entry_ref_to_path(device, directory,
1087 			name, true, path.LockBuffer(), path.BufferSize()) != B_OK)
1088 		return;
1089 
1090 	path.UnlockBuffer();
1091 
1092 	dprintf("driver \"%s\" %s\n", path.Leaf(),
1093 		opcode == B_ENTRY_CREATED ? "added" : "removed");
1094 
1095 	driver_event* driverEvent = new(std::nothrow) driver_event(
1096 		opcode == B_ENTRY_CREATED ? kAddDriver : kRemoveDriver);
1097 	if (driverEvent == NULL)
1098 		return;
1099 
1100 	strlcpy(driverEvent->path, path.Path(), sizeof(driverEvent->path));
1101 
1102 	MutexLocker _(sDriverEventsLock);
1103 	sDriverEvents.Add(driverEvent);
1104 	atomic_add(&sDriverEventsPending, 1);
1105 }
1106 
1107 
1108 //	#pragma mark -
1109 
1110 
1111 static void
1112 start_watching(const char* base, const char* sub)
1113 {
1114 	KPath path(base);
1115 	path.Append(sub);
1116 
1117 	// TODO: create missing directories?
1118 	struct stat stat;
1119 	if (::stat(path.Path(), &stat) != 0)
1120 		return;
1121 
1122 	add_node_listener(stat.st_dev, stat.st_ino, B_WATCH_DIRECTORY,
1123 		sDirectoryWatcher);
1124 
1125 	directory_node_entry* entry = new(std::nothrow) directory_node_entry;
1126 	if (entry != NULL) {
1127 		entry->node = stat.st_ino;
1128 		sDirectoryNodeHash.Insert(entry);
1129 	}
1130 }
1131 
1132 
1133 static struct driver_entry*
1134 new_driver_entry(const char* path, dev_t device, ino_t node)
1135 {
1136 	driver_entry* entry = new(std::nothrow) driver_entry;
1137 	if (entry == NULL)
1138 		return NULL;
1139 
1140 	entry->path = strdup(path);
1141 	if (entry->path == NULL) {
1142 		delete entry;
1143 		return NULL;
1144 	}
1145 
1146 	entry->device = device;
1147 	entry->node = node;
1148 	entry->busses = 0;
1149 	return entry;
1150 }
1151 
1152 
1153 /*!	Iterates over the given list and tries to load all drivers in that list.
1154 	The list is emptied and freed during the traversal.
1155 */
1156 static status_t
1157 try_drivers(DriverEntryList& list)
1158 {
1159 	while (true) {
1160 		driver_entry* entry = list.RemoveHead();
1161 		if (entry == NULL)
1162 			break;
1163 
1164 		image_id image = load_kernel_add_on(entry->path);
1165 		if (image >= 0) {
1166 			// check if it's an old-style driver
1167 			if (legacy_driver_add(entry->path) == B_OK) {
1168 				// we have a driver
1169 				dprintf("loaded driver %s\n", entry->path);
1170 			}
1171 
1172 			unload_kernel_add_on(image);
1173 		}
1174 
1175 		free(entry->path);
1176 		delete entry;
1177 	}
1178 
1179 	return B_OK;
1180 }
1181 
1182 
1183 static status_t
1184 probe_for_drivers(const char* type)
1185 {
1186 	TRACE(("probe_for_drivers(type = %s)\n", type));
1187 
1188 	if (gBootDevice < 0)
1189 		return B_OK;
1190 
1191 	DriverEntryList drivers;
1192 
1193 	// build list of potential drivers for that type
1194 
1195 	DirectoryIterator iterator(NULL, type, false);
1196 	struct stat stat;
1197 	KPath path;
1198 
1199 	while (iterator.GetNext(path, stat) == B_OK) {
1200 		if (S_ISDIR(stat.st_mode)) {
1201 			add_node_listener(stat.st_dev, stat.st_ino, B_WATCH_DIRECTORY,
1202 				sDirectoryWatcher);
1203 
1204 			directory_node_entry* entry
1205 				= new(std::nothrow) directory_node_entry;
1206 			if (entry != NULL) {
1207 				entry->node = stat.st_ino;
1208 				sDirectoryNodeHash.Insert(entry);
1209 			}
1210 
1211 			// We need to make sure that drivers in ie. "audio/raw/" can
1212 			// be found as well - therefore, we must make sure that "audio"
1213 			// exists on /dev.
1214 
1215 			size_t length = strlen("drivers/dev");
1216 			if (strncmp(type, "drivers/dev", length))
1217 				continue;
1218 
1219 			path.SetTo(type);
1220 			path.Append(iterator.CurrentName());
1221 			devfs_publish_directory(path.Path() + length + 1);
1222 			continue;
1223 		}
1224 
1225 		driver_entry* entry = new_driver_entry(path.Path(), stat.st_dev,
1226 			stat.st_ino);
1227 		if (entry == NULL)
1228 			return B_NO_MEMORY;
1229 
1230 		TRACE(("found potential driver: %s\n", path.Path()));
1231 		drivers.Add(entry);
1232 	}
1233 
1234 	if (drivers.IsEmpty())
1235 		return B_OK;
1236 
1237 	// ToDo: do something with the remaining drivers... :)
1238 	try_drivers(drivers);
1239 	return B_OK;
1240 }
1241 
1242 
1243 //	#pragma mark - LegacyDevice
1244 
1245 
1246 LegacyDevice::LegacyDevice(legacy_driver* driver, const char* path,
1247 		device_hooks* hooks)
1248 	:
1249 	fDriver(driver),
1250 	fRepublished(true),
1251 	fRemovedFromParent(false)
1252 {
1253 	fDeviceModule = (device_module_info*)malloc(sizeof(device_module_info));
1254 	if (fDeviceModule != NULL) {
1255 		memset(fDeviceModule, 0, sizeof(device_module_info));
1256 		SetHooks(hooks);
1257 	}
1258 
1259 	fDeviceData = this;
1260 	fPath = strdup(path);
1261 
1262 }
1263 
1264 
1265 LegacyDevice::~LegacyDevice()
1266 {
1267 	free(fDeviceModule);
1268 	free((char*)fPath);
1269 }
1270 
1271 
1272 status_t
1273 LegacyDevice::InitCheck() const
1274 {
1275 	return fDeviceModule != NULL && fPath != NULL ? B_OK : B_NO_MEMORY;
1276 }
1277 
1278 
1279 status_t
1280 LegacyDevice::InitDevice()
1281 {
1282 	RecursiveLocker _(sLock);
1283 
1284 	if (fInitialized++ > 0)
1285 		return B_OK;
1286 
1287 	if (fDriver != NULL && fDriver->devices_used == 0
1288 		&& (fDriver->image < 0 || fDriver->binary_updated)) {
1289 		status_t status = reload_driver(fDriver);
1290 		if (status < B_OK)
1291 			return status;
1292 	}
1293 
1294 	if (fDriver != NULL)
1295 		fDriver->devices_used++;
1296 
1297 	return B_OK;
1298 }
1299 
1300 
1301 void
1302 LegacyDevice::UninitDevice()
1303 {
1304 	RecursiveLocker _(sLock);
1305 
1306 	if (fInitialized-- > 1)
1307 		return;
1308 
1309 	if (fDriver != NULL) {
1310 		if (--fDriver->devices_used == 0 && fDriver->devices.IsEmpty())
1311 			unload_driver(fDriver);
1312 		fDriver = NULL;
1313 	}
1314 }
1315 
1316 
1317 void
1318 LegacyDevice::Removed()
1319 {
1320 	RecursiveLocker _(sLock);
1321 
1322 	if (!fRemovedFromParent && fDriver != NULL)
1323 		fDriver->devices.Remove(this);
1324 
1325 	delete this;
1326 }
1327 
1328 
1329 status_t
1330 LegacyDevice::Control(void* _cookie, int32 op, void* buffer, size_t length)
1331 {
1332 	switch (op) {
1333 		case B_GET_DRIVER_FOR_DEVICE:
1334 			if (length != 0 && length <= strlen(fDriver->path))
1335 				return ERANGE;
1336 			return user_strlcpy(static_cast<char*>(buffer), fDriver->path, length);
1337 		default:
1338 			return AbstractModuleDevice::Control(_cookie, op, buffer, length);
1339 	}
1340 }
1341 
1342 
1343 void
1344 LegacyDevice::SetHooks(device_hooks* hooks)
1345 {
1346 	// TODO: setup compatibility layer!
1347 	fHooks = hooks;
1348 
1349 	fDeviceModule->close = hooks->close;
1350 	fDeviceModule->free = hooks->free;
1351 	fDeviceModule->control = hooks->control;
1352 	fDeviceModule->read = hooks->read;
1353 	fDeviceModule->write = hooks->write;
1354 
1355 	if (fDriver == NULL || fDriver->api_version >= 2) {
1356 		// According to Be newsletter, vol II, issue 36,
1357 		// version 2 added readv/writev, which we don't support, but also
1358 		// select/deselect.
1359 		if (hooks->select != NULL) {
1360 			// Note we set the module's select to a non-null value to indicate
1361 			// that we have select. HasSelect() will therefore return the
1362 			// correct answer. As Select() is virtual our compatibility
1363 			// version below is going to be called though, that redirects to
1364 			// the proper select hook, so it is ok to set it to an invalid
1365 			// address here.
1366 			fDeviceModule->select = (status_t (*)(void*, uint8, selectsync*))~0;
1367 		}
1368 
1369 		fDeviceModule->deselect = hooks->deselect;
1370 	}
1371 }
1372 
1373 
1374 status_t
1375 LegacyDevice::Open(const char* path, int openMode, void** _cookie)
1376 {
1377 	return Hooks()->open(path, openMode, _cookie);
1378 }
1379 
1380 
1381 status_t
1382 LegacyDevice::Select(void* cookie, uint8 event, selectsync* sync)
1383 {
1384 	return Hooks()->select(cookie, event, 0, sync);
1385 }
1386 
1387 
1388 //	#pragma mark - kernel private API
1389 
1390 
1391 extern "C" void
1392 legacy_driver_add_preloaded(kernel_args* args)
1393 {
1394 	// NOTE: This function does not exit in case of error, since it
1395 	// needs to unload the images then. Also the return code of
1396 	// the path operations is kept separate from the add_driver()
1397 	// success, so that even if add_driver() fails for one driver, it
1398 	// is still tried for the other drivers.
1399 	// NOTE: The initialization success of the path objects is implicitely
1400 	// checked by the immediately following functions.
1401 	KPath basePath;
1402 	status_t status = __find_directory(B_SYSTEM_ADDONS_DIRECTORY,
1403 		gBootDevice, false, basePath.LockBuffer(), basePath.BufferSize());
1404 	if (status != B_OK) {
1405 		dprintf("legacy_driver_add_preloaded: find_directory() failed: "
1406 			"%s\n", strerror(status));
1407 	}
1408 	basePath.UnlockBuffer();
1409 	if (status == B_OK)
1410 		status = basePath.Append("kernel");
1411 	if (status != B_OK) {
1412 		dprintf("legacy_driver_add_preloaded: constructing base driver "
1413 			"path failed: %s\n", strerror(status));
1414 		return;
1415 	}
1416 
1417 	struct preloaded_image* image;
1418 	for (image = args->preloaded_images; image != NULL; image = image->next) {
1419 		if (image->is_module || image->id < 0)
1420 			continue;
1421 
1422 		KPath imagePath(basePath);
1423 		status = imagePath.Append(image->name);
1424 
1425 		// try to add the driver
1426 		TRACE(("legacy_driver_add_preloaded: adding driver %s\n",
1427 			imagePath.Path()));
1428 
1429 		if (status == B_OK)
1430 			status = add_driver(imagePath.Path(), image->id);
1431 		if (status != B_OK) {
1432 			dprintf("legacy_driver_add_preloaded: Failed to add \"%s\": %s\n",
1433 				(char*)image->name, strerror(status));
1434 			unload_kernel_add_on(image->id);
1435 		}
1436 	}
1437 }
1438 
1439 
1440 extern "C" status_t
1441 legacy_driver_add(const char* path)
1442 {
1443 	return add_driver(path, -1);
1444 }
1445 
1446 
1447 extern "C" status_t
1448 legacy_driver_publish(const char* path, device_hooks* hooks)
1449 {
1450 	// we don't have a driver, just publish the hooks
1451 	LegacyDevice* device = new(std::nothrow) LegacyDevice(NULL, path, hooks);
1452 	if (device == NULL)
1453 		return B_NO_MEMORY;
1454 
1455 	status_t status = device->InitCheck();
1456 	if (status == B_OK)
1457 		status = devfs_publish_device(path, device);
1458 
1459 	if (status != B_OK)
1460 		delete device;
1461 
1462 	return status;
1463 }
1464 
1465 
1466 extern "C" status_t
1467 legacy_driver_rescan(const char* driverName)
1468 {
1469 	RecursiveLocker locker(sLock);
1470 
1471 	legacy_driver* driver = sDriverHash->Lookup(driverName);
1472 	if (driver == NULL)
1473 		return B_ENTRY_NOT_FOUND;
1474 
1475 	// Republish the driver's entries
1476 	return republish_driver(driver);
1477 }
1478 
1479 
1480 extern "C" status_t
1481 legacy_driver_probe(const char* subPath)
1482 {
1483 	TRACE(("legacy_driver_probe(type = %s)\n", subPath));
1484 
1485 	char devicePath[64];
1486 	snprintf(devicePath, sizeof(devicePath), "drivers/dev%s%s",
1487 		subPath[0] ? "/" : "", subPath);
1488 
1489 	if (!sWatching && gBootDevice > 0) {
1490 		// We're probing the actual boot volume for the first time,
1491 		// let's watch its driver directories for changes
1492 		KPath path;
1493 
1494 		new(&sDirectoryWatcher) DirectoryWatcher;
1495 
1496 		bool disableUserAddOns = get_safemode_boolean(
1497 			B_SAFEMODE_DISABLE_USER_ADD_ONS, false);
1498 
1499 		for (uint32 i = 0; i < sizeof(kDriverPaths) / sizeof(kDriverPaths[0]); i++) {
1500 			if (i < 3 && disableUserAddOns)
1501 				continue;
1502 
1503 			if (__find_directory(kDriverPaths[i], gBootDevice, true,
1504 					path.LockBuffer(), path.BufferSize()) == B_OK) {
1505 				path.UnlockBuffer();
1506 				path.Append("kernel/drivers");
1507 
1508 				start_watching(path.Path(), "bin");
1509 			} else
1510 				path.UnlockBuffer();
1511 		}
1512 
1513 		sWatching = true;
1514 	}
1515 
1516 	return probe_for_drivers(devicePath);
1517 }
1518 
1519 
1520 extern "C" status_t
1521 legacy_driver_init(void)
1522 {
1523 	sDriverHash = new(std::nothrow) DriverTable();
1524 	if (sDriverHash == NULL || sDriverHash->Init(DRIVER_HASH_SIZE) != B_OK)
1525 		return B_NO_MEMORY;
1526 
1527 	recursive_lock_init(&sLock, "legacy driver");
1528 
1529 	new(&sDriverWatcher) DriverWatcher;
1530 	new(&sDriverEvents) DriverEventList;
1531 
1532 	register_kernel_daemon(&handle_driver_events, NULL, 10);
1533 		// once every second
1534 
1535 	add_debugger_command("legacy_driver", &dump_driver,
1536 		"info about a legacy driver entry");
1537 	add_debugger_command("legacy_device", &dump_device,
1538 		"info about a legacy device");
1539 
1540 	return B_OK;
1541 }
1542