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 43#include <debug.h> 44#include <DataExchange.h> 45#include <Notifications.h> 46 47 48namespace 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*/ 53class ReceiveTransfer { 54public: 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 91private: 92 area_id fArea; 93 void* fData; 94}; 95 96} // namespace media 97} // namespace BPrivate 98 99using BPrivate::media::ReceiveTransfer; 100 101 102// #pragma mark - protected 103 104 105BControllable::~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 118BParameterWeb* 119BControllable::Web() 120{ 121 CALLED(); 122 return fWeb; 123} 124 125 126bool 127BControllable::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 146void 147BControllable::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 161BControllable::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 173status_t 174BControllable::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 196status_t 197BControllable::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 318status_t 319BControllable::BroadcastChangedParameter(int32 id) 320{ 321 CALLED(); 322 return BPrivate::media::notifications::ParameterChanged(Node(), id); 323} 324 325 326status_t 327BControllable::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 336status_t 337BControllable::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: %" B_PRId32 "\n", 371 team); 372 373 if (_messenger) 374 *_messenger = BMessenger(NULL, team); 375 376 return B_OK; 377} 378 379 380status_t 381BControllable::ApplyParameterData(const void* value, size_t size) 382{ 383 UNIMPLEMENTED(); 384 385 return B_ERROR; 386} 387 388 389status_t 390BControllable::MakeParameterData(const int32* controls, int32 count, 391 void* buffer, size_t* ioSize) 392{ 393 UNIMPLEMENTED(); 394 395 return B_ERROR; 396} 397 398 399// #pragma mark - private 400 401 402status_t BControllable::_Reserved_Controllable_0(void *) { return B_ERROR; } 403status_t BControllable::_Reserved_Controllable_1(void *) { return B_ERROR; } 404status_t BControllable::_Reserved_Controllable_2(void *) { return B_ERROR; } 405status_t BControllable::_Reserved_Controllable_3(void *) { return B_ERROR; } 406status_t BControllable::_Reserved_Controllable_4(void *) { return B_ERROR; } 407status_t BControllable::_Reserved_Controllable_5(void *) { return B_ERROR; } 408status_t BControllable::_Reserved_Controllable_6(void *) { return B_ERROR; } 409status_t BControllable::_Reserved_Controllable_7(void *) { return B_ERROR; } 410status_t BControllable::_Reserved_Controllable_8(void *) { return B_ERROR; } 411status_t BControllable::_Reserved_Controllable_9(void *) { return B_ERROR; } 412status_t BControllable::_Reserved_Controllable_10(void *) { return B_ERROR; } 413status_t BControllable::_Reserved_Controllable_11(void *) { return B_ERROR; } 414status_t BControllable::_Reserved_Controllable_12(void *) { return B_ERROR; } 415status_t BControllable::_Reserved_Controllable_13(void *) { return B_ERROR; } 416status_t BControllable::_Reserved_Controllable_14(void *) { return B_ERROR; } 417status_t BControllable::_Reserved_Controllable_15(void *) { return B_ERROR; } 418