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