/* * Copyright 2007-2008, Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Michael Lotz */ #include #include #include #include #include #include #include #include #include #include #include class WatchedEntry { public: WatchedEntry(BUSBRoster *roster, BMessenger *messenger, entry_ref *ref); ~WatchedEntry(); bool EntryCreated(entry_ref *ref); bool EntryRemoved(ino_t node); private: BUSBRoster * fRoster; BMessenger * fMessenger; node_ref fNode; bool fIsDirectory; BUSBDevice * fDevice; WatchedEntry * fEntries; WatchedEntry * fLink; }; class RosterLooper : public BLooper { public: RosterLooper(BUSBRoster *roster); void Stop(); virtual void MessageReceived(BMessage *message); private: BUSBRoster * fRoster; WatchedEntry * fRoot; BMessenger * fMessenger; }; WatchedEntry::WatchedEntry(BUSBRoster *roster, BMessenger *messenger, entry_ref *ref) : fRoster(roster), fMessenger(messenger), fIsDirectory(false), fDevice(NULL), fEntries(NULL), fLink(NULL) { 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(fRoster, fMessenger, ref); if (child == NULL) continue; child->fLink = fEntries; fEntries = child; } watch_node(&fNode, B_WATCH_DIRECTORY, *fMessenger); } else { // filter pseudoentry that only handles ioctls if (strncmp(ref->name, "raw", 3) == 0) return; BPath path; entry.GetPath(&path); fDevice = new(std::nothrow) BUSBDevice(path.Path()); if (fDevice != NULL) { if (fRoster->DeviceAdded(fDevice) != B_OK) { delete fDevice; fDevice = NULL; } } } } 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) { fRoster->DeviceRemoved(fDevice); 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(fRoster, 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; } RosterLooper::RosterLooper(BUSBRoster *roster) : fRoster(roster), fRoot(NULL), fMessenger(NULL) { BEntry entry("/dev/bus/usb"); if (!entry.Exists()) { fprintf(stderr, "USBKit: usb_raw not published\n"); return; } Run(); fMessenger = new(std::nothrow) BMessenger(this); if (fMessenger == NULL) return; if (Lock()) { entry_ref ref; entry.GetRef(&ref); fRoot = new(std::nothrow) WatchedEntry(fRoster, fMessenger, &ref); Unlock(); } } void RosterLooper::Stop() { Lock(); delete fRoot; 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; } } } BUSBRoster::BUSBRoster() : fLooper(NULL) { } BUSBRoster::~BUSBRoster() { Stop(); } void BUSBRoster::Start() { if (fLooper) return; fLooper = new(std::nothrow) RosterLooper(this); } void BUSBRoster::Stop() { if (!fLooper) return; ((RosterLooper *)fLooper)->Stop(); fLooper = NULL; } // definition of reserved virtual functions void BUSBRoster::_ReservedUSBRoster1() {}; void BUSBRoster::_ReservedUSBRoster2() {}; void BUSBRoster::_ReservedUSBRoster3() {}; void BUSBRoster::_ReservedUSBRoster4() {}; void BUSBRoster::_ReservedUSBRoster5() {};