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 <[email protected]>
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 for_each_context(ctx) {
101 struct libusb_device *dev = usbi_get_device_by_session_id(ctx, session_id);
102 if (dev) {
103 usbi_dbg(NULL, "using previously allocated device with location %lu", session_id);
104 libusb_unref_device(dev);
105 continue;
106 }
107 usbi_dbg(NULL, "allocating new device with location %lu", session_id);
108 dev = usbi_alloc_device(ctx, session_id);
109 if (!dev) {
110 usbi_dbg(NULL, "device allocation failed");
111 continue;
112 }
113 *((USBDevice **)usbi_get_device_priv(dev)) = 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/%hhu", &dev->bus_number);
129 dev->device_address = addr - (dev->bus_number + 1);
130
131 static_assert(sizeof(dev->device_descriptor) == sizeof(usb_device_descriptor),
132 "mismatch between libusb and OS device descriptor sizes");
133 memcpy(&dev->device_descriptor, fDevice->Descriptor(), LIBUSB_DT_DEVICE_SIZE);
134 usbi_localize_device_descriptor(&dev->device_descriptor);
135
136 if (usbi_sanitize_device(dev) < 0) {
137 usbi_dbg(NULL, "device sanitization failed");
138 libusb_unref_device(dev);
139 continue;
140 }
141 usbi_connect_device(dev);
142 }
143 usbi_mutex_unlock(&active_contexts_lock);
144 }
145 else if (fDevice) {
146 delete fDevice;
147 fDevice = NULL;
148 return;
149 }
150 }
151 fInitCheck = true;
152 }
153
154
~WatchedEntry()155 WatchedEntry::~WatchedEntry()
156 {
157 if (fIsDirectory) {
158 watch_node(&fNode, B_STOP_WATCHING, *fMessenger);
159
160 WatchedEntry *child = fEntries;
161 while (child) {
162 WatchedEntry *next = child->fLink;
163 delete child;
164 child = next;
165 }
166 }
167
168 if (fDevice) {
169 // Remove this device from each active context's device list
170 struct libusb_context *ctx;
171 struct libusb_device *dev;
172 unsigned long session_id = (unsigned long)&fDevice;
173
174 usbi_mutex_lock(&active_contexts_lock);
175 for_each_context(ctx) {
176 dev = usbi_get_device_by_session_id(ctx, session_id);
177 if (dev != NULL) {
178 usbi_disconnect_device(dev);
179 libusb_unref_device(dev);
180 } else {
181 usbi_dbg(ctx, "device with location %lu not found", session_id);
182 }
183 }
184 usbi_mutex_static_unlock(&active_contexts_lock);
185 delete fDevice;
186 }
187 }
188
189
190 bool
EntryCreated(entry_ref * ref)191 WatchedEntry::EntryCreated(entry_ref *ref)
192 {
193 if (!fIsDirectory)
194 return false;
195
196 if (ref->directory != fNode.node) {
197 WatchedEntry *child = fEntries;
198 while (child) {
199 if (child->EntryCreated(ref))
200 return true;
201 child = child->fLink;
202 }
203 return false;
204 }
205
206 WatchedEntry *child = new(std::nothrow) WatchedEntry(fMessenger, ref);
207 if (child == NULL)
208 return false;
209 child->fLink = fEntries;
210 fEntries = child;
211 return true;
212 }
213
214
215 bool
EntryRemoved(ino_t node)216 WatchedEntry::EntryRemoved(ino_t node)
217 {
218 if (!fIsDirectory)
219 return false;
220
221 WatchedEntry *child = fEntries;
222 WatchedEntry *lastChild = NULL;
223 while (child) {
224 if (child->fNode.node == node) {
225 if (lastChild)
226 lastChild->fLink = child->fLink;
227 else
228 fEntries = child->fLink;
229 delete child;
230 return true;
231 }
232
233 if (child->EntryRemoved(node))
234 return true;
235
236 lastChild = child;
237 child = child->fLink;
238 }
239 return false;
240 }
241
242
243 bool
InitCheck()244 WatchedEntry::InitCheck()
245 {
246 return fInitCheck;
247 }
248
249
RosterLooper(USBRoster * roster)250 RosterLooper::RosterLooper(USBRoster *roster)
251 : BLooper("LibusbRoster Looper"),
252 fRoster(roster),
253 fRoot(NULL),
254 fMessenger(NULL),
255 fInitCheck(false)
256 {
257 BEntry entry("/dev/bus/usb");
258 if (!entry.Exists()) {
259 usbi_err(NULL, "usb_raw not published");
260 return;
261 }
262
263 Run();
264 fMessenger = new(std::nothrow) BMessenger(this);
265 if (fMessenger == NULL) {
266 usbi_err(NULL, "error creating BMessenger object");
267 return;
268 }
269
270 if (Lock()) {
271 entry_ref ref;
272 entry.GetRef(&ref);
273 fRoot = new(std::nothrow) WatchedEntry(fMessenger, &ref);
274 Unlock();
275 if (fRoot == NULL)
276 return;
277 if (fRoot->InitCheck() == false) {
278 delete fRoot;
279 fRoot = NULL;
280 return;
281 }
282 }
283 fInitCheck = true;
284 }
285
286
287 void
Stop()288 RosterLooper::Stop()
289 {
290 Lock();
291 delete fRoot;
292 delete fMessenger;
293 Quit();
294 }
295
296
297 void
MessageReceived(BMessage * message)298 RosterLooper::MessageReceived(BMessage *message)
299 {
300 int32 opcode;
301 if (message->FindInt32("opcode", &opcode) < B_OK)
302 return;
303
304 switch (opcode) {
305 case B_ENTRY_CREATED:
306 {
307 dev_t device;
308 ino_t directory;
309 const char *name;
310 if (message->FindInt32("device", &device) < B_OK ||
311 message->FindInt64("directory", &directory) < B_OK ||
312 message->FindString("name", &name) < B_OK)
313 break;
314
315 entry_ref ref(device, directory, name);
316 fRoot->EntryCreated(&ref);
317 break;
318 }
319 case B_ENTRY_REMOVED:
320 {
321 ino_t node;
322 if (message->FindInt64("node", &node) < B_OK)
323 break;
324 fRoot->EntryRemoved(node);
325 break;
326 }
327 }
328 }
329
330
331 bool
InitCheck()332 RosterLooper::InitCheck()
333 {
334 return fInitCheck;
335 }
336
337
USBRoster()338 USBRoster::USBRoster()
339 : fLooper(NULL)
340 {
341 }
342
343
~USBRoster()344 USBRoster::~USBRoster()
345 {
346 Stop();
347 }
348
349
350 int
Start()351 USBRoster::Start()
352 {
353 if (fLooper == NULL) {
354 fLooper = new(std::nothrow) RosterLooper(this);
355 if (fLooper == NULL || ((RosterLooper *)fLooper)->InitCheck() == false) {
356 if (fLooper)
357 fLooper = NULL;
358 return LIBUSB_ERROR_OTHER;
359 }
360 }
361 return LIBUSB_SUCCESS;
362 }
363
364
365 void
Stop()366 USBRoster::Stop()
367 {
368 if (fLooper) {
369 ((RosterLooper *)fLooper)->Stop();
370 fLooper = NULL;
371 }
372 }
373