/* $Id: lr.h,v 1.12 2004/03/06 14:47:32 yumo Exp $ */
/* LRCP: A virtual ethernet device driver for Linux header file. 
 *
 * This device driver is an implementation of Redundancy of Multiple
 * Link Segments (Yumo's original protocol).
 *
 * Author: Yumo (Katsuyuki Yumoto) 2001-2004
 *         yumo@st.rim.or.jp
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#ifndef LR_H
#define LR_H

#define MAX_LR  8
#define MAX_PHYIF 8

#define LR_VER_MAJOR 0
#define LR_VER_MINOR 8
#define LR_VER_PATCH 5

#define LR_MAGIC 0x3eac

/* actions */
#define LRCTL_ADD_PHYIF 1
#define LRCTL_DEL_PHYIF 2
#define LRCTL_GET_PORTLIST 3
#define LRCTL_VERSION 4
#define LRCTL_GET_DISTINFO 5
#define LRCTL_GET_SYSINFO 6
#define LRCTL_GET_GRPINFO 7
#define LRCTL_GET_BFUINFO 8
#define LRCTL_SET_GRPINFO 9

/* for ioctl() */
struct u_dist_info {
	unsigned char m_addr[6];
	unsigned short sgid;
	unsigned short age;
	unsigned char is_bfu;
};

struct u_bfu_info {
	unsigned char gaddr[6];
	unsigned short min_sgid;
	unsigned short age;
};

struct u_sys_info {
	unsigned char sys_id[6];
	unsigned short prio;
};

struct u_group_info {
	unsigned short gid; 
	unsigned char gaddr[6];
	unsigned short perio_time;
	unsigned short age_time;
	int tchg_opt;
	int pii_opt;
	unsigned short lstat; /* group link status */
};

struct u_port_info {
	unsigned short gid; /* group id */
	unsigned short pid; /* port id */
	unsigned short aid; /* active id */
	unsigned short sgid; /* sub-group id */
	char dev_name[8];
	unsigned short lstat; /* link status */
};

struct u_drv_info {
	unsigned char major;
	unsigned char minor;
	unsigned char patch;
	char name[20]; /* driver name */
	char author[50];
};

struct lrconf {
	unsigned short magic;
	int action;

	char pif_name[8];
	char vif_name[8];
	char pif_module[40];
	unsigned int size;
	unsigned char *buf;
};


#ifdef __KERNEL__

/* Linux version dependency */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
/* for 2.2.x */
#define netdevice_start(dev) dev->start = 1
#define netdevice_stop(dev) dev->start = 0
#define netif_start_queue(dev) dev->tbusy = 0
#define netif_stop_queue(dev) dev->tbusy = 1
#define net_device device
#define dev_get_by_name dev_get

#define NET_XMIT_DROP 0
#define NET_XMIT_SUCCESS 0
#define NET_RX_SUCCESS 0
#define NET_RX_DROP 0
#define NET_RX_BAD 0
#else
#define netdevice_start(dev)
#define netdevice_stop(dev)
#endif




/* LRCP constants */

typedef enum _bool {
	_TRUE = 1,
	_FALSE = 0
}bool;

#if 0
static char Slow_Protocols_Multicast[ETH_ALEN] = 
{0x01,0x80,0xc2,0x00,0x00,0x02}; /* Tbl 43B-1 */
#else
static char Slow_Protocols_Multicast[ETH_ALEN] = 
{0xff,0xff,0xff,0xff,0xff,0xff};
#endif
static unsigned short Slow_Protocols_Type = 0x8809; /* Tbl 43B-2 */

static unsigned char LRCP_Subtype = 0x20; /* Link Redundancy Control Protocol */
static unsigned char LRCP_Version = 0x3; /* LRCP version */
static unsigned char FF_Subtype = 0x21; /* Flush Frame */


/* LRCPDU structure */
/* LRCP nodes need to talk each other in order to avoid multiple paths
 * 2001/12/25.
 * 2002/1/5 Introduced priority field. */
struct lrcpdu {
	u8  subtype;			/* 0 */
	u8  version_number;		/* 1 */
	u8	system_id[ETH_ALEN]; /* 2 */
	u16	group_id;			/* 8 */
	u16	subgroup_id;		/* 10 */
	u16 port_id;			/* 12 */
	u16 sys_priority;		/* 14 */
	u8	group_address[ETH_ALEN];	/* 16 */
	u8  pad[1];				/* 22 */
	u8  flags;				/* 23 */
#define RCFFL_TOPOCHG  0x01
	u16	perio_time;			/* 24 */
	u8	pad1[28];			/* 26 */
	/* total length is 64 */
};

#define LRCPDU_LEN sizeof(struct lrcpdu) /* 64 */

#ifndef ETH_P_LR
#define ETH_P_LR	0x001b		/* Linux Link Redundancy */
#endif


/* Timer values */
#define One_Sec 20			/* must be GE 10 */
#define Periodic_Time 1*One_Sec		/* 1 sec */
#define Timeout_Time 3*One_Sec/10	/* 300 msec */
#define Timeout2_Time 5*One_Sec/10	/* 500 msec */
#define Age_Periodic_Time 1*One_Sec	/* 1 sec */
#define Age_Max 300			/* 300 sec */
#define PII_Live_Time 5*One_Sec		/* 5 sec */

/* RCF timer machine */
enum rcftm_state {
	NOT_RUNNING,
	RUNNING,
	TIMEOUT,
	TIMEOUT2
};

/* RCF generator machine */
enum rcfgm_state {
	WAIT_FOR_NTT,
	RCF_GENERATE
};

/* periodic timer machine */
enum perio_state {
	DISABLED,
	WAIT_FOR_TIMEOUT,
	EXPIRED
};

/* Sub group manager machine */
enum sgmm_state {
	INITIALIZE,
	WAIT_FOR_NTR,
	REBUILD_START,
	WAIT_FOR_RCF_NOTIFY,
	RCF_RECEIVED,
	RCF_TIMEOUT,
	NOT_CHANGED,
	CHANGED
};


/* timers */
struct lr_timer {
	struct lr_timer *next;
	int exp;
	int tim;
	bool active;
	void (*tick_func)(struct lr_timer *vtp); /* tick function */
};



#define MAX_RING 128


struct ring_buf {
	int r;
	int w;
	unsigned char *buf[MAX_RING];
};

static void ring_init(struct ring_buf *rb)
{
	rb->r = 0;
	rb->w = 0;
	memset(rb->buf, 0, MAX_RING*(sizeof(char *)));
}

static void ring_flush(struct ring_buf *rb)
{
	int i;
	rb->r = 0;
	rb->w = 0;
	for(i = 0; i < MAX_RING; i++){
		if (rb->buf[i]){
			kfree(rb->buf[i]);
			rb->buf[i] = NULL;
		}
	}
}
static bool ring_empty(struct ring_buf *rb)
{
	return (rb->r == rb->w)?_TRUE:_FALSE;
}

static void ring_put(struct ring_buf *rb, char *buf, int n)
{
	if (rb->buf[rb->w]){
		kfree(rb->buf[rb->w]);
		rb->buf[rb->w] = NULL;
	}

	rb->buf[rb->w] = kmalloc(n+5, GFP_ATOMIC);
	if (!rb->buf[rb->w]){
		return;
	}
	memcpy(rb->buf[rb->w], buf, n);
	if (++rb->w == MAX_RING){
		rb->w = 0;
	}
	if (rb->w == rb->r){
		if (++rb->r == MAX_RING){
			rb->r = 0;
		}
	}
	return;
}


static bool ring_get(struct ring_buf *rb, char *buf, int n)
{
	if (ring_empty(rb))
		return _FALSE;

	memcpy(buf, rb->buf[rb->r], n);
	kfree(rb->buf[rb->r]);
	rb->buf[rb->r] = NULL;
	if (++rb->r == MAX_RING){
		rb->r = 0;
	}
	return _TRUE;
}


#define DO_IT(x) \
	if (m->do_it){ \
		x; \
		m->do_it = _FALSE; \
	}

#define GO_NEXT_STATE(x) \
	m->do_it = _TRUE; \
	m->state = x;


/* state machines */
struct sgm_mach { /* each group has this */
	enum sgmm_state state;
	bool do_it;
	int cnt;
};

struct rcft_mach { /* each group has this */
	enum rcftm_state state;
	bool do_it;
};

struct rcfg_mach { /* each group has this */
	enum rcfgm_state state;
	bool do_it;
};

struct perio_mach { /* each group has this */
	enum perio_state state;
	bool do_it;
};


/* Broadcast Filtering Unit (BFU) */
#if 0
extern struct glist_ent {};
extern struct group_var {};
#endif
struct sglist_ent {
	struct sglist_ent *next;
	struct glist_ent *gle;

	unsigned short sgid;
	unsigned short rcv_sgid;
	int age;
};

struct glist_ent {
	struct glist_ent *next;
	struct glist_ent *next2;
	struct sglist_ent *sgl;
	struct sysid_ent *syside;
	struct group_var *gv;

	unsigned short gid;
	unsigned short min_sgid;
	unsigned char gaddr[ETH_ALEN];

	atomic_t use_count;
};

struct sysid_ent {
	struct sysid_ent *next;
	struct group_var *gv;

	struct glist_ent *gl;
	unsigned char sysid[ETH_ALEN];
};





struct dist_info {
	struct dist_info *next;
	u8	m_addr[ETH_ALEN];
	u16	sgid;
	u16 age;
	u8	bfu_mark;

	atomic_t use_count;
};



/* SGMT entry */
struct sgmte {
	struct sgmte *next;
	u16		port_id;
	u16		active_id;
	u16		sgid;
	bool	active;
};

/* RCT entry */
struct rcte {
	struct rcte *next;
	u16		port_id;
};



/* System scope variables */
struct system_var {
	u8 system_id[ETH_ALEN];
	u16 priority;
} lr_sysvar;



/* Port scope variables */
struct port_var {
	struct sgmte local_sgmt;
	struct rcte *rct;
	int prev_rcf_cnt;
	u16 group_id;
	bool	rcft_running;
};

struct nrcf {
	unsigned short port_id;
	unsigned short active_id;
};
	
/* Sub-group scope variables */
struct subgroup_var {
	unsigned short sgid;
	unsigned short active_id;
};


/* Group scope variables */
struct group_var {
	int group_id;
	u8	group_addr[ETH_ALEN];
	int	port_list[MAX_PHYIF];
	struct subgroup_var	subg_list[MAX_PHYIF];
	int perio_time;
	int admin_perio_time;
	int age_time;

	/* Phase II compatibility */
	int pii_live;

	struct sgmte	master_sgmt[MAX_PHYIF];
	struct sgmte	temp_sgmt[MAX_PHYIF];
	struct dist_info	*dist_info[32];
	bool	ntt;
	bool	ntr;
	bool	rcft_running;
	bool	sgmt_match;
	bool	begin;

	/* BFU */
	struct sysid_ent *sysidl;
	struct glist_ent *gl[32];

	bool	tchg_notified;
	bool	tchg_detected;
	bool	tchg_opt;

	bool	pii_opt;
	u16		agecnt;

	struct ring_buf rb;

	/* state machines */
	struct rcft_mach rcftm;
	struct rcfg_mach rcfgm;
	struct perio_mach prm;
	struct sgm_mach sgmm;

	/* timers */
	struct lr_timer *vth;
	struct lr_timer rcf_timer;
	struct lr_timer periodic_timer;

	spinlock_t lock; /* lr device scope lock */
	spinlock_t dist_lock; /* distinfo lock */
	spinlock_t bfu_lock; /* BFU */
};



/* lr port structure */
struct lr_port {
	struct port_var pv;
	struct net_device *dev;
	char module_name[40]; /* module name of physical i/f */
};

/* lr group structure */
struct lr_group {
	struct group_var var;
	struct net_device *dev;
	struct timer_list tl;
};


/* lr_private is built-in device structure */
struct lr_private {
	struct net_device_stats enet_stats;
	struct lr_group *group;
};

/* These are pool of ports */
static struct lr_port lr_ports[MAX_PHYIF];
static struct lr_group lr_groups[MAX_PHYIF];

#endif /* __KERNEL__ */
#endif /* LR_H */

/*
 * Local variables:
 *  c-indent-level: 4
 *  c-basic-offset: 4
 *  tab-width: 4
 *  indent-tabs-mode: t
 * End:
 */
