/*
 * proc.c - Implements directory entries for NETBEUI names , links and
 *          sessions below proc filesystem in memory.
 *
 * Notes:
 *	- Proc file system for NETBEUI is created under /proc/sys/netbeui.
 *      - There are two types of directory entries below netbeui under proc :
 *	  Static directory entries which are created at module initialization
 *	  into memory. Dynamic entries which are created at runtime like session
 *	  entries per link.
 *
 * Copyright (c) 1997 by Procom Technology,Inc.
 *
 * This program can be redistributed or modified under the terms of the 
 * GNU General Public License as published by the Free Software Foundation.
 * This program is distributed without any warranty or implied warranty
 * of merchantability or fitness for a particular purpose.
 *
 * See the GNU General Public License for more details.
 *
 */
 


#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/netdevice.h>
#include <asm/segment.h>
#include <linux/netbeui.h>



static int proc_netbeui_lookup(struct inode * dir,const char * name, int len,
               	               struct inode ** result);
static int proc_netbeui_readdir(struct inode * inode, struct file * filp, 
                 		void * dirent, filldir_t filldir);
static int proc_netbeui_read(struct inode * inode, struct file * filp,
                             char * buf, int count);



#define NETBEUI_PROJECT "Procom Technology" 
#define NETBEUI_RELEASE	"1.28"
#define NETBEUI_ENTRY_NR 3
#define ADAPTER_INO	201
#define VERSION_INO	200
#define MAX_NAME_LEN 10	
#define iget(a, b) __iget(a, b, 1)

static struct file_operations proc_netbeui_dir_operations = {
	NULL,			/* lseek - default */
        proc_netbeui_read,      /* read - bad */
	NULL,			/* write - bad */
	proc_netbeui_readdir,	/* readdir */
	NULL,			/* select - default */
	NULL,			/* ioctl - default */
	NULL,			/* mmap */
	NULL,			/* no special open code */
	NULL,			/* no special release code */
	NULL			/* can't fsync */
};

struct inode_operations proc_netbeui_inode_operations = {
	&proc_netbeui_dir_operations,
	NULL,			/* create */
	proc_netbeui_lookup,	/* 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 */
	NULL			/* permission */
};

const char * netbeui_version = "NetBEUI version " NETBEUI_RELEASE " "
	       NETBEUI_PROJECT "\n"; 

/* Netbeui dir entry under sys */
struct proc_dir_entry proc_netbeui = {
	PROC_NETBEUI, 7, "netbeui",
	S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
	0, &proc_netbeui_inode_operations,
	NULL, NULL,
	NULL, NULL, NULL
};

/* Links dir entry under netbeui */
struct proc_dir_entry proc_links = {
	PROC_NBLINKS, 5, "links",
	S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
	0, &proc_netbeui_inode_operations,
	NULL, NULL,
	NULL, NULL, NULL
};

/* Sessions dir entry under netbeui */
struct proc_dir_entry proc_sessions = {
	PROC_NBSESSIONS, 8, "sessions",
	S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
	0, &proc_netbeui_inode_operations,
	NULL, NULL,
	NULL, NULL, NULL
};

/* Names dir entry under netbeui */
struct proc_dir_entry proc_names = {
	PROC_NBNAMES, 5, "names",
	S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
	0, &proc_netbeui_inode_operations,
	NULL, NULL,
	NULL, NULL, NULL
};

/* A general dynamic file entry */
static struct proc_dir_entry proc_de = {
	0, 5, "     ",
	S_IFREG | S_IRUGO, 1, 0, 0,
	0, &proc_netbeui_inode_operations,
	NULL, NULL,
	NULL, NULL, NULL
};

/* A general dynamic dir entry */
static struct proc_dir_entry proc_dir = {
	0, 5, "     ",
	S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
	0, &proc_netbeui_inode_operations,
	NULL, NULL,
	NULL, NULL, NULL
};

/* Version file entry below netbeui */
static struct proc_dir_entry proc_version = {
	VERSION_INO, 7, "version",
	S_IFREG | S_IRUGO, 1, 0, 0, 
	0, &proc_netbeui_inode_operations,
	NULL, NULL,
	NULL, NULL, NULL
};

/* Adapter file entry below netbeui */
static struct proc_dir_entry proc_adapter = {
	ADAPTER_INO, 7, "adapter",
	S_IFREG | S_IRUGO, 1, 0, 0, 
	0, &proc_netbeui_inode_operations,
	NULL, NULL,
	NULL, NULL, NULL
};

/* Functions to get data from netbeui name, links and session service */
extern dextab_t * nbns_get_name_table(void);
extern dextab_t * nbll_get_link_table(void);
extern struct device * adapters[];

/* This variable holds the address of /proc/sys dir entry in memory */
static struct proc_dir_entry * n_proc_sys;


/*
 * Function: version_operation
 *	Contents of /proc/sys/netbeui/version which is netbeui version no 
 *
 * Parameters: 
 *	buff : A buffer which contains the contents of file
 *
 * Returns: 
 *	Buffer data size in bytes
 *
 * (1997 DEC 2'th,  R.S. - Commenting)
 */
int 
version_operation(char * buff)
{
	return sprintf(buff, netbeui_version);
}


/*
 * Function: adapter_operation
 *	Contents of /proc/sys/netbeui/adapter which is local adapter status
 *
 * Parameters: 
 *	buff : A buffer which contains the contents of file
 *
 * Returns: 
 *	Buffer data size in bytes
 *
 * (1997 DEC 2'th,  R.S. - Commenting)
 */
int 
adapter_operation(char * buff)
{
	int i, l = 0;
	int j = 0;
	char * b;

	for (i = 0, b = buff; adapters[i]; i++, j += l, b += l)  
		l = sprintf(b, "%s   %u   %02X:%02X:%02X:%02X:%02X:%02X\n", 
			adapters[i]->name, adapters[i]->mtu,
			adapters[i]->dev_addr[0],adapters[i]->dev_addr[1],
			adapters[i]->dev_addr[2],adapters[i]->dev_addr[3],
			adapters[i]->dev_addr[4],adapters[i]->dev_addr[5]
			);	
	return j;
}


/*
 * Function: netbeui_name_operation
 *	Contents of /proc/sys/netbeui/name which is netbeui name information
 *
 * Parameters: 
 *	buff	: A buffer which contains the contents of file
 *	ino	: I-node number of the name file entry
 *
 * Notes:
 *	- I-node number is used for file entry identification
 *	- There is a file entry for each name and the file name is the 
 *	  name number of that name
 * 
 * Returns: 
 *	Buffer data size in bytes
 *
 * (1997 DEC 2'th,  R.S. - Commenting)
 */
int 
netbeui_name_operation(char * buff, unsigned int ino)
{
	int i;
	dextab_t * dlist = nbns_get_name_table();
	name_t * nlist;
	unsigned char printable_name[NB_NAME_LEN+10];
	unsigned char name_port;
	unsigned long flags;

	save_flags(flags);
	cli();
	if (!dlist->addr) {
		restore_flags(flags);
		return 0;
	}
	for (i = dlist->reserved; i < dlist->size; i++)
		if (dlist->addr[i] &&
		   (((name_t *)(dlist->addr[i]))->name_number << 4) +
			2*PROC_NBNAMES == ino)
			break;
	if (i == dlist->size) {
		restore_flags(flags);
		return 0;
	}
	nlist = (name_t *)(dlist->addr[i]);
	restore_flags(flags);

	memcpy(printable_name, nlist->name, NB_NAME_LEN+1);
	name_port= nlist->name[NB_NAME_LEN-1];
	if ((name_port < 0x20) || (name_port >= 0x80))
		sprintf(printable_name+NB_NAME_LEN-1, "<%2x>", name_port);
	
	return sprintf(buff, "%d : state\n%d : status\n\"%s\" : name\n"
		"%u : conflicted\n%u : name_number\n%u : users\n", nlist->state, 
		nlist->status, printable_name, nlist->conflicted, 
		nlist->name_number, nlist->users);
}


/*
 * Function: netbeui_session_operation
 *	Contents of /proc/sys/netbeui/session which is netbeui session 
 *	information classified per link
 *
 * Parameters: 
 *	buff	: A buffer which contains the contents of file
 *	ino	: I-node number of the session file entry  
 *
 * Notes:
 *	- Session files are created per link and are placed below
 *	  /proc/sys/netbeui/sessions/<link_no>/
 *	- Each session file is named as <local_session_no>-<remote_session_no>
 *
 * Returns: 
 *	Buffer data size in bytes
 *
 * (1997 DEC 2'th,  R.S. - Commenting)
 */
int 
netbeui_session_operation(char * buff, unsigned int ino)
{
	int i, j = 0;
	dextab_t * dlist = nbll_get_link_table();
	dextab_t * slist = NULL;
	session_t * nlist;
	char * name = 0;
	unsigned long flags;

	save_flags(flags);
	cli();	
	if (!dlist->addr) {
		restore_flags(flags);
		return 0;
	}
	for (i = dlist->reserved; i < dlist->size; i++)
		if (dlist->addr[i]) { 
			slist = nbll_get_session_table(((link_t *)(dlist->addr[i]))->link_no);
			for (j = slist->reserved; j < slist->size; j++)
				if (slist->addr[j] &&
				   (((session_t *)(slist->addr[j]))->lsn << 4) +
					(((link_t *)(dlist->addr[i]))->link_no << 8) +
					2*PROC_NBSESSIONS == ino)
					break;
			if (j != slist->size)
				break;
		}

	if (i == dlist->size || !slist) { 
		restore_flags(flags);
		return 0;
	}
	nlist = (session_t *)(slist->addr[j]);
	restore_flags(flags);
	if (nlist->local_name)
		name = (char *) &(nlist->local_name->name);


	return sprintf(buff, "%d : state\n%d : status\n%u : link_no\n"
		"\"%s\" : local_name\n\"%s\" : remote_name\n%u : lsn\n"
		"%u : rsn\n%u : version\n%u : nack_indicator\n"
		"%u : tr_frame_lf\n%u : mtu\n%u : o_total\n%u : i_total\n",
		nlist->state, nlist->status, nlist->link_no, name, 
		nlist->remote_name, nlist->lsn, nlist->rsn, nlist->version, 
		nlist->nack_indicator, nlist->tr_frame_lf, nlist->mtu,
		nlist->o_total, nlist->i_total);

}


/*
 * Function: netbuei_link_operation
 *	Contents of /proc/sys/netbeui/links which is netbeui links 
 *	information
 *
 * Parameters: 
 *	buff	: A buffer which contains the contents of file
 *	ino	: I-node number of the link file entry
 *
 * Returns: 
 *	Buffer data size in bytes
 *
 * (1997 DEC 2'th,  R.S. - Commenting)
 */
int 
netbeui_link_operation(char * buff, unsigned int ino)
{
	int i;
	dextab_t * dlist = nbll_get_link_table();
	link_t * nlist;
	char * name = 0;
	unsigned long flags;

	save_flags(flags);
	cli();	
	if (!dlist->addr) {
		restore_flags(flags);
		return 0;
	}
	for (i = dlist->reserved; i < dlist->size; i++)
		if (dlist->addr[i] &&
		   (((link_t *)(dlist->addr[i]))->link_no << 4) + 
			2*PROC_NBLINKS == ino)
			break;
	if (i == dlist->size) {
		restore_flags(flags);
		return 0;
	}
	nlist = (link_t *)(dlist->addr[i]);
	if (nlist->remote_dev)
		name = nlist->remote_dev->name;
	return sprintf(buff, "%d : state\n%d : status\n%u : llc_handle\n"
		"%u : link_no\n%02X:%02X:%02X:%02X:%02X:%02X : remote_mac\n%s : remote_dev\n"
		"%u : llc_busy\n%u : frames\n",
		nlist->state, nlist->status, nlist->llc_handle, 
		nlist->link_no, nlist->remote_mac[0], nlist->remote_mac[1], 
		nlist->remote_mac[2], nlist->remote_mac[3], 
		nlist->remote_mac[4], nlist->remote_mac[5], name,
		nlist->llc_busy, skb_queue_len(&(nlist->skbq)));
	restore_flags(flags);
}


/*
 * Function: get_session_no
 *	Extracts lsn and rsn from session file name
 *
 * Parameters: 
 *	name	: name of the session file
 *	len	: length of the name
 *	rsn	: remote session number 
 *
 * Returns: 
 *	Local session number
 *
 * (1997 DEC 2'th,  R.S. - Commenting)
 */
int 
get_session_no(const char * name, int len, int * rsn)
{
	int i = 0;
	int j;
	for (j = 0; j < len && name[j] != '-'; j++) {
		i += name[j] - '0';
		i *= 10;
	}
	for (j++; j < len; j++) {
		*rsn += name[j] - '0';
		*rsn *= 10;
	}
	*rsn /= 10;
	return i / 10;
}


/*
 * Function: get_session_ino
 *	Returns the local session number which is used in file i-number
 *
 * Parameters: 
 *	name	: name of the session file
 *	len	: length of the session file name
 *
 * Returns: 
 *	Local session number
 *
 * (1997 DEC 2'th,  R.S. - Commenting)
 */
int 
get_session_ino(const char * name, int len)
{
	int d = 0;

	return get_session_no(name,len,&d);
}


/*
 * Function: netbeui_check_session
 *	Checks if a session exists in netbeui session table or not
 *
 * Parameters: 
 *	name	: name of the session
 *	len	: length of the name of session
 *	link	: link no of the session
 *
 * Returns: 
 *	0	: the session does not exist
 *	non-zero: the session with given lsn and rsn exits
 *
 * (1997 DEC 2'th,  R.S. - Commenting)
 */
int 
netbeui_check_session(const char * name, int len, int link)
{
	int i, j, rsn = 0;
	dextab_t * dlist = nbll_get_session_table(link);
	int lsn = get_session_no(name, len, &rsn);
	unsigned long flags;

	if (!lsn)
		return 0;
	save_flags(flags);
	cli();
	if (!dlist) {
		restore_flags(flags);
		return 0;
	}
	for (i = dlist->reserved; i < dlist->size; i++)
		if (dlist->addr[i] &&
		   ((session_t *)(dlist->addr[i]))->lsn == lsn &&
		   ((session_t *)(dlist->addr[i]))->rsn == rsn)
			break;
	j = dlist->size - i;
	restore_flags(flags);
	return j;
}


/*
 * Function: get_name_no
 *	Gets name number from name of the file
 *
 * Parameters: 
 *	name	: name of the name file entry
 *	len	: length of the file name
 *
 * Returns: 
 *	Name number of the name file entry
 *
 * (1997 DEC 2'th,  R.S. - Commenting)
 */
int 
get_name_no(const char * name, int len)
{
	int i = 0;
	int j = 0;
	while (j < len) {	
		i += name[j++] - '0';
		i *= 10;
	}
	return i / 10;
}


/*
 * Function: netbeui_check_name
 *	Checks if a name exists in netbeui name table or not
 *
 * Parameters: 
 *	name	: the name to be checked
 *	len	: length of the name 
 *
 * Returns: 
 *	0	: the name does not exist
 *	non-zero: the name with given name number exits
 *
 * (1997 DEC 2'th,  R.S. - Commenting)
 */
int 
netbeui_check_name(const char * name, int len)
{
	int i, j;
	dextab_t * dlist = nbns_get_name_table();
	int no = get_name_no(name, len);
	unsigned long flags;

	save_flags(flags);
	cli();	
	if (!dlist) {
		restore_flags(flags);
		return 0;
	}
	for (i = dlist->reserved; i < dlist->size; i++)
		if (dlist->addr[i] &&
		   ((name_t *)(dlist->addr[i]))->name_number == no)
			break;
	j = dlist->size - i;
	restore_flags(flags);
	return j;
}


/*
 * Function: netbeui_check_link
 *	Checks if a link exists in netbeui link table or not
 *
 * Parameters: 
 *	name	: the link name to be checked
 *	len	: length of the name 
 *
 * Returns: 
 *	0	: the link does not exist
 *	non-zero: the link with given link number exits
 *
 * (1997 DEC 2'th,  R.S. - Commenting)
 */
int 
netbeui_check_link(const char * name, int len)
{
	int i, j;
	dextab_t * dlist = nbll_get_link_table();
	int ino = get_name_no(name, len);
	unsigned long flags;

	save_flags(flags);
	cli();	
	if (!dlist) {
		restore_flags(flags);
		return 0;
	}
	for (i = dlist->reserved; i < dlist->size; i++)
		if (dlist->addr[i] &&
		   ((link_t *)(dlist->addr[i]))->link_no == ino)
			break;
	j = dlist->size - i;
	restore_flags(flags);
	return j;
}

/*
 * Function: proc_match
 *	Checks if the given name matches with the name of directory entry
 *
 * Parameters: 
 *	name	: name to be checked for matching
 *	len	: length of the name
 *	de	: proc file system directory entry
 *
 * Returns: 
 *	0	: name does not match with the name of directory entry
 *	non-zero: the names are identical
 *
 * (1997 DEC 2'th,  R.S. - Commenting)
 */
int 
proc_match(int len,const char * name,struct proc_dir_entry * de)
{
	if (!de || !de->low_ino)
		return 0;
	/* "" means "." ---> so paths like "/usr/lib//libc.a" work */
	if (!len && (de->name[0]=='.') && (de->name[1]=='\0'))
		return 1;
	if (de->namelen != len)
		return 0;
	return !memcmp(name, de->name, len);
}


/*
 * Function: n_proc_get_inode
 *	Finds and makes a directory entry and inode structure for the given 
 *	i-number
 *
 * Parameters: 
 *	s	: superblock structure to find i-number of file
 *	ino	: i-number to look for
 *	de	: directory structure of ino
 *
 * Returns: 
 *	NULL	: there is no entry with given i-number
 *	non-NULL: inode structure of the given i-number
 *
 * (1997 DEC 2'th,  R.S. - Commenting)
 */
struct inode * 
n_proc_get_inode(struct super_block * s, int ino, struct proc_dir_entry * de)
{
	struct inode * inode = iget(s, ino);
	if (inode && inode->i_sb == s) {
		inode->u.generic_ip = (void *) de;
		if (de) {
			if (de->mode) {
				inode->i_mode = de->mode;
				inode->i_uid = de->uid;
				inode->i_gid = de->gid;
			}
			if (de->size)
				inode->i_size = de->size;
			if (de->ops)
				inode->i_op = de->ops;
			if (de->nlink)
				inode->i_nlink = de->nlink;
			if (de->fill_inode)
				de->fill_inode(inode);
		}
	}
	return inode;
}			


/*
 * Function: proc_netbeui_lookup
 *	Looks up the inode structure from directory structure by name of the
 *	file or directory
 *
 * Parameters: 
 *	dir	: directory structure to search in
 *	name	: name of the file or directory to look for
 *	len	: length of the name
 *	result	: inode structure that was found after search
 *
 * Returns: 
 *	0	: the inode structure has been found with no error
 *	-ENOTDIR: dir is not a directory to search in
 *	-EINVAL	: invalid directory structure
 *	-ENOTENT: there is not such entry in dir structure
 *
 * (1997 DEC 2'th,  R.S. - Commenting)
 */
int 
proc_netbeui_lookup(struct inode * dir,const char * name, int len,
                        struct inode ** result)
{
	int ino, i;
	struct proc_dir_entry * de;
	int ilen;
	char iname[MAX_NAME_LEN];

	*result = NULL;
	if (!dir || !S_ISDIR(dir->i_mode)) {
		iput(dir);
		return -ENOTDIR;
	}
	
	de = (struct proc_dir_entry *) dir->u.generic_ip;
	if (!de) {
		iput(dir);
		return -EINVAL;
	}
	ilen = sprintf(iname, "%d", (de->low_ino - 2*PROC_NBLINKS) >> 4);
	
	*result = dir;
	if (!len)
		return 0;
	if (name[0] == '.') {
		if (len == 1)
			return 0;
		if (name[1] == '.' && len == 2) {
			struct inode * inode;
			inode = n_proc_get_inode(dir->i_sb, de->parent->low_ino, de->parent);
			iput(dir);
			if (!inode)
				return -EINVAL;
			*result = inode;
			return 0;
		}
	}
	if (de->low_ino == PROC_NBNAMES && netbeui_check_name(name, len)) {
		i = get_name_no(name, len);
		i <<= 4;
		i += 2*PROC_NBNAMES;
		proc_de.low_ino = i;
		if (!(*result = n_proc_get_inode(dir->i_sb, i, &proc_de))) {
			iput(dir);
			return -EINVAL;
		}	
		return 0;
	}
	if (de->low_ino == PROC_NBLINKS && netbeui_check_link(name, len)) {
		i = get_name_no(name, len);
		i <<= 4;
		i += 2*PROC_NBLINKS;
		proc_de.low_ino = i;
		if (!(*result = n_proc_get_inode(dir->i_sb, i, &proc_de))) {
			iput(dir);
			return -EINVAL;
		}	
		return 0;
	}
	if (de->low_ino == PROC_NBSESSIONS && netbeui_check_link(name, len)) {
		i = get_name_no(name, len);

		/* This is a dir not a file entry */
		i <<= 4;
		i += 2*PROC_NBLINKS;
		proc_dir.low_ino = i;
		proc_dir.nlink = 2;
		proc_dir.parent = &proc_sessions;
		if (!(*result = n_proc_get_inode(dir->i_sb, i, &proc_dir))) {
			iput(dir);
			return -EINVAL;
		}	
		return 0;		
	} 

	/* looks for files below sessions/<link_no>/ */
	if (netbeui_check_link(iname, ilen) && netbeui_check_session(name, len, 
		(de->low_ino - 2*PROC_NBLINKS) >> 4)) {
		i = get_session_ino(name, len);
		i <<= 4;
		i += 2*PROC_NBSESSIONS;
		i += (de->low_ino - 2*PROC_NBLINKS) << 4;
		proc_de.low_ino = i;
		if (!(*result = n_proc_get_inode(dir->i_sb, i, &proc_de))) {
			iput(dir);
			return -EINVAL;
		}	
		return 0;
	}  

	/* below is for normal file or dir
	   lookup created by proc_register */
	*result = NULL;
	for (de = de->subdir; de; de = de->next)
		if (proc_match(len, name, de))
			break;
	if (!de) {
		iput(dir);
		return -ENOENT;
	}
	ino = de->low_ino;
	
	if (!(*result = n_proc_get_inode(dir->i_sb, ino, de))) {
		iput(dir);
		return -EINVAL;
	}
	iput(dir);
	return 0;
}


/*
 * Function: proc_netbeui_readdir
 *	Reads the directory structure (files and subdirs) from any position
 *	to end
 *
 * Parameters: 
 *	inode	: inode structure for the directory to be read
 *	filp	: position of the directory file which is to be read till end
 *	dirent	: directory entry which is filled according to each entry in
 *		  the directory file
 *	filldir : function for filling directory entry 
 *
 * Returns: 
 *	0	: the task of filling the directory entries is finished
 *	-ENOTDIR: inode is not for a directory to search in
 *	-EINVAL	: invalid directory structure
 *
 * (1997 DEC 2'th,  R.S. - Commenting)
 */
int 
proc_netbeui_readdir(struct inode * inode, struct file * filp, 
                 	 void * dirent, filldir_t filldir)
{
	struct proc_dir_entry * de;
	unsigned int ino;
	dextab_t * dp;
	name_t * p;
	session_t * sp;
	link_t * lp;
	int i, len;
	int j;
	char name[MAX_NAME_LEN];
	unsigned long flags;

	if (!inode || !S_ISDIR(inode->i_mode))
		return -ENOTDIR;
	de = (struct proc_dir_entry *) inode->u.generic_ip;
	if (!de)
		return -EINVAL;
	ino = inode->i_ino;
	i = filp->f_pos;
	switch (i) {
		case 0:
			if (filldir(dirent, ".", 1, i, ino) < 0)
				return 0;
			i++;
			filp->f_pos++;
		case 1:
			if (filldir(dirent, "..", 2, i, de->parent->low_ino) < 0)
				return 0;
			i++;
			filp->f_pos++;
		default:	
			save_flags(flags);
			cli();
			switch (ino) {
			    case PROC_NBNAMES:
				dp = nbns_get_name_table();
				for (i -= 2, j = dp->reserved; i > 0 && j < dp->size; j++) 
					if (dp->addr[j])
						i--;
				for (j = j; j < dp->size; j++) {
				    if (dp->addr[j]) {
					p = (name_t *)(dp->addr[j]);
					len = sprintf(name, "%d", p->name_number);
					if (filldir(dirent, name,
						len, filp->f_pos, (p->name_number << 4) +
						2*PROC_NBNAMES) < 0) {
						restore_flags(flags);
						return 0;
					}
					filp->f_pos++;
				    }
				}
				break;
			    case PROC_NBLINKS:
				dp = nbll_get_link_table();
				for (i -= 2, j = dp->reserved; i > 0 && j < dp->size; j++) 
					if (dp->addr[j])
						i--;
				for (j = j; j < dp->size; j++) {
				    if (dp->addr[j]) {
					lp = (link_t *)(dp->addr[j]);
					len = sprintf(name, "%d", lp->link_no);
					if (filldir(dirent, name,
						len, filp->f_pos, (lp->link_no << 4) +
						2*PROC_NBLINKS) < 0) {
						restore_flags(flags);
						return 0;
					}
					filp->f_pos++;
				    }
				}
				break;
			    case PROC_NBSESSIONS:
				dp = nbll_get_link_table();
				for (i -= 2, j = dp->reserved; i > 0 && j < dp->size; j++) 
					if (dp->addr[j])
						i--;
				for (j = j; j < dp->size; j++) {
				    if (dp->addr[j]) {
					lp = (link_t *)(dp->addr[j]);
					len = sprintf(name, "%d", lp->link_no);
					if (filldir(dirent, name,
						len, filp->f_pos, (lp->link_no << 4) +
						2*PROC_NBLINKS) < 0) {
						restore_flags(flags);
						return 0;
					}
					filp->f_pos++;
				    }
				}		
			}
			restore_flags(flags);
			len = sprintf(name, "%d", (ino - 2*PROC_NBLINKS) >> 4);

			/* This fills the entries for each session
			   below sessions/<link_no>/ */
			if (netbeui_check_link(name, len)) {
				save_flags(flags);
				cli();
				dp = nbll_get_session_table((ino - 2*PROC_NBLINKS) >> 4);
				for (i -= 2, j = dp->reserved; i > 0 && j < dp->size; j++) 
					if (dp->addr[j])
						i--;
				for (j = j; j < dp->size; j++) {
				    if (dp->addr[j]) {
					sp = (session_t *)(dp->addr[j]);
					len = sprintf(name, "%d-%d", sp->lsn, 
						sp->rsn);
					if (filldir(dirent, name,
					   len, filp->f_pos, (sp->lsn << 4) + 
						((ino - 2*PROC_NBLINKS) << 4) +
						2*PROC_NBSESSIONS) < 0) {
						restore_flags(flags);
						return 0;
					}
					filp->f_pos++;
				    }
				}   
				restore_flags(flags);
			}  
			
			/* This is for filling the entries
			   created by proc_register */
			i -= 2;
			de = de->subdir;
			for (;;) {
				if (!de)
					return 1;
				if (!i)
					break;
				i--;
				de = de->next;
			}
				
			do {
				if (filldir(dirent, de->name, de->namelen, 
			    	   filp->f_pos, de->low_ino) < 0)
					return 0;
				filp->f_pos++;
				de = de->next;
			}
			while (de);
	}
	return 0;
}


/*
 * Function: proc_netbeui_read
 *	Reads the contents of proc filesystem files into a buffer
 *
 * Parameters: 
 *	inode	: inode of the file to be read
 *	filp	: last file pointer position inside the file
 *	buf	: data buffer which is to be filled by the data file contents
 *	count	: count of the bytes read inside the buffer
 *
 * Returns: 
 *	-EINVAL	: invalid inode for the file to be read
 *	-ENOMEM	: there is no free page to use for contents of data file
 *	-EBADF	: there is no file with given i-number
 *	0 	: a read after the end of file is performed
 *	other	: count of bytes read into data buffer
 *
 * (1997 DEC 2'th,  R.S. - Commenting)
 */
static int 
proc_netbeui_read(struct inode * inode, struct file * filp,
                             char * buf, int count)
{
	char * page;
	int length;
	int end;
	unsigned int ino;

	if (!inode || S_ISDIR(inode->i_mode))
		return -EINVAL;
	if (count < 0) 
		return -EINVAL;
	page = (char *) get_free_page(GFP_KERNEL);
	if (!page)
		return -ENOMEM;
	ino = inode->i_ino;
	
	/* First tries to search the file in 
	   name , link and session data structs */
	if (!(length = netbeui_name_operation(page, ino)) &&
	    !(length = netbeui_link_operation(page, ino)) && 
	    !(length = netbeui_session_operation(page, ino))) {

		switch (ino) {
			case ADAPTER_INO:
				length = adapter_operation(page);
				break;
			case VERSION_INO:
				length = version_operation(page);
				break;
			default:
				free_page((unsigned long) page);
				return -EBADF;
		} 
	}
	if (filp->f_pos > length) {			
		free_page((unsigned long) page);
		return 0;
	}
	if (count + filp->f_pos > length)
		count = length - filp->f_pos;
	end = count + filp->f_pos;
	memcpy_tofs(buf, page + filp->f_pos, count);
	free_page((unsigned long) page);
	filp->f_pos = end;
	return count;
}


/*
 * Function: find_proc_sys_entry
 *	Finds the location of the /proc/sys/ directory entry in proc 
 *	directory structure in memory 
 *
 * Parameters: 
 *	proot	: root of the proc filesystem that is /proc/
 *	psys	: address of the /proc/sys/ directory entry
 *
 * Returns: 
 *	none
 *
 * (1997 DEC 2'th,  R.S. - Commenting)
 */
void 
find_proc_sys_entry(struct proc_dir_entry * proot, struct proc_dir_entry ** psys)
{
	struct proc_dir_entry * de;
	char lname[MAX_NAME_LEN];

	de = proot->subdir;
	strncpy(lname, de->name, MAX_NAME_LEN - 1);
	while (de && strcmp(lname, "sys")) {
		de = de->next;
		if (de) 
			strncpy(lname, de->name, MAX_NAME_LEN - 1);
	}
	*psys = de;
}


/*
 * Function: proc_init
 *	looks for /proc/sys/ directory entry then creates netbeui proc
 *	directory entries below it
 *
 * Parameters: 
 *	none
 *
 * Returns: 
 *	none
 *
 * (1997 DEC 2'th,  R.S. - Commenting)
 */
void 
proc_init()
{
	find_proc_sys_entry(&proc_root, &n_proc_sys);
	proc_register(n_proc_sys, &proc_netbeui);
	proc_register(&proc_netbeui, &proc_links);
	proc_register(&proc_netbeui, &proc_sessions);
	proc_register(&proc_netbeui, &proc_names);
	proc_register(&proc_netbeui, &proc_version); 
	proc_register(&proc_netbeui, &proc_adapter); 
}

/*
 * Function: proc_clean
 *	Clears all netbeui directory entries for netbeui from /proc/sys/
 *
 * Parameters: 
 *	none
 *
 * Returns: 
 *	none
 *
 * (1997 DEC 2'th,  R.S. - Commenting)
 */
void 
proc_clean()
{
	proc_unregister(&proc_netbeui, PROC_NBNAMES);
	proc_unregister(&proc_netbeui, PROC_NBSESSIONS);
	proc_unregister(&proc_netbeui, PROC_NBLINKS);
	proc_unregister(&proc_netbeui, ADAPTER_INO);
	proc_unregister(&proc_netbeui, VERSION_INO);  
	proc_unregister(n_proc_sys, PROC_NETBEUI);
}
