#ifndef	XPROTO_H
#define	XPROTO_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"

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

#define	XPD_TYPE(n)	XPD_TYPE_ ## n
#define	PROTO_TABLE(n)	n ## _protocol_table

typedef enum xpd_type {
	XPD_TYPE(FXO)		= 0x02,
	XPD_TYPE(FXS)		= 0x03,
	XPD_TYPE(NOMODULE)	= 0x0F,
} xpd_type_t;

#define	LINE_BITS	(sizeof(xpp_line_t)*8)
#define	PCM_CHUNKSIZE	(CHANNELS_PERXPD * 8)	/* samples of 8 bytes */

typedef struct xpd_addr {
	byte	card_id:4;
	byte	bank_num:4;
} __attribute__((packed)) xpd_addr_t;

bool valid_xpd_addr(const xpd_addr_t *addr);
int xpd_addr2num(const xpd_addr_t *addr);
void xpd_set_addr(xpd_addr_t *addr, int xpd_num);

#define	XPD_NUM(x)		xpd_addr2num(&x)
#define	XPD_ADDR_SET(x,val)	xpd_set_addr(&x, val)
#define	MAX_XPACKET_DATALEN	100

#define	XPROTO_NAME(card,op)	card ## _ ## op
#define	XPROTO_HANDLER(card,op)	XPROTO_NAME(card,op ## _handler)
#define	XPROTO_CALLER(card,op)	XPROTO_NAME(card,op ## _send)

#define	HANDLER_DEF(card,op)	\
	int XPROTO_HANDLER(card,op) (		\
		xbus_t *xbus,			\
		xpd_t *xpd,			\
		const xproto_entry_t *cmd,	\
		xpacket_t *pack)

#define	CALL_PROTO(card,op, ...)	XPROTO_CALLER(card,op)( __VA_ARGS__ )

#define	DECLARE_CMD(card,op, ...)	\
	int CALL_PROTO(card, op, xbus_t *xbus, xpd_t *xpd, ## __VA_ARGS__ )

#define	HOSTCMD(card, op, ...)					\
			DECLARE_CMD(card, op, ## __VA_ARGS__ );	\
			EXPORT_SYMBOL(XPROTO_CALLER(card, op));	\
			DECLARE_CMD(card, op, ## __VA_ARGS__ )

#define	RPACKET_NAME(card,op)	XPROTO_NAME(RPACKET_ ## card, op)
#define	RPACKET_TYPE(card,op)	struct RPACKET_NAME(card, op)

#define	DEF_RPACKET_DATA(card,op, ...)	\
	struct RPACKET_NAME(card,op) {	\
		byte		opcode;	\
		xpd_addr_t	addr;	\
		__VA_ARGS__		\
	} __attribute__((packed))

#define	RPACKET_CAST(p,card,op)		((RPACKET_TYPE(card,op) *)p)
#define	RPACKET_FIELD(p,card,op,field)	(RPACKET_CAST(p,card,op)->field)
#define	RPACKET_SIZE(card,op)		sizeof(RPACKET_TYPE(card,op))

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

#define	XENTRY(card,op)					\
	[ XPROTO_NAME(card,op) ] {			\
		.handler = XPROTO_HANDLER(card,op),	\
		.datalen = RPACKET_SIZE(card,op),	\
		.name = #op,				\
		.table = &PROTO_TABLE(card)		\
	}


#define	XPACKET_INIT(p, card, op)					\
		do {							\
			p->content.opcode = XPROTO_NAME(card,op);	\
			p->datalen = RPACKET_SIZE(card,op);		\
		} while(0)

#define	XPACKET_NEW(p, xbus, card, op, to)			\
	do {							\
		p = xbus->ops->packet_new(xbus, GFP_ATOMIC);	\
		if(!p)						\
			return -ENOMEM;				\
		XPACKET_INIT(p, card, op);			\
		XPD_ADDR_SET(p->content.addr, to);		\
	} while(0);

typedef struct xproto_entry	xproto_entry_t;
typedef struct xproto_table	xproto_table_t;

typedef int (*xproto_handler_t)(
		xbus_t *xbus,
		xpd_t *xpd,
		const xproto_entry_t *cmd,
		xpacket_t *pack);

const xproto_entry_t *find_xproto_entry(xpd_t *xpd, byte opcode);

const xproto_table_t *get_xproto_table(xpd_type_t cardtype);
const xproto_entry_t *xproto_card_entry(const xproto_table_t *table, byte opcode);
const xproto_handler_t xproto_card_handler(const xproto_table_t *table, byte opcode);

const xproto_entry_t *xproto_global_entry(byte opcode);
const xproto_handler_t xproto_global_handler(byte opcode);

#define	CALL_XMETHOD(name, xbus, xpd, ...)				\
			(xpd)->xops->name(xbus, xpd, ## __VA_ARGS__ )

struct xops {
	 xpd_t *(*card_new)(xbus_t *xbus, int xpd_num, const xproto_table_t *proto_table, byte revision);
	int (*card_init)(xbus_t *xbus, xpd_t *xpd);
	int (*card_remove)(xbus_t *xbus, xpd_t *xpd);
	int (*card_tick)(xbus_t *xbus, xpd_t *xpd);

	int (*SYNC_SOURCE)(xbus_t *xbus, xpd_t *xpd, bool setit, bool is_master);
	int (*PCM_WRITE)(xbus_t *xbus, xpd_t *xpd, xpp_line_t hookstate,  volatile byte *buf);

	int (*CHAN_ENABLE)(xbus_t *xbus, xpd_t *xpd, xpp_line_t lines, bool on);
	int (*CHAN_POWER)(xbus_t *xbus, xpd_t *xpd, xpp_line_t lines, bool on);
	int (*CHAN_CID)(xbus_t *xbus, xpd_t *xpd, xpp_line_t lines);
	int (*RING)(xbus_t *xbus, xpd_t *xpd, int pos, bool on);
	int (*SETHOOK)(xbus_t *xbus, xpd_t *xpd, xpp_line_t hook_status);
	int (*LED)(xbus_t *xbus, xpd_t *xpd, xpp_line_t lines, byte which, bool on);
	int (*RELAY_OUT)(xbus_t *xbus, xpd_t *xpd, byte which, bool on);
};

const xops_t *get_xops(xpd_type_t xpd_type);

#undef	XMETHOD

struct xproto_entry {
	xproto_handler_t	handler;
	int			datalen;
	const char		*name;
	xproto_table_t		*table;
};

struct xproto_table {
	xproto_entry_t	entries[255];	/* Indexed by opcode */
	xops_t		xops;
	xpd_type_t	type;
	const char	*name;
	bool (*packet_is_valid)(xpacket_t *pack);
	void (*packet_dump)(xpacket_t *pack);
};

#include "card_global.h"
#include "card_fxs.h"

enum opcodes {
	XPROTO_NAME(GLOBAL, DESC_REQ)		= 0x04,
	XPROTO_NAME(GLOBAL, DEV_DESC)		= 0x05,
/**/
	XPROTO_NAME(GLOBAL, PCM_WRITE)		= 0x11,
	XPROTO_NAME(GLOBAL, PCM_READ)		= 0x12,
/**/
	XPROTO_NAME(GLOBAL, SYNC_SOURCE)	= 0x19,
	XPROTO_NAME(GLOBAL, SYNC_REPLY)		= 0x1A,

	XPROTO_NAME(FXS, SIG_CHANGED)		= 0x06,
/**/
	XPROTO_NAME(FXS, SLIC_WRITE)		= 0x0F,	/* Write to SLIC */
	XPROTO_NAME(FXS, CHAN_ENABLE)		= 0x0F,	/* Write to SLIC */
	XPROTO_NAME(FXS, CHAN_POWER)		= 0x0F,	/* Write to SLIC */
	XPROTO_NAME(FXS, CHAN_CID)		= 0x0F,	/* Write to SLIC */
	XPROTO_NAME(FXS, RING)			= 0x0F,	/* Write to SLIC */
	XPROTO_NAME(FXS, SETHOOK)		= 0x0F,	/* Write to SLIC */
	XPROTO_NAME(FXS, LED)			= 0x0F,	/* Write to SLIC */
	XPROTO_NAME(FXS, RELAY_OUT)		= 0x0F,	/* Write to SLIC */
	XPROTO_NAME(FXS, SLIC_INIT)		= 0x0F,	/* Write to SLIC */
	XPROTO_NAME(FXS, SLIC_QUERY)		= 0x0F,	/* Write to SLIC */
/**/
	XPROTO_NAME(FXS, SLIC_REPLY)		= 0x10,
};


#define	MEMBER(card,op)	RPACKET_TYPE(card,op)	RPACKET_NAME(card,op)

struct xpacket_raw {
	byte		opcode;
	xpd_addr_t	addr;
	union {
		MEMBER(GLOBAL, DESC_REQ);
		MEMBER(GLOBAL, DEV_DESC);
		MEMBER(GLOBAL, PCM_WRITE);
		MEMBER(GLOBAL, PCM_READ);
		MEMBER(GLOBAL, SYNC_REPLY);

		MEMBER(FXS, SIG_CHANGED);
		MEMBER(FXS, SLIC_REPLY);

		byte	data[0];
	};
} __attribute__((packed));

struct xpacket {
	xpacket_raw_t		content;
	size_t			datalen;
	struct list_head	list;
};

void dump_packet(const char *msg, xpacket_t *packet, bool print_dbg);
int packet_receive(xbus_t *xbus, xpacket_t *pack);
int xproto_register(const xproto_table_t *proto_table);
void xproto_unregister(const xproto_table_t *proto_table);
const xproto_entry_t *xproto_global_entry(byte opcode);
const char *xproto_name(xpd_type_t xpd_type);

#endif	/* XPROTO_H */
