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