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 (at) 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 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 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 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 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 239 WatchedEntry::InitCheck() 240 { 241 return fInitCheck; 242 } 243 244 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 283 RosterLooper::Stop() 284 { 285 Lock(); 286 delete fRoot; 287 delete fMessenger; 288 Quit(); 289 } 290 291 292 void 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 327 RosterLooper::InitCheck() 328 { 329 return fInitCheck; 330 } 331 332 333 USBRoster::USBRoster() 334 : fLooper(NULL) 335 { 336 } 337 338 339 USBRoster::~USBRoster() 340 { 341 Stop(); 342 } 343 344 345 int 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 361 USBRoster::Stop() 362 { 363 if (fLooper) { 364 ((RosterLooper *)fLooper)->Stop(); 365 fLooper = NULL; 366 } 367 } 368