summaryrefslogtreecommitdiff
path: root/sys/dev/std
diff options
context:
space:
mode:
authorjmc <jmc@NetBSD.org>2002-02-27 04:58:50 +0000
committerjmc <jmc@NetBSD.org>2002-02-27 04:58:50 +0000
commitb1eeba63dc5d517742215f543ae461dab30824b1 (patch)
tree2aa335abd78d1bf182f96c1cce93a68c8d07b840 /sys/dev/std
parent00ea0581b5dd7766fb9c99980ed713e30d0bc6f2 (diff)
Initial checkin.
A framework for parsing p1212 compliant ROM's and returning a tree that describes it. Include routines for searching the tree, walking it to apply functions against nodes and printing out the tree. Also provide a utility routine to take all the unit dirs present in a ROM, loop against them and present them to config_found to configure all possible sub devices.
Diffstat (limited to 'sys/dev/std')
-rw-r--r--sys/dev/std/ieee1212.c1340
-rw-r--r--sys/dev/std/ieee1212var.h107
2 files changed, 1447 insertions, 0 deletions
diff --git a/sys/dev/std/ieee1212.c b/sys/dev/std/ieee1212.c
new file mode 100644
index 00000000000..38916d36ffa
--- /dev/null
+++ b/sys/dev/std/ieee1212.c
@@ -0,0 +1,1340 @@
+/* $NetBSD: ieee1212.c,v 1.1 2002/02/27 04:58:50 jmc Exp $ */
+
+/*
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by James Chacon.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <dev/std/ieee1212reg.h>
+#include <dev/std/ieee1212var.h>
+
+static const char * const p1212_keytype_strings[] = P1212_KEYTYPE_STRINGS ;
+static const char * const p1212_keyvalue_strings[] = P1212_KEYVALUE_STRINGS ;
+
+static u_int16_t p1212_calc_crc(u_int32_t, u_int32_t *, int, int);
+static int p1212_parse_directory(struct p1212_dir *, u_int32_t *, u_int32_t);
+static struct p1212_leafdata *p1212_parse_leaf(u_int32_t *);
+static int p1212_parse_textdir(struct p1212_com *, u_int32_t *);
+static struct p1212_textdata *p1212_parse_text_desc(u_int32_t *);
+static void p1212_print_node(struct p1212_key *, void *);
+static int p1212_validate_offset(u_int16_t, u_int32_t);
+static int p1212_validate_immed(u_int16_t, u_int32_t);
+static int p1212_validate_leaf(u_int16_t, u_int32_t);
+static int p1212_validate_dir(u_int16_t, u_int32_t);
+
+#ifdef P1212_DEBUG
+#define DPRINTF(x) if (p1212debug) printf x
+#define DPRINTFN(n,x) if (p1212debug>(n)) printf x
+int p1212debug = 1;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+/*
+ * Routines to parse the ROM into a tree that's usable. Also verify integrity
+ * vs. the P1212 standard
+ */
+
+/*
+ * A buffer of u_int32_t's and a size in quads gets passed in. The output will
+ * return -1 on error, or 0 on success and possibly reset *size to a larger
+ * value.
+ *
+ * NOTE: Rom's are guarentee'd per the ISO spec to be contiguous but only the
+ * first 1k is directly mapped. Anything past 1k is supposed to use a loop
+ * around the indirect registers to read in the rom. This code only assumes the
+ * buffer passed in represents a total rom regardless of end size. It is the
+ * callers responsibility to treat a size > 1024 as a special case.
+ */
+
+int
+p1212_iscomplete(u_int32_t *t, u_int32_t *size)
+{
+ u_int16_t infolen, crclen, len;
+ u_int32_t newlen, offset, test;
+ int complete, i, numdirs, type, val, *dirs;
+
+ dirs = NULL;
+
+ if (*size == 0) {
+ DPRINTF(("Invalid size for ROM: %d\n", (unsigned int)*size));
+ return -1;
+ }
+
+ infolen = P1212_ROMFMT_GET_INFOLEN((ntohl(t[0])));
+ if (infolen <= 1) {
+ DPRINTF(("ROM not initialized or minimal ROM: Info "
+ "length: %d\n", infolen));
+ return -1;
+ }
+ crclen = P1212_ROMFMT_GET_CRCLEN((ntohl(t[0])));
+ if (crclen < infolen) {
+ DPRINTF(("CRC len less than info len. CRC len: %d, "
+ "Info len: %d\n", crclen, infolen));
+ return -1;
+ }
+
+ /*
+ * Now loop through it to check if all the offsets referenced are
+ * within the image stored so far. If not, get those as well.
+ */
+
+ offset = P1212_ROMFMT_GET_INFOLEN((ntohl(t[0]))) + 1;
+
+ /*
+ * Make sure at least the bus info block is in memory + the root dir
+ * header quad. Add 1 here since offset is an array offset and size is
+ * the total array size we want. If this is getting the root dir
+ * then add another since infolen doesn't end on the root dir entry but
+ * right before it.
+ */
+
+ if ((*size == 1) || (*size < (offset + 1))) {
+ *size = (crclen > infolen) ? crclen : infolen;
+ if (crclen == infolen)
+ (*size)++;
+ (*size)++;
+ return 0;
+ }
+
+ complete = 0;
+ numdirs = 0;
+ newlen = 0;
+
+ while (!complete) {
+
+ /*
+ * Make sure the whole directory is in memory. If not, bail now
+ * and read it in.
+ */
+
+ newlen = P1212_DIRENT_GET_LEN((ntohl(t[offset])));
+ if ((offset + newlen + 1) > *size) {
+ newlen += offset + 1;
+ break;
+ }
+
+ if (newlen == 0) {
+ DPRINTF(("Impossible directory length of 0!\n"));
+ return -1;
+ }
+
+ /*
+ * Starting with the first byte of the directory, read through
+ * and check the values found. On offsets and directories read
+ * them in if appropriate (always for offsets, if not in memory
+ * for leaf/directories).
+ */
+
+ offset++;
+ len = newlen;
+ newlen = 0;
+ for (i = 0; i < len; i++) {
+ type = P1212_DIRENT_GET_KEYTYPE((ntohl(t[offset + i])));
+ val = P1212_DIRENT_GET_VALUE((ntohl(t[offset + i])));
+ switch (type) {
+ case P1212_KEYTYPE_Immediate:
+ case P1212_KEYTYPE_Offset:
+ break;
+ case P1212_KEYTYPE_Leaf:
+
+ /*
+ * If a leaf is found, and it's beyond the
+ * current rom length and it's beyond the
+ * current newlen setting,
+ * then set newlen accordingly.
+ */
+
+ test = offset + i + val + 1;
+ if ((test > *size) && (test > newlen)) {
+ newlen = test;
+ break;
+ }
+
+ /*
+ * For leaf nodes just make sure the whole leaf
+ * length is in the buffer. There's no data
+ * inside of them that can refer to outside
+ * nodes. (Uless it's vendor specific and then
+ * you're on your own anyways).
+ */
+
+ test--;
+ infolen = P1212_DIRENT_GET_LEN((ntohl(t[test])));
+ test++;
+ test += infolen;
+ if ((test > *size) && (test > newlen)) {
+ newlen = test;
+ }
+ break;
+
+ case P1212_KEYTYPE_Directory:
+
+ /* Make sure the first quad is in memory. */
+
+ test = offset + i + val + 1;
+ if ((test > *size) && (test > newlen)) {
+ newlen = test;
+ break;
+ }
+
+ /*
+ * Can't just walk the ROM looking at type codes
+ * since these are only valid on directory
+ * entries. So save any directories
+ * we find into a queue and the bottom of the
+ * while loop will pop the last one off and walk
+ * that directory.
+ */
+
+ test--;
+ dirs = realloc(dirs,
+ sizeof(int) * (numdirs + 1), M_DEVBUF,
+ M_WAITOK);
+ dirs[numdirs++] = test;
+ break;
+ default:
+ panic("Impossible type code: 0x%04hx",
+ (unsigned short)type);
+ break;
+ }
+ }
+
+ if (newlen) {
+ /* Cleanup. */
+ if (dirs)
+ free(dirs, M_DEVBUF);
+ break;
+ }
+ if (dirs) {
+ offset = dirs[--numdirs];
+ dirs = realloc(dirs, sizeof(int) * numdirs, M_DEVBUF,
+ M_WAITOK);
+ } else
+ complete = 1;
+ }
+
+ if (newlen)
+ *size = newlen;
+ return 0;
+
+}
+
+struct p1212_rom *
+p1212_parse(u_int32_t *t, u_int32_t size, u_int32_t mask)
+{
+
+ u_int16_t crc, romcrc, crc1;
+ u_int32_t next, check;
+ struct p1212_rom *rom;
+ int i;
+
+ check = size;
+
+ if (p1212_iscomplete(t, &check) == -1) {
+ DPRINTF(("ROM is not complete\n"));
+ return NULL;
+ }
+ if (check != size) {
+ DPRINTF(("ROM is not complete (check != size)\n"));
+ return NULL;
+ }
+
+ /* Calculate both a good and known bad crc. */
+
+ /* CRC's are calculated from everything except the first quad. */
+
+ crc = p1212_calc_crc(0, &t[1], P1212_ROMFMT_GET_CRCLEN((ntohl(t[0]))),
+ 0);
+
+ romcrc = P1212_ROMFMT_GET_CRC((ntohl(t[0])));
+ if (crc != romcrc) {
+ crc1 = p1212_calc_crc(0, &t[1],
+ P1212_ROMFMT_GET_CRCLEN((ntohl(t[0]))), 1);
+ if (crc1 != romcrc) {
+ DPRINTF(("Invalid ROM: CRC: 0x%04hx, Calculated "
+ "CRC: 0x%04hx, CRC1: 0x%04hx\n",
+ (unsigned short)romcrc, (unsigned short)crc,
+ (unsigned short)crc1));
+ return NULL;
+ }
+ }
+
+ /* Now, walk the ROM. */
+
+ /* Get the initial offset for the root dir. */
+
+ rom = malloc(sizeof(struct p1212_rom), M_DEVBUF, M_WAITOK);
+ rom->len = P1212_ROMFMT_GET_INFOLEN((ntohl(t[0])));
+ next = rom->len + 1;
+
+ if ((rom->len < 1) || (rom->len > size)) {
+ DPRINTF(("Invalid ROM info length: %d\n", rom->len));
+ free(rom, M_DEVBUF);
+ return NULL;
+ }
+
+ /* Exclude the quad which covers the bus name. */
+ rom->len--;
+
+ if (rom->len) {
+ rom->data = malloc(sizeof(u_int32_t) * rom->len, M_DEVBUF,
+ M_WAITOK);
+ /* Add 2 to account for info/crc and bus name we're skipping. */
+ for (i = 0; i < rom->len; i++)
+ rom->data[i] = t[i + 2];
+ }
+
+ /* The name field is always 4 bytes and always the 2nd field. */
+ strncpy(rom->name, (char *)&t[1], 4);
+ rom->name[4] = 0;
+
+ /*
+ * Fill out the root directory. All these values are hardcoded so the
+ * parse/print/match routines have a standard layout to work against.
+ */
+
+ rom->root = malloc(sizeof(struct p1212_dir), M_DEVBUF, M_WAITOK|M_ZERO);
+ rom->root->com.key.key_type = P1212_KEYTYPE_Directory;
+ rom->root->com.key.key_value = 0;
+ rom->root->com.key.key = (u_int8_t)P1212_KEYTYPE_Directory;
+ rom->root->com.key.val = 0;
+ TAILQ_INIT(&rom->root->data_root);
+ TAILQ_INIT(&rom->root->subdir_root);
+
+ if (p1212_parse_directory(rom->root, &t[next], mask)) {
+ DPRINTF(("Parse error in ROM. Bailing\n"));
+ p1212_free(rom);
+ return NULL;
+ }
+ return rom;
+}
+
+static int
+p1212_parse_directory(struct p1212_dir *root, u_int32_t *addr, u_int32_t mask)
+{
+ struct p1212_dir *dir, *sdir;
+ struct p1212_data *data;
+ struct p1212_com *com;
+ u_int32_t *t, desc;
+ u_int16_t crclen, crc, crc1, romcrc;
+ u_int8_t type, val;
+ unsigned long size;
+ int i, module_vendor_flag, module_sw_flag, node_sw_flag, unit_sw_flag;
+ int node_capabilities_flag, offset, unit_location_flag, unitdir_cnt;
+ int leafoff;
+
+ t = addr;
+ dir = root;
+
+ module_vendor_flag = 0;
+ module_sw_flag = 0;
+ node_sw_flag = 0;
+ node_capabilities_flag = 0;
+ unitdir_cnt = 0;
+ offset = 0;
+
+ while (dir) {
+ dir->match = 0;
+ crclen = P1212_DIRENT_GET_LEN((ntohl(t[offset])));
+ romcrc = P1212_DIRENT_GET_CRC((ntohl(t[offset])));
+
+ crc = p1212_calc_crc(0, &t[offset + 1], crclen, 0);
+ if (crc != romcrc) {
+ crc1 = p1212_calc_crc(0, &t[offset + 1], crclen, 1);
+ if (crc1 != romcrc) {
+ DPRINTF(("Invalid ROM: CRC: 0x%04hx, "
+ "Calculated CRC: "
+ "0x%04hx, CRC1: 0x%04hx\n",
+ (unsigned short)romcrc,
+ (unsigned short)crc,
+ (unsigned short)crc1));
+ return 1;
+ }
+ }
+ com = NULL;
+ unit_sw_flag = 0;
+ unit_location_flag = 0;
+ offset++;
+
+ if ((dir->parent == NULL) && dir->com.key.val) {
+ DPRINTF(("Invalid root dir. key.val is 0x%0x and not"
+ " 0x0\n", dir->com.key.val));
+ return 1;
+ }
+
+ for (i = offset; i < (offset + crclen); i++) {
+ desc = ntohl(t[i]);
+ type = P1212_DIRENT_GET_KEYTYPE(desc);
+ val = P1212_DIRENT_GET_KEYVALUE(desc);
+
+ /*
+ * Sanity check for valid types/locations/etc.
+ *
+ * See pages 79-100 of
+ * ISO/IEC 13213:1194 (ANSI/IEEE Std 1212, 1994 edition)
+ * for specifics.
+ *
+ * XXX: These all really should be broken out into
+ * subroutines as it's grown large and complicated
+ * in certain cases.
+ */
+
+ switch (val) {
+ case P1212_KEYVALUE_Unit_Spec_Id:
+ case P1212_KEYVALUE_Unit_Sw_Version:
+ case P1212_KEYVALUE_Unit_Dependent_Info:
+ case P1212_KEYVALUE_Unit_Location:
+ case P1212_KEYVALUE_Unit_Poll_Mask:
+ if (dir->parent == NULL) {
+ DPRINTF(("Invalid ROM: %s is not "
+ "valid in the root directory.\n",
+ p1212_keyvalue_strings[val]));
+ return 1;
+ }
+ break;
+ default:
+ if (dir->com.key.val ==
+ P1212_KEYVALUE_Unit_Directory) {
+ DPRINTF(("Invalid ROM: %s is "
+ "not valid in a unit directory.\n",
+ p1212_keyvalue_strings[val]));
+ return 1;
+ }
+ break;
+ }
+
+ switch (type) {
+ case P1212_KEYTYPE_Immediate:
+ if (p1212_validate_immed(val, mask)) {
+ DPRINTF(("Invalid ROM: Can't have an "
+ "immediate type with %s value. Key "
+ "used at location 0x%0x in ROM\n",
+ p1212_keyvalue_strings[val],
+ (unsigned int)(&t[i]-&addr[0])));
+ return 1;
+ }
+ break;
+ case P1212_KEYTYPE_Offset:
+ if (p1212_validate_offset(val, mask)) {
+ DPRINTF(("Invalid ROM: Can't have "
+ "an offset type with key %s."
+ " Used at location 0x%0x in ROM\n",
+ p1212_keyvalue_strings[val],
+ (unsigned int)(&t[i]-&addr[0])));
+ return 1;
+ }
+ break;
+ case P1212_KEYTYPE_Leaf:
+ if (p1212_validate_leaf(val, mask)) {
+ DPRINTF(("Invalid ROM: Can't have a "
+ "leaf type with %s value. Key "
+ "used at location 0x%0x in ROM\n",
+ p1212_keyvalue_strings[val],
+ (unsigned int)(&t[i]-&addr[0])));
+ return 1;
+ }
+ break;
+ case P1212_KEYTYPE_Directory:
+ if (p1212_validate_dir(val, mask)) {
+ DPRINTF(("Invalid ROM: Can't have a "
+ "directory type with %s value. Key "
+ "used at location 0x%0x in ROM\n",
+ p1212_keyvalue_strings[val],
+ (unsigned int)(&t[i]-&addr[0])));
+ return 1;
+ }
+ break;
+ default:
+ panic("Impossible type code: 0x%04hx",
+ (unsigned short)type);
+ break;
+ }
+
+ /* Note flags for required fields. */
+
+ if (val == P1212_KEYVALUE_Module_Vendor_Id) {
+ module_vendor_flag = 1;
+ }
+
+ if (val == P1212_KEYVALUE_Node_Capabilities) {
+ node_capabilities_flag = 1;
+ }
+
+ if (val == P1212_KEYVALUE_Unit_Sw_Version)
+ unit_sw_flag = 1;
+
+ if (val == P1212_KEYVALUE_Unit_Location)
+ unit_location_flag = 1;
+
+ /*
+ * This is just easier to spell out. You can't have
+ * a module sw version if you include a node sw version
+ * and vice-versa. Both aren't allowed if you have unit
+ * dirs.
+ */
+
+ if (val == P1212_KEYVALUE_Module_Sw_Version) {
+ if (node_sw_flag) {
+ DPRINTF(("Can't have a module software "
+ "version along with a node "
+ "software version entry\n"));
+ return 1;
+ }
+ if (unitdir_cnt) {
+ DPRINTF(("Can't have unit directories "
+ "with module software version "
+ "defined.\n"));
+ return 1;
+ }
+ module_sw_flag = 1;
+ }
+
+ if (val == P1212_KEYVALUE_Node_Sw_Version) {
+ if (module_sw_flag) {
+ DPRINTF(("Can't have a node software "
+ "version along with a module "
+ "software version entry\n"));
+ return 1;
+ }
+ if (unitdir_cnt) {
+ DPRINTF(("Can't have unit directories "
+ "with node software version "
+ "defined.\n"));
+ return 1;
+ }
+ node_sw_flag = 1;
+ }
+
+ if (val == P1212_KEYVALUE_Unit_Directory) {
+ if (module_sw_flag || node_sw_flag) {
+ DPRINTF(("Can't have unit directories "
+ "with either module or node "
+ "software version defined.\n"));
+ return 1;
+ }
+ unitdir_cnt++;
+ }
+
+ /*
+ * Text descriptors are special. They describe the
+ * last entry they follow. So they need to be included
+ * with it's struct and there's nothing in the spec
+ * preventing one from putting text descriptors after
+ * directory descriptors. Also they can be a single
+ * value or a list of them in a directory format so
+ * account for either. Finally if they're in a directory
+ * those can be the only types in a directory.
+ */
+
+ if (val == P1212_KEYVALUE_Textual_Descriptor) {
+
+ size = sizeof(struct p1212_textdata *);
+ leafoff = P1212_DIRENT_GET_VALUE(desc);
+ leafoff += i;
+
+ if (com == NULL) {
+ DPRINTF(("Can't have a text descriptor "
+ "as the first entry in a "
+ "directory\n"));
+ return 1;
+ }
+
+ if (com->textcnt != 0) {
+ DPRINTF(("Text descriptors can't "
+ "follow each other in a "
+ "directory\n"));
+ return 1;
+ }
+
+ if (type == P1212_KEYTYPE_Leaf) {
+ com->text =
+ malloc(size, M_DEVBUF, M_WAITOK);
+ com->text[0] =
+ p1212_parse_text_desc(&t[leafoff]);
+ if (com->text[0] == NULL) {
+ DPRINTF(("Got an error parsing "
+ "text descriptor at offset "
+ "0x%0x\n",
+ &t[leafoff]-&addr[0]));
+ free(com->text, M_DEVBUF);
+ return 1;
+ }
+ com->textcnt = 1;
+ } else {
+ i = p1212_parse_textdir(com,
+ &t[leafoff]);
+ if (i)
+ return 1;
+ }
+ }
+
+ if ((type != P1212_KEYTYPE_Directory) &&
+ (val != P1212_KEYVALUE_Textual_Descriptor)) {
+ data = malloc(sizeof(struct p1212_data),
+ M_DEVBUF, M_WAITOK|M_ZERO);
+ data->com.key.key_type = type;
+ data->com.key.key_value = val;
+ data->com.key.key =
+ P1212_DIRENT_GET_KEY((ntohl(t[i])));
+ data->com.key.val =
+ P1212_DIRENT_GET_VALUE((ntohl(t[i])));
+ com = &data->com;
+
+ /*
+ * Don't try and read the offset. It may be
+ * a register or something special. Generally
+ * these are node specific so let the upper
+ * level code figure it out.
+ */
+
+ if ((type == P1212_KEYTYPE_Immediate) ||
+ (type == P1212_KEYTYPE_Offset))
+ data->val = data->com.key.val;
+
+ data->leafdata = NULL;
+ TAILQ_INSERT_TAIL(&dir->data_root, data, data);
+
+ if (type == P1212_KEYTYPE_Leaf) {
+ leafoff = i + data->com.key.val;
+ data->leafdata =
+ p1212_parse_leaf(&t[leafoff]);
+ if (data->leafdata == NULL) {
+ DPRINTF(("Error parsing leaf\n"));
+ return 1;
+ }
+ }
+ }
+ if (type == P1212_KEYTYPE_Directory) {
+
+ sdir = malloc(sizeof(struct p1212_dir),
+ M_DEVBUF, M_WAITOK|M_ZERO);
+ sdir->parent = dir;
+ sdir->com.key.key_type = type;
+ sdir->com.key.key_value = val;
+ sdir->com.key.key =
+ P1212_DIRENT_GET_KEY((ntohl(t[i])));
+ sdir->com.key.val =
+ P1212_DIRENT_GET_VALUE((ntohl(t[i])));
+ com = &sdir->com;
+ sdir->match = sdir->com.key.val + i;
+ TAILQ_INIT(&sdir->data_root);
+ TAILQ_INIT(&sdir->subdir_root);
+ TAILQ_INSERT_TAIL(&dir->subdir_root, sdir, dir);
+ }
+ }
+
+ /* More validity checks. */
+
+ if (dir->parent == NULL) {
+ if (module_vendor_flag == 0) {
+ DPRINTF(("Missing module vendor entry in root "
+ "directory.\n"));
+ return 1;
+ }
+ if (node_capabilities_flag == 0) {
+ DPRINTF(("Missing node capabilities entry in "
+ "root directory.\n"));
+ return 1;
+ }
+ } else {
+ if ((unitdir_cnt > 1) && (unit_location_flag == 0)) {
+ DPRINTF(("Must have a unit location in each "
+ "unit directory when more than one unit "
+ "directory exists.\n"));
+ return 1;
+ }
+ }
+
+ /*
+ * Ok, done with this directory and it's sanity checked. Now
+ * loop through and either find an unparsed subdir or one
+ * farther back up the chain.
+ */
+
+ if (!TAILQ_EMPTY(&dir->subdir_root)) {
+ sdir = TAILQ_FIRST(&dir->subdir_root);
+ } else {
+ do {
+ sdir = TAILQ_NEXT(dir, dir);
+ if (sdir == NULL) {
+ dir = dir->parent;
+ }
+ } while ((sdir == NULL) && (dir != NULL));
+ }
+ if (dir) {
+ dir = sdir;
+ if (!dir->match) {
+ DPRINTF(("Invalid subdir..Has no offset\n"));
+ return 1;
+ }
+ offset = dir->match;
+ }
+ }
+ return 0;
+}
+
+static struct p1212_leafdata *
+p1212_parse_leaf(u_int32_t *t)
+{
+ u_int16_t crclen, crc, crc1, romcrc;
+ struct p1212_leafdata *leafdata;
+ int i;
+
+ crclen = P1212_DIRENT_GET_LEN((ntohl(t[0])));
+ romcrc = P1212_DIRENT_GET_CRC((ntohl(t[0])));
+ crc = p1212_calc_crc(0, &t[1], crclen, 0);
+ crc1 = p1212_calc_crc(0,&t[1], crclen, 1);
+ if ((crc != romcrc) && (crc1 != romcrc)) {
+ DPRINTF(("Invalid ROM: CRC: 0x%04hx, Calculated CRC: "
+ "0x%04hx, CRC1: 0x%04hx\n", (unsigned short)romcrc,
+ (unsigned short)crc, (unsigned short)crc1));
+ return NULL;
+ }
+ t++;
+
+ /*
+ * Most of these are vendor specific so don't bother trying to map them
+ * out. Anything which needs them later on can extract them.
+ */
+
+ leafdata = malloc(sizeof(struct p1212_leafdata), M_DEVBUF, M_WAITOK);
+ leafdata->data = malloc((sizeof(u_int32_t) * crclen), M_DEVBUF,
+ M_WAITOK);
+ leafdata->len = crclen;
+ for (i = 0; i < crclen; i++)
+ leafdata->data[i] = t[i];
+ return leafdata;
+}
+
+static int
+p1212_parse_textdir(struct p1212_com *com, u_int32_t *addr)
+{
+ u_int32_t *t, entry, new;
+ u_int16_t crclen, crc, crc1, romcrc;
+ u_int8_t type, val;
+
+ int i, size;
+
+ /*
+ * A bit more complicated. A directory for a text descriptor can
+ * contain text descriptor leaf nodes only.
+ */
+
+ com->text = NULL;
+ size = sizeof(struct p1212_text *);
+
+ crclen = P1212_DIRENT_GET_LEN((ntohl(t[0])));
+ romcrc = P1212_DIRENT_GET_CRC((ntohl(t[0])));
+ crc = p1212_calc_crc(0, &t[1], crclen, 0);
+ crc1 = p1212_calc_crc(0,&t[1], crclen, 1);
+ if ((crc != romcrc) && (crc1 != romcrc)) {
+ DPRINTF(("Invalid ROM: CRC: 0x%04hx, Calculated CRC: "
+ "0x%04hx, CRC1: 0x%04hx\n", (unsigned short)romcrc,
+ (unsigned short)crc, (unsigned short)crc1));
+ return 1;
+ }
+ t++;
+ for (i = 0; i < crclen; i++) {
+ entry = ntohl(t[i]);
+
+ type = P1212_DIRENT_GET_KEYTYPE(entry);
+ val = P1212_DIRENT_GET_KEYVALUE(entry);
+ if ((type != P1212_KEYTYPE_Leaf) ||
+ (val != P1212_KEYVALUE_Textual_Descriptor)) {
+ DPRINTF(("Text descriptor directories can only "
+ "contain text descriptors. Type: %s, value: %s "
+ "isn't valid at offset 0x%0x\n",
+ p1212_keytype_strings[type],
+ p1212_keyvalue_strings[val], &t[i]-&addr[0]));
+ return 1;
+ }
+
+ new = P1212_DIRENT_GET_VALUE(entry);
+ com->text = realloc(com->text, size * (com->textcnt + 1),
+ M_DEVBUF, M_WAITOK);
+ if ((com->text[i] = p1212_parse_text_desc(&t[i+new])) == NULL) {
+ DPRINTF(("Got an error parsing text descriptor.\n"));
+ if (com->textcnt == 0)
+ free(com->text, M_DEVBUF);
+ return 1;
+ }
+ com->textcnt++;
+ }
+ return 0;
+}
+
+static struct p1212_textdata *
+p1212_parse_text_desc(u_int32_t *addr)
+{
+ u_int32_t *t;
+ u_int16_t crclen, crc, crc1, romcrc;
+ struct p1212_textdata *text;
+ int size;
+
+ t = addr;
+
+ crclen = P1212_DIRENT_GET_LEN((ntohl(t[0])));
+ romcrc = P1212_DIRENT_GET_CRC((ntohl(t[0])));
+
+ if (crclen < P1212_TEXT_Min_Leaf_Length) {
+ DPRINTF(("Invalid ROM: text descriptor too short\n"));
+ return NULL;
+ }
+
+ crc = p1212_calc_crc(0, &t[1], crclen, 0);
+ if (crc != romcrc) {
+ crc1 = p1212_calc_crc(0, &t[1], crclen, 1);
+ if (crc1 != romcrc) {
+ DPRINTF(("Invalid ROM: CRC: 0x%04hx, Calculated CRC: "
+ "0x%04hx, CRC1: 0x%04hx\n", (unsigned short)romcrc,
+ (unsigned short)crc, (unsigned short)crc1));
+ return NULL;
+ }
+ }
+
+ t++;
+ text = malloc(sizeof(struct p1212_textdata), M_DEVBUF, M_WAITOK);
+ text->spec_type = P1212_TEXT_GET_Spec_Type((ntohl(t[0])));
+ text->spec_id = P1212_TEXT_GET_Spec_Id((ntohl(t[0])));
+ text->lang_id = ntohl(t[1]);
+
+ t++;
+ t++;
+ crclen -= 2;
+ size = (crclen * sizeof(u_int32_t)) + 1;
+
+ text->text = malloc(size, M_DEVBUF, M_WAITOK|M_ZERO);
+
+ memcpy(text->text, &t[0], size);
+
+ return text;
+}
+
+struct p1212_key **
+p1212_find(struct p1212_dir *root, int type, int value, int flags)
+{
+ struct p1212_key **retkeys;
+ struct p1212_dir *dir, *sdir, *parent;
+ struct p1212_data *data;
+ int numkeys;
+
+ numkeys = 0;
+ retkeys = NULL;
+
+ if ((type < P1212_KEYTYPE_Immediate) ||
+ (type > P1212_KEYTYPE_Directory)) {
+#ifdef DIAGNOSTIC
+ printf("p1212_find: invalid type - %d\n", type);
+#endif
+ return NULL;
+ }
+
+ if ((value < -1) ||
+ (value > (sizeof(p1212_keyvalue_strings) / sizeof(char *)))) {
+#ifdef DIAGNOSTIC
+ printf("p1212_find: invalid value - %d\n", value);
+#endif
+ return NULL;
+ }
+
+ if (flags & ~(P1212_FIND_SEARCHALL | P1212_FIND_RETURNALL)) {
+#ifdef DIAGNOSTIC
+ printf("p1212_find: invalid flags - %d\n", flags);
+#endif
+ return NULL;
+ }
+
+ /*
+ * Part of this is copied from p1212_walk to do depth first traversal
+ * without using recursion. Using the walk API would have made things
+ * more complicated in trying to build up the return struct otherwise.
+ */
+
+ dir = root;
+ sdir = NULL;
+
+ parent = root->parent;
+ root->parent = NULL;
+
+ while (dir) {
+ if (type == P1212_KEYTYPE_Directory) {
+ TAILQ_FOREACH(sdir, &dir->subdir_root, dir) {
+ if ((sdir->com.key.key_value == value) ||
+ (value == -1)) {
+ numkeys++;
+ retkeys = realloc(retkeys,
+ sizeof(struct p1212_key *) *
+ (numkeys + 1), M_WAITOK, M_DEVBUF);
+ retkeys[numkeys - 1] = &sdir->com.key;
+ retkeys[numkeys] = NULL;
+ if ((flags & P1212_FIND_RETURNALL)
+ == 0) {
+ root->parent = parent;
+ return retkeys;
+ }
+ }
+ }
+ } else {
+ TAILQ_FOREACH(data, &dir->data_root, data) {
+ if (((data->com.key.key_type == type) &&
+ (data->com.key.key_value == value)) ||
+ ((data->com.key.key_type == type) &&
+ (value == -1))) {
+ numkeys++;
+ retkeys = realloc(retkeys,
+ sizeof(struct p1212_key *) *
+ (numkeys + 1), M_WAITOK, M_DEVBUF);
+ retkeys[numkeys - 1] = &data->com.key;
+ retkeys[numkeys] = NULL;
+ if ((flags & P1212_FIND_RETURNALL)
+ == 0) {
+ root->parent = parent;
+ return retkeys;
+ }
+ }
+ }
+ }
+ if (flags & P1212_FIND_SEARCHALL) {
+ do {
+ sdir = TAILQ_NEXT(dir, dir);
+ if (sdir == NULL) {
+ dir = dir->parent;
+ }
+ } while ((sdir == NULL) && (dir != NULL));
+ dir = sdir;
+ } else
+ dir = NULL;
+ }
+ root->parent = parent;
+ return retkeys;
+}
+
+void
+p1212_walk(struct p1212_dir *root, void *arg,
+ void (*func)(struct p1212_key *, void *))
+{
+ struct p1212_data *data;
+ struct p1212_dir *sdir, *dir, *parent;
+
+ dir = root;
+ sdir = NULL;
+
+ if (func == NULL) {
+#ifdef DIAGNOSTIC
+ printf("p1212_walk: Passed in NULL function\n");
+#endif
+ return;
+ }
+ if (root == NULL) {
+#ifdef DIAGNOSTIC
+ printf("p1212_walk: Called with NULL root\n");
+#endif
+ return;
+ }
+
+ /* Allow walking from any point. Just mark the starting point. */
+ parent = root->parent;
+ root->parent = NULL;
+
+ /*
+ * Depth first traversal that doesn't use recursion.
+ *
+ * Call the function first for the directory node and then loop through
+ * all the data nodes and call the function for them.
+ *
+ * Finally, figure out the next possible directory node if one is
+ * available or bail out.
+ */
+
+ while (dir) {
+ func((struct p1212_key *) dir, arg);
+ TAILQ_FOREACH(data, &dir->data_root, data)
+ func((struct p1212_key *) data, arg);
+ if (!TAILQ_EMPTY(&dir->subdir_root)) {
+ sdir = TAILQ_FIRST(&dir->subdir_root);
+ } else {
+ do {
+ sdir = TAILQ_NEXT(dir, dir);
+ if (sdir == NULL) {
+ dir = dir->parent;
+ }
+ } while ((sdir == NULL) && dir);
+ }
+ dir = sdir;
+ }
+
+ root->parent = parent;
+}
+
+void
+p1212_print(struct p1212_dir *dir)
+{
+ int indent;
+
+ indent = 0;
+
+ p1212_walk(dir, &indent, p1212_print_node);
+ printf("\n");
+}
+
+static void
+p1212_print_node(struct p1212_key *key, void *arg)
+{
+
+ struct p1212_data *data;
+ struct p1212_dir *sdir, *dir;
+ int i, j, *indent;
+
+ indent = arg;
+
+ if (key->key_type == P1212_KEYTYPE_Directory) {
+ dir = (struct p1212_dir *) key;
+ data = NULL;
+ } else {
+ data = (struct p1212_data *) key;
+ dir = NULL;
+ }
+
+ /* Recompute the indent level on each directory. */
+ if (dir) {
+ *indent = 0;
+ sdir = dir->parent;
+ while (sdir != NULL) {
+ (*indent)++;
+ sdir = sdir->parent;
+ }
+ }
+
+ if (dir && dir->parent)
+ printf("\n");
+
+ /* Set the indent string up. 4 spaces per level. */
+ for (i = 0; i < (*indent * 4); i++)
+ printf(" ");
+
+ if (dir) {
+ printf("Directory: ");
+ if (dir->print)
+ dir->print(dir);
+ else {
+ if (key->key_value >=
+ (sizeof(p1212_keyvalue_strings) / sizeof(char *)))
+ printf("Unknown type 0x%04hx\n",
+ (unsigned short)key->key_value);
+ else
+ printf("%s\n",
+ p1212_keyvalue_strings[key->key_value]);
+ }
+ if (dir->com.textcnt) {
+ for (i = 0; i < dir->com.textcnt; i++) {
+ for (j = 0; j < (*indent * 4); j++)
+ printf(" ");
+ printf("Text descriptor: %s\n",
+ dir->com.text[i]->text);
+ }
+ }
+ printf("\n");
+ } else {
+ if (data->print)
+ data->print(data);
+ else {
+ if (key->key_value >=
+ (sizeof(p1212_keyvalue_strings) / sizeof(char *)))
+ printf("Unknown type 0x%04hx: ",
+ (unsigned short)key->key_value);
+ else
+ printf("%s: ",
+ p1212_keyvalue_strings[key->key_value]);
+
+ printf("0x%08x\n", key->val);
+#ifdef DIAGNOSTIC
+ if ((data->com.key.key_type == P1212_KEYTYPE_Leaf) &&
+ (data->leafdata == NULL))
+ panic("Invalid data node in configrom tree");
+#endif
+
+ if (data->leafdata) {
+ for (i = 0; i < data->leafdata->len; i++) {
+ for (j = 0; j < (*indent * 4); j++)
+ printf(" ");
+ printf ("Leaf data: 0x%08x\n",
+ data->leafdata->data[i]);
+ }
+ }
+ if (data->com.textcnt)
+ for (i = 0; i < data->com.textcnt; i++) {
+ for (j = 0; j < (*indent * 4); j++)
+ printf(" ");
+ printf("Text descriptor: %s\n",
+ data->com.text[i]->text);
+ }
+
+ }
+ }
+}
+
+
+void
+p1212_free(struct p1212_rom *rom)
+{
+ struct p1212_dir *sdir, *dir;
+ struct p1212_data *data;
+ int i;
+
+ dir = rom->root;
+
+ /* Avoid recursing. Find the bottom most node and work back. */
+ while (dir) {
+ if (!TAILQ_EMPTY(&dir->subdir_root)) {
+ sdir = TAILQ_FIRST(&dir->subdir_root);
+ if (TAILQ_EMPTY(&sdir->subdir_root)) {
+ TAILQ_REMOVE(&dir->subdir_root, sdir, dir);
+ dir = sdir;
+ }
+ else {
+ dir = sdir;
+ continue;
+ }
+ } else {
+ if (dir->parent)
+ TAILQ_REMOVE(&dir->parent->subdir_root, dir,
+ dir);
+ }
+
+ while ((data = TAILQ_FIRST(&dir->data_root))) {
+ if (data->leafdata) {
+ if (data->leafdata->data)
+ free(data->leafdata->data, M_DEVBUF);
+ free(data->leafdata, M_DEVBUF);
+ }
+ TAILQ_REMOVE(&dir->data_root, data, data);
+ if (data->com.textcnt) {
+ for (i = 0; i < data->com.textcnt; i++)
+ free(data->com.text[i], M_DEVBUF);
+ free(data->com.text, M_DEVBUF);
+ }
+ free(data, M_DEVBUF);
+ }
+ sdir = dir;
+ if (dir->parent)
+ dir = dir->parent;
+ else
+ dir = NULL;
+ if (sdir->com.textcnt) {
+ for (i = 0; i < sdir->com.textcnt; i++)
+ free(sdir->com.text[i], M_DEVBUF);
+ free(sdir->com.text, M_DEVBUF);
+ }
+ free(sdir, M_DEVBUF);
+ }
+ if (rom->len)
+ free(rom->data, M_DEVBUF);
+ free(rom, M_DEVBUF);
+}
+
+/*
+ * A fairly well published reference implementation of the CRC routine had
+ * a typo in it and some devices may be using it rather than the correct one
+ * in calculating their ROM CRC's. To compensate an interface for generating
+ * either is provided.
+ *
+ * len is the number of u_int32_t entries, not bytes.
+ */
+
+static u_int16_t
+p1212_calc_crc(u_int32_t crc, u_int32_t *data, int len, int broke)
+{
+ int shift;
+ u_int32_t sum;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ for (shift = 28; shift > 0; shift -= 4) {
+ sum = ((crc >> 12) ^ (ntohl(data[i]) >> shift)) &
+ 0x0000000f;
+ crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ sum;
+ }
+
+
+ /* The broken implementation doesn't do the last shift. */
+ if (!broke) {
+ sum = ((crc >> 12) ^ ntohl(data[i])) & 0x0000000f;
+ crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ sum;
+ }
+ }
+ return (u_int16_t)crc;
+}
+
+/*
+ * This is almost identical to the standard autoconf *match idea except it
+ * can match and attach multiple children in one pass.
+ */
+
+struct device **
+p1212_match_units(struct device *sc, struct p1212_dir *dir,
+ int (*print)(void *, const char *))
+{
+ struct p1212_dir **udirs;
+ struct device **devret, *dev;
+ int numdev;
+
+ /*
+ * Setup typical return val. Always allocate one extra pointer for a
+ * NULL guard end pointer.
+ */
+
+ numdev = 0;
+ devret = malloc(sizeof(struct device *) * 2, M_DEVBUF, M_WAITOK);
+ devret[1] = NULL;
+
+ udirs = (struct p1212_dir **)p1212_find(dir, P1212_KEYTYPE_Directory,
+ P1212_KEYVALUE_Unit_Directory,
+ P1212_FIND_SEARCHALL|P1212_FIND_RETURNALL);
+
+ if (udirs) {
+ while (*udirs++) {
+ dev = config_found_sm(sc, udirs, print, NULL);
+ if (dev && numdev) {
+ devret = realloc(devret,
+ sizeof(struct device *) *
+ (numdev + 2), M_DEVBUF, M_WAITOK);
+ devret[numdev++] = dev;
+ devret[numdev] = NULL;
+ } else if (dev) {
+ devret[0] = dev;
+ numdev++;
+ }
+ }
+ }
+ if (numdev == 0) {
+ free(devret, M_DEVBUF);
+ return NULL;
+ }
+ return devret;
+}
+
+/*
+ * Make these their own functions as they have slightly complicated rules.
+ *
+ * For example:
+ *
+ * Under normal circumstances only the 2 extent types can be offset
+ * types. However some spec's which use p1212 like SBP2 for
+ * firewire/1394 will define a dependent info type as an offset value.
+ * Allow the upper level code to flag this and pass it down during
+ * parsing. The same thing applies to immediate types.
+ */
+
+static int
+p1212_validate_offset(u_int16_t val, u_int32_t mask)
+{
+ if ((val == P1212_KEYVALUE_Node_Units_Extent) ||
+ (val == P1212_KEYVALUE_Node_Memory_Extent) ||
+ ((mask & P1212_ALLOW_DEPENDENT_INFO_OFFSET_TYPE) &&
+ ((val == P1212_KEYVALUE_Unit_Dependent_Info) ||
+ (val == P1212_KEYVALUE_Node_Dependent_Info) ||
+ (val == P1212_KEYVALUE_Module_Dependent_Info))))
+ return 0;
+ return 1;
+}
+
+static int
+p1212_validate_immed(u_int16_t val, u_int32_t mask)
+{
+ switch (val) {
+ case P1212_KEYVALUE_Textual_Descriptor:
+ case P1212_KEYVALUE_Bus_Dependent_Info:
+ case P1212_KEYVALUE_Module_Dependent_Info:
+ case P1212_KEYVALUE_Node_Unique_Id:
+ case P1212_KEYVALUE_Node_Dependent_Info:
+ case P1212_KEYVALUE_Unit_Directory:
+ case P1212_KEYVALUE_Unit_Dependent_Info:
+ case P1212_KEYVALUE_Unit_Location:
+ if ((mask & P1212_ALLOW_DEPENDENT_INFO_IMMED_TYPE) &&
+ ((val == P1212_KEYVALUE_Module_Dependent_Info) ||
+ (val == P1212_KEYVALUE_Node_Dependent_Info) ||
+ (val == P1212_KEYVALUE_Unit_Dependent_Info)))
+ break;
+ return 1;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int
+p1212_validate_leaf(u_int16_t val, u_int32_t mask)
+{
+ switch(val) {
+ case P1212_KEYVALUE_Textual_Descriptor:
+ case P1212_KEYVALUE_Bus_Dependent_Info:
+ case P1212_KEYVALUE_Module_Dependent_Info:
+ case P1212_KEYVALUE_Node_Unique_Id:
+ case P1212_KEYVALUE_Node_Dependent_Info:
+ case P1212_KEYVALUE_Unit_Dependent_Info:
+ case P1212_KEYVALUE_Unit_Location:
+ break;
+ default:
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+static int
+p1212_validate_dir(u_int16_t val, u_int32_t mask)
+{
+ switch(val) {
+ case P1212_KEYVALUE_Textual_Descriptor:
+ case P1212_KEYVALUE_Bus_Dependent_Info:
+ case P1212_KEYVALUE_Module_Dependent_Info:
+ case P1212_KEYVALUE_Node_Dependent_Info:
+ case P1212_KEYVALUE_Unit_Directory:
+ case P1212_KEYVALUE_Unit_Dependent_Info:
+ break;
+ default:
+ return 1;
+ break;
+ }
+ return 0;
+}
diff --git a/sys/dev/std/ieee1212var.h b/sys/dev/std/ieee1212var.h
new file mode 100644
index 00000000000..54f3c7ea16d
--- /dev/null
+++ b/sys/dev/std/ieee1212var.h
@@ -0,0 +1,107 @@
+/* $NetBSD: ieee1212var.h,v 1.1 2002/02/27 04:58:51 jmc Exp $ */
+
+/*
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by James Chacon.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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 _DEV_STD_IEEE1212VAR_H
+#define _DEV_STD_IEEE1212VAR_H
+
+struct p1212_dir;
+
+struct p1212_key {
+ u_int8_t key_type;
+ u_int8_t key_value;
+ u_int8_t key;
+ u_int32_t val;
+};
+
+struct p1212_leafdata {
+ u_int32_t len;
+ u_int32_t *data;
+};
+
+struct p1212_textdata {
+ u_int8_t spec_type;
+ u_int32_t spec_id;
+ u_int32_t lang_id;
+ char *text;
+};
+
+struct p1212_com {
+ struct p1212_key key;
+ u_int32_t textcnt;
+ struct p1212_textdata **text;
+};
+
+struct p1212_data {
+ struct p1212_com com;
+
+ u_int32_t val;
+ struct p1212_leafdata *leafdata;
+ void (*print)(struct p1212_data *);
+ TAILQ_ENTRY(p1212_data) data;
+};
+
+
+struct p1212_dir {
+ struct p1212_com com;
+
+ int match;
+ void (*print)(struct p1212_dir *);
+ struct p1212_dir *parent;
+ TAILQ_HEAD(, p1212_data) data_root;
+ TAILQ_HEAD(, p1212_dir) subdir_root;
+ TAILQ_ENTRY(p1212_dir) dir;
+};
+
+struct p1212_rom {
+ char name[5];
+ u_int32_t len;
+ u_int32_t *data;
+ struct p1212_dir *root;
+};
+
+int p1212_iscomplete(u_int32_t *, u_int32_t *);
+struct p1212_rom *p1212_parse (u_int32_t *, u_int32_t, u_int32_t);
+void p1212_walk(struct p1212_dir *, void *,
+ void (*)(struct p1212_key *, void *));
+struct p1212_key **p1212_find(struct p1212_dir *, int, int, int);
+void p1212_print(struct p1212_dir *);
+void p1212_free(struct p1212_rom *);
+struct device **p1212_match_units(struct device *, struct p1212_dir *,
+ int (*)(void *, const char *));
+
+#endif /* _DEV_STD_IEEE1212VAR_H */