1 /* 2 * Copyright (c) 2002-2004 Matthijs Hollemans 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 * DEALINGS IN THE SOFTWARE. 21 */ 22 23 #include <Alert.h> 24 25 #include "debug.h" 26 #include "MidiServerApp.h" 27 #include "PortDrivers.h" 28 #include "ServerDefs.h" 29 #include "protocol.h" 30 31 //------------------------------------------------------------------------------ 32 33 MidiServerApp::MidiServerApp() 34 : BApplication(MIDI_SERVER_SIGNATURE) 35 { 36 TRACE(("Running OpenBeOS MIDI server")) 37 38 nextId = 1; 39 devWatcher.Start(); 40 } 41 42 //------------------------------------------------------------------------------ 43 44 MidiServerApp::~MidiServerApp() 45 { 46 for (int32 t = 0; t < CountApps(); ++t) 47 { 48 delete AppAt(t); 49 } 50 51 for (int32 t = 0; t < CountEndpoints(); ++t) 52 { 53 delete EndpointAt(t); 54 } 55 } 56 57 //------------------------------------------------------------------------------ 58 59 void MidiServerApp::AboutRequested() 60 { 61 (new BAlert(0, 62 "OpenBeOS midi_server\n\n" 63 "notes disguised as bytes\n" 64 "propagating to endpoints,\n" 65 "an aural delight", 66 "Okay", 0, 0, B_WIDTH_AS_USUAL, 67 B_INFO_ALERT))->Go(); 68 } 69 70 //------------------------------------------------------------------------------ 71 72 void MidiServerApp::MessageReceived(BMessage* msg) 73 { 74 #ifdef DEBUG 75 printf("IN "); msg->PrintToStream(); 76 #endif 77 78 // About thread safety inside the midi_server: even though 79 // multiple apps may be sending requests to the server at 80 // the same time, the BLooper's thread handles passes them 81 // to our MessageReceived() one after the other, so there 82 // is no need to synchronize anything. 83 84 switch (msg->what) 85 { 86 case MSG_REGISTER_APP: OnRegisterApp(msg); break; 87 case MSG_CREATE_ENDPOINT: OnCreateEndpoint(msg); break; 88 case MSG_DELETE_ENDPOINT: OnDeleteEndpoint(msg); break; 89 case MSG_PURGE_ENDPOINT: OnPurgeEndpoint(msg); break; 90 case MSG_CHANGE_ENDPOINT: OnChangeEndpoint(msg); break; 91 case MSG_CONNECT_ENDPOINTS: OnConnectDisconnect(msg); break; 92 case MSG_DISCONNECT_ENDPOINTS: OnConnectDisconnect(msg); break; 93 94 default: super::MessageReceived(msg); break; 95 } 96 } 97 98 //------------------------------------------------------------------------------ 99 100 void MidiServerApp::OnRegisterApp(BMessage* msg) 101 { 102 TRACE(("MidiServerApp::OnRegisterApp")) 103 104 // We only send the "app registered" message upon success, 105 // so if anything goes wrong here, we do not let the app 106 // know about it, and we consider it unregistered. (Most 107 // likely, the app is dead. If not, it freezes forever 108 // in anticipation of a message that will never arrive.) 109 110 app_t* app = new app_t; 111 112 if (msg->FindMessenger("midi:messenger", &app->messenger) == B_OK) 113 { 114 if (SendAllEndpoints(app)) 115 { 116 if (SendAllConnections(app)) 117 { 118 BMessage reply; 119 reply.what = MSG_APP_REGISTERED; 120 121 if (SendNotification(app, &reply)) 122 { 123 apps.AddItem(app); 124 125 #ifdef DEBUG 126 DumpApps(); 127 #endif 128 129 return; 130 } 131 } 132 } 133 } 134 135 delete app; 136 } 137 138 //------------------------------------------------------------------------------ 139 140 void MidiServerApp::OnCreateEndpoint(BMessage* msg) 141 { 142 TRACE(("MidiServerApp::OnCreateEndpoint")) 143 144 status_t err; 145 146 endpoint_t* endp = new endpoint_t; 147 148 endp->app = WhichApp(msg); 149 if (endp->app == NULL) 150 { 151 err = B_ERROR; 152 } 153 else 154 { 155 err = B_BAD_VALUE; 156 157 if ((msg->FindBool("midi:consumer", &endp->consumer) == B_OK) 158 && (msg->FindBool("midi:registered", &endp->registered) == B_OK) 159 && (msg->FindString("midi:name", &endp->name) == B_OK) 160 && (msg->FindMessage("midi:properties", &endp->properties) == B_OK)) 161 { 162 if (endp->consumer) 163 { 164 if ((msg->FindInt32("midi:port", &endp->port) == B_OK) 165 && (msg->FindInt64("midi:latency", &endp->latency) == B_OK)) 166 { 167 err = B_OK; 168 } 169 } 170 else 171 { 172 err = B_OK; 173 } 174 } 175 } 176 177 BMessage reply; 178 179 if (err == B_OK) 180 { 181 endp->id = nextId++; 182 reply.AddInt32("midi:id", endp->id); 183 } 184 185 reply.AddInt32("midi:result", err); 186 187 if (SendReply(endp->app, msg, &reply) && (err == B_OK)) 188 { 189 AddEndpoint(msg, endp); 190 } 191 else 192 { 193 delete endp; 194 } 195 } 196 197 //------------------------------------------------------------------------------ 198 199 void MidiServerApp::OnDeleteEndpoint(BMessage* msg) 200 { 201 TRACE(("MidiServerApp::OnDeleteEndpoint")) 202 203 // Clients send the "delete endpoint" message from 204 // the BMidiEndpoint destructor, so there is no point 205 // sending a reply, because the endpoint object will 206 // be destroyed no matter what. 207 208 app_t* app = WhichApp(msg); 209 if (app != NULL) 210 { 211 endpoint_t* endp = WhichEndpoint(msg, app); 212 if (endp != NULL) 213 { 214 RemoveEndpoint(app, endp); 215 } 216 } 217 } 218 219 //------------------------------------------------------------------------------ 220 221 void MidiServerApp::OnPurgeEndpoint(BMessage* msg) 222 { 223 TRACE(("MidiServerApp::OnPurgeEndpoint")) 224 225 // This performs the same task as OnDeleteEndpoint(), 226 // except that this message was send by the midi_server 227 // itself, so we don't check that the app that made the 228 // request really is the owner of the endpoint. (But we 229 // _do_ check that the message came from the server.) 230 231 if (!msg->IsSourceRemote()) 232 { 233 int32 id; 234 if (msg->FindInt32("midi:id", &id) == B_OK) 235 { 236 endpoint_t* endp = FindEndpoint(id); 237 if (endp != NULL) 238 { 239 RemoveEndpoint(NULL, endp); 240 } 241 } 242 } 243 } 244 245 //------------------------------------------------------------------------------ 246 247 void MidiServerApp::OnChangeEndpoint(BMessage* msg) 248 { 249 TRACE(("MidiServerApp::OnChangeEndpoint")) 250 251 endpoint_t* endp; 252 status_t err; 253 254 app_t* app = WhichApp(msg); 255 if (app == NULL) 256 { 257 err = B_ERROR; 258 } 259 else 260 { 261 endp = WhichEndpoint(msg, app); 262 if (endp == NULL) 263 { 264 err = B_BAD_VALUE; 265 } 266 else 267 { 268 err = B_OK; 269 } 270 } 271 272 BMessage reply; 273 reply.AddInt32("midi:result", err); 274 275 if (SendReply(app, msg, &reply) && (err == B_OK)) 276 { 277 TRACE(("Endpoint %ld (%p) changed", endp->id, endp)) 278 279 BMessage notify; 280 notify.what = MSG_ENDPOINT_CHANGED; 281 notify.AddInt32("midi:id", endp->id); 282 283 bool registered; 284 if (msg->FindBool("midi:registered", ®istered) == B_OK) 285 { 286 notify.AddBool("midi:registered", registered); 287 endp->registered = registered; 288 } 289 290 BString name; 291 if (msg->FindString("midi:name", &name) == B_OK) 292 { 293 notify.AddString("midi:name", name); 294 endp->name = name; 295 } 296 297 BMessage properties; 298 if (msg->FindMessage("midi:properties", &properties) == B_OK) 299 { 300 notify.AddMessage("midi:properties", &properties); 301 endp->properties = properties; 302 } 303 304 bigtime_t latency; 305 if (msg->FindInt64("midi:latency", &latency) == B_OK) 306 { 307 notify.AddInt64("midi:latency", latency); 308 endp->latency = latency; 309 } 310 311 NotifyAll(¬ify, app); 312 313 #ifdef DEBUG 314 DumpEndpoints(); 315 #endif 316 } 317 } 318 319 //------------------------------------------------------------------------------ 320 321 void MidiServerApp::OnConnectDisconnect(BMessage* msg) 322 { 323 TRACE(("MidiServerApp::OnConnectDisconnect")) 324 325 bool mustConnect = (msg->what == MSG_CONNECT_ENDPOINTS); 326 327 status_t err; 328 endpoint_t* prod; 329 endpoint_t* cons; 330 331 app_t* app = WhichApp(msg); 332 if (app == NULL) 333 { 334 err = B_ERROR; 335 } 336 else 337 { 338 err = B_BAD_VALUE; 339 340 int32 prodId, consId; 341 if ((msg->FindInt32("midi:producer", &prodId) == B_OK) 342 && (msg->FindInt32("midi:consumer", &consId) == B_OK)) 343 { 344 prod = FindEndpoint(prodId); 345 cons = FindEndpoint(consId); 346 347 if ((prod != NULL) && !prod->consumer) 348 { 349 if ((cons != NULL) && cons->consumer) 350 { 351 // It is an error to connect two endpoints that 352 // are already connected, or to disconnect two 353 // endpoints that are not connected at all. 354 355 if (mustConnect == prod->connections.HasItem(cons)) 356 { 357 err = B_ERROR; 358 } 359 else 360 { 361 err = B_OK; 362 } 363 } 364 } 365 } 366 } 367 368 BMessage reply; 369 reply.AddInt32("midi:result", err); 370 371 if (SendReply(app, msg, &reply) && (err == B_OK)) 372 { 373 if (mustConnect) 374 { 375 TRACE(("Connection made: %ld ---> %ld", prod->id, cons->id)) 376 377 prod->connections.AddItem(cons); 378 } 379 else 380 { 381 TRACE(("Connection broken: %ld -X-> %ld", prod->id, cons->id)) 382 383 prod->connections.RemoveItem(cons); 384 } 385 386 BMessage notify; 387 MakeConnectedNotification(¬ify, prod, cons, mustConnect); 388 NotifyAll(¬ify, app); 389 390 #ifdef DEBUG 391 DumpEndpoints(); 392 #endif 393 } 394 } 395 396 //------------------------------------------------------------------------------ 397 398 bool MidiServerApp::SendAllEndpoints(app_t* app) 399 { 400 ASSERT(app != NULL) 401 402 BMessage notify; 403 404 for (int32 t = 0; t < CountEndpoints(); ++t) 405 { 406 endpoint_t* endp = EndpointAt(t); 407 408 MakeCreatedNotification(¬ify, endp); 409 410 if (!SendNotification(app, ¬ify)) { return false; } 411 } 412 413 return true; 414 } 415 416 //------------------------------------------------------------------------------ 417 418 bool MidiServerApp::SendAllConnections(app_t* app) 419 { 420 ASSERT(app != NULL) 421 422 BMessage notify; 423 424 for (int32 t = 0; t < CountEndpoints(); ++t) 425 { 426 endpoint_t* prod = EndpointAt(t); 427 if (!prod->consumer) 428 { 429 for (int32 k = 0; k < CountConnections(prod); ++k) 430 { 431 endpoint_t* cons = ConnectionAt(prod, k); 432 433 MakeConnectedNotification(¬ify, prod, cons, true); 434 435 if (!SendNotification(app, ¬ify)) { return false; } 436 } 437 } 438 } 439 440 return true; 441 } 442 443 //------------------------------------------------------------------------------ 444 445 void MidiServerApp::AddEndpoint(BMessage* msg, endpoint_t* endp) 446 { 447 ASSERT(msg != NULL) 448 ASSERT(endp != NULL) 449 ASSERT(!endpoints.HasItem(endp)) 450 451 TRACE(("Endpoint %ld (%p) added", endp->id, endp)) 452 453 endpoints.AddItem(endp); 454 455 BMessage notify; 456 MakeCreatedNotification(¬ify, endp); 457 NotifyAll(¬ify, endp->app); 458 459 #ifdef DEBUG 460 DumpEndpoints(); 461 #endif 462 } 463 464 //------------------------------------------------------------------------------ 465 466 void MidiServerApp::RemoveEndpoint(app_t* app, endpoint_t* endp) 467 { 468 ASSERT(endp != NULL) 469 ASSERT(endpoints.HasItem(endp)) 470 471 TRACE(("Endpoint %ld (%p) removed", endp->id, endp)) 472 473 endpoints.RemoveItem(endp); 474 475 if (endp->consumer) 476 { 477 DisconnectDeadConsumer(endp); 478 } 479 480 BMessage notify; 481 notify.what = MSG_ENDPOINT_DELETED; 482 notify.AddInt32("midi:id", endp->id); 483 NotifyAll(¬ify, app); 484 485 delete endp; 486 487 #ifdef DEBUG 488 DumpEndpoints(); 489 #endif 490 } 491 492 //------------------------------------------------------------------------------ 493 494 void MidiServerApp::DisconnectDeadConsumer(endpoint_t* cons) 495 { 496 ASSERT(cons != NULL) 497 ASSERT(cons->consumer) 498 499 for (int32 t = 0; t < CountEndpoints(); ++t) 500 { 501 endpoint_t* prod = EndpointAt(t); 502 if (!prod->consumer) 503 { 504 prod->connections.RemoveItem(cons); 505 } 506 } 507 } 508 509 //------------------------------------------------------------------------------ 510 511 void MidiServerApp::MakeCreatedNotification(BMessage* msg, endpoint_t* endp) 512 { 513 ASSERT(msg != NULL) 514 ASSERT(endp != NULL) 515 516 msg->MakeEmpty(); 517 msg->what = MSG_ENDPOINT_CREATED; 518 msg->AddInt32("midi:id", endp->id); 519 msg->AddBool("midi:consumer", endp->consumer); 520 msg->AddBool("midi:registered", endp->registered); 521 msg->AddString("midi:name", endp->name); 522 msg->AddMessage("midi:properties", &endp->properties); 523 524 if (endp->consumer) 525 { 526 msg->AddInt32("midi:port", endp->port); 527 msg->AddInt64("midi:latency", endp->latency); 528 } 529 } 530 531 //------------------------------------------------------------------------------ 532 533 void MidiServerApp::MakeConnectedNotification( 534 BMessage* msg, endpoint_t* prod, endpoint_t* cons, bool mustConnect) 535 { 536 ASSERT(msg != NULL) 537 ASSERT(prod != NULL) 538 ASSERT(cons != NULL) 539 ASSERT(!prod->consumer) 540 ASSERT(cons->consumer) 541 542 msg->MakeEmpty(); 543 544 if (mustConnect) 545 { 546 msg->what = MSG_ENDPOINTS_CONNECTED; 547 } 548 else 549 { 550 msg->what = MSG_ENDPOINTS_DISCONNECTED; 551 } 552 553 msg->AddInt32("midi:producer", prod->id); 554 msg->AddInt32("midi:consumer", cons->id); 555 } 556 557 //------------------------------------------------------------------------------ 558 559 app_t* MidiServerApp::WhichApp(BMessage* msg) 560 { 561 ASSERT(msg != NULL) 562 563 BMessenger retadr = msg->ReturnAddress(); 564 565 for (int32 t = 0; t < CountApps(); ++t) 566 { 567 app_t* app = AppAt(t); 568 if (app->messenger.Team() == retadr.Team()) 569 { 570 return app; 571 } 572 } 573 574 TRACE(("Application %ld is not registered", retadr.Team())) 575 576 return NULL; 577 } 578 579 //------------------------------------------------------------------------------ 580 581 endpoint_t* MidiServerApp::WhichEndpoint(BMessage* msg, app_t* app) 582 { 583 ASSERT(msg != NULL) 584 ASSERT(app != NULL) 585 586 int32 id; 587 if (msg->FindInt32("midi:id", &id) == B_OK) 588 { 589 endpoint_t* endp = FindEndpoint(id); 590 if ((endp != NULL) && (endp->app == app)) 591 { 592 return endp; 593 } 594 } 595 596 TRACE(("Endpoint not found or wrong app")) 597 598 return NULL; 599 } 600 601 //------------------------------------------------------------------------------ 602 603 endpoint_t* MidiServerApp::FindEndpoint(int32 id) 604 { 605 if (id > 0) 606 { 607 for (int32 t = 0; t < CountEndpoints(); ++t) 608 { 609 endpoint_t* endp = EndpointAt(t); 610 if (endp->id == id) 611 { 612 return endp; 613 } 614 } 615 } 616 617 TRACE(("Endpoint %ld not found", id)) 618 619 return NULL; 620 } 621 622 //------------------------------------------------------------------------------ 623 624 void MidiServerApp::NotifyAll(BMessage* msg, app_t* except) 625 { 626 ASSERT(msg != NULL) 627 628 for (int32 t = CountApps() - 1; t >= 0; --t) 629 { 630 app_t* app = AppAt(t); 631 if (app != except) 632 { 633 if (!SendNotification(app, msg)) 634 { 635 delete (app_t*) apps.RemoveItem(t); 636 637 #ifdef DEBUG 638 DumpApps(); 639 #endif 640 } 641 } 642 } 643 } 644 645 //------------------------------------------------------------------------------ 646 647 bool MidiServerApp::SendNotification(app_t* app, BMessage* msg) 648 { 649 ASSERT(app != NULL) 650 ASSERT(msg != NULL) 651 652 status_t err = app->messenger.SendMessage(msg, (BHandler*) NULL, TIMEOUT); 653 654 if (err != B_OK) 655 { 656 DeliveryError(app); 657 } 658 659 return (err == B_OK); 660 } 661 662 //------------------------------------------------------------------------------ 663 664 bool MidiServerApp::SendReply(app_t* app, BMessage* msg, BMessage* reply) 665 { 666 ASSERT(msg != NULL) 667 ASSERT(reply != NULL) 668 669 status_t err = msg->SendReply(reply, (BHandler*) NULL, TIMEOUT); 670 671 if ((err != B_OK) && (app != NULL)) 672 { 673 DeliveryError(app); 674 apps.RemoveItem(app); 675 delete app; 676 677 #ifdef DEBUG 678 DumpApps(); 679 #endif 680 } 681 682 return (err == B_OK); 683 } 684 685 //------------------------------------------------------------------------------ 686 687 void MidiServerApp::DeliveryError(app_t* app) 688 { 689 ASSERT(app != NULL) 690 691 // We cannot communicate with the app, so we assume it's 692 // dead. We need to remove its endpoints from the roster, 693 // but we cannot do that right away; removing endpoints 694 // triggers a bunch of new notifications and we don't want 695 // those to get in the way of the notifications we are 696 // currently sending out. Instead, we consider the death 697 // of an app as a separate event, and pretend that the 698 // now-dead app sent us delete requests for its endpoints. 699 700 TRACE(("Delivery error; unregistering app (%p)", app)) 701 702 BMessage msg; 703 704 for (int32 t = 0; t < CountEndpoints(); ++t) 705 { 706 endpoint_t* endp = EndpointAt(t); 707 if (endp->app == app) 708 { 709 msg.MakeEmpty(); 710 msg.what = MSG_PURGE_ENDPOINT; 711 msg.AddInt32("midi:id", endp->id); 712 713 // It is not safe to post a message to your own 714 // looper's message queue, because you risk a 715 // deadlock if the queue is full. The chance of 716 // that happening is fairly small, but just in 717 // case, we catch it with a timeout. Because this 718 // situation is so unlikely, I decided to simply 719 // forget about the whole "purge" message then. 720 721 if (be_app_messenger.SendMessage( 722 &msg, (BHandler*) NULL, TIMEOUT) != B_OK) 723 { 724 WARN("Could not deliver purge message") 725 } 726 } 727 } 728 } 729 730 //------------------------------------------------------------------------------ 731 732 int32 MidiServerApp::CountApps() 733 { 734 return apps.CountItems(); 735 } 736 737 //------------------------------------------------------------------------------ 738 739 app_t* MidiServerApp::AppAt(int32 index) 740 { 741 ASSERT(index >= 0 && index < CountApps()) 742 743 return (app_t*) apps.ItemAt(index); 744 } 745 746 //------------------------------------------------------------------------------ 747 748 int32 MidiServerApp::CountEndpoints() 749 { 750 return endpoints.CountItems(); 751 } 752 753 //------------------------------------------------------------------------------ 754 755 endpoint_t* MidiServerApp::EndpointAt(int32 index) 756 { 757 ASSERT(index >= 0 && index < CountEndpoints()) 758 759 return (endpoint_t*) endpoints.ItemAt(index); 760 } 761 762 //------------------------------------------------------------------------------ 763 764 int32 MidiServerApp::CountConnections(endpoint_t* prod) 765 { 766 ASSERT(prod != NULL) 767 ASSERT(!prod->consumer) 768 769 return prod->connections.CountItems(); 770 } 771 772 //------------------------------------------------------------------------------ 773 774 endpoint_t* MidiServerApp::ConnectionAt(endpoint_t* prod, int32 index) 775 { 776 ASSERT(prod != NULL) 777 ASSERT(!prod->consumer) 778 ASSERT(index >= 0 && index < CountConnections(prod)) 779 780 return (endpoint_t*) prod->connections.ItemAt(index); 781 } 782 783 //------------------------------------------------------------------------------ 784 785 #ifdef DEBUG 786 void MidiServerApp::DumpApps() 787 { 788 printf("*** START DumpApps\n"); 789 790 for (int32 t = 0; t < CountApps(); ++t) 791 { 792 app_t* app = AppAt(t); 793 794 printf("\tapp %ld (%p): team %ld\n", t, app, app->messenger.Team()); 795 } 796 797 printf("*** END DumpApps\n"); 798 } 799 #endif 800 801 //------------------------------------------------------------------------------ 802 803 #ifdef DEBUG 804 void MidiServerApp::DumpEndpoints() 805 { 806 printf("*** START DumpEndpoints\n"); 807 808 for (int32 t = 0; t < CountEndpoints(); ++t) 809 { 810 endpoint_t* endp = EndpointAt(t); 811 812 printf("\tendpoint %ld (%p):\n", t, endp); 813 814 printf("\t\tid %ld, name '%s', %s, %s, app %p\n", 815 endp->id, endp->name.String(), 816 endp->consumer ? "consumer" : "producer", 817 endp->registered ? "registered" : "unregistered", 818 endp->app); 819 820 printf("\t\tproperties: "); endp->properties.PrintToStream(); 821 822 if (endp->consumer) 823 { 824 printf("\t\tport %ld, latency %Ld\n", endp->port, endp->latency); 825 } 826 else 827 { 828 printf("\t\tconnections:\n"); 829 for (int32 k = 0; k < CountConnections(endp); ++k) 830 { 831 endpoint_t* cons = ConnectionAt(endp, k); 832 printf("\t\t\tid %ld (%p)\n", cons->id, cons); 833 } 834 } 835 } 836 837 printf("*** END DumpEndpoints\n"); 838 } 839 #endif 840 841 //------------------------------------------------------------------------------ 842 843 int main() 844 { 845 MidiServerApp app; 846 app.Run(); 847 return 0; 848 } 849 850 //------------------------------------------------------------------------------ 851