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