#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ieee1212.c,v 1.15 2021/08/07 16:19:16 thorpej 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
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;
}
offset = P1212_ROMFMT_GET_INFOLEN((ntohl(t[0]))) + 1;
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) {
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;
}
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:
test = offset + i + val + 1;
if ((test > *size) && (test > newlen)) {
newlen = test;
break;
}
test--;
infolen =
P1212_DIRENT_GET_LEN((ntohl(t[test])));
test++;
test += infolen;
if ((test > *size) && (test > newlen)) {
newlen = test;
}
break;
case P1212_KEYTYPE_Directory:
test = offset + i + val + 1;
if ((test > *size) && (test > newlen)) {
newlen = test;
break;
}
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) {
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;
}
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;
}
}
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;
}
rom->len--;
if (rom->len) {
rom->data = malloc(sizeof(u_int32_t) * rom->len, M_DEVBUF,
M_WAITOK);
for (i = 0; i < rom->len; i++)
rom->data[i] = t[i + 2];
}
strncpy(rom->name, (char *)&t[1], 4);
rom->name[4] = 0;
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);
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;
}
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;
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++;
}
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;
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);
}
}
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;
}
}
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++;
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;
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;
}
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;
}
parent = root->parent;
root->parent = NULL;
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;
}
if (dir) {
*indent = 0;
sdir = dir->parent;
while (sdir != NULL) {
(*indent)++;
sdir = sdir->parent;
}
}
if (dir && dir->parent)
printf("\n");
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;
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);
}
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;
}
if (!broke) {
sum = ((crc >> 12) ^ ntohl(data[i])) & 0x0000000f;
crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ sum;
}
}
return (u_int16_t)crc;
}
device_t *
p1212_match_units(device_t sc, struct p1212_dir *dir,
int (*print)(void *, const char *))
{
struct p1212_dir **udirs;
device_t *devret, *dev;
int numdev;
numdev = 0;
devret = malloc(sizeof(device_t) * 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(sc, udirs, print,
CFARGS(.iattr = "fwnode"));
if (dev && numdev) {
devret = realloc(devret,
sizeof(device_t) *
(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;
}
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;
}