/*
 * Written by Oron Peled <oron@actcom.co.il>
 * Copyright (C) 2004-2007, Xorcom
 *
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include "xbus-pcm.h"
#include "xbus-core.h"
#include "xpp_dahdi.h"
#include "dahdi_debug.h"
#include "parport_debug.h"

static const char rcsid[] = "$Id$";

extern int debug;
#ifdef	OPTIMIZE_CHANMUTE
static DEF_PARM_BOOL(optimize_chanmute, 1, 0644,
		     "Optimize by muting inactive channels");
#endif

static DEF_PARM(int, disable_pcm, 0, 0644, "Disable all PCM transmissions");
#ifdef	DEBUG_PCMTX
DEF_PARM(int, pcmtx, -1, 0644,
	 "Forced PCM value to transmit (negative to disable)");
EXPORT_SYMBOL(pcmtx);
DEF_PARM(int, pcmtx_chan, 0, 0644, "channel to force PCM value");
EXPORT_SYMBOL(pcmtx_chan);
#endif
static DEF_PARM_BOOL(disable_pll_sync, 0, 0644,
		     "Disable automatic adjustment of AB clocks");

static xbus_t *syncer;		/* current syncer */
static atomic_t xpp_tick_counter = ATOMIC_INIT(0);
static struct xpp_ticker dahdi_ticker;
/*
 * The ref_ticker points to the current referece tick source.
 * I.e: one of our AB or dahdi_ticker
 */
static struct xpp_ticker *ref_ticker;
static DEFINE_SPINLOCK(ref_ticker_lock);
static DEFINE_SPINLOCK(elect_syncer_lock);
/* from /sys/bus/astribanks/drivers/xppdrv/sync */
static bool force_dahdi_sync;
static xbus_t *global_ticker;
static struct xpp_ticker global_ticks_series;

#define	PROC_SYNC		"sync"
/* Sampling cycle in usec */
#define SYNC_CYCLE		500
/* Samples from end of SYNC_CYCLE */
#define SYNC_CYCLE_SAMPLE	100
/* Number of SYNC_CYCLE's to converge speed */
#define SYNC_CONVERGE		10
/* Offset from ref_ticker to other AB's */
#define SYNC_CENTER		500
/* If within +/-SYNC_DELTA, try to stay there */
#define SYNC_DELTA		40
#define	BIG_TICK_INTERVAL	1000
/* maximal firmware drift unit (hardware limit 63) */
#define	SYNC_ADJ_MAX		20

/*
 * The USB bulk endpoints have a large jitter in the timing of frames
 * from the AB to the ehci-hcd. This is because we cannot predict
 * in which USB micro-frame our data passes. Each micro-frame is
 * A 125 usec.
 */
#define	SYNC_ADJ_QUICK	1000
#define	SYNC_ADJ_SLOW	10000

#ifdef	DAHDI_SYNC_TICK
static unsigned int dahdi_tick_count;
#endif

/*------------------------- SYNC Handling --------------------------*/

static void send_drift(xbus_t *xbus, int drift);

static void ticker_set_cycle(struct xpp_ticker *ticker, int cycle)
{
	unsigned long flags;

	spin_lock_irqsave(&ticker->lock, flags);
	if (cycle < SYNC_ADJ_QUICK)
		cycle = SYNC_ADJ_QUICK;
	if (cycle > SYNC_ADJ_SLOW)
		cycle = SYNC_ADJ_SLOW;
	ticker->cycle = cycle;
	spin_unlock_irqrestore(&ticker->lock, flags);
}

static void xpp_ticker_init(struct xpp_ticker *ticker)
{
	memset(ticker, 0, sizeof(*ticker));
	spin_lock_init(&ticker->lock);
	ticker->last_sample = ktime_get();
	ticker->first_sample = ticker->last_sample;
	ticker_set_cycle(ticker, SYNC_ADJ_QUICK);
}

static int xpp_ticker_step(struct xpp_ticker *ticker, const ktime_t t)
{
	unsigned long flags;
	s64 usec;
	bool cycled = 0;

	spin_lock_irqsave(&ticker->lock, flags);
	ticker->last_sample = t;
	/* rate adjust */
	if ((ticker->count % ticker->cycle) == ticker->cycle - 1) {
		usec = ktime_us_delta(ticker->last_sample,
					ticker->first_sample);
		ticker->first_sample = ticker->last_sample;
		ticker->tick_period = usec / ticker->cycle;
		cycled = 1;
	}
	ticker->count++;
	spin_unlock_irqrestore(&ticker->lock, flags);
	return cycled;
}

/*
 * No locking. It is called only from:
 *   - update_sync_master() in a globall spinlock protected code.
 *   - initalization.
 */
static inline void xbus_drift_clear(xbus_t *xbus)
{
	struct xpp_drift *di = &xbus->drift;

	di->last_lost_tick = ktime_get();
	ticker_set_cycle(&xbus->ticker, SYNC_ADJ_QUICK);
	di->max_speed = -SYNC_ADJ_MAX;
	di->min_speed = SYNC_ADJ_MAX;
}

void xpp_drift_init(xbus_t *xbus)
{
	memset(&xbus->drift, 0, sizeof(xbus->drift));
	spin_lock_init(&xbus->drift.lock);
	xpp_ticker_init(&xbus->ticker);
	xbus_drift_clear(xbus);
}

#ifdef	SAMPLE_TICKS
static void sample_tick(xbus_t *xbus, int sample)
{
	if (!xbus->sample_running)
		return;
	if (xbus->sample_pos < SAMPLE_SIZE)
		xbus->sample_ticks[xbus->sample_pos++] = sample;
	else {
		xbus->sample_running = 0;
		xbus->sample_pos = 0;
	}
}
#else
#define	sample_tick(x, y)
#endif

/*
 * The following function adjust the clock of an astribank according
 * to our reference clock (another astribank or another DAHDI device).
 *
 * It is VERY hard to stabilise these corrections:
 *    - The measurments are affected by 125usec USB micro-frames.
 *    - However, if we are off by more than +/-500usec we are risking
 *      loosing frames.
 *
 * Every change must be tested rigorously with many device combinations
 * including abrupt changes in sync -- to verify the stability of the
 * algorithm.
 */
static void xpp_drift_step(xbus_t *xbus, const ktime_t kt)
{
	struct xpp_drift *di = &xbus->drift;
	struct xpp_ticker *ticker = &xbus->ticker;
	unsigned long flags;

	spin_lock_irqsave(&di->lock, flags);
	xpp_ticker_step(&xbus->ticker, kt);
	/*
	 * Do we need to be synchronized and is there an established reference
	 * ticker (another Astribank or another DAHDI device) already?
	 */
	if (ref_ticker && ref_ticker != &xbus->ticker && syncer
	    && xbus->sync_mode == SYNC_MODE_PLL) {
		int new_delta_tick = ticker->count - ref_ticker->count;
		int lost_ticks = new_delta_tick - di->delta_tick;
		s64 usec_delta;

		di->delta_tick = new_delta_tick;
		if (lost_ticks) {
			static int rate_limit;

			/*
			 * We just lost some ticks. Just report it and don't
			 * try to adjust anything until we are stable again.
			 */
			di->lost_ticks++;
			di->lost_tick_count += abs(lost_ticks);
			if ((rate_limit++ % 1003) == 0) {
				/* FIXME: This should be a NOTICE.
				 * However we have several false ones at
				 * startup.
				 */
				XBUS_DBG(SYNC, xbus, "Lost %d tick%s\n",
					 lost_ticks,
					 (abs(lost_ticks) > 1) ? "s" : "");
			}
			if (abs(lost_ticks) > 100) {
				xbus_drift_clear(xbus);
				ticker->count = ref_ticker->count;
			}
		} else {
			/* Sample a delta */
			usec_delta = ktime_us_delta(ticker->last_sample,
						    ref_ticker->last_sample);
			sample_tick(xbus, usec_delta);
			if ((ticker->count % SYNC_CYCLE) >
			    (SYNC_CYCLE - SYNC_CYCLE_SAMPLE))
				di->delta_sum += usec_delta;

			if ((ticker->count % SYNC_CYCLE) == 0) {
				/*
				 * Full sampling cycle passed. Let's calculate
				 */
				int offset =
				    di->delta_sum / SYNC_CYCLE_SAMPLE -
				    SYNC_CENTER;
				int offset_prev = di->offset_prev;
				int speed = xbus->sync_adjustment;
				int fix = 0;
				int best_speed =
				    (di->max_speed + di->min_speed) >> 1;

				if (offset > 0 && offset < SYNC_DELTA) {
					speed = best_speed - 1;
				} else if (offset < 0 && offset > -SYNC_DELTA) {
					speed = best_speed + 1;
				} else {
					if (offset > 0) {
						if (offset > offset_prev)
							fix--;
					} else {
						if (offset < offset_prev)
							fix++;
					}
					speed += fix;
				}
				if (speed < -SYNC_ADJ_MAX)
					speed = -SYNC_ADJ_MAX;
				if (speed > SYNC_ADJ_MAX)
					speed = SYNC_ADJ_MAX;
				if (speed < di->min_speed)
					di->min_speed = speed;
				if (speed > di->max_speed)
					di->max_speed = speed;
				if (offset > di->offset_max)
					di->offset_max = offset;
				if (offset < di->offset_min)
					di->offset_min = offset;

				XBUS_DBG(SYNC, xbus,
					"offset: %d, min_speed=%d, max_speed=%d, usec_delta(last)=%lld\n",
					offset_prev, di->min_speed,
					di->max_speed, usec_delta);
				XBUS_DBG(SYNC, xbus,
					"ADJ: speed=%d (best_speed=%d) fix=%d\n",
					speed, best_speed, fix);
				xbus->sync_adjustment_offset = speed;
				if (xbus != syncer
				    && xbus->sync_adjustment != speed)
					send_drift(xbus, speed);
				di->sync_inaccuracy =
				    abs(offset) + abs(di->offset_range) / 2;
				if (ticker->count >= SYNC_CYCLE * SYNC_CONVERGE) {
					di->offset_range =
					    di->offset_max - di->offset_min;
					di->offset_min = INT_MAX;
					di->offset_max = -INT_MAX;
					if (di->max_speed > best_speed)
						di->max_speed--;
					if (di->min_speed < best_speed)
						di->min_speed++;
				}
				di->offset_prev = offset;
				di->delta_sum = 0;
			}
		}
	}
	spin_unlock_irqrestore(&di->lock, flags);
}

const char *sync_mode_name(enum sync_mode mode)
{
	static const char *sync_mode_names[] = {
		[SYNC_MODE_AB] = "AB",
		[SYNC_MODE_NONE] = "NONE",
		[SYNC_MODE_PLL] = "PLL",
		[SYNC_MODE_QUERY] = "QUERY",
	};
	if (mode >= ARRAY_SIZE(sync_mode_names))
		return NULL;
	return sync_mode_names[mode];
}
EXPORT_SYMBOL(sync_mode_name);

/*
 * Caller must aquire/release the 'ref_ticker_lock' spinlock
 */
static void xpp_set_syncer(xbus_t *xbus, bool on)
{
	if (!xbus) {		/* Special case, no more syncers */
		DBG(SYNC, "No more syncers\n");
		syncer = NULL;
		if (ref_ticker != &dahdi_ticker)
			ref_ticker = NULL;
		return;
	}
	if (syncer != xbus && on) {
		XBUS_DBG(SYNC, xbus, "New syncer\n");
		syncer = xbus;
	} else if (syncer == xbus && !on) {
		XBUS_DBG(SYNC, xbus, "Lost syncer\n");
		syncer = NULL;
		if (ref_ticker != &dahdi_ticker)
			ref_ticker = NULL;
	} else
		XBUS_DBG(SYNC, xbus, "ignore %s (current syncer: %s)\n",
			 (on) ? "ON" : "OFF",
			 (syncer) ? syncer->busname : "NO-SYNC");
}

static void xbus_command_timer(TIMER_DATA_TYPE timer)
{
	xbus_t *xbus = from_timer(xbus, timer, command_timer);

	BUG_ON(!xbus);
	xbus_command_queue_tick(xbus);
	if (!xbus->self_ticking) /* Must be 1KHz rate */
		mod_timer(&xbus->command_timer, jiffies + 1);
}

void xbus_set_command_timer(xbus_t *xbus, bool on)
{
	XBUS_DBG(SYNC, xbus, "%s\n", (on) ? "ON" : "OFF");
	if (on) {
		if (!timer_pending(&xbus->command_timer)) {
			XBUS_DBG(SYNC, xbus, "add_timer\n");
			timer_setup(&xbus->command_timer,
				xbus_command_timer, 0);
			mod_timer(&xbus->command_timer, jiffies + 1);
		}
	} else if (timer_pending(&xbus->command_timer)) {
		XBUS_DBG(SYNC, xbus, "del_timer\n");
		del_timer(&xbus->command_timer);
	}
	xbus->self_ticking = !on;
}

/*
 * Called when the Astribank replies to a sync change request
 */
void got_new_syncer(xbus_t *xbus, enum sync_mode mode, int drift)
{
	unsigned long flags;
	unsigned long flags2;

	spin_lock_irqsave(&xbus->lock, flags);
	spin_lock_irqsave(&ref_ticker_lock, flags2);
	xbus->sync_adjustment = (signed char)drift;
	if (xbus->sync_mode == mode) {
		XBUS_DBG(SYNC, xbus, "Already in mode '%s'. Ignored\n",
			 sync_mode_name(mode));
		goto out;
	}
	XBUS_DBG(SYNC, xbus, "Mode %s (%d), drift=%d (pcm_rx_counter=%d)\n",
		 sync_mode_name(mode), mode, drift,
		 atomic_read(&xbus->pcm_rx_counter));
	switch (mode) {
	case SYNC_MODE_AB:
		xbus->sync_mode = mode;
		xbus_set_command_timer(xbus, 0);
		xpp_set_syncer(xbus, 1);
		global_ticker = xbus;
		break;
	case SYNC_MODE_PLL:
		xbus->sync_mode = mode;
		xbus_set_command_timer(xbus, 0);
		xpp_set_syncer(xbus, 0);
		global_ticker = xbus;
		break;
	case SYNC_MODE_NONE:	/* lost sync source */
		xbus->sync_mode = mode;
		xbus_set_command_timer(xbus, 1);
		xpp_set_syncer(xbus, 0);
		break;
	case SYNC_MODE_QUERY:	/* ignore           */
		break;
	default:
		XBUS_ERR(xbus, "%s: unknown mode=0x%X\n", __func__, mode);
	}
out:
	spin_unlock_irqrestore(&ref_ticker_lock, flags2);
	spin_unlock_irqrestore(&xbus->lock, flags);
}
EXPORT_SYMBOL(got_new_syncer);

void xbus_request_sync(xbus_t *xbus, enum sync_mode mode)
{
	unsigned long flags;

	BUG_ON(!xbus);
	XBUS_DBG(SYNC, xbus, "sent request (mode=%d)\n", mode);
	CALL_PROTO(GLOBAL, SYNC_SOURCE, xbus, NULL, mode, 0);
	if (mode == SYNC_MODE_NONE) {
		/*
		 * We must deselect the syncer *now* and not wait for the
		 * reply from the AB. Otherwise, a disconnect of the syncing
		 * AB would result in a corrupted 'syncer'.
		 */
		spin_lock_irqsave(&ref_ticker_lock, flags);
		xpp_set_syncer(xbus, 0);
		spin_unlock_irqrestore(&ref_ticker_lock, flags);
		/*
		 * We must activate timer now since the commands go to
		 * the command queue, and we should maintain something to
		 * "tick" meanwhile until the AB get these commands and
		 * start being "self_ticking".
		 */
		xbus_set_command_timer(xbus, 1);
	}
}
EXPORT_SYMBOL(xbus_request_sync);

static void reset_sync_counters(void)
{
	int i;

	//DBG(SYNC, "%d\n", atomic_read(&xpp_tick_counter));
	for (i = 0; i < MAX_BUSES; i++) {
		xbus_t *xbus = get_xbus(__func__, i);

		if (xbus == NULL)
			continue;
		/*
		 * Don't send to non self_ticking Astribanks:
		 *  - Maybe they didn't finish initialization
		 *  - Or maybe they didn't answer us in the first place
		 (e.g: wrong firmware version, etc).
		 */
		if (xbus->self_ticking) {
			if (!XBUS_FLAGS(xbus, CONNECTED)) {
				XBUS_DBG(GENERAL, xbus,
					 "Dropped packet. Is shutting down.\n");
			} else {
				/* Reset sync LEDs once in a while */
				CALL_PROTO(GLOBAL, RESET_SYNC_COUNTERS, xbus,
					   NULL);
			}
		}
		put_xbus(__func__, xbus);
	}
}

static void send_drift(xbus_t *xbus, int drift)
{
	const ktime_t now = ktime_get();
	const char *msg;
	s64 msec_delta;

	BUG_ON(abs(drift) > SYNC_ADJ_MAX);
	if (drift > xbus->sync_adjustment)
		msg = "up";
	else
		msg = "down";
	msec_delta = ktime_ms_delta(now, xbus->pll_updated_at);
	XBUS_DBG(SYNC, xbus,
		 "%sDRIFT adjust %s (%d) (last update %lld seconds ago)\n",
		 (disable_pll_sync) ? "Fake " : "", msg, drift,
		 msec_delta / MSEC_PER_SEC);
	if (!disable_pll_sync)
		CALL_PROTO(GLOBAL, SYNC_SOURCE, xbus, NULL, SYNC_MODE_PLL,
			   drift);
	xbus->pll_updated_at = now;
}

static void global_tick(void)
{
	ktime_t now = ktime_get();

	atomic_inc(&xpp_tick_counter);
	if ((atomic_read(&xpp_tick_counter) % BIG_TICK_INTERVAL) == 0)
		reset_sync_counters();
	xpp_ticker_step(&global_ticks_series, now);
}

#ifdef	DAHDI_SYNC_TICK
void dahdi_sync_tick(struct dahdi_span *span, int is_master)
{
	struct phonedev *phonedev = container_of(span, struct phonedev, span);
	xpd_t *xpd = container_of(phonedev, struct xpd, phonedev);
	static int redundant_ticks;	/* for extra spans */
	ktime_t now;

	if (!force_dahdi_sync)
		goto noop;
	now = ktime_get();
	BUG_ON(!xpd);
	/*
	 * Detect if any of our spans is dahdi sync master
	 */
	if (is_master) {
		static int rate_limit;

		if ((rate_limit++ % 10003) == 0)
			XPD_NOTICE(xpd,
				"Is a DAHDI sync master: "
				"ignore sync from DAHDI\n");
		goto noop;
	}
	/* Now we know for sure someone else is dahdi sync master */
	if (syncer) {
		static int rate_limit;

		if ((rate_limit++ % 5003) == 0)
			XBUS_DBG(SYNC, syncer,
				 "is a SYNCer: ignore sync from DAHDI\n");
		goto noop;
	}
	/* ignore duplicate calls from all our registered spans */
	if ((redundant_ticks++ % total_registered_spans()) != 0) {
#if 0
		static int rate_limit;

		if ((rate_limit++ % 1003) < 16)
			XPD_NOTICE(xpd, "boop (%d)\n", dahdi_tick_count);
#endif
		goto noop;
	}
	xpp_ticker_step(&dahdi_ticker, now);
	dahdi_tick_count++;
	//flip_parport_bit(1);
noop:
	return;
}
EXPORT_SYMBOL(dahdi_sync_tick);
#endif

/*
 * called from elect_syncer()
 * if new_syncer is NULL, than we move all to SYNC_MODE_PLL
 * for DAHDI sync.
 */
static void update_sync_master(xbus_t *new_syncer, bool force_dahdi)
{
	const char *msg;
	int i;
	unsigned long flags;

	WARN_ON(new_syncer && force_dahdi);	/* Ambigous */
	force_dahdi_sync = force_dahdi;
	msg = (force_dahdi_sync) ? "DAHDI" : "NO-SYNC";
	DBG(SYNC, "%s => %s\n", (syncer) ? syncer->busname : msg,
	    (new_syncer) ? new_syncer->busname : msg);
	/*
	 * This global locking protects:
	 *   - The ref_ticker so it won't be used while we change it.
	 *   - The xbus_drift_clear() from corrupting driftinfo data.
	 * It's important to set ref_ticker now:
	 *   - We cannot make the new xbus a syncer yet (until we get
	 *     a reply from AB). Maybe it's still not self_ticking, so
	 *     we must keep the timer for the command_queue to function.
	 *   - However, we must not send drift commands to it, because
	 *     they'll revert it to PLL instead of AB.
	 */
	spin_lock_irqsave(&ref_ticker_lock, flags);
	if (syncer)
		xbus_drift_clear(syncer);	/* Clean old data */
	if (new_syncer) {
		XBUS_DBG(SYNC, new_syncer, "pcm_rx_counter=%d\n",
			 atomic_read(&new_syncer->pcm_rx_counter));
		force_dahdi_sync = 0;
		ref_ticker = &new_syncer->ticker;
		xbus_drift_clear(new_syncer);	/* Clean new data */
		xpp_set_syncer(new_syncer, 1);
		xbus_request_sync(new_syncer, SYNC_MODE_AB);
	} else if (force_dahdi_sync) {
		ref_ticker = &dahdi_ticker;
	} else {
		ref_ticker = NULL;
	}
	spin_unlock_irqrestore(&ref_ticker_lock, flags);
	DBG(SYNC, "stop unwanted syncers\n");
	/* Shut all down except the wanted sync master */
	for (i = 0; i < MAX_BUSES; i++) {
		xbus_t *xbus = get_xbus(__func__, i);
		if (xbus == NULL)
			continue;
		if (XBUS_FLAGS(xbus, CONNECTED) && xbus != new_syncer) {
			if (xbus->self_ticking)
				xbus_request_sync(xbus,
						  xbus->sync_mode_default);
			else
				XBUS_DBG(SYNC, xbus,
					 "Not self_ticking yet. Ignore\n");
		}
		put_xbus(__func__, xbus);
	}
}

void elect_syncer(const char *msg)
{
	int i;
	int j;
	uint timing_priority = INT_MAX;
	xpd_t *best_xpd = NULL;
	xbus_t *the_xbus = NULL;
	unsigned long flags;

	spin_lock_irqsave(&elect_syncer_lock, flags);
	DBG(SYNC, "%s: %s syncer=%s\n", __func__, msg,
	    (syncer) ? syncer->busname : "NULL");
	for (i = 0; i < MAX_BUSES; i++) {
		xbus_t *xbus = get_xbus(__func__, i);

		if (xbus == NULL)
			continue;
		if (XBUS_IS(xbus, READY)) {
			if (!the_xbus)
				the_xbus = xbus;	/* First candidate */
			for (j = 0; j < MAX_XPDS; j++) {
				xpd_t *xpd = xpd_of(xbus, j);
				int prio;

				if (!xpd || !xpd->card_present
				    || !IS_PHONEDEV(xpd))
					continue;
				prio =
				    CALL_PHONE_METHOD(card_timing_priority,
						      xpd);
				if (prio < 0) {
					DBG(SYNC, "%s/%s: skip sync\n",
					    xbus->busname, xpd->xpdname);
					continue;
				}
				if (prio > 0 && prio < timing_priority) {
					timing_priority = prio;
					best_xpd = xpd;
				}
			}
		}
		put_xbus(__func__, xbus);
	}
	if (best_xpd) {
		the_xbus = best_xpd->xbus;
		XPD_DBG(SYNC, best_xpd, "%s: elected with priority %d\n", msg,
			timing_priority);
	} else if (the_xbus) {
		XBUS_DBG(SYNC, the_xbus, "%s: elected\n", msg);
	} else {
		unsigned long flags;

		DBG(SYNC, "%s: No more syncers\n", msg);
		spin_lock_irqsave(&ref_ticker_lock, flags);
		xpp_set_syncer(NULL, 0);
		spin_unlock_irqrestore(&ref_ticker_lock, flags);
		the_xbus = NULL;
	}
	if (the_xbus != syncer)
		update_sync_master(the_xbus, force_dahdi_sync);
	spin_unlock_irqrestore(&elect_syncer_lock, flags);
}
EXPORT_SYMBOL(elect_syncer);

/*
 * This function should be called with the xpd already locked
 */
void update_wanted_pcm_mask(xpd_t *xpd, xpp_line_t new_mask, uint new_pcm_len)
{
	PHONEDEV(xpd).pcm_len = new_pcm_len;
	PHONEDEV(xpd).wanted_pcm_mask = new_mask;
	XPD_DBG(SIGNAL, xpd, "pcm_len=%d wanted_pcm_mask=0x%X\n",
		PHONEDEV(xpd).pcm_len, PHONEDEV(xpd).wanted_pcm_mask);
}
EXPORT_SYMBOL(update_wanted_pcm_mask);

/*
 * This function is used by FXS/FXO. The pcm_mask argument signifies
 * channels which should be *added* to the automatic calculation.
 * Normally, this argument is 0.
 */
void generic_card_pcm_recompute(xpd_t *xpd, xpp_line_t pcm_mask)
{
	int i;
	int line_count = 0;
	unsigned long flags;
	uint pcm_len;

	spin_lock_irqsave(&PHONEDEV(xpd).lock_recompute_pcm, flags);
	//XPD_DBG(SIGNAL, xpd, "pcm_mask=0x%X\n", pcm_mask);
	/* Add/remove all the trivial cases */
	pcm_mask |= PHONEDEV(xpd).offhook_state;
	pcm_mask |= PHONEDEV(xpd).oht_pcm_pass;
	pcm_mask &= ~(PHONEDEV(xpd).digital_inputs);
	pcm_mask &= ~(PHONEDEV(xpd).digital_outputs);
	for_each_line(xpd, i)
	    if (IS_SET(pcm_mask, i))
		line_count++;
	/*
	 * FIXME: Workaround a bug in sync code of the Astribank.
	 *        Send dummy PCM for sync.
	 */
	if (xpd->addr.unit == 0 && pcm_mask == 0) {
		pcm_mask = BIT(0);
		line_count = 1;
	}
	pcm_len = (line_count)
	    ? RPACKET_HEADERSIZE + sizeof(xpp_line_t) +
	    line_count * DAHDI_CHUNKSIZE : 0L;
	update_wanted_pcm_mask(xpd, pcm_mask, pcm_len);
	spin_unlock_irqrestore(&PHONEDEV(xpd).lock_recompute_pcm, flags);
}
EXPORT_SYMBOL(generic_card_pcm_recompute);

void fill_beep(u_char *buf, int num, int duration)
{
	bool alternate = (duration) ? (jiffies / (duration * 1000)) & 0x1 : 0;
	int which;
	u_char *snd;

	/*
	 * debug tones
	 */
	static u_char beep[] = {
		0x7F, 0xBE, 0xD8, 0xBE, 0x80, 0x41, 0x24, 0x41,	/* Dima */
		0x67, 0x90, 0x89, 0x90, 0xFF, 0x10, 0x09, 0x10,	/* Izzy */
	};
	static u_char beep_alt[] = {
		0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F,	/* silence */
	};
	if (alternate) {
		which = num % ARRAY_SIZE(beep_alt);
		snd = &beep_alt[which];
	} else {
		which = num % ARRAY_SIZE(beep);
		snd = &beep[which];
	}
	memcpy(buf, snd, DAHDI_CHUNKSIZE);
}

static void do_ec(xpd_t *xpd)
{
	int i;

	for (i = 0; i < PHONEDEV(xpd).span.channels; i++) {
		struct dahdi_chan *chan = XPD_CHAN(xpd, i);

		/* Don't echo cancel BRI D-chans */
		if (unlikely(IS_SET(PHONEDEV(xpd).digital_signalling, i)))
			continue;
		/* No ec for unwanted PCM */
		if (!IS_SET(PHONEDEV(xpd).wanted_pcm_mask, i))
			continue;
		dahdi_ec_chunk(chan, chan->readchunk,
			       PHONEDEV(xpd).ec_chunk2[i]);
		memcpy(PHONEDEV(xpd).ec_chunk2[i], PHONEDEV(xpd).ec_chunk1[i],
		       DAHDI_CHUNKSIZE);
		memcpy(PHONEDEV(xpd).ec_chunk1[i], chan->writechunk,
		       DAHDI_CHUNKSIZE);
	}
}

#if 0
/* Okay, now we get to the signalling.  You have several options: */

/* Option 1: If you're a T1 like interface, you can just provide a
   rbsbits function and we'll assert robbed bits for you.  Be sure to
   set the DAHDI_FLAG_RBS in this case.  */

/* Opt: If the span uses A/B bits, set them here */
int (*rbsbits) (struct dahdi_chan * chan, int bits);

/* Option 2: If you don't know about sig bits, but do have their
   equivalents (i.e. you can disconnect battery, detect off hook,
   generate ring, etc directly) then you can just specify a
   sethook function, and we'll call you with appropriate hook states
   to set.  Still set the DAHDI_FLAG_RBS in this case as well */
int (*hooksig) (struct dahdi_chan * chan, enum dahdi_txsig hookstate);

/* Option 3: If you can't use sig bits, you can write a function
   which handles the individual hook states  */
int (*sethook) (struct dahdi_chan * chan, int hookstate);
#endif

static bool pcm_valid(xpd_t *xpd, xpacket_t *pack)
{
	xpp_line_t lines = RPACKET_FIELD(pack, GLOBAL, PCM_READ, lines);
	int i;
	int count = 0;
	uint16_t good_len;

	BUG_ON(!pack);
	BUG_ON(XPACKET_OP(pack) != XPROTO_NAME(GLOBAL, PCM_READ));
	/*
	 * Don't use for_each_line(xpd, i) here because for BRI it will
	 * ignore the channels of the other xpd's in the same unit.
	 */
	for (i = 0; i < CHANNELS_PERXPD; i++)
		if (IS_SET(lines, i))
			count++;
	/* FRAMES: include opcode in calculation */
	good_len = RPACKET_HEADERSIZE + sizeof(xpp_line_t) + count * 8;
	if (XPACKET_LEN(pack) != good_len) {
		static int rate_limit;

		XPD_COUNTER(xpd, RECV_ERRORS)++;
		if ((rate_limit++ % 1000) <= 10) {
			XPD_ERR(xpd,
				"BAD PCM REPLY: packet_len=%d "
				"(should be %d), count=%d\n",
				XPACKET_LEN(pack), good_len, count);
			dump_packet("BAD PCM REPLY", pack, 1);
		}
		return 0;
	}
	return 1;
}

static inline void pcm_frame_out(xbus_t *xbus, xframe_t *xframe)
{
	unsigned long flags;
	const ktime_t now = ktime_get();
	s64 usec;

	spin_lock_irqsave(&xbus->lock, flags);
	if (unlikely(disable_pcm || !XBUS_IS(xbus, READY)))
		goto dropit;
	if (XPACKET_ADDR_SYNC((xpacket_t *)xframe->packets)) {
		usec = ktime_us_delta(now, xbus->last_tx_sync);
		xbus->last_tx_sync = now;
		/* ignore startup statistics */
		if (likely
		    (atomic_read(&xbus->pcm_rx_counter) > BIG_TICK_INTERVAL)) {
			if (abs(usec - 1000) > TICK_TOLERANCE) {
				static int rate_limit;

				if ((rate_limit++ % 5003) == 0)
					XBUS_DBG(SYNC, xbus,
						"Bad PCM TX timing(%d): usec=%lld.\n",
						rate_limit, usec);
			}
			if (usec > xbus->max_tx_sync)
				xbus->max_tx_sync = usec;
			if (usec < xbus->min_tx_sync)
				xbus->min_tx_sync = usec;
		}
	}
	spin_unlock_irqrestore(&xbus->lock, flags);
	/* OK, really send it */
	if (debug & DBG_PCM)
		dump_xframe("TX_XFRAME_PCM", xbus, xframe, debug);
	send_pcm_frame(xbus, xframe);
	XBUS_COUNTER(xbus, TX_XFRAME_PCM)++;
	return;
dropit:
	spin_unlock_irqrestore(&xbus->lock, flags);
	FREE_SEND_XFRAME(xbus, xframe);
}

/*
 * Generic implementations of card_pcmfromspan()/card_pcmtospan()
 * For FXS/FXO
 */
void generic_card_pcm_fromspan(xpd_t *xpd, xpacket_t *pack)
{
	__u8 *pcm;
	unsigned long flags;
	xpp_line_t wanted_lines;
	int i;

	BUG_ON(!xpd);
	BUG_ON(!pack);
	wanted_lines = PHONEDEV(xpd).wanted_pcm_mask;
	RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, lines) = wanted_lines;
	pcm = RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, pcm);
	spin_lock_irqsave(&xpd->lock, flags);
	for (i = 0; i < PHONEDEV(xpd).channels; i++) {
		struct dahdi_chan *chan = XPD_CHAN(xpd, i);

		if (IS_SET(wanted_lines, i)) {
			if (SPAN_REGISTERED(xpd)) {
#ifdef	DEBUG_PCMTX
				int channo = chan->channo;

				if (pcmtx >= 0 && pcmtx_chan == channo)
					memset((u_char *)pcm, pcmtx,
					       DAHDI_CHUNKSIZE);
				else
#endif
					memcpy((u_char *)pcm, chan->writechunk,
					       DAHDI_CHUNKSIZE);
			} else
				memset((u_char *)pcm, 0x7F, DAHDI_CHUNKSIZE);
			pcm += DAHDI_CHUNKSIZE;
		}
	}
	XPD_COUNTER(xpd, PCM_WRITE)++;
	spin_unlock_irqrestore(&xpd->lock, flags);
}
EXPORT_SYMBOL(generic_card_pcm_fromspan);

void generic_card_pcm_tospan(xpd_t *xpd, xpacket_t *pack)
{
	__u8 *pcm;
	xpp_line_t pcm_mask;
	xpp_line_t pcm_mute;
	unsigned long flags;
	int i;

	pcm = RPACKET_FIELD(pack, GLOBAL, PCM_READ, pcm);
	pcm_mask = RPACKET_FIELD(pack, GLOBAL, PCM_READ, lines);
	spin_lock_irqsave(&xpd->lock, flags);
	/*
	 * Calculate the channels we want to mute
	 */
	pcm_mute = ~(PHONEDEV(xpd).wanted_pcm_mask);
	pcm_mute |= PHONEDEV(xpd).mute_dtmf | PHONEDEV(xpd).silence_pcm;
	if (!SPAN_REGISTERED(xpd))
		goto out;
	for (i = 0; i < PHONEDEV(xpd).channels; i++) {
		volatile u_char *r = XPD_CHAN(xpd, i)->readchunk;
		bool got_data = IS_SET(pcm_mask, i);

		if (got_data && !IS_SET(pcm_mute, i)) {
			/* We have and want real data */
			// memset((u_char *)r, 0x5A, DAHDI_CHUNKSIZE);  // DEBUG
			memcpy((u_char *)r, pcm, DAHDI_CHUNKSIZE);
		} else
		    if (IS_SET
			(PHONEDEV(xpd).wanted_pcm_mask | PHONEDEV(xpd).
			 silence_pcm, i)) {
			/* Inject SILENCE */
			memset((u_char *)r, 0x7F, DAHDI_CHUNKSIZE);
			if (IS_SET(PHONEDEV(xpd).silence_pcm, i)) {
				/*
				 * This will clear the EC buffers until next
				 * tick so we don't have noise residues
				 * from the past.
				 */
				memset(PHONEDEV(xpd).ec_chunk2[i], 0x7F,
				       DAHDI_CHUNKSIZE);
				memset(PHONEDEV(xpd).ec_chunk1[i], 0x7F,
				       DAHDI_CHUNKSIZE);
			}
		}
		if (got_data)
			pcm += DAHDI_CHUNKSIZE;
	}
out:
	XPD_COUNTER(xpd, PCM_READ)++;
	spin_unlock_irqrestore(&xpd->lock, flags);
}
EXPORT_SYMBOL(generic_card_pcm_tospan);

int generic_echocancel_timeslot(xpd_t *xpd, int pos)
{
	return xpd->addr.unit * 32 + pos;
}
EXPORT_SYMBOL(generic_echocancel_timeslot);

int generic_echocancel_setmask(xpd_t *xpd, xpp_line_t ec_mask)
{
	int i;

	BUG_ON(!xpd);
	XPD_DBG(GENERAL, xpd, "0x%8X\n", ec_mask);
	if (!ECHOOPS(xpd->xbus)) {
		XPD_DBG(GENERAL, xpd,
			"No echo canceller in XBUS: Doing nothing.\n");
		return -EINVAL;
	}
	for (i = 0; i < PHONEDEV(xpd).channels; i++) {
		int on = BIT(i) & ec_mask;
		CALL_EC_METHOD(ec_set, xpd->xbus, xpd, i, on);
	}
	CALL_EC_METHOD(ec_update, xpd->xbus, xpd->xbus);
	return 0;
}
EXPORT_SYMBOL(generic_echocancel_setmask);

static int copy_pcm_tospan(xbus_t *xbus, xframe_t *xframe)
{
	__u8 *xframe_end;
	xpacket_t *pack;
	__u8 *p;
	int ret = -EPROTO;	/* Assume error */

	if (debug & DBG_PCM)
		dump_xframe("RX_XFRAME_PCM", xbus, xframe, debug);
	/* handle content */

	p = xframe->packets;
	xframe_end = p + XFRAME_LEN(xframe);
	do {
		int len;
		xpd_t *xpd;

		pack = (xpacket_t *)p;
		len = XPACKET_LEN(pack);
		/* Sanity checks */
		if (unlikely(XPACKET_OP(pack) != XPROTO_NAME(GLOBAL, PCM_READ))) {
			static int rate_limit;

			if ((rate_limit++ % 1003) == 0) {
				XBUS_NOTICE(xbus,
					"%s: Non-PCM packet within "
					"a PCM xframe. (%d)\n",
					__func__, rate_limit);
				dump_xframe("In PCM xframe", xbus, xframe,
					    debug);
			}
			goto out;
		}
		p += len;
		if (p > xframe_end || len < RPACKET_HEADERSIZE) {
			static int rate_limit;

			if ((rate_limit++ % 1003) == 0) {
				XBUS_NOTICE(xbus,
					"%s: Invalid packet length %d. (%d)\n",
					__func__, len, rate_limit);
				dump_xframe("BAD LENGTH", xbus, xframe, debug);
			}
			goto out;
		}
		xpd =
		    xpd_byaddr(xbus, XPACKET_ADDR_UNIT(pack),
			       XPACKET_ADDR_SUBUNIT(pack));
		if (unlikely(!xpd)) {
			static int rate_limit;

			if ((rate_limit++ % 1003) == 0) {
				notify_bad_xpd(__func__, xbus,
					       XPACKET_ADDR(pack),
					       "RECEIVE PCM");
				dump_xframe("Unknown XPD addr", xbus, xframe,
					    debug);
			}
			goto out;
		}
		if (!pcm_valid(xpd, pack))
			goto out;
		if (SPAN_REGISTERED(xpd)) {
			XBUS_COUNTER(xbus, RX_PACK_PCM)++;
			CALL_PHONE_METHOD(card_pcm_tospan, xpd, pack);
		}
	} while (p < xframe_end);
	ret = 0;		/* all good */
	XBUS_COUNTER(xbus, RX_XFRAME_PCM)++;
out:
	FREE_RECV_XFRAME(xbus, xframe);
	return ret;
}

int generic_timing_priority(xpd_t *xpd)
{
	return PHONEDEV(xpd).timing_priority;
}
EXPORT_SYMBOL(generic_timing_priority);

static void xbus_tick(xbus_t *xbus)
{
	int i;
	xpd_t *xpd;
	xframe_t *xframe = NULL;
	xpacket_t *pack = NULL;
	bool sent_sync_bit = 0;

	/*
	 * Update dahdi
	 */
	for (i = 0; i < MAX_XPDS; i++) {
		xpd = xpd_of(xbus, i);
		if (xpd && SPAN_REGISTERED(xpd)) {
#ifdef	OPTIMIZE_CHANMUTE
			int j;
			xpp_line_t xmit_mask = PHONEDEV(xpd).wanted_pcm_mask;

			xmit_mask |= PHONEDEV(xpd).silence_pcm;
			xmit_mask |= PHONEDEV(xpd).digital_signalling;
			for_each_line(xpd, j) {
				XPD_CHAN(xpd, j)->chanmute = (optimize_chanmute)
				    ? !IS_SET(xmit_mask, j)
				    : 0;
			}
#endif
			/*
			 * calls to dahdi_transmit should be out of spinlocks,
			 * as it may call back our hook setting methods.
			 */
			dahdi_transmit(&PHONEDEV(xpd).span);
		}
	}
	/*
	 * Fill xframes
	 */
	for (i = 0; i < MAX_XPDS; i++) {
		if ((xpd = xpd_of(xbus, i)) == NULL)
			continue;
		if (!IS_PHONEDEV(xpd))
			continue;
		if (SPAN_REGISTERED(xpd)) {
			size_t pcm_len = PHONEDEV(xpd).pcm_len;

			if (pcm_len && xpd->card_present) {
				do {
#if 0
					/* FORCE single packet frames */
					pack = NULL;
#endif
					if (xframe && !pack) {	/* FULL frame */
						pcm_frame_out(xbus, xframe);
						xframe = NULL;
						XBUS_COUNTER(xbus,
							     TX_PCM_FRAG)++;
					}
					if (!xframe) {	/* Alloc frame */
						xframe =
						    ALLOC_SEND_XFRAME(xbus);
						if (!xframe) {
							static int rate_limit;

							if ((rate_limit++ % 3001) == 0)
								XBUS_ERR(xbus,
									"%s: failed to allocate new xframe\n",
									__func__);
							return;
						}
					}
					pack =
					    xframe_next_packet(xframe, pcm_len);
				} while (!pack);
				XPACKET_INIT(pack, GLOBAL, PCM_WRITE,
					     xpd->xbus_idx, 1, 0);
				XPACKET_LEN(pack) = pcm_len;
				if (!sent_sync_bit) {
					XPACKET_ADDR_SYNC(pack) = 1;
					sent_sync_bit = 1;
				}
				CALL_PHONE_METHOD(card_pcm_fromspan, xpd, pack);
				XBUS_COUNTER(xbus, TX_PACK_PCM)++;
			}
		}
	}
	if (xframe)		/* clean any leftovers */
		pcm_frame_out(xbus, xframe);
	/*
	 * Receive PCM
	 */
	while ((xframe = xframe_dequeue(&xbus->pcm_tospan)) != NULL) {
		copy_pcm_tospan(xbus, xframe);
		if (XPACKET_ADDR_SYNC((xpacket_t *)xframe->packets)) {
			ktime_t now = ktime_get();
			s64 usec;

			usec = ktime_us_delta(now, xbus->last_rx_sync);
			xbus->last_rx_sync = now;
			/* ignore startup statistics */
			if (likely
			    (atomic_read(&xbus->pcm_rx_counter) >
			     BIG_TICK_INTERVAL)) {
				if (abs(usec - 1000) > TICK_TOLERANCE) {
					static int rate_limit;

					if ((rate_limit++ % 5003) == 0)
						XBUS_DBG(SYNC, xbus,
							"Bad PCM RX timing(%d): usec=%lld.\n",
							rate_limit, usec);
				}
				if (usec > xbus->max_rx_sync)
					xbus->max_rx_sync = usec;
				if (usec < xbus->min_rx_sync)
					xbus->min_rx_sync = usec;
			}
		}
	}
	for (i = 0; i < MAX_XPDS; i++) {
		xpd = xpd_of(xbus, i);
		if (!xpd || !xpd->card_present)
			continue;
		if (IS_PHONEDEV(xpd)) {
			if (SPAN_REGISTERED(xpd)) {
				do_ec(xpd);
				dahdi_receive(&PHONEDEV(xpd).span);
			}
			/* silence was injected */
			PHONEDEV(xpd).silence_pcm = 0;
		}
		xpd->timer_count = xbus->global_counter;
		/*
		 * Must be called *after* tx/rx so
		 * D-Chan counters may be cleared
		 */
		CALL_XMETHOD(card_tick, xpd);
	}
}

static void do_tick(xbus_t *xbus, const ktime_t kt_received)
{
	int counter = atomic_read(&xpp_tick_counter);
	unsigned long flags;

	xbus_command_queue_tick(xbus);
	if (global_ticker == xbus)
		global_tick();	/* called from here or dahdi_sync_tick() */
	spin_lock_irqsave(&ref_ticker_lock, flags);
	xpp_drift_step(xbus, kt_received);
	spin_unlock_irqrestore(&ref_ticker_lock, flags);
	if (likely(xbus->self_ticking))
		xbus_tick(xbus);
	xbus->global_counter = counter;
}

void xframe_receive_pcm(xbus_t *xbus, xframe_t *xframe)
{
	if (!xframe_enqueue(&xbus->pcm_tospan, xframe)) {
		static int rate_limit;

		if ((rate_limit++ % 1003) == 0)
			XBUS_DBG(SYNC, xbus,
				 "Failed to enqueue received pcm frame. (%d)\n",
				 rate_limit);
		FREE_RECV_XFRAME(xbus, xframe);
	}
	/*
	 * The sync_master bit is marked at the first packet
	 * of the frame, regardless of the XPD that is sync master.
	 * FIXME: what about PRI split?
	 */
	if (XPACKET_ADDR_SYNC((xpacket_t *)xframe->packets)) {
		do_tick(xbus, xframe->kt_received);
		atomic_inc(&xbus->pcm_rx_counter);
	} else
		xbus->xbus_frag_count++;
}

int exec_sync_command(const char *buf, size_t count)
{
	int ret = count;
	int xbusno;
	xbus_t *xbus;

	if (strncmp("DAHDI", buf, 6) == 0) {	/* Ignore the newline */
		DBG(SYNC, "DAHDI");
		update_sync_master(NULL, 1);
	} else if (sscanf(buf, "SYNC=%d", &xbusno) == 1) {
		DBG(SYNC, "SYNC=%d\n", xbusno);
		xbus = get_xbus(__func__, xbusno);
		if (xbus == NULL) {
			ERR("No bus %d exists\n", xbusno);
			return -ENXIO;
		}
		update_sync_master(xbus, 0);
		put_xbus(__func__, xbus);
	} else if (sscanf(buf, "QUERY=%d", &xbusno) == 1) {
		DBG(SYNC, "QUERY=%d\n", xbusno);
		xbus = get_xbus(__func__, xbusno);
		if (xbus == NULL) {
			ERR("No bus %d exists\n", xbusno);
			return -ENXIO;
		}
		CALL_PROTO(GLOBAL, SYNC_SOURCE, xbus, NULL, SYNC_MODE_QUERY, 0);
		put_xbus(__func__, xbus);
	} else {
		ERR("%s: cannot parse '%s'\n", __func__, buf);
		ret = -EINVAL;
	}
	return ret;
}

int fill_sync_string(char *buf, size_t count)
{
	int len = 0;

	if (!syncer) {
		len +=
		    snprintf(buf, count, "%s\n",
			     (force_dahdi_sync) ? "DAHDI" : "NO-SYNC");
	} else
		len += snprintf(buf, count, "SYNC=%02d\n", syncer->num);
	return len;
}

int xbus_pcm_init(void *toplevel)
{
	int ret = 0;

#ifdef	OPTIMIZE_CHANMUTE
	INFO("FEATURE: with CHANMUTE optimization (%sactivated)\n",
	     (optimize_chanmute) ? "" : "de");
#endif
#ifdef	DAHDI_SYNC_TICK
	INFO("FEATURE: with sync_tick() from DAHDI\n");
#else
	INFO("FEATURE: without sync_tick() from DAHDI\n");
#endif
	xpp_ticker_init(&global_ticks_series);
	xpp_ticker_init(&dahdi_ticker);
	return ret;
}

void xbus_pcm_shutdown(void)
{
}
