• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "haiku_usb.h"
10 #include <cstdio>
11 #include <Directory.h>
12 #include <Entry.h>
13 #include <Looper.h>
14 #include <Messenger.h>
15 #include <Node.h>
16 #include <NodeMonitor.h>
17 #include <Path.h>
18 #include <cstring>
19 
20 class WatchedEntry {
21 public:
22 			WatchedEntry(BMessenger *, entry_ref *);
23 			~WatchedEntry();
24 	bool		EntryCreated(entry_ref *ref);
25 	bool		EntryRemoved(ino_t node);
26 	bool		InitCheck();
27 
28 private:
29 	BMessenger*	fMessenger;
30 	node_ref	fNode;
31 	bool		fIsDirectory;
32 	USBDevice*	fDevice;
33 	WatchedEntry*	fEntries;
34 	WatchedEntry*	fLink;
35 	bool		fInitCheck;
36 };
37 
38 
39 class RosterLooper : public BLooper {
40 public:
41 			RosterLooper(USBRoster *);
42 	void		Stop();
43 	virtual void	MessageReceived(BMessage *);
44 	bool		InitCheck();
45 
46 private:
47 	USBRoster*	fRoster;
48 	WatchedEntry*	fRoot;
49 	BMessenger*	fMessenger;
50 	bool		fInitCheck;
51 };
52 
53 
WatchedEntry(BMessenger * messenger,entry_ref * ref)54 WatchedEntry::WatchedEntry(BMessenger *messenger, entry_ref *ref)
55 	:	fMessenger(messenger),
56 		fIsDirectory(false),
57 		fDevice(NULL),
58 		fEntries(NULL),
59 		fLink(NULL),
60 		fInitCheck(false)
61 {
62 	BEntry entry(ref);
63 	entry.GetNodeRef(&fNode);
64 
65 	BDirectory directory;
66 	if (entry.IsDirectory() && directory.SetTo(ref) >= B_OK) {
67 		fIsDirectory = true;
68 
69 		while (directory.GetNextEntry(&entry) >= B_OK) {
70 			if (entry.GetRef(ref) < B_OK)
71 				continue;
72 
73 			WatchedEntry *child = new(std::nothrow) WatchedEntry(fMessenger, ref);
74 			if (child == NULL)
75 				continue;
76 			if (child->InitCheck() == false) {
77 				delete child;
78 				continue;
79 			}
80 
81 			child->fLink = fEntries;
82 			fEntries = child;
83 		}
84 
85 		watch_node(&fNode, B_WATCH_DIRECTORY, *fMessenger);
86 	}
87 	else {
88 		if (strncmp(ref->name, "raw", 3) == 0)
89 			return;
90 
91 		BPath path, parent_path;
92 		entry.GetPath(&path);
93 		fDevice = new(std::nothrow) USBDevice(path.Path());
94 		if (fDevice != NULL && fDevice->InitCheck() == true) {
95 			// Add this new device to each active context's device list
96 			struct libusb_context *ctx;
97 			unsigned long session_id = (unsigned long)&fDevice;
98 
99 			usbi_mutex_lock(&active_contexts_lock);
100 			list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) {
101 				struct libusb_device *dev = usbi_get_device_by_session_id(ctx, session_id);
102 				if (dev) {
103 					usbi_dbg("using previously allocated device with location %lu", session_id);
104 					libusb_unref_device(dev);
105 					continue;
106 				}
107 				usbi_dbg("allocating new device with location %lu", session_id);
108 				dev = usbi_alloc_device(ctx, session_id);
109 				if (!dev) {
110 					usbi_dbg("device allocation failed");
111 					continue;
112 				}
113 				*((USBDevice **)dev->os_priv) = fDevice;
114 
115 				// Calculate pseudo-device-address
116 				int addr, tmp;
117 				if (strcmp(path.Leaf(), "hub") == 0)
118 					tmp = 100;	//Random Number
119 				else
120 					sscanf(path.Leaf(), "%d", &tmp);
121 				addr = tmp + 1;
122 				path.GetParent(&parent_path);
123 				while (strcmp(parent_path.Leaf(), "usb") != 0) {
124 					sscanf(parent_path.Leaf(), "%d", &tmp);
125 					addr += tmp + 1;
126 					parent_path.GetParent(&parent_path);
127 				}
128 				sscanf(path.Path(), "/dev/bus/usb/%d", &dev->bus_number);
129 				dev->device_address = addr - (dev->bus_number + 1);
130 
131 				if (usbi_sanitize_device(dev) < 0) {
132 					usbi_dbg("device sanitization failed");
133 					libusb_unref_device(dev);
134 					continue;
135 				}
136 				usbi_connect_device(dev);
137 			}
138 			usbi_mutex_unlock(&active_contexts_lock);
139 		}
140 		else if (fDevice) {
141 			delete fDevice;
142 			fDevice = NULL;
143 			return;
144 		}
145 	}
146 	fInitCheck = true;
147 }
148 
149 
~WatchedEntry()150 WatchedEntry::~WatchedEntry()
151 {
152 	if (fIsDirectory) {
153 		watch_node(&fNode, B_STOP_WATCHING, *fMessenger);
154 
155 		WatchedEntry *child = fEntries;
156 		while (child) {
157 			WatchedEntry *next = child->fLink;
158 			delete child;
159 			child = next;
160 		}
161 	}
162 
163 	if (fDevice) {
164 		// Remove this device from each active context's device list
165 		struct libusb_context *ctx;
166 		struct libusb_device *dev;
167 		unsigned long session_id = (unsigned long)&fDevice;
168 
169 		usbi_mutex_lock(&active_contexts_lock);
170 		list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) {
171 			dev = usbi_get_device_by_session_id(ctx, session_id);
172 			if (dev != NULL) {
173 				usbi_disconnect_device(dev);
174 				libusb_unref_device(dev);
175 			} else {
176 				usbi_dbg("device with location %lu not found", session_id);
177 			}
178 		}
179 		usbi_mutex_static_unlock(&active_contexts_lock);
180 		delete fDevice;
181 	}
182 }
183 
184 
185 bool
EntryCreated(entry_ref * ref)186 WatchedEntry::EntryCreated(entry_ref *ref)
187 {
188 	if (!fIsDirectory)
189 		return false;
190 
191 	if (ref->directory != fNode.node) {
192 		WatchedEntry *child = fEntries;
193 		while (child) {
194 			if (child->EntryCreated(ref))
195 				return true;
196 			child = child->fLink;
197 		}
198 		return false;
199 	}
200 
201 	WatchedEntry *child = new(std::nothrow) WatchedEntry(fMessenger, ref);
202 	if (child == NULL)
203 		return false;
204 	child->fLink = fEntries;
205 	fEntries = child;
206 	return true;
207 }
208 
209 
210 bool
EntryRemoved(ino_t node)211 WatchedEntry::EntryRemoved(ino_t node)
212 {
213 	if (!fIsDirectory)
214 		return false;
215 
216 	WatchedEntry *child = fEntries;
217 	WatchedEntry *lastChild = NULL;
218 	while (child) {
219 		if (child->fNode.node == node) {
220 			if (lastChild)
221 				lastChild->fLink = child->fLink;
222 			else
223 				fEntries = child->fLink;
224 			delete child;
225 			return true;
226 		}
227 
228 		if (child->EntryRemoved(node))
229 			return true;
230 
231 		lastChild = child;
232 		child = child->fLink;
233 	}
234 	return false;
235 }
236 
237 
238 bool
InitCheck()239 WatchedEntry::InitCheck()
240 {
241 	return fInitCheck;
242 }
243 
244 
RosterLooper(USBRoster * roster)245 RosterLooper::RosterLooper(USBRoster *roster)
246 	:	BLooper("LibusbRoster Looper"),
247 		fRoster(roster),
248 		fRoot(NULL),
249 		fMessenger(NULL),
250 		fInitCheck(false)
251 {
252 	BEntry entry("/dev/bus/usb");
253 	if (!entry.Exists()) {
254 		usbi_err(NULL, "usb_raw not published");
255 		return;
256 	}
257 
258 	Run();
259 	fMessenger = new(std::nothrow) BMessenger(this);
260 	if (fMessenger == NULL) {
261 		usbi_err(NULL, "error creating BMessenger object");
262 		return;
263 	}
264 
265 	if (Lock()) {
266 		entry_ref ref;
267 		entry.GetRef(&ref);
268 		fRoot = new(std::nothrow) WatchedEntry(fMessenger, &ref);
269 		Unlock();
270 		if (fRoot == NULL)
271 			return;
272 		if (fRoot->InitCheck() == false) {
273 			delete fRoot;
274 			fRoot = NULL;
275 			return;
276 		}
277 	}
278 	fInitCheck = true;
279 }
280 
281 
282 void
Stop()283 RosterLooper::Stop()
284 {
285 	Lock();
286 	delete fRoot;
287 	delete fMessenger;
288 	Quit();
289 }
290 
291 
292 void
MessageReceived(BMessage * message)293 RosterLooper::MessageReceived(BMessage *message)
294 {
295 	int32 opcode;
296 	if (message->FindInt32("opcode", &opcode) < B_OK)
297 		return;
298 
299 	switch (opcode) {
300 		case B_ENTRY_CREATED:
301 		{
302 			dev_t device;
303 			ino_t directory;
304 			const char *name;
305 			if (message->FindInt32("device", &device) < B_OK ||
306 				message->FindInt64("directory", &directory) < B_OK ||
307 				message->FindString("name", &name) < B_OK)
308 				break;
309 
310 			entry_ref ref(device, directory, name);
311 			fRoot->EntryCreated(&ref);
312 			break;
313 		}
314 		case B_ENTRY_REMOVED:
315 		{
316 			ino_t node;
317 			if (message->FindInt64("node", &node) < B_OK)
318 				break;
319 			fRoot->EntryRemoved(node);
320 			break;
321 		}
322 	}
323 }
324 
325 
326 bool
InitCheck()327 RosterLooper::InitCheck()
328 {
329 	return fInitCheck;
330 }
331 
332 
USBRoster()333 USBRoster::USBRoster()
334 	:	fLooper(NULL)
335 {
336 }
337 
338 
~USBRoster()339 USBRoster::~USBRoster()
340 {
341 	Stop();
342 }
343 
344 
345 int
Start()346 USBRoster::Start()
347 {
348 	if (fLooper == NULL) {
349 		fLooper = new(std::nothrow) RosterLooper(this);
350 		if (fLooper == NULL || ((RosterLooper *)fLooper)->InitCheck() == false) {
351 			if (fLooper)
352 				fLooper = NULL;
353 			return LIBUSB_ERROR_OTHER;
354 		}
355 	}
356 	return LIBUSB_SUCCESS;
357 }
358 
359 
360 void
Stop()361 USBRoster::Stop()
362 {
363 	if (fLooper) {
364 		((RosterLooper *)fLooper)->Stop();
365 		fLooper = NULL;
366 	}
367 }
368