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@mlotz.ch>
7 */
8
9 #include <USBKit.h>
10 #include <Directory.h>
11 #include <Entry.h>
12 #include <Looper.h>
13 #include <Messenger.h>
14 #include <Node.h>
15 #include <NodeMonitor.h>
16 #include <Path.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <new>
20
21
22 class WatchedEntry {
23 public:
24 WatchedEntry(BUSBRoster *roster,
25 BMessenger *messenger, entry_ref *ref);
26 ~WatchedEntry();
27
28 bool EntryCreated(entry_ref *ref);
29 bool EntryRemoved(ino_t node);
30
31 private:
32 BUSBRoster * fRoster;
33 BMessenger * fMessenger;
34
35 node_ref fNode;
36 bool fIsDirectory;
37 BUSBDevice * fDevice;
38
39 WatchedEntry * fEntries;
40 WatchedEntry * fLink;
41 };
42
43
44 class RosterLooper : public BLooper {
45 public:
46 RosterLooper(BUSBRoster *roster);
47
48 void Stop();
49
50 virtual void MessageReceived(BMessage *message);
51
52 private:
53 BUSBRoster * fRoster;
54 WatchedEntry * fRoot;
55 BMessenger * fMessenger;
56 };
57
58
WatchedEntry(BUSBRoster * roster,BMessenger * messenger,entry_ref * ref)59 WatchedEntry::WatchedEntry(BUSBRoster *roster, BMessenger *messenger,
60 entry_ref *ref)
61 : fRoster(roster),
62 fMessenger(messenger),
63 fIsDirectory(false),
64 fDevice(NULL),
65 fEntries(NULL),
66 fLink(NULL)
67 {
68 BEntry entry(ref);
69 entry.GetNodeRef(&fNode);
70
71 BDirectory directory;
72 if (entry.IsDirectory() && directory.SetTo(ref) >= B_OK) {
73 fIsDirectory = true;
74
75 while(directory.GetNextEntry(&entry) >= B_OK) {
76 if (entry.GetRef(ref) < B_OK)
77 continue;
78
79 WatchedEntry *child = new(std::nothrow) WatchedEntry(fRoster,
80 fMessenger, ref);
81 if (child == NULL)
82 continue;
83
84 child->fLink = fEntries;
85 fEntries = child;
86 }
87
88 watch_node(&fNode, B_WATCH_DIRECTORY, *fMessenger);
89 } else {
90 // filter pseudoentry that only handles ioctls
91 if (strncmp(ref->name, "raw", 3) == 0)
92 return;
93
94 BPath path;
95 entry.GetPath(&path);
96 fDevice = new(std::nothrow) BUSBDevice(path.Path());
97 if (fDevice != NULL) {
98 if (fRoster->DeviceAdded(fDevice) != B_OK) {
99 delete fDevice;
100 fDevice = NULL;
101 }
102 }
103 }
104 }
105
106
~WatchedEntry()107 WatchedEntry::~WatchedEntry()
108 {
109 if (fIsDirectory) {
110 watch_node(&fNode, B_STOP_WATCHING, *fMessenger);
111
112 WatchedEntry *child = fEntries;
113 while (child) {
114 WatchedEntry *next = child->fLink;
115 delete child;
116 child = next;
117 }
118 }
119
120 if (fDevice) {
121 fRoster->DeviceRemoved(fDevice);
122 delete fDevice;
123 }
124 }
125
126
127 bool
EntryCreated(entry_ref * ref)128 WatchedEntry::EntryCreated(entry_ref *ref)
129 {
130 if (!fIsDirectory)
131 return false;
132
133 if (ref->directory != fNode.node) {
134 WatchedEntry *child = fEntries;
135 while (child) {
136 if (child->EntryCreated(ref))
137 return true;
138 child = child->fLink;
139 }
140
141 return false;
142 }
143
144 WatchedEntry *child = new(std::nothrow) WatchedEntry(fRoster, fMessenger,
145 ref);
146 if (child == NULL)
147 return false;
148
149 child->fLink = fEntries;
150 fEntries = child;
151 return true;
152 }
153
154
155 bool
EntryRemoved(ino_t node)156 WatchedEntry::EntryRemoved(ino_t node)
157 {
158 if (!fIsDirectory)
159 return false;
160
161 WatchedEntry *child = fEntries;
162 WatchedEntry *lastChild = NULL;
163 while (child) {
164 if (child->fNode.node == node) {
165 if (lastChild)
166 lastChild->fLink = child->fLink;
167 else
168 fEntries = child->fLink;
169
170 delete child;
171 return true;
172 }
173
174 if (child->EntryRemoved(node))
175 return true;
176
177 lastChild = child;
178 child = child->fLink;
179 }
180
181 return false;
182 }
183
184
RosterLooper(BUSBRoster * roster)185 RosterLooper::RosterLooper(BUSBRoster *roster)
186 : BLooper("BUSBRoster looper"),
187 fRoster(roster),
188 fRoot(NULL),
189 fMessenger(NULL)
190 {
191 BEntry entry("/dev/bus/usb");
192 if (!entry.Exists()) {
193 fprintf(stderr, "USBKit: usb_raw not published\n");
194 return;
195 }
196
197 Run();
198 fMessenger = new(std::nothrow) BMessenger(this);
199 if (fMessenger == NULL)
200 return;
201
202 if (Lock()) {
203 entry_ref ref;
204 entry.GetRef(&ref);
205 fRoot = new(std::nothrow) WatchedEntry(fRoster, fMessenger, &ref);
206 Unlock();
207 }
208 }
209
210
211 void
Stop()212 RosterLooper::Stop()
213 {
214 Lock();
215 delete fRoot;
216 Quit();
217 }
218
219
220 void
MessageReceived(BMessage * message)221 RosterLooper::MessageReceived(BMessage *message)
222 {
223 int32 opcode;
224 if (message->FindInt32("opcode", &opcode) < B_OK)
225 return;
226
227 switch (opcode) {
228 case B_ENTRY_CREATED: {
229 dev_t device;
230 ino_t directory;
231 const char *name;
232 if (message->FindInt32("device", &device) < B_OK
233 || message->FindInt64("directory", &directory) < B_OK
234 || message->FindString("name", &name) < B_OK)
235 break;
236
237 entry_ref ref(device, directory, name);
238 fRoot->EntryCreated(&ref);
239 break;
240 }
241
242 case B_ENTRY_REMOVED: {
243 ino_t node;
244 if (message->FindInt64("node", &node) < B_OK)
245 break;
246
247 fRoot->EntryRemoved(node);
248 break;
249 }
250 }
251 }
252
253
BUSBRoster()254 BUSBRoster::BUSBRoster()
255 : fLooper(NULL)
256 {
257 }
258
259
~BUSBRoster()260 BUSBRoster::~BUSBRoster()
261 {
262 Stop();
263 }
264
265
266 void
Start()267 BUSBRoster::Start()
268 {
269 if (fLooper)
270 return;
271
272 fLooper = new(std::nothrow) RosterLooper(this);
273 }
274
275
276 void
Stop()277 BUSBRoster::Stop()
278 {
279 if (!fLooper)
280 return;
281
282 ((RosterLooper *)fLooper)->Stop();
283 fLooper = NULL;
284 }
285
286
287 // definition of reserved virtual functions
_ReservedUSBRoster1()288 void BUSBRoster::_ReservedUSBRoster1() {};
_ReservedUSBRoster2()289 void BUSBRoster::_ReservedUSBRoster2() {};
_ReservedUSBRoster3()290 void BUSBRoster::_ReservedUSBRoster3() {};
_ReservedUSBRoster4()291 void BUSBRoster::_ReservedUSBRoster4() {};
_ReservedUSBRoster5()292 void BUSBRoster::_ReservedUSBRoster5() {};
293