root/drivers/isdn/mISDN/clock.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mISDN_init_clock
  2. select_iclock
  3. mISDN_register_clock
  4. mISDN_unregister_clock
  5. mISDN_clock_update
  6. mISDN_clock_get

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Copyright 2008  by Andreas Eversberg <andreas@eversberg.eu>
   4  *
   5  * Quick API description:
   6  *
   7  * A clock source registers using mISDN_register_clock:
   8  *      name = text string to name clock source
   9  *      priority = value to priorize clock sources (0 = default)
  10  *      ctl = callback function to enable/disable clock source
  11  *      priv = private pointer of clock source
  12  *      return = pointer to clock source structure;
  13  *
  14  * Note: Callback 'ctl' can be called before mISDN_register_clock returns!
  15  *       Also it can be called during mISDN_unregister_clock.
  16  *
  17  * A clock source calls mISDN_clock_update with given samples elapsed, if
  18  * enabled. If function call is delayed, tv must be set with the timestamp
  19  * of the actual event.
  20  *
  21  * A clock source unregisters using mISDN_unregister_clock.
  22  *
  23  * To get current clock, call mISDN_clock_get. The signed short value
  24  * counts the number of samples since. Time since last clock event is added.
  25  */
  26 
  27 #include <linux/slab.h>
  28 #include <linux/types.h>
  29 #include <linux/stddef.h>
  30 #include <linux/spinlock.h>
  31 #include <linux/ktime.h>
  32 #include <linux/mISDNif.h>
  33 #include <linux/export.h>
  34 #include "core.h"
  35 
  36 static u_int *debug;
  37 static LIST_HEAD(iclock_list);
  38 static DEFINE_RWLOCK(iclock_lock);
  39 static u16 iclock_count;                /* counter of last clock */
  40 static ktime_t iclock_timestamp;        /* time stamp of last clock */
  41 static int iclock_timestamp_valid;      /* already received one timestamp */
  42 static struct mISDNclock *iclock_current;
  43 
  44 void
  45 mISDN_init_clock(u_int *dp)
  46 {
  47         debug = dp;
  48         iclock_timestamp = ktime_get();
  49 }
  50 
  51 static void
  52 select_iclock(void)
  53 {
  54         struct mISDNclock *iclock, *bestclock = NULL, *lastclock = NULL;
  55         int pri = -128;
  56 
  57         list_for_each_entry(iclock, &iclock_list, list) {
  58                 if (iclock->pri > pri) {
  59                         pri = iclock->pri;
  60                         bestclock = iclock;
  61                 }
  62                 if (iclock_current == iclock)
  63                         lastclock = iclock;
  64         }
  65         if (lastclock && bestclock != lastclock) {
  66                 /* last used clock source still exists but changes, disable */
  67                 if (*debug & DEBUG_CLOCK)
  68                         printk(KERN_DEBUG "Old clock source '%s' disable.\n",
  69                                lastclock->name);
  70                 lastclock->ctl(lastclock->priv, 0);
  71         }
  72         if (bestclock && bestclock != iclock_current) {
  73                 /* new clock source selected, enable */
  74                 if (*debug & DEBUG_CLOCK)
  75                         printk(KERN_DEBUG "New clock source '%s' enable.\n",
  76                                bestclock->name);
  77                 bestclock->ctl(bestclock->priv, 1);
  78         }
  79         if (bestclock != iclock_current) {
  80                 /* no clock received yet */
  81                 iclock_timestamp_valid = 0;
  82         }
  83         iclock_current = bestclock;
  84 }
  85 
  86 struct mISDNclock
  87 *mISDN_register_clock(char *name, int pri, clockctl_func_t *ctl, void *priv)
  88 {
  89         u_long                  flags;
  90         struct mISDNclock       *iclock;
  91 
  92         if (*debug & (DEBUG_CORE | DEBUG_CLOCK))
  93                 printk(KERN_DEBUG "%s: %s %d\n", __func__, name, pri);
  94         iclock = kzalloc(sizeof(struct mISDNclock), GFP_ATOMIC);
  95         if (!iclock) {
  96                 printk(KERN_ERR "%s: No memory for clock entry.\n", __func__);
  97                 return NULL;
  98         }
  99         strncpy(iclock->name, name, sizeof(iclock->name) - 1);
 100         iclock->pri = pri;
 101         iclock->priv = priv;
 102         iclock->ctl = ctl;
 103         write_lock_irqsave(&iclock_lock, flags);
 104         list_add_tail(&iclock->list, &iclock_list);
 105         select_iclock();
 106         write_unlock_irqrestore(&iclock_lock, flags);
 107         return iclock;
 108 }
 109 EXPORT_SYMBOL(mISDN_register_clock);
 110 
 111 void
 112 mISDN_unregister_clock(struct mISDNclock *iclock)
 113 {
 114         u_long  flags;
 115 
 116         if (*debug & (DEBUG_CORE | DEBUG_CLOCK))
 117                 printk(KERN_DEBUG "%s: %s %d\n", __func__, iclock->name,
 118                        iclock->pri);
 119         write_lock_irqsave(&iclock_lock, flags);
 120         if (iclock_current == iclock) {
 121                 if (*debug & DEBUG_CLOCK)
 122                         printk(KERN_DEBUG
 123                                "Current clock source '%s' unregisters.\n",
 124                                iclock->name);
 125                 iclock->ctl(iclock->priv, 0);
 126         }
 127         list_del(&iclock->list);
 128         select_iclock();
 129         write_unlock_irqrestore(&iclock_lock, flags);
 130 }
 131 EXPORT_SYMBOL(mISDN_unregister_clock);
 132 
 133 void
 134 mISDN_clock_update(struct mISDNclock *iclock, int samples, ktime_t *timestamp)
 135 {
 136         u_long          flags;
 137         ktime_t         timestamp_now;
 138         u16             delta;
 139 
 140         write_lock_irqsave(&iclock_lock, flags);
 141         if (iclock_current != iclock) {
 142                 printk(KERN_ERR "%s: '%s' sends us clock updates, but we do "
 143                        "listen to '%s'. This is a bug!\n", __func__,
 144                        iclock->name,
 145                        iclock_current ? iclock_current->name : "nothing");
 146                 iclock->ctl(iclock->priv, 0);
 147                 write_unlock_irqrestore(&iclock_lock, flags);
 148                 return;
 149         }
 150         if (iclock_timestamp_valid) {
 151                 /* increment sample counter by given samples */
 152                 iclock_count += samples;
 153                 if (timestamp) { /* timestamp must be set, if function call is delayed */
 154                         iclock_timestamp = *timestamp;
 155                 } else  {
 156                         iclock_timestamp = ktime_get();
 157                 }
 158         } else {
 159                 /* calc elapsed time by system clock */
 160                 if (timestamp) { /* timestamp must be set, if function call is delayed */
 161                         timestamp_now = *timestamp;
 162                 } else {
 163                         timestamp_now = ktime_get();
 164                 }
 165                 delta = ktime_divns(ktime_sub(timestamp_now, iclock_timestamp),
 166                                 (NSEC_PER_SEC / 8000));
 167                 /* add elapsed time to counter and set new timestamp */
 168                 iclock_count += delta;
 169                 iclock_timestamp = timestamp_now;
 170                 iclock_timestamp_valid = 1;
 171                 if (*debug & DEBUG_CLOCK)
 172                         printk("Received first clock from source '%s'.\n",
 173                                iclock_current ? iclock_current->name : "nothing");
 174         }
 175         write_unlock_irqrestore(&iclock_lock, flags);
 176 }
 177 EXPORT_SYMBOL(mISDN_clock_update);
 178 
 179 unsigned short
 180 mISDN_clock_get(void)
 181 {
 182         u_long          flags;
 183         ktime_t         timestamp_now;
 184         u16             delta;
 185         u16             count;
 186 
 187         read_lock_irqsave(&iclock_lock, flags);
 188         /* calc elapsed time by system clock */
 189         timestamp_now = ktime_get();
 190         delta = ktime_divns(ktime_sub(timestamp_now, iclock_timestamp),
 191                         (NSEC_PER_SEC / 8000));
 192         /* add elapsed time to counter */
 193         count = iclock_count + delta;
 194         read_unlock_irqrestore(&iclock_lock, flags);
 195         return count;
 196 }
 197 EXPORT_SYMBOL(mISDN_clock_get);

/* [<][>][^][v][top][bottom][index][help] */