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