1/* Realtek RTL8169 Family Driver
2 * Copyright (C) 2004 Marcus Overhagen <marcus@overhagen.de>. All rights reserved.
3 *
4 * Permission to use, copy, modify and distribute this software and its
5 * documentation for any purpose and without fee is hereby granted, provided
6 * that the above copyright notice appear in all copies, and that both the
7 * copyright notice and this permission notice appear in supporting documentation.
8 *
9 * Marcus Overhagen makes no representations about the suitability of this software
10 * for any purpose. It is provided "as is" without express or implied warranty.
11 *
12 * MARCUS OVERHAGEN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
13 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL MARCUS
14 * OVERHAGEN BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
15 * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
17 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19#include <Errors.h>
20#include <OS.h>
21#include <string.h>
22
23#include "debug.h"
24#include "fwdebug.h"
25#include "timer.h"
26
27#define MAX_TIMERS 8
28
29
30struct timer_info
31{
32	timer_id 		id;
33	timer_function	func;
34	void *			cookie;
35	bigtime_t		next_event;
36	bigtime_t		interval;
37	bool			periodic;
38};
39
40
41static struct timer_info	sTimerData[MAX_TIMERS];
42static int					sTimerCount;
43static timer_id				sTimerNextId;
44static thread_id			sTimerThread;
45static sem_id				sTimerSem;
46static spinlock				sTimerSpinlock;
47
48
49static int32
50timer_thread(void *cookie)
51{
52	status_t status = 0;
53
54	do {
55		bigtime_t timeout;
56		bigtime_t now;
57		cpu_status cpu;
58		timer_function func;
59		void * cookie;
60		int i;
61		int index;
62
63		cpu = disable_interrupts();
64		acquire_spinlock(&sTimerSpinlock);
65
66		now = system_time();
67		cookie = 0;
68		func = 0;
69
70		// find timer with smallest event time
71		index = -1;
72		timeout = B_INFINITE_TIMEOUT;
73		for (i = 0; i < sTimerCount; i++) {
74			if (sTimerData[i].next_event < timeout) {
75				timeout = sTimerData[i].next_event;
76				index = i;
77			}
78		}
79
80		if (timeout < now) {
81			// timer is ready for execution, load func and cookie
82			ASSERT(index >= 0 && index < sTimerCount);
83			func = sTimerData[index].func;
84			cookie = sTimerData[index].cookie;
85			if (sTimerData[index].periodic) {
86				// periodic timer is ready, update the entry
87				sTimerData[index].next_event += sTimerData[index].interval;
88			} else {
89				// single shot timer is ready, delete the entry
90				if (index != (sTimerCount - 1) && sTimerCount != 1) {
91					memcpy(&sTimerData[index], &sTimerData[sTimerCount - 1], sizeof(struct timer_info));
92				}
93				sTimerCount--;
94			}
95		}
96
97		release_spinlock(&sTimerSpinlock);
98		restore_interrupts(cpu);
99
100		// execute timer hook
101		if (timeout < now) {
102			ASSERT(func);
103			func(cookie);
104			continue;
105		}
106
107		status = acquire_sem_etc(sTimerSem, 1, B_ABSOLUTE_TIMEOUT, timeout);
108	} while (status != B_BAD_SEM_ID);
109
110	return 0;
111}
112
113
114timer_id
115create_timer(timer_function func, void *cookie, bigtime_t interval, uint32 flags)
116{
117	cpu_status cpu;
118	timer_id id;
119
120	if (func == 0)
121		return -1;
122
123	// Attention: flags are not real flags, as B_PERIODIC_TIMER is 3
124
125	cpu = disable_interrupts();
126	acquire_spinlock(&sTimerSpinlock);
127
128	if (sTimerCount < MAX_TIMERS) {
129		id = sTimerNextId;
130		sTimerData[sTimerCount].id = id;
131		sTimerData[sTimerCount].func = func;
132		sTimerData[sTimerCount].cookie = cookie;
133		sTimerData[sTimerCount].next_event = (flags == B_ONE_SHOT_ABSOLUTE_TIMER) ? interval : system_time() + interval;
134		sTimerData[sTimerCount].interval = interval;
135		sTimerData[sTimerCount].periodic = flags == B_PERIODIC_TIMER;
136		sTimerNextId++;
137		sTimerCount++;
138	} else {
139		id = -1;
140	}
141
142	release_spinlock(&sTimerSpinlock);
143	restore_interrupts(cpu);
144
145	if (id != -1)
146		release_sem_etc(sTimerSem, 1, B_DO_NOT_RESCHEDULE);
147
148	return id;
149}
150
151
152status_t
153delete_timer(timer_id id)
154{
155	cpu_status cpu;
156	bool deleted;
157	int i;
158
159	deleted = false;
160
161	cpu = disable_interrupts();
162	acquire_spinlock(&sTimerSpinlock);
163
164	for (i = 0; i < sTimerCount; i++) {
165		if (sTimerData[i].id == id) {
166			if (i != (sTimerCount - 1) && sTimerCount != 1) {
167				memcpy(&sTimerData[i], &sTimerData[sTimerCount - 1], sizeof(struct timer_info));
168			}
169			sTimerCount--;
170			deleted = true;
171			break;
172		}
173	}
174
175	release_spinlock(&sTimerSpinlock);
176	restore_interrupts(cpu);
177
178	if (!deleted)
179		return B_ERROR;
180
181	release_sem_etc(sTimerSem, 1, B_DO_NOT_RESCHEDULE);
182	return B_OK;
183}
184
185
186status_t
187initialize_timer(void)
188{
189	sTimerCount = 0;
190	sTimerNextId = 1;
191	B_INITIALIZE_SPINLOCK(&sTimerSpinlock);
192
193	sTimerThread = spawn_kernel_thread(timer_thread, "firewire timer", 80, 0);
194	sTimerSem = create_sem(0, "firewire timer");
195	set_sem_owner(sTimerSem, B_SYSTEM_TEAM);
196
197	if (sTimerSem < 0 || sTimerThread < 0) {
198		delete_sem(sTimerSem);
199		kill_thread(sTimerThread);
200		return B_ERROR;
201	}
202
203	resume_thread(sTimerThread);
204	return B_OK;
205}
206
207
208status_t
209terminate_timer(void)
210{
211	status_t thread_return_value;
212
213	delete_sem(sTimerSem);
214	return wait_for_thread(sTimerThread, &thread_return_value);
215}
216
217