1 /* 2 * Copyright 2004-2009, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Matthijs Hollemans 7 * Jerome Leveque 8 * Philippe Houdoin 9 * Pete Goodeve 10 */ 11 12 #include "debug.h" 13 #include "DeviceWatcher.h" 14 #include "PortDrivers.h" 15 16 #include <errno.h> 17 #include <fcntl.h> 18 #include <stdio.h> 19 #include <new> 20 21 #include <Application.h> 22 #include <Bitmap.h> 23 #include <Directory.h> 24 #include <Entry.h> 25 #include <File.h> 26 #include <IconUtils.h> 27 #include <Path.h> 28 #include <PathMonitor.h> 29 #include <Resources.h> 30 #include <Roster.h> 31 32 using std::nothrow; 33 34 using namespace BPrivate; 35 using BPrivate::HashMap; 36 using BPrivate::HashString; 37 38 39 const char *kDevicesRoot = "/dev/midi"; 40 41 42 class DeviceEndpoints { 43 public: 44 DeviceEndpoints(int fd, MidiPortConsumer* consumer, MidiPortProducer* producer) 45 : fFD(fd), fConsumer(consumer), fProducer(producer) 46 { 47 } 48 49 int fFD; 50 MidiPortConsumer* fConsumer; 51 MidiPortProducer* fProducer; 52 }; 53 54 55 56 DeviceWatcher::DeviceWatcher() 57 : BLooper("MIDI devices watcher"), 58 fDeviceEndpointsMap(), fVectorIconData(NULL), fVectorIconDataSize(0), 59 fLargeIcon(NULL), fMiniIcon(NULL) 60 { 61 // Load midi endpoint vector icon data 62 app_info info; 63 be_app->GetAppInfo(&info); 64 BFile file(&info.ref, B_READ_ONLY); 65 66 BResources resources; 67 if (resources.SetTo(&file) == B_OK) { 68 size_t dataSize; 69 // Load MIDI port endpoint vector icon 70 const uint8* data = (const uint8*)resources.LoadResource( 71 B_VECTOR_ICON_TYPE, "endpoint_vector_icon", &dataSize); 72 73 if (data != NULL && dataSize > 0) 74 fVectorIconData = new(std::nothrow) uint8[dataSize]; 75 76 if (fVectorIconData) { 77 // data is own by resources local object: copy its content for 78 // later use 79 memcpy(fVectorIconData, data, dataSize); 80 fVectorIconDataSize = dataSize; 81 } 82 } 83 84 // Render 32x32 and 16x16 B_CMAP8 icons for R5 compatibility 85 if (fVectorIconData != NULL) { 86 fLargeIcon = new(std::nothrow) BBitmap(BRect(0, 0, 31, 31), B_CMAP8); 87 fMiniIcon = new(std::nothrow) BBitmap(BRect(0, 0, 15, 15), B_CMAP8); 88 89 if (BIconUtils::GetVectorIcon(fVectorIconData, fVectorIconDataSize, 90 fLargeIcon) != B_OK) { 91 delete fLargeIcon; 92 fLargeIcon = NULL; 93 } 94 if (BIconUtils::GetVectorIcon(fVectorIconData, fVectorIconDataSize, 95 fMiniIcon) != B_OK) { 96 delete fMiniIcon; 97 fMiniIcon = NULL; 98 } 99 } 100 101 Start(); 102 } 103 104 105 DeviceWatcher::~DeviceWatcher() 106 { 107 Stop(); 108 109 delete fLargeIcon; 110 delete fMiniIcon; 111 delete[] fVectorIconData; 112 } 113 114 115 status_t 116 DeviceWatcher::Start() 117 { 118 // Do an initial scan 119 120 // We need to do this from a separate thread, otherwise we will deadlock. 121 // The reason is that we instantiate a BMidiRoster object, which sends a 122 // message to the midi_server to register itself, and blocks until it gets 123 // a response. But since we _are_ the midi_server we will never be able to 124 // send that response if our main thread is already blocking. 125 126 resume_thread(spawn_thread(_InitialDevicesScanThread, 127 "Initial devices scan", B_NORMAL_PRIORITY, this)); 128 129 // And watch for any change 130 return BPathMonitor::StartWatching(kDevicesRoot, B_ENTRY_CREATED 131 | B_ENTRY_REMOVED | B_ENTRY_MOVED | B_WATCH_FILES_ONLY 132 | B_WATCH_RECURSIVELY, this); 133 } 134 135 136 status_t 137 DeviceWatcher::Stop() 138 { 139 return BPathMonitor::StopWatching(kDevicesRoot, this); 140 } 141 142 143 void 144 DeviceWatcher::MessageReceived(BMessage* message) 145 { 146 if (message->what != B_PATH_MONITOR) 147 return; 148 149 int32 opcode; 150 if (message->FindInt32("opcode", &opcode) != B_OK) 151 return; 152 153 // message->PrintToStream(); 154 155 const char* path; 156 if (message->FindString("path", &path) != B_OK) 157 return; 158 159 switch (opcode) { 160 case B_ENTRY_CREATED: { 161 _AddDevice(path); 162 break; 163 } 164 case B_ENTRY_REMOVED: { 165 _RemoveDevice(path); 166 break; 167 } 168 } 169 } 170 171 172 // #pragma mark - 173 174 175 /* static */ 176 int32 177 DeviceWatcher::_InitialDevicesScanThread(void* data) 178 { 179 ((DeviceWatcher*)data)->_ScanDevices(kDevicesRoot); 180 return 0; 181 } 182 183 184 void 185 DeviceWatcher::_ScanDevices(const char* path) 186 { 187 TRACE(("DeviceWatcher::_ScanDevices(\"%s\");\n", path)); 188 189 BDirectory dir(path); 190 if (dir.InitCheck() != B_OK) 191 return; 192 193 BEntry entry; 194 while (dir.GetNextEntry(&entry) == B_OK) { 195 BPath name; 196 entry.GetPath(&name); 197 if (entry.IsDirectory()) 198 _ScanDevices(name.Path()); 199 else 200 _AddDevice(name.Path()); 201 } 202 } 203 204 205 void 206 DeviceWatcher::_AddDevice(const char* path) 207 { 208 TRACE(("DeviceWatcher::_AddDevice(\"%s\");\n", path)); 209 210 if (fDeviceEndpointsMap.ContainsKey(path)) { 211 // Already known 212 TRACE(("already known...!\n")); 213 return; 214 } 215 216 BEntry entry(path); 217 if (entry.IsDirectory()) 218 // Invalid path! 219 return; 220 221 if (entry.IsSymLink()) { 222 BEntry symlink(path, true); 223 if (symlink.IsDirectory()) { 224 // Invalid path! 225 return; 226 } 227 } 228 229 int fd = open(path, O_RDWR | O_EXCL); 230 if (fd < 0) { 231 if (errno != EACCES) 232 return; 233 234 // maybe it's a input or output only port? 235 fd = open(path, O_RDONLY | O_EXCL); 236 if (fd < 0 && errno == EACCES) 237 fd = open(path, O_WRONLY | O_EXCL); 238 if (fd < 0) 239 return; 240 } 241 242 TRACE(("Doing _AddDevice(\"%s\"); fd=%d\n", path, fd)); 243 244 MidiPortConsumer* consumer = NULL; 245 MidiPortProducer* producer = NULL; 246 247 int flags = fcntl(fd, F_GETFL); 248 249 if ((flags & O_ACCMODE) != O_RDONLY) { 250 consumer = new MidiPortConsumer(fd, path); 251 _SetIcons(consumer); 252 TRACE(("Register %s MidiPortConsumer\n", consumer->Name())); 253 consumer->Register(); 254 } 255 256 if ((flags & O_ACCMODE) != O_WRONLY) { 257 producer = new MidiPortProducer(fd, path); 258 _SetIcons(producer); 259 TRACE(("Register %s MidiPortProducer\n", producer->Name())); 260 producer->Register(); 261 } 262 263 fDeviceEndpointsMap.Put(path, new DeviceEndpoints(fd, consumer, producer)); 264 TRACE(("Done _AddDevice(\"%s\")\n", path)); 265 } 266 267 268 void 269 DeviceWatcher::_RemoveDevice(const char* path) 270 { 271 TRACE(("DeviceWatcher::_RemoveDevice(\"%s\");\n", path)); 272 273 DeviceEndpoints* deviceEndpoints = fDeviceEndpointsMap.Get(path); 274 if (!deviceEndpoints) { 275 TRACE(("_RemoveDevice(\"%s\") didn't find endpoint in map!!\n", path)); 276 return; 277 } 278 279 TRACE((" _RemoveDevice(\"%s\") unregistering\n", path)); 280 if (deviceEndpoints->fConsumer) 281 deviceEndpoints->fConsumer->Unregister(); 282 if (deviceEndpoints->fProducer) 283 deviceEndpoints->fProducer->Unregister(); 284 285 TRACE((" _RemoveDevice(\"%s\") releasing\n", path)); 286 if (deviceEndpoints->fConsumer) 287 deviceEndpoints->fConsumer->Release(); 288 if (deviceEndpoints->fProducer) 289 deviceEndpoints->fProducer->Release(); 290 291 TRACE((" _RemoveDevice(\"%s\") removing from map\n", path)); 292 fDeviceEndpointsMap.Remove(path); 293 TRACE(("Done _RemoveDevice(\"%s\")\n", path)); 294 } 295 296 297 void 298 DeviceWatcher::_SetIcons(BMidiEndpoint* endpoint) 299 { 300 BMessage msg; 301 302 if (fVectorIconData && fVectorIconDataSize > 0) { 303 msg.AddData("icon", B_VECTOR_ICON_TYPE, fVectorIconData, 304 fVectorIconDataSize); 305 } 306 307 if (fLargeIcon) { 308 msg.AddData("be:large_icon", B_LARGE_ICON_TYPE, fLargeIcon->Bits(), 309 fLargeIcon->BitsLength()); 310 } 311 312 if (fMiniIcon) { 313 msg.AddData("be:mini_icon", B_MINI_ICON_TYPE, fMiniIcon->Bits(), 314 fMiniIcon->BitsLength()); 315 } 316 317 endpoint->SetProperties(&msg); 318 } 319