diff options
Diffstat (limited to 'xpp/xpp_zap.c')
-rw-r--r-- | xpp/xpp_zap.c | 189 |
1 files changed, 138 insertions, 51 deletions
diff --git a/xpp/xpp_zap.c b/xpp/xpp_zap.c index 8642b7c..fb49e69 100644 --- a/xpp/xpp_zap.c +++ b/xpp/xpp_zap.c @@ -42,6 +42,7 @@ #include "xbus-core.h" #include "xproto.h" #include "xpp_zap.h" +#include "parport_debug.h" static const char rcsid[] = "$Id$"; @@ -64,24 +65,28 @@ static unsigned int xpp_timer_count = 0; static unsigned int xpp_last_jiffies = 0; #ifdef ZAPTEL_SYNC_TICK +static unsigned int zaptel_tick_count = 0; /* * Statistics variables */ static struct timeval ticked_zaptel; static struct timeval ticked_xpp; static long usec_lag_curr; /* current: zaptel - xpp */ -static long usec_lag_prev; /* previous: zaptel - xpp */ -static long usec_delta; /* previous - current */ -static long usec_sigma; /* sum of deltas */ -static long status_cleared_at; -static long since_status_clear; +static long sigma_lag; /* sum of lags over SYNC_ADJ_INTERVAL */ +static long average_lag; /* average of lags over SYNC_ADJ_INTERVAL */ static bool zaptel_is_ticking; +static bool have_sync_mastership; +static unsigned int sync_tick_nomaster; -#define ZAPTEL_BIG_LAG 500 /* usec */ +#define ZAPTEL_BIG_LAG 2000 /* usec */ + +#define SYNC_ADJ_MIN (-30) /* minimal firmware drift unit */ +#define SYNC_ADJ_MAX 30 /* maximal firmware drift unit */ +#define SYNC_ADJ_INTERVAL 5000 /* minimum interval between fixes (usec) */ +#define SYNC_ADJ_FACTOR 30 /* sigma usec/drift_unit */ DEF_PARM_BOOL(sync_tick_active, 1, 0644, "Measure via zaptel sync_tick() method"); -static void sync_status_clear(void); #endif @@ -102,6 +107,41 @@ static int xpp_ec = 0; #include "supress/ec_xpp.h" #endif +#ifdef DEBUG_SYNC_PARPORT +/* + * Use parallel port to sample our PCM sync and diagnose quality and + * potential problems. A logic analizer or a scope should be connected + * to the data bits of the parallel port. + * + * Array parameter: Choose the two xbuses Id's to sample. + * This can be changed on runtime as well. Example: + * echo "3,5" > /sys/module/xpp/parameters/parport_xbuses + */ +static int parport_xbuses[2] = { 0, 1 }; +unsigned int parport_xbuses_num_values; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) +module_param_array(parport_xbuses, int, &parport_xbuses_num_values, 0577); +#else +module_param_array(parport_xbuses, int, parport_xbuses_num_values, 0577); +#endif +MODULE_PARM_DESC(parport_xbuses, "Id's of xbuses to sample (1-2)"); + +/* + * Flip a single bit in the parallel port: + * - The bit number is either bitnum0 or bitnum1 + * - Bit is selected by xbus number from parport_xbuses[] + */ +void xbus_flip_bit(xbus_t *xbus, unsigned int bitnum0, unsigned int bitnum1) +{ + int num = xbus->num; + + if(num == parport_xbuses[0]) + flip_parport_bit(bitnum0); + if(num == parport_xbuses[1]) + flip_parport_bit(bitnum1); +} +EXPORT_SYMBOL(xbus_flip_bit); +#endif static void xpp_tick(unsigned long param); static int zaptel_register_xpd(xpd_t *xpd); @@ -175,8 +215,8 @@ static inline void send_pcm_frame(xbus_t *xbus, xframe_t *xframe) static int rate_limit; unsigned long flags; struct timeval now; - long sec_diff; - long usec_diff; + unsigned long sec_diff; + unsigned long usec_diff; spin_lock_irqsave(&xbus->lock, flags); do_gettimeofday(&now); @@ -336,7 +376,8 @@ void xpp_tick(unsigned long param) int i; #ifdef ZAPTEL_SYNC_TICK - do_gettimeofday (&ticked_xpp); + flip_parport_bit(3); + do_gettimeofday(&ticked_xpp); #endif if(!sync_master) /* Called from timer */ mod_timer(&xpp_timer, jiffies + 1); /* Must be 1KHz rate */ @@ -390,46 +431,92 @@ void got_sync_from(xpd_t *xpd) } #ifdef ZAPTEL_SYNC_TICK -static void sync_status_clear(void) +static void send_drift(int drift) { - struct timeval now; + struct timeval now; + const char *msg; + xbus_t *xbus; - do_gettimeofday (&now); - usec_sigma = 0; - status_cleared_at = now.tv_sec; + BUG_ON(!sync_master); + xbus = sync_master->xbus; + BUG_ON(drift < SYNC_ADJ_MIN || drift > SYNC_ADJ_MAX); + do_gettimeofday(&now); + if(drift == 0) + msg = "stop"; + else if(drift > 0) + msg = "up"; + else + msg = "down"; + DBG("DRIFT adjust %s (%d) (last update %ld seconds ago)\n", + msg, drift, now.tv_sec - xbus->pll_updated_at); + CALL_PROTO(GLOBAL, SYNC_SOURCE, xbus, NULL, SYNC_MODE_PLL, drift); + xbus->pll_updated_at = now.tv_sec; } int zaptel_sync_tick(struct zt_span *span, int is_master) { xpd_t *xpd = span->pvt; + int offset; - if(!sync_tick_active) { - zaptel_is_ticking = 0; - usec_sigma = 0; - return 0; /* No auto sync from zaptel */ + if(!sync_tick_active) + goto noop; + /* + * Detect if any of our spans is zaptel sync master + */ + if(is_master) { + sync_tick_nomaster = 0; /* Yes */ + have_sync_mastership = 1; } + if(sync_tick_nomaster < MAX_BUSES * MAX_XPDS) { + sync_tick_nomaster++; /* Maybe */ + goto noop; + } + /* Now we know for sure someone else is zaptel sync master */ + have_sync_mastership = 0; BUG_ON(!xpd); /* * Calculate only if: - * - Called for the sync_master * - HOST sync (for information only) + * - Called for the sync_master (the function is called + * for each span, so we don't want to do it multiple times). */ - if(sync_master && xpd != sync_master) + if(sync_master && sync_master != xpd) return 0; + zaptel_tick_count++; + zaptel_is_ticking = 1; + flip_parport_bit(1); do_gettimeofday(&ticked_zaptel); usec_lag_curr = (ticked_zaptel.tv_sec - ticked_xpp.tv_sec)*1000*1000 + (ticked_zaptel.tv_usec - ticked_xpp.tv_usec); - if(usec_lag_curr > ZAPTEL_BIG_LAG) { - if(printk_ratelimit()) - NOTICE("Big lag of Xpp ticks relative to zaptel ticks: %ld\n", usec_lag_curr); + if(unlikely(usec_lag_curr > ZAPTEL_BIG_LAG)) { + static int rate_limit; + + if((rate_limit++ % 1003) == 0) + DBG("Big lag of Xpp ticks relative to zaptel ticks: %ld\n", usec_lag_curr); + } + sigma_lag += usec_lag_curr; + if((zaptel_tick_count % SYNC_ADJ_INTERVAL) == 0) { + average_lag = sigma_lag / SYNC_ADJ_INTERVAL; + sigma_lag = 0; + if(sync_master == xpd) { + xbus_t *xbus = sync_master->xbus; + + /* Calculate required fix */ + offset = (average_lag - 500) / SYNC_ADJ_FACTOR; + if(offset < SYNC_ADJ_MIN) + offset = SYNC_ADJ_MIN; + if(offset > SYNC_ADJ_MAX) + offset = SYNC_ADJ_MAX; + if(xbus->sync_adjustment != offset) /* Only fix if needed */ + send_drift(offset); + } } - usec_delta = usec_lag_curr - usec_lag_prev; - usec_sigma += usec_delta; - zaptel_is_ticking = 1; - since_status_clear = ticked_zaptel.tv_sec - status_cleared_at; - usec_lag_prev = usec_lag_curr; return 0; +noop: + zaptel_is_ticking = 0; + sigma_lag = 0; + return 0; /* No auto sync from zaptel */ } #endif @@ -938,40 +1025,48 @@ int proc_sync_read(char *page, char **start, off_t off, int count, int *eof, voi { int len = 0; unsigned int xpp_timer_rate; - unsigned int now; + unsigned int jiffies_now; len += sprintf(page + len, "# To modify sync source write into this file:\n"); len += sprintf(page + len, "# HOST - For host based sync\n"); len += sprintf(page + len, "# SYNC=nn - XBUS-nn provide sync\n"); -#ifdef ZAPTEL_SYNC_TICK - len += sprintf(page + len, "# CLS - Clear Statistics\n"); -#endif len += sprintf(page + len, "# QUERY=nn - Query XBUS-nn for sync information (DEBUG)\n"); if(!sync_master) len += sprintf(page + len, "HOST\n"); - else + else { len += sprintf(page + len, "SYNC=%02d\n", sync_master->xbus->num); + len += sprintf(page + len, "PLL DRIFT=%d\n", + sync_master->xbus->sync_adjustment); + } #ifdef ZAPTEL_SYNC_TICK if(sync_tick_active) { - len += sprintf(page + len, "\nZaptel Reference Sync:\n"); + len += sprintf(page + len, "\nZaptel Reference Sync (%s):\n", + (have_sync_mastership)?"xpp":"external"); if(zaptel_is_ticking) { + if(sync_master) { + struct timeval now; + xbus_t *xbus = sync_master->xbus; + + do_gettimeofday(&now); + len += sprintf(page + len, "\t%-19s: %5ld seconds ago\n", + "PLL updated", + (xbus->pll_updated_at == 0) ? 0 : now.tv_sec - xbus->pll_updated_at); + } len += sprintf(page + len, "\t%-19s: %5ld (usec)\n", "current lag", usec_lag_curr); len += sprintf(page + len, "\t%-19s: %5ld (usec)\n", - "delta lag", usec_delta); - len += sprintf(page + len, "\t%-19s: %5ld (usec)\n", - "sigma of deltas", usec_sigma); - len += sprintf(page + len, "\t%-19s: %5ld seconds ago\n", - "status cleared", since_status_clear); + "average lag", average_lag); } else len += sprintf(page + len, "\tNot activated\n"); } + len += sprintf(page + len, "zaptel_tick: #%d\n", zaptel_tick_count); + len += sprintf(page + len, "tick - zaptel_tick = %d\n", xpp_timer_count - zaptel_tick_count); #endif len += sprintf(page + len, "\ntick: #%d\n", xpp_timer_count); xpp_timer_rate = 0; - now = jiffies; - if(now - xpp_last_jiffies > 0) { - unsigned long delta = (now - xpp_last_jiffies); + jiffies_now = jiffies; + if(jiffies_now - xpp_last_jiffies > 0) { + unsigned long delta = (jiffies_now - xpp_last_jiffies); xpp_timer_rate = (xpp_timer_count % SAMPLE_TICKS) * HZ / delta; len += sprintf(page + len, "tick rate: %4d/second (SAMPLE_TICKS=%d)\n", xpp_timer_rate, SAMPLE_TICKS); } @@ -1003,10 +1098,6 @@ static int proc_sync_write(struct file *file, const char __user *buffer, unsigne buf[count] = '\0'; if(strncmp("HOST", buf, 4) == 0) { sync_master_is(NULL); -#ifdef ZAPTEL_SYNC_TICK - } else if(strncmp("CLS", buf, 3) == 0) { - sync_status_clear(); -#endif } else if(sscanf(buf, "SYNC=%d", &xbus_num) == 1) { DBG("SYNC=%d\n", xbus_num); if((xbus = xbus_of(xbus_num)) == NULL) { @@ -1775,7 +1866,6 @@ int __init xpp_zap_init(void) #else INFO("FEATURE: %s without sync_tick() from ZAPTEL\n", THIS_MODULE->name); #endif - #ifdef CONFIG_PROC_FS xpp_proc_toplevel = proc_mkdir(PROC_DIR, NULL); if(!xpp_proc_toplevel) { @@ -1801,9 +1891,6 @@ int __init xpp_zap_init(void) /* Only timer init. We add it only *after* zt_register */ init_timer(&xpp_timer); sync_master_is(NULL); /* Internal ticking */ -#ifdef ZAPTEL_SYNC_TICK - sync_status_clear(); -#endif return 0; err: do_cleanup(); |