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