/*
 * Copyright 2003 Digi International (www.digi.com)
 *	Scott H Kilau <Scott_Kilau at digi dot com>
 *
 * 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, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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.
 *
 *
 *	NOTE TO LINUX KERNEL HACKERS:  DO NOT REFORMAT THIS CODE! 
 *
 *	This is shared code between Digi's CVS archive and the
 *	Linux Kernel sources.
 *	Changing the source just for reformatting needlessly breaks
 *	our CVS diff history.
 *
 *	Send any bug fixes/changes to:  Eng.Linux at digi dot com. 
 *	Thank you.
 *
 *
 *****************************************************************************
 *
 * dgap_parse.c - Parses the configuration information from the input file.
 *
 * $Id: dgap_parse.c,v 1.1 2009/10/23 14:01:57 markh Exp $
 *
 */
#include <linux/kernel.h>
#include <linux/ctype.h>
#include <linux/slab.h>

#include "dgap_types.h"
#include "dgap_fep5.h"
#include "dgap_driver.h"
#include "dgap_parse.h"
#include "dgap_conf.h"


/*
 * Function prototypes.
 */
static int dgap_gettok(char **in, struct cnode *p);
static char *dgap_getword(char **in);
static char *dgap_savestring(char *s);
static struct cnode *dgap_newnode(int t);
static int dgap_checknode(struct cnode *p);
static void dgap_err(char *s);

/*
 * Our needed internal static variables...
 */
static struct cnode dgap_head;
#define MAXCWORD 200
static char dgap_cword[MAXCWORD];

struct toklist {
	int	token;
	char	*string;
};

static struct toklist dgap_tlist[] = {
	{	BEGIN,		"config_begin"			},
	{	END,		"config_end"			},
	{	BOARD,		"board"				},
	{	PCX,		"Digi_AccelePort_C/X_PCI"	},	/* C/X_PCI */
	{	PEPC,		"Digi_AccelePort_EPC/X_PCI"	},	/* EPC/X_PCI */
	{	PPCM,		"Digi_AccelePort_Xem_PCI"	},	/* PCI/Xem */
	{	APORT2_920P,	"Digi_AccelePort_2r_920_PCI"	},
	{	APORT4_920P,	"Digi_AccelePort_4r_920_PCI"	},
	{	APORT8_920P,	"Digi_AccelePort_8r_920_PCI"	},
	{	PAPORT4,	"Digi_AccelePort_4r_PCI(EIA-232/RS-422)" },
	{	PAPORT8,	"Digi_AccelePort_8r_PCI(EIA-232/RS-422)" },
	{	IO,		"io"				},
	{	PCIINFO,	"pciinfo"			},
	{	LINE,		"line"				},
	{	CONC,		"conc"				},
	{	CONC,		"concentrator"			},
	{	CX,		"cx"				},
	{	CX,		"ccon"				},
	{	EPC,		"epccon"			},
	{	EPC,		"epc"				},
	{	MOD,		"module"			},
	{	ID,		"id"				},
	{	STARTO,		"start"				},
	{	SPEED,		"speed"				},
	{	CABLE,		"cable"				},
	{	CONNECT,	"connect"			},
	{	METHOD,		"method"			},
	{	STATUS,		"status"			},
	{	CUSTOM,		"Custom"			},
	{	BASIC,		"Basic"				},
	{	MEM,		"mem"				},
	{	MEM,		"memory"			},
	{	PORTS,		"ports"				},
	{	MODEM,		"modem"				},
	{	NPORTS,		"nports"			},
	{	TTYN,		"ttyname"			},
	{	CU,		"cuname"			},
	{	PRINT,		"prname"			},
	{	CMAJOR,		"major"				},
	{	ALTPIN,		"altpin"			},
	{	USEINTR,	"useintr"			},
	{	TTSIZ,		"ttysize"			},
	{	CHSIZ,		"chsize"			},
	{	BSSIZ,		"boardsize"			},
	{	UNTSIZ,		"schedsize"			},
	{	F2SIZ,		"f2200size"			},
	{	VPSIZ,		"vpixsize"			},
	{	0,		NULL				}
};


/*
 * Parse a configuration file read into memory as a string.
 */
int	dgap_parsefile(char **in, int Remove)
{
	struct cnode *p, *brd, *line, *conc;
	int	rc;
	char	*s = NULL, *s2 = NULL;
	int	linecnt = 0;

	p = &dgap_head;
	brd = line = conc = NULL;

	/* perhaps we are adding to an existing list? */
	while (p->next != NULL) {
		p = p->next;
	}

	/* file must start with a BEGIN */
	while ( (rc = dgap_gettok(in,p)) != BEGIN ) {
		if (rc == 0) {
			dgap_err("unexpected EOF");
			return(-1);
		}
	}

	for (; ; ) {
		rc = dgap_gettok(in,p);
		if (rc == 0) {
			dgap_err("unexpected EOF");
			return(-1);
		}

		switch (rc) {
		case 0:
			dgap_err("unexpected end of file");
			return(-1);

		case BEGIN:	/* should only be 1 begin */
			dgap_err("unexpected config_begin\n");
			return(-1);

		case END:
			return(0);

		case BOARD:	/* board info */
			if (dgap_checknode(p))
				return(-1);
			if ( (p->next = dgap_newnode(BNODE)) == NULL ) {
				dgap_err("out of memory");
				return(-1);
			}
			p = p->next;

			p->u.board.status = dgap_savestring("No");
			line = conc = NULL;
			brd = p;
			linecnt = -1;
			break;

		case APORT2_920P:	/* AccelePort_4 */
			if (p->type != BNODE) {
				dgap_err("unexpected Digi_2r_920 string");
				return(-1);
			}
			p->u.board.type = APORT2_920P;
			p->u.board.v_type = 1;
			DPR_INIT(("Adding Digi_2r_920 PCI to config...\n"));
			break;

		case APORT4_920P:	/* AccelePort_4 */
			if (p->type != BNODE) {
				dgap_err("unexpected Digi_4r_920 string");
				return(-1);
			}
			p->u.board.type = APORT4_920P;
			p->u.board.v_type = 1;
			DPR_INIT(("Adding Digi_4r_920 PCI to config...\n"));
			break;

		case APORT8_920P:	/* AccelePort_8 */
			if (p->type != BNODE) {
				dgap_err("unexpected Digi_8r_920 string");
				return(-1);
			}
			p->u.board.type = APORT8_920P;
			p->u.board.v_type = 1;
			DPR_INIT(("Adding Digi_8r_920 PCI to config...\n"));
			break;

		case PAPORT4:	/* AccelePort_4 PCI */
			if (p->type != BNODE) {
				dgap_err("unexpected Digi_4r(PCI) string");
				return(-1);
			}
			p->u.board.type = PAPORT4;
			p->u.board.v_type = 1;
			DPR_INIT(("Adding Digi_4r PCI to config...\n"));
			break;

		case PAPORT8:	/* AccelePort_8 PCI */
			if (p->type != BNODE) {
				dgap_err("unexpected Digi_8r string");
				return(-1);
			}
			p->u.board.type = PAPORT8;
			p->u.board.v_type = 1;
			DPR_INIT(("Adding Digi_8r PCI to config...\n"));
			break;

		case PCX:	/* PCI C/X */
			if (p->type != BNODE) {
				dgap_err("unexpected Digi_C/X_(PCI) string");
				return(-1);
			}
			p->u.board.type = PCX;
			p->u.board.v_type = 1;
			p->u.board.conc1 = 0;
			p->u.board.conc2 = 0;
			p->u.board.module1 = 0;
			p->u.board.module2 = 0;
			DPR_INIT(("Adding PCI C/X to config...\n"));
			break;

		case PEPC:	/* PCI EPC/X */
			if (p->type != BNODE) {
				dgap_err("unexpected \"Digi_EPC/X_(PCI)\" string");
				return(-1);
			}
			p->u.board.type = PEPC;
			p->u.board.v_type = 1;
			p->u.board.conc1 = 0;
			p->u.board.conc2 = 0;
			p->u.board.module1 = 0;
			p->u.board.module2 = 0;
			DPR_INIT(("Adding PCI EPC/X to config...\n"));
			break;

		case PPCM:	/* PCI/Xem */
			if (p->type != BNODE) {
				dgap_err("unexpected PCI/Xem string");
				return(-1);
			}
			p->u.board.type = PPCM;
			p->u.board.v_type = 1;
			p->u.board.conc1 = 0;
			p->u.board.conc2 = 0;
			DPR_INIT(("Adding PCI XEM to config...\n"));
			break;

		case IO:	/* i/o port */
			if (p->type != BNODE) {
				dgap_err("IO port only vaild for boards");
				return(-1);
			}
			s = dgap_getword(in);
			if (s == NULL) {
				dgap_err("unexpected end of file");
				return(-1);
			}
			p->u.board.portstr = dgap_savestring(s);
			p->u.board.port = (short)simple_strtol(s, &s2, 0);
			if ((short)strlen(s) > (short)(s2 - s)) {
				dgap_err("bad number for IO port");
				return(-1);
			}
			p->u.board.v_port = 1;
			DPR_INIT(("Adding IO (%s) to config...\n", s));
			break;

		case MEM:	/* memory address */
			if (p->type != BNODE) {
				dgap_err("memory address only vaild for boards");
				return(-1);
			}
			s = dgap_getword(in);
			if (s == NULL) {
				dgap_err("unexpected end of file");
				return(-1);
			}
			p->u.board.addrstr = dgap_savestring(s);
			p->u.board.addr = simple_strtoul(s, &s2, 0);
			if ((int)strlen(s) > (int)(s2 - s)) {
				dgap_err("bad number for memory address");
				return(-1);
			}
			p->u.board.v_addr = 1;
			DPR_INIT(("Adding MEM (%s) to config...\n", s));
			break;

		case PCIINFO:	/* pci information */
			if (p->type != BNODE) {
				dgap_err("memory address only vaild for boards");
				return(-1);
			}
			s = dgap_getword(in);
			if (s == NULL) {
				dgap_err("unexpected end of file");
				return(-1);
			}
			p->u.board.pcibusstr = dgap_savestring(s);
			p->u.board.pcibus = simple_strtoul(s, &s2, 0);
			if ((int)strlen(s) > (int)(s2 - s)) {
				dgap_err("bad number for pci bus");
				return(-1);
			}
			p->u.board.v_pcibus = 1;
			s = dgap_getword(in);
			if (s == NULL) {
				dgap_err("unexpected end of file");
				return(-1);
			}
			p->u.board.pcislotstr = dgap_savestring(s);
			p->u.board.pcislot = simple_strtoul(s, &s2, 0);
			if ((int)strlen(s) > (int)(s2 - s)) {
				dgap_err("bad number for pci slot");
				return(-1);
			}
			p->u.board.v_pcislot = 1;

			DPR_INIT(("Adding PCIINFO (%s %s) to config...\n", p->u.board.pcibusstr, 
				p->u.board.pcislotstr));
			break;

		case METHOD:
			if (p->type != BNODE) {
				dgap_err("install method only vaild for boards");
				return(-1);
			}
			s = dgap_getword(in);
			if (s == NULL) {
				dgap_err("unexpected end of file");
				return(-1);
			}
			p->u.board.method = dgap_savestring(s);
			p->u.board.v_method = 1;
			DPR_INIT(("Adding METHOD (%s) to config...\n", s));
			break;

		case STATUS:
			if (p->type != BNODE) {
				dgap_err("config status only vaild for boards");
				return(-1);
			}
			s = dgap_getword(in);
			if (s == NULL) {
				dgap_err("unexpected end of file");
				return(-1);
			}
			p->u.board.status = dgap_savestring(s);
			DPR_INIT(("Adding STATUS (%s) to config...\n", s));
			break;

		case NPORTS:	/* number of ports */
			if (p->type == BNODE) {
				s = dgap_getword(in);
				if (s == NULL) {
					dgap_err("unexpected end of file");
					return(-1);
				}
				p->u.board.nport = (char)simple_strtol(s, &s2, 0);
				if ((int)strlen(s) > (int)(s2 - s)) {
					dgap_err("bad number for number of ports");
					return(-1);
				}
				p->u.board.v_nport = 1;
			} else if (p->type == CNODE) {
				s = dgap_getword(in);
				if (s == NULL) {
					dgap_err("unexpected end of file");
					return(-1);
				}
				p->u.conc.nport = (char)simple_strtol(s, &s2, 0);
				if ((int)strlen(s) > (int)(s2 - s)) {
					dgap_err("bad number for number of ports");
					return(-1);
				}
				p->u.conc.v_nport = 1;
			} else if (p->type == MNODE) {
				s = dgap_getword(in);
				if (s == NULL) {
					dgap_err("unexpected end of file");
					return(-1);
				}
				p->u.module.nport = (char)simple_strtol(s, &s2, 0);
				if ((int)strlen(s) > (int)(s2 - s)) {
					dgap_err("bad number for number of ports");
					return(-1);
				}
				p->u.module.v_nport = 1;
			} else {
				dgap_err("nports only valid for concentrators or modules");
				return(-1);
			}
			DPR_INIT(("Adding NPORTS (%s) to config...\n", s));
			break;

		case ID:	/* letter ID used in tty name */
			s = dgap_getword(in);
			if (s == NULL) {
				dgap_err("unexpected end of file");
				return(-1);
			}

			p->u.board.status = dgap_savestring(s);

			if (p->type == CNODE) {
				p->u.conc.id = dgap_savestring(s);
				p->u.conc.v_id = 1;
			} else if (p->type == MNODE) {
				p->u.module.id = dgap_savestring(s);
				p->u.module.v_id = 1;
			} else {
				dgap_err("id only valid for concentrators or modules");
				return(-1);
			}
			DPR_INIT(("Adding ID (%s) to config...\n", s));
			break;

		case STARTO:	/* start offset of ID */
			if (p->type == BNODE) {
				s = dgap_getword(in);
				if (s == NULL) {
					dgap_err("unexpected end of file");
					return(-1);
				}
				p->u.board.start = simple_strtol(s, &s2, 0);
				if ((int)strlen(s) > (int)(s2 - s)) {
					dgap_err("bad number for start of tty count");
					return(-1);
				}
				p->u.board.v_start = 1;
			} else if (p->type == CNODE) {
				s = dgap_getword(in);
				if (s == NULL) {
					dgap_err("unexpected end of file");
					return(-1);
				}
				p->u.conc.start = simple_strtol(s, &s2, 0);
				if ((int)strlen(s) > (int)(s2 - s)) {
					dgap_err("bad number for start of tty count");
					return(-1);
				}
				p->u.conc.v_start = 1;
			} else if (p->type == MNODE) {
				s = dgap_getword(in);
				if (s == NULL) {
					dgap_err("unexpected end of file");
					return(-1);
				}
				p->u.module.start = simple_strtol(s, &s2, 0);
				if ((int)strlen(s) > (int)(s2 - s)) {
					dgap_err("bad number for start of tty count");
					return(-1);
				}
				p->u.module.v_start = 1;
			} else {
				dgap_err("start only valid for concentrators or modules");
				return(-1);
			}
			DPR_INIT(("Adding START (%s) to config...\n", s));
			break;

		case TTYN:	/* tty name prefix */
			if (dgap_checknode(p))
				return(-1);
			if ( (p->next = dgap_newnode(TNODE)) == NULL ) {
				dgap_err("out of memory");
				return(-1);
			}
			p = p->next;
			if ( (s = dgap_getword(in)) == NULL ) {
				dgap_err("unexpeced end of file");
				return(-1);
			}
			if ( (p->u.ttyname = dgap_savestring(s)) == NULL ) {
				dgap_err("out of memory");
				return(-1);
			}
			DPR_INIT(("Adding TTY (%s) to config...\n", s));
			break;

		case CU:	/* cu name prefix */
			if (dgap_checknode(p))
				return(-1);
			if ( (p->next = dgap_newnode(CUNODE)) == NULL ) {
				dgap_err("out of memory");
				return(-1);
			}
			p = p->next;
			if ( (s = dgap_getword(in)) == NULL ) {
				dgap_err("unexpeced end of file");
				return(-1);
			}
			if ( (p->u.cuname = dgap_savestring(s)) == NULL ) {
				dgap_err("out of memory");
				return(-1);
			}
			DPR_INIT(("Adding CU (%s) to config...\n", s));
			break;

		case LINE:	/* line information */
			if (dgap_checknode(p))
				return(-1);
			if (brd == NULL) {
				dgap_err("must specify board before line info");
				return(-1);
			}
			switch (brd->u.board.type) {
			case PPCM:
				dgap_err("line not vaild for PC/em");
				return(-1);
			}
			if ( (p->next = dgap_newnode(LNODE)) == NULL ) {
				dgap_err("out of memory");
				return(-1);
			}
			p = p->next;
			conc = NULL;
			line = p;
			linecnt++;
			DPR_INIT(("Adding LINE to config...\n"));
			break;

		case CONC:	/* concentrator information */
			if (dgap_checknode(p))
				return(-1);
			if (line == NULL) {
				dgap_err("must specify line info before concentrator");
				return(-1);
			}
			if ( (p->next = dgap_newnode(CNODE)) == NULL ) {
				dgap_err("out of memory");
				return(-1);
			}
			p = p->next;
			conc = p;
			if (linecnt)
				brd->u.board.conc2++;
			else
				brd->u.board.conc1++;

			DPR_INIT(("Adding CONC to config...\n"));
			break;

		case CX:	/* c/x type concentrator */
			if (p->type != CNODE) {
				dgap_err("cx only valid for concentrators");
				return(-1);
			}
			p->u.conc.type = CX;
			p->u.conc.v_type = 1;
			DPR_INIT(("Adding CX to config...\n"));
			break;

		case EPC:	/* epc type concentrator */
			if (p->type != CNODE) {
				dgap_err("cx only valid for concentrators");
				return(-1);
			}
			p->u.conc.type = EPC;
			p->u.conc.v_type = 1;
			DPR_INIT(("Adding EPC to config...\n"));
			break;

		case MOD:	/* EBI module */
			if (dgap_checknode(p))
				return(-1);
			if (brd == NULL) {
				dgap_err("must specify board info before EBI modules");
				return(-1);
			}
			switch (brd->u.board.type) {
			case PPCM:
				linecnt = 0;
				break;
			default:
				if (conc == NULL) {
					dgap_err("must specify concentrator info before EBI module");
					return(-1);
				}
			}
			if ( (p->next = dgap_newnode(MNODE)) == NULL ) {
				dgap_err("out of memory");
				return(-1);
			}
			p = p->next;
			if (linecnt)
				brd->u.board.module2++;
			else
				brd->u.board.module1++;

			DPR_INIT(("Adding MOD to config...\n"));
			break;

		case PORTS:	/* ports type EBI module */
			if (p->type != MNODE) {
				dgap_err("ports only valid for EBI modules");
				return(-1);
			}
			p->u.module.type = PORTS;
			p->u.module.v_type = 1;
			DPR_INIT(("Adding PORTS to config...\n"));
			break;

		case MODEM:	/* ports type EBI module */
			if (p->type != MNODE) {
				dgap_err("modem only valid for modem modules");
				return(-1);
			}
			p->u.module.type = MODEM;
			p->u.module.v_type = 1;
			DPR_INIT(("Adding MODEM to config...\n"));
			break;

		case CABLE:
			if (p->type == LNODE) {
				if ((s = dgap_getword(in)) == NULL) {
					dgap_err("unexpected end of file");
					return(-1);
				}
				p->u.line.cable = dgap_savestring(s);
				p->u.line.v_cable = 1;
			}
			DPR_INIT(("Adding CABLE (%s) to config...\n", s));
			break;

		case SPEED:	/* sync line speed indication */
			if (p->type == LNODE) {
				s = dgap_getword(in);
				if (s == NULL) {
					dgap_err("unexpected end of file");
					return(-1);
				}
				p->u.line.speed = (char)simple_strtol(s, &s2, 0);
				if ((short)strlen(s) > (short)(s2 - s)) {
					dgap_err("bad number for line speed");
					return(-1);
				}
				p->u.line.v_speed = 1;
			} else if (p->type == CNODE) {
				s = dgap_getword(in);
				if (s == NULL) {
					dgap_err("unexpected end of file");
					return(-1);
				}
				p->u.conc.speed = (char)simple_strtol(s, &s2, 0);
				if ((short)strlen(s) > (short)(s2 - s)) {
					dgap_err("bad number for line speed");
					return(-1);
				}
				p->u.conc.v_speed = 1;
			} else {
				dgap_err("speed valid only for lines or concentrators.");
				return(-1);
			}
			DPR_INIT(("Adding SPEED (%s) to config...\n", s));
			break;

		case CONNECT:
			if (p->type == CNODE) {
				if ((s = dgap_getword(in)) == NULL) {
					dgap_err("unexpected end of file");
					return(-1);
				}
				p->u.conc.connect = dgap_savestring(s);
				p->u.conc.v_connect = 1;
			}
			DPR_INIT(("Adding CONNECT (%s) to config...\n", s));
			break;
		case PRINT:	/* transparent print name prefix */
			if (dgap_checknode(p))
				return(-1);
			if ( (p->next = dgap_newnode(PNODE)) == NULL ) {
				dgap_err("out of memory");
				return(-1);
			}
			p = p->next;
			if ( (s = dgap_getword(in)) == NULL ) {
				dgap_err("unexpeced end of file");
				return(-1);
			}
			if ( (p->u.printname = dgap_savestring(s)) == NULL ) {
				dgap_err("out of memory");
				return(-1);
			}
			DPR_INIT(("Adding PRINT (%s) to config...\n", s));
			break;

		case CMAJOR:	/* major number */
			if (dgap_checknode(p))
				return(-1);
			if ( (p->next = dgap_newnode(JNODE)) == NULL ) {
				dgap_err("out of memory");
				return(-1);
			}
			p = p->next;
			s = dgap_getword(in);
			if (s == NULL) {
				dgap_err("unexpected end of file");
				return(-1);
			}
			p->u.majornumber = simple_strtol(s, &s2, 0);
			if ((int)strlen(s) > (int)(s2 - s)) {
				dgap_err("bad number for major number");
				return(-1);
			}
			DPR_INIT(("Adding CMAJOR (%s) to config...\n", s));
			break;

		case ALTPIN:	/* altpin setting */
			if (dgap_checknode(p))
				return(-1);
			if ( (p->next = dgap_newnode(ANODE)) == NULL ) {
				dgap_err("out of memory");
				return(-1);
			}
			p = p->next;
			s = dgap_getword(in);
			if (s == NULL) {
				dgap_err("unexpected end of file");
				return(-1);
			}
			p->u.altpin = simple_strtol(s, &s2, 0);
			if ((int)strlen(s) > (int)(s2 - s)) {
				dgap_err("bad number for altpin");
				return(-1);
			}
			DPR_INIT(("Adding ALTPIN (%s) to config...\n", s));
			break;

		case USEINTR:		/* enable interrupt setting */
			if (dgap_checknode(p))
				return(-1);
			if ( (p->next = dgap_newnode(INTRNODE)) == NULL ) {
				dgap_err("out of memory");
				return(-1);
			}
			p = p->next;
			s = dgap_getword(in);
			if (s == NULL) {
				dgap_err("unexpected end of file");
				return(-1);
			}
			p->u.useintr = simple_strtol(s, &s2, 0);
			if ((int)strlen(s) > (int)(s2 - s)) {
				dgap_err("bad number for useintr");
				return(-1);
			}
			DPR_INIT(("Adding USEINTR (%s) to config...\n", s));
			break;

		case TTSIZ:	/* size of tty structure */
			if (dgap_checknode(p))
				return(-1);
			if ( (p->next = dgap_newnode(TSNODE)) == NULL ) {
				dgap_err("out of memory");
				return(-1);
			}
			p = p->next;
			s = dgap_getword(in);
			if (s == NULL) {
				dgap_err("unexpected end of file");
				return(-1);
			}
			p->u.ttysize = simple_strtol(s, &s2, 0);
			if ((int)strlen(s) > (int)(s2 - s)) {
				dgap_err("bad number for ttysize");
				return(-1);
			}
			DPR_INIT(("Adding TTSIZ (%s) to config...\n", s));
			break;

		case CHSIZ:	/* channel structure size */
			if (dgap_checknode(p))
				return(-1);
			if ( (p->next = dgap_newnode(CSNODE)) == NULL ) {
				dgap_err("out of memory");
				return(-1);
			}
			p = p->next;
			s = dgap_getword(in);
			if (s == NULL) {
				dgap_err("unexpected end of file");
				return(-1);
			}
			p->u.chsize = simple_strtol(s, &s2, 0);
			if ((int)strlen(s) > (int)(s2 - s)) {
				dgap_err("bad number for chsize");
				return(-1);
			}
			DPR_INIT(("Adding CHSIZE (%s) to config...\n", s));
			break;

		case BSSIZ:	/* board structure size */
			if (dgap_checknode(p))
				return(-1);
			if ( (p->next = dgap_newnode(BSNODE)) == NULL ) {
				dgap_err("out of memory");
				return(-1);
			}
			p = p->next;
			s = dgap_getword(in);
			if (s == NULL) {
				dgap_err("unexpected end of file");
				return(-1);
			}
			p->u.bssize = simple_strtol(s, &s2, 0);
			if ((int)strlen(s) > (int)(s2 - s)) {
				dgap_err("bad number for bssize");
				return(-1);
			}
			DPR_INIT(("Adding BSSIZ (%s) to config...\n", s));
			break;

		case UNTSIZ:	/* sched structure size */
			if (dgap_checknode(p))
				return(-1);
			if ( (p->next = dgap_newnode(USNODE)) == NULL ) {
				dgap_err("out of memory");
				return(-1);
			}
			p = p->next;
			s = dgap_getword(in);
			if (s == NULL) {
				dgap_err("unexpected end of file");
				return(-1);
			}
			p->u.unsize = simple_strtol(s, &s2, 0);
			if ((int)strlen(s) > (int)(s2 - s)) {
				dgap_err("bad number for schedsize");
				return(-1);
			}
			DPR_INIT(("Adding UNTSIZ (%s) to config...\n", s));
			break;

		case F2SIZ:	/* f2200 structure size */
			if (dgap_checknode(p))
				return(-1);
			if ( (p->next = dgap_newnode(FSNODE)) == NULL ) {
				dgap_err("out of memory");
				return(-1);
			}
			p = p->next;
			s = dgap_getword(in);
			if (s == NULL) {
				dgap_err("unexpected end of file");
				return(-1);
			}
			p->u.f2size = simple_strtol(s, &s2, 0);
			if ((int)strlen(s) > (int)(s2 - s)) {
				dgap_err("bad number for f2200size");
				return(-1);
			}
			DPR_INIT(("Adding F2SIZ (%s) to config...\n", s));
			break;

		case VPSIZ:	/* vpix structure size */
			if (dgap_checknode(p))
				return(-1);
			if ( (p->next = dgap_newnode(VSNODE)) == NULL ) {
				dgap_err("out of memory");
				return(-1);
			}
			p = p->next;
			s = dgap_getword(in);
			if (s == NULL) {
				dgap_err("unexpected end of file");
				return(-1);
			}
			p->u.vpixsize = simple_strtol(s, &s2, 0);
			if ((int)strlen(s) > (int)(s2 - s)) {
				dgap_err("bad number for vpixsize");
				return(-1);
			}
			DPR_INIT(("Adding VPSIZ (%s) to config...\n", s));
			break;
		}
	}
}


/*
 * dgap_sindex: much like index(), but it looks for a match of any character in
 * the group, and returns that position.  If the first character is a ^, then
 * this will match the first occurrence not in that group.
 */
static char *dgap_sindex (char *string, char *group)
{
	char    *ptr;

	if (!string || !group)
		return (char *) NULL;

	if (*group == '^') {   
		group++;
		for (; *string; string++) {
			for (ptr = group; *ptr; ptr++) {
				if (*ptr == *string)
					break;
			}
			if (*ptr == '\0')
				return string;
		}
	}   
	else {
		for (; *string; string++) {
			for (ptr = group; *ptr; ptr++) {
				if (*ptr == *string)
					return string;
			}
		}
	}

	return (char *) NULL;
}


/*
 * Get a token from the input file; return 0 if end of file is reached
 */
static int dgap_gettok(char **in, struct cnode *p)
{
	char	*w;
	struct toklist *t;
	
	if (strstr(dgap_cword, "boar")) {
		w = dgap_getword(in);
		snprintf(dgap_cword, MAXCWORD, "%s", w);
		for (t = dgap_tlist; t->token != 0; t++) {
			if ( !strcmp(w, t->string)) {
				return(t->token);
			} 
		}
		dgap_err("board !!type not specified");
		return(1);
	}
	else {
		while ( (w = dgap_getword(in)) != NULL ) {
			snprintf(dgap_cword, MAXCWORD, "%s", w);
			for (t = dgap_tlist; t->token != 0; t++) {
				if ( !strcmp(w, t->string) )
					return(t->token);
			}
		}
		return(0);
	}
}


/*
 * get a word from the input stream, also keep track of current line number.
 * words are separated by whitespace.
 */
static char *dgap_getword(char **in)
{
	char *ret_ptr = *in;

        char *ptr = dgap_sindex(*in, " \t\n");

	/* If no word found, return null */
	if (!ptr)
		return NULL;

	/* Mark new location for our buffer */
	*ptr = '\0';
	*in = ptr + 1;

	/* Eat any extra spaces/tabs/newlines that might be present */
	while (*in && **in && ((**in == ' ') || (**in == '\t') || (**in == '\n'))) {
		**in = '\0';
		*in = *in + 1;
	}

	return ret_ptr;
}


/*
 * print an error message, giving the line number in the file where
 * the error occurred.
 */
static void dgap_err(char *s)
{
	printk("DGAP: parse: %s\n", s);
}


/*
 * allocate a new configuration node of type t
 */
static struct cnode *dgap_newnode(int t)
{
	struct cnode *n;

	n = kmalloc(sizeof(struct cnode), GFP_ATOMIC);
	if (n != NULL) {
		memset((char *)n, 0, sizeof(struct cnode));
		n->type = t;
	}
	return(n);
}


/*
 * dgap_checknode: see if all the necessary info has been supplied for a node
 * before creating the next node.
 */
static int dgap_checknode(struct cnode *p)
{
	switch (p->type) {
	case BNODE:
		if (p->u.board.v_type == 0) {
			dgap_err("board type !not specified");
			return(1);
		}

		return(0);

	case LNODE:
		if (p->u.line.v_speed == 0) {
			dgap_err("line speed not specified");
			return(1);
		}
		return(0);

	case CNODE:
		if (p->u.conc.v_type == 0) {
			dgap_err("concentrator type not specified");
			return(1);
		}
		if (p->u.conc.v_speed == 0) {
			dgap_err("concentrator line speed not specified");
			return(1);
		}
		if (p->u.conc.v_nport == 0) {
			dgap_err("number of ports on concentrator not specified");
			return(1);
		}
		if (p->u.conc.v_id == 0) {
			dgap_err("concentrator id letter not specified");
			return(1);
		}
		return(0);

	case MNODE:
		if (p->u.module.v_type == 0) {
			dgap_err("EBI module type not specified");
			return(1);
		}
		if (p->u.module.v_nport == 0) {
			dgap_err("number of ports on EBI module not specified");
			return(1);
		}
		if (p->u.module.v_id == 0) {
			dgap_err("EBI module id letter not specified");
			return(1);
		}
		return(0);
	}
	return(0);
}

/*
 * save a string somewhere
 */
static char	*dgap_savestring(char *s)
{
	char	*p;
	if ( (p = kmalloc(strlen(s) + 1, GFP_ATOMIC) ) != NULL) {
		strcpy(p, s);
	}
	return(p);
}


/*
 * Given a board pointer, returns whether we should use interrupts or not.
 */
uint dgap_config_get_useintr(struct board_t *bd)
{
	struct cnode *p = NULL;

	if (!bd)
		return(0);

	for (p = bd->bd_config; p; p = p->next) {
		switch (p->type) {
		case INTRNODE:
			/*
			 * check for pcxr types.
			 */
			return p->u.useintr;
		default:
			break;
		}
	}

	/* If not found, then don't turn on interrupts. */
	return 0;
}


/*
 * Given a board pointer, returns whether we turn on altpin or not.
 */
uint dgap_config_get_altpin(struct board_t *bd)
{
	struct cnode *p = NULL;

	if (!bd)
		return(0);

	for (p = bd->bd_config; p; p = p->next) {
		switch (p->type) {
		case ANODE:
			/*
			 * check for pcxr types.
			 */
			return p->u.altpin;
		default:
			break;
		}
	}

	/* If not found, then don't turn on interrupts. */
	return 0;
}



/*
 * Given a specific type of board, if found, detached link and 
 * returns the first occurrence in the list.
 */
struct cnode *dgap_find_config(int type, int bus, int slot)
{
	struct cnode *p, *prev = NULL, *prev2 = NULL, *found = NULL;

	p = &dgap_head;

	while (p->next != NULL) {
		prev = p;
		p = p->next;

		if (p->type == BNODE) {

			if (p->u.board.type == type) {

				if (p->u.board.v_pcibus && p->u.board.pcibus != bus) {
					DPR(("Found matching board, but wrong bus position. System says bus %d, we want bus %ld\n",
						bus, p->u.board.pcibus));
					continue;
				}
				if (p->u.board.v_pcislot && p->u.board.pcislot != slot) {
					DPR_INIT(("Found matching board, but wrong slot position. System says slot %d, we want slot %ld\n",
						slot, p->u.board.pcislot));
					continue;
				}

				DPR_INIT(("Matched type in config file\n"));

				found = p;
				/*
				 * Keep walking thru the list till we find the next board.
				 */
				while (p->next != NULL) {
					prev2 = p;
					p = p->next;
					if (p->type == BNODE) {

						/*
						 * Mark the end of our 1 board chain of configs.
						 */
						prev2->next = NULL;

						/*
						 * Link the "next" board to the previous board,
						 * effectively "unlinking" our board from the main config.
						 */
						prev->next = p;

						return found;
					}
				}
				/*
				 * It must be the last board in the list.
				 */
				prev->next = NULL;
				return found;
			}
		}
	}
	return NULL;
}

/*
 * Given a board pointer, walks the config link, counting up
 * all ports user specified should be on the board.
 * (This does NOT mean they are all actually present right now tho)
 */
uint dgap_config_get_number_of_ports(struct board_t *bd)
{
	int count = 0;
	struct cnode *p = NULL;

	if (!bd)
		return(0);

	for (p = bd->bd_config; p; p = p->next) {

		switch (p->type) {
		case BNODE:
			/*
			 * check for pcxr types.
			 */
			if (p->u.board.type > EPCFE)
				count += p->u.board.nport;
			break;
		case CNODE:
			count += p->u.conc.nport;
			break;
		case MNODE:
			count += p->u.module.nport;
			break;
		}
	}
	return (count);
}

char *dgap_create_config_string(struct board_t *bd, char *string)
{
	char *ptr = string;
	struct cnode *p = NULL;
	struct cnode *q = NULL;
	int speed;

	if (!bd) {
		*ptr = 0xff;
		return string;
	}

	for (p = bd->bd_config; p; p = p->next) {

		switch (p->type) {
		case LNODE:
			*ptr = '\0';
			ptr++;
			*ptr = p->u.line.speed;
			ptr++;
			break;
		case CNODE:
			/*
			 * Because the EPC/con concentrators can have EM modules
			 * hanging off of them, we have to walk ahead in the list
			 * and keep adding the number of ports on each EM to the config.
			 * UGH!
			 */
			speed = p->u.conc.speed;
			q = p->next;
			if ((q != NULL) && (q->type == MNODE) ) {
				*ptr = (p->u.conc.nport + 0x80);
				ptr++;
				p = q;
				while ((q->next != NULL) && (q->next->type) == MNODE) {
					*ptr = (q->u.module.nport + 0x80);
					ptr++;
					p = q;
					q = q->next;
				}
				*ptr = q->u.module.nport;
				ptr++;
			} else {
				*ptr = p->u.conc.nport;
				ptr++;
			}

			*ptr = speed;
			ptr++;
			break;
		}
	}

	*ptr = 0xff;
	return string;
}



char *dgap_get_config_letters(struct board_t *bd, char *string)
{
	int found = FALSE;
	char *ptr = string;
	struct cnode *cptr = NULL;
	int len = 0;
	int left = MAXTTYNAMELEN;

	if (!bd) {
		return "<NULL>";
	}

	for (cptr = bd->bd_config; cptr; cptr = cptr->next) {

		if ((cptr->type == BNODE) &&
		     ((cptr->u.board.type == APORT2_920P) || (cptr->u.board.type == APORT4_920P) ||
		     (cptr->u.board.type == APORT8_920P) || (cptr->u.board.type == PAPORT4) ||
		     (cptr->u.board.type == PAPORT8))) {

			found = TRUE;
		}

		if (cptr->type == TNODE && found == TRUE) {
			char *ptr1;
			if (strstr(cptr->u.ttyname, "tty")) {
				ptr1 = cptr->u.ttyname;
				ptr1 += 3;
			}
			else {
				ptr1 = cptr->u.ttyname;
			}
			if (ptr1) {
				len = snprintf(ptr, left, "%s", ptr1);
				left -= len;
				ptr  += len;
				if (left <= 0)
					break;
			}
		}

		if (cptr->type == CNODE) {
			if (cptr->u.conc.id) {
				len = snprintf(ptr, left, "%s", cptr->u.conc.id);
				left -= len;
				ptr  += len;
				if (left <= 0)
					break;
			}
                }

		if (cptr->type == MNODE) {
			if (cptr->u.module.id) {
				len = snprintf(ptr, left, "%s", cptr->u.module.id);
				left -= len;
				ptr  += len;
				if (left <= 0)
					break;
			}
		}
	}

	return string;
}