xref: /haiku/src/system/kernel/device_manager/legacy_drivers.cpp (revision 776c58b2b56d8bcf33638a2ecb6c697f95a1cbf3)
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 %" B_PRId32 " not handled\n",
374 				driver->name, *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\" (%" B_PRIdDEV ", %" B_PRIdINO ")\n",
668 		driver->name, 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:          %" B_PRId32 "\n", driver->image);
829 	kprintf(" device:         %" B_PRIdDEV "\n", driver->device);
830 	kprintf(" node:           %" B_PRIdINO "\n", driver->node);
831 	kprintf(" last modified:  %" B_PRIdTIME ".%ld\n", driver->last_modified.tv_sec,
832 		driver->last_modified.tv_nsec);
833 	kprintf(" devs used:      %" B_PRIu32 "\n", driver->devices_used);
834 	kprintf(" devs published: %" B_PRId32 "\n", driver->devices.Count());
835 	kprintf(" binary updated: %d\n", driver->binary_updated);
836 	kprintf(" priority:       %" B_PRId32 "\n", driver->priority);
837 	kprintf(" api version:    %" B_PRId32 "\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  %5" B_PRId32 " %3" B_PRIu32 " %5" B_PRId32 " %c "
887 				"%3" B_PRId32 " %s\n", driver,
888 				driver->image < 0 ? -1 : driver->image,
889 				driver->devices_used, driver->devices.Count(),
890 				driver->binary_updated ? 'U' : ' ', driver->priority,
891 				driver->name);
892 		}
893 
894 		hash_close(sDriverHash, &iterator, false);
895 		return 0;
896 	}
897 
898 	if (!strcmp(argv[1], "--help")) {
899 		kprintf("usage: %s [name]\n", argv[0]);
900 		return 0;
901 	}
902 
903 	legacy_driver* driver = (legacy_driver*)hash_lookup(sDriverHash, argv[1]);
904 	if (driver == NULL) {
905 		kprintf("Driver named \"%s\" not found.\n", argv[1]);
906 		return 0;
907 	}
908 
909 	dump_driver(driver);
910 	return 0;
911 }
912 
913 
914 //	#pragma mark -
915 
916 
917 DirectoryIterator::DirectoryIterator(const char* path, const char* subPath,
918 		bool recursive)
919 	:
920 	fDirectory(NULL),
921 	fBasePath(NULL),
922 	fCurrentName(NULL)
923 {
924 	SetTo(path, subPath, recursive);
925 }
926 
927 
928 DirectoryIterator::~DirectoryIterator()
929 {
930 	Unset();
931 }
932 
933 
934 void
935 DirectoryIterator::SetTo(const char* path, const char* subPath, bool recursive)
936 {
937 	Unset();
938 	fRecursive = recursive;
939 
940 	if (path == NULL) {
941 		// add default paths
942 		const directory_which whichPath[] = {
943 			B_USER_ADDONS_DIRECTORY,
944 			B_COMMON_ADDONS_DIRECTORY,
945 			B_BEOS_ADDONS_DIRECTORY
946 		};
947 		KPath pathBuffer;
948 
949 		bool disableUserAddOns = get_safemode_boolean(
950 			B_SAFEMODE_DISABLE_USER_ADD_ONS, false);
951 
952 		for (uint32 i = 0; i < sizeof(whichPath) / sizeof(whichPath[0]); i++) {
953 			if (i < 2 && disableUserAddOns)
954 				continue;
955 
956 			if (find_directory(whichPath[i], gBootDevice, true,
957 					pathBuffer.LockBuffer(), pathBuffer.BufferSize()) == B_OK) {
958 				pathBuffer.UnlockBuffer();
959 				pathBuffer.Append("kernel");
960 				AddPath(pathBuffer.Path(), subPath);
961 			} else
962 				pathBuffer.UnlockBuffer();
963 		}
964 	} else
965 		AddPath(path, subPath);
966 }
967 
968 
969 status_t
970 DirectoryIterator::GetNext(KPath& path, struct stat& stat)
971 {
972 next_directory:
973 	while (fDirectory == NULL) {
974 		delete fBasePath;
975 		fBasePath = NULL;
976 
977 		if (!fPaths.Pop(&fBasePath))
978 			return B_ENTRY_NOT_FOUND;
979 
980 		fDirectory = opendir(fBasePath->Path());
981 	}
982 
983 next_entry:
984 	struct dirent* dirent = readdir(fDirectory);
985 	if (dirent == NULL) {
986 		// get over to next directory on the stack
987 		closedir(fDirectory);
988 		fDirectory = NULL;
989 
990 		goto next_directory;
991 	}
992 
993 	if (!strcmp(dirent->d_name, "..") || !strcmp(dirent->d_name, "."))
994 		goto next_entry;
995 
996 	fCurrentName = dirent->d_name;
997 
998 	path.SetTo(fBasePath->Path());
999 	path.Append(fCurrentName);
1000 
1001 	if (::stat(path.Path(), &stat) != 0)
1002 		goto next_entry;
1003 
1004 	if (S_ISDIR(stat.st_mode) && fRecursive) {
1005 		KPath *nextPath = new(nothrow) KPath(path);
1006 		if (!nextPath)
1007 			return B_NO_MEMORY;
1008 		if (fPaths.Push(nextPath) != B_OK)
1009 			return B_NO_MEMORY;
1010 
1011 		goto next_entry;
1012 	}
1013 
1014 	return B_OK;
1015 }
1016 
1017 
1018 void
1019 DirectoryIterator::Unset()
1020 {
1021 	if (fDirectory != NULL) {
1022 		closedir(fDirectory);
1023 		fDirectory = NULL;
1024 	}
1025 
1026 	delete fBasePath;
1027 	fBasePath = NULL;
1028 
1029 	KPath *path;
1030 	while (fPaths.Pop(&path))
1031 		delete path;
1032 }
1033 
1034 
1035 void
1036 DirectoryIterator::AddPath(const char* basePath, const char* subPath)
1037 {
1038 	KPath *path = new(nothrow) KPath(basePath);
1039 	if (!path)
1040 		panic("out of memory");
1041 	if (subPath != NULL)
1042 		path->Append(subPath);
1043 
1044 	fPaths.Push(path);
1045 }
1046 
1047 
1048 //	#pragma mark -
1049 
1050 
1051 DirectoryWatcher::DirectoryWatcher()
1052 {
1053 }
1054 
1055 
1056 DirectoryWatcher::~DirectoryWatcher()
1057 {
1058 }
1059 
1060 
1061 void
1062 DirectoryWatcher::EventOccurred(NotificationService& service,
1063 	const KMessage* event)
1064 {
1065 	int32 opcode = event->GetInt32("opcode", -1);
1066 	dev_t device = event->GetInt32("device", -1);
1067 	ino_t directory = event->GetInt64("directory", -1);
1068 	const char *name = event->GetString("name", NULL);
1069 
1070 	if (opcode == B_ENTRY_MOVED) {
1071 		// Determine whether it's a move within, out of, or into one
1072 		// of our watched directories.
1073 		ino_t from = event->GetInt64("from directory", -1);
1074 		ino_t to = event->GetInt64("to directory", -1);
1075 		if (sDirectoryNodeHash.Lookup(&from) == NULL) {
1076 			directory = to;
1077 			opcode = B_ENTRY_CREATED;
1078 		} else if (sDirectoryNodeHash.Lookup(&to) == NULL) {
1079 			directory = from;
1080 			opcode = B_ENTRY_REMOVED;
1081 		} else {
1082 			// Move within, don't do anything for now
1083 			// TODO: adjust driver priority if necessary
1084 			return;
1085 		}
1086 	}
1087 
1088 	KPath path(B_PATH_NAME_LENGTH + 1);
1089 	if (path.InitCheck() != B_OK || vfs_entry_ref_to_path(device, directory,
1090 			name, path.LockBuffer(), path.BufferSize()) != B_OK)
1091 		return;
1092 
1093 	path.UnlockBuffer();
1094 
1095 	dprintf("driver \"%s\" %s\n", path.Leaf(),
1096 		opcode == B_ENTRY_CREATED ? "added" : "removed");
1097 
1098 	driver_event* driverEvent = new(std::nothrow) driver_event(
1099 		opcode == B_ENTRY_CREATED ? kAddDriver : kRemoveDriver);
1100 	if (driverEvent == NULL)
1101 		return;
1102 
1103 	strlcpy(driverEvent->path, path.Path(), sizeof(driverEvent->path));
1104 
1105 	MutexLocker _(sDriverEventsLock);
1106 	sDriverEvents.Add(driverEvent);
1107 	atomic_add(&sDriverEventsPending, 1);
1108 }
1109 
1110 
1111 //	#pragma mark -
1112 
1113 
1114 static void
1115 start_watching(const char *base, const char *sub)
1116 {
1117 	KPath path(base);
1118 	path.Append(sub);
1119 
1120 	// TODO: create missing directories?
1121 	struct stat stat;
1122 	if (::stat(path.Path(), &stat) != 0)
1123 		return;
1124 
1125 	add_node_listener(stat.st_dev, stat.st_ino, B_WATCH_DIRECTORY,
1126 		sDirectoryWatcher);
1127 
1128 	directory_node_entry *entry = new(std::nothrow) directory_node_entry;
1129 	if (entry != NULL) {
1130 		entry->node = stat.st_ino;
1131 		sDirectoryNodeHash.Insert(entry);
1132 	}
1133 }
1134 
1135 
1136 static struct driver_entry*
1137 new_driver_entry(const char* path, dev_t device, ino_t node)
1138 {
1139 	driver_entry* entry = (driver_entry*)malloc(sizeof(driver_entry));
1140 	if (entry == NULL)
1141 		return NULL;
1142 
1143 	entry->path = strdup(path);
1144 	if (entry->path == NULL) {
1145 		free(entry);
1146 		return NULL;
1147 	}
1148 
1149 	entry->device = device;
1150 	entry->node = node;
1151 	entry->busses = 0;
1152 	return entry;
1153 }
1154 
1155 
1156 /*!	Iterates over the given list and tries to load all drivers in that list.
1157 	The list is emptied and freed during the traversal.
1158 */
1159 static status_t
1160 try_drivers(DriverEntryList& list)
1161 {
1162 	while (true) {
1163 		driver_entry* entry = list.RemoveHead();
1164 		if (entry == NULL)
1165 			break;
1166 
1167 		image_id image = load_kernel_add_on(entry->path);
1168 		if (image >= 0) {
1169 			// check if it's an old-style driver
1170 			if (legacy_driver_add(entry->path) == B_OK) {
1171 				// we have a driver
1172 				dprintf("loaded driver %s\n", entry->path);
1173 			}
1174 
1175 			unload_kernel_add_on(image);
1176 		}
1177 
1178 		free(entry->path);
1179 		free(entry);
1180 	}
1181 
1182 	return B_OK;
1183 }
1184 
1185 
1186 static status_t
1187 probe_for_drivers(const char *type)
1188 {
1189 	TRACE(("probe_for_drivers(type = %s)\n", type));
1190 
1191 	if (gBootDevice < 0)
1192 		return B_OK;
1193 
1194 	DriverEntryList drivers;
1195 
1196 	// build list of potential drivers for that type
1197 
1198 	DirectoryIterator iterator(NULL, type, false);
1199 	struct stat stat;
1200 	KPath path;
1201 
1202 	while (iterator.GetNext(path, stat) == B_OK) {
1203 		if (S_ISDIR(stat.st_mode)) {
1204 			add_node_listener(stat.st_dev, stat.st_ino, B_WATCH_DIRECTORY,
1205 				sDirectoryWatcher);
1206 
1207 			directory_node_entry *entry
1208 				= new(std::nothrow) directory_node_entry;
1209 			if (entry != NULL) {
1210 				entry->node = stat.st_ino;
1211 				sDirectoryNodeHash.Insert(entry);
1212 			}
1213 
1214 			// We need to make sure that drivers in ie. "audio/raw/" can
1215 			// be found as well - therefore, we must make sure that "audio"
1216 			// exists on /dev.
1217 
1218 			size_t length = strlen("drivers/dev");
1219 			if (strncmp(type, "drivers/dev", length))
1220 				continue;
1221 
1222 			path.SetTo(type);
1223 			path.Append(iterator.CurrentName());
1224 			devfs_publish_directory(path.Path() + length + 1);
1225 			continue;
1226 		}
1227 
1228 		driver_entry *entry = new_driver_entry(path.Path(), stat.st_dev,
1229 			stat.st_ino);
1230 		if (entry == NULL)
1231 			return B_NO_MEMORY;
1232 
1233 		TRACE(("found potential driver: %s\n", path.Path()));
1234 		drivers.Add(entry);
1235 	}
1236 
1237 	if (drivers.IsEmpty())
1238 		return B_OK;
1239 
1240 	// ToDo: do something with the remaining drivers... :)
1241 	try_drivers(drivers);
1242 	return B_OK;
1243 }
1244 
1245 
1246 //	#pragma mark - LegacyDevice
1247 
1248 
1249 LegacyDevice::LegacyDevice(legacy_driver* driver, const char* path,
1250 		device_hooks* hooks)
1251 	:
1252 	fDriver(driver),
1253 	fRepublished(true),
1254 	fRemovedFromParent(false)
1255 {
1256 	fDeviceModule = (device_module_info*)malloc(sizeof(device_module_info));
1257 	if (fDeviceModule != NULL)
1258 		memset(fDeviceModule, 0, sizeof(device_module_info));
1259 
1260 	fDeviceData = this;
1261 	fPath = strdup(path);
1262 
1263 	SetHooks(hooks);
1264 }
1265 
1266 
1267 LegacyDevice::~LegacyDevice()
1268 {
1269 	free(fDeviceModule);
1270 	free((char*)fPath);
1271 }
1272 
1273 
1274 status_t
1275 LegacyDevice::InitCheck() const
1276 {
1277 	return fDeviceModule != NULL && fPath != NULL ? B_OK : B_NO_MEMORY;
1278 }
1279 
1280 
1281 status_t
1282 LegacyDevice::InitDevice()
1283 {
1284 	RecursiveLocker _(sLock);
1285 
1286 	if (fInitialized++ > 0)
1287 		return B_OK;
1288 
1289 	if (fDriver != NULL && fDriver->devices_used == 0
1290 		&& (fDriver->image < 0 || fDriver->binary_updated)) {
1291 		status_t status = reload_driver(fDriver);
1292 		if (status < B_OK)
1293 			return status;
1294 	}
1295 
1296 	if (fDriver != NULL)
1297 		fDriver->devices_used++;
1298 
1299 	return B_OK;
1300 }
1301 
1302 
1303 void
1304 LegacyDevice::UninitDevice()
1305 {
1306 	RecursiveLocker _(sLock);
1307 
1308 	if (fInitialized-- > 1)
1309 		return;
1310 
1311 	if (fDriver != NULL) {
1312 		if (--fDriver->devices_used == 0 && fDriver->devices.IsEmpty())
1313 			unload_driver(fDriver);
1314 		fDriver = NULL;
1315 	}
1316 }
1317 
1318 
1319 void
1320 LegacyDevice::Removed()
1321 {
1322 	RecursiveLocker _(sLock);
1323 
1324 	if (!fRemovedFromParent && fDriver != NULL)
1325 		fDriver->devices.Remove(this);
1326 
1327 	delete this;
1328 }
1329 
1330 
1331 void
1332 LegacyDevice::SetHooks(device_hooks* hooks)
1333 {
1334 	// TODO: setup compatibility layer!
1335 	fHooks = hooks;
1336 
1337 	fDeviceModule->close = hooks->close;
1338 	fDeviceModule->free = hooks->free;
1339 	fDeviceModule->control = hooks->control;
1340 	fDeviceModule->read = hooks->read;
1341 	fDeviceModule->write = hooks->write;
1342 
1343 	if (fDriver == NULL || fDriver->api_version >= 2) {
1344 		// According to Be newsletter, vol II, issue 36,
1345 		// version 2 added readv/writev, which we don't support, but also
1346 		// select/deselect.
1347 		if (hooks->select != NULL) {
1348 			// Note we set the module's select to a non-null value to indicate
1349 			// that we have select. HasSelect() will therefore return the
1350 			// correct answer. As Select() is virtual our compatibility
1351 			// version below is going to be called though, that redirects to
1352 			// the proper select hook, so it is ok to set it to an invalid
1353 			// address here.
1354 			fDeviceModule->select = (status_t (*)(void*, uint8, selectsync*))~0;
1355 		}
1356 
1357 		fDeviceModule->deselect = hooks->deselect;
1358 	}
1359 }
1360 
1361 
1362 status_t
1363 LegacyDevice::Open(const char* path, int openMode, void** _cookie)
1364 {
1365 	return Hooks()->open(path, openMode, _cookie);
1366 }
1367 
1368 
1369 status_t
1370 LegacyDevice::Select(void* cookie, uint8 event, selectsync* sync)
1371 {
1372 	return Hooks()->select(cookie, event, 0, sync);
1373 }
1374 
1375 
1376 //	#pragma mark - kernel private API
1377 
1378 
1379 extern "C" void
1380 legacy_driver_add_preloaded(kernel_args* args)
1381 {
1382 	// NOTE: This function does not exit in case of error, since it
1383 	// needs to unload the images then. Also the return code of
1384 	// the path operations is kept separate from the add_driver()
1385 	// success, so that even if add_driver() fails for one driver, it
1386 	// is still tried for the other drivers.
1387 	// NOTE: The initialization success of the path objects is implicitely
1388 	// checked by the immediately following functions.
1389 	KPath basePath;
1390 	status_t status = find_directory(B_BEOS_ADDONS_DIRECTORY,
1391 		gBootDevice, false, basePath.LockBuffer(), basePath.BufferSize());
1392 	if (status != B_OK) {
1393 		dprintf("legacy_driver_add_preloaded: find_directory() failed: "
1394 			"%s\n", strerror(status));
1395 	}
1396 	basePath.UnlockBuffer();
1397 	if (status == B_OK)
1398 		status = basePath.Append("kernel");
1399 	if (status != B_OK) {
1400 		dprintf("legacy_driver_add_preloaded: constructing base driver "
1401 			"path failed: %s\n", strerror(status));
1402 		return;
1403 	}
1404 
1405 	struct preloaded_image* image;
1406 	for (image = args->preloaded_images; image != NULL; image = image->next) {
1407 		if (image->is_module || image->id < 0)
1408 			continue;
1409 
1410 		KPath imagePath(basePath);
1411 		status = imagePath.Append(image->name);
1412 
1413 		// try to add the driver
1414 		TRACE(("legacy_driver_add_preloaded: adding driver %s\n",
1415 			imagePath.Path()));
1416 
1417 		if (status == B_OK)
1418 			status = add_driver(imagePath.Path(), image->id);
1419 		if (status != B_OK) {
1420 			dprintf("legacy_driver_add_preloaded: Failed to add \"%s\": %s\n",
1421 				(char *)image->name, strerror(status));
1422 			unload_kernel_add_on(image->id);
1423 		}
1424 	}
1425 }
1426 
1427 
1428 extern "C" status_t
1429 legacy_driver_add(const char* path)
1430 {
1431 	return add_driver(path, -1);
1432 }
1433 
1434 
1435 extern "C" status_t
1436 legacy_driver_publish(const char *path, device_hooks *hooks)
1437 {
1438 	// we don't have a driver, just publish the hooks
1439 	LegacyDevice* device = new(std::nothrow) LegacyDevice(NULL, path, hooks);
1440 	if (device == NULL)
1441 		return B_NO_MEMORY;
1442 
1443 	status_t status = device->InitCheck();
1444 	if (status == B_OK)
1445 		status = devfs_publish_device(path, device);
1446 
1447 	if (status != B_OK)
1448 		delete device;
1449 
1450 	return status;
1451 }
1452 
1453 
1454 extern "C" status_t
1455 legacy_driver_rescan(const char* driverName)
1456 {
1457 	RecursiveLocker locker(sLock);
1458 
1459 	legacy_driver* driver = (legacy_driver*)hash_lookup(sDriverHash,
1460 		driverName);
1461 	if (driver == NULL)
1462 		return B_ENTRY_NOT_FOUND;
1463 
1464 	// Republish the driver's entries
1465 	return republish_driver(driver);
1466 }
1467 
1468 
1469 extern "C" status_t
1470 legacy_driver_probe(const char* subPath)
1471 {
1472 	TRACE(("legacy_driver_probe(type = %s)\n", subPath));
1473 
1474 	char devicePath[64];
1475 	snprintf(devicePath, sizeof(devicePath), "drivers/dev%s%s",
1476 		subPath[0] ? "/" : "", subPath);
1477 
1478 	if (!sWatching && gBootDevice > 0) {
1479 		// We're probing the actual boot volume for the first time,
1480 		// let's watch its driver directories for changes
1481 		const directory_which whichPath[] = {
1482 			B_USER_ADDONS_DIRECTORY,
1483 			B_COMMON_ADDONS_DIRECTORY,
1484 			B_BEOS_ADDONS_DIRECTORY
1485 		};
1486 		KPath path;
1487 
1488 		new(&sDirectoryWatcher) DirectoryWatcher;
1489 
1490 		bool disableUserAddOns = get_safemode_boolean(
1491 			B_SAFEMODE_DISABLE_USER_ADD_ONS, false);
1492 
1493 		for (uint32 i = 0; i < sizeof(whichPath) / sizeof(whichPath[0]); i++) {
1494 			if (i < 2 && disableUserAddOns)
1495 				continue;
1496 
1497 			if (find_directory(whichPath[i], gBootDevice, true,
1498 					path.LockBuffer(), path.BufferSize()) == B_OK) {
1499 				path.UnlockBuffer();
1500 				path.Append("kernel/drivers");
1501 
1502 				start_watching(path.Path(), "dev");
1503 				start_watching(path.Path(), "bin");
1504 			} else
1505 				path.UnlockBuffer();
1506 		}
1507 
1508 		sWatching = true;
1509 	}
1510 
1511 	return probe_for_drivers(devicePath);
1512 }
1513 
1514 
1515 extern "C" status_t
1516 legacy_driver_init(void)
1517 {
1518 	legacy_driver dummyDriver;
1519 	sDriverHash = hash_init(DRIVER_HASH_SIZE,
1520 		offset_of_member(dummyDriver, next), &driver_entry_compare,
1521 		&driver_entry_hash);
1522 	if (sDriverHash == NULL)
1523 		return B_NO_MEMORY;
1524 
1525 	recursive_lock_init(&sLock, "legacy driver");
1526 
1527 	new(&sDriverWatcher) DriverWatcher;
1528 	new(&sDriverEvents) DriverEventList;
1529 
1530 	register_kernel_daemon(&handle_driver_events, NULL, 10);
1531 		// once every second
1532 
1533 	add_debugger_command("legacy_driver", &dump_driver,
1534 		"info about a legacy driver entry");
1535 	add_debugger_command("legacy_device", &dump_device,
1536 		"info about a legacy device");
1537 
1538 	return B_OK;
1539 }
1540