It is supposed to be cleaner and more object oriented. It also adds some features (like a string getter for the interface) that were appearantly left out in the Be implementation. It is currently built as a small static library from src/libs. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19913 a95241bf-73f2-0310-859d-f6bbb57e9c96
270 lines
4.4 KiB
C++
270 lines
4.4 KiB
C++
/*
|
|
* Copyright 2007, Haiku Inc. All rights reserved.
|
|
* Distributed under the terms of the MIT License.
|
|
*
|
|
* Authors:
|
|
* Michael Lotz <[email protected]>
|
|
*/
|
|
|
|
#include <USBKit.h>
|
|
#include <usb_raw.h>
|
|
#include <Directory.h>
|
|
#include <Entry.h>
|
|
#include <Looper.h>
|
|
#include <Messenger.h>
|
|
#include <Node.h>
|
|
#include <NodeMonitor.h>
|
|
#include <Path.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
|
|
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 WatchedEntry(fRoster, fMessenger, ref);
|
|
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 BUSBDevice(path.Path());
|
|
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);
|
|
}
|
|
|
|
|
|
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 WatchedEntry(fRoster, fMessenger, ref);
|
|
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");
|
|
return;
|
|
}
|
|
|
|
Run();
|
|
fMessenger = new BMessenger(this);
|
|
|
|
if (Lock()) {
|
|
entry_ref ref;
|
|
entry.GetRef(&ref);
|
|
fRoot = new 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 RosterLooper(this);
|
|
}
|
|
|
|
|
|
void
|
|
BUSBRoster::Stop()
|
|
{
|
|
if (!fLooper)
|
|
return;
|
|
|
|
((RosterLooper *)fLooper)->Stop();
|
|
fLooper = NULL;
|
|
}
|