/*****************************************************************************
* wanproc.c	WAN Router Module. /proc filesystem interface.
*
*		This module is completely hardware-independent and provides
*		access to the router using Linux /proc filesystem.
*
* Author: 	Gideon Hack	
* 		Nenad Corbic
*
* Copyright:	(c) 1995-2004 Sangoma Technologies Inc.
*
*		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.
* ============================================================================
* Aug 20, 2001  Alex Feldman  	Support SNMP.
* May 25, 2001  Alex Feldman  	Added T1/E1 support (TE1).
* May 23, 2001  Nenad Corbic 	Bug fix supplied by Akash Jain. If the user
*                               copy fails free up allocated memory.
* Jun 02, 1999  Gideon Hack	Updates for Linux 2.2.X kernels.
* Jun 29, 1997	Alan Cox	Merged with 1.0.3 vendor code
* Jan 29, 1997	Gene Kozin	v1.0.1. Implemented /proc read routines
* Jan 30, 1997	Alan Cox	Hacked around for 2.1
* Dec 13, 1996	Gene Kozin	Initial version (based on Sangoma's WANPIPE)
*****************************************************************************/
#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
# include <wanpipe_includes.h>
# include <wanpipe_version.h>
# include <wanpipe_defines.h>
# include <wanpipe_debug.h>
# include <wanpipe_common.h>
# include <wanpipe.h>	/* WAN router API definitions */
# include <sdladrv.h>
# include <wanproc.h>
# include <if_wanpipe_common.h>
# define STATIC	
# define CONFIG_PROC_FS
#else
# include <linux/wanpipe_includes.h>
# include <linux/wanpipe_defines.h>
# include <linux/wanpipe_debug.h>
# include <linux/wanpipe.h>	/* WAN router API definitions */
# include <linux/sdladrv.h>
# include <linux/wanproc.h>
# include <linux/if_wanpipe_common.h>
# define STATIC	static
#endif
 
#if defined(LINUX_2_1) || defined(LINUX_2_4)
# ifndef proc_mkdir
#  define proc_mkdir(buf, usbdir) create_proc_entry(buf, S_IFDIR, usbdir)
# endif
#endif

#if defined(LINUX_2_6)
# define M_STOP_CNT(m) NULL
#else
# define M_STOP_CNT(m) &m->stop_cnt
#endif

#define PROC_STATS_1_FORMAT "%25s: %10lu\n"
#define PROC_STATS_2_FORMAT "%25s: %10lu %25s: %10lu\n"
#define PROC_STATS_ALARM_FORMAT "%25s: %10s %25s: %10s\n"
#define PROC_STATS_STR_FORMAT "%25s: %10s\n"

/****** Defines and Macros **************************************************/

#ifndef	wp_min
#define wp_min(a,b) (((a)<(b))?(a):(b))
#endif
#ifndef	wp_max
#define wp_max(a,b) (((a)>(b))?(a):(b))
#endif

#define	PROC_BUFSZ	4000	/* buffer size for printing proc info */

#define PROT_UNKNOWN	"Unknown"
#define PROT_DECODE(prot,cap) ((prot == WANCONFIG_FR) ? ((cap)?"FR":"fr") :\
		                (prot == WANCONFIG_MFR) ? ((cap)?"FR":"fr") : \
			        (prot == WANCONFIG_X25) ? ((cap)?"X25":"x25") : \
			          (prot == WANCONFIG_PPP) ? ((cap)?"PPP":"ppp") : \
				    (prot == WANCONFIG_CHDLC) ? ((cap)?"CHDLC":"chdlc") :\
				     (prot == WANCONFIG_ADSL) ? ((cap)?"ADSL":"adsl") :\
				      (prot == WANCONFIG_MPPP) ? ((cap)?"S51X":"s51x") : \
				       (prot == WANCONFIG_SDLC) ? ((cap)?"SDLC":"sdlc") : \
				       (prot == WANCONFIG_ATM) ? ((cap)?"ATM":"atm") : \
				       (prot == WANCONFIG_AFT) ? ((cap)?"AFT TE1":"aft te1") : \
				       (prot == WANCONFIG_AFT_SERIAL) ? ((cap)?"A-SERIAL":"aft serial") : \
				       (prot == WANCONFIG_AFT_ANALOG) ? ((cap)?"A-ANALOG":"aft analog") : \
				       (prot == WANCONFIG_AFT_ISDN_BRI) ? ((cap)?"AFT ISDN":"aft isdn") : \
				       (prot == WANCONFIG_AFT_56K) ? ((cap)?"AFT 56K":"aft 56k") : \
				       (prot == WANCONFIG_AFT_TE1) ? ((cap)?"AFT TE1":"aft te1") : \
				       (prot == WANCONFIG_AFT_TE3) ? ((cap)?"AFT TE3":"aft te3") : \
				        PROT_UNKNOWN )
	
#define CARD_DECODE(wandev)    ((wandev->card_type == WANOPT_ADSL) ? "ADSL" : \
				(wandev->card_type == WANOPT_S50X) ? "S508" : \
				(wandev->card_type == WANOPT_AFT) ? "AFT TE1" : \
				(wandev->card_type == WANOPT_AFT_101) ? "A101" : \
				(wandev->card_type == WANOPT_AFT_102) ? "A102" : \
				(wandev->card_type == WANOPT_AFT_104) ? "A104" : \
				(wandev->card_type == WANOPT_AFT_108) ? "A108" : \
				(wandev->card_type == WANOPT_AFT_300) ? "A301" : \
				(wandev->card_type == WANOPT_AFT_ANALOG) ? "A200/A400" : \
				(wandev->card_type == WANOPT_AFT_ISDN) ? "A500" : \
				(wandev->card_type == WANOPT_AFT_SERIAL) ? "A14X" : \
				(wandev->fe_iface.get_fe_media_string) ? 			\
		 		wandev->fe_iface.get_fe_media_string() : "S514")


/****** Data Types **********************************************************/
#if defined(__LINUX__)
typedef struct wan_stat_entry
{
	struct wan_stat_entry *next;
	char *description;		/* description string */
	void *data;			/* -> data */
	unsigned data_type;		/* data type */
} wan_stat_entry_t;

typedef struct wan_proc_entry
{
	struct proc_dir_entry *protocol_entry;
	int count;
} wan_proc_entry_t;
#endif
/****** Function Prototypes *************************************************/

extern wan_spinlock_t 		wan_devlist_lock;
extern struct wan_devlist_	wan_devlist;

#if defined(CONFIG_PROC_FS)

#define CONF_PCI_FRM	 "%-12s| %-13s| %-9s| %-4u| %-8u| %-5u| %-4s| %-10u|\n"
#define CONF_ISA_FRM	 "%-12s| %-13s| %-9s| %-4u| 0x%-6X| %-5u| %-4s| %-10u|\n"
#define MAP_FRM		 "%-12s| %-50s\n"
#define INTERFACES_FRM	 "%-15s| %-12s| %-6u| %-18s|\n"
#define PROC_FR_FRM	 "%-30s\n"
#define PROC_FR_CFG_FRM	 "%-15s| %-12s| %-5u|\n"
#define PROC_FR_STAT_FRM "%-15s| %-12s| %-14s|\n"

#ifdef CONFIG_PRODUCT_WANPIPE_ANNEXG
 #define STAT_FRM	 "%-12s| %-9s| %-8s| %-14s| %-3u | %-3u | %-3u | %-3u | %-3u | %-3u | %-3u | %-3u | %-3u | %-3u |\n"
#else
 #define STAT_FRM	 "%-12s| %-9s| %-8s| %-14s|\n"
#endif


/* NEW_PROC */
/* Strings */
static char conf_hdr[] =
	"Device name | Protocol Map | Adapter  | IRQ | Slot/IO "
	"| If's | CLK | Baud rate |\n";
#if defined(__LINUX__)
static char map_hdr[] =
	"Device name | Protocol Map \n";
#endif

#ifdef CONFIG_PRODUCT_WANPIPE_ANNEXG
static char stat_hdr[] =
	"Device name | Protocol | Station | Status        |  Wanpipe  |    Lapb   |  X25 Link |  X25 Svc  |    Dsp    |\n";
#else
static char stat_hdr[] =
	"Device name | Protocol | Station | Status        |\n";
#endif
	
static char interfaces_hdr[] =
	"Interface name | Device name | Media | Operational State |\n";

#if defined(__LINUX__)

/* Proc filesystem interface */
#ifndef LINUX_2_6
static int router_proc_perms(struct inode *, int);
static ssize_t router_proc_read(struct file*, char*, size_t,loff_t*);
static ssize_t router_proc_write(struct file*, const char*, size_t,loff_t*);
#endif

/* Methods for preparing data for reading proc entries */

#if defined(LINUX_2_6)
static int config_get_info(struct seq_file *m, void *v);
static int status_get_info(struct seq_file *m, void *v);
static int probe_get_info(struct seq_file *m, void *v);
static int probe_get_info_legacy(struct seq_file *m, void *v);
static int probe_get_info_verbose(struct seq_file *m, void *v);
static int wandev_get_info(struct seq_file *m, void *v);

static int map_get_info(struct seq_file *m, void *v);
static int interfaces_get_info(struct seq_file *m, void *v);

static int get_dev_config_info(struct seq_file *m, void *v);
static int get_dev_status_info(struct seq_file *m, void *v);

static int wandev_mapdir_get_info(struct seq_file *m, void *v);

#elif defined(LINUX_2_4)
static int config_get_info(char* buf, char** start, off_t offs, int len);
static int status_get_info(char* buf, char** start, off_t offs, int len);
static int probe_get_info(char* buf, char** start, off_t offs, int len);
static int probe_get_info_legacy(char* buf, char** start, off_t offs, int len);
static int probe_get_info_verbose(char* buf, char** start, off_t offs, int len);
static int wandev_get_info(char* buf, char** start, off_t offs, int len);

static int map_get_info(char* buf, char** start, off_t offs, int len);
static int interfaces_get_info(char* buf, char** start, off_t offs, int len);

static int get_dev_config_info(char* buf, char** start, off_t offs, int len);
static int get_dev_status_info(char* buf, char** start, off_t offs, int len);

static int wandev_mapdir_get_info(char* buf, char** start, off_t offs, int len);

#else
static int config_get_info(char* buf, char** start, off_t offs, int len, int dummy);
static int status_get_info(char* buf, char** start, off_t offs, int len, int dummy);
static int probe_get_info(char* buf, char** start, off_t offs, int len, int dummy);
static int probe_get_info_legacy(char* buf, char** start, off_t offs, int len, int dummy);
static int probe_get_info_verbose(char* buf, char** start, off_t offs, int len, int dummy);
static int wandev_get_info(char* buf, char** start, off_t offs, int len, int dummy);

static int map_get_info(char* buf, char** start, off_t offs, int len, int dummy);
static int interfaces_get_info(char* buf, char** start, off_t offs, int len, int dummy);

static int get_dev_config_info(char* buf, char** start, off_t offs, int len, int dummy);
static int get_dev_status_info(char* buf, char** start, off_t offs, int len, int dummy);
#endif


/* Miscellaneous */

/*
 *	Structures for interfacing with the /proc filesystem.
 *	Router creates its own directory /proc/net/wanrouter with the folowing
 *	entries:
 *	config		device configuration
 *	status		global device statistics
 *	<device>	entry for each WAN device
 */

/*
 *	Generic /proc/net/wanrouter/<file> file and inode operations 
 */



struct proc_dir_entry *proc_router;
static struct proc_dir_entry *map_dir;

#ifdef LINUX_2_6

static int config_open(struct inode *inode, struct file *file)
{
	return single_open(file, config_get_info, WP_PDE(inode)->data);
}

static int status_open(struct inode *inode, struct file *file)
{
	return single_open(file, status_get_info, WP_PDE(inode)->data);
}

static struct file_operations config_fops = {
	.owner	 = THIS_MODULE,
	.open	 = config_open,
	.read	 = seq_read,
	.llseek	 = seq_lseek,
	.release = seq_release,
};

static struct file_operations status_fops = {
	.owner	 = THIS_MODULE,
	.open	 = status_open,
	.read	 = seq_read,
	.llseek	 = seq_lseek,
	.release = seq_release,
};

static int wandev_open(struct inode *inode, struct file *file)
{
	return single_open(file, wandev_get_info, WP_PDE(inode)->data);
}

static struct file_operations wandev_fops = {
	.owner	 = THIS_MODULE,
	.open	 = wandev_open,
	.read	 = seq_read,
	.llseek	 = seq_lseek,
	.release = single_release,
	.ioctl	 = wanrouter_ioctl,
};

static int wp_hwprobe_open(struct inode *inode, struct file *file)
{
 	return single_open(file, probe_get_info, WP_PDE(inode)->data);
}

static struct file_operations wp_hwprobe_fops = {
	.owner	 = THIS_MODULE,
	.open	 = wp_hwprobe_open,
	.read	 = seq_read,
	.llseek	 = seq_lseek,
	.release = single_release,
};

static int wp_hwprobe_legacy_open(struct inode *inode, struct file *file)
{
 	return single_open(file, probe_get_info_legacy, WP_PDE(inode)->data);
}

static struct file_operations wp_hwprobe_legacy_fops = {
	.owner	 = THIS_MODULE,
	.open	 = wp_hwprobe_legacy_open,
	.read	 = seq_read,
	.llseek	 = seq_lseek,
	.release = single_release,
};

static int wp_hwprobe_verbose_open(struct inode *inode, struct file *file)
{
 	return single_open(file, probe_get_info_verbose, WP_PDE(inode)->data);
}

static struct file_operations wp_hwprobe_verbose_fops = {
	.owner	 = THIS_MODULE,
	.open	 = wp_hwprobe_verbose_open,
	.read	 = seq_read,
	.llseek	 = seq_lseek,
	.release = single_release,
};

static int wp_map_open(struct inode *inode, struct file *file)
{
 	return single_open(file, map_get_info, WP_PDE(inode)->data);
}

static struct file_operations wp_map_fops = {
	.owner	 = THIS_MODULE,
	.open	 = wp_map_open,
	.read	 = seq_read,
	.llseek	 = seq_lseek,
	.release = single_release,
};

static int wp_iface_open(struct inode *inode, struct file *file)
{
 	return single_open(file, interfaces_get_info, WP_PDE(inode)->data);
}

static struct file_operations wp_iface_fops = {
	.owner	 = THIS_MODULE,
	.open	 = wp_iface_open,
	.read	 = seq_read,
	.llseek	 = seq_lseek,
	.release = single_release,
};

static int wandev_mapdir_open(struct inode *inode, struct file *file)
{
 	return single_open(file, wandev_mapdir_get_info, WP_PDE(inode)->data);
}

static struct file_operations wandev_mapdir_fops = {
	.owner	 = THIS_MODULE,
	.open	 = wandev_mapdir_open,
	.read	 = seq_read,
	.llseek	 = seq_lseek,
	.release = single_release,
};

static int  wp_get_dev_config_open(struct inode *inode, struct file *file)
{
 	return single_open(file, get_dev_config_info, WP_PDE(inode)->data);
}

static struct file_operations wp_get_dev_config_fops = {
	.owner	 = THIS_MODULE,
	.open	 =  wp_get_dev_config_open,
	.read	 = seq_read,
	.llseek	 = seq_lseek,
	.release = single_release,
};

static int wp_get_dev_status_open(struct inode *inode, struct file *file)
{
 	return single_open(file, get_dev_status_info, WP_PDE(inode)->data);
}

static struct file_operations wp_get_dev_status_fops = {
	.owner	 = THIS_MODULE,
	.open	 =  wp_get_dev_status_open,
	.read	 = seq_read,
	.llseek	 = seq_lseek,
	.release = single_release,
};


#if 0
static int  wp_prot_dev_config_open(struct inode *inode, struct file *file)
{
	return 0;
 	//return single_open(file, get_dev_status_info, WP_PDE(inode)->data);
}

static struct file_operations wp_prot_dev_config_fops = {
	.owner	 = THIS_MODULE,
	.open	 =  wp_prot_dev_config_open,
	.read	 = seq_read,
	.llseek	 = seq_lseek,
	.release = single_release,
};
#endif

#elif defined(LINUX_2_4)

static struct file_operations router_fops =
{
	read:		router_proc_read,
	write:		router_proc_write,
};

static struct inode_operations router_inode =
{
	permission:	router_proc_perms,
};

/*
 *	/proc/net/wanrouter/<device> file operations
 */

static struct file_operations wandev_fops =
{
	read:		router_proc_read,
	ioctl:		wanrouter_ioctl,
};

/*
 *	/proc/net/wanrouter 
 */


#else

static struct file_operations router_fops =
{
	NULL,			/* lseek   */
	router_proc_read,	/* read	   */
	router_proc_write,	/* write   */
	NULL,			/* readdir */
	NULL,			/* select  */
	NULL,			/* ioctl   */
	NULL,			/* mmap	   */
	NULL,			/* no special open code	   */
	NULL,			/* flush */
	NULL,			/* no special release code */
	NULL			/* can't fsync */
};

static struct inode_operations router_inode =
{
	&router_fops,
	NULL,			/* create */
	NULL,			/* lookup */
	NULL,			/* link */
	NULL,			/* unlink */
	NULL,			/* symlink */
	NULL,			/* mkdir */
	NULL,			/* rmdir */
	NULL,			/* mknod */
	NULL,			/* rename */
	NULL,			/* follow link */
	NULL,			/* readlink */
	NULL,			/* readpage */
	NULL,			/* writepage */
	NULL,			/* bmap */
	NULL,			/* truncate */
	router_proc_perms
};


static struct file_operations wandev_fops =
{
	NULL,			/* lseek   */
	router_proc_read,	/* read	   */
	NULL,			/* write   */
	NULL,			/* readdir */
	NULL,			/* select  */
	wanrouter_ioctl,	/* ioctl   */
	NULL,			/* mmap	   */
	NULL,			/* no special open code	   */
	NULL,			/* flush */
	NULL,			/* no special release code */
	NULL			/* can't fsync */
};

static struct inode_operations wandev_inode =
{
	&wandev_fops,
	NULL,			/* create */
	NULL,			/* lookup */
	NULL,			/* link */
	NULL,			/* unlink */
	NULL,			/* symlink */
	NULL,			/* mkdir */
	NULL,			/* rmdir */
	NULL,			/* mknod */
	NULL,			/* rename */
	NULL,			/* readlink */
	NULL,			/* follow_link */
	NULL,			/* readpage */
	NULL,			/* writepage */
	NULL,			/* bmap */
	NULL,			/* truncate */
	router_proc_perms
};

#endif


/*
 *	/proc/net/wanrouter/<protocol>
 */
wan_proc_entry_t proc_router_fr;
wan_proc_entry_t proc_router_chdlc;
wan_proc_entry_t proc_router_ppp;
wan_proc_entry_t proc_router_x25;

#endif /* __LINUX__ */

/*
 *	Interface functions
 */

/*
 *	Prepare data for reading 'Config' entry.
 *	Return length of data.
 */
#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(LINUX_2_4)
STATIC int config_get_info(char* buf, char** start, off_t offs, int len)
#else
#if defined(LINUX_2_6)
static int config_get_info(struct seq_file *m, void *v)
#elif defined(LINUX_2_4)
static int config_get_info(char* buf, char** start, off_t offs, int len)
#else
static int config_get_info(char* buf, char** start, off_t offs, int len, int dummy)
#endif
#endif
{
	wan_device_t* wandev = NULL;
	PROC_ADD_DECL(m);
	PROC_ADD_INIT(m, buf, offs, len);

	PROC_ADD_LINE(m, conf_hdr);

	wan_spin_lock(&wan_devlist_lock);  
	WAN_LIST_FOREACH(wandev, &wan_devlist, next){
		/*for (wandev = router_devlist; wandev; wandev = wandev->next){*/
		sdla_t*	card = (sdla_t*)wandev->priv;
		u16 arg = 0;

		if (!wandev->state) continue;
		
		if (card && card->hw_iface.getcfg){
			card->hw_iface.getcfg(card->hw,
					(wandev->card_type==WANOPT_S50X) ? 
						SDLA_IOPORT : SDLA_SLOT,
					&arg);
		}
		if (wandev->state){ 
			PROC_ADD_LINE(m ,
			       (wandev->card_type==WANOPT_S50X)?CONF_ISA_FRM:CONF_PCI_FRM,
				wandev->name,
				"N/A",	/* FIXME */
				SDLA_DECODE_CARDTYPE(wandev->card_type),
				wandev->irq,
				arg,
				wandev->ndev,
				(wandev->card_type==WANOPT_S51X || wandev->card_type==WANOPT_AFT_SERIAL) ? CLK_DECODE(wandev->clocking) : "N/A",
				wandev->bps);
		}
	}
	wan_spin_unlock(&wan_devlist_lock);  

	PROC_ADD_RET(m);
}


/*
 *	Prepare data for reading 'Status' entry.
 *	Return length of data.
 */

#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(LINUX_2_4)
STATIC int status_get_info(char* buf, char** start, off_t offs, int len)
#else
#if defined(LINUX_2_6)
static int status_get_info(struct seq_file *m, void *v)
#elif defined(LINUX_2_4)
static int status_get_info(char* buf, char** start, off_t offs, int len)
#else
static int status_get_info(char* buf, char** start, off_t offs, int len, int dummy)
#endif
#endif
{
	struct wan_dev_le	*devle;
	wan_device_t*	wandev = NULL;

#ifdef CONFIG_PRODUCT_WANPIPE_ANNEXG
	netdevice_t *dev;
	wp_stack_stats_t wp_stats;
#endif
	PROC_ADD_DECL(m);
	PROC_ADD_INIT(m, buf, offs, len);

	PROC_ADD_LINE(m,  stat_hdr);

	devle=NULL;

	wan_spin_lock(&wan_devlist_lock);  
	WAN_LIST_FOREACH(wandev, &wan_devlist, next){
		
		if (!wandev->state) continue;

#ifdef CONFIG_PRODUCT_WANPIPE_ANNEXG
		memset(&wp_stats,0,sizeof(wp_stack_stats_t));

		if (wandev->get_active_inactive){
			wan_spin_lock(&wandev->dev_head_lock);
			WAN_LIST_FOREACH(devle, &wandev->dev_head, dev_link){
				dev = WAN_DEVLE2DEV(devle);
				if (!dev || !(dev->flags&IFF_UP) || !wan_netif_priv(dev)){ 
			   	     continue;
				}  
				wandev->get_active_inactive(wandev,dev,&wp_stats);
			}
			wan_spin_unlock(&wandev->dev_head_lock);
		}
	
		PROC_ADD_LINE(m, 
			      
			      STAT_FRM,
			      wandev->name,
			      PROT_DECODE(wandev->config_id,1),
			      wandev->config_id == WANCONFIG_FR ?  
			      		FR_STATION_DECODE(wandev->station) :
			       wandev->config_id == WANCONFIG_MFR ?  
			       		FR_STATION_DECODE(wandev->station) :
			      wandev->config_id == WANCONFIG_ADSL ? 
			      		ADSL_STATION_DECODE(wandev->station) :
			      wandev->config_id == WANCONFIG_X25 ? 
			      		X25_STATION_DECODE(wandev->station) :
			      wandev->config_id == WANCONFIG_AFT ? 
			      		"A1/2 TE1":
			       wandev->config_id == WANCONFIG_AFT_TE1 ?	
			       		"A104 TE1":
				wandev->config_id == WANCONFIG_AFT_TE3 ?
					"A300 TE3" :
				wandev->config_id == WANCONFIG_AFT_ANALOG ?
					"A200 RM" :
					("N/A"),
			       STATE_DECODE(wandev->state),
			       wp_stats.fr_active,wp_stats.fr_inactive,
			       wp_stats.lapb_active,wp_stats.lapb_inactive,
			       wp_stats.x25_link_active,wp_stats.x25_link_inactive,
			       wp_stats.x25_active,wp_stats.x25_inactive,
			       wp_stats.dsp_active,wp_stats.dsp_inactive);

#else		
		PROC_ADD_LINE(m, 
			      
			      STAT_FRM,
			      wandev->name,
			      PROT_DECODE(wandev->config_id,1),
			      wandev->config_id == WANCONFIG_FR ?
			      		FR_STATION_DECODE(wandev->station) :
			      wandev->config_id == WANCONFIG_MFR ?
			      		FR_STATION_DECODE(wandev->station) :
			      wandev->config_id == WANCONFIG_ADSL ?
			      		ADSL_STATION_DECODE(wandev->station) :
			      wandev->config_id == WANCONFIG_X25 ?
			      		X25_STATION_DECODE(wandev->station) :
					("N/A"),
				STATE_DECODE(wandev->state));
#endif
	}
	wan_spin_unlock(&wan_devlist_lock);  
	
	PROC_ADD_RET(m);
}


/*
 *	Prepare data for reading 'Interfaces' entry.
 *	Return length of data.
 */

#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(LINUX_2_4)
STATIC int interfaces_get_info(char* buf, char** start, off_t offs, int len)
#else
#if defined(LINUX_2_6)
static int interfaces_get_info(struct seq_file *m, void *v)
#elif defined(LINUX_2_4)
static int interfaces_get_info(char* buf, char** start, off_t offs, int len)
#else
static int interfaces_get_info(char* buf, char** start, off_t offs, int len, int dummy)
#endif
#endif
{
	struct wan_dev_le	*devle;
	wan_device_t*	wandev = NULL;
	netdevice_t*	dev=NULL;
	PROC_ADD_DECL(m);
	PROC_ADD_INIT(m, buf, offs, len);

	PROC_ADD_LINE(m, interfaces_hdr);

	wan_spin_lock(&wan_devlist_lock);  
	WAN_LIST_FOREACH(wandev, &wan_devlist, next){
		wanpipe_common_t *dev_priv;
		if (!(m->count < (m->size - 80))) break;
		if (!wandev->state) continue;

		wan_spin_lock(&wandev->dev_head_lock);
		WAN_LIST_FOREACH(devle, &wandev->dev_head, dev_link){
			dev = WAN_DEVLE2DEV(devle);
			
			if (!dev || !WAN_NETIF_UP(dev) || !wan_netif_priv(dev)){ 
				continue;
			}

			dev_priv = wan_netif_priv(dev);
			PROC_ADD_LINE(m, 
				INTERFACES_FRM,
				wan_netif_name(dev), wandev->name, 
				dev_priv->lcn,
				dev_priv?STATE_DECODE(dev_priv->state):"N/A");

			if (dev_priv->usedby == STACK){
				wanpipe_lip_get_if_status(dev_priv,m);	
			}
		}
		wan_spin_unlock(&wandev->dev_head_lock);
	}
	wan_spin_unlock(&wan_devlist_lock);  

	PROC_ADD_RET(m);
}

#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(LINUX_2_4)
STATIC int probe_get_info(char* buf, char** start, off_t offs, int len)
#else
#if defined(LINUX_2_6)
static int probe_get_info(struct seq_file *m, void *v)
#elif defined(LINUX_2_4)
static int probe_get_info(char* buf, char** start, off_t offs, int len)
#else
static int probe_get_info(char* buf, char** start, off_t offs, int len, int dummy)
#endif
#endif
{
	int i=0;
	sdla_hw_probe_t* hw_probe;
	sdla_hw_type_cnt_t *hw_cnt;
	PROC_ADD_DECL(m);
	PROC_ADD_INIT(m, buf, offs, len);

	PROC_ADD_LINE(m,  "-------------------------------\n");
	PROC_ADD_LINE(m,  "| Wanpipe Hardware Probe Info |\n");
	PROC_ADD_LINE(m,  "-------------------------------\n");

	hw_probe = (sdla_hw_probe_t *)sdla_get_hw_probe();	
	
	for (;
	     hw_probe;
	     hw_probe = WAN_LIST_NEXT(hw_probe, next)) {

		i++;
		PROC_ADD_LINE(m, 
		       "%-2d. %s\n", i, hw_probe->hw_info);
	}

	hw_cnt=(sdla_hw_type_cnt_t*)sdla_get_hw_adptr_cnt();	
	
	PROC_ADD_LINE(m, "\nCard Cnt: ");
	if (hw_cnt->s508_adapters){
		PROC_ADD_LINE(m, "S508=%d ", hw_cnt->s508_adapters);
	}
	if (hw_cnt->s514x_adapters){
		PROC_ADD_LINE(m, "S514X=%d ", hw_cnt->s514x_adapters);
	}
	if (hw_cnt->s518_adapters){
		PROC_ADD_LINE(m, "S518=%d ", hw_cnt->s518_adapters);
	}
	if (hw_cnt->aft101_adapters){
		PROC_ADD_LINE(m, "A101-2=%d ", hw_cnt->aft101_adapters);
	}
	if (hw_cnt->aft104_adapters){
		PROC_ADD_LINE(m, "A104=%d ", hw_cnt->aft104_adapters);
	}
	if (hw_cnt->aft108_adapters){
		PROC_ADD_LINE(m, "A108=%d ", hw_cnt->aft108_adapters);
	}
	if (hw_cnt->aft200_adapters){
		PROC_ADD_LINE(m, "A200=%d ", hw_cnt->aft200_adapters);
	}
	if (hw_cnt->aft_56k_adapters){
		PROC_ADD_LINE(m, "A056=%d ", hw_cnt->aft_56k_adapters);
	}
	if (hw_cnt->aft_isdn_adapters){
		PROC_ADD_LINE(m, "A500=%d ", hw_cnt->aft_isdn_adapters);
	}
	if (hw_cnt->aft_serial_adapters){
		PROC_ADD_LINE(m, "A14x=%d ", hw_cnt->aft_serial_adapters);
	}
	if (hw_cnt->aft300_adapters){
		PROC_ADD_LINE(m, "A300=%d ", hw_cnt->aft300_adapters);
	}
	PROC_ADD_LINE(m, "\n");

	PROC_ADD_RET(m);
}

#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(LINUX_2_4)
STATIC int probe_get_info_legacy(char* buf, char** start, off_t offs, int len)
#else
#if defined(LINUX_2_6)
static int probe_get_info_legacy(struct seq_file *m, void *v)
#elif defined(LINUX_2_4)
static int probe_get_info_legacy(char* buf, char** start, off_t offs, int len)
#else
static int probe_get_info_legacy(char* buf, char** start, off_t offs, int len, int dummy)
#endif
#endif
{
	int i=0;
	sdla_hw_probe_t* hw_probe;
	sdla_hw_type_cnt_t *hw_cnt;
	PROC_ADD_DECL(m);
	PROC_ADD_INIT(m, buf, offs, len);

	PROC_ADD_LINE(m,  "----------------------------------------\n");
	PROC_ADD_LINE(m,  "| Wanpipe Hardware Probe Info (Legacy) |\n");
	PROC_ADD_LINE(m,  "----------------------------------------\n");

	hw_probe = (sdla_hw_probe_t *)sdla_get_hw_probe();	
	
	for (;
	     hw_probe;
	     hw_probe = WAN_LIST_NEXT(hw_probe, next)) {

		i++;
		PROC_ADD_LINE(m, 
		       "%-2d. %s\n", i, hw_probe->hw_info);
	}

	hw_cnt=(sdla_hw_type_cnt_t*)sdla_get_hw_adptr_cnt();	
	
	PROC_ADD_LINE(m,
		"\nCard Cnt: S508=%d S514X=%d S518=%d A101-2=%d A104=%d A300=%d A200=%d A108=%d A056=%d\n          A500=%d A14x=%d\n",
		hw_cnt->s508_adapters,
		hw_cnt->s514x_adapters,
		hw_cnt->s518_adapters,
		hw_cnt->aft101_adapters,
		hw_cnt->aft104_adapters,
		hw_cnt->aft300_adapters,
		hw_cnt->aft200_adapters,
		hw_cnt->aft108_adapters,
		hw_cnt->aft_56k_adapters,
		hw_cnt->aft_isdn_adapters,
		hw_cnt->aft_serial_adapters
		);

	PROC_ADD_RET(m);
}

#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(LINUX_2_4)
STATIC int probe_get_info_verbose(char* buf, char** start, off_t offs, int len)
#else
# if defined(LINUX_2_6)
static int probe_get_info_verbose(struct seq_file *m, void *v)
# elif defined(LINUX_2_4)
static int probe_get_info_verbose(char* buf, char** start, off_t offs, int len)
# else
static int probe_get_info_verbose(char* buf, char** start, off_t offs, int len, int dummy)
# endif
#endif
{
	int i=0;
	sdla_hw_probe_t* hw_probe;
	sdla_hw_type_cnt_t *hw_cnt;
	PROC_ADD_DECL(m);
	PROC_ADD_INIT(m, buf, offs, len);

	PROC_ADD_LINE(m,  "-----------------------------------------\n");
	PROC_ADD_LINE(m,  "| Wanpipe Hardware Probe Info (verbose) |\n");
	PROC_ADD_LINE(m,  "-----------------------------------------\n");

	hw_probe = (sdla_hw_probe_t *)sdla_get_hw_probe();	
	
	for (;
	     hw_probe;
	     hw_probe = WAN_LIST_NEXT(hw_probe, next)) {

		i++;
		PROC_ADD_LINE(m, 
		       "%-2d. %s", i, hw_probe->hw_info);
		PROC_ADD_LINE(m, 
		       "%s\n", hw_probe->hw_info_verbose); 
	}

	hw_cnt=(sdla_hw_type_cnt_t*)sdla_get_hw_adptr_cnt();	
	
	PROC_ADD_LINE(m, "\nCard Cnt: ");
	if (hw_cnt->s508_adapters){
		PROC_ADD_LINE(m, "S508=%d ", hw_cnt->s508_adapters);
	}
	if (hw_cnt->s514x_adapters){
		PROC_ADD_LINE(m, "S514X=%d ", hw_cnt->s514x_adapters);
	}
	if (hw_cnt->s518_adapters){
		PROC_ADD_LINE(m, "S518=%d ", hw_cnt->s518_adapters);
	}
	if (hw_cnt->aft101_adapters){
		PROC_ADD_LINE(m, "A101-2=%d ", hw_cnt->aft101_adapters);
	}
	if (hw_cnt->aft104_adapters){
		PROC_ADD_LINE(m, "A104=%d ", hw_cnt->aft104_adapters);
	}
	if (hw_cnt->aft108_adapters){
		PROC_ADD_LINE(m, "A108=%d ", hw_cnt->aft108_adapters);
	}
	if (hw_cnt->aft200_adapters){
		PROC_ADD_LINE(m, "A200=%d ", hw_cnt->aft200_adapters);
	}
	if (hw_cnt->aft_56k_adapters){
		PROC_ADD_LINE(m, "A056=%d ", hw_cnt->aft_56k_adapters);
	}
	if (hw_cnt->aft_isdn_adapters){
		PROC_ADD_LINE(m, "A500=%d ", hw_cnt->aft_isdn_adapters);
	}
	if (hw_cnt->aft_serial_adapters){
		PROC_ADD_LINE(m, "A14x=%d ", hw_cnt->aft_serial_adapters);
	}
	if (hw_cnt->aft300_adapters){
		PROC_ADD_LINE(m, "A300=%d ", hw_cnt->aft300_adapters);
	}
	PROC_ADD_LINE(m, "\n");

	PROC_ADD_RET(m);
}

	
/*
 *	Prepare data for reading <device> entry.
 *	Return length of data.
 *
 *	On entry, the 'start' argument will contain a pointer to WAN device
 *	data space.
 */

#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(LINUX_2_4)
STATIC int wandev_get_info(char* buf, char** start, off_t offs, int len)
#else
#if defined(LINUX_2_6)
static int wandev_get_info(struct seq_file *m, void *v)
#elif defined(LINUX_2_4)
static int wandev_get_info(char* buf, char** start, off_t offs, int len)
#else
static int wandev_get_info(char* buf, char** start, off_t offs, int len, int dummy)
#endif
#endif
{
	wan_device_t* wandev = PROC_GET_DATA();
	int rslt = 0;
	PROC_ADD_DECL(m);

	if ((wandev == NULL) || (wandev->magic != ROUTER_MAGIC))
		return 0;
	
	PROC_ADD_INIT(m, buf, offs, len);
	if (!wandev->state){
		PROC_ADD_LINE(m,
			"device is not configured!\n");
		goto wandev_get_info_end;
	}

	/* Update device statistics */
	if (wandev->update && !m->from){

		rslt = wandev->update(wandev);
		if(rslt) {
			switch (rslt) {
			case -EAGAIN:
				PROC_ADD_LINE(m, 
					
					"Device is busy!\n");
				break;
					

			default:
				PROC_ADD_LINE(m,
					
					"Device is not configured!\n");
				break;
			}
			goto wandev_get_info_end;
		}
	}

	PROC_ADD_LINE(m, 
		
		PROC_STATS_2_FORMAT,
		"total rx packets", wandev->stats.rx_packets,
		"total tx packets", wandev->stats.tx_packets);
	PROC_ADD_LINE(m,
		
		PROC_STATS_2_FORMAT,
		"total rx bytes", wandev->stats.rx_bytes, 
		"total tx bytes", wandev->stats.tx_bytes);
	PROC_ADD_LINE(m,
		
		PROC_STATS_2_FORMAT,
		"bad rx packets", wandev->stats.rx_errors, 
		"packet tx problems", wandev->stats.tx_errors);
	PROC_ADD_LINE(m,
		 
		PROC_STATS_2_FORMAT,
		"rx frames dropped", wandev->stats.rx_dropped,
		"tx frames dropped", wandev->stats.tx_dropped);
	PROC_ADD_LINE(m,
		
		PROC_STATS_2_FORMAT,
		"multicast rx packets", wandev->stats.multicast,
		"tx collisions", wandev->stats.collisions);
	PROC_ADD_LINE(m,
		
		PROC_STATS_2_FORMAT,
		"rx length errors", wandev->stats.rx_length_errors,
		"rx overrun errors", wandev->stats.rx_over_errors);
	PROC_ADD_LINE(m,
		
		PROC_STATS_2_FORMAT,
		"CRC errors", wandev->stats.rx_crc_errors,
		"abort frames", wandev->stats.rx_frame_errors);
	PROC_ADD_LINE(m,
		
		PROC_STATS_2_FORMAT,
		"rx fifo overrun", wandev->stats.rx_fifo_errors,
		"rx missed packet", wandev->stats.rx_missed_errors);
	PROC_ADD_LINE(m,
		
		PROC_STATS_1_FORMAT,
		"aborted tx frames", wandev->stats.tx_aborted_errors);

	/* Update Front-End information (alarms, performance monitor counters */
	if (wandev->get_info){
		m->count = wandev->get_info(
				wandev->priv, 
				m, 
				M_STOP_CNT(m)); 
	}

wandev_get_info_end:
	PROC_ADD_RET(m);
}



#if defined(__LINUX__)

/*
 *	Initialize router proc interface.
 */

int wanrouter_proc_init (void)
{
	struct proc_dir_entry *p;
	proc_router = proc_mkdir(ROUTER_NAME, wan_init_net(proc_net));
	if (!proc_router)
		goto fail;
	
	p = create_proc_entry("config",S_IRUGO,proc_router);
	if (!p)
		goto fail_config;

#if defined(LINUX_2_6)
	p->proc_fops = &config_fops;
#elif defined(LINUX_2_4)
	p->proc_fops = &router_fops;
	p->proc_iops = &router_inode;
	p->get_info = config_get_info;
#else
	p->ops = &router_inode;
	p->nlink = 1;
	p->get_info = config_get_info;
#endif
	
	p = create_proc_entry("status",S_IRUGO,proc_router);
	if (!p)
		goto fail_stat;

#if defined(LINUX_2_6)
	p->proc_fops = &status_fops;
#elif defined(LINUX_2_4)
	p->proc_fops = &router_fops;
	p->proc_iops = &router_inode;
	p->get_info = status_get_info;
#else
	p->ops = &router_inode;
	p->nlink = 1;
	p->get_info = status_get_info;
#endif
	
	p = create_proc_entry("hwprobe",0,proc_router);
	if (!p)
		goto fail_probe;

#if defined(LINUX_2_6)
	p->proc_fops = &wp_hwprobe_fops;
#elif defined(LINUX_2_4)
	p->proc_fops = &router_fops;
	p->proc_iops = &router_inode;
	p->get_info = probe_get_info;
#else
	p->ops = &router_inode;
	p->nlink = 1;
	p->get_info = probe_get_info;
#endif

	p = create_proc_entry("hwprobe_legacy",0,proc_router);
	if (!p)
		goto fail_probe_legacy;

#if defined(LINUX_2_6)
	p->proc_fops = &wp_hwprobe_legacy_fops;
#elif defined(LINUX_2_4)
	p->proc_fops = &router_fops;
	p->proc_iops = &router_inode;
	p->get_info = probe_get_info_legacy;
#else
	p->ops = &router_inode;
	p->nlink = 1;
	p->get_info = probe_get_info_legacy;
#endif

	p = create_proc_entry("hwprobe_verbose",0,proc_router);
	if (!p)
		goto fail_probe_verbose;

#if defined(LINUX_2_6)
	p->proc_fops = &wp_hwprobe_verbose_fops;
#elif defined(LINUX_2_4)
	p->proc_fops = &router_fops;
	p->proc_iops = &router_inode;
	p->get_info = probe_get_info_verbose;
#else
	p->ops = &router_inode;
	p->nlink = 1;
	p->get_info = probe_get_info_verbose;
#endif

	p = create_proc_entry("map",0,proc_router);
	if (!p)
		goto fail_map;

#if defined(LINUX_2_6)
	p->proc_fops = &wp_map_fops;
#elif defined(LINUX_2_4)
	p->proc_fops = &router_fops;
	p->proc_iops = &router_inode;
	p->get_info = map_get_info;
#else
	p->ops = &router_inode;
	p->nlink = 1;
	p->get_info = map_get_info;
#endif

	p = create_proc_entry("interfaces",0,proc_router);
	if (!p)
		goto fail_interfaces;

#if defined(LINUX_2_6)
	p->proc_fops = &wp_iface_fops;
#elif defined(LINUX_2_4)
	p->proc_fops = &router_fops;
	p->proc_iops = &router_inode;
	p->get_info = interfaces_get_info;
#else
	p->ops = &router_inode;
	p->nlink = 1;
	p->get_info = interfaces_get_info;
#endif

	map_dir = proc_mkdir("dev_map", proc_router);
	if (!map_dir)
		goto fail_dev_map;

	/* Initialize protocol proc fs. */
	proc_router_chdlc.count = 0;
	proc_router_chdlc.protocol_entry = NULL;
	proc_router_fr.count = 0;
	proc_router_fr.protocol_entry = NULL;
	proc_router_ppp.count = 0;
	proc_router_ppp.protocol_entry = NULL;
	proc_router_x25.count = 0;
	proc_router_x25.protocol_entry = NULL;
	return 0;

fail_dev_map:
	remove_proc_entry("interfaces", proc_router);	
fail_interfaces:	
	remove_proc_entry("map", proc_router);
fail_map:	
	remove_proc_entry("hwprobe_verbose", proc_router);
fail_probe_verbose:	
	remove_proc_entry("hwprobe_legacy", proc_router);
fail_probe_legacy:
	remove_proc_entry("hwprobe", proc_router);
fail_probe:
	remove_proc_entry("status", proc_router);
fail_stat:
	remove_proc_entry("config", proc_router);
fail_config:
	remove_proc_entry(ROUTER_NAME, wan_init_net(proc_net));
fail:
	return -ENOMEM;
}

int wanrouter_proc_usage_check(void)
{
	if (proc_router){
		return atomic_read(&proc_router->count);
	}
	return 0;
}

/*
 *	Clean up router proc interface.
 */
void wanrouter_proc_cleanup (void)
{
	remove_proc_entry("config", proc_router);
	remove_proc_entry("status", proc_router);
	remove_proc_entry("hwprobe", proc_router);
	remove_proc_entry("hwprobe_legacy", proc_router);
	remove_proc_entry("hwprobe_verbose", proc_router);
	remove_proc_entry("map", proc_router);
	remove_proc_entry("interfaces", proc_router);
	remove_proc_entry("dev_map",proc_router);
	remove_proc_entry(ROUTER_NAME,wan_init_net(proc_net));
}

/*
 *	Add directory entry for WAN device.
 */

int wanrouter_proc_add (wan_device_t* wandev)
{	
	int err=0;
	struct proc_dir_entry *p;	

	wan_spin_lock_init(&wandev->get_map_lock, "wan_proc_lock");
	
	if (wandev->magic != ROUTER_MAGIC)
		return -EINVAL;


	wandev->dent = create_proc_entry(wandev->name, S_IRUGO, proc_router);
	if (!wandev->dent)
		return -ENOMEM;

#if defined(LINUX_2_6)
	wandev->dent->proc_fops	= &wandev_fops;
#elif defined(LINUX_2_4)
	wandev->dent->proc_fops	= &wandev_fops;
	wandev->dent->proc_iops	= &router_inode;
	wandev->dent->get_info	= wandev_get_info;
#else
	wandev->dent->ops = &wandev_inode;
	wandev->dent->nlink	= 1;
	wandev->dent->get_info	= wandev_get_info;
#endif
	wandev->dent->data	= wandev;

	p=create_proc_entry(wandev->name, 0, map_dir);
	if (!p){
		remove_proc_entry(wandev->name, proc_router);
		wandev->dent=NULL;
		return -ENOMEM;
	}

#if defined(LINUX_2_6)
	//FIXME: ADD THE FOPS HERE 
	p->proc_fops	= &wandev_mapdir_fops;
#elif defined(LINUX_2_4)
	p->proc_fops	= &wandev_fops;
	p->proc_iops	= &router_inode;
	p->get_info	= wandev_mapdir_get_info;
#else
	p->ops = &wandev_inode;
	p->nlink	= 1;
	p->get_info	= wandev_mapdir_get_info;
#endif
	p->data	= wandev;

	
	return err;
}

/*
 *	Delete directory entry for WAN device.
 */
 
int wanrouter_proc_delete(wan_device_t* wandev)
{
	if (wandev->magic != ROUTER_MAGIC)
		return -EINVAL;

	remove_proc_entry(wandev->name, proc_router);
	remove_proc_entry(wandev->name, map_dir);
	return 0;
}
/*
 *	Add directory entry for Protocol.
 */

int wanrouter_proc_add_protocol(wan_device_t* wandev)
{
	struct proc_dir_entry*	p = NULL;
	//struct proc_dir_entry**	proc_protocol;
	wan_proc_entry_t*	proc_protocol;

	if (wandev->magic != ROUTER_MAGIC)
		return -EINVAL;

	switch(wandev->config_id){
		case WANCONFIG_FR:
		case WANCONFIG_MFR:
			proc_protocol = &proc_router_fr;
			break;
			
		case WANCONFIG_CHDLC:
			proc_protocol = &proc_router_chdlc;
			break;
			
		case WANCONFIG_PPP:
			proc_protocol = &proc_router_ppp;
			break;
			
		case WANCONFIG_X25:
			proc_protocol = &proc_router_x25;
			break;
			
		default:
			proc_protocol = NULL;
			return 0;
	}

	if (proc_protocol->protocol_entry == NULL){

		proc_protocol->count=0;
		
		/* Create /proc/net/wanrouter/<protocol> directory */
		proc_protocol->protocol_entry = 
			proc_mkdir(PROT_DECODE(wandev->config_id,0), proc_router);
		
		if (!proc_protocol->protocol_entry)
			goto fail;
	
		/* Create /proc/net/wanrouter/<protocol>/config directory */
		p = create_proc_entry("config",0,proc_protocol->protocol_entry);
		if (!p)
			goto fail_config;

#if defined(LINUX_2_6)
		p->proc_fops 	= &wp_get_dev_config_fops;

#elif defined(LINUX_2_4)	
		p->proc_fops 	= &router_fops;
		p->proc_iops 	= &router_inode;
		p->get_info 	= get_dev_config_info;
#else

		p->ops = &router_inode;
		p->nlink = 1;
		p->get_info 	= get_dev_config_info;
#endif
		p->data 	= (void*)wandev->config_id;
	
		/* Create /proc/net/wanrouter/<protocol>/status directory */
		p = create_proc_entry("status",0,proc_protocol->protocol_entry);
		if (!p)
			goto fail_stat;

#if defined(LINUX_2_6)
		p->proc_fops 	= &wp_get_dev_status_fops; 

#elif defined(LINUX_2_4)
		p->proc_fops 	= &router_fops;
		p->proc_iops 	= &router_inode;
		p->get_info 	= get_dev_status_info;
#else
		p->ops = &router_inode;
		p->nlink = 1;
		p->get_info 	= get_dev_status_info;
#endif
		p->data 	= (void*)wandev->config_id;
	}	
	
	/* Create /proc/net/wanrouter/<protocol>/<wanpipe#> directory */
	wandev->link = proc_mkdir(wandev->name, proc_protocol->protocol_entry);
	if (!wandev->link)
		goto fail_link;

	/* Create /proc/net/wanrouter/<protocol>/<wanpipe#>config directory */
	p = create_proc_entry("config",0,wandev->link);
	if (!p)
		goto fail_link_config;

#if defined(LINUX_2_6)
	p->proc_fops 	= NULL; //&wp_prot_dev_config_fops;
#elif defined(LINUX_2_4)
	p->proc_fops 	= &router_fops;
	p->proc_iops 	= &router_inode;
	p->get_info 	= wandev->get_dev_config_info;
	p->write_proc 	= wandev->set_dev_config;
#else
	p->ops = &router_inode;
	p->nlink = 1;
	p->get_info 	= wandev->get_dev_config_info;
	p->write_proc 	= wandev->set_dev_config;
#endif
	p->data 	= wandev;

	proc_protocol->count ++;
	return 0;

fail_link_config:
	remove_proc_entry(wandev->name, proc_protocol->protocol_entry);
fail_link:
	if (proc_protocol->count){
		/* Do not remove /proc/net/wanrouter/<protocol>... because 
		 * another device is still using this entry.
		 */
		return -ENOMEM;
	}
	remove_proc_entry("status", proc_protocol->protocol_entry);
	return -ENOMEM;

fail_stat:
	remove_proc_entry("config", proc_protocol->protocol_entry);
fail_config:
	remove_proc_entry(PROT_DECODE(wandev->config_id,0), proc_router);
	proc_protocol->protocol_entry = NULL;
fail:
	return -ENOMEM;
}

/*
 *	Delete directory entry for Protocol.
 */
 
int wanrouter_proc_delete_protocol(wan_device_t* wandev)
{
	wan_proc_entry_t*	proc_protocol = NULL;

	if (wandev->magic != ROUTER_MAGIC)
		return -EINVAL;

	switch(wandev->config_id){
		case WANCONFIG_FR:
		case WANCONFIG_MFR:
			proc_protocol = &proc_router_fr;
			break;
			
		case WANCONFIG_CHDLC:
			proc_protocol = &proc_router_chdlc;
			break;
			
		case WANCONFIG_PPP:
			proc_protocol = &proc_router_ppp;
			break;
			
		case WANCONFIG_X25:
			proc_protocol = &proc_router_x25;
			break;
			
		default:
			proc_protocol = NULL;
			return 0;
			break;
	}

	remove_proc_entry("config", wandev->link);
	remove_proc_entry(wandev->name, proc_protocol->protocol_entry);
	proc_protocol->count --;
	if (!proc_protocol->count){
		remove_proc_entry("config", proc_protocol->protocol_entry);
		remove_proc_entry("status", proc_protocol->protocol_entry);
		remove_proc_entry(PROT_DECODE(wandev->config_id,0), proc_router);
		proc_protocol->protocol_entry = NULL;
	}
	return 0;
}

/*
 *	Add directory entry for interface.
 */

int wanrouter_proc_add_interface(wan_device_t* wandev, 
				 struct proc_dir_entry** dent,
				 char* if_name,
				 void* priv)
{

#if 0
	if (wandev->magic != ROUTER_MAGIC)
		return -EINVAL;
	if (wandev->link == NULL || wandev->get_if_info == NULL)
		return -ENODEV;

	*dent = create_proc_entry(if_name, 0, wandev->link);
	if (!*dent)
		return -ENOMEM;
#ifdef LINUX_2_4
	(*dent)->proc_fops	= &router_fops;
	(*dent)->proc_iops	= &router_inode;
#else
	(*dent)->ops = &router_inode;
	(*dent)->nlink = 1;
#endif
	(*dent)->get_info	= wandev->get_if_info;
	(*dent)->write_proc	= wandev->set_if_info;
	(*dent)->data		= priv;
#endif
	return 0;
}

/*
 *	Delete directory entry for interface.
 */
 
int wanrouter_proc_delete_interface(wan_device_t* wandev, char* if_name)
{
#if 0
	if (wandev->magic != ROUTER_MAGIC)
		return -EINVAL;
	if (wandev->link == NULL)
		return -ENODEV;

	remove_proc_entry(if_name, wandev->link);
#endif
	return 0;
}

/****** Proc filesystem entry points ****************************************/

/*
 *	Verify access rights.
 */

#ifndef LINUX_2_6
static int router_proc_perms (struct inode* inode, int op)
{
	return 0;
}

/*
 *	Read router proc directory entry.
 *	This is universal routine for reading all entries in /proc/net/wanrouter
 *	directory.  Each directory entry contains a pointer to the 'method' for
 *	preparing data for that entry.
 *	o verify arguments
 *	o allocate kernel buffer
 *	o call get_info() to prepare data
 *	o copy data to user space
 *	o release kernel buffer
 *
 *	Return:	number of bytes copied to user space (0, if no data)
 *		<0	error
 */

static ssize_t router_proc_read(struct file* file, char* buf, size_t count,
				loff_t *ppos)
{
	struct inode *inode = file->f_dentry->d_inode;
	struct proc_dir_entry* dent;
	char* page;
	int len;

	if (count <= 0)
		return 0;
	
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)
	dent = inode->i_private;
#else
	dent = inode->u.generic_ip;
#endif
	if ((dent == NULL) || (dent->get_info == NULL))
		return 0;
		
	page = wan_malloc(count);
	if (page == NULL)
		return -ENOBUFS;
#ifdef LINUX_2_4
	len = dent->get_info(page, dent->data, file->f_pos, count);
#else
	len = dent->get_info(page, dent->data, file->f_pos, count, 0);
#endif
	if (len) {
		if(copy_to_user(buf, page, len)){
			DEBUG_SUB_MEM(count);
			wan_free(page);
			return -EFAULT;
		}
		file->f_pos += len;
	}
	wan_free(page);
	return len;
}

/*
 *	Write router proc directory entry.
 *	This is universal routine for writing all entries in /proc/net/wanrouter
 *	directory.  Each directory entry contains a pointer to the 'method' for
 *	preparing data for that entry.
 *	o verify arguments
 *	o allocate kernel buffer
 *	o copy data from user space
 *	o call write_info()
 *	o release kernel buffer
 *
 *	Return:	number of bytes copied to user space (0, if no data)
 *		<0	error
 */
static ssize_t router_proc_write (struct file *file, const char *buf, size_t count, 
					loff_t *ppos)
{
	struct inode*		inode = file->f_dentry->d_inode;
	struct proc_dir_entry* 	dent = NULL;
	char* 			page = NULL;
#ifdef WAN_DEBUG_MEM
	unsigned int 		ocount=count;
#endif

	if (count <= 0)
		return 0;
		
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
        dent = inode->i_private;
#else
        dent = inode->u.generic_ip;
#endif

	if ((dent == NULL) || (dent->write_proc == NULL))
	return count;
		
	page = wan_malloc(count);
	if (page == NULL)
		return -ENOBUFS;
	if (copy_from_user(page, buf, count)){
		DEBUG_SUB_MEM(count);
		wan_free(page);
		return -EINVAL;
	}
	page[count-1] = '\0';
		
	/* Add supporting Write to proc fs */
	count = dent->write_proc(file, page, count, dent->data);

	DEBUG_SUB_MEM(ocount);
	wan_free(page);
	return count;
}

#endif


/*
 *	Prepare data for reading 'MAP' entry.
 *	Return length of data.
 */

#if defined(LINUX_2_6)
static int map_get_info(struct seq_file *m, void *v)
#elif defined(LINUX_2_4)
static int map_get_info(char* buf, char** start, off_t offs, int len)
#else
static int map_get_info(char* buf, char** start, off_t offs, int len, int dummy)	
#endif
{
	wan_device_t*	wandev = NULL;
	struct wan_dev_le	*devle;
	netdevice_t*	dev=NULL;

	PROC_ADD_DECL(m);
	PROC_ADD_INIT(m, buf, offs, len);

	PROC_ADD_LINE(m, map_hdr);

	wan_spin_lock(&wan_devlist_lock);  
	WAN_LIST_FOREACH(wandev, &wan_devlist, next){
		
		if (!wandev->state){ 
			continue;
		}
		if (!wandev->get_map){
			continue;
		}

		wan_spin_lock(&wandev->dev_head_lock);
		WAN_LIST_FOREACH(devle, &wandev->dev_head, dev_link){
			dev = WAN_DEVLE2DEV(devle);
			if (!dev || !(dev->flags&IFF_UP) || !wan_netif_priv(dev)){ 
				continue;
			}

			m->count = wandev->get_map(wandev, dev, m, M_STOP_CNT(m));
		}
		wan_spin_unlock(&wandev->dev_head_lock);
	}
	wan_spin_unlock(&wan_devlist_lock);  

	PROC_ADD_RET(m);
}



/*
 *	Prepare data for reading FR 'Config' entry.
 *	Return length of data.
 */
#if defined(LINUX_2_6)
static int get_dev_config_info(struct seq_file *m, void *v)
#elif defined(LINUX_2_4)
static int get_dev_config_info(char* buf, char** start, off_t offs, int len)
#else
static int get_dev_config_info(char* buf, char** start, off_t offs, int len,int dummy)
#endif
{
	wan_device_t*	wandev = NULL;
	netdevice_t*	dev = NULL;
	struct wan_dev_le	*devle;
	PROC_ADD_DECL(m);
	PROC_ADD_INIT(m, buf, offs, len);

	wan_spin_lock(&wan_devlist_lock);  
	WAN_LIST_FOREACH(wandev, &wan_devlist, next){
	     if (!(m->count < (PROC_BUFSZ - 80))) break;
		if (!wandev->get_config_info)
			continue;

#ifndef LINUX_2_6
		if ((wandev->config_id != (unsigned)start) || !wandev->state) 
			continue;
#else
		if (!wandev->state) 
			continue;
#endif

	       	wan_spin_lock(&wandev->dev_head_lock);
		WAN_LIST_FOREACH(devle, &wandev->dev_head, dev_link){
			dev = WAN_DEVLE2DEV(devle);
			if (!dev || !(dev->flags&IFF_UP) || !wan_netif_priv(dev)){ 
				continue;
			}
			m->count = wandev->get_config_info(dev->priv, 
							   m, 
							   M_STOP_CNT(m)); 
		}
	       	wan_spin_unlock(&wandev->dev_head_lock);
	}
	wan_spin_unlock(&wan_devlist_lock);  

	PROC_ADD_RET(m);
}	

/*
 *	Prepare data for reading FR 'Status' entry.
 *	Return length of data.
 */

#if defined(LINUX_2_6)
static int get_dev_status_info(struct seq_file *m, void *v)
#elif defined(LINUX_2_4)
static int get_dev_status_info(char* buf, char** start, off_t offs, int len)
#else
static int get_dev_status_info(char* buf, char** start, off_t offs, int len, int dummy)	
#endif
{
	wan_device_t*	wandev = NULL;
	struct wan_dev_le	*devle;
	netdevice_t*	dev = NULL;
	int		cnt = 0;
	PROC_ADD_DECL(m);
	
	PROC_ADD_INIT(m, buf, offs, len);

	wan_spin_lock(&wan_devlist_lock);  
	WAN_LIST_FOREACH(wandev, &wan_devlist, next){
	     if (!(cnt < (PROC_BUFSZ - 80))) break;

		if (!wandev->get_status_info)
			continue;
	
#ifndef LINUX_2_6
		if ((wandev->config_id != (unsigned)start) || !wandev->state) 
			continue;
#else
		if (!wandev->state) 
			continue;
#endif

	       	wan_spin_lock(&wandev->dev_head_lock);
		WAN_LIST_FOREACH(devle, &wandev->dev_head, dev_link){
			dev = WAN_DEVLE2DEV(devle);
			if (!dev || !(dev->flags&IFF_UP) || !wan_netif_priv(dev)){ 
				continue;
			}
			m->count = wandev->get_status_info(dev->priv, m, M_STOP_CNT(m)); 
		}
	       	wan_spin_unlock(&wandev->dev_head_lock);
	}
	wan_spin_unlock(&wan_devlist_lock);  
	

	PROC_ADD_RET(m);
}


#if defined(LINUX_2_6)
static int wandev_mapdir_get_info(struct seq_file *m, void *v)
#elif defined(LINUX_2_4)
static int wandev_mapdir_get_info(char* buf, char** start, off_t offs, int len)
#else
static int wandev_mapdir_get_info(char* buf, char** start, off_t offs, int len, int dummy)
#endif
{
	wan_device_t* wandev = PROC_GET_DATA();
	struct wan_dev_le	*devle;
	netdevice_t*	dev = NULL;
	PROC_ADD_DECL(m);

	if (!wandev){
		return -ENODEV;
	}

	PROC_ADD_INIT(m, buf, offs, len);

	PROC_ADD_LINE(m, map_hdr);

	if (!wandev->state){ 
		goto wandev_mapdir_get_info_exit;
	}
	if (!wandev->get_map){
		goto wandev_mapdir_get_info_exit;
	}

	wan_spin_lock(&wandev->dev_head_lock);
	WAN_LIST_FOREACH(devle, &wandev->dev_head, dev_link){
		dev = WAN_DEVLE2DEV(devle);
		if (!dev || !(dev->flags&IFF_UP) || !wan_netif_priv(dev)){ 
		       	continue;
		}   
		m->count = wandev->get_map(wandev, dev, m, M_STOP_CNT(m));
	}
        wan_spin_unlock(&wandev->dev_head_lock);
	
wandev_mapdir_get_info_exit:
	PROC_ADD_RET(m);
}



#endif /* __LINUX__ */

#else

/*
 *	No /proc - output stubs
 */

int wanrouter_proc_init(void)
{
	return 0;
}

void wanrouter_proc_cleanup(void)
{
	return;
}

int wanrouter_proc_add(wan_device_t *wandev)
{
	return 0;
}

int wanrouter_proc_delete(wan_device_t *wandev)
{
	return 0;
}

int wanrouter_proc_add_protocol(wan_device_t *wandev)
{
	return 0;
}

int wanrouter_proc_delete_protocol(wan_device_t *wandev)
{
	return 0;
}

int wanrouter_proc_add_interface(wan_device_t* wandev, 
				 struct proc_dir_entry** dent,
				 char* if_name,
				 void* priv)
{
	return 0;
}

int wanrouter_proc_delete_interface(wan_device_t* wandev, char* if_name)
{
	return 0;
}

#endif

/*============================================================================
 * Write WAN device ???.
 * o Find WAN device associated with this node
 */
#ifdef LINUX_2_0
static int device_write(
        struct inode* inode, struct file* file, const char* buf, int count)
{
        int err = verify_area(VERIFY_READ, buf, count);
        struct proc_dir_entry* dent;
        wan_device_t* wandev;

        if (err) return err;

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)
        dent = inode->i_private;
#else
        dent = inode->u.generic_ip;
#endif

        if ((dent == NULL) || (dent->data == NULL))
                return -ENODATA;

        wandev = dent->data;

        DEBUG_TEST("%s: writing %d bytes to %s...\n",
                name_root, count, dent->name);
        
	return 0;
}
#endif


 #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
int proc_add_line(struct seq_file* m, char* frm, ...)
{
	char 	tmp[400];
	int 	ret = PROC_BUF_CONT;
	int 	size = 0;
	va_list	arg;
	
	va_start(arg, frm);
	if (m->count && m->stop_cnt){
		va_end(arg);
		return PROC_BUF_EXIT;
	}
	size = vsprintf(tmp, frm, arg);
	if (m->stop_cnt){
		if (m->stop_cnt < size){
			DEBUG_EVENT("!!! Error in writting in proc buffer !!!\n");
			m->stop_cnt = size;
		}
		m->stop_cnt -= size;
	}else{
		if (size < m->size - m->count){
			/*vsprintf(&m->buf[m->count], frm, arg);*/
			memcpy(&m->buf[m->count], tmp, size);
			m->count += size;
			/* *cnt += vsprintf(&buf[*cnt], frm, arg); */
		}else{
			m->stop_cnt = m->from + m->count;
			ret = PROC_BUF_EXIT;
		}
	}
	va_end(arg);
	return ret;
}
#endif


#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
#else
int proc_add_line(struct seq_file* m, char* frm, ...)
{
#if defined(LINUX_2_6)
	return 0;
#else
	char 	tmp[400];
	int 	ret = PROC_BUF_CONT;
	int 	size = 0;
	va_list	arg;
	
	va_start(arg, frm);
	if (m->count && m->stop_cnt){
		va_end(arg);
		return PROC_BUF_EXIT;
	}
	size = vsprintf(tmp, frm, arg);
	if (m->stop_cnt){
		if (m->stop_cnt < size){
			DEBUG_EVENT("!!! Error in writting in proc buffer !!!\n");
			m->stop_cnt = size;
		}
		m->stop_cnt -= size;
	}else{
		if (size < m->size - m->count){
			/*vsprintf(&m->buf[m->count], frm, arg);*/
			memcpy(&m->buf[m->count], tmp, size);
			m->count += size;
			/* *cnt += vsprintf(&buf[*cnt], frm, arg); */
		}else{
			m->stop_cnt = m->from + m->count;
			ret = PROC_BUF_EXIT;
		}
	}
	va_end(arg);
	return ret;
#endif
}
#endif   
/*
 *	End
 */
