master_ops.c revision aa2aa9a662539940ddbc8610da5a3a874ebd7503
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/kobj.h>
#include <sys/kobj_lex.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/sunndi.h>
#include <sys/acpi/acpi.h>
#include <sys/acpica.h>
#define masterfile "/boot/solaris/devicedb/master"
/*
* Internal definitions
*/
typedef enum {
MF_UNEXPECTED = -1,
MF_IDENT,
MF_STRING,
MF_EOF,
MF_NEWLINE,
MF_EQUALS,
MF_BIT_OR
} mftoken_t;
typedef enum {
MF_INIT,
MF_DEVID,
MF_NAME,
MF_DEVTYPE,
MF_BUSTYPE,
MF_BEFNAME,
MF_DESCRIPTION,
MF_PROPNAME,
MF_PROPASSIGN,
MF_PROPVAL,
MF_VERSION_DONE,
MF_VALID_DONE,
MF_ERROR_DONE
} mfparse_t;
static master_rec_t *master_list = NULL;
device_id_t *
mf_alloc_device_id()
{
return ((device_id_t *)kmem_zalloc(sizeof (device_id_t), KM_SLEEP));
}
void
mf_free_device_id(device_id_t *d)
{
if (d->id != NULL)
strfree(d->id);
kmem_free(d, sizeof (device_id_t));
}
static property_t *
mf_alloc_property()
{
return ((property_t *)kmem_zalloc(sizeof (property_t), KM_SLEEP));
}
static void
mf_free_property(property_t *p)
{
if (p->name != NULL)
strfree(p->name);
if (p->value != NULL)
strfree(p->value);
kmem_free(p, sizeof (property_t));
}
static master_rec_t *
mf_alloc_master_rec()
{
return ((master_rec_t *)kmem_zalloc(sizeof (master_rec_t), KM_SLEEP));
}
static void
mf_free_master_rec(master_rec_t *m)
{
device_id_t *d;
property_t *p;
if (m->name != NULL)
strfree(m->name);
if (m->description != NULL)
strfree(m->description);
d = m->device_ids;
while (d != NULL) {
device_id_t *next;
next = d->next;
mf_free_device_id(d);
d = next;
}
p = m->properties;
while (p != NULL) {
property_t *next;
next = p->next;
mf_free_property(p);
p = next;
}
kmem_free(m, sizeof (master_rec_t));
}
void
free_master_data()
{
master_rec_t *m;
m = master_list;
while (m != NULL) {
master_rec_t *next;
next = m->next;
mf_free_master_rec(m);
m = next;
}
master_list = NULL;
}
/*
* Unfortunately, kobj_lex() is too sophisticated for our needs
*/
static mftoken_t
mf_lex(struct _buf *file, char *val, size_t size)
{
char *cp;
int ch, badquote;
size_t remain;
mftoken_t token = MF_UNEXPECTED;
if (size < 2)
return (token); /* MF_UNEXPECTED */
cp = val;
/* skip leading whitespace */
while ((ch = kobj_getc(file)) == ' ' || ch == '\t')
;
/* strip comments */
if (ch == '#') {
while ((ch = kobj_getc(file)) != '\n' && ch != '\r' &&
ch != -1)
;
}
remain = size - 1;
*cp++ = (char)ch;
switch (ch) {
case -1:
token = MF_EOF;
break;
case '\n':
case '\r':
token = MF_NEWLINE;
break;
case '=':
token = MF_EQUALS;
break;
case '|':
token = MF_BIT_OR;
break;
case '"':
remain++;
cp--;
badquote = 0;
while (!badquote && (ch = kobj_getc(file)) != '"') {
switch (ch) {
case '\n':
case -1:
remain = size - 1;
cp = val;
*cp++ = '\n';
badquote = 1;
/* since we consumed the newline/EOF */
(void) kobj_ungetc(file);
break;
default:
if (--remain == 0) {
token = MF_UNEXPECTED;
goto out;
}
*cp++ = (char)ch;
break;
}
}
token = MF_STRING;
break;
default:
do {
if (--remain == 0) {
token = MF_UNEXPECTED;
break;
}
token = MF_IDENT;
*cp++ = (char)(ch = kobj_getc(file));
/* if terminating character, break out */
if ((ch == -1) || (ch == ' ') || (ch == '\t') ||
(ch == '\n') || (ch == '\r') || (ch == '=') ||
(ch == '|')) {
(void) kobj_ungetc(file);
remain++;
cp--;
break;
}
if ((ch == '#') || (ch == '"'))
token = MF_UNEXPECTED;
} while (token != MF_UNEXPECTED);
break;
}
out:
*cp = '\0';
return (token);
}
static master_rec_t *
get_line(struct _buf *file)
{
master_rec_t *m = NULL;
device_id_t *d = NULL;
property_t *p = NULL;
mftoken_t token;
char tokval[MAXPATHLEN];
mfparse_t parse_state;
parse_state = MF_INIT;
token = mf_lex(file, tokval, sizeof (tokval));
while (token != MF_EOF) {
switch (parse_state) {
case MF_INIT:
m = mf_alloc_master_rec();
parse_state = MF_DEVID;
/*FALLTHROUGH*/
case MF_DEVID:
if (token == MF_IDENT) {
d = mf_alloc_device_id();
d->id = strdup(tokval);
d->next = m->device_ids;
m->device_ids = d;
parse_state = MF_NAME;
} else if (token != MF_NEWLINE)
parse_state = MF_ERROR_DONE;
break;
case MF_NAME:
if (token == MF_IDENT) {
m->name = strdup(tokval);
parse_state = MF_DEVTYPE;
} else if (token == MF_BIT_OR) {
parse_state = MF_DEVID;
} else
parse_state = MF_ERROR_DONE;
break;
case MF_DEVTYPE:
if (token == MF_IDENT) {
/* device_type not used */
parse_state = MF_BUSTYPE;
} else if (token == MF_NEWLINE) {
/* version line ignored */
parse_state = MF_VERSION_DONE;
} else
parse_state = MF_ERROR_DONE;
break;
case MF_BUSTYPE:
if (token == MF_IDENT) {
/* bus_type ignored */
parse_state = MF_BEFNAME;
} else
parse_state = MF_ERROR_DONE;
break;
case MF_BEFNAME:
if (token == MF_IDENT) {
/* realmode driver name ignored */
parse_state = MF_DESCRIPTION;
} else
parse_state = MF_ERROR_DONE;
break;
case MF_DESCRIPTION:
if (token == MF_STRING) {
m->description = strdup(tokval);
parse_state = MF_PROPNAME;
} else
parse_state = MF_ERROR_DONE;
break;
case MF_PROPNAME:
if (token == MF_IDENT) {
p = mf_alloc_property();
p->name = strdup(tokval);
parse_state = MF_PROPASSIGN;
} else if (token == MF_NEWLINE) {
parse_state = MF_VALID_DONE;
} else
parse_state = MF_ERROR_DONE;
break;
case MF_PROPASSIGN:
if (token == MF_EQUALS) {
parse_state = MF_PROPVAL;
} else
parse_state = MF_ERROR_DONE;
break;
case MF_PROPVAL:
if (token == MF_STRING || token == MF_IDENT) {
p->value = strdup(tokval);
p->next = m->properties;
/* delete properties which begin with '$' */
if (*p->name == '$') {
mf_free_property(p);
} else
m->properties = p;
p = NULL;
parse_state = MF_PROPNAME;
} else
parse_state = MF_ERROR_DONE;
break;
case MF_VERSION_DONE:
case MF_VALID_DONE:
case MF_ERROR_DONE:
/* terminating states handled outside switch() */
break;
}
if (parse_state == MF_VERSION_DONE) {
/* ignore version line */
mf_free_master_rec(m);
parse_state = MF_INIT;
} else if (parse_state == MF_VALID_DONE) {
/* valid line */
break;
} else if (parse_state == MF_ERROR_DONE) {
mf_free_master_rec(m);
if (p != NULL)
mf_free_property(p);
/*
* Error in master file. Should never happen
* since master file is not user-edited. Eat rest
* of line to attempt error recovery
*/
cmn_err(CE_NOTE, "!error in %s", masterfile);
while (token != MF_NEWLINE && token != MF_EOF)
token = mf_lex(file, tokval, sizeof (tokval));
parse_state = MF_INIT;
continue;
}
token = mf_lex(file, tokval, sizeof (tokval));
}
return (m);
}
void
process_master_file()
{
struct _buf *file;
master_rec_t *m;
if ((file = kobj_open_file(masterfile)) == NULL) {
cmn_err(CE_WARN, "!cannot open master file: %s", masterfile);
return;
}
while ((m = get_line(file)) != NULL) {
m->next = master_list;
master_list = m;
}
kobj_close_file(file);
}
/*
* Return the first master file record found matching pnpid list
*/
const master_rec_t *
master_file_lookup(device_id_t *pnpid)
{
master_rec_t *m;
device_id_t *d;
while (pnpid != NULL) {
m = master_list;
while (m != NULL) {
d = m->device_ids;
while (d != NULL) {
if (strcmp(pnpid->id, d->id) == 0)
return (m);
d = d->next;
}
m = m->next;
}
pnpid = pnpid->next;
}
return (NULL);
}