1350 lines
35 KiB
C
1350 lines
35 KiB
C
/* $NetBSD: ieee1212.c,v 1.7 2003/10/26 20:53:09 fvdl 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/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: ieee1212.c,v 1.7 2003/10/26 20:53:09 fvdl Exp $");
|
|
|
|
#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 guaranteed 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 skipped. */
|
|
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(*rom->root), 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] = ntohl(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 *);
|
|
t = addr;
|
|
|
|
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));
|
|
|
|
text->text = malloc(size + 1, 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_DEVBUF, M_WAITOK);
|
|
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_DEVBUF, M_WAITOK);
|
|
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) {
|
|
do {
|
|
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++;
|
|
}
|
|
udirs++;
|
|
} while (*udirs);
|
|
}
|
|
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:
|
|
if ((mask & P1212_ALLOW_VENDOR_DIRECTORY_TYPE) &&
|
|
(val == P1212_KEYVALUE_Module_Vendor_Id))
|
|
break;
|
|
return 1;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|