1/*
2 * Copyright 2007-2008, Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Michael Lotz <mmlr@mlotz.ch>
7 */
8
9#include <USBKit.h>
10#include <Directory.h>
11#include <Entry.h>
12#include <Looper.h>
13#include <Messenger.h>
14#include <Node.h>
15#include <NodeMonitor.h>
16#include <Path.h>
17#include <stdio.h>
18#include <string.h>
19#include <new>
20
21
22class WatchedEntry {
23public:
24							WatchedEntry(BUSBRoster *roster,
25								BMessenger *messenger, entry_ref *ref);
26							~WatchedEntry();
27
28		bool				EntryCreated(entry_ref *ref);
29		bool				EntryRemoved(ino_t node);
30
31private:
32		BUSBRoster *		fRoster;
33		BMessenger *		fMessenger;
34
35		node_ref			fNode;
36		bool				fIsDirectory;
37		BUSBDevice *		fDevice;
38
39		WatchedEntry *		fEntries;
40		WatchedEntry *		fLink;
41};
42
43
44class RosterLooper : public BLooper {
45public:
46							RosterLooper(BUSBRoster *roster);
47
48		void				Stop();
49
50virtual	void				MessageReceived(BMessage *message);
51
52private:
53		BUSBRoster *		fRoster;
54		WatchedEntry *		fRoot;
55		BMessenger *		fMessenger;
56};
57
58
59WatchedEntry::WatchedEntry(BUSBRoster *roster, BMessenger *messenger,
60	entry_ref *ref)
61	:	fRoster(roster),
62		fMessenger(messenger),
63		fIsDirectory(false),
64		fDevice(NULL),
65		fEntries(NULL),
66		fLink(NULL)
67{
68	BEntry entry(ref);
69	entry.GetNodeRef(&fNode);
70
71	BDirectory directory;
72	if (entry.IsDirectory() && directory.SetTo(ref) >= B_OK) {
73		fIsDirectory = true;
74
75		while(directory.GetNextEntry(&entry) >= B_OK) {
76			if (entry.GetRef(ref) < B_OK)
77				continue;
78
79			WatchedEntry *child = new(std::nothrow) WatchedEntry(fRoster,
80				fMessenger, ref);
81			if (child == NULL)
82				continue;
83
84			child->fLink = fEntries;
85			fEntries = child;
86		}
87
88		watch_node(&fNode, B_WATCH_DIRECTORY, *fMessenger);
89	} else {
90		// filter pseudoentry that only handles ioctls
91		if (strncmp(ref->name, "raw", 3) == 0)
92			return;
93
94		BPath path;
95		entry.GetPath(&path);
96		fDevice = new(std::nothrow) BUSBDevice(path.Path());
97		if (fDevice != NULL) {
98			if (fRoster->DeviceAdded(fDevice) != B_OK) {
99				delete fDevice;
100				fDevice = NULL;
101			}
102		}
103	}
104}
105
106
107WatchedEntry::~WatchedEntry()
108{
109	if (fIsDirectory) {
110		watch_node(&fNode, B_STOP_WATCHING, *fMessenger);
111
112		WatchedEntry *child = fEntries;
113		while (child) {
114			WatchedEntry *next = child->fLink;
115			delete child;
116			child = next;
117		}
118	}
119
120	if (fDevice) {
121		fRoster->DeviceRemoved(fDevice);
122		delete fDevice;
123	}
124}
125
126
127bool
128WatchedEntry::EntryCreated(entry_ref *ref)
129{
130	if (!fIsDirectory)
131		return false;
132
133	if (ref->directory != fNode.node) {
134		WatchedEntry *child = fEntries;
135		while (child) {
136			if (child->EntryCreated(ref))
137				return true;
138			child = child->fLink;
139		}
140
141		return false;
142	}
143
144	WatchedEntry *child = new(std::nothrow) WatchedEntry(fRoster, fMessenger,
145		ref);
146	if (child == NULL)
147		return false;
148
149	child->fLink = fEntries;
150	fEntries = child;
151	return true;
152}
153
154
155bool
156WatchedEntry::EntryRemoved(ino_t node)
157{
158	if (!fIsDirectory)
159		return false;
160
161	WatchedEntry *child = fEntries;
162	WatchedEntry *lastChild = NULL;
163	while (child) {
164		if (child->fNode.node == node) {
165			if (lastChild)
166				lastChild->fLink = child->fLink;
167			else
168				fEntries = child->fLink;
169
170			delete child;
171			return true;
172		}
173
174		if (child->EntryRemoved(node))
175			return true;
176
177		lastChild = child;
178		child = child->fLink;
179	}
180
181	return false;
182}
183
184
185RosterLooper::RosterLooper(BUSBRoster *roster)
186	:	BLooper("BUSBRoster looper"),
187		fRoster(roster),
188		fRoot(NULL),
189		fMessenger(NULL)
190{
191	BEntry entry("/dev/bus/usb");
192	if (!entry.Exists()) {
193		fprintf(stderr, "USBKit: usb_raw not published\n");
194		return;
195	}
196
197	Run();
198	fMessenger = new(std::nothrow) BMessenger(this);
199	if (fMessenger == NULL)
200		return;
201
202	if (Lock()) {
203		entry_ref ref;
204		entry.GetRef(&ref);
205		fRoot = new(std::nothrow) WatchedEntry(fRoster, fMessenger, &ref);
206		Unlock();
207	}
208}
209
210
211void
212RosterLooper::Stop()
213{
214	Lock();
215	delete fRoot;
216	Quit();
217}
218
219
220void
221RosterLooper::MessageReceived(BMessage *message)
222{
223	int32 opcode;
224	if (message->FindInt32("opcode", &opcode) < B_OK)
225		return;
226
227	switch (opcode) {
228		case B_ENTRY_CREATED: {
229			dev_t device;
230			ino_t directory;
231			const char *name;
232			if (message->FindInt32("device", &device) < B_OK
233				|| message->FindInt64("directory", &directory) < B_OK
234				|| message->FindString("name", &name) < B_OK)
235				break;
236
237			entry_ref ref(device, directory, name);
238			fRoot->EntryCreated(&ref);
239			break;
240		}
241
242		case B_ENTRY_REMOVED: {
243			ino_t node;
244			if (message->FindInt64("node", &node) < B_OK)
245				break;
246
247			fRoot->EntryRemoved(node);
248			break;
249		}
250	}
251}
252
253
254BUSBRoster::BUSBRoster()
255	:	fLooper(NULL)
256{
257}
258
259
260BUSBRoster::~BUSBRoster()
261{
262	Stop();
263}
264
265
266void
267BUSBRoster::Start()
268{
269	if (fLooper)
270		return;
271
272	fLooper = new(std::nothrow) RosterLooper(this);
273}
274
275
276void
277BUSBRoster::Stop()
278{
279	if (!fLooper)
280		return;
281
282	((RosterLooper *)fLooper)->Stop();
283	fLooper = NULL;
284}
285
286
287// definition of reserved virtual functions
288void BUSBRoster::_ReservedUSBRoster1() {};
289void BUSBRoster::_ReservedUSBRoster2() {};
290void BUSBRoster::_ReservedUSBRoster3() {};
291void BUSBRoster::_ReservedUSBRoster4() {};
292void BUSBRoster::_ReservedUSBRoster5() {};
293