Home | History | Annotate | Download | only in os
      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 (at) mlotz.ch>
      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 
     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 			list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) {
    101 				struct libusb_device *dev = usbi_get_device_by_session_id(ctx, session_id);
    102 				if (dev) {
    103 					usbi_dbg("using previously allocated device with location %lu", session_id);
    104 					libusb_unref_device(dev);
    105 					continue;
    106 				}
    107 				usbi_dbg("allocating new device with location %lu", session_id);
    108 				dev = usbi_alloc_device(ctx, session_id);
    109 				if (!dev) {
    110 					usbi_dbg("device allocation failed");
    111 					continue;
    112 				}
    113 				*((USBDevice **)dev->os_priv) = 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/%d", &dev->bus_number);
    129 				dev->device_address = addr - (dev->bus_number + 1);
    130 
    131 				if (usbi_sanitize_device(dev) < 0) {
    132 					usbi_dbg("device sanitization failed");
    133 					libusb_unref_device(dev);
    134 					continue;
    135 				}
    136 				usbi_connect_device(dev);
    137 			}
    138 			usbi_mutex_unlock(&active_contexts_lock);
    139 		}
    140 		else if (fDevice) {
    141 			delete fDevice;
    142 			fDevice = NULL;
    143 			return;
    144 		}
    145 	}
    146 	fInitCheck = true;
    147 }
    148 
    149 
    150 WatchedEntry::~WatchedEntry()
    151 {
    152 	if (fIsDirectory) {
    153 		watch_node(&fNode, B_STOP_WATCHING, *fMessenger);
    154 
    155 		WatchedEntry *child = fEntries;
    156 		while (child) {
    157 			WatchedEntry *next = child->fLink;
    158 			delete child;
    159 			child = next;
    160 		}
    161 	}
    162 
    163 	if (fDevice) {
    164 		// Remove this device from each active context's device list
    165 		struct libusb_context *ctx;
    166 		struct libusb_device *dev;
    167 		unsigned long session_id = (unsigned long)&fDevice;
    168 
    169 		usbi_mutex_lock(&active_contexts_lock);
    170 		list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) {
    171 			dev = usbi_get_device_by_session_id(ctx, session_id);
    172 			if (dev != NULL) {
    173 				usbi_disconnect_device(dev);
    174 				libusb_unref_device(dev);
    175 			} else {
    176 				usbi_dbg("device with location %lu not found", session_id);
    177 			}
    178 		}
    179 		usbi_mutex_static_unlock(&active_contexts_lock);
    180 		delete fDevice;
    181 	}
    182 }
    183 
    184 
    185 bool
    186 WatchedEntry::EntryCreated(entry_ref *ref)
    187 {
    188 	if (!fIsDirectory)
    189 		return false;
    190 
    191 	if (ref->directory != fNode.node) {
    192 		WatchedEntry *child = fEntries;
    193 		while (child) {
    194 			if (child->EntryCreated(ref))
    195 				return true;
    196 			child = child->fLink;
    197 		}
    198 		return false;
    199 	}
    200 
    201 	WatchedEntry *child = new(std::nothrow) WatchedEntry(fMessenger, ref);
    202 	if (child == NULL)
    203 		return false;
    204 	child->fLink = fEntries;
    205 	fEntries = child;
    206 	return true;
    207 }
    208 
    209 
    210 bool
    211 WatchedEntry::EntryRemoved(ino_t node)
    212 {
    213 	if (!fIsDirectory)
    214 		return false;
    215 
    216 	WatchedEntry *child = fEntries;
    217 	WatchedEntry *lastChild = NULL;
    218 	while (child) {
    219 		if (child->fNode.node == node) {
    220 			if (lastChild)
    221 				lastChild->fLink = child->fLink;
    222 			else
    223 				fEntries = child->fLink;
    224 			delete child;
    225 			return true;
    226 		}
    227 
    228 		if (child->EntryRemoved(node))
    229 			return true;
    230 
    231 		lastChild = child;
    232 		child = child->fLink;
    233 	}
    234 	return false;
    235 }
    236 
    237 
    238 bool
    239 WatchedEntry::InitCheck()
    240 {
    241 	return fInitCheck;
    242 }
    243 
    244 
    245 RosterLooper::RosterLooper(USBRoster *roster)
    246 	:	BLooper("LibusbRoster Looper"),
    247 		fRoster(roster),
    248 		fRoot(NULL),
    249 		fMessenger(NULL),
    250 		fInitCheck(false)
    251 {
    252 	BEntry entry("/dev/bus/usb");
    253 	if (!entry.Exists()) {
    254 		usbi_err(NULL, "usb_raw not published");
    255 		return;
    256 	}
    257 
    258 	Run();
    259 	fMessenger = new(std::nothrow) BMessenger(this);
    260 	if (fMessenger == NULL) {
    261 		usbi_err(NULL, "error creating BMessenger object");
    262 		return;
    263 	}
    264 
    265 	if (Lock()) {
    266 		entry_ref ref;
    267 		entry.GetRef(&ref);
    268 		fRoot = new(std::nothrow) WatchedEntry(fMessenger, &ref);
    269 		Unlock();
    270 		if (fRoot == NULL)
    271 			return;
    272 		if (fRoot->InitCheck() == false) {
    273 			delete fRoot;
    274 			fRoot = NULL;
    275 			return;
    276 		}
    277 	}
    278 	fInitCheck = true;
    279 }
    280 
    281 
    282 void
    283 RosterLooper::Stop()
    284 {
    285 	Lock();
    286 	delete fRoot;
    287 	delete fMessenger;
    288 	Quit();
    289 }
    290 
    291 
    292 void
    293 RosterLooper::MessageReceived(BMessage *message)
    294 {
    295 	int32 opcode;
    296 	if (message->FindInt32("opcode", &opcode) < B_OK)
    297 		return;
    298 
    299 	switch (opcode) {
    300 		case B_ENTRY_CREATED:
    301 		{
    302 			dev_t device;
    303 			ino_t directory;
    304 			const char *name;
    305 			if (message->FindInt32("device", &device) < B_OK ||
    306 				message->FindInt64("directory", &directory) < B_OK ||
    307 				message->FindString("name", &name) < B_OK)
    308 				break;
    309 
    310 			entry_ref ref(device, directory, name);
    311 			fRoot->EntryCreated(&ref);
    312 			break;
    313 		}
    314 		case B_ENTRY_REMOVED:
    315 		{
    316 			ino_t node;
    317 			if (message->FindInt64("node", &node) < B_OK)
    318 				break;
    319 			fRoot->EntryRemoved(node);
    320 			break;
    321 		}
    322 	}
    323 }
    324 
    325 
    326 bool
    327 RosterLooper::InitCheck()
    328 {
    329 	return fInitCheck;
    330 }
    331 
    332 
    333 USBRoster::USBRoster()
    334 	:	fLooper(NULL)
    335 {
    336 }
    337 
    338 
    339 USBRoster::~USBRoster()
    340 {
    341 	Stop();
    342 }
    343 
    344 
    345 int
    346 USBRoster::Start()
    347 {
    348 	if (fLooper == NULL) {
    349 		fLooper = new(std::nothrow) RosterLooper(this);
    350 		if (fLooper == NULL || ((RosterLooper *)fLooper)->InitCheck() == false) {
    351 			if (fLooper)
    352 				fLooper = NULL;
    353 			return LIBUSB_ERROR_OTHER;
    354 		}
    355 	}
    356 	return LIBUSB_SUCCESS;
    357 }
    358 
    359 
    360 void
    361 USBRoster::Stop()
    362 {
    363 	if (fLooper) {
    364 		((RosterLooper *)fLooper)->Stop();
    365 		fLooper = NULL;
    366 	}
    367 }
    368