#ifndef	XPD_H
#define	XPD_H

/*
 * Written by Oron Peled <oron@actcom.co.il>
 * Copyright (C) 2004-2005, 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 "xdefs.h"
#include "xproto.h"

#ifdef	__KERNEL__
#include <linux/kernel.h>
#include <asm/atomic.h>
#include <asm/semaphore.h>
#include <linux/moduleparam.h>
#endif

#include <zaptel.h>

#ifdef __KERNEL__
#define	DEF_PARM(type,name,init,desc)	\
	type name = init;	\
	module_param(name, type, 0600);		\
	MODULE_PARM_DESC(name, desc)

#if	LINUX_VERSION_CODE	< KERNEL_VERSION(2,6,10)
/*
 * Old 2.6 kernels had module_param_array() macro that receive the counter
 * by value.
 */
#define	DEF_ARRAY(type,name,count,init,desc)	\
	unsigned int name ## _num_values;	\
	type name[count] = { [0 ... ((count)-1)] = (init) };			\
	module_param_array(name, type, name ## _num_values, 0600);	\
	MODULE_PARM_DESC(name, desc " ( 1-" __MODULE_STRING(count) ")")
#else
#define	DEF_ARRAY(type,name,count,init,desc)	\
	unsigned int name ## _num_values;	\
	type name[count] = {[0 ... ((count)-1)] = init};			\
	module_param_array(name, type, &name ## _num_values, 0600);	\
	MODULE_PARM_DESC(name, desc " ( 1-" __MODULE_STRING(count) ")")
#endif
#endif	// __KERNEL__


#define	MAX_SPANNAME	20
#define	MAX_SPANDESC	40
#define	MAX_CHANNAME	20

#define	XPD_NAMELEN	10	/* must be <= from maximal workqueue name */
#define	XPD_DESCLEN	20
#define	XBUS_NAMELEN	20	/* must be <= from maximal workqueue name */
#define	XBUS_DESCLEN	40

/* Hardware does not check bank_num yet. So only 4 cards can be used */
#define	MAX_XPDS	4	// 1 FXS + 2 E1/T1 + 1 (Quad * E1/T1)

#define	VALID_XPD_NUM(x)	((x) < MAX_XPDS && (x) >= 0)

typedef	struct xbus_ops		xbus_ops_t;

typedef enum xbus_type {
		FIRMWARE_LOOPBACK = 1,
		FIRMWARE_XPP = 2,
} xbus_type_t;

#ifdef	__KERNEL__


typedef struct packet_queue {
	char			qname[XPD_NAMELEN];
	struct list_head	head;
	unsigned int		count;
	unsigned int		worst_count;
	unsigned int		overflows;
	spinlock_t		lock;
} packet_queue_t;

struct xbus_ops {
	int (*packet_send)(xbus_t *xbus, xpacket_t *packet);
	xpacket_t *(*packet_new)(xbus_t *xbus, int flags);
	void (*packet_free)(xbus_t *xbus, xpacket_t *p);
};

enum {
	XBUS_N_DESC_REQ,
	XBUS_N_DEV_DESC,
	XBUS_N_PCM_WRITE,
	XBUS_N_PCM_READ,
	XBUS_N_TX_BYTES,
	XBUS_N_RX_BYTES,
	XBUS_N_SOFTSIM_PACKETS,
	XBUS_N_SIM_PACKETS,
};

#define	XBUS_COUNTER(xbus, counter)	((xbus)->counters[XBUS_N_ ## counter])

#define	C_(x)	[ XBUS_N_ ## x ] = { #x }

/* yucky, make an instance so we can size it... */
static struct xbus_counters {
	char	*name;
} xbus_counters[] = {
	C_(DESC_REQ),
	C_(DEV_DESC),
	C_(PCM_WRITE),
	C_(PCM_READ),
	C_(TX_BYTES),
	C_(RX_BYTES),
	C_(SOFTSIM_PACKETS),
	C_(SIM_PACKETS),
};

#undef C_

#define	XBUS_COUNTER_MAX	ARRAY_SIZE(xbus_counters)

struct xpd_sim {
	bool		simulated;
	bool		softloop_xpd;
	int		loopto;
	xpd_type_t	xpd_type;
	xpp_line_t	hookstate;
};


struct xbus {
	char		busname[XBUS_NAMELEN];	/* only xbus_new set this */
	char		busdesc[XBUS_DESCLEN];	/* lowlevel drivers set this */
	int		num;
	xbus_ops_t	*ops;
	struct xpd	*xpds[MAX_XPDS];

	/* Simulator data */
	xbus_type_t	bus_type;
#if SOFT_SIMULATOR
	struct xpd_sim	sim[MAX_XPDS];
	struct workqueue_struct *sim_workqueue;
	struct work_struct sim_work;		// workqueue job for running simulator
	packet_queue_t	sim_packet_queue;
#endif

	spinlock_t	lock;

	bool		hardware_exists;	/* Hardware is functional */
	int		open_counter;		/* Number of open channels */
	atomic_t	packet_counter;		/* Allocated packets */
	wait_queue_head_t packet_cache_empty;

	struct timer_list poll_timer;
	struct	rw_semaphore in_use;
	int	num_xpds;
	void	*priv;				/* Pointer to transport level data structures */

#ifdef	XPP_PACKET_LOG
	struct cyclic_buff *packet_log;
#endif

#ifdef CONFIG_PROC_FS
	struct proc_dir_entry	*proc_xbus_dir;
	struct proc_dir_entry	*proc_xbus_summary;
#endif

	/* statistics */
	int		counters[XBUS_COUNTER_MAX];

};
#endif

typedef enum xpd_direction {
	TO_PHONE = 0,
	TO_PSTN = 1,
} xpd_direction_t;

#define	LINE_BITS	(sizeof(xpp_line_t)*8)


#ifdef	__KERNEL__
#define	BIT_SET(x,i)	((x) |= (1 << (i)))
#define	BIT_CLR(x,i)	((x) &= ~(1 << (i)))
#define	IS_SET(x,i)	(((x) & (1 << (i))) != 0)
#define	BIT(i)		(1 << (i))

enum {
	XPD_N_PCM_READ,
	XPD_N_PCM_WRITE,
	XPD_N_RECV_ERRORS,
};

#define	XPD_COUNTER(xpd, counter)	((xpd)->counters[XPD_N_ ## counter])

#define	C_(x)	[ XPD_N_ ## x ] = { #x }

/* yucky, make an instance so we can size it... */
static struct xpd_counters {
	char	*name;
} xpd_counters[] = {
	C_(PCM_READ),
	C_(PCM_WRITE),
	C_(RECV_ERRORS),
};

#undef C_

#define	XPD_COUNTER_MAX	(sizeof(xpd_counters)/sizeof(xpd_counters[0]))

enum leds {
	LED_GREEN,
	LED_RED,
	LED_BLUE,
};

#define	NUM_LEDS	3

struct xpd {
	char xpdname[XPD_NAMELEN];
	struct zt_span	span;
	struct zt_chan	*chans;
	int channels;
	xpd_type_t	type;
	xpd_direction_t	direction;		/* TO_PHONE, TO_PSTN */
	xpp_line_t	enabled_chans;		/* hardware activation: 0 - off, 1 - on */
	xpp_line_t	hookstate;		/* 0 - ONHOOK, 1 - OFHOOK */
	xpp_line_t	ledstate[NUM_LEDS];	/* 0 - OFF, 1 - ON */
	xpp_line_t	digital_outputs;	/* 0 - no, 1 - yes */
	xpp_line_t	digital_inputs;		/* 0 - no, 1 - yes */

	int	ringing[CHANNELS_PERXPD];
	bool	ringer_on[CHANNELS_PERXPD];	/* For ring toggling */
	bool	led_on[CHANNELS_PERXPD];	/* For led toggling */
	int	lasttxhook[CHANNELS_PERXPD];

	struct work_struct xpd_post_init;
	xbus_t *xbus;

	spinlock_t	lock;
	atomic_t	open_counter;		/* Number of open channels */

	int		flags;

	unsigned int	board_flags;
#define	XPD_BOARD_LOOPBACK	1

#ifdef CONFIG_PROC_FS
	struct proc_dir_entry	*proc_xpd_dir;
	struct proc_dir_entry	*proc_xpd_summary;
	struct proc_dir_entry	*proc_xpd_ztregister;
#endif
	// Bit numbers of board_flags

	int		counters[XPD_COUNTER_MAX];

	const xops_t	*xops;		/* Card level operations */
	void		*priv;		/* Card level private data */
	atomic_t	card_present;

	unsigned int	recv_errors;
	unsigned int	seq_errors;
	unsigned long	last_response;	/* in jiffies */
	unsigned	id;
	struct list_head xpd_list;
	unsigned int	timer_count;
	volatile u_char *writechunk;	/* Double-word aligned write memory */
	volatile u_char *readchunk;	/* Double-word aligned read memory */
	/* Echo cancelation */
	u_char ec_chunk1[CHANNELS_PERXPD][ZT_CHUNKSIZE];
	u_char ec_chunk2[CHANNELS_PERXPD][ZT_CHUNKSIZE];
};

#endif

#endif	/* XPD_H */
