13bd69ad1SAndreas Eversberg /* 23bd69ad1SAndreas Eversberg * Copyright 2008 by Andreas Eversberg <andreas@eversberg.eu> 33bd69ad1SAndreas Eversberg * 43bd69ad1SAndreas Eversberg * This program is free software; you can redistribute it and/or modify 53bd69ad1SAndreas Eversberg * it under the terms of the GNU General Public License version 2 as 63bd69ad1SAndreas Eversberg * published by the Free Software Foundation. 73bd69ad1SAndreas Eversberg * 83bd69ad1SAndreas Eversberg * This program is distributed in the hope that it will be useful, 93bd69ad1SAndreas Eversberg * but WITHOUT ANY WARRANTY; without even the implied warranty of 103bd69ad1SAndreas Eversberg * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 113bd69ad1SAndreas Eversberg * GNU General Public License for more details. 123bd69ad1SAndreas Eversberg * 133bd69ad1SAndreas Eversberg * Quick API description: 143bd69ad1SAndreas Eversberg * 153bd69ad1SAndreas Eversberg * A clock source registers using mISDN_register_clock: 163bd69ad1SAndreas Eversberg * name = text string to name clock source 173bd69ad1SAndreas Eversberg * priority = value to priorize clock sources (0 = default) 183bd69ad1SAndreas Eversberg * ctl = callback function to enable/disable clock source 193bd69ad1SAndreas Eversberg * priv = private pointer of clock source 203bd69ad1SAndreas Eversberg * return = pointer to clock source structure; 213bd69ad1SAndreas Eversberg * 223bd69ad1SAndreas Eversberg * Note: Callback 'ctl' can be called before mISDN_register_clock returns! 233bd69ad1SAndreas Eversberg * Also it can be called during mISDN_unregister_clock. 243bd69ad1SAndreas Eversberg * 253bd69ad1SAndreas Eversberg * A clock source calls mISDN_clock_update with given samples elapsed, if 263bd69ad1SAndreas Eversberg * enabled. If function call is delayed, tv must be set with the timestamp 273bd69ad1SAndreas Eversberg * of the actual event. 283bd69ad1SAndreas Eversberg * 293bd69ad1SAndreas Eversberg * A clock source unregisters using mISDN_unregister_clock. 303bd69ad1SAndreas Eversberg * 313bd69ad1SAndreas Eversberg * To get current clock, call mISDN_clock_get. The signed short value 323bd69ad1SAndreas Eversberg * counts the number of samples since. Time since last clock event is added. 333bd69ad1SAndreas Eversberg * 343bd69ad1SAndreas Eversberg */ 353bd69ad1SAndreas Eversberg 365a0e3ad6STejun Heo #include <linux/slab.h> 373bd69ad1SAndreas Eversberg #include <linux/types.h> 383bd69ad1SAndreas Eversberg #include <linux/stddef.h> 393bd69ad1SAndreas Eversberg #include <linux/spinlock.h> 403bd69ad1SAndreas Eversberg #include <linux/mISDNif.h> 41*5d76fc21SPaul Gortmaker #include <linux/export.h> 423bd69ad1SAndreas Eversberg #include "core.h" 433bd69ad1SAndreas Eversberg 443bd69ad1SAndreas Eversberg static u_int *debug; 453bd69ad1SAndreas Eversberg static LIST_HEAD(iclock_list); 46f8532fdeSHannes Eder static DEFINE_RWLOCK(iclock_lock); 47f8532fdeSHannes Eder static u16 iclock_count; /* counter of last clock */ 48f8532fdeSHannes Eder static struct timeval iclock_tv; /* time stamp of last clock */ 49f8532fdeSHannes Eder static int iclock_tv_valid; /* already received one timestamp */ 50f8532fdeSHannes Eder static struct mISDNclock *iclock_current; 513bd69ad1SAndreas Eversberg 523bd69ad1SAndreas Eversberg void 533bd69ad1SAndreas Eversberg mISDN_init_clock(u_int *dp) 543bd69ad1SAndreas Eversberg { 553bd69ad1SAndreas Eversberg debug = dp; 563bd69ad1SAndreas Eversberg do_gettimeofday(&iclock_tv); 573bd69ad1SAndreas Eversberg } 583bd69ad1SAndreas Eversberg 593bd69ad1SAndreas Eversberg static void 603bd69ad1SAndreas Eversberg select_iclock(void) 613bd69ad1SAndreas Eversberg { 623bd69ad1SAndreas Eversberg struct mISDNclock *iclock, *bestclock = NULL, *lastclock = NULL; 633bd69ad1SAndreas Eversberg int pri = -128; 643bd69ad1SAndreas Eversberg 653bd69ad1SAndreas Eversberg list_for_each_entry(iclock, &iclock_list, list) { 663bd69ad1SAndreas Eversberg if (iclock->pri > pri) { 673bd69ad1SAndreas Eversberg pri = iclock->pri; 683bd69ad1SAndreas Eversberg bestclock = iclock; 693bd69ad1SAndreas Eversberg } 703bd69ad1SAndreas Eversberg if (iclock_current == iclock) 713bd69ad1SAndreas Eversberg lastclock = iclock; 723bd69ad1SAndreas Eversberg } 733bd69ad1SAndreas Eversberg if (lastclock && bestclock != lastclock) { 743bd69ad1SAndreas Eversberg /* last used clock source still exists but changes, disable */ 753bd69ad1SAndreas Eversberg if (*debug & DEBUG_CLOCK) 763bd69ad1SAndreas Eversberg printk(KERN_DEBUG "Old clock source '%s' disable.\n", 773bd69ad1SAndreas Eversberg lastclock->name); 783bd69ad1SAndreas Eversberg lastclock->ctl(lastclock->priv, 0); 793bd69ad1SAndreas Eversberg } 803bd69ad1SAndreas Eversberg if (bestclock && bestclock != iclock_current) { 813bd69ad1SAndreas Eversberg /* new clock source selected, enable */ 823bd69ad1SAndreas Eversberg if (*debug & DEBUG_CLOCK) 833bd69ad1SAndreas Eversberg printk(KERN_DEBUG "New clock source '%s' enable.\n", 843bd69ad1SAndreas Eversberg bestclock->name); 853bd69ad1SAndreas Eversberg bestclock->ctl(bestclock->priv, 1); 863bd69ad1SAndreas Eversberg } 873bd69ad1SAndreas Eversberg if (bestclock != iclock_current) { 883bd69ad1SAndreas Eversberg /* no clock received yet */ 893bd69ad1SAndreas Eversberg iclock_tv_valid = 0; 903bd69ad1SAndreas Eversberg } 913bd69ad1SAndreas Eversberg iclock_current = bestclock; 923bd69ad1SAndreas Eversberg } 933bd69ad1SAndreas Eversberg 943bd69ad1SAndreas Eversberg struct mISDNclock 953bd69ad1SAndreas Eversberg *mISDN_register_clock(char *name, int pri, clockctl_func_t *ctl, void *priv) 963bd69ad1SAndreas Eversberg { 973bd69ad1SAndreas Eversberg u_long flags; 983bd69ad1SAndreas Eversberg struct mISDNclock *iclock; 993bd69ad1SAndreas Eversberg 1003bd69ad1SAndreas Eversberg if (*debug & (DEBUG_CORE | DEBUG_CLOCK)) 1013bd69ad1SAndreas Eversberg printk(KERN_DEBUG "%s: %s %d\n", __func__, name, pri); 1023bd69ad1SAndreas Eversberg iclock = kzalloc(sizeof(struct mISDNclock), GFP_ATOMIC); 1033bd69ad1SAndreas Eversberg if (!iclock) { 1043bd69ad1SAndreas Eversberg printk(KERN_ERR "%s: No memory for clock entry.\n", __func__); 1053bd69ad1SAndreas Eversberg return NULL; 1063bd69ad1SAndreas Eversberg } 1073bd69ad1SAndreas Eversberg strncpy(iclock->name, name, sizeof(iclock->name)-1); 1083bd69ad1SAndreas Eversberg iclock->pri = pri; 1093bd69ad1SAndreas Eversberg iclock->priv = priv; 1103bd69ad1SAndreas Eversberg iclock->ctl = ctl; 1113bd69ad1SAndreas Eversberg write_lock_irqsave(&iclock_lock, flags); 1123bd69ad1SAndreas Eversberg list_add_tail(&iclock->list, &iclock_list); 1133bd69ad1SAndreas Eversberg select_iclock(); 1143bd69ad1SAndreas Eversberg write_unlock_irqrestore(&iclock_lock, flags); 1153bd69ad1SAndreas Eversberg return iclock; 1163bd69ad1SAndreas Eversberg } 1173bd69ad1SAndreas Eversberg EXPORT_SYMBOL(mISDN_register_clock); 1183bd69ad1SAndreas Eversberg 1193bd69ad1SAndreas Eversberg void 1203bd69ad1SAndreas Eversberg mISDN_unregister_clock(struct mISDNclock *iclock) 1213bd69ad1SAndreas Eversberg { 1223bd69ad1SAndreas Eversberg u_long flags; 1233bd69ad1SAndreas Eversberg 1243bd69ad1SAndreas Eversberg if (*debug & (DEBUG_CORE | DEBUG_CLOCK)) 1253bd69ad1SAndreas Eversberg printk(KERN_DEBUG "%s: %s %d\n", __func__, iclock->name, 1263bd69ad1SAndreas Eversberg iclock->pri); 1273bd69ad1SAndreas Eversberg write_lock_irqsave(&iclock_lock, flags); 1283bd69ad1SAndreas Eversberg if (iclock_current == iclock) { 1293bd69ad1SAndreas Eversberg if (*debug & DEBUG_CLOCK) 1303bd69ad1SAndreas Eversberg printk(KERN_DEBUG 1313bd69ad1SAndreas Eversberg "Current clock source '%s' unregisters.\n", 1323bd69ad1SAndreas Eversberg iclock->name); 1333bd69ad1SAndreas Eversberg iclock->ctl(iclock->priv, 0); 1343bd69ad1SAndreas Eversberg } 1353bd69ad1SAndreas Eversberg list_del(&iclock->list); 1363bd69ad1SAndreas Eversberg select_iclock(); 1373bd69ad1SAndreas Eversberg write_unlock_irqrestore(&iclock_lock, flags); 1383bd69ad1SAndreas Eversberg } 1393bd69ad1SAndreas Eversberg EXPORT_SYMBOL(mISDN_unregister_clock); 1403bd69ad1SAndreas Eversberg 1413bd69ad1SAndreas Eversberg void 1423bd69ad1SAndreas Eversberg mISDN_clock_update(struct mISDNclock *iclock, int samples, struct timeval *tv) 1433bd69ad1SAndreas Eversberg { 1443bd69ad1SAndreas Eversberg u_long flags; 1453bd69ad1SAndreas Eversberg struct timeval tv_now; 1463bd69ad1SAndreas Eversberg time_t elapsed_sec; 1473bd69ad1SAndreas Eversberg int elapsed_8000th; 1483bd69ad1SAndreas Eversberg 1493bd69ad1SAndreas Eversberg write_lock_irqsave(&iclock_lock, flags); 1503bd69ad1SAndreas Eversberg if (iclock_current != iclock) { 1513bd69ad1SAndreas Eversberg printk(KERN_ERR "%s: '%s' sends us clock updates, but we do " 1523bd69ad1SAndreas Eversberg "listen to '%s'. This is a bug!\n", __func__, 1533bd69ad1SAndreas Eversberg iclock->name, 1543bd69ad1SAndreas Eversberg iclock_current ? iclock_current->name : "nothing"); 1553bd69ad1SAndreas Eversberg iclock->ctl(iclock->priv, 0); 1563bd69ad1SAndreas Eversberg write_unlock_irqrestore(&iclock_lock, flags); 1573bd69ad1SAndreas Eversberg return; 1583bd69ad1SAndreas Eversberg } 1593bd69ad1SAndreas Eversberg if (iclock_tv_valid) { 1603bd69ad1SAndreas Eversberg /* increment sample counter by given samples */ 1613bd69ad1SAndreas Eversberg iclock_count += samples; 1623bd69ad1SAndreas Eversberg if (tv) { /* tv must be set, if function call is delayed */ 1633bd69ad1SAndreas Eversberg iclock_tv.tv_sec = tv->tv_sec; 1643bd69ad1SAndreas Eversberg iclock_tv.tv_usec = tv->tv_usec; 1653bd69ad1SAndreas Eversberg } else 1663bd69ad1SAndreas Eversberg do_gettimeofday(&iclock_tv); 1673bd69ad1SAndreas Eversberg } else { 1683bd69ad1SAndreas Eversberg /* calc elapsed time by system clock */ 1693bd69ad1SAndreas Eversberg if (tv) { /* tv must be set, if function call is delayed */ 1703bd69ad1SAndreas Eversberg tv_now.tv_sec = tv->tv_sec; 1713bd69ad1SAndreas Eversberg tv_now.tv_usec = tv->tv_usec; 1723bd69ad1SAndreas Eversberg } else 1733bd69ad1SAndreas Eversberg do_gettimeofday(&tv_now); 1743bd69ad1SAndreas Eversberg elapsed_sec = tv_now.tv_sec - iclock_tv.tv_sec; 1753bd69ad1SAndreas Eversberg elapsed_8000th = (tv_now.tv_usec / 125) 1763bd69ad1SAndreas Eversberg - (iclock_tv.tv_usec / 125); 1773bd69ad1SAndreas Eversberg if (elapsed_8000th < 0) { 1783bd69ad1SAndreas Eversberg elapsed_sec -= 1; 1793bd69ad1SAndreas Eversberg elapsed_8000th += 8000; 1803bd69ad1SAndreas Eversberg } 1813bd69ad1SAndreas Eversberg /* add elapsed time to counter and set new timestamp */ 1823bd69ad1SAndreas Eversberg iclock_count += elapsed_sec * 8000 + elapsed_8000th; 1833bd69ad1SAndreas Eversberg iclock_tv.tv_sec = tv_now.tv_sec; 1843bd69ad1SAndreas Eversberg iclock_tv.tv_usec = tv_now.tv_usec; 1853bd69ad1SAndreas Eversberg iclock_tv_valid = 1; 1863bd69ad1SAndreas Eversberg if (*debug & DEBUG_CLOCK) 1873bd69ad1SAndreas Eversberg printk("Received first clock from source '%s'.\n", 1883bd69ad1SAndreas Eversberg iclock_current ? iclock_current->name : "nothing"); 1893bd69ad1SAndreas Eversberg } 1903bd69ad1SAndreas Eversberg write_unlock_irqrestore(&iclock_lock, flags); 1913bd69ad1SAndreas Eversberg } 1923bd69ad1SAndreas Eversberg EXPORT_SYMBOL(mISDN_clock_update); 1933bd69ad1SAndreas Eversberg 1943bd69ad1SAndreas Eversberg unsigned short 1953bd69ad1SAndreas Eversberg mISDN_clock_get(void) 1963bd69ad1SAndreas Eversberg { 1973bd69ad1SAndreas Eversberg u_long flags; 1983bd69ad1SAndreas Eversberg struct timeval tv_now; 1993bd69ad1SAndreas Eversberg time_t elapsed_sec; 2003bd69ad1SAndreas Eversberg int elapsed_8000th; 2013bd69ad1SAndreas Eversberg u16 count; 2023bd69ad1SAndreas Eversberg 2033bd69ad1SAndreas Eversberg read_lock_irqsave(&iclock_lock, flags); 2043bd69ad1SAndreas Eversberg /* calc elapsed time by system clock */ 2053bd69ad1SAndreas Eversberg do_gettimeofday(&tv_now); 2063bd69ad1SAndreas Eversberg elapsed_sec = tv_now.tv_sec - iclock_tv.tv_sec; 2073bd69ad1SAndreas Eversberg elapsed_8000th = (tv_now.tv_usec / 125) - (iclock_tv.tv_usec / 125); 2083bd69ad1SAndreas Eversberg if (elapsed_8000th < 0) { 2093bd69ad1SAndreas Eversberg elapsed_sec -= 1; 2103bd69ad1SAndreas Eversberg elapsed_8000th += 8000; 2113bd69ad1SAndreas Eversberg } 2123bd69ad1SAndreas Eversberg /* add elapsed time to counter */ 2133bd69ad1SAndreas Eversberg count = iclock_count + elapsed_sec * 8000 + elapsed_8000th; 2143bd69ad1SAndreas Eversberg read_unlock_irqrestore(&iclock_lock, flags); 2153bd69ad1SAndreas Eversberg return count; 2163bd69ad1SAndreas Eversberg } 2173bd69ad1SAndreas Eversberg EXPORT_SYMBOL(mISDN_clock_get); 2183bd69ad1SAndreas Eversberg 219