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