clock.c 6.2 KB

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