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