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