xref: /aosp_15_r20/external/libusb/libusb/os/haiku_pollfs.cpp (revision 86b64dcb59b3a0b37502ecd56e119234366a6f7e)
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 <[email protected]>
7  */
8 
9 #include "haiku_usb.h"
10 #include <cstdio>
11 #include <Directory.h>
12 #include <Entry.h>
13 #include <Looper.h>
14 #include <Messenger.h>
15 #include <Node.h>
16 #include <NodeMonitor.h>
17 #include <Path.h>
18 #include <cstring>
19 
20 class WatchedEntry {
21 public:
22 			WatchedEntry(BMessenger *, entry_ref *);
23 			~WatchedEntry();
24 	bool		EntryCreated(entry_ref *ref);
25 	bool		EntryRemoved(ino_t node);
26 	bool		InitCheck();
27 
28 private:
29 	BMessenger*	fMessenger;
30 	node_ref	fNode;
31 	bool		fIsDirectory;
32 	USBDevice*	fDevice;
33 	WatchedEntry*	fEntries;
34 	WatchedEntry*	fLink;
35 	bool		fInitCheck;
36 };
37 
38 
39 class RosterLooper : public BLooper {
40 public:
41 			RosterLooper(USBRoster *);
42 	void		Stop();
43 	virtual void	MessageReceived(BMessage *);
44 	bool		InitCheck();
45 
46 private:
47 	USBRoster*	fRoster;
48 	WatchedEntry*	fRoot;
49 	BMessenger*	fMessenger;
50 	bool		fInitCheck;
51 };
52 
53 
WatchedEntry(BMessenger * messenger,entry_ref * ref)54 WatchedEntry::WatchedEntry(BMessenger *messenger, entry_ref *ref)
55 	:	fMessenger(messenger),
56 		fIsDirectory(false),
57 		fDevice(NULL),
58 		fEntries(NULL),
59 		fLink(NULL),
60 		fInitCheck(false)
61 {
62 	BEntry entry(ref);
63 	entry.GetNodeRef(&fNode);
64 
65 	BDirectory directory;
66 	if (entry.IsDirectory() && directory.SetTo(ref) >= B_OK) {
67 		fIsDirectory = true;
68 
69 		while (directory.GetNextEntry(&entry) >= B_OK) {
70 			if (entry.GetRef(ref) < B_OK)
71 				continue;
72 
73 			WatchedEntry *child = new(std::nothrow) WatchedEntry(fMessenger, ref);
74 			if (child == NULL)
75 				continue;
76 			if (child->InitCheck() == false) {
77 				delete child;
78 				continue;
79 			}
80 
81 			child->fLink = fEntries;
82 			fEntries = child;
83 		}
84 
85 		watch_node(&fNode, B_WATCH_DIRECTORY, *fMessenger);
86 	}
87 	else {
88 		if (strncmp(ref->name, "raw", 3) == 0)
89 			return;
90 
91 		BPath path, parent_path;
92 		entry.GetPath(&path);
93 		fDevice = new(std::nothrow) USBDevice(path.Path());
94 		if (fDevice != NULL && fDevice->InitCheck() == true) {
95 			// Add this new device to each active context's device list
96 			struct libusb_context *ctx;
97 			unsigned long session_id = (unsigned long)&fDevice;
98 
99 			usbi_mutex_lock(&active_contexts_lock);
100 			for_each_context(ctx) {
101 				struct libusb_device *dev = usbi_get_device_by_session_id(ctx, session_id);
102 				if (dev) {
103 					usbi_dbg(NULL, "using previously allocated device with location %lu", session_id);
104 					libusb_unref_device(dev);
105 					continue;
106 				}
107 				usbi_dbg(NULL, "allocating new device with location %lu", session_id);
108 				dev = usbi_alloc_device(ctx, session_id);
109 				if (!dev) {
110 					usbi_dbg(NULL, "device allocation failed");
111 					continue;
112 				}
113 				*((USBDevice **)usbi_get_device_priv(dev)) = fDevice;
114 
115 				// Calculate pseudo-device-address
116 				int addr, tmp;
117 				if (strcmp(path.Leaf(), "hub") == 0)
118 					tmp = 100;	//Random Number
119 				else
120 					sscanf(path.Leaf(), "%d", &tmp);
121 				addr = tmp + 1;
122 				path.GetParent(&parent_path);
123 				while (strcmp(parent_path.Leaf(), "usb") != 0) {
124 					sscanf(parent_path.Leaf(), "%d", &tmp);
125 					addr += tmp + 1;
126 					parent_path.GetParent(&parent_path);
127 				}
128 				sscanf(path.Path(), "/dev/bus/usb/%hhu", &dev->bus_number);
129 				dev->device_address = addr - (dev->bus_number + 1);
130 
131 				static_assert(sizeof(dev->device_descriptor) == sizeof(usb_device_descriptor),
132 					      "mismatch between libusb and OS device descriptor sizes");
133 				memcpy(&dev->device_descriptor, fDevice->Descriptor(), LIBUSB_DT_DEVICE_SIZE);
134 				usbi_localize_device_descriptor(&dev->device_descriptor);
135 
136 				if (usbi_sanitize_device(dev) < 0) {
137 					usbi_dbg(NULL, "device sanitization failed");
138 					libusb_unref_device(dev);
139 					continue;
140 				}
141 				usbi_connect_device(dev);
142 			}
143 			usbi_mutex_unlock(&active_contexts_lock);
144 		}
145 		else if (fDevice) {
146 			delete fDevice;
147 			fDevice = NULL;
148 			return;
149 		}
150 	}
151 	fInitCheck = true;
152 }
153 
154 
~WatchedEntry()155 WatchedEntry::~WatchedEntry()
156 {
157 	if (fIsDirectory) {
158 		watch_node(&fNode, B_STOP_WATCHING, *fMessenger);
159 
160 		WatchedEntry *child = fEntries;
161 		while (child) {
162 			WatchedEntry *next = child->fLink;
163 			delete child;
164 			child = next;
165 		}
166 	}
167 
168 	if (fDevice) {
169 		// Remove this device from each active context's device list
170 		struct libusb_context *ctx;
171 		struct libusb_device *dev;
172 		unsigned long session_id = (unsigned long)&fDevice;
173 
174 		usbi_mutex_lock(&active_contexts_lock);
175 		for_each_context(ctx) {
176 			dev = usbi_get_device_by_session_id(ctx, session_id);
177 			if (dev != NULL) {
178 				usbi_disconnect_device(dev);
179 				libusb_unref_device(dev);
180 			} else {
181 				usbi_dbg(ctx, "device with location %lu not found", session_id);
182 			}
183 		}
184 		usbi_mutex_static_unlock(&active_contexts_lock);
185 		delete fDevice;
186 	}
187 }
188 
189 
190 bool
EntryCreated(entry_ref * ref)191 WatchedEntry::EntryCreated(entry_ref *ref)
192 {
193 	if (!fIsDirectory)
194 		return false;
195 
196 	if (ref->directory != fNode.node) {
197 		WatchedEntry *child = fEntries;
198 		while (child) {
199 			if (child->EntryCreated(ref))
200 				return true;
201 			child = child->fLink;
202 		}
203 		return false;
204 	}
205 
206 	WatchedEntry *child = new(std::nothrow) WatchedEntry(fMessenger, ref);
207 	if (child == NULL)
208 		return false;
209 	child->fLink = fEntries;
210 	fEntries = child;
211 	return true;
212 }
213 
214 
215 bool
EntryRemoved(ino_t node)216 WatchedEntry::EntryRemoved(ino_t node)
217 {
218 	if (!fIsDirectory)
219 		return false;
220 
221 	WatchedEntry *child = fEntries;
222 	WatchedEntry *lastChild = NULL;
223 	while (child) {
224 		if (child->fNode.node == node) {
225 			if (lastChild)
226 				lastChild->fLink = child->fLink;
227 			else
228 				fEntries = child->fLink;
229 			delete child;
230 			return true;
231 		}
232 
233 		if (child->EntryRemoved(node))
234 			return true;
235 
236 		lastChild = child;
237 		child = child->fLink;
238 	}
239 	return false;
240 }
241 
242 
243 bool
InitCheck()244 WatchedEntry::InitCheck()
245 {
246 	return fInitCheck;
247 }
248 
249 
RosterLooper(USBRoster * roster)250 RosterLooper::RosterLooper(USBRoster *roster)
251 	:	BLooper("LibusbRoster Looper"),
252 		fRoster(roster),
253 		fRoot(NULL),
254 		fMessenger(NULL),
255 		fInitCheck(false)
256 {
257 	BEntry entry("/dev/bus/usb");
258 	if (!entry.Exists()) {
259 		usbi_err(NULL, "usb_raw not published");
260 		return;
261 	}
262 
263 	Run();
264 	fMessenger = new(std::nothrow) BMessenger(this);
265 	if (fMessenger == NULL) {
266 		usbi_err(NULL, "error creating BMessenger object");
267 		return;
268 	}
269 
270 	if (Lock()) {
271 		entry_ref ref;
272 		entry.GetRef(&ref);
273 		fRoot = new(std::nothrow) WatchedEntry(fMessenger, &ref);
274 		Unlock();
275 		if (fRoot == NULL)
276 			return;
277 		if (fRoot->InitCheck() == false) {
278 			delete fRoot;
279 			fRoot = NULL;
280 			return;
281 		}
282 	}
283 	fInitCheck = true;
284 }
285 
286 
287 void
Stop()288 RosterLooper::Stop()
289 {
290 	Lock();
291 	delete fRoot;
292 	delete fMessenger;
293 	Quit();
294 }
295 
296 
297 void
MessageReceived(BMessage * message)298 RosterLooper::MessageReceived(BMessage *message)
299 {
300 	int32 opcode;
301 	if (message->FindInt32("opcode", &opcode) < B_OK)
302 		return;
303 
304 	switch (opcode) {
305 		case B_ENTRY_CREATED:
306 		{
307 			dev_t device;
308 			ino_t directory;
309 			const char *name;
310 			if (message->FindInt32("device", &device) < B_OK ||
311 				message->FindInt64("directory", &directory) < B_OK ||
312 				message->FindString("name", &name) < B_OK)
313 				break;
314 
315 			entry_ref ref(device, directory, name);
316 			fRoot->EntryCreated(&ref);
317 			break;
318 		}
319 		case B_ENTRY_REMOVED:
320 		{
321 			ino_t node;
322 			if (message->FindInt64("node", &node) < B_OK)
323 				break;
324 			fRoot->EntryRemoved(node);
325 			break;
326 		}
327 	}
328 }
329 
330 
331 bool
InitCheck()332 RosterLooper::InitCheck()
333 {
334 	return fInitCheck;
335 }
336 
337 
USBRoster()338 USBRoster::USBRoster()
339 	:	fLooper(NULL)
340 {
341 }
342 
343 
~USBRoster()344 USBRoster::~USBRoster()
345 {
346 	Stop();
347 }
348 
349 
350 int
Start()351 USBRoster::Start()
352 {
353 	if (fLooper == NULL) {
354 		fLooper = new(std::nothrow) RosterLooper(this);
355 		if (fLooper == NULL || ((RosterLooper *)fLooper)->InitCheck() == false) {
356 			if (fLooper)
357 				fLooper = NULL;
358 			return LIBUSB_ERROR_OTHER;
359 		}
360 	}
361 	return LIBUSB_SUCCESS;
362 }
363 
364 
365 void
Stop()366 USBRoster::Stop()
367 {
368 	if (fLooper) {
369 		((RosterLooper *)fLooper)->Stop();
370 		fLooper = NULL;
371 	}
372 }
373