1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright 2008 by Andreas Eversberg <andreas@eversberg.eu>
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Quick API description:
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * A clock source registers using mISDN_register_clock:
8*4882a593Smuzhiyun * name = text string to name clock source
9*4882a593Smuzhiyun * priority = value to priorize clock sources (0 = default)
10*4882a593Smuzhiyun * ctl = callback function to enable/disable clock source
11*4882a593Smuzhiyun * priv = private pointer of clock source
12*4882a593Smuzhiyun * return = pointer to clock source structure;
13*4882a593Smuzhiyun *
14*4882a593Smuzhiyun * Note: Callback 'ctl' can be called before mISDN_register_clock returns!
15*4882a593Smuzhiyun * Also it can be called during mISDN_unregister_clock.
16*4882a593Smuzhiyun *
17*4882a593Smuzhiyun * A clock source calls mISDN_clock_update with given samples elapsed, if
18*4882a593Smuzhiyun * enabled. If function call is delayed, tv must be set with the timestamp
19*4882a593Smuzhiyun * of the actual event.
20*4882a593Smuzhiyun *
21*4882a593Smuzhiyun * A clock source unregisters using mISDN_unregister_clock.
22*4882a593Smuzhiyun *
23*4882a593Smuzhiyun * To get current clock, call mISDN_clock_get. The signed short value
24*4882a593Smuzhiyun * counts the number of samples since. Time since last clock event is added.
25*4882a593Smuzhiyun */
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun #include <linux/slab.h>
28*4882a593Smuzhiyun #include <linux/types.h>
29*4882a593Smuzhiyun #include <linux/stddef.h>
30*4882a593Smuzhiyun #include <linux/spinlock.h>
31*4882a593Smuzhiyun #include <linux/ktime.h>
32*4882a593Smuzhiyun #include <linux/mISDNif.h>
33*4882a593Smuzhiyun #include <linux/export.h>
34*4882a593Smuzhiyun #include "core.h"
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun static u_int *debug;
37*4882a593Smuzhiyun static LIST_HEAD(iclock_list);
38*4882a593Smuzhiyun static DEFINE_RWLOCK(iclock_lock);
39*4882a593Smuzhiyun static u16 iclock_count; /* counter of last clock */
40*4882a593Smuzhiyun static ktime_t iclock_timestamp; /* time stamp of last clock */
41*4882a593Smuzhiyun static int iclock_timestamp_valid; /* already received one timestamp */
42*4882a593Smuzhiyun static struct mISDNclock *iclock_current;
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun void
mISDN_init_clock(u_int * dp)45*4882a593Smuzhiyun mISDN_init_clock(u_int *dp)
46*4882a593Smuzhiyun {
47*4882a593Smuzhiyun debug = dp;
48*4882a593Smuzhiyun iclock_timestamp = ktime_get();
49*4882a593Smuzhiyun }
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun static void
select_iclock(void)52*4882a593Smuzhiyun select_iclock(void)
53*4882a593Smuzhiyun {
54*4882a593Smuzhiyun struct mISDNclock *iclock, *bestclock = NULL, *lastclock = NULL;
55*4882a593Smuzhiyun int pri = -128;
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun list_for_each_entry(iclock, &iclock_list, list) {
58*4882a593Smuzhiyun if (iclock->pri > pri) {
59*4882a593Smuzhiyun pri = iclock->pri;
60*4882a593Smuzhiyun bestclock = iclock;
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun if (iclock_current == iclock)
63*4882a593Smuzhiyun lastclock = iclock;
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun if (lastclock && bestclock != lastclock) {
66*4882a593Smuzhiyun /* last used clock source still exists but changes, disable */
67*4882a593Smuzhiyun if (*debug & DEBUG_CLOCK)
68*4882a593Smuzhiyun printk(KERN_DEBUG "Old clock source '%s' disable.\n",
69*4882a593Smuzhiyun lastclock->name);
70*4882a593Smuzhiyun lastclock->ctl(lastclock->priv, 0);
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun if (bestclock && bestclock != iclock_current) {
73*4882a593Smuzhiyun /* new clock source selected, enable */
74*4882a593Smuzhiyun if (*debug & DEBUG_CLOCK)
75*4882a593Smuzhiyun printk(KERN_DEBUG "New clock source '%s' enable.\n",
76*4882a593Smuzhiyun bestclock->name);
77*4882a593Smuzhiyun bestclock->ctl(bestclock->priv, 1);
78*4882a593Smuzhiyun }
79*4882a593Smuzhiyun if (bestclock != iclock_current) {
80*4882a593Smuzhiyun /* no clock received yet */
81*4882a593Smuzhiyun iclock_timestamp_valid = 0;
82*4882a593Smuzhiyun }
83*4882a593Smuzhiyun iclock_current = bestclock;
84*4882a593Smuzhiyun }
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun struct mISDNclock
mISDN_register_clock(char * name,int pri,clockctl_func_t * ctl,void * priv)87*4882a593Smuzhiyun *mISDN_register_clock(char *name, int pri, clockctl_func_t *ctl, void *priv)
88*4882a593Smuzhiyun {
89*4882a593Smuzhiyun u_long flags;
90*4882a593Smuzhiyun struct mISDNclock *iclock;
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun if (*debug & (DEBUG_CORE | DEBUG_CLOCK))
93*4882a593Smuzhiyun printk(KERN_DEBUG "%s: %s %d\n", __func__, name, pri);
94*4882a593Smuzhiyun iclock = kzalloc(sizeof(struct mISDNclock), GFP_ATOMIC);
95*4882a593Smuzhiyun if (!iclock) {
96*4882a593Smuzhiyun printk(KERN_ERR "%s: No memory for clock entry.\n", __func__);
97*4882a593Smuzhiyun return NULL;
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun strncpy(iclock->name, name, sizeof(iclock->name) - 1);
100*4882a593Smuzhiyun iclock->pri = pri;
101*4882a593Smuzhiyun iclock->priv = priv;
102*4882a593Smuzhiyun iclock->ctl = ctl;
103*4882a593Smuzhiyun write_lock_irqsave(&iclock_lock, flags);
104*4882a593Smuzhiyun list_add_tail(&iclock->list, &iclock_list);
105*4882a593Smuzhiyun select_iclock();
106*4882a593Smuzhiyun write_unlock_irqrestore(&iclock_lock, flags);
107*4882a593Smuzhiyun return iclock;
108*4882a593Smuzhiyun }
109*4882a593Smuzhiyun EXPORT_SYMBOL(mISDN_register_clock);
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun void
mISDN_unregister_clock(struct mISDNclock * iclock)112*4882a593Smuzhiyun mISDN_unregister_clock(struct mISDNclock *iclock)
113*4882a593Smuzhiyun {
114*4882a593Smuzhiyun u_long flags;
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun if (*debug & (DEBUG_CORE | DEBUG_CLOCK))
117*4882a593Smuzhiyun printk(KERN_DEBUG "%s: %s %d\n", __func__, iclock->name,
118*4882a593Smuzhiyun iclock->pri);
119*4882a593Smuzhiyun write_lock_irqsave(&iclock_lock, flags);
120*4882a593Smuzhiyun if (iclock_current == iclock) {
121*4882a593Smuzhiyun if (*debug & DEBUG_CLOCK)
122*4882a593Smuzhiyun printk(KERN_DEBUG
123*4882a593Smuzhiyun "Current clock source '%s' unregisters.\n",
124*4882a593Smuzhiyun iclock->name);
125*4882a593Smuzhiyun iclock->ctl(iclock->priv, 0);
126*4882a593Smuzhiyun }
127*4882a593Smuzhiyun list_del(&iclock->list);
128*4882a593Smuzhiyun select_iclock();
129*4882a593Smuzhiyun write_unlock_irqrestore(&iclock_lock, flags);
130*4882a593Smuzhiyun }
131*4882a593Smuzhiyun EXPORT_SYMBOL(mISDN_unregister_clock);
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun void
mISDN_clock_update(struct mISDNclock * iclock,int samples,ktime_t * timestamp)134*4882a593Smuzhiyun mISDN_clock_update(struct mISDNclock *iclock, int samples, ktime_t *timestamp)
135*4882a593Smuzhiyun {
136*4882a593Smuzhiyun u_long flags;
137*4882a593Smuzhiyun ktime_t timestamp_now;
138*4882a593Smuzhiyun u16 delta;
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun write_lock_irqsave(&iclock_lock, flags);
141*4882a593Smuzhiyun if (iclock_current != iclock) {
142*4882a593Smuzhiyun printk(KERN_ERR "%s: '%s' sends us clock updates, but we do "
143*4882a593Smuzhiyun "listen to '%s'. This is a bug!\n", __func__,
144*4882a593Smuzhiyun iclock->name,
145*4882a593Smuzhiyun iclock_current ? iclock_current->name : "nothing");
146*4882a593Smuzhiyun iclock->ctl(iclock->priv, 0);
147*4882a593Smuzhiyun write_unlock_irqrestore(&iclock_lock, flags);
148*4882a593Smuzhiyun return;
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun if (iclock_timestamp_valid) {
151*4882a593Smuzhiyun /* increment sample counter by given samples */
152*4882a593Smuzhiyun iclock_count += samples;
153*4882a593Smuzhiyun if (timestamp) { /* timestamp must be set, if function call is delayed */
154*4882a593Smuzhiyun iclock_timestamp = *timestamp;
155*4882a593Smuzhiyun } else {
156*4882a593Smuzhiyun iclock_timestamp = ktime_get();
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun } else {
159*4882a593Smuzhiyun /* calc elapsed time by system clock */
160*4882a593Smuzhiyun if (timestamp) { /* timestamp must be set, if function call is delayed */
161*4882a593Smuzhiyun timestamp_now = *timestamp;
162*4882a593Smuzhiyun } else {
163*4882a593Smuzhiyun timestamp_now = ktime_get();
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun delta = ktime_divns(ktime_sub(timestamp_now, iclock_timestamp),
166*4882a593Smuzhiyun (NSEC_PER_SEC / 8000));
167*4882a593Smuzhiyun /* add elapsed time to counter and set new timestamp */
168*4882a593Smuzhiyun iclock_count += delta;
169*4882a593Smuzhiyun iclock_timestamp = timestamp_now;
170*4882a593Smuzhiyun iclock_timestamp_valid = 1;
171*4882a593Smuzhiyun if (*debug & DEBUG_CLOCK)
172*4882a593Smuzhiyun printk("Received first clock from source '%s'.\n",
173*4882a593Smuzhiyun iclock_current ? iclock_current->name : "nothing");
174*4882a593Smuzhiyun }
175*4882a593Smuzhiyun write_unlock_irqrestore(&iclock_lock, flags);
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun EXPORT_SYMBOL(mISDN_clock_update);
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun unsigned short
mISDN_clock_get(void)180*4882a593Smuzhiyun mISDN_clock_get(void)
181*4882a593Smuzhiyun {
182*4882a593Smuzhiyun u_long flags;
183*4882a593Smuzhiyun ktime_t timestamp_now;
184*4882a593Smuzhiyun u16 delta;
185*4882a593Smuzhiyun u16 count;
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun read_lock_irqsave(&iclock_lock, flags);
188*4882a593Smuzhiyun /* calc elapsed time by system clock */
189*4882a593Smuzhiyun timestamp_now = ktime_get();
190*4882a593Smuzhiyun delta = ktime_divns(ktime_sub(timestamp_now, iclock_timestamp),
191*4882a593Smuzhiyun (NSEC_PER_SEC / 8000));
192*4882a593Smuzhiyun /* add elapsed time to counter */
193*4882a593Smuzhiyun count = iclock_count + delta;
194*4882a593Smuzhiyun read_unlock_irqrestore(&iclock_lock, flags);
195*4882a593Smuzhiyun return count;
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun EXPORT_SYMBOL(mISDN_clock_get);
198