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