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
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:
ReceiveTransfer(const area_request_data & request,const void * smallBuffer)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
~ReceiveTransfer()76 ~ReceiveTransfer()
77 {
78 if (fArea >= B_OK)
79 delete_area(fArea);
80 }
81
InitCheck() const82 status_t InitCheck() const
83 {
84 return fData != NULL ? B_OK : fArea;
85 }
86
Data() const87 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
~BControllable()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*
Web()120 BControllable::Web()
121 {
122 CALLED();
123 return fWeb;
124 }
125
126
127 bool
LockParameterWeb()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
UnlockParameterWeb()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
BControllable()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
SetParameterWeb(BParameterWeb * web)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
HandleMessage(int32 message,const void * data,size_t size)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
BroadcastChangedParameter(int32 id)329 BControllable::BroadcastChangedParameter(int32 id)
330 {
331 CALLED();
332 return BPrivate::media::notifications::ParameterChanged(Node(), id);
333 }
334
335
336 status_t
BroadcastNewParameterValue(bigtime_t when,int32 id,void * newValue,size_t valueSize)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
StartControlPanel(BMessenger * _messenger)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
ApplyParameterData(const void * value,size_t size)391 BControllable::ApplyParameterData(const void* value, size_t size)
392 {
393 UNIMPLEMENTED();
394
395 return B_ERROR;
396 }
397
398
399 status_t
MakeParameterData(const int32 * controls,int32 count,void * buffer,size_t * ioSize)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
_Reserved_Controllable_0(void *)412 status_t BControllable::_Reserved_Controllable_0(void *) { return B_ERROR; }
_Reserved_Controllable_1(void *)413 status_t BControllable::_Reserved_Controllable_1(void *) { return B_ERROR; }
_Reserved_Controllable_2(void *)414 status_t BControllable::_Reserved_Controllable_2(void *) { return B_ERROR; }
_Reserved_Controllable_3(void *)415 status_t BControllable::_Reserved_Controllable_3(void *) { return B_ERROR; }
_Reserved_Controllable_4(void *)416 status_t BControllable::_Reserved_Controllable_4(void *) { return B_ERROR; }
_Reserved_Controllable_5(void *)417 status_t BControllable::_Reserved_Controllable_5(void *) { return B_ERROR; }
_Reserved_Controllable_6(void *)418 status_t BControllable::_Reserved_Controllable_6(void *) { return B_ERROR; }
_Reserved_Controllable_7(void *)419 status_t BControllable::_Reserved_Controllable_7(void *) { return B_ERROR; }
_Reserved_Controllable_8(void *)420 status_t BControllable::_Reserved_Controllable_8(void *) { return B_ERROR; }
_Reserved_Controllable_9(void *)421 status_t BControllable::_Reserved_Controllable_9(void *) { return B_ERROR; }
_Reserved_Controllable_10(void *)422 status_t BControllable::_Reserved_Controllable_10(void *) { return B_ERROR; }
_Reserved_Controllable_11(void *)423 status_t BControllable::_Reserved_Controllable_11(void *) { return B_ERROR; }
_Reserved_Controllable_12(void *)424 status_t BControllable::_Reserved_Controllable_12(void *) { return B_ERROR; }
_Reserved_Controllable_13(void *)425 status_t BControllable::_Reserved_Controllable_13(void *) { return B_ERROR; }
_Reserved_Controllable_14(void *)426 status_t BControllable::_Reserved_Controllable_14(void *) { return B_ERROR; }
_Reserved_Controllable_15(void *)427 status_t BControllable::_Reserved_Controllable_15(void *) { return B_ERROR; }
428