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 <MediaDebug.h> 45#include <DataExchange.h> 46#include <Notifications.h> 47 48 49namespace 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*/ 54class ReceiveTransfer { 55public: 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 92private: 93 area_id fArea; 94 void* fData; 95}; 96 97} // namespace media 98} // namespace BPrivate 99 100using BPrivate::media::ReceiveTransfer; 101 102 103// #pragma mark - protected 104 105 106BControllable::~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 119BParameterWeb* 120BControllable::Web() 121{ 122 CALLED(); 123 return fWeb; 124} 125 126 127bool 128BControllable::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 147void 148BControllable::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 162BControllable::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 174status_t 175BControllable::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 197status_t 198BControllable::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 328status_t 329BControllable::BroadcastChangedParameter(int32 id) 330{ 331 CALLED(); 332 return BPrivate::media::notifications::ParameterChanged(Node(), id); 333} 334 335 336status_t 337BControllable::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 346status_t 347BControllable::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 390status_t 391BControllable::ApplyParameterData(const void* value, size_t size) 392{ 393 UNIMPLEMENTED(); 394 395 return B_ERROR; 396} 397 398 399status_t 400BControllable::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 412status_t BControllable::_Reserved_Controllable_0(void *) { return B_ERROR; } 413status_t BControllable::_Reserved_Controllable_1(void *) { return B_ERROR; } 414status_t BControllable::_Reserved_Controllable_2(void *) { return B_ERROR; } 415status_t BControllable::_Reserved_Controllable_3(void *) { return B_ERROR; } 416status_t BControllable::_Reserved_Controllable_4(void *) { return B_ERROR; } 417status_t BControllable::_Reserved_Controllable_5(void *) { return B_ERROR; } 418status_t BControllable::_Reserved_Controllable_6(void *) { return B_ERROR; } 419status_t BControllable::_Reserved_Controllable_7(void *) { return B_ERROR; } 420status_t BControllable::_Reserved_Controllable_8(void *) { return B_ERROR; } 421status_t BControllable::_Reserved_Controllable_9(void *) { return B_ERROR; } 422status_t BControllable::_Reserved_Controllable_10(void *) { return B_ERROR; } 423status_t BControllable::_Reserved_Controllable_11(void *) { return B_ERROR; } 424status_t BControllable::_Reserved_Controllable_12(void *) { return B_ERROR; } 425status_t BControllable::_Reserved_Controllable_13(void *) { return B_ERROR; } 426status_t BControllable::_Reserved_Controllable_14(void *) { return B_ERROR; } 427status_t BControllable::_Reserved_Controllable_15(void *) { return B_ERROR; } 428