| /* |
| * Copyright 2007-2008, Haiku Inc. All rights reserved. |
| * Distributed under the terms of the MIT License. |
| * |
| * Authors: |
| * Michael Lotz <mmlr@mlotz.ch> |
| */ |
| |
| #include "haiku_usb.h" |
| #include <cstdio> |
| #include <Directory.h> |
| #include <Entry.h> |
| #include <Looper.h> |
| #include <Messenger.h> |
| #include <Node.h> |
| #include <NodeMonitor.h> |
| #include <Path.h> |
| #include <cstring> |
| |
| 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("using previously allocated device with location %lu", session_id); |
| libusb_unref_device(dev); |
| continue; |
| } |
| usbi_dbg("allocating new device with location %lu", session_id); |
| dev = usbi_alloc_device(ctx, session_id); |
| if (!dev) { |
| usbi_dbg("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("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("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; |
| } |
| } |