freebsd-nq/usr.sbin/pccard/pccardd/file.c
1995-08-25 09:46:01 +00:00

878 lines
13 KiB
C

/*
* Decode pcmciad file.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "cardd.h"
static FILE *in;
static int pushc, pusht;
static int lineno;
static char *filename;
static char *keys[] =
{
"io", /* 1 */
"irq", /* 2 */
"memory", /* 3 */
"card", /* 4 */
"device", /* 5 */
"config", /* 6 */
"__EOF__", /* 7 */
"reset", /* 8 */
"ether", /* 9 */
"insert", /* 10 */
"remove", /* 11 */
"iosize", /* 12 */
"memsize", /* 13 */
0
};
struct flags
{
char *name;
int mask;
};
void parsefile();
char *token();
char *getline();
char *next_tok();
int num_tok();
void error(char *);
int keyword(char *);
struct allocblk *ioblk_tok(int);
struct allocblk *memblk_tok(int);
int irq_tok(int);
void setflags(struct flags *, int *);
struct driver *new_driver(char *);
void addcmd(struct cmd **cp);
void parse_card();
/*
* Read a file and parse the pcmcia configuration data.
* After parsing, verify the links.
*/
void
readfile(char *name)
{
struct card *cp;
in = fopen(name, "r");
if (in == 0)
{
perror(name);
exit(1);
}
parsefile();
for (cp = cards; cp; cp = cp->next)
{
if (cp->config == 0)
fprintf(stderr, "warning: card %s(%s) has no valid configuration\n",
cp->manuf, cp->version);
}
}
void
parsefile()
{
int i;
struct allocblk *bp;
pushc = 0;
lineno = 1;
for(;;)
switch(keyword(next_tok()))
{
default:
error("Syntax error");
pusht = 0;
break;
case 7:
return;
/*
* reserved I/O blocks
*/
case 1:
while ((bp = ioblk_tok(0)) != 0)
{
if (bp->size == 0 || bp->addr == 0)
{
free(bp);
continue;
}
bit_nset(io_avail, bp->addr, bp->addr+bp->size-1);
bp->next = pool_ioblks;
pool_ioblks = bp;
}
pusht = 1;
break;
/*
* reserved irqs
*/
case 2:
while ((i = irq_tok(0)) > 0)
pool_irq[i] = 1;
pusht = 1;
break;
/*
* reserved memory blocks.
*/
case 3:
while ((bp = memblk_tok(0)) != 0)
{
if (bp->size == 0 || bp->addr == 0)
{
free(bp);
continue;
}
bit_nset(mem_avail, MEM2BIT(bp->addr),
MEM2BIT(bp->addr+bp->size)-1);
bp->next = pool_mem;
pool_mem = bp;
}
pusht = 1;
break;
/*
* Card definition.
*/
case 4:
parse_card();
break;
/*
* Device description
*/
#if 0
case 5:
parse_device();
break;
#endif
}
}
/*
* Parse a card definition.
*/
void
parse_card()
{
char *man, *vers;
struct card *cp;
int i;
struct card_config *confp, *lastp;
man = newstr(next_tok());
vers = newstr(next_tok());
cp = xmalloc(sizeof(*cp));
cp->manuf = man;
cp->version = vers;
cp->reset_time = 50;
cp->next = cards;
cards = cp;
for (;;)
{
switch(keyword(next_tok()))
{
default:
pusht = 1;
return;
case 8:
i = num_tok();
if (i == -1)
{
error("Illegal card reset time");
break;
}
cp->reset_time = i;
break;
case 6:
i = num_tok();
if (i == -1)
{
error("Illegal card config index");
break;
}
confp = xmalloc(sizeof(*confp));
man = next_tok();
confp->driver = new_driver(man);
confp->irq = num_tok();
confp->flags = num_tok();
if (confp->flags == -1)
{
pusht = 1;
confp->flags = 0;
}
if (confp->irq < 0 || confp->irq > 15)
{
error("Illegal card IRQ value");
break;
}
confp->index = i & 0x3F;
/*
* If no valid driver for this config, then do not save
* this configuration entry.
*/
if (confp->driver)
{
if (cp->config == 0)
cp->config = confp;
else
{
for (lastp = cp->config; lastp->next;
lastp = lastp->next)
;
lastp->next = confp;
}
}
else
free(confp);
break;
case 9:
cp->ether = num_tok();
if (cp->ether == -1)
{
error("Illegal ether address offset");
cp->ether = 0;
}
break;
case 10:
addcmd(&cp->insert);
break;
case 11:
addcmd(&cp->remove);
break;
}
}
}
/*
* Generate a new driver structure. If one exists, use
* that one after confirming the correct class.
*/
struct driver *
new_driver(char *name)
{
struct driver *drvp;
char *p;
for (drvp = drivers; drvp; drvp = drvp->next)
if (strcmp(drvp->name, name)==0)
return(drvp);
drvp = xmalloc(sizeof(*drvp));
drvp->next = drivers;
drivers = drvp;
drvp->name = newstr(name);
drvp->kernel = newstr(name);
p = drvp->kernel;
while (*p++)
if (*p >= '0' && *p <= '9')
{
drvp->unit = atoi(p);
*p = 0;
break;
}
#ifdef DEBUG
if (verbose)
printf("Drv %s%d created\n", drvp->kernel, drvp->unit);
#endif
return(drvp);
}
#if 0
/*
* Parse the device description.
*/
parse_device()
{
enum drvclass type = drvclass_tok();
struct device *dp;
static struct device *lastp;
if (type == drv_none)
{
error("Unknown driver class");
return;
}
dp = xmalloc(sizeof(*dp));
dp->type = type;
if (devlist == 0)
devlist = dp;
else
lastp->next = dp;
lastp = dp;
for (;;)
switch(keyword(next_tok()))
{
default:
pusht = 1;
return;
case 10:
addcmd(&dp->insert);
break;
case 11:
addcmd(&dp->remove);
break;
}
}
/*
* Parse the driver description.
*/
parse_driver()
{
char *name, *dev, *p;
struct driver *dp;
static struct driver *lastp;
int i;
struct allocblk *bp;
static struct flags io_flags[] =
{
{ "ws", 0x01 },
{ "16bit", 0x02 },
{ "cs16", 0x04 },
{ "zerows", 0x08 },
{ 0, 0 }
};
static struct flags mem_flags[] =
{
{ "16bit", 0x01 },
{ "zerows", 0x02 },
{ "ws0", 0x04 },
{ "ws1", 0x08 },
{ 0, 0 }
};
name = newstr(next_tok());
dev = newstr(next_tok());
type = drvclass_tok();
if (type == drv_none)
{
error("Unknown driver class");
return;
}
dp = xmalloc(sizeof(*dp));
dp->name = name;
dp->kernel = dev;
dp->type = type;
dp->unit = -1;
dp->irq = -1;
/*
* Check for unit number in driver name.
*/
p = dev;
while (*p++)
if (*p >= '0' && *p <= '9')
{
dp->unit = atoi(p);
*p = 0;
break;
}
if (dp->unit < 0)
error("Illegal kernel driver unit");
/*
* Place at end of list.
*/
if (lastp == 0)
drivers = dp;
else
lastp->next = dp;
lastp = dp;
for (;;)
switch(keyword(next_tok()))
{
default:
pusht = 1;
return;
case 1:
bp = ioblk_tok(1);
if (bp)
{
setflags(io_flags, &bp->flags);
if (dp->io)
{
error("Duplicate I/O spec");
free(bp);
}
else
{
bit_nclear(io_avail, bp->addr,
bp->addr+bp->size-1);
dp->io = bp;
}
}
break;
case 2:
dp->irq = irq_tok(1);
if (dp->irq > 0)
pool_irq[i] = 0;
break;
case 3:
bp = memblk_tok(1);
if (bp)
{
setflags(mem_flags, &bp->flags);
if (dp->mem)
{
error("Duplicate memory spec");
free(bp);
}
else
{
bit_nclear(mem_avail,
MEM2BIT(bp->addr),
MEM2BIT(bp->addr+bp->size)-1);
dp->mem = bp;
}
}
break;
case 10:
addcmd(&dp->insert);
break;
case 11:
addcmd(&dp->remove);
break;
/*
* iosize - Don't allocate an I/O port, but specify
* a size for the range of ports. The actual port number
* will be allocated dynamically.
*/
case 12:
i = num_tok();
if (i <= 0 || i > 128)
error("Illegal iosize");
else
{
int flags = 0;
setflags(io_flags, &flags);
if (dp->io)
error("Duplicate I/O spec");
else
{
dp->io = xmalloc(sizeof(*dp->io));
dp->io->flags = flags;
dp->io->size = i;
}
}
break;
case 13:
i = num_tok();
if (i <= 0 || i > 256*1024)
error("Illegal memsize");
else
{
int flags = 0;
setflags(mem_flags, &flags);
if (dp->mem)
error("Duplicate memory spec");
else
{
dp->mem = xmalloc(sizeof(*dp->mem));
dp->mem->flags = flags;
dp->mem->size = i;
}
}
break;
}
}
/*
* drvclass_tok - next token is expected to
* be a driver class.
*/
enum drvclass
drvclass_tok()
{
char *s = next_tok();
if (strcmp(s, "tty")==0)
return(drv_tty);
else if (strcmp(s, "net")==0)
return(drv_net);
else if (strcmp(s, "bio")==0)
return(drv_bio);
else if (strcmp(s, "null")==0)
return(drv_null);
return(drv_none);
}
#endif /* 0 */
/*
* Parse one I/O block.
*/
struct allocblk *
ioblk_tok(int force)
{
struct allocblk *io;
int i, j;
if ((i = num_tok()) >= 0)
{
if (strcmp("-", next_tok()) || (j = num_tok()) < 0 || j < i)
{
error("I/O block format error");
return(0);
}
io = xmalloc(sizeof(*io));
io->addr = i;
io->size = j - i + 1;
if (j > IOPORTS)
{
error("I/O port out of range");
if (force)
{
free(io);
io = 0;
}
else
io->addr = io->size = 0;
}
return(io);
}
if (force)
error("Illegal or missing I/O block spec");
return(0);
}
/*
* Parse a memory block.
*/
struct allocblk *
memblk_tok(int force)
{
struct allocblk *mem;
int i, j;
if ((i = num_tok()) >= 0)
if ((j = num_tok()) < 0)
error("Illegal memory block");
else
{
mem = xmalloc(sizeof(*mem));
mem->addr = i & ~(MEMUNIT-1);
mem->size = (j + MEMUNIT - 1) & ~(MEMUNIT-1);
if (i < MEMSTART || (i + j) > MEMEND)
{
error("Memory address out of range");
if (force)
{
free(mem);
mem = 0;
}
else
mem->addr = mem->size = 0;
}
return(mem);
}
if (force)
error("Illegal or missing memory block spec");
return(0);
}
/*
* IRQ token. Must be number > 0 && < 16.
* If force is set, IRQ must exist, and can also be '?'.
*/
int
irq_tok(int force)
{
int i;
if (strcmp("?", next_tok())==0 && force)
return(0);
pusht = 1;
i = num_tok();
if (i > 0 && i < 16)
return(i);
if (force)
error("Illegal IRQ value");
return(-1);
}
/*
* search the table for a match.
*/
int
keyword(char *str)
{
char **s;
int i = 1;
for (s = keys; *s; s++, i++)
if (strcmp(*s, str)==0)
return(i);
return(0);
}
/*
* Set/clear flags
*/
void
setflags(struct flags *flags, int *value)
{
char *s;
struct flags *fp;
int set = 1;
do {
s = next_tok();
if (*s == '!')
{
s++;
set = 0;
}
for (fp = flags; fp->name; fp++)
if (strcmp(s, fp->name)==0)
{
if (set)
*value |= fp->mask;
else
*value &= ~fp->mask;
break;
}
} while (fp->name);
pusht = 1;
}
/*
* addcmd - Append the command line to the list of
* commands.
*/
void
addcmd(struct cmd **cp)
{
char *s = getline();
struct cmd *ncp;
if (*s)
{
ncp = xmalloc(sizeof(*ncp));
ncp->line = s;
while (*cp)
cp = &(*cp)->next;
*cp = ncp;
}
}
void
error(char *msg)
{
pusht = 1;
fprintf(stderr, "%s: %s at line %d, near %s\n",
filename, msg, lineno, next_tok());
pusht = 1;
}
int last_char;
int
get()
{
int c;
if (pushc)
c = pushc;
else
c = getc(in);
pushc = 0;
while (c == '\\')
{
c = getc(in);
switch(c)
{
case '#':
return(last_char = c);
case '\n':
lineno++;
c = getc(in);
continue;
}
pushc = c;
return('\\');
}
if (c == '\n')
lineno++;
if (c == '#')
{
while (get() != '\n')
;
return(last_char = '\n');
}
return(last_char = c);
}
/*
* num_tok - expecting a number token. If not a number,
* return -1.
* Handles octal (who uses octal anymore?)
* hex
* decimal
* Looks for a 'k' at the end of decimal numbers
* and multiplies by 1024.
*/
int
num_tok()
{
char *s = next_tok(), c;
int val=0, base;
base = 10;
c = *s++;
if (c == '0')
{
base = 8;
c = *s++;
if (c == 'x' || c == 'X')
{
c = *s++;
base = 16;
}
}
do {
switch(c)
{
case 'k':
case 'K':
if (val && base == 10 && *s == 0)
return(val * 1024);
return(-1);
default:
return(-1);
case '0': case '1':
case '2': case '3':
case '4': case '5':
case '6': case '7':
val = val * base + c - '0';
break;
case '8': case '9':
if (base == 8)
return(-1);
else
val = val * base + c - '0';
break;
case 'a': case 'b':
case 'c': case 'd':
case 'e': case 'f':
if (base == 16)
val = val * base + c - 'a' + 10;
else
return(-1);
break;
case 'A': case 'B':
case 'C': case 'D':
case 'E': case 'F':
if (base == 16)
val = val * base + c - 'A' + 10;
else
return(-1);
break;
}
} while ((c = *s++) != 0);
return(val);
}
char *_next_tok();
char *
next_tok()
{
char *s = _next_tok();
#if 0
printf("Tok = %s\n", s);
#endif
return(s);
}
/*
* get one token. Handles string quoting etc.
*/
char *
_next_tok()
{
static char buf[1024];
char *p = buf, instr = 0;
int c;
if (pusht)
{
pusht = 0;
return(buf);
}
for(;;)
{
c = get();
switch(c)
{
default:
*p++ = c;
break;
case '"':
if (instr)
{
*p++ = 0;
return(buf);
}
instr = 1;
break;
case '\n':
if (instr)
{
error("Unterminated string");
break;
}
/*
* Eat whitespace unless in a string.
*/
case ' ':
case '\t':
if (!instr)
{
if (p!=buf)
{
*p++ = 0;
return(buf);
}
}
else
*p++ = c;
break;
/*
* Special characters that must be tokens on their own.
*/
case '-':
case '?':
case '*':
if (instr)
*p++ = c;
else
{
if (p != buf)
pushc = c;
else
*p++ = c;
*p++ = 0;
return(buf);
}
break;
case EOF:
if (p != buf)
{
*p++ = 0;
return(buf);
}
strcpy(buf, "__EOF__");
return(buf);
}
}
}
/*
* get the rest of the line. If the
* last character scanned was a newline, then
* return an empty line. If this isn't checked, then
* a getline may incorrectly return the next line.
*/
char *
getline()
{
char buf[1024], *p = buf;
int c, i = 0;
if (last_char == '\n')
return(newstr(""));
do {
c = get();
} while (c == ' ' || c == '\t');
for (;c != '\n' && c != EOF; c = get())
if (i++ < sizeof(buf)-10)
*p++ = c;
*p = 0;
return(newstr(buf));
}