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