18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2008 by Andreas Eversberg <andreas@eversberg.eu> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Quick API description: 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * A clock source registers using mISDN_register_clock: 88c2ecf20Sopenharmony_ci * name = text string to name clock source 98c2ecf20Sopenharmony_ci * priority = value to priorize clock sources (0 = default) 108c2ecf20Sopenharmony_ci * ctl = callback function to enable/disable clock source 118c2ecf20Sopenharmony_ci * priv = private pointer of clock source 128c2ecf20Sopenharmony_ci * return = pointer to clock source structure; 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Note: Callback 'ctl' can be called before mISDN_register_clock returns! 158c2ecf20Sopenharmony_ci * Also it can be called during mISDN_unregister_clock. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * A clock source calls mISDN_clock_update with given samples elapsed, if 188c2ecf20Sopenharmony_ci * enabled. If function call is delayed, tv must be set with the timestamp 198c2ecf20Sopenharmony_ci * of the actual event. 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * A clock source unregisters using mISDN_unregister_clock. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * To get current clock, call mISDN_clock_get. The signed short value 248c2ecf20Sopenharmony_ci * counts the number of samples since. Time since last clock event is added. 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <linux/slab.h> 288c2ecf20Sopenharmony_ci#include <linux/types.h> 298c2ecf20Sopenharmony_ci#include <linux/stddef.h> 308c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 318c2ecf20Sopenharmony_ci#include <linux/ktime.h> 328c2ecf20Sopenharmony_ci#include <linux/mISDNif.h> 338c2ecf20Sopenharmony_ci#include <linux/export.h> 348c2ecf20Sopenharmony_ci#include "core.h" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic u_int *debug; 378c2ecf20Sopenharmony_cistatic LIST_HEAD(iclock_list); 388c2ecf20Sopenharmony_cistatic DEFINE_RWLOCK(iclock_lock); 398c2ecf20Sopenharmony_cistatic u16 iclock_count; /* counter of last clock */ 408c2ecf20Sopenharmony_cistatic ktime_t iclock_timestamp; /* time stamp of last clock */ 418c2ecf20Sopenharmony_cistatic int iclock_timestamp_valid; /* already received one timestamp */ 428c2ecf20Sopenharmony_cistatic struct mISDNclock *iclock_current; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_civoid 458c2ecf20Sopenharmony_cimISDN_init_clock(u_int *dp) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci debug = dp; 488c2ecf20Sopenharmony_ci iclock_timestamp = ktime_get(); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic void 528c2ecf20Sopenharmony_ciselect_iclock(void) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct mISDNclock *iclock, *bestclock = NULL, *lastclock = NULL; 558c2ecf20Sopenharmony_ci int pri = -128; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci list_for_each_entry(iclock, &iclock_list, list) { 588c2ecf20Sopenharmony_ci if (iclock->pri > pri) { 598c2ecf20Sopenharmony_ci pri = iclock->pri; 608c2ecf20Sopenharmony_ci bestclock = iclock; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci if (iclock_current == iclock) 638c2ecf20Sopenharmony_ci lastclock = iclock; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci if (lastclock && bestclock != lastclock) { 668c2ecf20Sopenharmony_ci /* last used clock source still exists but changes, disable */ 678c2ecf20Sopenharmony_ci if (*debug & DEBUG_CLOCK) 688c2ecf20Sopenharmony_ci printk(KERN_DEBUG "Old clock source '%s' disable.\n", 698c2ecf20Sopenharmony_ci lastclock->name); 708c2ecf20Sopenharmony_ci lastclock->ctl(lastclock->priv, 0); 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci if (bestclock && bestclock != iclock_current) { 738c2ecf20Sopenharmony_ci /* new clock source selected, enable */ 748c2ecf20Sopenharmony_ci if (*debug & DEBUG_CLOCK) 758c2ecf20Sopenharmony_ci printk(KERN_DEBUG "New clock source '%s' enable.\n", 768c2ecf20Sopenharmony_ci bestclock->name); 778c2ecf20Sopenharmony_ci bestclock->ctl(bestclock->priv, 1); 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci if (bestclock != iclock_current) { 808c2ecf20Sopenharmony_ci /* no clock received yet */ 818c2ecf20Sopenharmony_ci iclock_timestamp_valid = 0; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci iclock_current = bestclock; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistruct mISDNclock 878c2ecf20Sopenharmony_ci*mISDN_register_clock(char *name, int pri, clockctl_func_t *ctl, void *priv) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci u_long flags; 908c2ecf20Sopenharmony_ci struct mISDNclock *iclock; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (*debug & (DEBUG_CORE | DEBUG_CLOCK)) 938c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: %s %d\n", __func__, name, pri); 948c2ecf20Sopenharmony_ci iclock = kzalloc(sizeof(struct mISDNclock), GFP_ATOMIC); 958c2ecf20Sopenharmony_ci if (!iclock) { 968c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: No memory for clock entry.\n", __func__); 978c2ecf20Sopenharmony_ci return NULL; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci strncpy(iclock->name, name, sizeof(iclock->name) - 1); 1008c2ecf20Sopenharmony_ci iclock->pri = pri; 1018c2ecf20Sopenharmony_ci iclock->priv = priv; 1028c2ecf20Sopenharmony_ci iclock->ctl = ctl; 1038c2ecf20Sopenharmony_ci write_lock_irqsave(&iclock_lock, flags); 1048c2ecf20Sopenharmony_ci list_add_tail(&iclock->list, &iclock_list); 1058c2ecf20Sopenharmony_ci select_iclock(); 1068c2ecf20Sopenharmony_ci write_unlock_irqrestore(&iclock_lock, flags); 1078c2ecf20Sopenharmony_ci return iclock; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDN_register_clock); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_civoid 1128c2ecf20Sopenharmony_cimISDN_unregister_clock(struct mISDNclock *iclock) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci u_long flags; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (*debug & (DEBUG_CORE | DEBUG_CLOCK)) 1178c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: %s %d\n", __func__, iclock->name, 1188c2ecf20Sopenharmony_ci iclock->pri); 1198c2ecf20Sopenharmony_ci write_lock_irqsave(&iclock_lock, flags); 1208c2ecf20Sopenharmony_ci if (iclock_current == iclock) { 1218c2ecf20Sopenharmony_ci if (*debug & DEBUG_CLOCK) 1228c2ecf20Sopenharmony_ci printk(KERN_DEBUG 1238c2ecf20Sopenharmony_ci "Current clock source '%s' unregisters.\n", 1248c2ecf20Sopenharmony_ci iclock->name); 1258c2ecf20Sopenharmony_ci iclock->ctl(iclock->priv, 0); 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci list_del(&iclock->list); 1288c2ecf20Sopenharmony_ci select_iclock(); 1298c2ecf20Sopenharmony_ci write_unlock_irqrestore(&iclock_lock, flags); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDN_unregister_clock); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_civoid 1348c2ecf20Sopenharmony_cimISDN_clock_update(struct mISDNclock *iclock, int samples, ktime_t *timestamp) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci u_long flags; 1378c2ecf20Sopenharmony_ci ktime_t timestamp_now; 1388c2ecf20Sopenharmony_ci u16 delta; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci write_lock_irqsave(&iclock_lock, flags); 1418c2ecf20Sopenharmony_ci if (iclock_current != iclock) { 1428c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: '%s' sends us clock updates, but we do " 1438c2ecf20Sopenharmony_ci "listen to '%s'. This is a bug!\n", __func__, 1448c2ecf20Sopenharmony_ci iclock->name, 1458c2ecf20Sopenharmony_ci iclock_current ? iclock_current->name : "nothing"); 1468c2ecf20Sopenharmony_ci iclock->ctl(iclock->priv, 0); 1478c2ecf20Sopenharmony_ci write_unlock_irqrestore(&iclock_lock, flags); 1488c2ecf20Sopenharmony_ci return; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci if (iclock_timestamp_valid) { 1518c2ecf20Sopenharmony_ci /* increment sample counter by given samples */ 1528c2ecf20Sopenharmony_ci iclock_count += samples; 1538c2ecf20Sopenharmony_ci if (timestamp) { /* timestamp must be set, if function call is delayed */ 1548c2ecf20Sopenharmony_ci iclock_timestamp = *timestamp; 1558c2ecf20Sopenharmony_ci } else { 1568c2ecf20Sopenharmony_ci iclock_timestamp = ktime_get(); 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci } else { 1598c2ecf20Sopenharmony_ci /* calc elapsed time by system clock */ 1608c2ecf20Sopenharmony_ci if (timestamp) { /* timestamp must be set, if function call is delayed */ 1618c2ecf20Sopenharmony_ci timestamp_now = *timestamp; 1628c2ecf20Sopenharmony_ci } else { 1638c2ecf20Sopenharmony_ci timestamp_now = ktime_get(); 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci delta = ktime_divns(ktime_sub(timestamp_now, iclock_timestamp), 1668c2ecf20Sopenharmony_ci (NSEC_PER_SEC / 8000)); 1678c2ecf20Sopenharmony_ci /* add elapsed time to counter and set new timestamp */ 1688c2ecf20Sopenharmony_ci iclock_count += delta; 1698c2ecf20Sopenharmony_ci iclock_timestamp = timestamp_now; 1708c2ecf20Sopenharmony_ci iclock_timestamp_valid = 1; 1718c2ecf20Sopenharmony_ci if (*debug & DEBUG_CLOCK) 1728c2ecf20Sopenharmony_ci printk("Received first clock from source '%s'.\n", 1738c2ecf20Sopenharmony_ci iclock_current ? iclock_current->name : "nothing"); 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci write_unlock_irqrestore(&iclock_lock, flags); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDN_clock_update); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ciunsigned short 1808c2ecf20Sopenharmony_cimISDN_clock_get(void) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci u_long flags; 1838c2ecf20Sopenharmony_ci ktime_t timestamp_now; 1848c2ecf20Sopenharmony_ci u16 delta; 1858c2ecf20Sopenharmony_ci u16 count; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci read_lock_irqsave(&iclock_lock, flags); 1888c2ecf20Sopenharmony_ci /* calc elapsed time by system clock */ 1898c2ecf20Sopenharmony_ci timestamp_now = ktime_get(); 1908c2ecf20Sopenharmony_ci delta = ktime_divns(ktime_sub(timestamp_now, iclock_timestamp), 1918c2ecf20Sopenharmony_ci (NSEC_PER_SEC / 8000)); 1928c2ecf20Sopenharmony_ci /* add elapsed time to counter */ 1938c2ecf20Sopenharmony_ci count = iclock_count + delta; 1948c2ecf20Sopenharmony_ci read_unlock_irqrestore(&iclock_lock, flags); 1958c2ecf20Sopenharmony_ci return count; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDN_clock_get); 198