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