1 /* 2 * Copyright 2008 by Andreas Eversberg <andreas@eversberg.eu> 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/types.h> 37 #include <linux/stddef.h> 38 #include <linux/spinlock.h> 39 #include <linux/mISDNif.h> 40 #include "core.h" 41 42 static u_int *debug; 43 static LIST_HEAD(iclock_list); 44 static DEFINE_RWLOCK(iclock_lock); 45 static u16 iclock_count; /* counter of last clock */ 46 static struct timeval iclock_tv; /* time stamp of last clock */ 47 static int iclock_tv_valid; /* already received one timestamp */ 48 static struct mISDNclock *iclock_current; 49 50 void 51 mISDN_init_clock(u_int *dp) 52 { 53 debug = dp; 54 do_gettimeofday(&iclock_tv); 55 } 56 57 static void 58 select_iclock(void) 59 { 60 struct mISDNclock *iclock, *bestclock = NULL, *lastclock = NULL; 61 int pri = -128; 62 63 list_for_each_entry(iclock, &iclock_list, list) { 64 if (iclock->pri > pri) { 65 pri = iclock->pri; 66 bestclock = iclock; 67 } 68 if (iclock_current == iclock) 69 lastclock = iclock; 70 } 71 if (lastclock && bestclock != lastclock) { 72 /* last used clock source still exists but changes, disable */ 73 if (*debug & DEBUG_CLOCK) 74 printk(KERN_DEBUG "Old clock source '%s' disable.\n", 75 lastclock->name); 76 lastclock->ctl(lastclock->priv, 0); 77 } 78 if (bestclock && bestclock != iclock_current) { 79 /* new clock source selected, enable */ 80 if (*debug & DEBUG_CLOCK) 81 printk(KERN_DEBUG "New clock source '%s' enable.\n", 82 bestclock->name); 83 bestclock->ctl(bestclock->priv, 1); 84 } 85 if (bestclock != iclock_current) { 86 /* no clock received yet */ 87 iclock_tv_valid = 0; 88 } 89 iclock_current = bestclock; 90 } 91 92 struct mISDNclock 93 *mISDN_register_clock(char *name, int pri, clockctl_func_t *ctl, void *priv) 94 { 95 u_long flags; 96 struct mISDNclock *iclock; 97 98 if (*debug & (DEBUG_CORE | DEBUG_CLOCK)) 99 printk(KERN_DEBUG "%s: %s %d\n", __func__, name, pri); 100 iclock = kzalloc(sizeof(struct mISDNclock), GFP_ATOMIC); 101 if (!iclock) { 102 printk(KERN_ERR "%s: No memory for clock entry.\n", __func__); 103 return NULL; 104 } 105 strncpy(iclock->name, name, sizeof(iclock->name)-1); 106 iclock->pri = pri; 107 iclock->priv = priv; 108 iclock->ctl = ctl; 109 write_lock_irqsave(&iclock_lock, flags); 110 list_add_tail(&iclock->list, &iclock_list); 111 select_iclock(); 112 write_unlock_irqrestore(&iclock_lock, flags); 113 return iclock; 114 } 115 EXPORT_SYMBOL(mISDN_register_clock); 116 117 void 118 mISDN_unregister_clock(struct mISDNclock *iclock) 119 { 120 u_long flags; 121 122 if (*debug & (DEBUG_CORE | DEBUG_CLOCK)) 123 printk(KERN_DEBUG "%s: %s %d\n", __func__, iclock->name, 124 iclock->pri); 125 write_lock_irqsave(&iclock_lock, flags); 126 if (iclock_current == iclock) { 127 if (*debug & DEBUG_CLOCK) 128 printk(KERN_DEBUG 129 "Current clock source '%s' unregisters.\n", 130 iclock->name); 131 iclock->ctl(iclock->priv, 0); 132 } 133 list_del(&iclock->list); 134 select_iclock(); 135 write_unlock_irqrestore(&iclock_lock, flags); 136 } 137 EXPORT_SYMBOL(mISDN_unregister_clock); 138 139 void 140 mISDN_clock_update(struct mISDNclock *iclock, int samples, struct timeval *tv) 141 { 142 u_long flags; 143 struct timeval tv_now; 144 time_t elapsed_sec; 145 int elapsed_8000th; 146 147 write_lock_irqsave(&iclock_lock, flags); 148 if (iclock_current != iclock) { 149 printk(KERN_ERR "%s: '%s' sends us clock updates, but we do " 150 "listen to '%s'. This is a bug!\n", __func__, 151 iclock->name, 152 iclock_current ? iclock_current->name : "nothing"); 153 iclock->ctl(iclock->priv, 0); 154 write_unlock_irqrestore(&iclock_lock, flags); 155 return; 156 } 157 if (iclock_tv_valid) { 158 /* increment sample counter by given samples */ 159 iclock_count += samples; 160 if (tv) { /* tv must be set, if function call is delayed */ 161 iclock_tv.tv_sec = tv->tv_sec; 162 iclock_tv.tv_usec = tv->tv_usec; 163 } else 164 do_gettimeofday(&iclock_tv); 165 } else { 166 /* calc elapsed time by system clock */ 167 if (tv) { /* tv must be set, if function call is delayed */ 168 tv_now.tv_sec = tv->tv_sec; 169 tv_now.tv_usec = tv->tv_usec; 170 } else 171 do_gettimeofday(&tv_now); 172 elapsed_sec = tv_now.tv_sec - iclock_tv.tv_sec; 173 elapsed_8000th = (tv_now.tv_usec / 125) 174 - (iclock_tv.tv_usec / 125); 175 if (elapsed_8000th < 0) { 176 elapsed_sec -= 1; 177 elapsed_8000th += 8000; 178 } 179 /* add elapsed time to counter and set new timestamp */ 180 iclock_count += elapsed_sec * 8000 + elapsed_8000th; 181 iclock_tv.tv_sec = tv_now.tv_sec; 182 iclock_tv.tv_usec = tv_now.tv_usec; 183 iclock_tv_valid = 1; 184 if (*debug & DEBUG_CLOCK) 185 printk("Received first clock from source '%s'.\n", 186 iclock_current ? iclock_current->name : "nothing"); 187 } 188 write_unlock_irqrestore(&iclock_lock, flags); 189 } 190 EXPORT_SYMBOL(mISDN_clock_update); 191 192 unsigned short 193 mISDN_clock_get(void) 194 { 195 u_long flags; 196 struct timeval tv_now; 197 time_t elapsed_sec; 198 int elapsed_8000th; 199 u16 count; 200 201 read_lock_irqsave(&iclock_lock, flags); 202 /* calc elapsed time by system clock */ 203 do_gettimeofday(&tv_now); 204 elapsed_sec = tv_now.tv_sec - iclock_tv.tv_sec; 205 elapsed_8000th = (tv_now.tv_usec / 125) - (iclock_tv.tv_usec / 125); 206 if (elapsed_8000th < 0) { 207 elapsed_sec -= 1; 208 elapsed_8000th += 8000; 209 } 210 /* add elapsed time to counter */ 211 count = iclock_count + elapsed_sec * 8000 + elapsed_8000th; 212 read_unlock_irqrestore(&iclock_lock, flags); 213 return count; 214 } 215 EXPORT_SYMBOL(mISDN_clock_get); 216 217