1 /* 2 * Copyright 2009-2012, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT license. 4 */ 5 6 /* 7 * Copyright (c) 2002, 2003 Marcus Overhagen <Marcus@Overhagen.de> 8 * 9 * Permission is hereby granted, free of charge, to any person obtaining 10 * a copy of this software and associated documentation files or portions 11 * thereof (the "Software"), to deal in the Software without restriction, 12 * including without limitation the rights to use, copy, modify, merge, 13 * publish, distribute, sublicense, and/or sell copies of the Software, 14 * and to permit persons to whom the Software is furnished to do so, subject 15 * to the following conditions: 16 * 17 * * Redistributions of source code must retain the above copyright notice, 18 * this list of conditions and the following disclaimer. 19 * 20 * * Redistributions in binary form must reproduce the above copyright notice 21 * in the binary, as well as this list of conditions and the following 22 * disclaimer in the documentation and/or other materials provided with 23 * the distribution. 24 * 25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 26 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 28 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 31 * THE SOFTWARE. 32 * 33 */ 34 35 #include <Controllable.h> 36 37 #include <string.h> 38 39 #include <OS.h> 40 #include <ParameterWeb.h> 41 #include <Roster.h> 42 #include <TimeSource.h> 43 44 #include <debug.h> 45 #include <DataExchange.h> 46 #include <Notifications.h> 47 48 49 namespace BPrivate { namespace media { 50 51 /*! A helper class for the communication with the media server that 52 takes care of large buffers that need an area. 53 */ 54 class ReceiveTransfer { 55 public: 56 ReceiveTransfer(const area_request_data& request, const void* smallBuffer) 57 { 58 if (request.area == -1 && smallBuffer != NULL) { 59 // small data transfer uses buffer in reply 60 fArea = -1; 61 fData = const_cast<void*>(smallBuffer); 62 // The caller is actually responsible to enforce the const; 63 // we don't touch the data. 64 } else { 65 // large data transfer, clone area 66 fArea = clone_area("get parameter data clone", &fData, 67 B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, request.area); 68 if (fArea < B_OK) { 69 ERROR("BControllabe: cloning area failed: %s\n", 70 strerror(fArea)); 71 fData = NULL; 72 } 73 } 74 } 75 76 ~ReceiveTransfer() 77 { 78 if (fArea >= B_OK) 79 delete_area(fArea); 80 } 81 82 status_t InitCheck() const 83 { 84 return fData != NULL ? B_OK : fArea; 85 } 86 87 void* Data() const 88 { 89 return fData; 90 } 91 92 private: 93 area_id fArea; 94 void* fData; 95 }; 96 97 } // namespace media 98 } // namespace BPrivate 99 100 using BPrivate::media::ReceiveTransfer; 101 102 103 // #pragma mark - protected 104 105 106 BControllable::~BControllable() 107 { 108 CALLED(); 109 if (fSem > 0) 110 delete_sem(fSem); 111 112 delete fWeb; 113 } 114 115 116 // #pragma mark - public 117 118 119 BParameterWeb* 120 BControllable::Web() 121 { 122 CALLED(); 123 return fWeb; 124 } 125 126 127 bool 128 BControllable::LockParameterWeb() 129 { 130 CALLED(); 131 if (fSem <= 0) 132 return false; 133 134 if (atomic_add(&fBen, 1) > 0) { 135 status_t status; 136 do { 137 status = acquire_sem(fSem); 138 } while (status == B_INTERRUPTED); 139 140 return status == B_OK; 141 } 142 143 return true; 144 } 145 146 147 void 148 BControllable::UnlockParameterWeb() 149 { 150 CALLED(); 151 if (fSem <= 0) 152 return; 153 154 if (atomic_add(&fBen, -1) > 1) 155 release_sem(fSem); 156 } 157 158 159 // #pragma mark - protected 160 161 162 BControllable::BControllable() 163 : BMediaNode("this one is never called"), 164 fWeb(NULL), 165 fSem(create_sem(0, "BControllable lock")), 166 fBen(0) 167 { 168 CALLED(); 169 170 AddNodeKind(B_CONTROLLABLE); 171 } 172 173 174 status_t 175 BControllable::SetParameterWeb(BParameterWeb* web) 176 { 177 CALLED(); 178 179 LockParameterWeb(); 180 BParameterWeb* old = fWeb; 181 fWeb = web; 182 183 if (fWeb != NULL) { 184 // initialize BParameterWeb member variable 185 fWeb->fNode = Node(); 186 } 187 188 UnlockParameterWeb(); 189 190 if (old != web && web != NULL) 191 BPrivate::media::notifications::WebChanged(Node()); 192 delete old; 193 return B_OK; 194 } 195 196 197 status_t 198 BControllable::HandleMessage(int32 message, const void* data, size_t size) 199 { 200 PRINT(4, "BControllable::HandleMessage %#lx, node %ld\n", message, ID()); 201 202 switch (message) { 203 case CONTROLLABLE_GET_PARAMETER_DATA: 204 { 205 const controllable_get_parameter_data_request& request 206 = *static_cast<const controllable_get_parameter_data_request*>( 207 data); 208 controllable_get_parameter_data_reply reply; 209 210 ReceiveTransfer transfer(request, reply.raw_data); 211 if (transfer.InitCheck() != B_OK) { 212 request.SendReply(transfer.InitCheck(), &reply, sizeof(reply)); 213 return B_OK; 214 } 215 216 reply.size = request.request_size; 217 status_t status = GetParameterValue(request.parameter_id, 218 &reply.last_change, transfer.Data(), &reply.size); 219 220 request.SendReply(status, &reply, sizeof(reply)); 221 return B_OK; 222 } 223 224 case CONTROLLABLE_SET_PARAMETER_DATA: 225 { 226 const controllable_set_parameter_data_request& request 227 = *static_cast<const controllable_set_parameter_data_request*>( 228 data); 229 controllable_set_parameter_data_reply reply; 230 231 ReceiveTransfer transfer(request, request.raw_data); 232 if (transfer.InitCheck() != B_OK) { 233 request.SendReply(transfer.InitCheck(), &reply, sizeof(reply)); 234 return B_OK; 235 } 236 237 // NOTE: This is not very fair, but the alternative 238 // would have been to mess with friends classes and 239 // member variables. 240 bigtime_t perfTime = 0; 241 if (request.when == -1) 242 perfTime = TimeSource()->Now(); 243 else 244 perfTime = request.when; 245 246 SetParameterValue(request.parameter_id, perfTime, 247 transfer.Data(), request.size); 248 request.SendReply(B_OK, &reply, sizeof(reply)); 249 return B_OK; 250 } 251 252 case CONTROLLABLE_GET_PARAMETER_WEB: 253 { 254 const controllable_get_parameter_web_request& request 255 = *static_cast<const controllable_get_parameter_web_request*>( 256 data); 257 controllable_get_parameter_web_reply reply; 258 259 status_t status = B_OK; 260 bool wasLocked = true; 261 if (!LockParameterWeb()) { 262 status = B_ERROR; 263 wasLocked = false; 264 } 265 266 if (status == B_OK && fWeb != NULL) { 267 if (fWeb->FlattenedSize() > request.max_size) { 268 // parameter web too large 269 reply.code = 0; 270 reply.size = -1; 271 status = B_OK; 272 } else { 273 ReceiveTransfer transfer(request, NULL); 274 status = transfer.InitCheck(); 275 if (status == B_OK) { 276 reply.code = fWeb->TypeCode(); 277 reply.size = fWeb->FlattenedSize(); 278 status = fWeb->Flatten(transfer.Data(), reply.size); 279 if (status != B_OK) { 280 ERROR("BControllable::HandleMessage " 281 "CONTROLLABLE_GET_PARAMETER_WEB Flatten failed\n"); 282 #if 0 283 } else { 284 printf("BControllable::HandleMessage CONTROLLABLE_GET_PARAMETER_WEB %ld bytes, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx\n", 285 reply.size, ((uint32*)buffer)[0], ((uint32*)buffer)[1], ((uint32*)buffer)[2], ((uint32*)buffer)[3]); 286 #endif 287 } 288 } 289 } 290 } else { 291 // no parameter web 292 reply.code = 0; 293 reply.size = 0; 294 } 295 if (wasLocked) 296 UnlockParameterWeb(); 297 298 request.SendReply(status, &reply, sizeof(reply)); 299 return B_OK; 300 } 301 302 case CONTROLLABLE_START_CONTROL_PANEL: 303 { 304 const controllable_start_control_panel_request* request 305 = static_cast<const controllable_start_control_panel_request*>( 306 data); 307 controllable_start_control_panel_reply reply; 308 BMessenger targetMessenger; 309 status_t status = StartControlPanel(&targetMessenger); 310 if (status != B_OK) { 311 ERROR("BControllable::HandleMessage " 312 "CONTROLLABLE_START_CONTROL_PANEL failed\n"); 313 } 314 reply.result = status; 315 reply.team = targetMessenger.Team(); 316 request->SendReply(status, &reply, sizeof(reply)); 317 return B_OK; 318 } 319 320 default: 321 return B_ERROR; 322 } 323 324 return B_OK; 325 } 326 327 328 status_t 329 BControllable::BroadcastChangedParameter(int32 id) 330 { 331 CALLED(); 332 return BPrivate::media::notifications::ParameterChanged(Node(), id); 333 } 334 335 336 status_t 337 BControllable::BroadcastNewParameterValue(bigtime_t when, int32 id, 338 void* newValue, size_t valueSize) 339 { 340 CALLED(); 341 return BPrivate::media::notifications::NewParameterValue(Node(), id, when, 342 newValue, valueSize); 343 } 344 345 346 status_t 347 BControllable::StartControlPanel(BMessenger* _messenger) 348 { 349 CALLED(); 350 351 int32 internalId; 352 BMediaAddOn* addon = AddOn(&internalId); 353 if (!addon) { 354 ERROR("BControllable::StartControlPanel not instantiated per AddOn\n"); 355 return B_ERROR; 356 } 357 358 image_id imageID = addon->ImageID(); 359 image_info info; 360 if (imageID <= 0 || get_image_info(imageID, &info) != B_OK) { 361 ERROR("BControllable::StartControlPanel Error accessing image\n"); 362 return B_BAD_VALUE; 363 } 364 365 entry_ref ref; 366 if (get_ref_for_path(info.name, &ref) != B_OK) { 367 ERROR("BControllable::StartControlPanel Error getting ref\n"); 368 return B_BAD_VALUE; 369 } 370 371 // The first argument is "node=id" with id meaning the media_node_id 372 char arg[32]; 373 snprintf(arg, sizeof(arg), "node=%d", (int)ID()); 374 375 team_id team; 376 if (be_roster->Launch(&ref, 1, (const char* const*)&arg, &team) != B_OK) { 377 ERROR("BControllable::StartControlPanel Error launching application\n"); 378 return B_BAD_VALUE; 379 } 380 printf("BControllable::StartControlPanel done with id: %" B_PRId32 "\n", 381 team); 382 383 if (_messenger) 384 *_messenger = BMessenger(NULL, team); 385 386 return B_OK; 387 } 388 389 390 status_t 391 BControllable::ApplyParameterData(const void* value, size_t size) 392 { 393 UNIMPLEMENTED(); 394 395 return B_ERROR; 396 } 397 398 399 status_t 400 BControllable::MakeParameterData(const int32* controls, int32 count, 401 void* buffer, size_t* ioSize) 402 { 403 UNIMPLEMENTED(); 404 405 return B_ERROR; 406 } 407 408 409 // #pragma mark - private 410 411 412 status_t BControllable::_Reserved_Controllable_0(void *) { return B_ERROR; } 413 status_t BControllable::_Reserved_Controllable_1(void *) { return B_ERROR; } 414 status_t BControllable::_Reserved_Controllable_2(void *) { return B_ERROR; } 415 status_t BControllable::_Reserved_Controllable_3(void *) { return B_ERROR; } 416 status_t BControllable::_Reserved_Controllable_4(void *) { return B_ERROR; } 417 status_t BControllable::_Reserved_Controllable_5(void *) { return B_ERROR; } 418 status_t BControllable::_Reserved_Controllable_6(void *) { return B_ERROR; } 419 status_t BControllable::_Reserved_Controllable_7(void *) { return B_ERROR; } 420 status_t BControllable::_Reserved_Controllable_8(void *) { return B_ERROR; } 421 status_t BControllable::_Reserved_Controllable_9(void *) { return B_ERROR; } 422 status_t BControllable::_Reserved_Controllable_10(void *) { return B_ERROR; } 423 status_t BControllable::_Reserved_Controllable_11(void *) { return B_ERROR; } 424 status_t BControllable::_Reserved_Controllable_12(void *) { return B_ERROR; } 425 status_t BControllable::_Reserved_Controllable_13(void *) { return B_ERROR; } 426 status_t BControllable::_Reserved_Controllable_14(void *) { return B_ERROR; } 427 status_t BControllable::_Reserved_Controllable_15(void *) { return B_ERROR; } 428