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