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