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, 131 B_WATCH_FILES_ONLY | B_WATCH_RECURSIVELY, this); 132 } 133 134 135 status_t 136 DeviceWatcher::Stop() 137 { 138 return BPathMonitor::StopWatching(kDevicesRoot, this); 139 } 140 141 142 void 143 DeviceWatcher::MessageReceived(BMessage* message) 144 { 145 if (message->what != B_PATH_MONITOR) 146 return; 147 148 int32 opcode; 149 if (message->FindInt32("opcode", &opcode) != B_OK) 150 return; 151 152 // message->PrintToStream(); 153 154 const char* path; 155 if (message->FindString("path", &path) != B_OK) 156 return; 157 158 switch (opcode) { 159 case B_ENTRY_CREATED: { 160 _AddDevice(path); 161 break; 162 } 163 case B_ENTRY_REMOVED: { 164 _RemoveDevice(path); 165 break; 166 } 167 } 168 } 169 170 171 // #pragma mark - 172 173 174 /* static */ 175 int32 176 DeviceWatcher::_InitialDevicesScanThread(void* data) 177 { 178 ((DeviceWatcher*)data)->_ScanDevices(kDevicesRoot); 179 return 0; 180 } 181 182 183 void 184 DeviceWatcher::_ScanDevices(const char* path) 185 { 186 TRACE(("DeviceWatcher::_ScanDevices(\"%s\");\n", path)); 187 188 BDirectory dir(path); 189 if (dir.InitCheck() != B_OK) 190 return; 191 192 BEntry entry; 193 while (dir.GetNextEntry(&entry) == B_OK) { 194 BPath name; 195 entry.GetPath(&name); 196 if (entry.IsDirectory()) 197 _ScanDevices(name.Path()); 198 else 199 _AddDevice(name.Path()); 200 } 201 } 202 203 204 void 205 DeviceWatcher::_AddDevice(const char* path) 206 { 207 TRACE(("DeviceWatcher::_AddDevice(\"%s\");\n", path)); 208 209 if (fDeviceEndpointsMap.ContainsKey(path)) { 210 // Already known 211 TRACE(("already known...!\n")); 212 return; 213 } 214 215 BEntry entry(path); 216 if (entry.IsDirectory()) 217 // Invalid path! 218 return; 219 220 if (entry.IsSymLink()) { 221 BEntry symlink(path, true); 222 if (symlink.IsDirectory()) { 223 // Invalid path! 224 return; 225 } 226 } 227 228 int fd = open(path, O_RDWR | O_EXCL); 229 if (fd < 0) { 230 if (errno != EACCES) 231 return; 232 233 // maybe it's a input or output only port? 234 fd = open(path, O_RDONLY | O_EXCL); 235 if (fd < 0 && errno == EACCES) 236 fd = open(path, O_WRONLY | O_EXCL); 237 if (fd < 0) 238 return; 239 } 240 241 TRACE(("Doing _AddDevice(\"%s\"); fd=%d\n", path, fd)); 242 243 MidiPortConsumer* consumer = NULL; 244 MidiPortProducer* producer = NULL; 245 246 int flags = fcntl(fd, F_GETFL); 247 248 if ((flags & O_ACCMODE) != O_RDONLY) { 249 consumer = new MidiPortConsumer(fd, path); 250 _SetIcons(consumer); 251 TRACE(("Register %s MidiPortConsumer\n", consumer->Name())); 252 consumer->Register(); 253 } 254 255 if ((flags & O_ACCMODE) != O_WRONLY) { 256 producer = new MidiPortProducer(fd, path); 257 _SetIcons(producer); 258 TRACE(("Register %s MidiPortProducer\n", producer->Name())); 259 producer->Register(); 260 } 261 262 fDeviceEndpointsMap.Put(path, new DeviceEndpoints(fd, consumer, producer)); 263 TRACE(("Done _AddDevice(\"%s\")\n", path)); 264 } 265 266 267 void 268 DeviceWatcher::_RemoveDevice(const char* path) 269 { 270 TRACE(("DeviceWatcher::_RemoveDevice(\"%s\");\n", path)); 271 272 DeviceEndpoints* deviceEndpoints = fDeviceEndpointsMap.Get(path); 273 if (!deviceEndpoints) { 274 TRACE(("_RemoveDevice(\"%s\") didn't find endpoint in map!!\n", path)); 275 return; 276 } 277 278 TRACE((" _RemoveDevice(\"%s\") unregistering\n", path)); 279 if (deviceEndpoints->fConsumer) 280 deviceEndpoints->fConsumer->Unregister(); 281 if (deviceEndpoints->fProducer) 282 deviceEndpoints->fProducer->Unregister(); 283 284 TRACE((" _RemoveDevice(\"%s\") releasing\n", path)); 285 if (deviceEndpoints->fConsumer) 286 deviceEndpoints->fConsumer->Release(); 287 if (deviceEndpoints->fProducer) 288 deviceEndpoints->fProducer->Release(); 289 290 TRACE((" _RemoveDevice(\"%s\") removing from map\n", path)); 291 fDeviceEndpointsMap.Remove(path); 292 TRACE(("Done _RemoveDevice(\"%s\")\n", path)); 293 } 294 295 296 void 297 DeviceWatcher::_SetIcons(BMidiEndpoint* endpoint) 298 { 299 BMessage msg; 300 301 if (fVectorIconData && fVectorIconDataSize > 0) { 302 msg.AddData("icon", B_VECTOR_ICON_TYPE, fVectorIconData, 303 fVectorIconDataSize); 304 } 305 306 if (fLargeIcon) { 307 msg.AddData("be:large_icon", B_LARGE_ICON_TYPE, fLargeIcon->Bits(), 308 fLargeIcon->BitsLength()); 309 } 310 311 if (fMiniIcon) { 312 msg.AddData("be:mini_icon", B_MINI_ICON_TYPE, fMiniIcon->Bits(), 313 fMiniIcon->BitsLength()); 314 } 315 316 endpoint->SetProperties(&msg); 317 } 318