Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/isdn/mISDN/clock.c
15111 views
1
/*
2
* Copyright 2008 by Andreas Eversberg <[email protected]>
3
*
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License version 2 as
6
* published by the Free Software Foundation.
7
*
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details.
12
*
13
* Quick API description:
14
*
15
* A clock source registers using mISDN_register_clock:
16
* name = text string to name clock source
17
* priority = value to priorize clock sources (0 = default)
18
* ctl = callback function to enable/disable clock source
19
* priv = private pointer of clock source
20
* return = pointer to clock source structure;
21
*
22
* Note: Callback 'ctl' can be called before mISDN_register_clock returns!
23
* Also it can be called during mISDN_unregister_clock.
24
*
25
* A clock source calls mISDN_clock_update with given samples elapsed, if
26
* enabled. If function call is delayed, tv must be set with the timestamp
27
* of the actual event.
28
*
29
* A clock source unregisters using mISDN_unregister_clock.
30
*
31
* To get current clock, call mISDN_clock_get. The signed short value
32
* counts the number of samples since. Time since last clock event is added.
33
*
34
*/
35
36
#include <linux/slab.h>
37
#include <linux/types.h>
38
#include <linux/stddef.h>
39
#include <linux/spinlock.h>
40
#include <linux/mISDNif.h>
41
#include "core.h"
42
43
static u_int *debug;
44
static LIST_HEAD(iclock_list);
45
static DEFINE_RWLOCK(iclock_lock);
46
static u16 iclock_count; /* counter of last clock */
47
static struct timeval iclock_tv; /* time stamp of last clock */
48
static int iclock_tv_valid; /* already received one timestamp */
49
static struct mISDNclock *iclock_current;
50
51
void
52
mISDN_init_clock(u_int *dp)
53
{
54
debug = dp;
55
do_gettimeofday(&iclock_tv);
56
}
57
58
static void
59
select_iclock(void)
60
{
61
struct mISDNclock *iclock, *bestclock = NULL, *lastclock = NULL;
62
int pri = -128;
63
64
list_for_each_entry(iclock, &iclock_list, list) {
65
if (iclock->pri > pri) {
66
pri = iclock->pri;
67
bestclock = iclock;
68
}
69
if (iclock_current == iclock)
70
lastclock = iclock;
71
}
72
if (lastclock && bestclock != lastclock) {
73
/* last used clock source still exists but changes, disable */
74
if (*debug & DEBUG_CLOCK)
75
printk(KERN_DEBUG "Old clock source '%s' disable.\n",
76
lastclock->name);
77
lastclock->ctl(lastclock->priv, 0);
78
}
79
if (bestclock && bestclock != iclock_current) {
80
/* new clock source selected, enable */
81
if (*debug & DEBUG_CLOCK)
82
printk(KERN_DEBUG "New clock source '%s' enable.\n",
83
bestclock->name);
84
bestclock->ctl(bestclock->priv, 1);
85
}
86
if (bestclock != iclock_current) {
87
/* no clock received yet */
88
iclock_tv_valid = 0;
89
}
90
iclock_current = bestclock;
91
}
92
93
struct mISDNclock
94
*mISDN_register_clock(char *name, int pri, clockctl_func_t *ctl, void *priv)
95
{
96
u_long flags;
97
struct mISDNclock *iclock;
98
99
if (*debug & (DEBUG_CORE | DEBUG_CLOCK))
100
printk(KERN_DEBUG "%s: %s %d\n", __func__, name, pri);
101
iclock = kzalloc(sizeof(struct mISDNclock), GFP_ATOMIC);
102
if (!iclock) {
103
printk(KERN_ERR "%s: No memory for clock entry.\n", __func__);
104
return NULL;
105
}
106
strncpy(iclock->name, name, sizeof(iclock->name)-1);
107
iclock->pri = pri;
108
iclock->priv = priv;
109
iclock->ctl = ctl;
110
write_lock_irqsave(&iclock_lock, flags);
111
list_add_tail(&iclock->list, &iclock_list);
112
select_iclock();
113
write_unlock_irqrestore(&iclock_lock, flags);
114
return iclock;
115
}
116
EXPORT_SYMBOL(mISDN_register_clock);
117
118
void
119
mISDN_unregister_clock(struct mISDNclock *iclock)
120
{
121
u_long flags;
122
123
if (*debug & (DEBUG_CORE | DEBUG_CLOCK))
124
printk(KERN_DEBUG "%s: %s %d\n", __func__, iclock->name,
125
iclock->pri);
126
write_lock_irqsave(&iclock_lock, flags);
127
if (iclock_current == iclock) {
128
if (*debug & DEBUG_CLOCK)
129
printk(KERN_DEBUG
130
"Current clock source '%s' unregisters.\n",
131
iclock->name);
132
iclock->ctl(iclock->priv, 0);
133
}
134
list_del(&iclock->list);
135
select_iclock();
136
write_unlock_irqrestore(&iclock_lock, flags);
137
}
138
EXPORT_SYMBOL(mISDN_unregister_clock);
139
140
void
141
mISDN_clock_update(struct mISDNclock *iclock, int samples, struct timeval *tv)
142
{
143
u_long flags;
144
struct timeval tv_now;
145
time_t elapsed_sec;
146
int elapsed_8000th;
147
148
write_lock_irqsave(&iclock_lock, flags);
149
if (iclock_current != iclock) {
150
printk(KERN_ERR "%s: '%s' sends us clock updates, but we do "
151
"listen to '%s'. This is a bug!\n", __func__,
152
iclock->name,
153
iclock_current ? iclock_current->name : "nothing");
154
iclock->ctl(iclock->priv, 0);
155
write_unlock_irqrestore(&iclock_lock, flags);
156
return;
157
}
158
if (iclock_tv_valid) {
159
/* increment sample counter by given samples */
160
iclock_count += samples;
161
if (tv) { /* tv must be set, if function call is delayed */
162
iclock_tv.tv_sec = tv->tv_sec;
163
iclock_tv.tv_usec = tv->tv_usec;
164
} else
165
do_gettimeofday(&iclock_tv);
166
} else {
167
/* calc elapsed time by system clock */
168
if (tv) { /* tv must be set, if function call is delayed */
169
tv_now.tv_sec = tv->tv_sec;
170
tv_now.tv_usec = tv->tv_usec;
171
} else
172
do_gettimeofday(&tv_now);
173
elapsed_sec = tv_now.tv_sec - iclock_tv.tv_sec;
174
elapsed_8000th = (tv_now.tv_usec / 125)
175
- (iclock_tv.tv_usec / 125);
176
if (elapsed_8000th < 0) {
177
elapsed_sec -= 1;
178
elapsed_8000th += 8000;
179
}
180
/* add elapsed time to counter and set new timestamp */
181
iclock_count += elapsed_sec * 8000 + elapsed_8000th;
182
iclock_tv.tv_sec = tv_now.tv_sec;
183
iclock_tv.tv_usec = tv_now.tv_usec;
184
iclock_tv_valid = 1;
185
if (*debug & DEBUG_CLOCK)
186
printk("Received first clock from source '%s'.\n",
187
iclock_current ? iclock_current->name : "nothing");
188
}
189
write_unlock_irqrestore(&iclock_lock, flags);
190
}
191
EXPORT_SYMBOL(mISDN_clock_update);
192
193
unsigned short
194
mISDN_clock_get(void)
195
{
196
u_long flags;
197
struct timeval tv_now;
198
time_t elapsed_sec;
199
int elapsed_8000th;
200
u16 count;
201
202
read_lock_irqsave(&iclock_lock, flags);
203
/* calc elapsed time by system clock */
204
do_gettimeofday(&tv_now);
205
elapsed_sec = tv_now.tv_sec - iclock_tv.tv_sec;
206
elapsed_8000th = (tv_now.tv_usec / 125) - (iclock_tv.tv_usec / 125);
207
if (elapsed_8000th < 0) {
208
elapsed_sec -= 1;
209
elapsed_8000th += 8000;
210
}
211
/* add elapsed time to counter */
212
count = iclock_count + elapsed_sec * 8000 + elapsed_8000th;
213
read_unlock_irqrestore(&iclock_lock, flags);
214
return count;
215
}
216
EXPORT_SYMBOL(mISDN_clock_get);
217
218
219