/* * Copyright (c) 1995 Andrew McRae. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include "cardd.h" static FILE *in; static int includes = 0; static struct { FILE *filep; char *filename; int lineno; } configfiles[MAXINCLUDES] = {{NULL, NULL, 0}, }; static int pushc, pusht; static int lineno; static char *filename; static char *keys[] = { "__EOF__", /* 1 */ "io", /* 2 */ "irq", /* 3 */ "memory", /* 4 */ "card", /* 5 */ "device", /* 6 */ "config", /* 7 */ "reset", /* 8 */ "ether", /* 9 */ "insert", /* 10 */ "remove", /* 11 */ "iosize", /* 12 */ "debuglevel", /* 13 */ "include", /* 14 */ "function", /* 15 */ 0 }; #define KWD_EOF 1 #define KWD_IO 2 #define KWD_IRQ 3 #define KWD_MEMORY 4 #define KWD_CARD 5 #define KWD_DEVICE 6 #define KWD_CONFIG 7 #define KWD_RESET 8 #define KWD_ETHER 9 #define KWD_INSERT 10 #define KWD_REMOVE 11 #define KWD_IOSIZE 12 #define KWD_DEBUGLEVEL 13 #define KWD_INCLUDE 14 #define KWD_FUNCTION 15 /* for keyword compatibility with PAO/plain FreeBSD */ static struct { char *alias; u_int key; } key_aliases[] = { {"generic", KWD_FUNCTION}, {0, 0} }; struct flags { char *name; int mask; }; extern int doverbose; static void parsefile(void); static char *getline(void); static char *next_tok(void); static int num_tok(void); static void error(char *); static int keyword(char *); static int irq_tok(int); static int config_tok(unsigned char *); static int func_tok(void); static int debuglevel_tok(int); static struct allocblk *ioblk_tok(int); static struct allocblk *memblk_tok(int); static struct driver *new_driver(char *); static int iosize_tok(void); static void file_include(char *); static void addcmd(struct cmd **); static void parse_card(int); static void delete_card(struct card *cp) { struct ether *etherp, *ether_next; struct card_config *configp, *config_next; struct cmd *cmdp, *cmd_next; /* free characters */ if (cp->manuf != NULL) free(cp->manuf); if (cp->version != NULL) free(cp->version); if (cp->add_info1 != NULL) free(cp->add_info1); if (cp->add_info2 != NULL) free(cp->add_info2); /* free structures */ for (etherp = cp->ether; etherp; etherp = ether_next) { ether_next = etherp->next; free(etherp); } for (configp = cp->config; configp; configp = config_next) { config_next = configp->next; free(configp); } for (cmdp = cp->insert; cmdp; cmdp = cmd_next) { cmd_next = cmdp->next; free(cmdp->line); free(cmdp); } for (cmdp = cp->remove; cmdp; cmdp = cmd_next) { cmd_next = cmdp->next; free(cmdp->line); free(cmdp); } free(cp); } /* * Read a file and parse the pcmcia configuration data. * After parsing, verify the links. */ void readfile(char *name) { int i, inuse; struct card *cp, *card_next; struct card *genericp, *tail_gp; struct card_config *configp; /* delete all card configuration data before we proceed */ genericp = 0; cp = cards; cards = last_card = 0; while (cp) { card_next = cp->next; /* check whether this card is in use */ inuse = 0; for (configp = cp->config; configp; configp = configp->next) { if (configp->inuse) { inuse = 1; break; } } /* * don't delete entry in use for consistency. * leave normal entry in the cards list, * insert generic entry into the list after re-loading config files. */ if (inuse == 1) { cp->next = 0; /* unchain from the cards list */ switch (cp->deftype) { case DT_VERS: /* don't delete this entry for consistency */ if (debug_level >= 1) { logmsg("Card \"%s\"(\"%s\") is in use, " "can't change configuration\n", cp->manuf, cp->version); } /* add this to the card list */ if (!last_card) { cards = last_card = cp; } else { last_card->next = cp; last_card = cp; } break; case DT_FUNC: /* generic entry must be inserted to the list later */ if (debug_level >= 1) { logmsg("Generic entry is in use, " "can't change configuration\n"); } cp->next = genericp; genericp = cp; break; } } else { delete_card(cp); } cp = card_next; } for (i = 0; i < MAXINCLUDES; i++) { if (configfiles[i].filep) { fclose(configfiles[i].filep); configfiles[i].filep = NULL; if (i > 0) { free(configfiles[i].filename); } } } in = fopen(name, "r"); if (in == 0) { logerr(name); die("readfile"); } includes = 0; configfiles[includes].filep = in; filename = configfiles[includes].filename = name; parsefile(); for (cp = cards; cp; cp = cp->next) { if (cp->config == 0) logmsg("warning: card %s(%s) has no valid configuration\n", cp->manuf, cp->version); } /* insert generic entries in use into the top of generic entries */ if (genericp) { /* search tail of generic entries in use */ for (tail_gp = genericp; tail_gp->next; tail_gp = tail_gp->next) ; /* * if the top of cards list is generic entry, * insert generic entries in use before it. */ if (cards && cards->deftype == DT_FUNC) { tail_gp->next = cards; cards = genericp; goto generic_done; } /* search top of generic entries */ for (cp = cards; cp; cp = cp->next) { if (cp->next && cp->next->deftype == DT_FUNC) { break; } } /* * if we have generic entry in the cards list, * insert generic entries in use into there. */ if (cp) { tail_gp->next = cp->next; cp->next = genericp; goto generic_done; } /* * otherwise we don't have generic entries in * cards list, just add them to the list. */ if (!last_card) { cards = genericp; } else { last_card->next = genericp; last_card = tail_gp; } generic_done: } /* save the initial state of resource pool */ bcopy(io_avail, io_init, bitstr_size(IOPORTS)); bcopy(mem_avail, mem_init, bitstr_size(MEMBLKS)); bcopy(pool_irq, irq_init, sizeof(pool_irq)); } static void parsefile(void) { int i; int errors = 0; struct allocblk *bp, *next; char *incl; pushc = 0; lineno = 1; for (;;) switch (keyword(next_tok())) { case KWD_EOF: /* EOF */ return; case KWD_IO: /* override reserved I/O blocks */ bit_nclear(io_avail, 0, IOPORTS-1); for (bp = pool_ioblks; bp; bp = next) { next = bp->next; free(bp); } pool_ioblks = NULL; 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; case KWD_IRQ: /* override reserved irqs */ bzero(pool_irq, sizeof(pool_irq)); while ((i = irq_tok(0)) > 0) pool_irq[i] = 1; pusht = 1; break; case KWD_MEMORY: /* override reserved memory blocks. */ bit_nclear(mem_avail, 0, MEMBLKS-1); for (bp = pool_mem; bp; bp = next) { next = bp->next; free(bp); } pool_mem = NULL; 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; case KWD_CARD: /* Card definition. */ parse_card(DT_VERS); break; case KWD_FUNCTION: /* Function definition. */ parse_card(DT_FUNC); break; case KWD_DEBUGLEVEL: i = debuglevel_tok(0); if (i > 0) debug_level = i; break; case KWD_INCLUDE: incl = newstr(next_tok()); file_include(incl); break; default: error("syntax error"); pusht = 0; if (errors++ >= MAXERRORS) { error("too many errors, giving up"); return; } break; } } /* * Parse a card definition. */ static void parse_card(int deftype) { char *man, *vers, *tmp; char *add_info; unsigned char index_type; struct card *cp; int i, iosize; struct card_config *confp, *lastp; struct ether *ether; confp = 0; cp = xmalloc(sizeof(*cp)); cp->deftype = deftype; switch (deftype) { case DT_VERS: man = newstr(next_tok()); vers = newstr(next_tok()); add_info = newstr(next_tok()); if (keyword(add_info)) { pusht = 1; free(add_info); cp->add_info1 = NULL; cp->add_info2 = NULL; } else { cp->add_info1 = add_info; add_info = newstr(next_tok()); if (keyword(add_info)) { pusht = 1; free(add_info); cp->add_info2 = NULL; } else { cp->add_info2 = add_info; } } cp->manuf = man; cp->version = vers; cp->func_id = 0; break; case DT_FUNC: cp->manuf = NULL; cp->version = NULL; cp->func_id = (u_char) func_tok(); break; default: fprintf(stderr, "parse_card: unknown deftype %d\n", deftype); exit(1); } cp->reset_time = 50; cp->next = 0; if (!last_card) { cards = last_card = cp; } else { last_card->next = cp; last_card = cp; } for (;;) { switch (keyword(next_tok())) { case KWD_CONFIG: /* config */ i = config_tok(&index_type); if (i == -1) { error("illegal card config index"); break; } confp = xmalloc(sizeof(*confp)); man = next_tok(); confp->driver = new_driver(man); confp->irq = irq_tok(1); 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; confp->index_type = index_type; /* * 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 KWD_RESET: /* reset */ i = num_tok(); if (i == -1) { error("illegal card reset time"); break; } cp->reset_time = i; break; case KWD_ETHER: /* ether */ ether = xmalloc(sizeof(*ether)); ether->type = ETHTYPE_GENERIC; tmp = next_tok(); if (strcmp("attr2", tmp) == 0) ether->type = ETHTYPE_ATTR2; else { pusht = 1; ether->value = num_tok(); if (ether->value == -1) { error("illegal ether address offset"); free(ether); break; } } ether->next = cp->ether; cp->ether = ether; break; case KWD_INSERT: /* insert */ addcmd(&cp->insert); break; case KWD_REMOVE: /* remove */ addcmd(&cp->remove); break; case KWD_IOSIZE: /* iosize */ iosize = iosize_tok(); if (!iosize) { error("Illegal cardio arguments"); break; } if (!confp) { error("iosize should be placed after config"); break; } cp->iosize = iosize; break; default: pusht = 1; return; } } } /* * Generate a new driver structure. If one exists, use * that one after confirming the correct class. */ static 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 printf("Drv %s%d created\n", drvp->kernel, drvp->unit); #endif return (drvp); } /* * Parse one I/O block. */ static struct allocblk * ioblk_tok(int force) { struct allocblk *io; int i, j; /* ignore the keyword to allow separete blocks in multiple lines */ if (keyword(next_tok()) != KWD_IO) { pusht = 1; } 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. */ static struct allocblk * memblk_tok(int force) { struct allocblk *mem; int i, j; /* ignore the keyword to allow separete blocks in multiple lines */ if (keyword(next_tok()) != KWD_MEMORY) { pusht = 1; } 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 '?'. */ static int irq_tok(int force) { int i; /* ignore the keyword to allow separete blocks in multiple lines */ if (keyword(next_tok()) != KWD_IRQ) { pusht = 1; } 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); } /* * Config index token */ static int config_tok(unsigned char *index_type) { if (strcmp("default", next_tok()) == 0) { *index_type = DEFAULT_INDEX; return 0; } pusht = 1; if (strcmp("auto", next_tok()) == 0) { *index_type = AUTO_INDEX; return 0; } pusht = 1; *index_type = NORMAL_INDEX; return num_tok(); } /* * Function ID token */ static int func_tok(void) { if (strcmp("serial", next_tok()) == 0) return 2; pusht = 1; if (strcmp("fixed_disk", next_tok()) == 0) return 4; pusht = 1; return num_tok(); } /* * debuglevel token. Must be between 0 and 9. */ static int debuglevel_tok(int force) { int i; i = num_tok(); if (i >= 0 && i <= 9) return (i); return (-1); } /* * iosize token * iosize {|auto} */ static int iosize_tok(void) { int iosize = 0; if (strcmp("auto", next_tok()) == 0) iosize = -1; /* wildcard */ else { pusht = 1; iosize = num_tok(); if (iosize == -1) return 0; } #ifdef DEBUG if (doverbose) printf("iosize: size=%x\n", iosize); #endif return iosize; } /* * search the table for a match. */ static int keyword(char *str) { char **s; int i = 1; for (s = keys; *s; s++, i++) if (strcmp(*s, str) == 0) return (i); /* search keyword aliases too */ for (i = 0; key_aliases[i].key ; i++) if (strcmp(key_aliases[i].alias, str) == 0) return (key_aliases[i].key); return (0); } /* * addcmd - Append the command line to the list of * commands. */ static void addcmd(struct cmd **cp) { struct cmd *ncp; char *s = getline(); if (*s) { ncp = xmalloc(sizeof(*ncp)); ncp->line = s; while (*cp) cp = &(*cp)->next; *cp = ncp; } } static void error(char *msg) { pusht = 1; logmsg("%s: %s at line %d, near %s\n", filename, msg, lineno, next_tok()); pusht = 1; } static int last_char; static int get(void) { 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. */ static int num_tok(void) { char *s = next_tok(), c; int val = 0, base; base = 10; c = *s++; if (c == '0') { base = 8; c = *s++; if (c == '\0') return 0; else 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); } static char *_next_tok(void); static char * next_tok(void) { char *s = _next_tok(); #if 0 printf("Tok = %s\n", s); #endif return (s); } /* * get one token. Handles string quoting etc. */ static char * _next_tok(void) { 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; } case ' ': case '\t': /* Eat whitespace unless in a string. */ if (!instr) { if (p != buf) { *p++ = 0; return (buf); } } else *p++ = c; break; case '-': case '?': case '*': /* Special characters that are tokens on their own. */ if (instr) *p++ = c; else { if (p != buf) pushc = c; else *p++ = c; *p++ = 0; return (buf); } break; case EOF: if (includes) { fclose(in); /* go back to previous config file */ includes--; in = configfiles[includes].filep; filename = configfiles[includes].filename; lineno = configfiles[includes].lineno; return _next_tok(); /* recursive */ } 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. */ static char * getline(void) { 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)); } /* * Include configuration file */ static void file_include(char *incl) { int i, included; FILE *fp; /* check nesting overflow */ if (includes >= MAXINCLUDES) { if (debug_level >= 1) { logmsg("%s: include nesting overflow " "at line %d, near %s\n", filename, lineno, incl); } free(incl); goto out; } /* check recursive inclusion */ for (i = 0, included = 0; i <= includes; i++) { if (strcmp(incl, configfiles[i].filename) == 0) { included = 1; break; } } if (included == 1) { if (debug_level >= 1) { logmsg("%s: can't include the same file twice " "at line %d, near %s\n", filename, lineno, incl); } free(incl); goto out; } if (!(fp = fopen(incl, "r"))) { if (debug_level >= 1) { logmsg("%s: can't open include file " "at line %d, near %s\n", filename, lineno, incl); } free(incl); goto out; } /* save line number of the current config file */ configfiles[includes].lineno = lineno; lineno = 1; /* now we start parsing new config file */ includes++; in = configfiles[includes].filep = fp; filename = configfiles[includes].filename = incl; out: return; }