1/* Intel PRO/1000 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 "timer.h"
25
26#define MAX_TIMERS 4
27
28
29struct timer_info
30{
31	timer_id 		id;
32	timer_function	func;
33	void *			cookie;
34	bigtime_t		next_event;
35	bigtime_t		interval;
36	bool			periodic;
37};
38
39
40static struct timer_info	sTimerData[MAX_TIMERS];
41static int					sTimerCount;
42static timer_id				sTimerNextId;
43static thread_id			sTimerThread;
44static sem_id				sTimerSem;
45static spinlock				sTimerSpinlock;
46
47
48static int32
49timer_thread(void *cookie)
50{
51	status_t status = 0;
52
53	do {
54		bigtime_t timeout;
55		bigtime_t now;
56		cpu_status cpu;
57		timer_function func;
58		void * cookie;
59		int i;
60		int index;
61
62		cpu = disable_interrupts();
63		acquire_spinlock(&sTimerSpinlock);
64
65		now = system_time();
66		cookie = 0;
67		func = 0;
68
69		// find timer with smallest event time
70		index = -1;
71		timeout = B_INFINITE_TIMEOUT;
72		for (i = 0; i < sTimerCount; i++) {
73			if (sTimerData[i].next_event < timeout) {
74				timeout = sTimerData[i].next_event;
75				index = i;
76			}
77		}
78
79		if (timeout < now) {
80			// timer is ready for execution, load func and cookie
81			ASSERT(index >= 0 && index < sTimerCount);
82			func = sTimerData[index].func;
83			cookie = sTimerData[index].cookie;
84			if (sTimerData[index].periodic) {
85				// periodic timer is ready, update the entry
86				sTimerData[index].next_event += sTimerData[index].interval;
87			} else {
88				// single shot timer is ready, delete the entry
89				if (index != (sTimerCount - 1) && sTimerCount != 1) {
90					memcpy(&sTimerData[index], &sTimerData[sTimerCount - 1], sizeof(struct timer_info));
91				}
92				sTimerCount--;
93			}
94		}
95
96		release_spinlock(&sTimerSpinlock);
97		restore_interrupts(cpu);
98
99		// execute timer hook
100		if (timeout < now) {
101			ASSERT(func);
102			func(cookie);
103			continue;
104		}
105
106		status = acquire_sem_etc(sTimerSem, 1, B_ABSOLUTE_TIMEOUT, timeout);
107	} while (status != B_BAD_SEM_ID);
108
109	return 0;
110}
111
112
113timer_id
114create_timer(timer_function func, void *cookie, bigtime_t interval, uint32 flags)
115{
116	cpu_status cpu;
117	timer_id id;
118
119	if (func == 0)
120		return -1;
121
122	// Attention: flags are not real flags, as B_PERIODIC_TIMER is 3
123
124	cpu = disable_interrupts();
125	acquire_spinlock(&sTimerSpinlock);
126
127	if (sTimerCount < MAX_TIMERS) {
128		id = sTimerNextId;
129		sTimerData[sTimerCount].id = id;
130		sTimerData[sTimerCount].func = func;
131		sTimerData[sTimerCount].cookie = cookie;
132		sTimerData[sTimerCount].next_event = (flags == B_ONE_SHOT_ABSOLUTE_TIMER) ? interval : system_time() + interval;
133		sTimerData[sTimerCount].interval = interval;
134		sTimerData[sTimerCount].periodic = flags == B_PERIODIC_TIMER;
135		sTimerNextId++;
136		sTimerCount++;
137	} else {
138		id = -1;
139	}
140
141	release_spinlock(&sTimerSpinlock);
142	restore_interrupts(cpu);
143
144	if (id != -1)
145		release_sem_etc(sTimerSem, 1, B_DO_NOT_RESCHEDULE);
146
147	return id;
148}
149
150
151status_t
152delete_timer(timer_id id)
153{
154	cpu_status cpu;
155	bool deleted;
156	int i;
157
158	deleted = false;
159
160	cpu = disable_interrupts();
161	acquire_spinlock(&sTimerSpinlock);
162
163	for (i = 0; i < sTimerCount; i++) {
164		if (sTimerData[i].id == id) {
165			if (i != (sTimerCount - 1) && sTimerCount != 1) {
166				memcpy(&sTimerData[i], &sTimerData[sTimerCount - 1], sizeof(struct timer_info));
167			}
168			sTimerCount--;
169			deleted = true;
170			break;
171		}
172	}
173
174	release_spinlock(&sTimerSpinlock);
175	restore_interrupts(cpu);
176
177	if (!deleted)
178		return B_ERROR;
179
180	release_sem_etc(sTimerSem, 1, B_DO_NOT_RESCHEDULE);
181	return B_OK;
182}
183
184
185status_t
186initialize_timer(void)
187{
188	sTimerCount = 0;
189	sTimerNextId = 1;
190	sTimerSpinlock = 0;
191
192	sTimerThread = spawn_kernel_thread(timer_thread, "ipro1000 timer", 80, 0);
193	sTimerSem = create_sem(0, "ipro1000 timer");
194	set_sem_owner(sTimerSem, B_SYSTEM_TEAM);
195
196	if (sTimerSem < 0 || sTimerThread < 0) {
197		delete_sem(sTimerSem);
198		kill_thread(sTimerThread);
199		return B_ERROR;
200	}
201
202	resume_thread(sTimerThread);
203	return B_OK;
204}
205
206
207status_t
208terminate_timer(void)
209{
210	status_t thread_return_value;
211
212	delete_sem(sTimerSem);
213	return wait_for_thread(sTimerThread, &thread_return_value);
214}
215
216