1 /* 2 * Copyright 2009, 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 43 #include <debug.h> 44 #include <DataExchange.h> 45 #include <Notifications.h> 46 47 48 namespace BPrivate { namespace media { 49 50 /*! A helper class for the communication with the media server that 51 takes care of large buffers that need an area. 52 */ 53 class ReceiveTransfer { 54 public: 55 ReceiveTransfer(const area_request_data& request, const void* smallBuffer) 56 { 57 if (request.area == -1 && smallBuffer != NULL) { 58 // small data transfer uses buffer in reply 59 fArea = -1; 60 fData = const_cast<void*>(smallBuffer); 61 // The caller is actually responsible to enforce the const; 62 // we don't touch the data. 63 } else { 64 // large data transfer, clone area 65 fArea = clone_area("get parameter data clone", &fData, 66 B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, request.area); 67 if (fArea < B_OK) { 68 ERROR("BControllabe: cloning area failed: %s\n", 69 strerror(fArea)); 70 fData = NULL; 71 } 72 } 73 } 74 75 ~ReceiveTransfer() 76 { 77 if (fArea >= B_OK) 78 delete_area(fArea); 79 } 80 81 status_t InitCheck() const 82 { 83 return fData != NULL ? B_OK : fArea; 84 } 85 86 void* Data() const 87 { 88 return fData; 89 } 90 91 private: 92 area_id fArea; 93 void* fData; 94 }; 95 96 } // namespace media 97 } // namespace BPrivate 98 99 using BPrivate::media::ReceiveTransfer; 100 101 102 // #pragma mark - protected 103 104 105 BControllable::~BControllable() 106 { 107 CALLED(); 108 if (fSem > 0) 109 delete_sem(fSem); 110 111 delete fWeb; 112 } 113 114 115 // #pragma mark - public 116 117 118 BParameterWeb* 119 BControllable::Web() 120 { 121 CALLED(); 122 return fWeb; 123 } 124 125 126 bool 127 BControllable::LockParameterWeb() 128 { 129 CALLED(); 130 if (fSem <= 0) 131 return false; 132 133 if (atomic_add(&fBen, 1) > 0) { 134 status_t status; 135 do { 136 status = acquire_sem(fSem); 137 } while (status == B_INTERRUPTED); 138 139 return status == B_OK; 140 } 141 142 return true; 143 } 144 145 146 void 147 BControllable::UnlockParameterWeb() 148 { 149 CALLED(); 150 if (fSem <= 0) 151 return; 152 153 if (atomic_add(&fBen, -1) > 1) 154 release_sem(fSem); 155 } 156 157 158 // #pragma mark - protected 159 160 161 BControllable::BControllable() 162 : BMediaNode("this one is never called"), 163 fWeb(NULL), 164 fSem(create_sem(0, "BControllable lock")), 165 fBen(0) 166 { 167 CALLED(); 168 169 AddNodeKind(B_CONTROLLABLE); 170 } 171 172 173 status_t 174 BControllable::SetParameterWeb(BParameterWeb* web) 175 { 176 CALLED(); 177 178 LockParameterWeb(); 179 BParameterWeb* old = fWeb; 180 fWeb = web; 181 182 if (fWeb != NULL) { 183 // initialize BParameterWeb member variable 184 fWeb->fNode = Node(); 185 } 186 187 UnlockParameterWeb(); 188 189 if (old != web && web != NULL) 190 BPrivate::media::notifications::WebChanged(Node()); 191 delete old; 192 return B_OK; 193 } 194 195 196 status_t 197 BControllable::HandleMessage(int32 message, const void* data, size_t size) 198 { 199 PRINT(4, "BControllable::HandleMessage %#lx, node %ld\n", message, ID()); 200 201 switch (message) { 202 case CONTROLLABLE_GET_PARAMETER_DATA: 203 { 204 const controllable_get_parameter_data_request& request 205 = *static_cast<const controllable_get_parameter_data_request*>( 206 data); 207 controllable_get_parameter_data_reply reply; 208 209 ReceiveTransfer transfer(request, reply.raw_data); 210 if (transfer.InitCheck() != B_OK) { 211 request.SendReply(transfer.InitCheck(), &reply, sizeof(reply)); 212 return B_OK; 213 } 214 215 reply.size = request.request_size; 216 status_t status = GetParameterValue(request.parameter_id, 217 &reply.last_change, transfer.Data(), &reply.size); 218 219 request.SendReply(status, &reply, sizeof(reply)); 220 return B_OK; 221 } 222 223 case CONTROLLABLE_SET_PARAMETER_DATA: 224 { 225 const controllable_set_parameter_data_request& request 226 = *static_cast<const controllable_set_parameter_data_request*>( 227 data); 228 controllable_set_parameter_data_reply reply; 229 230 ReceiveTransfer transfer(request, request.raw_data); 231 if (transfer.InitCheck() != B_OK) { 232 request.SendReply(transfer.InitCheck(), &reply, sizeof(reply)); 233 return B_OK; 234 } 235 236 SetParameterValue(request.parameter_id, request.when, 237 transfer.Data(), request.size); 238 request.SendReply(B_OK, &reply, sizeof(reply)); 239 return B_OK; 240 } 241 242 case CONTROLLABLE_GET_PARAMETER_WEB: 243 { 244 const controllable_get_parameter_web_request& request 245 = *static_cast<const controllable_get_parameter_web_request*>( 246 data); 247 controllable_get_parameter_web_reply reply; 248 249 status_t status = B_OK; 250 bool wasLocked = true; 251 if (!LockParameterWeb()) { 252 status = B_ERROR; 253 wasLocked = false; 254 } 255 256 if (status == B_OK && fWeb != NULL) { 257 if (fWeb->FlattenedSize() > request.max_size) { 258 // parameter web too large 259 reply.code = 0; 260 reply.size = -1; 261 status = B_OK; 262 } else { 263 ReceiveTransfer transfer(request, NULL); 264 status = transfer.InitCheck(); 265 if (status == B_OK) { 266 reply.code = fWeb->TypeCode(); 267 reply.size = fWeb->FlattenedSize(); 268 status = fWeb->Flatten(transfer.Data(), reply.size); 269 if (status != B_OK) { 270 ERROR("BControllable::HandleMessage " 271 "CONTROLLABLE_GET_PARAMETER_WEB Flatten failed\n"); 272 #if 0 273 } else { 274 printf("BControllable::HandleMessage CONTROLLABLE_GET_PARAMETER_WEB %ld bytes, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx\n", 275 reply.size, ((uint32*)buffer)[0], ((uint32*)buffer)[1], ((uint32*)buffer)[2], ((uint32*)buffer)[3]); 276 #endif 277 } 278 } 279 } 280 } else { 281 // no parameter web 282 reply.code = 0; 283 reply.size = 0; 284 } 285 if (wasLocked) 286 UnlockParameterWeb(); 287 288 request.SendReply(status, &reply, sizeof(reply)); 289 return B_OK; 290 } 291 292 case CONTROLLABLE_START_CONTROL_PANEL: 293 { 294 const controllable_start_control_panel_request* request 295 = static_cast<const controllable_start_control_panel_request*>( 296 data); 297 controllable_start_control_panel_reply reply; 298 BMessenger targetMessenger; 299 status_t status = StartControlPanel(&targetMessenger); 300 if (status != B_OK) { 301 ERROR("BControllable::HandleMessage " 302 "CONTROLLABLE_START_CONTROL_PANEL failed\n"); 303 } 304 reply.result = status; 305 reply.team = targetMessenger.Team(); 306 request->SendReply(status, &reply, sizeof(reply)); 307 return B_OK; 308 } 309 310 default: 311 return B_ERROR; 312 } 313 314 return B_OK; 315 } 316 317 318 status_t 319 BControllable::BroadcastChangedParameter(int32 id) 320 { 321 CALLED(); 322 return BPrivate::media::notifications::ParameterChanged(Node(), id); 323 } 324 325 326 status_t 327 BControllable::BroadcastNewParameterValue(bigtime_t when, int32 id, 328 void* newValue, size_t valueSize) 329 { 330 CALLED(); 331 return BPrivate::media::notifications::NewParameterValue(Node(), id, when, 332 newValue, valueSize); 333 } 334 335 336 status_t 337 BControllable::StartControlPanel(BMessenger* _messenger) 338 { 339 CALLED(); 340 341 int32 internalId; 342 BMediaAddOn* addon = AddOn(&internalId); 343 if (!addon) { 344 ERROR("BControllable::StartControlPanel not instantiated per AddOn\n"); 345 return B_ERROR; 346 } 347 348 image_id imageID = addon->ImageID(); 349 image_info info; 350 if (imageID <= 0 || get_image_info(imageID, &info) != B_OK) { 351 ERROR("BControllable::StartControlPanel Error accessing image\n"); 352 return B_BAD_VALUE; 353 } 354 355 entry_ref ref; 356 if (get_ref_for_path(info.name, &ref) != B_OK) { 357 ERROR("BControllable::StartControlPanel Error getting ref\n"); 358 return B_BAD_VALUE; 359 } 360 361 // The first argument is "node=id" with id meaning the media_node_id 362 char arg[32]; 363 snprintf(arg, sizeof(arg), "node=%d", (int)ID()); 364 365 team_id team; 366 if (be_roster->Launch(&ref, 1, (const char* const*)&arg, &team) != B_OK) { 367 ERROR("BControllable::StartControlPanel Error launching application\n"); 368 return B_BAD_VALUE; 369 } 370 printf("BControllable::StartControlPanel done with id: %ld\n", team); 371 372 if (_messenger) 373 *_messenger = BMessenger(NULL, team); 374 375 return B_OK; 376 } 377 378 379 status_t 380 BControllable::ApplyParameterData(const void* value, size_t size) 381 { 382 UNIMPLEMENTED(); 383 384 return B_ERROR; 385 } 386 387 388 status_t 389 BControllable::MakeParameterData(const int32* controls, int32 count, 390 void* buffer, size_t* ioSize) 391 { 392 UNIMPLEMENTED(); 393 394 return B_ERROR; 395 } 396 397 398 // #pragma mark - private 399 400 401 status_t BControllable::_Reserved_Controllable_0(void *) { return B_ERROR; } 402 status_t BControllable::_Reserved_Controllable_1(void *) { return B_ERROR; } 403 status_t BControllable::_Reserved_Controllable_2(void *) { return B_ERROR; } 404 status_t BControllable::_Reserved_Controllable_3(void *) { return B_ERROR; } 405 status_t BControllable::_Reserved_Controllable_4(void *) { return B_ERROR; } 406 status_t BControllable::_Reserved_Controllable_5(void *) { return B_ERROR; } 407 status_t BControllable::_Reserved_Controllable_6(void *) { return B_ERROR; } 408 status_t BControllable::_Reserved_Controllable_7(void *) { return B_ERROR; } 409 status_t BControllable::_Reserved_Controllable_8(void *) { return B_ERROR; } 410 status_t BControllable::_Reserved_Controllable_9(void *) { return B_ERROR; } 411 status_t BControllable::_Reserved_Controllable_10(void *) { return B_ERROR; } 412 status_t BControllable::_Reserved_Controllable_11(void *) { return B_ERROR; } 413 status_t BControllable::_Reserved_Controllable_12(void *) { return B_ERROR; } 414 status_t BControllable::_Reserved_Controllable_13(void *) { return B_ERROR; } 415 status_t BControllable::_Reserved_Controllable_14(void *) { return B_ERROR; } 416 status_t BControllable::_Reserved_Controllable_15(void *) { return B_ERROR; } 417