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