Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright 2008  by Andreas Eversberg <andreas@eversberg.eu>
0004  *
0005  * Quick API description:
0006  *
0007  * A clock source registers using mISDN_register_clock:
0008  *  name = text string to name clock source
0009  *  priority = value to priorize clock sources (0 = default)
0010  *  ctl = callback function to enable/disable clock source
0011  *  priv = private pointer of clock source
0012  *  return = pointer to clock source structure;
0013  *
0014  * Note: Callback 'ctl' can be called before mISDN_register_clock returns!
0015  *       Also it can be called during mISDN_unregister_clock.
0016  *
0017  * A clock source calls mISDN_clock_update with given samples elapsed, if
0018  * enabled. If function call is delayed, tv must be set with the timestamp
0019  * of the actual event.
0020  *
0021  * A clock source unregisters using mISDN_unregister_clock.
0022  *
0023  * To get current clock, call mISDN_clock_get. The signed short value
0024  * counts the number of samples since. Time since last clock event is added.
0025  */
0026 
0027 #include <linux/slab.h>
0028 #include <linux/types.h>
0029 #include <linux/stddef.h>
0030 #include <linux/spinlock.h>
0031 #include <linux/ktime.h>
0032 #include <linux/mISDNif.h>
0033 #include <linux/export.h>
0034 #include "core.h"
0035 
0036 static u_int *debug;
0037 static LIST_HEAD(iclock_list);
0038 static DEFINE_RWLOCK(iclock_lock);
0039 static u16 iclock_count;        /* counter of last clock */
0040 static ktime_t iclock_timestamp;    /* time stamp of last clock */
0041 static int iclock_timestamp_valid;  /* already received one timestamp */
0042 static struct mISDNclock *iclock_current;
0043 
0044 void
0045 mISDN_init_clock(u_int *dp)
0046 {
0047     debug = dp;
0048     iclock_timestamp = ktime_get();
0049 }
0050 
0051 static void
0052 select_iclock(void)
0053 {
0054     struct mISDNclock *iclock, *bestclock = NULL, *lastclock = NULL;
0055     int pri = -128;
0056 
0057     list_for_each_entry(iclock, &iclock_list, list) {
0058         if (iclock->pri > pri) {
0059             pri = iclock->pri;
0060             bestclock = iclock;
0061         }
0062         if (iclock_current == iclock)
0063             lastclock = iclock;
0064     }
0065     if (lastclock && bestclock != lastclock) {
0066         /* last used clock source still exists but changes, disable */
0067         if (*debug & DEBUG_CLOCK)
0068             printk(KERN_DEBUG "Old clock source '%s' disable.\n",
0069                    lastclock->name);
0070         lastclock->ctl(lastclock->priv, 0);
0071     }
0072     if (bestclock && bestclock != iclock_current) {
0073         /* new clock source selected, enable */
0074         if (*debug & DEBUG_CLOCK)
0075             printk(KERN_DEBUG "New clock source '%s' enable.\n",
0076                    bestclock->name);
0077         bestclock->ctl(bestclock->priv, 1);
0078     }
0079     if (bestclock != iclock_current) {
0080         /* no clock received yet */
0081         iclock_timestamp_valid = 0;
0082     }
0083     iclock_current = bestclock;
0084 }
0085 
0086 struct mISDNclock
0087 *mISDN_register_clock(char *name, int pri, clockctl_func_t *ctl, void *priv)
0088 {
0089     u_long          flags;
0090     struct mISDNclock   *iclock;
0091 
0092     if (*debug & (DEBUG_CORE | DEBUG_CLOCK))
0093         printk(KERN_DEBUG "%s: %s %d\n", __func__, name, pri);
0094     iclock = kzalloc(sizeof(struct mISDNclock), GFP_ATOMIC);
0095     if (!iclock) {
0096         printk(KERN_ERR "%s: No memory for clock entry.\n", __func__);
0097         return NULL;
0098     }
0099     strncpy(iclock->name, name, sizeof(iclock->name) - 1);
0100     iclock->pri = pri;
0101     iclock->priv = priv;
0102     iclock->ctl = ctl;
0103     write_lock_irqsave(&iclock_lock, flags);
0104     list_add_tail(&iclock->list, &iclock_list);
0105     select_iclock();
0106     write_unlock_irqrestore(&iclock_lock, flags);
0107     return iclock;
0108 }
0109 EXPORT_SYMBOL(mISDN_register_clock);
0110 
0111 void
0112 mISDN_unregister_clock(struct mISDNclock *iclock)
0113 {
0114     u_long  flags;
0115 
0116     if (*debug & (DEBUG_CORE | DEBUG_CLOCK))
0117         printk(KERN_DEBUG "%s: %s %d\n", __func__, iclock->name,
0118                iclock->pri);
0119     write_lock_irqsave(&iclock_lock, flags);
0120     if (iclock_current == iclock) {
0121         if (*debug & DEBUG_CLOCK)
0122             printk(KERN_DEBUG
0123                    "Current clock source '%s' unregisters.\n",
0124                    iclock->name);
0125         iclock->ctl(iclock->priv, 0);
0126     }
0127     list_del(&iclock->list);
0128     select_iclock();
0129     write_unlock_irqrestore(&iclock_lock, flags);
0130 }
0131 EXPORT_SYMBOL(mISDN_unregister_clock);
0132 
0133 void
0134 mISDN_clock_update(struct mISDNclock *iclock, int samples, ktime_t *timestamp)
0135 {
0136     u_long      flags;
0137     ktime_t     timestamp_now;
0138     u16     delta;
0139 
0140     write_lock_irqsave(&iclock_lock, flags);
0141     if (iclock_current != iclock) {
0142         printk(KERN_ERR "%s: '%s' sends us clock updates, but we do "
0143                "listen to '%s'. This is a bug!\n", __func__,
0144                iclock->name,
0145                iclock_current ? iclock_current->name : "nothing");
0146         iclock->ctl(iclock->priv, 0);
0147         write_unlock_irqrestore(&iclock_lock, flags);
0148         return;
0149     }
0150     if (iclock_timestamp_valid) {
0151         /* increment sample counter by given samples */
0152         iclock_count += samples;
0153         if (timestamp) { /* timestamp must be set, if function call is delayed */
0154             iclock_timestamp = *timestamp;
0155         } else  {
0156             iclock_timestamp = ktime_get();
0157         }
0158     } else {
0159         /* calc elapsed time by system clock */
0160         if (timestamp) { /* timestamp must be set, if function call is delayed */
0161             timestamp_now = *timestamp;
0162         } else {
0163             timestamp_now = ktime_get();
0164         }
0165         delta = ktime_divns(ktime_sub(timestamp_now, iclock_timestamp),
0166                 (NSEC_PER_SEC / 8000));
0167         /* add elapsed time to counter and set new timestamp */
0168         iclock_count += delta;
0169         iclock_timestamp = timestamp_now;
0170         iclock_timestamp_valid = 1;
0171         if (*debug & DEBUG_CLOCK)
0172             printk("Received first clock from source '%s'.\n",
0173                    iclock_current ? iclock_current->name : "nothing");
0174     }
0175     write_unlock_irqrestore(&iclock_lock, flags);
0176 }
0177 EXPORT_SYMBOL(mISDN_clock_update);
0178 
0179 unsigned short
0180 mISDN_clock_get(void)
0181 {
0182     u_long      flags;
0183     ktime_t     timestamp_now;
0184     u16     delta;
0185     u16     count;
0186 
0187     read_lock_irqsave(&iclock_lock, flags);
0188     /* calc elapsed time by system clock */
0189     timestamp_now = ktime_get();
0190     delta = ktime_divns(ktime_sub(timestamp_now, iclock_timestamp),
0191             (NSEC_PER_SEC / 8000));
0192     /* add elapsed time to counter */
0193     count = iclock_count + delta;
0194     read_unlock_irqrestore(&iclock_lock, flags);
0195     return count;
0196 }
0197 EXPORT_SYMBOL(mISDN_clock_get);