#ifndef	XPP_PROTO_H
#define	XPP_PROTO_H

#include "xpd.h"
#include "slic.h"
#ifdef	__KERNEL__
#include <linux/list.h>
#endif

#define	PCM_CHUNKSIZE	(CHANNELS_PERXPD * ZT_MAX_CHUNKSIZE)

typedef enum xpp_opcode {
	XPP_NOTIMP		= 0x00,
//
	XPP_DESC_REQ		= 0x04,
	XPP_DEV_DESC		= 0x05,
//
	XPP_SIG_CHANGED		= 0x06,
//
	XPP_SLIC_WRITE		= 0x0F,	// Write to SLIC
	XPP_CHAN_ENABLE		= 0x0F,	// Write to SLIC
	XPP_CHAN_POWER		= 0x0F,	// Write to SLIC
	XPP_RING		= 0x0F,	// Write to SLIC
	XPP_SETHOOK		= 0x0F,	// Write to SLIC
	XPP_LED			= 0x0F,	// Write to SLIC
	XPP_RELAY_OUT		= 0x0F,	// Write to SLIC
	XPP_SLIC_INIT		= 0x0F,	// Write to SLIC
	XPP_SLIC_QUERY		= 0x0F,	// Write to SLIC
//
	XPP_SLIC_REPLY		= 0x10,
//
	XPP_PCM_WRITE		= 0x11,
	XPP_PCM_READ		= 0x12,
//
	XPP_PCM_GEN		= 0x13,
//
	XPP_SYNC_SOURCE		= 0x19,
	XPP_SYNC_REPLY		= 0x1A,
//
	XPP_LOOPBACK_AX		= 0x31,
	XPP_LOOPBACK_XA		= 0x32,
	XPP_DIAG_FE		= 0xFE,
} xpp_opcode_t;

/*------------------------- PROTOCOL COMMANDS ----------------------*/

#define	XPP_MAX_DATA		50

typedef	struct slic_data {
	byte	len;
	byte	data[40];
} __attribute__((packed)) slic_data_t;

#define	PROTO_FUNC(name)	xpp_proto_ ## name
#define	CALL_PROTO(name, ...)	PROTO_FUNC(name)( __VA_ARGS__ )
#define	DECLARE_CMD(name, ...)	\
	int CALL_PROTO(name, xbus_t *xbus, xpd_t *xpd, ## __VA_ARGS__ )

/* 0x04 */ DECLARE_CMD(DESC_REQ, int xpd_num);
/* 0x0F */ DECLARE_CMD(CHAN_ENABLE, xpp_line_t lines, bool on);
/* 0x0F */ DECLARE_CMD(CHAN_POWER, xpp_line_t lines, bool on);
/* 0x0F */ DECLARE_CMD(RING, int pos, bool on);
/* 0x0F */ DECLARE_CMD(SETHOOK, xpp_line_t hook_status);
/* 0x0F */ DECLARE_CMD(LED, xpp_line_t lines, byte which, bool on);
/* 0x0F */ DECLARE_CMD(RELAY_OUT, byte which, bool on);
/* 0x0F */ DECLARE_CMD(SLIC_INIT);
/* 0x0F */ DECLARE_CMD(SLIC_QUERY, int pos, byte reg_num);
/* 0x11 */ DECLARE_CMD(PCM_WRITE, xpp_line_t hookstate,  volatile byte *buf);
/* 0x13 */ DECLARE_CMD(PCM_GEN, xpp_line_t lines,  volatile byte *buf);
/* 0x19 */ DECLARE_CMD(SYNC_SOURCE, bool setit, bool is_master);
/* 0x31 */ DECLARE_CMD(LOOPBACK_AX, byte *data, unsigned int size);

#define	H_(op, ...)	struct { \
				__VA_ARGS__ \
	} __attribute__((packed)) cmd_##op

/*
 * This struct must be packed exactly as the wire
 * representation of the packet header after the
 * XPD address byte
 */
typedef struct xpp_packet_r {
	byte		opcode;
	xpp_addr_t	addr;
	union {

		H_(NOTIMP);
		H_(DESC_REQ);
		H_(DEV_DESC,
			byte		rev;					/* Revision number */
			byte		type;
			xpp_line_t	line_status;	/* hook/ring status, depending on unit */
			);

		H_(SIG_CHANGED,
			byte		type;		/* unused -- we have it from DEV_DESC */
			xpp_line_t	sig_status;	/* channels: lsb=1, msb=8 */
			xpp_line_t	sig_toggles;	/* channels: lsb=1, msb=8 */
			);

		H_(SLIC_INIT,
			xpp_line_t      lines;
			slic_data_t     slic_data;
			);
		H_(SLIC_WRITE,
			slic_cmd_t	slic_cmd;
			);
		H_(SLIC_REPLY,					/* Get status of a single SLIC (for debugging) */
			xpp_line_t	lines;
			slic_reply_t	info;
			);

		H_(PCM_WRITE,
			xpp_line_t	lines;	// Must be 0xFF
			byte		pcm[PCM_CHUNKSIZE];
			);
		H_(PCM_READ,
			xpp_line_t	lines;	// Must be 0xFF
			byte		pcm[PCM_CHUNKSIZE];
			);

		H_(PCM_GEN,
			xpp_line_t	lines;
			byte		gen;
			byte		pcm_seq[ZT_CHUNKSIZE];
			);

		H_(SYNC_SOURCE,
			byte		mask;
			);
		H_(SYNC_REPLY,
			byte		mask;
			);

		H_(LOOPBACK_AX,
			byte		data[XPP_MAX_DATA];	// FIXME: what data size?
			);
		H_(LOOPBACK_XA,
			byte		data[XPP_MAX_DATA];	// FIXME: what data size?
			);
		H_(DIAG_FE);
		unsigned char	raw[0];
	};
} __attribute__((packed)) xpp_packet_r_t;
#undef	H_

#ifdef	__KERNEL__

enum {
	XPP_PACKET_FIREANDFORGET	= 0x1,
};

/**
 * The packet that will actually be sent on the wire.
 *
 * TODO: not a good medium-level abstrction
 */
struct xpp_packet {
	struct xpp_packet_r	content;
	unsigned int		flags;
	size_t				datalen;
	struct list_head	list;
};

#define DATA_LEN(p, name) \
	sizeof(p->content.cmd_ ## name)

#define	PACKET_LEN(p) \
	((p)->datalen + sizeof(xpp_addr_t) + 1)

#define	PACKET_INIT(p, name)		\
	p->content.opcode = XPP_ ## name;	\
	p->datalen = DATA_LEN(p, name)

#define	PACKET_FIELD(p, name, field)	\
	p->content.cmd_ ## name.field

void dump_packet(const char *msg, xpacket_t *packet, bool print_dbg);
void enqueue_xmit(xbus_t *xbus, xpacket_t *pack);
void process_sim_queue(void *xbus);
int validate_reply(xpacket_t *reply);
int packet_receive(xbus_t *xbus, xpacket_t *pack);
int proc_xpd_slic_write(struct file *file, const char __user *buffer, unsigned long count, void *data);
int proc_xpd_slic_read(char *page, char **start, off_t off, int count, int *eof, void *data);

#endif

#endif	/* XPP_PROTO_H */
