1/*
2 * Copyright 2002-2012, Haiku. All Rights Reserved.
3 * This file may be used under the terms of the MIT License.
4 *
5 * Authors:
6 *    Dario Casalinuovo
7 *    Marcus Overhagen
8 */
9
10
11#include <TimeSource.h>
12
13#include <Autolock.h>
14
15#include <string.h>
16
17#include "MediaDebug.h"
18#include "DataExchange.h"
19#include "ServerInterface.h"
20#include "TimeSourceObject.h"
21#include "TMap.h"
22
23#define DEBUG_TIMESOURCE 0
24
25#if DEBUG_TIMESOURCE
26	#define TRACE_TIMESOURCE printf
27#else
28	#define TRACE_TIMESOURCE if (1) {} else printf
29#endif
30
31namespace BPrivate { namespace media {
32
33#define _atomic_read(p) 	atomic_or((p), 0)
34
35// must be multiple of page size
36#define TS_AREA_SIZE		B_PAGE_SIZE
37// must be power of two
38#define TS_INDEX_COUNT		128
39
40// sizeof(TimeSourceTransmit) must be <= TS_AREA_SIZE
41struct TimeSourceTransmit
42{
43	int32 readindex;
44	int32 writeindex;
45	int32 isrunning;
46	bigtime_t realtime[TS_INDEX_COUNT];
47	bigtime_t perftime[TS_INDEX_COUNT];
48	float drift[TS_INDEX_COUNT];
49};
50
51#define MAX_SLAVE_NODES 300
52
53
54class SlaveNodes : public BLocker
55{
56public:
57								SlaveNodes();
58								~SlaveNodes();
59
60	int32						CountSlaves() const;
61	bool						GetNextSlave(port_id** id);
62	void						Rewind();
63
64	bool						InsertSlave(const media_node& node);
65	bool						RemoveSlave(const media_node& node);
66private:
67	Map<media_node_id, port_id>	fSlaveList;
68};
69
70
71SlaveNodes::SlaveNodes()
72	:
73	BLocker("BTimeSource slavenodes")
74{
75}
76
77
78SlaveNodes::~SlaveNodes()
79{
80	fSlaveList.MakeEmpty();
81}
82
83
84int32
85SlaveNodes::CountSlaves() const
86{
87	return fSlaveList.CountItems();
88}
89
90
91bool
92SlaveNodes::GetNextSlave(port_id** id)
93{
94	return fSlaveList.GetNext(id);
95}
96
97
98void
99SlaveNodes::Rewind()
100{
101	fSlaveList.Rewind();
102}
103
104
105bool
106SlaveNodes::InsertSlave(const media_node& node)
107{
108	return fSlaveList.Insert(node.node, node.port);
109}
110
111
112bool
113SlaveNodes::RemoveSlave(const media_node& node)
114{
115	return fSlaveList.Remove(node.node);
116}
117
118
119} } // namespace BPrivate::media
120
121
122/*************************************************************
123 * protected BTimeSource
124 *************************************************************/
125
126BTimeSource::~BTimeSource()
127{
128	CALLED();
129	if (fArea > 0)
130		delete_area(fArea);
131	delete fSlaveNodes;
132}
133
134/*************************************************************
135 * public BTimeSource
136 *************************************************************/
137
138status_t
139BTimeSource::SnoozeUntil(bigtime_t performance_time,
140	bigtime_t with_latency, bool retry_signals)
141{
142	CALLED();
143	bigtime_t time;
144	status_t err;
145	do {
146		time = RealTimeFor(performance_time, with_latency);
147		err = snooze_until(time, B_SYSTEM_TIMEBASE);
148	} while (err == B_INTERRUPTED && retry_signals);
149	return err;
150}
151
152
153bigtime_t
154BTimeSource::Now()
155{
156	PRINT(8, "CALLED BTimeSource::Now()\n");
157	return PerformanceTimeFor(RealTime());
158}
159
160
161bigtime_t
162BTimeSource::PerformanceTimeFor(bigtime_t real_time)
163{
164	PRINT(8, "CALLED BTimeSource::PerformanceTimeFor()\n");
165	bigtime_t last_perf_time;
166	bigtime_t last_real_time;
167	float last_drift;
168
169	if (GetTime(&last_perf_time, &last_real_time, &last_drift) != B_OK)
170		debugger("BTimeSource::PerformanceTimeFor: GetTime failed");
171
172	return last_perf_time
173		+ (bigtime_t)((real_time - last_real_time) * last_drift);
174}
175
176
177bigtime_t
178BTimeSource::RealTimeFor(bigtime_t performance_time,
179	bigtime_t with_latency)
180{
181	PRINT(8, "CALLED BTimeSource::RealTimeFor()\n");
182
183	if (fIsRealtime) {
184		return performance_time - with_latency;
185	}
186
187	bigtime_t last_perf_time;
188	bigtime_t last_real_time;
189	float last_drift;
190
191	if (GetTime(&last_perf_time, &last_real_time, &last_drift) != B_OK)
192		debugger("BTimeSource::RealTimeFor: GetTime failed");
193
194	return last_real_time - with_latency
195		+ (bigtime_t)((performance_time - last_perf_time) / last_drift);
196}
197
198
199bool
200BTimeSource::IsRunning()
201{
202	PRINT(8, "CALLED BTimeSource::IsRunning()\n");
203
204	bool isrunning;
205
206	// The system time source is always running
207	if (fIsRealtime)
208		isrunning = true;
209	else
210		isrunning = fBuf ? atomic_add(&fBuf->isrunning, 0) : fStarted;
211
212	TRACE_TIMESOURCE("BTimeSource::IsRunning() node %" B_PRId32 ", port %"
213		B_PRId32 ", %s\n", fNodeID, fControlPort, isrunning ? "yes" : "no");
214	return isrunning;
215}
216
217
218status_t
219BTimeSource::GetTime(bigtime_t* performance_time,
220	bigtime_t* real_time, float* drift)
221{
222	PRINT(8, "CALLED BTimeSource::GetTime()\n");
223
224	if (fIsRealtime) {
225		*performance_time = *real_time = system_time();
226		*drift = 1.0f;
227		return B_OK;
228	}
229
230	if (fBuf == NULL)
231		debugger("BTimeSource::GetTime: fBuf == NULL");
232
233	int32 index = _atomic_read(&fBuf->readindex);
234	index &= (TS_INDEX_COUNT - 1);
235	*real_time = fBuf->realtime[index];
236	*performance_time = fBuf->perftime[index];
237	*drift = fBuf->drift[index];
238
239	TRACE_TIMESOURCE("BTimeSource::GetTime     timesource %" B_PRId32
240		", perf %16" B_PRId64 ", real %16" B_PRId64 ", drift %2.2f\n", ID(),
241		*performance_time, *real_time, *drift);
242	return B_OK;
243}
244
245
246bigtime_t
247BTimeSource::RealTime()
248{
249	PRINT(8, "CALLED BTimeSource::RealTime()\n");
250	return system_time();
251}
252
253
254status_t
255BTimeSource::GetStartLatency(bigtime_t* out_latency)
256{
257	CALLED();
258	*out_latency = 0;
259	return B_OK;
260}
261
262/*************************************************************
263 * protected BTimeSource
264 *************************************************************/
265
266
267BTimeSource::BTimeSource()
268	:
269	BMediaNode("This one is never called"),
270	fStarted(false),
271	fArea(-1),
272	fBuf(NULL),
273	fSlaveNodes(new BPrivate::media::SlaveNodes),
274	fIsRealtime(false)
275{
276	CALLED();
277	AddNodeKind(B_TIME_SOURCE);
278	// This constructor is only called by real time sources that inherit
279	// BTimeSource. We create the communication area in FinishCreate(),
280	// since we don't have a correct ID() until this node is registered.
281}
282
283
284status_t
285BTimeSource::HandleMessage(int32 message, const void* rawdata,
286	size_t size)
287{
288	PRINT(4, "BTimeSource::HandleMessage %#" B_PRIx32 ", node %" B_PRId32 "\n",
289		message, fNodeID);
290	status_t rv;
291	switch (message) {
292		case TIMESOURCE_OP:
293		{
294			const time_source_op_info* data
295				= static_cast<const time_source_op_info*>(rawdata);
296
297			status_t result;
298			result = TimeSourceOp(*data, NULL);
299			if (result != B_OK) {
300				ERROR("BTimeSource::HandleMessage: TimeSourceOp failed\n");
301			}
302
303			switch (data->op) {
304				case B_TIMESOURCE_START:
305					DirectStart(data->real_time);
306					break;
307				case B_TIMESOURCE_STOP:
308					DirectStop(data->real_time, false);
309					break;
310				case B_TIMESOURCE_STOP_IMMEDIATELY:
311					DirectStop(data->real_time, true);
312					break;
313				case B_TIMESOURCE_SEEK:
314					DirectSeek(data->performance_time, data->real_time);
315					break;
316			}
317			return B_OK;
318		}
319
320		case TIMESOURCE_ADD_SLAVE_NODE:
321		{
322			const timesource_add_slave_node_command* data
323				= static_cast<const timesource_add_slave_node_command*>(rawdata);
324			DirectAddMe(data->node);
325			return B_OK;
326		}
327
328		case TIMESOURCE_REMOVE_SLAVE_NODE:
329		{
330			const timesource_remove_slave_node_command* data
331				= static_cast<const timesource_remove_slave_node_command*>(rawdata);
332			DirectRemoveMe(data->node);
333			return B_OK;
334		}
335
336		case TIMESOURCE_GET_START_LATENCY:
337		{
338			const timesource_get_start_latency_request* request
339				= static_cast<const timesource_get_start_latency_request*>(rawdata);
340			timesource_get_start_latency_reply reply;
341			rv = GetStartLatency(&reply.start_latency);
342			request->SendReply(rv, &reply, sizeof(reply));
343			return B_OK;
344		}
345	}
346	return B_ERROR;
347}
348
349
350void
351BTimeSource::PublishTime(bigtime_t performance_time,
352	bigtime_t real_time, float drift)
353{
354	TRACE_TIMESOURCE("BTimeSource::PublishTime timesource %" B_PRId32
355		", perf %16" B_PRId64 ", real %16" B_PRId64 ", drift %2.2f\n", ID(),
356		performance_time, real_time, drift);
357	if (0 == fBuf) {
358		ERROR("BTimeSource::PublishTime timesource %" B_PRId32
359			", fBuf = NULL\n", ID());
360		fStarted = true;
361		return;
362	}
363
364	int32 index = atomic_add(&fBuf->writeindex, 1);
365	index &= (TS_INDEX_COUNT - 1);
366	fBuf->realtime[index] = real_time;
367	fBuf->perftime[index] = performance_time;
368	fBuf->drift[index] = drift;
369	atomic_add(&fBuf->readindex, 1);
370}
371
372
373void
374BTimeSource::BroadcastTimeWarp(bigtime_t at_real_time,
375	bigtime_t new_performance_time)
376{
377	CALLED();
378	ASSERT(fSlaveNodes != NULL);
379
380	// calls BMediaNode::TimeWarp() of all slaved nodes
381
382	TRACE("BTimeSource::BroadcastTimeWarp: at_real_time %" B_PRId64
383		", new_performance_time %" B_PRId64 "\n", at_real_time,
384		new_performance_time);
385
386	BAutolock lock(fSlaveNodes);
387
388	port_id* port = NULL;
389	while (fSlaveNodes->GetNextSlave(&port) == true) {
390		node_time_warp_command cmd;
391		cmd.at_real_time = at_real_time;
392		cmd.to_performance_time = new_performance_time;
393		SendToPort(*port, NODE_TIME_WARP,
394			&cmd, sizeof(cmd));
395	}
396	fSlaveNodes->Rewind();
397}
398
399
400void
401BTimeSource::SendRunMode(run_mode mode)
402{
403	CALLED();
404	ASSERT(fSlaveNodes != NULL);
405
406	// send the run mode change to all slaved nodes
407
408	BAutolock lock(fSlaveNodes);
409
410	port_id* port = NULL;
411	while (fSlaveNodes->GetNextSlave(&port) == true) {
412		node_set_run_mode_command cmd;
413		cmd.mode = mode;
414		SendToPort(*port, NODE_SET_RUN_MODE,
415			&cmd, sizeof(cmd));
416	}
417	fSlaveNodes->Rewind();
418}
419
420
421void
422BTimeSource::SetRunMode(run_mode mode)
423{
424	CALLED();
425	BMediaNode::SetRunMode(mode);
426	SendRunMode(mode);
427}
428/*************************************************************
429 * private BTimeSource
430 *************************************************************/
431
432/*
433//unimplemented
434BTimeSource::BTimeSource(const BTimeSource &clone)
435BTimeSource &BTimeSource::operator=(const BTimeSource &clone)
436*/
437
438status_t BTimeSource::_Reserved_TimeSource_0(void *) { return B_ERROR; }
439status_t BTimeSource::_Reserved_TimeSource_1(void *) { return B_ERROR; }
440status_t BTimeSource::_Reserved_TimeSource_2(void *) { return B_ERROR; }
441status_t BTimeSource::_Reserved_TimeSource_3(void *) { return B_ERROR; }
442status_t BTimeSource::_Reserved_TimeSource_4(void *) { return B_ERROR; }
443status_t BTimeSource::_Reserved_TimeSource_5(void *) { return B_ERROR; }
444
445/* explicit */
446BTimeSource::BTimeSource(media_node_id id)
447	:
448	BMediaNode("This one is never called"),
449	fStarted(false),
450	fArea(-1),
451	fBuf(NULL),
452	fSlaveNodes(NULL),
453	fIsRealtime(false)
454{
455	CALLED();
456	AddNodeKind(B_TIME_SOURCE);
457	ASSERT(id > 0);
458
459	// This constructor is only called by the derived
460	// BPrivate::media::TimeSourceObject objects
461	// We create a clone of the communication area.
462	char name[32];
463	sprintf(name, "__timesource_buf_%" B_PRId32, id);
464	area_id area = find_area(name);
465	if (area <= 0) {
466		ERROR("BTimeSource::BTimeSource couldn't find area, node %" B_PRId32
467			"\n", id);
468		return;
469	}
470	sprintf(name, "__cloned_timesource_buf_%" B_PRId32, id);
471
472	void** buf = reinterpret_cast<void**>
473		(const_cast<BPrivate::media::TimeSourceTransmit**>(&fBuf));
474
475	fArea = clone_area(name, buf, B_ANY_ADDRESS,
476		B_READ_AREA | B_WRITE_AREA, area);
477
478	if (fArea <= 0) {
479		ERROR("BTimeSource::BTimeSource couldn't clone area, node %" B_PRId32
480			"\n", id);
481		return;
482	}
483}
484
485
486void
487BTimeSource::FinishCreate()
488{
489	CALLED();
490
491	char name[32];
492	sprintf(name, "__timesource_buf_%" B_PRId32, ID());
493
494	void** buf = reinterpret_cast<void**>
495		(const_cast<BPrivate::media::TimeSourceTransmit**>(&fBuf));
496
497	fArea = create_area(name, buf, B_ANY_ADDRESS, TS_AREA_SIZE,
498		B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA);
499
500	if (fArea <= 0) {
501		ERROR("BTimeSource::BTimeSource couldn't create area, node %" B_PRId32
502			"\n", ID());
503		fBuf = NULL;
504		return;
505	}
506	fBuf->readindex = 0;
507	fBuf->writeindex = 1;
508	fBuf->realtime[0] = 0;
509	fBuf->perftime[0] = 0;
510	fBuf->drift[0] = 1.0f;
511	fBuf->isrunning = fStarted;
512}
513
514
515status_t
516BTimeSource::RemoveMe(BMediaNode* node)
517{
518	CALLED();
519	if (fKinds & NODE_KIND_SHADOW_TIMESOURCE) {
520		timesource_remove_slave_node_command cmd;
521		cmd.node = node->Node();
522		SendToPort(fControlPort, TIMESOURCE_REMOVE_SLAVE_NODE,
523			&cmd, sizeof(cmd));
524	} else {
525		DirectRemoveMe(node->Node());
526	}
527	return B_OK;
528}
529
530
531status_t
532BTimeSource::AddMe(BMediaNode* node)
533{
534	CALLED();
535	if (fKinds & NODE_KIND_SHADOW_TIMESOURCE) {
536		timesource_add_slave_node_command cmd;
537		cmd.node = node->Node();
538		SendToPort(fControlPort, TIMESOURCE_ADD_SLAVE_NODE, &cmd, sizeof(cmd));
539	} else {
540		DirectAddMe(node->Node());
541	}
542	return B_OK;
543}
544
545
546void
547BTimeSource::DirectAddMe(const media_node& node)
548{
549	CALLED();
550	ASSERT(fSlaveNodes != NULL);
551	BAutolock lock(fSlaveNodes);
552
553	if (fSlaveNodes->CountSlaves() == MAX_SLAVE_NODES) {
554		ERROR("BTimeSource::DirectAddMe reached maximum number of slaves\n");
555		return;
556	}
557	if (fNodeID == node.node) {
558		ERROR("BTimeSource::DirectAddMe should not add itself to slave nodes\n");
559		return;
560	}
561
562	if (fSlaveNodes->InsertSlave(node) != true) {
563		ERROR("BTimeSource::DirectAddMe failed\n");
564		return;
565	}
566
567	if (fSlaveNodes->CountSlaves() == 1) {
568		// start the time source
569		time_source_op_info msg;
570		msg.op = B_TIMESOURCE_START;
571		msg.real_time = RealTime();
572
573		TRACE_TIMESOURCE("starting time source %" B_PRId32 "\n", ID());
574
575		write_port(fControlPort, TIMESOURCE_OP, &msg, sizeof(msg));
576	}
577 }
578
579
580void
581BTimeSource::DirectRemoveMe(const media_node& node)
582{
583	CALLED();
584	ASSERT(fSlaveNodes != NULL);
585	BAutolock lock(fSlaveNodes);
586
587	if (fSlaveNodes->CountSlaves() == 0) {
588		ERROR("BTimeSource::DirectRemoveMe no slots used\n");
589		return;
590	}
591
592	if (fSlaveNodes->RemoveSlave(node) != true) {
593		ERROR("BTimeSource::DirectRemoveMe failed\n");
594		return;
595	}
596
597	if (fSlaveNodes->CountSlaves() == 0) {
598		// stop the time source
599		time_source_op_info msg;
600		msg.op = B_TIMESOURCE_STOP_IMMEDIATELY;
601		msg.real_time = RealTime();
602
603		TRACE_TIMESOURCE("stopping time source %" B_PRId32 "\n", ID());
604
605		write_port(fControlPort, TIMESOURCE_OP, &msg, sizeof(msg));
606	}
607}
608
609
610void
611BTimeSource::DirectStart(bigtime_t at)
612{
613	CALLED();
614	if (fBuf)
615		atomic_or(&fBuf->isrunning, 1);
616	else
617		fStarted = true;
618}
619
620
621void
622BTimeSource::DirectStop(bigtime_t at, bool immediate)
623{
624	CALLED();
625	if (fBuf)
626		atomic_and(&fBuf->isrunning, 0);
627	else
628		fStarted = false;
629}
630
631
632void
633BTimeSource::DirectSeek(bigtime_t to, bigtime_t at)
634{
635	UNIMPLEMENTED();
636}
637
638
639void
640BTimeSource::DirectSetRunMode(run_mode mode)
641{
642	UNIMPLEMENTED();
643}
644