linux/drivers/isdn/mISDN/clock.c
<<
>>
Prefs
   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
  42static u_int *debug;
  43static LIST_HEAD(iclock_list);
  44static DEFINE_RWLOCK(iclock_lock);
  45static u16 iclock_count;                /* counter of last clock */
  46static struct timeval iclock_tv;        /* time stamp of last clock */
  47static int iclock_tv_valid;             /* already received one timestamp */
  48static struct mISDNclock *iclock_current;
  49
  50void
  51mISDN_init_clock(u_int *dp)
  52{
  53        debug = dp;
  54        do_gettimeofday(&iclock_tv);
  55}
  56
  57static void
  58select_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
  92struct 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}
 115EXPORT_SYMBOL(mISDN_register_clock);
 116
 117void
 118mISDN_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}
 137EXPORT_SYMBOL(mISDN_unregister_clock);
 138
 139void
 140mISDN_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}
 190EXPORT_SYMBOL(mISDN_clock_update);
 191
 192unsigned short
 193mISDN_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}
 215EXPORT_SYMBOL(mISDN_clock_get);
 216
 217