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