mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-20 11:11:24 +00:00
878 lines
13 KiB
C
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));
|
|
}
|