1165619Sglebius/*
2165619Sglebius * Copyright 2002-2011, Axel D��rfler, axeld@pinc-software.de.
3165619Sglebius * Distributed under the terms of the MIT License.
4165619Sglebius */
5165619Sglebius
6165619Sglebius
7165619Sglebius#include "legacy_drivers.h"
8165619Sglebius
9165619Sglebius#include <dirent.h>
10165619Sglebius#include <errno.h>
11165619Sglebius#include <new>
12165619Sglebius#include <stdio.h>
13165619Sglebius
14165619Sglebius#include <FindDirectory.h>
15165619Sglebius#include <image.h>
16165619Sglebius#include <NodeMonitor.h>
17165619Sglebius
18165619Sglebius#include <boot_device.h>
19165619Sglebius#include <boot/kernel_args.h>
20165619Sglebius#include <elf.h>
21165619Sglebius#include <find_directory_private.h>
22165619Sglebius#include <fs/devfs.h>
23165619Sglebius#include <fs/KPath.h>
24165619Sglebius#include <fs/node_monitor.h>
25165619Sglebius#include <Notifications.h>
26165619Sglebius#include <safemode.h>
27165619Sglebius#include <util/DoublyLinkedList.h>
28165619Sglebius#include <util/OpenHashTable.h>
29165619Sglebius#include <util/Stack.h>
30165619Sglebius#include <vfs.h>
31165619Sglebius
32165619Sglebius#include "AbstractModuleDevice.h"
33165619Sglebius#include "devfs_private.h"
34165619Sglebius
35165619Sglebius
36165619Sglebius//#define TRACE_LEGACY_DRIVERS
37165619Sglebius#ifdef TRACE_LEGACY_DRIVERS
38165619Sglebius#	define TRACE(x) dprintf x
39165619Sglebius#else
40165619Sglebius#	define TRACE(x)
41165619Sglebius#endif
42165619Sglebius
43165619Sglebius#define DRIVER_HASH_SIZE 16
44165619Sglebius
45165619Sglebius
46165619Sglebiusnamespace {
47165619Sglebius
48165619Sglebiusstruct legacy_driver;
49227293Sed
50165619Sglebiusclass LegacyDevice : public AbstractModuleDevice,
51165619Sglebius	public DoublyLinkedListLinkImpl<LegacyDevice> {
52165619Sglebiuspublic:
53165619Sglebius							LegacyDevice(legacy_driver* driver,
54165619Sglebius								const char* path, device_hooks* hooks);
55165619Sglebius	virtual					~LegacyDevice();
56165619Sglebius
57165619Sglebius			status_t		InitCheck() const;
58165619Sglebius
59165619Sglebius	virtual	status_t		InitDevice();
60165619Sglebius	virtual	void			UninitDevice();
61165619Sglebius
62165619Sglebius	virtual	void			Removed();
63165619Sglebius
64165619Sglebius			void			SetHooks(device_hooks* hooks);
65165619Sglebius
66165619Sglebius			legacy_driver*	Driver() const { return fDriver; }
67165619Sglebius			const char*		Path() const { return fPath; }
68165619Sglebius			device_hooks*	Hooks() const { return fHooks; }
69165619Sglebius
70165619Sglebius	virtual	status_t		Open(const char* path, int openMode,
71165619Sglebius								void** _cookie);
72165619Sglebius	virtual	status_t		Select(void* cookie, uint8 event, selectsync* sync);
73165619Sglebius
74165619Sglebius	virtual	status_t		Control(void* cookie, int32 op, void* buffer, size_t length);
75165619Sglebius
76165619Sglebius			bool			Republished() const { return fRepublished; }
77165619Sglebius			void			SetRepublished(bool republished)
78165619Sglebius								{ fRepublished = republished; }
79165619Sglebius
80165619Sglebius			void			SetRemovedFromParent(bool removed)
81165619Sglebius								{ fRemovedFromParent = removed; }
82165619Sglebius
83165619Sglebiusprivate:
84165619Sglebius	legacy_driver*			fDriver;
85165619Sglebius	const char*				fPath;
86165619Sglebius	device_hooks*			fHooks;
87165619Sglebius	bool					fRepublished;
88165619Sglebius	bool					fRemovedFromParent;
89165619Sglebius};
90165619Sglebius
91165619Sglebiustypedef DoublyLinkedList<LegacyDevice> DeviceList;
92165619Sglebius
93165619Sglebiusstruct legacy_driver {
94165619Sglebius	legacy_driver*	next;
95165619Sglebius	const char*		path;
96165619Sglebius	const char*		name;
97165619Sglebius	dev_t			device;
98165619Sglebius	ino_t			node;
99165619Sglebius	timespec		last_modified;
100165619Sglebius	image_id		image;
101165619Sglebius	uint32			devices_used;
102165619Sglebius	bool			binary_updated;
103165619Sglebius	int32			priority;
104165619Sglebius	DeviceList		devices;
105165619Sglebius
106165619Sglebius	// driver image information
107165619Sglebius	int32			api_version;
108165619Sglebius	device_hooks*	(*find_device)(const char *);
109165619Sglebius	const char**	(*publish_devices)(void);
110165619Sglebius	status_t		(*uninit_driver)(void);
111165619Sglebius	status_t		(*uninit_hardware)(void);
112165619Sglebius};
113165619Sglebius
114165619Sglebius
115165619Sglebiusenum driver_event_type {
116165619Sglebius	kAddDriver,
117165619Sglebius	kRemoveDriver,
118165619Sglebius	kAddWatcher,
119165619Sglebius	kRemoveWatcher
120165619Sglebius};
121165619Sglebius
122165619Sglebiusstruct driver_event : DoublyLinkedListLinkImpl<driver_event> {
123165619Sglebius	driver_event(driver_event_type _type) : type(_type) {}
124165619Sglebius
125165619Sglebius	struct ref {
126165619Sglebius		dev_t		device;
127165619Sglebius		ino_t		node;
128165619Sglebius	};
129165619Sglebius
130165619Sglebius	driver_event_type	type;
131165619Sglebius	union {
132165619Sglebius		char			path[B_PATH_NAME_LENGTH];
133165619Sglebius		ref				node;
134165619Sglebius	};
135165619Sglebius};
136165619Sglebius
137165619Sglebiustypedef DoublyLinkedList<driver_event> DriverEventList;
138165619Sglebius
139165619Sglebius
140165619Sglebiusstruct driver_entry : DoublyLinkedListLinkImpl<driver_entry> {
141165619Sglebius	char*			path;
142165619Sglebius	dev_t			device;
143165619Sglebius	ino_t			node;
144165619Sglebius	int32			busses;
145165619Sglebius};
146165619Sglebius
147165619Sglebiustypedef DoublyLinkedList<driver_entry> DriverEntryList;
148165619Sglebius
149165619Sglebius
150165619Sglebiusstruct node_entry : DoublyLinkedListLinkImpl<node_entry> {
151165619Sglebius};
152165619Sglebius
153165619Sglebiustypedef DoublyLinkedList<node_entry> NodeList;
154165619Sglebius
155165619Sglebius
156165619Sglebiusstruct directory_node_entry {
157165619Sglebius	directory_node_entry*	hash_link;
158165619Sglebius	ino_t					node;
159165619Sglebius};
160165619Sglebius
161165619Sglebiusstruct DirectoryNodeHashDefinition {
162165619Sglebius	typedef ino_t* KeyType;
163165619Sglebius	typedef directory_node_entry ValueType;
164165619Sglebius
165165619Sglebius	size_t HashKey(ino_t* key) const
166165619Sglebius		{ return _Hash(*key); }
167165619Sglebius	size_t Hash(directory_node_entry* entry) const
168165619Sglebius		{ return _Hash(entry->node); }
169165619Sglebius	bool Compare(ino_t* key, directory_node_entry* entry) const
170165619Sglebius		{ return *key == entry->node; }
171165619Sglebius	directory_node_entry*&
172165619Sglebius		GetLink(directory_node_entry* entry) const
173165619Sglebius		{ return entry->hash_link; }
174165619Sglebius
175165619Sglebius	uint32 _Hash(ino_t node) const
176165619Sglebius		{ return (uint32)(node >> 32) + (uint32)node; }
177165619Sglebius};
178165619Sglebius
179165619Sglebiustypedef BOpenHashTable<DirectoryNodeHashDefinition> DirectoryNodeHash;
180165619Sglebius
181165619Sglebiusclass DirectoryIterator {
182165619Sglebiuspublic:
183165619Sglebius						DirectoryIterator(const char *path,
184165619Sglebius							const char *subPath = NULL, bool recursive = false);
185165619Sglebius						~DirectoryIterator();
186165619Sglebius
187165619Sglebius			void		SetTo(const char *path, const char *subPath = NULL,
188165619Sglebius							bool recursive = false);
189165619Sglebius
190165619Sglebius			status_t	GetNext(KPath &path, struct stat &stat);
191165619Sglebius			const char*	CurrentName() const { return fCurrentName; }
192165619Sglebius
193165619Sglebius			void		Unset();
194165619Sglebius			void		AddPath(const char *path, const char *subPath = NULL);
195165619Sglebius
196165619Sglebiusprivate:
197165619Sglebius	Stack<KPath*>		fPaths;
198165619Sglebius	bool				fRecursive;
199165619Sglebius	DIR*				fDirectory;
200165619Sglebius	KPath*				fBasePath;
201165619Sglebius	const char*			fCurrentName;
202165619Sglebius};
203165619Sglebius
204165619Sglebius
205165619Sglebiusclass DirectoryWatcher : public NotificationListener {
206165619Sglebiuspublic:
207165619Sglebius						DirectoryWatcher();
208165619Sglebius	virtual				~DirectoryWatcher();
209165619Sglebius
210165619Sglebius	virtual void		EventOccurred(NotificationService& service,
211165619Sglebius							const KMessage* event);
212165619Sglebius};
213165619Sglebius
214165619Sglebiusclass DriverWatcher : public NotificationListener {
215165619Sglebiuspublic:
216165619Sglebius						DriverWatcher();
217165619Sglebius	virtual				~DriverWatcher();
218165619Sglebius
219165619Sglebius	virtual void		EventOccurred(NotificationService& service,
220165619Sglebius							const KMessage* event);
221165619Sglebius};
222165619Sglebius
223165619Sglebius
224165619Sglebiusstruct DriverHash {
225165619Sglebius	typedef const char*			KeyType;
226165619Sglebius	typedef legacy_driver		ValueType;
227165619Sglebius
228165619Sglebius	size_t HashKey(KeyType key) const
229165619Sglebius	{
230165619Sglebius		return hash_hash_string(key);
231165619Sglebius	}
232165619Sglebius
233165619Sglebius	size_t Hash(ValueType* driver) const
234165619Sglebius	{
235165619Sglebius		return HashKey(driver->name);
236165619Sglebius	}
237165619Sglebius
238166019Sglebius	bool Compare(KeyType key, ValueType* driver) const
239165619Sglebius	{
240165619Sglebius		return strcmp(driver->name, key) == 0;
241165619Sglebius	}
242165619Sglebius
243165619Sglebius	ValueType*& GetLink(ValueType* value) const
244165619Sglebius	{
245165619Sglebius		return value->next;
246165619Sglebius	}
247165619Sglebius};
248165619Sglebius
249165619Sglebiustypedef BOpenHashTable<DriverHash> DriverTable;
250165619Sglebius
251165619Sglebius
252165619Sglebius}	// unnamed namespace
253165619Sglebius
254165619Sglebius
255165619Sglebiusstatic status_t unload_driver(legacy_driver *driver);
256165619Sglebiusstatic status_t load_driver(legacy_driver *driver);
257165619Sglebius
258165619Sglebius
259165619Sglebiusstatic const directory_which kDriverPaths[] = {
260165619Sglebius	B_USER_NONPACKAGED_ADDONS_DIRECTORY,
261165619Sglebius	B_USER_ADDONS_DIRECTORY,
262165619Sglebius	B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY,
263165619Sglebius	B_SYSTEM_ADDONS_DIRECTORY
264165619Sglebius};
265165619Sglebius
266165619Sglebiusstatic DriverWatcher sDriverWatcher;
267165619Sglebiusstatic int32 sDriverEventsPending;
268165619Sglebiusstatic DriverEventList sDriverEvents;
269165619Sglebiusstatic mutex sDriverEventsLock = MUTEX_INITIALIZER("driver events");
270165619Sglebius	// inner lock, protects the sDriverEvents list only
271165619Sglebiusstatic DirectoryWatcher sDirectoryWatcher;
272165619Sglebiusstatic DirectoryNodeHash sDirectoryNodeHash;
273165619Sglebiusstatic recursive_lock sLock;
274165619Sglebiusstatic bool sWatching;
275166019Sglebius
276165619Sglebiusstatic DriverTable* sDriverHash;
277165619Sglebius
278165619Sglebius
279165619Sglebius//	#pragma mark - driver private
280165619Sglebius
281165619Sglebius
282165619Sglebius/*!	Collects all published devices of a driver, compares them to what the
283165619Sglebius	driver would publish now, and then publishes/unpublishes the devices
284165619Sglebius	as needed.
285165619Sglebius	If the driver does not publish any devices anymore, it is unloaded.
286165619Sglebius*/
287165619Sglebiusstatic status_t
288165619Sglebiusrepublish_driver(legacy_driver* driver)
289165619Sglebius{
290165619Sglebius	if (driver->image < 0) {
291165619Sglebius		// The driver is not yet loaded - go through the normal load procedure
292165619Sglebius		return load_driver(driver);
293165619Sglebius	}
294165619Sglebius
295165619Sglebius	// mark all devices
296165619Sglebius	DeviceList::Iterator iterator = driver->devices.GetIterator();
297165619Sglebius	while (LegacyDevice* device = iterator.Next()) {
298165619Sglebius		device->SetRepublished(false);
299165619Sglebius	}
300165619Sglebius
301165619Sglebius	// now ask the driver for it's currently published devices
302165619Sglebius	const char** devicePaths = driver->publish_devices();
303165619Sglebius
304165619Sglebius	int32 exported = 0;
305165619Sglebius	for (; devicePaths != NULL && devicePaths[0]; devicePaths++) {
306165619Sglebius		LegacyDevice* device;
307165619Sglebius
308166019Sglebius		iterator = driver->devices.GetIterator();
309165619Sglebius		while ((device = iterator.Next()) != NULL) {
310165619Sglebius			if (!strncmp(device->Path(), devicePaths[0], B_PATH_NAME_LENGTH)) {
311165619Sglebius				// mark device as republished
312165619Sglebius				device->SetRepublished(true);
313165619Sglebius				exported++;
314165619Sglebius				break;
315165619Sglebius			}
316165619Sglebius		}
317165619Sglebius
318165619Sglebius		device_hooks* hooks = driver->find_device(devicePaths[0]);
319165619Sglebius		if (hooks == NULL)
320165619Sglebius			continue;
321165619Sglebius
322165619Sglebius		if (device != NULL) {
323165619Sglebius			// update hooks
324165619Sglebius			device->SetHooks(hooks);
325165619Sglebius			continue;
326165619Sglebius		}
327165619Sglebius
328165619Sglebius		// the device was not present before -> publish it now
329165619Sglebius		TRACE(("devfs: publishing new device \"%s\"\n", devicePaths[0]));
330165619Sglebius		device = new(std::nothrow) LegacyDevice(driver, devicePaths[0], hooks);
331165619Sglebius		if (device != NULL && device->InitCheck() == B_OK
332165619Sglebius			&& devfs_publish_device(devicePaths[0], device) == B_OK) {
333165619Sglebius			driver->devices.Add(device);
334165619Sglebius			exported++;
335165619Sglebius		} else
336165619Sglebius			delete device;
337165619Sglebius	}
338165619Sglebius
339165619Sglebius	// remove all devices that weren't republished
340165619Sglebius	iterator = driver->devices.GetIterator();
341165619Sglebius	while (LegacyDevice* device = iterator.Next()) {
342165619Sglebius		if (device->Republished())
343165619Sglebius			continue;
344165619Sglebius
345165619Sglebius		TRACE(("devfs: unpublishing no more present \"%s\"\n", device->Path()));
346165619Sglebius		iterator.Remove();
347165619Sglebius		device->SetRemovedFromParent(true);
348165619Sglebius
349165619Sglebius		devfs_unpublish_device(device, true);
350165619Sglebius	}
351165619Sglebius
352165619Sglebius	if (exported == 0 && driver->devices_used == 0 && gBootDevice >= 0) {
353165619Sglebius		TRACE(("devfs: driver \"%s\" does not publish any more nodes and is "
354165619Sglebius			"unloaded\n", driver->path));
355165619Sglebius		unload_driver(driver);
356165619Sglebius	}
357165619Sglebius
358165619Sglebius	return B_OK;
359165619Sglebius}
360165619Sglebius
361165619Sglebius
362165619Sglebiusstatic status_t
363165619Sglebiusload_driver(legacy_driver* driver)
364165619Sglebius{
365165619Sglebius	status_t (*init_hardware)(void);
366165619Sglebius	status_t (*init_driver)(void);
367165619Sglebius	status_t status;
368165619Sglebius
369165619Sglebius	driver->binary_updated = false;
370165619Sglebius
371165619Sglebius	// load the module
372165619Sglebius	image_id image = driver->image;
373165619Sglebius	if (image < 0) {
374165619Sglebius		image = load_kernel_add_on(driver->path);
375165619Sglebius		if (image < 0)
376165619Sglebius			return image;
377165619Sglebius	}
378165619Sglebius
379165619Sglebius	// For a valid device driver the following exports are required
380165619Sglebius
381165619Sglebius	int32* apiVersion;
382165619Sglebius	if (get_image_symbol(image, "api_version", B_SYMBOL_TYPE_DATA,
383165619Sglebius			(void**)&apiVersion) == B_OK) {
384165619Sglebius#if B_CUR_DRIVER_API_VERSION != 2
385165619Sglebius		// just in case someone decides to bump up the api version
386165619Sglebius#error Add checks here for new vs old api version!
387165619Sglebius#endif
388165619Sglebius		if (*apiVersion > B_CUR_DRIVER_API_VERSION) {
389165619Sglebius			dprintf("devfs: \"%s\" api_version %" B_PRId32 " not handled\n",
390165619Sglebius				driver->name, *apiVersion);
391165619Sglebius			status = B_BAD_VALUE;
392165619Sglebius			goto error1;
393165619Sglebius		}
394165619Sglebius		if (*apiVersion < 1) {
395165619Sglebius			dprintf("devfs: \"%s\" api_version invalid\n", driver->name);
396165619Sglebius			status = B_BAD_VALUE;
397165619Sglebius			goto error1;
398165619Sglebius		}
399165619Sglebius
400165619Sglebius		driver->api_version = *apiVersion;
401165619Sglebius	} else
402166019Sglebius		dprintf("devfs: \"%s\" api_version missing\n", driver->name);
403187405Smav
404243882Sglebius	if (get_image_symbol(image, "publish_devices", B_SYMBOL_TYPE_TEXT,
405187405Smav				(void**)&driver->publish_devices) != B_OK
406187405Smav		|| get_image_symbol(image, "find_device", B_SYMBOL_TYPE_TEXT,
407187405Smav				(void**)&driver->find_device) != B_OK) {
408187405Smav		dprintf("devfs: \"%s\" mandatory driver symbol(s) missing!\n",
409187405Smav			driver->name);
410165619Sglebius		status = B_BAD_VALUE;
411165619Sglebius		goto error1;
412165619Sglebius	}
413165619Sglebius
414165619Sglebius	// Init the driver
415165619Sglebius
416165619Sglebius	if (get_image_symbol(image, "init_hardware", B_SYMBOL_TYPE_TEXT,
417165619Sglebius			(void**)&init_hardware) == B_OK
418165619Sglebius		&& (status = init_hardware()) != B_OK) {
419165619Sglebius		TRACE(("%s: init_hardware() failed: %s\n", driver->name,
420165619Sglebius			strerror(status)));
421165619Sglebius		status = ENXIO;
422165619Sglebius		goto error1;
423165619Sglebius	}
424165619Sglebius
425165619Sglebius	if (get_image_symbol(image, "init_driver", B_SYMBOL_TYPE_TEXT,
426165619Sglebius			(void**)&init_driver) == B_OK
427165619Sglebius		&& (status = init_driver()) != B_OK) {
428165619Sglebius		TRACE(("%s: init_driver() failed: %s\n", driver->name,
429165619Sglebius			strerror(status)));
430165619Sglebius		status = ENXIO;
431165619Sglebius		goto error2;
432165619Sglebius	}
433165619Sglebius
434165619Sglebius	// resolve and cache those for the driver unload code
435165619Sglebius	if (get_image_symbol(image, "uninit_driver", B_SYMBOL_TYPE_TEXT,
436165619Sglebius		(void**)&driver->uninit_driver) != B_OK)
437165619Sglebius		driver->uninit_driver = NULL;
438165619Sglebius	if (get_image_symbol(image, "uninit_hardware", B_SYMBOL_TYPE_TEXT,
439165619Sglebius		(void**)&driver->uninit_hardware) != B_OK)
440165619Sglebius		driver->uninit_hardware = NULL;
441165619Sglebius
442165619Sglebius	// The driver has successfully been initialized, now we can
443165619Sglebius	// finally publish its device entries
444165619Sglebius
445187405Smav	driver->image = image;
446187405Smav	return republish_driver(driver);
447187405Smav
448187405Smaverror2:
449187405Smav	if (driver->uninit_hardware)
450187405Smav		driver->uninit_hardware();
451187405Smav
452187405Smaverror1:
453165619Sglebius	if (driver->image < 0) {
454165619Sglebius		unload_kernel_add_on(image);
455165619Sglebius		driver->image = status;
456165619Sglebius	}
457165619Sglebius
458165619Sglebius	return status;
459165619Sglebius}
460165619Sglebius
461165619Sglebius
462165619Sglebiusstatic status_t
463165619Sglebiusunload_driver(legacy_driver* driver)
464165619Sglebius{
465165619Sglebius	if (driver->image < 0) {
466165619Sglebius		// driver is not currently loaded
467165619Sglebius		return B_NO_INIT;
468165619Sglebius	}
469165619Sglebius
470165619Sglebius	if (driver->uninit_driver)
471165619Sglebius		driver->uninit_driver();
472165619Sglebius
473165619Sglebius	if (driver->uninit_hardware)
474166019Sglebius		driver->uninit_hardware();
475165619Sglebius
476165619Sglebius	unload_kernel_add_on(driver->image);
477165619Sglebius	driver->image = -1;
478165619Sglebius	driver->binary_updated = false;
479165619Sglebius	driver->find_device = NULL;
480165619Sglebius	driver->publish_devices = NULL;
481187405Smav	driver->uninit_driver = NULL;
482243882Sglebius	driver->uninit_hardware = NULL;
483187405Smav
484187405Smav	return B_OK;
485187405Smav}
486187405Smav
487187405Smav
488165619Sglebius/*!	Unpublishes all devices belonging to the \a driver. */
489165619Sglebiusstatic void
490165619Sglebiusunpublish_driver(legacy_driver* driver)
491165619Sglebius{
492166019Sglebius	while (LegacyDevice* device = driver->devices.RemoveHead()) {
493165619Sglebius		device->SetRemovedFromParent(true);
494165619Sglebius		devfs_unpublish_device(device, true);
495165619Sglebius	}
496165619Sglebius}
497165619Sglebius
498165619Sglebius
499165619Sglebiusstatic void
500165619Sglebiuschange_driver_watcher(dev_t device, ino_t node, bool add)
501165619Sglebius{
502165619Sglebius	if (device == -1)
503165619Sglebius		return;
504165619Sglebius
505165619Sglebius	driver_event* event = new (std::nothrow) driver_event(
506165619Sglebius		add ? kAddWatcher : kRemoveWatcher);
507187405Smav	if (event == NULL)
508165619Sglebius		return;
509165619Sglebius
510165619Sglebius	event->node.device = device;
511165619Sglebius	event->node.node = node;
512165619Sglebius
513165619Sglebius	MutexLocker _(sDriverEventsLock);
514165619Sglebius	sDriverEvents.Add(event);
515165619Sglebius
516165619Sglebius	atomic_add(&sDriverEventsPending, 1);
517165619Sglebius}
518165619Sglebius
519165619Sglebius
520165619Sglebiusstatic int32
521165619Sglebiusget_priority(const char* path)
522165619Sglebius{
523165619Sglebius	// TODO: would it be better to initialize a static structure here
524165619Sglebius	// using find_directory()?
525165619Sglebius	const directory_which whichPath[] = {
526187405Smav		B_SYSTEM_DIRECTORY,
527165619Sglebius		B_SYSTEM_NONPACKAGED_DIRECTORY,
528165619Sglebius		B_USER_DIRECTORY
529165619Sglebius	};
530165619Sglebius	KPath pathBuffer;
531165619Sglebius
532165619Sglebius	for (uint32 index = 0; index < sizeof(whichPath) / sizeof(whichPath[0]);
533187405Smav			index++) {
534187405Smav		if (__find_directory(whichPath[index], gBootDevice, false,
535187405Smav			pathBuffer.LockBuffer(), pathBuffer.BufferSize()) == B_OK) {
536165619Sglebius			pathBuffer.UnlockBuffer();
537165619Sglebius			if (!strncmp(pathBuffer.Path(), path, pathBuffer.BufferSize()))
538187405Smav				return index;
539187405Smav		} else
540187405Smav			pathBuffer.UnlockBuffer();
541165619Sglebius	}
542165619Sglebius
543165619Sglebius	return -1;
544165619Sglebius}
545165619Sglebius
546165619Sglebius
547165619Sglebiusstatic const char*
548165619Sglebiusget_leaf(const char* path)
549165619Sglebius{
550165619Sglebius	const char* name = strrchr(path, '/');
551165619Sglebius	if (name == NULL)
552165619Sglebius		return path;
553165619Sglebius
554165619Sglebius	return name + 1;
555165619Sglebius}
556165619Sglebius
557165619Sglebius
558165619Sglebiusstatic legacy_driver*
559165619Sglebiusfind_driver(dev_t device, ino_t node)
560165619Sglebius{
561165619Sglebius	DriverTable::Iterator iterator(sDriverHash);
562165619Sglebius	while (iterator.HasNext()) {
563165619Sglebius		legacy_driver* driver = iterator.Next();
564165619Sglebius		if (driver->device == device && driver->node == node)
565165619Sglebius			return driver;
566165619Sglebius	}
567165619Sglebius
568165619Sglebius	return NULL;
569165619Sglebius}
570165619Sglebius
571165619Sglebius
572165619Sglebiusstatic status_t
573165619Sglebiusadd_driver(const char* path, image_id image)
574165619Sglebius{
575165619Sglebius	// Check if we already know this driver
576165619Sglebius
577165619Sglebius	struct stat stat;
578165619Sglebius	if (image >= 0) {
579165619Sglebius		// The image ID should be a small number and hopefully the boot FS
580165619Sglebius		// doesn't use small negative values -- if it is inode based, we should
581165619Sglebius		// be relatively safe.
582165619Sglebius		stat.st_dev = -1;
583165619Sglebius		stat.st_ino = -1;
584165619Sglebius	} else {
585175706Smav		if (::stat(path, &stat) != 0)
586165619Sglebius			return errno;
587165619Sglebius	}
588165619Sglebius
589165619Sglebius	int32 priority = get_priority(path);
590165619Sglebius
591165619Sglebius	RecursiveLocker _(sLock);
592165619Sglebius
593175706Smav	legacy_driver* driver = sDriverHash->Lookup(get_leaf(path));
594165619Sglebius	if (driver != NULL) {
595165619Sglebius		// we know this driver
596175706Smav		if (strcmp(driver->path, path) != 0) {
597165619Sglebius			// TODO: do properly, but for now we just update the path if it
598165619Sglebius			// isn't the same anymore so rescanning of drivers will work in
599165619Sglebius			// case this driver was loaded so early that it has a boot module
600165619Sglebius			// path and not a proper driver path
601165619Sglebius			free((char*)driver->path);
602165619Sglebius			driver->path = strdup(path);
603165619Sglebius			driver->name = get_leaf(driver->path);
604165619Sglebius			driver->binary_updated = true;
605165619Sglebius		}
606165619Sglebius
607165619Sglebius		// TODO: check if this driver is a different one and has precendence
608165619Sglebius		// (ie. common supersedes system).
609165619Sglebius		//dprintf("new driver has priority %ld, old %ld\n", priority, driver->priority);
610165619Sglebius		if (priority >= driver->priority) {
611165619Sglebius			driver->binary_updated = true;
612165619Sglebius			return B_OK;
613165619Sglebius		}
614165619Sglebius
615165619Sglebius		// TODO: test for changes here and/or via node monitoring and reload
616165619Sglebius		//	the driver if necessary
617165619Sglebius		if (driver->image < B_OK)
618165619Sglebius			return driver->image;
619165619Sglebius
620175706Smav		return B_OK;
621165619Sglebius	}
622165619Sglebius
623165619Sglebius	// we don't know this driver, create a new entry for it
624165619Sglebius
625165619Sglebius	driver = (legacy_driver*)malloc(sizeof(legacy_driver));
626165619Sglebius	if (driver == NULL)
627175706Smav		return B_NO_MEMORY;
628165619Sglebius
629165619Sglebius	driver->path = strdup(path);
630175706Smav	if (driver->path == NULL) {
631165619Sglebius		free(driver);
632165619Sglebius		return B_NO_MEMORY;
633165619Sglebius	}
634165619Sglebius
635165619Sglebius	driver->name = get_leaf(driver->path);
636165619Sglebius	driver->device = stat.st_dev;
637165619Sglebius	driver->node = stat.st_ino;
638165619Sglebius	driver->image = image;
639165619Sglebius	driver->last_modified = stat.st_mtim;
640165619Sglebius	driver->devices_used = 0;
641165619Sglebius	driver->binary_updated = false;
642165619Sglebius	driver->priority = priority;
643165619Sglebius
644165619Sglebius	driver->api_version = 1;
645165619Sglebius	driver->find_device = NULL;
646165619Sglebius	driver->publish_devices = NULL;
647165619Sglebius	driver->uninit_driver = NULL;
648165619Sglebius	driver->uninit_hardware = NULL;
649165619Sglebius	new(&driver->devices) DeviceList;
650165619Sglebius
651165619Sglebius	sDriverHash->Insert(driver);
652165619Sglebius	if (stat.st_dev > 0)
653165619Sglebius		change_driver_watcher(stat.st_dev, stat.st_ino, true);
654165619Sglebius
655165619Sglebius	// Even if loading the driver fails - its entry will stay with us
656165619Sglebius	// so that we don't have to go through it again
657165619Sglebius	return load_driver(driver);
658165619Sglebius}
659165619Sglebius
660165619Sglebius
661165619Sglebius/*!	This is no longer part of the public kernel API, so we just export the
662165619Sglebius	symbol
663165619Sglebius*/
664165619Sglebiusextern "C" status_t load_driver_symbols(const char* driverName);
665165619Sglebiusstatus_t
666165619Sglebiusload_driver_symbols(const char* driverName)
667165619Sglebius{
668165619Sglebius	// This is done globally for the whole kernel via the settings file.
669165619Sglebius	// We don't have to do anything here.
670165619Sglebius
671165619Sglebius	return B_OK;
672165619Sglebius}
673165619Sglebius
674165619Sglebius
675165619Sglebiusstatic status_t
676165619Sglebiusreload_driver(legacy_driver* driver)
677165619Sglebius{
678165619Sglebius	dprintf("devfs: reload driver \"%s\" (%" B_PRIdDEV ", %" B_PRIdINO ")\n",
679165619Sglebius		driver->name, driver->device, driver->node);
680165619Sglebius
681165619Sglebius	unload_driver(driver);
682165619Sglebius
683165619Sglebius	struct stat stat;
684165619Sglebius	if (::stat(driver->path, &stat) == 0
685165619Sglebius		&& (stat.st_dev != driver->device || stat.st_ino != driver->node)) {
686165619Sglebius		// The driver file has been changed, so we need to update its listener
687165619Sglebius		change_driver_watcher(driver->device, driver->node, false);
688165619Sglebius
689165619Sglebius		driver->device = stat.st_dev;
690165619Sglebius		driver->node = stat.st_ino;
691165619Sglebius
692165619Sglebius		change_driver_watcher(driver->device, driver->node, true);
693165619Sglebius	}
694165619Sglebius
695165619Sglebius	status_t status = load_driver(driver);
696165619Sglebius	if (status != B_OK)
697165619Sglebius		unpublish_driver(driver);
698165619Sglebius
699165619Sglebius	return status;
700165619Sglebius}
701165619Sglebius
702165619Sglebius
703165619Sglebiusstatic void
704165619Sglebiushandle_driver_events(void* /*_fs*/, int /*iteration*/)
705165619Sglebius{
706165619Sglebius	if (atomic_and(&sDriverEventsPending, 0) == 0)
707165619Sglebius		return;
708165619Sglebius
709165619Sglebius	// something happened, let's see what it was
710165619Sglebius
711165619Sglebius	while (true) {
712165619Sglebius		MutexLocker eventLocker(sDriverEventsLock);
713165619Sglebius
714165619Sglebius		driver_event* event = sDriverEvents.RemoveHead();
715165619Sglebius		if (event == NULL)
716			break;
717
718		eventLocker.Unlock();
719		TRACE(("driver event %p, type %d\n", event, event->type));
720
721		switch (event->type) {
722			case kAddDriver:
723			{
724				// Add new drivers
725				RecursiveLocker locker(sLock);
726				TRACE(("  add driver %p\n", event->path));
727
728				legacy_driver* driver = sDriverHash->Lookup(
729					get_leaf(event->path));
730				if (driver == NULL)
731					legacy_driver_add(event->path);
732				else if (get_priority(event->path) >= driver->priority)
733					driver->binary_updated = true;
734				break;
735			}
736
737			case kRemoveDriver:
738			{
739				// Mark removed drivers as updated
740				RecursiveLocker locker(sLock);
741				TRACE(("  remove driver %p\n", event->path));
742
743				legacy_driver* driver = sDriverHash->Lookup(
744					get_leaf(event->path));
745				if (driver != NULL
746					&& get_priority(event->path) >= driver->priority)
747					driver->binary_updated = true;
748				break;
749			}
750
751			case kAddWatcher:
752				TRACE(("  add watcher %ld:%lld\n", event->node.device,
753					event->node.node));
754				add_node_listener(event->node.device, event->node.node,
755					B_WATCH_STAT | B_WATCH_NAME, sDriverWatcher);
756				break;
757
758			case kRemoveWatcher:
759				TRACE(("  remove watcher %ld:%lld\n", event->node.device,
760					event->node.node));
761				remove_node_listener(event->node.device, event->node.node,
762					sDriverWatcher);
763				break;
764		}
765
766		delete event;
767	}
768
769	// Reload updated drivers
770
771	RecursiveLocker locker(sLock);
772
773	DriverTable::Iterator iterator(sDriverHash);
774	while (iterator.HasNext()) {
775		legacy_driver* driver = iterator.Next();
776
777		if (!driver->binary_updated || driver->devices_used != 0)
778			continue;
779
780		// try to reload the driver
781		reload_driver(driver);
782	}
783
784	locker.Unlock();
785}
786
787
788//	#pragma mark - DriverWatcher
789
790
791DriverWatcher::DriverWatcher()
792{
793}
794
795
796DriverWatcher::~DriverWatcher()
797{
798}
799
800
801void
802DriverWatcher::EventOccurred(NotificationService& service,
803	const KMessage* event)
804{
805	int32 opcode = event->GetInt32("opcode", -1);
806	if (opcode != B_STAT_CHANGED
807		|| (event->GetInt32("fields", 0) & B_STAT_MODIFICATION_TIME) == 0)
808		return;
809
810	RecursiveLocker locker(sLock);
811
812	legacy_driver* driver = find_driver(event->GetInt32("device", -1),
813		event->GetInt64("node", 0));
814	if (driver == NULL)
815		return;
816
817	driver->binary_updated = true;
818
819	if (driver->devices_used == 0) {
820		// trigger a reload of the driver
821		atomic_add(&sDriverEventsPending, 1);
822	} else {
823		// driver is in use right now
824		dprintf("devfs: changed driver \"%s\" is still in use\n", driver->name);
825	}
826}
827
828
829static void
830dump_driver(legacy_driver* driver)
831{
832	kprintf("DEVFS DRIVER: %p\n", driver);
833	kprintf(" name:           %s\n", driver->name);
834	kprintf(" path:           %s\n", driver->path);
835	kprintf(" image:          %" B_PRId32 "\n", driver->image);
836	kprintf(" device:         %" B_PRIdDEV "\n", driver->device);
837	kprintf(" node:           %" B_PRIdINO "\n", driver->node);
838	kprintf(" last modified:  %" B_PRIdTIME ".%ld\n", driver->last_modified.tv_sec,
839		driver->last_modified.tv_nsec);
840	kprintf(" devs used:      %" B_PRIu32 "\n", driver->devices_used);
841	kprintf(" devs published: %" B_PRId32 "\n", driver->devices.Count());
842	kprintf(" binary updated: %d\n", driver->binary_updated);
843	kprintf(" priority:       %" B_PRId32 "\n", driver->priority);
844	kprintf(" api version:    %" B_PRId32 "\n", driver->api_version);
845	kprintf(" hooks:          find_device %p, publish_devices %p\n"
846		"                 uninit_driver %p, uninit_hardware %p\n",
847		driver->find_device, driver->publish_devices, driver->uninit_driver,
848		driver->uninit_hardware);
849}
850
851
852static int
853dump_device(int argc, char** argv)
854{
855	if (argc < 2 || !strcmp(argv[1], "--help")) {
856		kprintf("usage: %s [device]\n", argv[0]);
857		return 0;
858	}
859
860	LegacyDevice* device = (LegacyDevice*)parse_expression(argv[1]);
861
862	kprintf("LEGACY DEVICE: %p\n", device);
863	kprintf(" path:     %s\n", device->Path());
864	kprintf(" hooks:    %p\n", device->Hooks());
865	device_hooks* hooks = device->Hooks();
866	kprintf("  close()     %p\n", hooks->close);
867	kprintf("  free()      %p\n", hooks->free);
868	kprintf("  control()   %p\n", hooks->control);
869	kprintf("  read()      %p\n", hooks->read);
870	kprintf("  write()     %p\n", hooks->write);
871	kprintf("  select()    %p\n", hooks->select);
872	kprintf("  deselect()  %p\n", hooks->deselect);
873	dump_driver(device->Driver());
874
875	return 0;
876}
877
878
879static int
880dump_driver(int argc, char** argv)
881{
882	if (argc < 2) {
883		// print list of all drivers
884		kprintf("address    image used publ.   pri name\n");
885		DriverTable::Iterator iterator(sDriverHash);
886		while (iterator.HasNext()) {
887			legacy_driver* driver = iterator.Next();
888
889			kprintf("%p  %5" B_PRId32 " %3" B_PRIu32 " %5" B_PRId32 " %c "
890				"%3" B_PRId32 " %s\n", driver,
891				driver->image < 0 ? -1 : driver->image,
892				driver->devices_used, driver->devices.Count(),
893				driver->binary_updated ? 'U' : ' ', driver->priority,
894				driver->name);
895		}
896
897		return 0;
898	}
899
900	if (!strcmp(argv[1], "--help")) {
901		kprintf("usage: %s [name]\n", argv[0]);
902		return 0;
903	}
904
905	legacy_driver* driver = sDriverHash->Lookup(argv[1]);
906	if (driver == NULL) {
907		kprintf("Driver named \"%s\" not found.\n", argv[1]);
908		return 0;
909	}
910
911	dump_driver(driver);
912	return 0;
913}
914
915
916//	#pragma mark -
917
918
919DirectoryIterator::DirectoryIterator(const char* path, const char* subPath,
920		bool recursive)
921	:
922	fDirectory(NULL),
923	fBasePath(NULL),
924	fCurrentName(NULL)
925{
926	SetTo(path, subPath, recursive);
927}
928
929
930DirectoryIterator::~DirectoryIterator()
931{
932	Unset();
933}
934
935
936void
937DirectoryIterator::SetTo(const char* path, const char* subPath, bool recursive)
938{
939	Unset();
940	fRecursive = recursive;
941
942	if (path == NULL) {
943		// add default paths
944		KPath pathBuffer;
945
946		bool disableUserAddOns = get_safemode_boolean(
947			B_SAFEMODE_DISABLE_USER_ADD_ONS, false);
948
949		for (uint32 i = 0; i < sizeof(kDriverPaths) / sizeof(kDriverPaths[0]); i++) {
950			if (i < 3 && disableUserAddOns)
951				continue;
952
953			if (__find_directory(kDriverPaths[i], gBootDevice, true,
954					pathBuffer.LockBuffer(), pathBuffer.BufferSize()) == B_OK) {
955				pathBuffer.UnlockBuffer();
956				pathBuffer.Append("kernel");
957				AddPath(pathBuffer.Path(), subPath);
958			} else
959				pathBuffer.UnlockBuffer();
960		}
961	} else
962		AddPath(path, subPath);
963}
964
965
966status_t
967DirectoryIterator::GetNext(KPath& path, struct stat& stat)
968{
969next_directory:
970	while (fDirectory == NULL) {
971		delete fBasePath;
972		fBasePath = NULL;
973
974		if (!fPaths.Pop(&fBasePath))
975			return B_ENTRY_NOT_FOUND;
976
977		fDirectory = opendir(fBasePath->Path());
978	}
979
980next_entry:
981	struct dirent* dirent = readdir(fDirectory);
982	if (dirent == NULL) {
983		// get over to next directory on the stack
984		closedir(fDirectory);
985		fDirectory = NULL;
986
987		goto next_directory;
988	}
989
990	if (!strcmp(dirent->d_name, "..") || !strcmp(dirent->d_name, "."))
991		goto next_entry;
992
993	fCurrentName = dirent->d_name;
994
995	path.SetTo(fBasePath->Path());
996	path.Append(fCurrentName);
997
998	if (::stat(path.Path(), &stat) != 0)
999		goto next_entry;
1000
1001	if (S_ISDIR(stat.st_mode) && fRecursive) {
1002		KPath* nextPath = new(nothrow) KPath(path);
1003		if (!nextPath)
1004			return B_NO_MEMORY;
1005		if (fPaths.Push(nextPath) != B_OK)
1006			return B_NO_MEMORY;
1007
1008		goto next_entry;
1009	}
1010
1011	return B_OK;
1012}
1013
1014
1015void
1016DirectoryIterator::Unset()
1017{
1018	if (fDirectory != NULL) {
1019		closedir(fDirectory);
1020		fDirectory = NULL;
1021	}
1022
1023	delete fBasePath;
1024	fBasePath = NULL;
1025
1026	KPath* path;
1027	while (fPaths.Pop(&path))
1028		delete path;
1029}
1030
1031
1032void
1033DirectoryIterator::AddPath(const char* basePath, const char* subPath)
1034{
1035	KPath* path = new(nothrow) KPath(basePath);
1036	if (!path)
1037		panic("out of memory");
1038	if (subPath != NULL)
1039		path->Append(subPath);
1040
1041	fPaths.Push(path);
1042}
1043
1044
1045//	#pragma mark -
1046
1047
1048DirectoryWatcher::DirectoryWatcher()
1049{
1050}
1051
1052
1053DirectoryWatcher::~DirectoryWatcher()
1054{
1055}
1056
1057
1058void
1059DirectoryWatcher::EventOccurred(NotificationService& service,
1060	const KMessage* event)
1061{
1062	int32 opcode = event->GetInt32("opcode", -1);
1063	dev_t device = event->GetInt32("device", -1);
1064	ino_t directory = event->GetInt64("directory", -1);
1065	const char* name = event->GetString("name", NULL);
1066
1067	if (opcode == B_ENTRY_MOVED) {
1068		// Determine whether it's a move within, out of, or into one
1069		// of our watched directories.
1070		ino_t from = event->GetInt64("from directory", -1);
1071		ino_t to = event->GetInt64("to directory", -1);
1072		if (sDirectoryNodeHash.Lookup(&from) == NULL) {
1073			directory = to;
1074			opcode = B_ENTRY_CREATED;
1075		} else if (sDirectoryNodeHash.Lookup(&to) == NULL) {
1076			directory = from;
1077			opcode = B_ENTRY_REMOVED;
1078		} else {
1079			// Move within, don't do anything for now
1080			// TODO: adjust driver priority if necessary
1081			return;
1082		}
1083	}
1084
1085	KPath path(B_PATH_NAME_LENGTH + 1);
1086	if (path.InitCheck() != B_OK || vfs_entry_ref_to_path(device, directory,
1087			name, true, path.LockBuffer(), path.BufferSize()) != B_OK)
1088		return;
1089
1090	path.UnlockBuffer();
1091
1092	dprintf("driver \"%s\" %s\n", path.Leaf(),
1093		opcode == B_ENTRY_CREATED ? "added" : "removed");
1094
1095	driver_event* driverEvent = new(std::nothrow) driver_event(
1096		opcode == B_ENTRY_CREATED ? kAddDriver : kRemoveDriver);
1097	if (driverEvent == NULL)
1098		return;
1099
1100	strlcpy(driverEvent->path, path.Path(), sizeof(driverEvent->path));
1101
1102	MutexLocker _(sDriverEventsLock);
1103	sDriverEvents.Add(driverEvent);
1104	atomic_add(&sDriverEventsPending, 1);
1105}
1106
1107
1108//	#pragma mark -
1109
1110
1111static void
1112start_watching(const char* base, const char* sub)
1113{
1114	KPath path(base);
1115	path.Append(sub);
1116
1117	// TODO: create missing directories?
1118	struct stat stat;
1119	if (::stat(path.Path(), &stat) != 0)
1120		return;
1121
1122	add_node_listener(stat.st_dev, stat.st_ino, B_WATCH_DIRECTORY,
1123		sDirectoryWatcher);
1124
1125	directory_node_entry* entry = new(std::nothrow) directory_node_entry;
1126	if (entry != NULL) {
1127		entry->node = stat.st_ino;
1128		sDirectoryNodeHash.Insert(entry);
1129	}
1130}
1131
1132
1133static struct driver_entry*
1134new_driver_entry(const char* path, dev_t device, ino_t node)
1135{
1136	driver_entry* entry = new(std::nothrow) driver_entry;
1137	if (entry == NULL)
1138		return NULL;
1139
1140	entry->path = strdup(path);
1141	if (entry->path == NULL) {
1142		delete entry;
1143		return NULL;
1144	}
1145
1146	entry->device = device;
1147	entry->node = node;
1148	entry->busses = 0;
1149	return entry;
1150}
1151
1152
1153/*!	Iterates over the given list and tries to load all drivers in that list.
1154	The list is emptied and freed during the traversal.
1155*/
1156static status_t
1157try_drivers(DriverEntryList& list)
1158{
1159	while (true) {
1160		driver_entry* entry = list.RemoveHead();
1161		if (entry == NULL)
1162			break;
1163
1164		image_id image = load_kernel_add_on(entry->path);
1165		if (image >= 0) {
1166			// check if it's an old-style driver
1167			if (legacy_driver_add(entry->path) == B_OK) {
1168				// we have a driver
1169				dprintf("loaded driver %s\n", entry->path);
1170			}
1171
1172			unload_kernel_add_on(image);
1173		}
1174
1175		free(entry->path);
1176		delete entry;
1177	}
1178
1179	return B_OK;
1180}
1181
1182
1183static status_t
1184probe_for_drivers(const char* type)
1185{
1186	TRACE(("probe_for_drivers(type = %s)\n", type));
1187
1188	if (gBootDevice < 0)
1189		return B_OK;
1190
1191	DriverEntryList drivers;
1192
1193	// build list of potential drivers for that type
1194
1195	DirectoryIterator iterator(NULL, type, false);
1196	struct stat stat;
1197	KPath path;
1198
1199	while (iterator.GetNext(path, stat) == B_OK) {
1200		if (S_ISDIR(stat.st_mode)) {
1201			add_node_listener(stat.st_dev, stat.st_ino, B_WATCH_DIRECTORY,
1202				sDirectoryWatcher);
1203
1204			directory_node_entry* entry
1205				= new(std::nothrow) directory_node_entry;
1206			if (entry != NULL) {
1207				entry->node = stat.st_ino;
1208				sDirectoryNodeHash.Insert(entry);
1209			}
1210
1211			// We need to make sure that drivers in ie. "audio/raw/" can
1212			// be found as well - therefore, we must make sure that "audio"
1213			// exists on /dev.
1214
1215			size_t length = strlen("drivers/dev");
1216			if (strncmp(type, "drivers/dev", length))
1217				continue;
1218
1219			path.SetTo(type);
1220			path.Append(iterator.CurrentName());
1221			devfs_publish_directory(path.Path() + length + 1);
1222			continue;
1223		}
1224
1225		driver_entry* entry = new_driver_entry(path.Path(), stat.st_dev,
1226			stat.st_ino);
1227		if (entry == NULL)
1228			return B_NO_MEMORY;
1229
1230		TRACE(("found potential driver: %s\n", path.Path()));
1231		drivers.Add(entry);
1232	}
1233
1234	if (drivers.IsEmpty())
1235		return B_OK;
1236
1237	// ToDo: do something with the remaining drivers... :)
1238	try_drivers(drivers);
1239	return B_OK;
1240}
1241
1242
1243//	#pragma mark - LegacyDevice
1244
1245
1246LegacyDevice::LegacyDevice(legacy_driver* driver, const char* path,
1247		device_hooks* hooks)
1248	:
1249	fDriver(driver),
1250	fRepublished(true),
1251	fRemovedFromParent(false)
1252{
1253	fDeviceModule = (device_module_info*)malloc(sizeof(device_module_info));
1254	if (fDeviceModule != NULL)
1255		memset(fDeviceModule, 0, sizeof(device_module_info));
1256
1257	fDeviceData = this;
1258	fPath = strdup(path);
1259
1260	SetHooks(hooks);
1261}
1262
1263
1264LegacyDevice::~LegacyDevice()
1265{
1266	free(fDeviceModule);
1267	free((char*)fPath);
1268}
1269
1270
1271status_t
1272LegacyDevice::InitCheck() const
1273{
1274	return fDeviceModule != NULL && fPath != NULL ? B_OK : B_NO_MEMORY;
1275}
1276
1277
1278status_t
1279LegacyDevice::InitDevice()
1280{
1281	RecursiveLocker _(sLock);
1282
1283	if (fInitialized++ > 0)
1284		return B_OK;
1285
1286	if (fDriver != NULL && fDriver->devices_used == 0
1287		&& (fDriver->image < 0 || fDriver->binary_updated)) {
1288		status_t status = reload_driver(fDriver);
1289		if (status < B_OK)
1290			return status;
1291	}
1292
1293	if (fDriver != NULL)
1294		fDriver->devices_used++;
1295
1296	return B_OK;
1297}
1298
1299
1300void
1301LegacyDevice::UninitDevice()
1302{
1303	RecursiveLocker _(sLock);
1304
1305	if (fInitialized-- > 1)
1306		return;
1307
1308	if (fDriver != NULL) {
1309		if (--fDriver->devices_used == 0 && fDriver->devices.IsEmpty())
1310			unload_driver(fDriver);
1311		fDriver = NULL;
1312	}
1313}
1314
1315
1316void
1317LegacyDevice::Removed()
1318{
1319	RecursiveLocker _(sLock);
1320
1321	if (!fRemovedFromParent && fDriver != NULL)
1322		fDriver->devices.Remove(this);
1323
1324	delete this;
1325}
1326
1327
1328status_t
1329LegacyDevice::Control(void* _cookie, int32 op, void* buffer, size_t length)
1330{
1331	switch (op) {
1332		case B_GET_DRIVER_FOR_DEVICE:
1333			if (length != 0 && length <= strlen(fDriver->path))
1334				return ERANGE;
1335			return user_strlcpy(static_cast<char*>(buffer), fDriver->path, length);
1336		default:
1337			return AbstractModuleDevice::Control(_cookie, op, buffer, length);
1338	}
1339}
1340
1341
1342void
1343LegacyDevice::SetHooks(device_hooks* hooks)
1344{
1345	// TODO: setup compatibility layer!
1346	fHooks = hooks;
1347
1348	fDeviceModule->close = hooks->close;
1349	fDeviceModule->free = hooks->free;
1350	fDeviceModule->control = hooks->control;
1351	fDeviceModule->read = hooks->read;
1352	fDeviceModule->write = hooks->write;
1353
1354	if (fDriver == NULL || fDriver->api_version >= 2) {
1355		// According to Be newsletter, vol II, issue 36,
1356		// version 2 added readv/writev, which we don't support, but also
1357		// select/deselect.
1358		if (hooks->select != NULL) {
1359			// Note we set the module's select to a non-null value to indicate
1360			// that we have select. HasSelect() will therefore return the
1361			// correct answer. As Select() is virtual our compatibility
1362			// version below is going to be called though, that redirects to
1363			// the proper select hook, so it is ok to set it to an invalid
1364			// address here.
1365			fDeviceModule->select = (status_t (*)(void*, uint8, selectsync*))~0;
1366		}
1367
1368		fDeviceModule->deselect = hooks->deselect;
1369	}
1370}
1371
1372
1373status_t
1374LegacyDevice::Open(const char* path, int openMode, void** _cookie)
1375{
1376	return Hooks()->open(path, openMode, _cookie);
1377}
1378
1379
1380status_t
1381LegacyDevice::Select(void* cookie, uint8 event, selectsync* sync)
1382{
1383	return Hooks()->select(cookie, event, 0, sync);
1384}
1385
1386
1387//	#pragma mark - kernel private API
1388
1389
1390extern "C" void
1391legacy_driver_add_preloaded(kernel_args* args)
1392{
1393	// NOTE: This function does not exit in case of error, since it
1394	// needs to unload the images then. Also the return code of
1395	// the path operations is kept separate from the add_driver()
1396	// success, so that even if add_driver() fails for one driver, it
1397	// is still tried for the other drivers.
1398	// NOTE: The initialization success of the path objects is implicitely
1399	// checked by the immediately following functions.
1400	KPath basePath;
1401	status_t status = __find_directory(B_SYSTEM_ADDONS_DIRECTORY,
1402		gBootDevice, false, basePath.LockBuffer(), basePath.BufferSize());
1403	if (status != B_OK) {
1404		dprintf("legacy_driver_add_preloaded: find_directory() failed: "
1405			"%s\n", strerror(status));
1406	}
1407	basePath.UnlockBuffer();
1408	if (status == B_OK)
1409		status = basePath.Append("kernel");
1410	if (status != B_OK) {
1411		dprintf("legacy_driver_add_preloaded: constructing base driver "
1412			"path failed: %s\n", strerror(status));
1413		return;
1414	}
1415
1416	struct preloaded_image* image;
1417	for (image = args->preloaded_images; image != NULL; image = image->next) {
1418		if (image->is_module || image->id < 0)
1419			continue;
1420
1421		KPath imagePath(basePath);
1422		status = imagePath.Append(image->name);
1423
1424		// try to add the driver
1425		TRACE(("legacy_driver_add_preloaded: adding driver %s\n",
1426			imagePath.Path()));
1427
1428		if (status == B_OK)
1429			status = add_driver(imagePath.Path(), image->id);
1430		if (status != B_OK) {
1431			dprintf("legacy_driver_add_preloaded: Failed to add \"%s\": %s\n",
1432				(char*)image->name, strerror(status));
1433			unload_kernel_add_on(image->id);
1434		}
1435	}
1436}
1437
1438
1439extern "C" status_t
1440legacy_driver_add(const char* path)
1441{
1442	return add_driver(path, -1);
1443}
1444
1445
1446extern "C" status_t
1447legacy_driver_publish(const char* path, device_hooks* hooks)
1448{
1449	// we don't have a driver, just publish the hooks
1450	LegacyDevice* device = new(std::nothrow) LegacyDevice(NULL, path, hooks);
1451	if (device == NULL)
1452		return B_NO_MEMORY;
1453
1454	status_t status = device->InitCheck();
1455	if (status == B_OK)
1456		status = devfs_publish_device(path, device);
1457
1458	if (status != B_OK)
1459		delete device;
1460
1461	return status;
1462}
1463
1464
1465extern "C" status_t
1466legacy_driver_rescan(const char* driverName)
1467{
1468	RecursiveLocker locker(sLock);
1469
1470	legacy_driver* driver = sDriverHash->Lookup(driverName);
1471	if (driver == NULL)
1472		return B_ENTRY_NOT_FOUND;
1473
1474	// Republish the driver's entries
1475	return republish_driver(driver);
1476}
1477
1478
1479extern "C" status_t
1480legacy_driver_probe(const char* subPath)
1481{
1482	TRACE(("legacy_driver_probe(type = %s)\n", subPath));
1483
1484	char devicePath[64];
1485	snprintf(devicePath, sizeof(devicePath), "drivers/dev%s%s",
1486		subPath[0] ? "/" : "", subPath);
1487
1488	if (!sWatching && gBootDevice > 0) {
1489		// We're probing the actual boot volume for the first time,
1490		// let's watch its driver directories for changes
1491		KPath path;
1492
1493		new(&sDirectoryWatcher) DirectoryWatcher;
1494
1495		bool disableUserAddOns = get_safemode_boolean(
1496			B_SAFEMODE_DISABLE_USER_ADD_ONS, false);
1497
1498		for (uint32 i = 0; i < sizeof(kDriverPaths) / sizeof(kDriverPaths[0]); i++) {
1499			if (i < 3 && disableUserAddOns)
1500				continue;
1501
1502			if (__find_directory(kDriverPaths[i], gBootDevice, true,
1503					path.LockBuffer(), path.BufferSize()) == B_OK) {
1504				path.UnlockBuffer();
1505				path.Append("kernel/drivers");
1506
1507				start_watching(path.Path(), "bin");
1508			} else
1509				path.UnlockBuffer();
1510		}
1511
1512		sWatching = true;
1513	}
1514
1515	return probe_for_drivers(devicePath);
1516}
1517
1518
1519extern "C" status_t
1520legacy_driver_init(void)
1521{
1522	sDriverHash = new(std::nothrow) DriverTable();
1523	if (sDriverHash == NULL || sDriverHash->Init(DRIVER_HASH_SIZE) != B_OK)
1524		return B_NO_MEMORY;
1525
1526	recursive_lock_init(&sLock, "legacy driver");
1527
1528	new(&sDriverWatcher) DriverWatcher;
1529	new(&sDriverEvents) DriverEventList;
1530
1531	register_kernel_daemon(&handle_driver_events, NULL, 10);
1532		// once every second
1533
1534	add_debugger_command("legacy_driver", &dump_driver,
1535		"info about a legacy driver entry");
1536	add_debugger_command("legacy_device", &dump_device,
1537		"info about a legacy device");
1538
1539	return B_OK;
1540}
1541