/* * Copyright 2007-2008, Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Michael Lotz */ #include "haiku_usb.h" #include #include #include #include #include #include #include #include #include class WatchedEntry { public: WatchedEntry(BMessenger *, entry_ref *); ~WatchedEntry(); bool EntryCreated(entry_ref *ref); bool EntryRemoved(ino_t node); bool InitCheck(); private: BMessenger* fMessenger; node_ref fNode; bool fIsDirectory; USBDevice* fDevice; WatchedEntry* fEntries; WatchedEntry* fLink; bool fInitCheck; }; class RosterLooper : public BLooper { public: RosterLooper(USBRoster *); void Stop(); virtual void MessageReceived(BMessage *); bool InitCheck(); private: USBRoster* fRoster; WatchedEntry* fRoot; BMessenger* fMessenger; bool fInitCheck; }; WatchedEntry::WatchedEntry(BMessenger *messenger, entry_ref *ref) : fMessenger(messenger), fIsDirectory(false), fDevice(NULL), fEntries(NULL), fLink(NULL), fInitCheck(false) { BEntry entry(ref); entry.GetNodeRef(&fNode); BDirectory directory; if (entry.IsDirectory() && directory.SetTo(ref) >= B_OK) { fIsDirectory = true; while (directory.GetNextEntry(&entry) >= B_OK) { if (entry.GetRef(ref) < B_OK) continue; WatchedEntry *child = new(std::nothrow) WatchedEntry(fMessenger, ref); if (child == NULL) continue; if (child->InitCheck() == false) { delete child; continue; } child->fLink = fEntries; fEntries = child; } watch_node(&fNode, B_WATCH_DIRECTORY, *fMessenger); } else { if (strncmp(ref->name, "raw", 3) == 0) return; BPath path, parent_path; entry.GetPath(&path); fDevice = new(std::nothrow) USBDevice(path.Path()); if (fDevice != NULL && fDevice->InitCheck() == true) { // Add this new device to each active context's device list struct libusb_context *ctx; unsigned long session_id = (unsigned long)&fDevice; usbi_mutex_lock(&active_contexts_lock); for_each_context(ctx) { struct libusb_device *dev = usbi_get_device_by_session_id(ctx, session_id); if (dev) { usbi_dbg(NULL, "using previously allocated device with location %lu", session_id); libusb_unref_device(dev); continue; } usbi_dbg(NULL, "allocating new device with location %lu", session_id); dev = usbi_alloc_device(ctx, session_id); if (!dev) { usbi_dbg(NULL, "device allocation failed"); continue; } *((USBDevice **)usbi_get_device_priv(dev)) = fDevice; // Calculate pseudo-device-address int addr, tmp; if (strcmp(path.Leaf(), "hub") == 0) tmp = 100; //Random Number else sscanf(path.Leaf(), "%d", &tmp); addr = tmp + 1; path.GetParent(&parent_path); while (strcmp(parent_path.Leaf(), "usb") != 0) { sscanf(parent_path.Leaf(), "%d", &tmp); addr += tmp + 1; parent_path.GetParent(&parent_path); } sscanf(path.Path(), "/dev/bus/usb/%hhu", &dev->bus_number); dev->device_address = addr - (dev->bus_number + 1); static_assert(sizeof(dev->device_descriptor) == sizeof(usb_device_descriptor), "mismatch between libusb and OS device descriptor sizes"); memcpy(&dev->device_descriptor, fDevice->Descriptor(), LIBUSB_DT_DEVICE_SIZE); usbi_localize_device_descriptor(&dev->device_descriptor); if (usbi_sanitize_device(dev) < 0) { usbi_dbg(NULL, "device sanitization failed"); libusb_unref_device(dev); continue; } usbi_connect_device(dev); } usbi_mutex_unlock(&active_contexts_lock); } else if (fDevice) { delete fDevice; fDevice = NULL; return; } } fInitCheck = true; } WatchedEntry::~WatchedEntry() { if (fIsDirectory) { watch_node(&fNode, B_STOP_WATCHING, *fMessenger); WatchedEntry *child = fEntries; while (child) { WatchedEntry *next = child->fLink; delete child; child = next; } } if (fDevice) { // Remove this device from each active context's device list struct libusb_context *ctx; struct libusb_device *dev; unsigned long session_id = (unsigned long)&fDevice; usbi_mutex_lock(&active_contexts_lock); for_each_context(ctx) { dev = usbi_get_device_by_session_id(ctx, session_id); if (dev != NULL) { usbi_disconnect_device(dev); libusb_unref_device(dev); } else { usbi_dbg(ctx, "device with location %lu not found", session_id); } } usbi_mutex_static_unlock(&active_contexts_lock); delete fDevice; } } bool WatchedEntry::EntryCreated(entry_ref *ref) { if (!fIsDirectory) return false; if (ref->directory != fNode.node) { WatchedEntry *child = fEntries; while (child) { if (child->EntryCreated(ref)) return true; child = child->fLink; } return false; } WatchedEntry *child = new(std::nothrow) WatchedEntry(fMessenger, ref); if (child == NULL) return false; child->fLink = fEntries; fEntries = child; return true; } bool WatchedEntry::EntryRemoved(ino_t node) { if (!fIsDirectory) return false; WatchedEntry *child = fEntries; WatchedEntry *lastChild = NULL; while (child) { if (child->fNode.node == node) { if (lastChild) lastChild->fLink = child->fLink; else fEntries = child->fLink; delete child; return true; } if (child->EntryRemoved(node)) return true; lastChild = child; child = child->fLink; } return false; } bool WatchedEntry::InitCheck() { return fInitCheck; } RosterLooper::RosterLooper(USBRoster *roster) : BLooper("LibusbRoster Looper"), fRoster(roster), fRoot(NULL), fMessenger(NULL), fInitCheck(false) { BEntry entry("/dev/bus/usb"); if (!entry.Exists()) { usbi_err(NULL, "usb_raw not published"); return; } Run(); fMessenger = new(std::nothrow) BMessenger(this); if (fMessenger == NULL) { usbi_err(NULL, "error creating BMessenger object"); return; } if (Lock()) { entry_ref ref; entry.GetRef(&ref); fRoot = new(std::nothrow) WatchedEntry(fMessenger, &ref); Unlock(); if (fRoot == NULL) return; if (fRoot->InitCheck() == false) { delete fRoot; fRoot = NULL; return; } } fInitCheck = true; } void RosterLooper::Stop() { Lock(); delete fRoot; delete fMessenger; Quit(); } void RosterLooper::MessageReceived(BMessage *message) { int32 opcode; if (message->FindInt32("opcode", &opcode) < B_OK) return; switch (opcode) { case B_ENTRY_CREATED: { dev_t device; ino_t directory; const char *name; if (message->FindInt32("device", &device) < B_OK || message->FindInt64("directory", &directory) < B_OK || message->FindString("name", &name) < B_OK) break; entry_ref ref(device, directory, name); fRoot->EntryCreated(&ref); break; } case B_ENTRY_REMOVED: { ino_t node; if (message->FindInt64("node", &node) < B_OK) break; fRoot->EntryRemoved(node); break; } } } bool RosterLooper::InitCheck() { return fInitCheck; } USBRoster::USBRoster() : fLooper(NULL) { } USBRoster::~USBRoster() { Stop(); } int USBRoster::Start() { if (fLooper == NULL) { fLooper = new(std::nothrow) RosterLooper(this); if (fLooper == NULL || ((RosterLooper *)fLooper)->InitCheck() == false) { if (fLooper) fLooper = NULL; return LIBUSB_ERROR_OTHER; } } return LIBUSB_SUCCESS; } void USBRoster::Stop() { if (fLooper) { ((RosterLooper *)fLooper)->Stop(); fLooper = NULL; } }