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