/*****************************************************************************/

/*
 *	sm.h  --  soundcard radio modem driver internal header.
 *
 *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
 *
 *	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.
 *
 *  Please note that the GPL allows you to use the driver, NOT the radio.
 *  In order to use the radio, you need a license from the communications
 *  authority of your country.
 *
 */

#ifndef _SM_H
#define _SM_H

/* ---------------------------------------------------------------------- */

#include <linux/hdlcdrv.h>
#include <linux/soundmodem.h>

#define SM_DEBUG

/* ---------------------------------------------------------------------- */
/*
 * Information that need to be kept for each board.
 */

struct sm_state {
	struct hdlcdrv_state hdrv;

	const struct modem_tx_info *mode_tx;
	const struct modem_rx_info *mode_rx;

	const struct hardware_info *hwdrv;

	/*
	 * Hardware (soundcard) access routines state
	 */
	struct {
		void *ibuf;
		unsigned int ifragsz;
		unsigned int ifragptr;
		unsigned int i16bit;
		void *obuf;
		unsigned int ofragsz;
		unsigned int ofragptr;
		unsigned int o16bit;
		int ptt_cnt;
	} dma;

	union {
		long hw[32/sizeof(long)];
	} hw;

	/*
	 * state of the modem code
	 */
	union {
		long m[32/sizeof(long)];
	} m;
	union {
		long d[256/sizeof(long)];
	} d;

#define DIAGDATALEN 64
	struct diag_data {
		unsigned int mode;
		unsigned int flags;
		volatile int ptr;
		short data[DIAGDATALEN];
	} diag;


#ifdef SM_DEBUG
	struct debug_vals {
		unsigned long last_jiffies;
		unsigned cur_intcnt;
		unsigned last_intcnt;
		unsigned mod_cyc;
		unsigned demod_cyc;
		unsigned dma_residue;
	} debug_vals;
#endif /* SM_DEBUG */
};

/* ---------------------------------------------------------------------- */
/*
 * Mode definition structure
 */

struct modem_tx_info {
	const char *name;
	unsigned int loc_storage;
	int srate;
	int bitrate;
        void (*modulator_u8)(struct sm_state *, unsigned char *, unsigned int);
        void (*modulator_s16)(struct sm_state *, short *, unsigned int);
        void (*init)(struct sm_state *);
};

struct modem_rx_info {
	const char *name;
	unsigned int loc_storage;
	int srate;
	int bitrate;
	unsigned int overlap;
	unsigned int sperbit;
        void (*demodulator_u8)(struct sm_state *, const unsigned char *, unsigned int);
        void (*demodulator_s16)(struct sm_state *, const short *, unsigned int);
        void (*init)(struct sm_state *);
};

/* ---------------------------------------------------------------------- */
/*
 * Soundcard driver definition structure
 */

struct hardware_info {
	char *hw_name; /* used for request_{region,irq,dma} */
	unsigned int loc_storage;
	/*
	 * mode specific open/close
	 */
	int (*open)(struct device *, struct sm_state *);
	int (*close)(struct device *, struct sm_state *);
	int (*ioctl)(struct device *, struct sm_state *, struct ifreq *,
		     struct hdlcdrv_ioctl *, int);
	int (*sethw)(struct device *, struct sm_state *, char *);
};

/* --------------------------------------------------------------------- */

#define min(a, b) (((a) < (b)) ? (a) : (b))
#define max(a, b) (((a) > (b)) ? (a) : (b))

/* --------------------------------------------------------------------- */

extern const char sm_drvname[];
extern const char sm_drvinfo[];

/* --------------------------------------------------------------------- */
/*
 * ===================== diagnostics stuff ===============================
 */

extern inline void diag_trigger(struct sm_state *sm)
{
	if (sm->diag.ptr < 0)
		if (!(sm->diag.flags & SM_DIAGFLAG_DCDGATE) || sm->hdrv.hdlcrx.dcd)
			sm->diag.ptr = 0;
}

/* --------------------------------------------------------------------- */

#define SHRT_MAX ((short)(((unsigned short)(~0U))>>1))
#define SHRT_MIN (-SHRT_MAX-1)

extern inline void diag_add(struct sm_state *sm, int valinp, int valdemod)
{
	int val;

	if ((sm->diag.mode != SM_DIAGMODE_INPUT &&
	     sm->diag.mode != SM_DIAGMODE_DEMOD) ||
	    sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0)
		return;
	val = (sm->diag.mode == SM_DIAGMODE_DEMOD) ? valdemod : valinp;
	/* clip */
	if (val > SHRT_MAX)
		val = SHRT_MAX;
	if (val < SHRT_MIN)
		val = SHRT_MIN;
	sm->diag.data[sm->diag.ptr++] = val;
}

/* --------------------------------------------------------------------- */

extern inline void diag_add_one(struct sm_state *sm, int val)
{
	if ((sm->diag.mode != SM_DIAGMODE_INPUT &&
	     sm->diag.mode != SM_DIAGMODE_DEMOD) ||
	    sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0)
		return;
	/* clip */
	if (val > SHRT_MAX)
		val = SHRT_MAX;
	if (val < SHRT_MIN)
		val = SHRT_MIN;
	sm->diag.data[sm->diag.ptr++] = val;
}

/* --------------------------------------------------------------------- */

static inline void diag_add_constellation(struct sm_state *sm, int vali, int valq)
{
	if ((sm->diag.mode != SM_DIAGMODE_CONSTELLATION) ||
	    sm->diag.ptr >= DIAGDATALEN-1 || sm->diag.ptr < 0)
		return;
	/* clip */
	if (vali > SHRT_MAX)
		vali = SHRT_MAX;
	if (vali < SHRT_MIN)
		vali = SHRT_MIN;
	if (valq > SHRT_MAX)
		valq = SHRT_MAX;
	if (valq < SHRT_MIN)
		valq = SHRT_MIN;
	sm->diag.data[sm->diag.ptr++] = vali;
	sm->diag.data[sm->diag.ptr++] = valq;
}

/* --------------------------------------------------------------------- */
/*
 * ===================== utility functions ===============================
 */

extern inline unsigned int hweight32(unsigned int w)
	__attribute__ ((unused));
extern inline unsigned int hweight16(unsigned short w)
	__attribute__ ((unused));
extern inline unsigned int hweight8(unsigned char w)
        __attribute__ ((unused));

extern inline unsigned int hweight32(unsigned int w)
{
        unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
        res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
        res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);
        res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);
        return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
}

extern inline unsigned int hweight16(unsigned short w)
{
        unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555);
        res = (res & 0x3333) + ((res >> 2) & 0x3333);
        res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F);
        return (res & 0x00FF) + ((res >> 8) & 0x00FF);
}

extern inline unsigned int hweight8(unsigned char w)
{
        unsigned short res = (w & 0x55) + ((w >> 1) & 0x55);
        res = (res & 0x33) + ((res >> 2) & 0x33);
        return (res & 0x0F) + ((res >> 4) & 0x0F);
}

extern inline unsigned int gcd(unsigned int x, unsigned int y)
	__attribute__ ((unused));
extern inline unsigned int lcm(unsigned int x, unsigned int y)
	__attribute__ ((unused));

extern inline unsigned int gcd(unsigned int x, unsigned int y)
{
	for (;;) {
		if (!x)
			return y;
		if (!y)
			return x;
		if (x > y)
			x %= y;
		else
			y %= x;
	}
}

extern inline unsigned int lcm(unsigned int x, unsigned int y)
{
	return x * y / gcd(x, y);
}

/* --------------------------------------------------------------------- */
/*
 * ===================== profiling =======================================
 */


#ifdef __i386__

extern int sm_x86_capability;

#define HAS_RDTSC (sm_x86_capability & 0x10)

/*
 * only do 32bit cycle counter arithmetic; we hope we won't overflow :-)
 * in fact, overflowing modems would require over 2THz clock speeds :-)
 */

#define time_exec(var,cmd)                                              \
({                                                                      \
	if (HAS_RDTSC) {                                                \
		unsigned int cnt1, cnt2, cnt3;                          \
		__asm__(".byte 0x0f,0x31" : "=a" (cnt1), "=d" (cnt3));  \
		cmd;                                                    \
		__asm__(".byte 0x0f,0x31" : "=a" (cnt2), "=d" (cnt3));  \
		var = cnt2-cnt1;                                        \
	} else {                                                        \
		cmd;                                                    \
	}                                                               \
})

#else /* __i386__ */

#define time_exec(var,cmd) cmd

#endif /* __i386__ */

/* --------------------------------------------------------------------- */

extern const struct modem_tx_info sm_afsk1200_tx;
extern const struct modem_tx_info sm_afsk2400_7_tx;
extern const struct modem_tx_info sm_afsk2400_8_tx;
extern const struct modem_tx_info sm_afsk2666_tx;
extern const struct modem_tx_info sm_psk4800_tx;
extern const struct modem_tx_info sm_hapn4800_8_tx;
extern const struct modem_tx_info sm_hapn4800_10_tx;
extern const struct modem_tx_info sm_hapn4800_pm8_tx;
extern const struct modem_tx_info sm_hapn4800_pm10_tx;
extern const struct modem_tx_info sm_fsk9600_4_tx;
extern const struct modem_tx_info sm_fsk9600_5_tx;

extern const struct modem_rx_info sm_afsk1200_rx;
extern const struct modem_rx_info sm_afsk2400_7_rx;
extern const struct modem_rx_info sm_afsk2400_8_rx;
extern const struct modem_rx_info sm_afsk2666_rx;
extern const struct modem_rx_info sm_psk4800_rx;
extern const struct modem_rx_info sm_hapn4800_8_rx;
extern const struct modem_rx_info sm_hapn4800_10_rx;
extern const struct modem_rx_info sm_hapn4800_pm8_rx;
extern const struct modem_rx_info sm_hapn4800_pm10_rx;
extern const struct modem_rx_info sm_fsk9600_4_rx;
extern const struct modem_rx_info sm_fsk9600_5_rx;

extern const struct hardware_info sm_hw_sbc;
extern const struct hardware_info sm_hw_sbcfdx;
extern const struct hardware_info sm_hw_wss;
extern const struct hardware_info sm_hw_wssfdx;

extern const struct modem_tx_info *sm_modem_tx_table[];
extern const struct modem_rx_info *sm_modem_rx_table[];
extern const struct hardware_info *sm_hardware_table[];

/* --------------------------------------------------------------------- */

void sm_output_status(struct sm_state *sm);
/*void sm_output_open(struct sm_state *sm);*/
/*void sm_output_close(struct sm_state *sm);*/

/* --------------------------------------------------------------------- */

extern void inline sm_int_freq(struct sm_state *sm)
{
#ifdef SM_DEBUG
	unsigned long cur_jiffies = jiffies;
	/*
	 * measure the interrupt frequency
	 */
	sm->debug_vals.cur_intcnt++;
	if ((cur_jiffies - sm->debug_vals.last_jiffies) >= HZ) {
		sm->debug_vals.last_jiffies = cur_jiffies;
		sm->debug_vals.last_intcnt = sm->debug_vals.cur_intcnt;
		sm->debug_vals.cur_intcnt = 0;
	}
#endif /* SM_DEBUG */
}

/* --------------------------------------------------------------------- */
#endif /* _SM_H */
