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