NetBSD/sys/dev/ieee1394/fwnode.c

1164 lines
32 KiB
C

/* $NetBSD: fwnode.c,v 1.8 2002/01/12 16:58:16 tsutsui 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: fwnode.c,v 1.8 2002/01/12 16:58:16 tsutsui Exp $");
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <machine/bus.h>
#include <dev/std/ieee1212reg.h>
#include <dev/std/sbp2reg.h>
#include <dev/ieee1394/ieee1394reg.h>
#include <dev/ieee1394/fwnodereg.h>
#include <dev/ieee1394/ieee1394var.h>
#include <dev/ieee1394/fwnodevar.h>
static const char * const ieee1394_speeds[] = { IEEE1394_SPD_STRINGS };
static const char * const p1212_keytype_strings[] = P1212_KEYTYPE_STRINGS ;
static const char * const p1212_keyvalue_strings[] = P1212_KEYVALUE_STRINGS ;
int fwnode_match(struct device *, struct cfdata *, void *);
void fwnode_attach(struct device *, struct device *, void *);
int fwnode_detach(struct device *, int);
static void fwnode_configrom_input(struct ieee1394_abuf *, int);
static u_int16_t calc_crc(u_int32_t, u_int32_t *, int, int);
static int fwnode_parse_configrom (struct fwnode_softc *);
static int fwnode_parse_directory(struct fwnode_softc *, u_int32_t *,
struct configrom_dir *);
static void fwnode_print_configrom_tree(struct fwnode_softc *,
struct configrom_dir *, int);
static int fwnode_parse_leaf(u_int32_t *, struct configrom_data *);
static int fwnode_match_dev_type(struct fwnode_softc *,
struct configrom_data *);
static int sbp2_print_data(struct configrom_data *);
static int sbp2_print_dir(u_int8_t);
static void sbp2_init(struct fwnode_softc *, struct fwnode_device_cap *);
static int fwnode_print(void *, const char *);
static void fwnode_match_subdevs(struct fwnode_softc *);
static void sbp2_login(struct ieee1394_abuf *, int);
static void sbp2_login_resp(struct ieee1394_abuf *, int);
#ifdef FW_DEBUG
#define DPRINTF(x) if (fwnodedebug) printf x
#define DPRINTFN(n,x) if (fwnodedebug>(n)) printf x
int fwnodedebug = 1;
#else
#define DPRINTF(x)
#define DPRINTFN(n,x)
#endif
struct cfattach fwnode_ca = {
sizeof(struct fwnode_softc), fwnode_match, fwnode_attach,
fwnode_detach
};
int
fwnode_match(struct device *parent, struct cfdata *match, void *aux)
{
struct ieee1394_attach_args *fwa = aux;
if (strcmp(fwa->name, "fwnode") == 0)
return 1;
return 0;
}
void
fwnode_attach(struct device *parent, struct device *self, void *aux)
{
struct fwnode_softc *sc = (struct fwnode_softc *)self;
struct ieee1394_softc *psc = (struct ieee1394_softc *)parent;
struct ieee1394_attach_args *fwa = aux;
struct ieee1394_abuf *ab;
ab = malloc(sizeof(struct ieee1394_abuf), M_1394DATA, M_WAITOK|M_ZERO);
ab->ab_data = malloc(4, M_1394DATA, M_WAITOK);
ab->ab_data[0] = 0;
sc->sc_sc1394.sc1394_node_id = fwa->nodeid;
memcpy(sc->sc_sc1394.sc1394_guid, fwa->uid, 8);
sc->sc1394_read = fwa->read;
sc->sc1394_write = fwa->write;
sc->sc1394_inreg = fwa->inreg;
TAILQ_INIT(&sc->sc_configrom_root);
TAILQ_INIT(&sc->sc_dev_cap_head);
/* XXX. Fix the fw code to use the generic routines. */
sc->sc_sc1394.sc1394_ifinreg = psc->sc1394_ifinreg;
sc->sc_sc1394.sc1394_ifoutput = psc->sc1394_ifoutput;
printf(" Node %d: UID %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
sc->sc_sc1394.sc1394_node_id,
sc->sc_sc1394.sc1394_guid[0], sc->sc_sc1394.sc1394_guid[1],
sc->sc_sc1394.sc1394_guid[2], sc->sc_sc1394.sc1394_guid[3],
sc->sc_sc1394.sc1394_guid[4], sc->sc_sc1394.sc1394_guid[5],
sc->sc_sc1394.sc1394_guid[6], sc->sc_sc1394.sc1394_guid[7]);
ab->ab_req = (struct ieee1394_softc *)sc;
ab->ab_csr = CSR_BASE + CSR_CONFIG_ROM;
ab->ab_length = 4;
ab->ab_retlen = 0;
ab->ab_cbarg = (void *)1;
ab->ab_cb = fwnode_configrom_input;
sc->sc1394_read(ab);
}
int
fwnode_detach(struct device *self, int flags)
{
struct fwnode_softc *sc = (struct fwnode_softc *)self;
struct configrom_dir *dir, *sdir;
struct configrom_data *data;
struct fwnode_device_cap *devcap;
if (sc->sc_sc1394.sc1394_configrom &&
sc->sc_sc1394.sc1394_configrom_len)
free(sc->sc_sc1394.sc1394_configrom, M_1394DATA);
/* Avoid recursing. Find the bottom most node and work back. */
dir = TAILQ_FIRST(&sc->sc_configrom_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);
else
TAILQ_REMOVE(&sc->sc_configrom_root, dir,
dir);
}
while ((data = TAILQ_FIRST(&dir->data_root))) {
if (data->leafdata) {
if (data->leafdata->text)
free(data->leafdata->text, M_1394DATA);
free(data->leafdata, M_1394DATA);
}
TAILQ_REMOVE(&dir->data_root, data, data);
free(data, M_1394DATA);
}
sdir = dir;
if (dir->parent)
dir = dir->parent;
else
dir = NULL;
free(sdir, M_1394DATA);
}
while ((devcap = TAILQ_FIRST(&sc->sc_dev_cap_head))) {
TAILQ_REMOVE(&sc->sc_dev_cap_head, devcap, dev_list);
if (devcap->dev_data)
free(devcap->dev_data, M_1394DATA);
free(devcap, M_1394DATA);
}
return 0;
}
/*
* This code is complex since it's getting called via callbacks from the
* handlers in the 1394 chip specific code. In addition it's trying
* to build a complete image of the ROM in memory plus a list of any immediate
* values referenced in the ROM. This is done all here to keep the bus_read
* logic/callback for the ROM in one place since reading the whole ROM may
* require lots of small reads up front and building separate callback handlers
* for each step would be even worse.
*/
static void
fwnode_configrom_input(struct ieee1394_abuf *ab, int rcode)
{
struct fwnode_softc *sc = (struct fwnode_softc *)ab->ab_req;
int i, len, infolen, crclen, newlen, offset, complete, val, *dirs;
int numdirs, *tdirs;
u_int32_t *t;
u_int8_t type;
dirs = NULL;
if (rcode != IEEE1394_RCODE_COMPLETE) {
DPRINTF(("Aborting configrom input, rcode: %d\n", rcode));
goto fail;
}
/*
* 2 phases:
*
* Read the first quad and get size.
* Then read the rest of the rom.
*/
if (ab->ab_length != ab->ab_retlen) {
DPRINTF(("%s: config rom short read. Expected :%d, received: "
"%d. Not attaching\n", sc->sc_sc1394.sc1394_dev.dv_xname,
ab->ab_length, ab->ab_retlen));
goto fail;
}
if (ab->ab_retlen % 4) {
DPRINTF(("%s: configrom read of invalid length: %d\n",
sc->sc_sc1394.sc1394_dev.dv_xname, ab->ab_retlen));
goto fail;
}
switch ((int)ab->ab_cbarg) {
case 1:
infolen = P1212_ROMFMT_GET_INFOLEN((ntohl(ab->ab_data[0])));
if (infolen <= 1) {
DPRINTF(("%s: ROM not initialized or minimal ROM: Info "
"length: %d\n", sc->sc_sc1394.sc1394_dev.dv_xname,
infolen));
goto fail;
}
crclen = P1212_ROMFMT_GET_CRCLEN((ntohl(ab->ab_data[0])));
if (crclen < infolen) {
DPRINTF(("%s: CRC len less than info len. CRC len: %d, "
"Info len: %d\n", sc->sc_sc1394.sc1394_dev.dv_xname,
crclen, infolen));
goto fail;
}
/*
* Make sure and at least read up to the root directory first
* quad. It's guarentee'd to be there per the 1212 spec.
*/
newlen = crclen;
if (crclen == infolen)
newlen++;
/* Make sure to include the quad we're going to reread. */
newlen++;
newlen *= 4;
free(ab->ab_data, M_1394DATA);
ab->ab_data = malloc(newlen, M_1394DATA, M_WAITOK);
/*
* Don't fill in configrom_len until a valid read is returned.
* Also, assign configrom to the data buffer to avoid a copy
* later.
*/
sc->sc_sc1394.sc1394_configrom = ab->ab_data;
sc->sc_sc1394.sc1394_configrom_len = 0;
memset(ab->ab_data, 0, crclen);
ab->ab_csr = CSR_BASE + CSR_CONFIG_ROM;
ab->ab_length = newlen;
ab->ab_retlen = 0;
ab->ab_cbarg = (void *)2;
ab->ab_cb = fwnode_configrom_input;
sc->sc1394_read(ab);
return;
/*
* This case can happen multiple times depending on what the ROM
* listed for items that need to be read.
*/
case 2:
sc->sc_sc1394.sc1394_configrom_len = (ab->ab_retlen / 4);
break;
default:
panic("Got a callback value that's impossible: %d",
(int)ab->ab_cbarg);
break;
}
ab->ab_data = NULL;
/*
* Now loop through it to check if all the offsets referenced are
* within the image stored so far. If not, get those as well.
*/
t = sc->sc_sc1394.sc1394_configrom;
offset = P1212_ROMFMT_GET_INFOLEN((ntohl(t[0]))) + 1;
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) > sc->sc_sc1394.sc1394_configrom_len) {
newlen += offset + 1;
newlen *= 4;
break;
}
/*
* 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.
*/
if ((offset + i + val + 1) >
sc->sc_sc1394.sc1394_configrom_len) {
if ((offset + i + val + 1) > newlen) {
newlen = offset + i + val + 1;
}
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.
*/
infolen =
P1212_DIRENT_GET_LEN((ntohl(t[offset + i +
val])));
if ((offset + i + val + infolen + 1) >
sc->sc_sc1394.sc1394_configrom_len) {
if ((offset + i + val + infolen + 1) >
newlen) {
newlen = offset + i + val +
infolen + 1;
}
}
break;
case P1212_KEYTYPE_Directory:
/* Make sure the first quad is in memory. */
if ((offset + i + val + 1) >
sc->sc_sc1394.sc1394_configrom_len) {
if ((offset + i + val + 1) > newlen) {
newlen = offset + i + val + 1;
}
}
/*
* 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.
*/
tdirs = malloc(sizeof(int) * (numdirs + 1),
M_1394DATA, M_WAITOK);
if (dirs) {
memcpy(tdirs, dirs,
numdirs * sizeof(int));
free(dirs, M_1394DATA);
}
dirs = tdirs;
dirs[numdirs++] = offset + i + val;
break;
default:
panic("Impossible type code: 0x%04hx",
(unsigned short)type);
break;
}
}
if (newlen) {
newlen *= 4;
if (dirs)
free(dirs, M_1394DATA);
break;
}
if (dirs) {
offset = dirs[--numdirs];
if (!numdirs) {
free(dirs, M_1394DATA);
dirs = NULL;
}
} else
complete = 1;
}
/* If not complete then newlen is set to a new length to read. */
if (!complete) {
free(t, M_1394DATA);
ab->ab_data = malloc(newlen, M_1394DATA, M_WAITOK|M_ZERO);
sc->sc_sc1394.sc1394_configrom = ab->ab_data;
sc->sc_sc1394.sc1394_configrom_len = 0;
ab->ab_csr = CSR_BASE + CSR_CONFIG_ROM;
ab->ab_length = newlen;
ab->ab_retlen = 0;
ab->ab_cbarg = (void *)2;
ab->ab_cb = fwnode_configrom_input;
sc->sc1394_read(ab);
return;
}
free(ab, M_1394DATA);
fwnode_parse_configrom(sc);
return;
fail:
/* This is only valid if len is > 0 */
if ((sc->sc_sc1394.sc1394_configrom) &&
sc->sc_sc1394.sc1394_configrom_len)
free(sc->sc_sc1394.sc1394_configrom, M_1394DATA);
sc->sc_sc1394.sc1394_configrom = NULL;
sc->sc_sc1394.sc1394_configrom_len = 0;
if (dirs)
free(dirs, M_1394DATA);
if (ab->ab_data)
free(ab->ab_data, M_1394DATA);
if (ab)
free(ab, M_1394DATA);
return;
}
/*
* 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 in quadlets.
*/
static u_int16_t
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;
}
/*
* Routines to parse the ROM into a tree that's usable. Also verify integrity
* vs. the P1212 standard
*/
static int
fwnode_parse_configrom(struct fwnode_softc *sc)
{
struct configrom_dir *root;
#ifdef FW_DEBUG
int i;
#endif
int next;
u_int16_t crc, crc1, romcrc;
u_int32_t *t;
t = sc->sc_sc1394.sc1394_configrom;
#ifdef FW_DEBUG
printf("%s: Config rom dump:\n", sc->sc_sc1394.sc1394_dev.dv_xname);
for (i = 0; i < sc->sc_sc1394.sc1394_configrom_len; i++) {
if ((i % 4) == 0) {
if (i)
printf("\n");
printf("%s: 0x%02hx: ",
sc->sc_sc1394.sc1394_dev.dv_xname, (short)(4 * i));
}
printf("0x%08x ", ntohl(t[i]));
}
printf("\n");
#endif
if (ntohl(t[1]) != IEEE1394_SIGNATURE) {
DPRINTF(("%s: Invalid signature found in rom. Expected: "
"IEEE1394_SIGNATURE, received: 0x%08x\n",
sc->sc_sc1394.sc1394_dev.dv_xname, ntohl(t[1])));
return 1;
}
/* Calculate both a good and known bad crc. */
/* CRC's are calculated from everything except the first quad. */
crc = calc_crc(0, &t[1], P1212_ROMFMT_GET_CRCLEN((ntohl(t[0]))), 0);
crc1 = calc_crc(0, &t[1], P1212_ROMFMT_GET_CRCLEN((ntohl(t[0]))), 1);
/* XXX: Need macro's to extract these. */
sc->sc_sc1394.sc1394_max_receive = (0x00000001 <<
(((ntohl(t[2]) & 0x0000f000) >> 12) + 1));
sc->sc_sc1394.sc1394_link_speed =
ntohl(t[2]) & 0x00000007;
printf("%s: Link Speed: %s, max_rec: %d bytes\n",
sc->sc_sc1394.sc1394_dev.dv_xname,
ieee1394_speeds[sc->sc_sc1394.sc1394_link_speed],
sc->sc_sc1394.sc1394_max_receive);
romcrc = P1212_ROMFMT_GET_CRC((ntohl(t[0])));
if (crc != romcrc)
if (crc1 != romcrc) {
DPRINTF(("%s: Invalid ROM: CRC: 0x%04hx, Calculated "
"CRC: 0x%04hx, CRC1: 0x%04hx\n",
sc->sc_sc1394.sc1394_dev.dv_xname,
(unsigned short)romcrc, (unsigned short)crc,
(unsigned short)crc1));
return 1;
}
/* Now, walk the ROM. */
/* Get the initlal offset for the root dir. */
next = P1212_ROMFMT_GET_INFOLEN((ntohl(t[0]))) + 1;
root = malloc(sizeof(struct configrom_dir), M_1394DATA, M_WAITOK);
TAILQ_INSERT_TAIL(&sc->sc_configrom_root, root, dir);
root->dir_type = 0;
root->offset = next;
root->refs = 1;
root->parent = NULL;
if (!fwnode_parse_directory(sc, &t[next], root)) {
#ifdef FW_DEBUG
fwnode_print_configrom_tree(sc, root, 0);
#endif
fwnode_match_subdevs(sc);
} else
return 1;
return 0;
}
static int
fwnode_parse_directory(struct fwnode_softc *sc, u_int32_t *addr,
struct configrom_dir *head)
{
struct configrom_dir *dir;
struct configrom_data *data;
u_int16_t len, crclen, crc, crc1, romcrc;
u_int8_t type, val;
int i;
/* XXX: This whole piece needs reworking. Complex modern p1212
rom's can have multiple references to the same directory. Need
to add real ref counting, detection, post processing (to prune
duplicated refs), and a complete tree validation suite. The only
checking done at this level is the CRC checks, but there are hard
requirements for what can be in a directory at which level which
also needs to be verified. Plus all the recursive routines should be
unrolled since they're mostly tail recursive anyways.
*/
TAILQ_INIT(&head->data_root);
TAILQ_INIT(&head->subdir_root);
len = P1212_DIRENT_GET_LEN((ntohl(addr[0])));
romcrc = P1212_DIRENT_GET_CRC((ntohl(addr[0])));
crc = calc_crc(0, &addr[1], len, 0);
crc1 = calc_crc(0, &addr[1], len, 1);
if (crc != romcrc)
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;
}
addr++;
for (i = 0; i < len; i++) {
type = P1212_DIRENT_GET_KEYTYPE((ntohl(addr[i])));
val = P1212_DIRENT_GET_KEYVALUE((ntohl(addr[i])));
switch (type) {
case P1212_KEYTYPE_Immediate:
case P1212_KEYTYPE_Offset:
case P1212_KEYTYPE_Leaf:
data = malloc (sizeof(struct configrom_data),
M_1394DATA, M_WAITOK);
data->key_type = type;
data->key_value = val;
data->key = P1212_DIRENT_GET_KEY((ntohl(addr[i])));
data->val = P1212_DIRENT_GET_VALUE((ntohl(addr[i])));
data->leafdata = NULL;
TAILQ_INSERT_TAIL(&head->data_root, data, data);
if (type != P1212_KEYTYPE_Leaf)
if (fwnode_match_dev_type(sc, data))
return 1;
if (type == P1212_KEYTYPE_Leaf) {
crclen = P1212_DIRENT_GET_LEN((ntohl(addr[i +
data->val])));
romcrc = P1212_DIRENT_GET_CRC((ntohl(addr[i +
data->val])));
crc = calc_crc(0, &addr[i + data->val + 1],
crclen, 0);
crc1 = calc_crc(0, &addr[i + data->val + 1],
crclen, 1);
if (crc != romcrc)
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;
}
if (fwnode_parse_leaf(addr + i + data->val,
data))
return 1;
}
break;
case P1212_KEYTYPE_Directory:
dir = malloc(sizeof(struct configrom_dir), M_1394DATA,
M_WAITOK);
dir->parent = head;
dir->dir_type = val;
dir->offset = &addr[i] - sc->sc_sc1394.sc1394_configrom;
TAILQ_INSERT_TAIL(&head->subdir_root, dir, dir);
val = P1212_DIRENT_GET_VALUE((ntohl(addr[i])));
if (fwnode_parse_directory(sc, &addr[i + val], dir))
return 1;
break;
default:
panic("Impossible type code: 0x%04hx",
(unsigned short)type);
break;
}
}
return 0;
}
int fwnode_parse_leaf(u_int32_t *addr, struct configrom_data *data)
{
struct configrom_leafdata *l;
int len, version;
l = malloc(sizeof(struct configrom_leafdata), M_1394DATA,
M_WAITOK|M_ZERO);
data->leafdata = l;
l->desc_type = (ntohl(addr[1]) >> 24) & 0xff;
l->spec_id = ntohl(addr[1]) & 0xffffff;
len = P1212_DIRENT_GET_LEN((ntohl(addr[0])));
switch (l->desc_type) {
case 0:
if (l->spec_id) {
DPRINTF(("Invalid text spec id 0x%08x in descriptor "
"starting at: 0x%08x\n", l->spec_id,
ntohl(addr[0])));
return 1;
}
l->char_width = (ntohl(addr[2]) >> 28) & 0xf;
if (l->char_width == 8) {
DPRINTF(("Invalid character width 0x%04hx in text "
"descriptor starting at: 0x%08x\n",
(unsigned short)l->char_width, ntohl(addr[0])));
return 1;
}
l->char_set = (ntohl(addr[2]) >> 16) & 0xfff;
l->char_lang = ntohl(addr[2]) & 0xffff;
if ((!l->char_set && l->char_lang) ||
(l->char_set && (l->char_lang & 0x8000))) {
DPRINTF(("Invalid character set 0x%04hx or character "
"language 0x%04hx in text descriptor starting at: "
"0x%08x\n", (unsigned short)l->char_set,
(unsigned short)l->char_lang, ntohl(addr[0])));
return 1;
}
/* First 2 quads contain the data filled in above. Trim them. */
len -= 2;
/* No guarentee this is zero terminated so account for that. */
l->datalen = (len * 4) + 1;
l->text = malloc(l->datalen, M_1394DATA, M_WAITOK);
/* Offset 3 of a text descriptor begins the text. */
memcpy (l->text, &addr[3], l->datalen);
l->text[l->datalen - 1] = 0x0;
break;
case 1:
if (l->spec_id) {
DPRINTF(("Invalid icon spec id 0x%08x in descriptor "
"starting at: 0x%08x\n", l->spec_id,
ntohl(addr[0])));
return 1;
}
version = ntohl(addr[2]) & 0xffffff;
if (version) {
DPRINTF(("Invalid icon version 0x%08x in descriptor "
"starting at: 0x%08x\n", version, ntohl(addr[0])));
return 1;
}
/* First 2 quads contain the data filled in above. Trim them. */
len -= 2;
l->datalen = len * 4;
l->text = malloc(l->datalen, M_1394DATA, M_WAITOK);
/* Offset 3 of a icon descriptor begins the data. */
memcpy(l->text, &addr[3], l->datalen);
break;
default:
/* Trim the first quad off since it contains type and spec id. */
len--;
l->datalen = len * 4;
l->text = malloc(l->datalen, M_1394DATA, M_WAITOK);
/* Offset 2 of a descriptor begins the data. */
memcpy(l->text, &addr[2], l->datalen);
break;
}
return 0;
}
static int
fwnode_match_dev_type(struct fwnode_softc *sc, struct configrom_data *data)
{
/* XXX: This whole structure needs to be gut'd and redone.
Only after reading in the whole tree should sections of the tree
be passed along to config_found. */
struct fwnode_device_cap *devcap;
int done, found_sbp2;
done = found_sbp2 = 0;
switch (data->key_value) {
case P1212_KEYVALUE_Unit_Spec_Id:
if (data->val == SBP2_UNIT_SPEC_ID)
found_sbp2 = 1;
break;
case P1212_KEYVALUE_Unit_Sw_Version:
if (data->val == SBP2_UNIT_SW_VERSION)
found_sbp2 = 1;
break;
case P1212_KEYVALUE_Unit_Dependent_Info:
if (data->key_type == P1212_KEYTYPE_Offset)
found_sbp2 = 1;
default:
break;
}
if (found_sbp2) {
TAILQ_FOREACH(devcap, &sc->sc_dev_cap_head, dev_list) {
if ((devcap->dev_type == DEVTYPE_SBP2) && found_sbp2) {
if (!devcap->dev_valid) {
done = 1;
devcap->dev_valid = 1;
} else {
DPRINTF(("found_sbp2: 0x%08x\n",
data->val));
devcap->dev_data =
malloc(4, M_1394DATA, M_WAITOK);
((u_int32_t *)devcap->dev_data)[0] =
data->val;
done = 1;
}
}
}
/* If one wasn't found get a struct started. */
if (!done) {
devcap = malloc(sizeof(*devcap), M_1394DATA, M_WAITOK);
devcap->dev_valid = 0;
devcap->dev_spec = 0;
devcap->dev_info = 0;
devcap->dev_data = NULL;
if (found_sbp2) {
devcap->dev_type = DEVTYPE_SBP2;
devcap->dev_print_data = sbp2_print_data;
devcap->dev_print_dir = sbp2_print_dir;
devcap->dev_init = sbp2_init;
}
TAILQ_INSERT_TAIL(&sc->sc_dev_cap_head, devcap,
dev_list);
}
}
return 0;
}
static void
fwnode_print_configrom_tree(struct fwnode_softc *sc, struct configrom_dir *dir,
int indent)
{
int i, found;
struct configrom_data *data;
struct configrom_dir *subdir;
struct configrom_leafdata *leaf;
struct fwnode_device_cap *devcap;
char *space;
/* Set the indent string up. 4 spaces per level. */
space = malloc((indent * 4) + 1, M_1394DATA, M_WAITOK);
for (i = 0; i < (indent * 4); i++)
space[i] = 0x20;
space[indent * 4] = 0;
if (dir->dir_type) {
found = 0;
TAILQ_FOREACH(devcap, &sc->sc_dev_cap_head, dev_list) {
if (devcap->dev_print_dir(dir->dir_type)) {
found = 1;
break;
}
}
if (!found) {
printf("%sUnit ", space);
}
} else
printf("Root ");
printf("directory:\n\n");
TAILQ_FOREACH(data, &dir->data_root, data) {
found = 0;
printf("%s", space);
/*
* Try any capable devices first.
* If none match just give up and print out the value.
*
*/
TAILQ_FOREACH(devcap, &sc->sc_dev_cap_head, dev_list) {
if (devcap->dev_print_data(data)) {
found = 1;
break;
}
}
if (found)
continue;
if (data->key_value >= (sizeof(p1212_keyvalue_strings) /
sizeof(char *)))
printf("Unknown type 0x%04hx: ",
(unsigned short)data->key_value);
else
printf("%s: ", p1212_keyvalue_strings[data->key_value]);
if (data->key_value != P1212_KEYVALUE_Textual_Descriptor)
printf("0x%08x\n", data->val);
else {
if ((data->key_type != P1212_KEYTYPE_Leaf) ||
(data->leafdata == NULL))
panic("Invalid data node in configrom tree\n");
leaf = data->leafdata;
switch (leaf->desc_type) {
case 0:
if (leaf->char_width || leaf->char_set ||
leaf->char_lang)
printf("Not ASCII printable: width: "
"0x%04hx, char set: "
"0x%04hx, lang: 0x%04hx\n",
leaf->char_width, leaf->char_set,
leaf->char_lang);
else
printf("%s\n", leaf->text);
break;
case 1:
printf("Icon data (not printing)\n");
break;
default:
printf("Unknown descriptor type: 0x%04hx\n",
data->leafdata->desc_type);
break;
}
}
}
TAILQ_FOREACH(subdir, &dir->subdir_root, dir) {
printf("\n");
fwnode_print_configrom_tree(sc, subdir, (indent + 1));
}
free(space, M_1394DATA);
}
/* XXX: All the sbp2 routines are a complete hack simply to probe a device for
the moment to make sure all the other code is good. This should all be gutted
out to it's own file and properly abstracted.
*/
static int
sbp2_print_data(struct configrom_data *data)
{
switch (data->key_value) {
case SBP2_KEYVALUE_Command_Set:
printf("SBP2 Command Set: ");
if (data->val == 0x104d8)
printf("SCSI 2\n");
else
printf("0x%08x\n", data->val);
break;
case SBP2_KEYVALUE_Unit_Characteristics:
printf("SBP2 Unit Characteristics: 0x%08x\n", data->val);
break;
case SBP2_KEYVALUE_Command_Set_Revision:
printf("SBP2 Command Set Revision: 0x%08x\n", data->val);
break;
case SBP2_KEYVALUE_Command_Set_Spec_Id:
printf("SBP2 Command Set Spec Id: 0x%08x\n", data->val);
break;
case SBP2_KEYVALUE_Firmware_Revision:
printf("SBP2 Firmware Revision: 0x%08x\n", data->val);
break;
case SBP2_KEYVALUE_Reconnect_Timeout:
printf("SBP2 Reconnect Timeout: 0x%08x\n", data->val);
break;
case SBP2_KEYVALUE_Unit_Unique_Id:
printf("SBP2 Unit Unique Id: 0x%08x\n", data->val);
break;
case P1212_KEYVALUE_Unit_Dependent_Info:
if (data->key_type == P1212_KEYTYPE_Immediate)
printf("SBP2 Logical Unit Number: 0x%08x\n", data->val);
else if (data->key_type == P1212_KEYTYPE_Offset)
printf("SBP2 Management Agent: 0x%08x\n", data->val);
break;
default:
return 0;
}
return 1;
}
static int
sbp2_print_dir(u_int8_t dir_type)
{
switch(dir_type) {
case SBP2_KEYVALUE_Logical_Unit_Directory:
printf("Logical Unit ");
break;
default:
return 0;
}
return 1;
}
static void
sbp2_init(struct fwnode_softc *sc, struct fwnode_device_cap *devcap)
{
struct ieee1394_abuf *ab, *ab2;
u_int32_t loc = ((int32_t *)devcap->dev_data)[0];
ab = malloc(sizeof(struct ieee1394_abuf), M_1394DATA, M_WAITOK|M_ZERO);
ab->ab_data = malloc(8, M_1394DATA, M_WAITOK|M_ZERO);
ab2 = malloc(sizeof(struct ieee1394_abuf), M_1394DATA, M_WAITOK|M_ZERO);
loc *= 4;
ab->ab_length = 8;
ab->ab_req = (struct ieee1394_softc *)sc;
ab->ab_retlen = 0;
ab->ab_cb = NULL;
ab->ab_cbarg = NULL;
ab->ab_csr = CSR_BASE + loc;
ab->ab_data[0] = htonl(0x4000);
ab->ab_data[1] = 0;
ab->ab_tcode = IEEE1394_TCODE_WRITE_REQ_BLOCK;
ab2->ab_length = 32;
ab2->ab_tcode = IEEE1394_TCODE_READ_REQ_BLOCK;
ab2->ab_retlen = 0;
ab2->ab_data = NULL;
ab2->ab_csr = 0x0000400000000000;
ab2->ab_cb = sbp2_login;
ab2->ab_cbarg = devcap;
ab2->ab_req = (struct ieee1394_softc *)sc;
sc->sc1394_inreg(ab2, FALSE);
sc->sc1394_write(ab);
return;
}
static void
sbp2_login(struct ieee1394_abuf *ab, int rcode)
{
struct fwnode_softc *sc = (struct fwnode_softc *)ab->ab_req;
/* struct fwnode_device_cap *devcap = ab->ab_cbarg;*/
struct ieee1394_abuf *statab, *respab;
/* Got a read so allocate the buffer and write out the response. */
if (rcode) {
#ifdef FW_DEBUG
printf ("sbp2_login: Bad return code: %d\n", rcode);
#endif
if (ab->ab_data)
free (ab->ab_data, M_1394DATA);
free(ab, M_1394DATA);
}
ab->ab_data = malloc(32, M_1394DATA, M_WAITOK);
respab = malloc(sizeof(struct ieee1394_abuf), M_1394DATA,
M_WAITOK|M_ZERO);
statab = malloc(sizeof(struct ieee1394_abuf), M_1394DATA,
M_WAITOK|M_ZERO);
statab->ab_length = 32;
statab->ab_tcode = IEEE1394_TCODE_WRITE_REQ_BLOCK;
statab->ab_retlen = 0;
statab->ab_data = NULL;
statab->ab_csr = 0x0000400000000030;
statab->ab_cb = sbp2_login_resp;
statab->ab_cbarg = ab->ab_cbarg;
statab->ab_req = ab->ab_req;
sc->sc1394_inreg(statab, TRUE);
respab->ab_length = 16;
respab->ab_tcode = IEEE1394_TCODE_WRITE_REQ_BLOCK;
respab->ab_retlen = 0;
respab->ab_data = NULL;
respab->ab_csr = 0x0000400000000020;
respab->ab_cb = sbp2_login_resp;
respab->ab_cbarg = ab->ab_cbarg;
respab->ab_req = ab->ab_req;
sc->sc1394_inreg(respab, TRUE);
memset(ab->ab_data, 0, 32);
ab->ab_data[2] = htonl(0x4000);
ab->ab_data[3] = htonl(0x20);
ab->ab_data[4] = htonl(0x90000000);
ab->ab_data[5] = htonl(0x00000010);
ab->ab_data[6] = htonl(0x4000);
ab->ab_data[7] = htonl(0x30);
ab->ab_retlen = 0;
ab->ab_cb = NULL;
ab->ab_cbarg = NULL;
ab->ab_tcode = IEEE1394_TCODE_READ_RESP_BLOCK;
ab->ab_length = 32;
sc->sc1394_write(ab);
}
static void
sbp2_login_resp(struct ieee1394_abuf *ab, int rcode)
{
/* struct fwnode_device_cap *devcap = (struct fwnode_device_cap *)ab->ab_cbarg;*/
#ifdef FW_DEBUG
int i;
#endif
if (rcode) {
DPRINTF(("Bad return code: %d\n", rcode));
if (ab->ab_data)
free(ab->ab_data, M_1394DATA);
free(ab, M_1394DATA);
}
DPRINTF(("csr: 0x%016qx\n", ab->ab_csr));
#ifdef FW_DEBUG
for (i = 0; i < (ab->ab_retlen / 4); i++)
printf("%d: 0x%08x\n", i, ntohl(ab->ab_data[i]));
#endif
free(ab->ab_data, M_1394DATA);
free(ab, M_1394DATA);
}
static void
fwnode_match_subdevs(struct fwnode_softc *sc)
{
struct fwnode_device_cap *devcap;
TAILQ_FOREACH(devcap, &sc->sc_dev_cap_head, dev_list) {
switch(devcap->dev_type) {
case DEVTYPE_UNKNOWN:
DPRINTF(("%s: Unknown device %d\n",
sc->sc_sc1394.sc1394_dev.dv_xname,
devcap->dev_spec));
break;
case DEVTYPE_SBP2:
devcap->dev_init(sc, devcap);
break;
default:
panic("Unknown devcap type: %d", devcap->dev_type);
fwnode_print(NULL, NULL);
break;
}
}
}
static int
fwnode_print(void *aux, const char *pnp)
{
char *name = aux;
if (pnp)
printf("%s at %s", name, pnp);
return UNCONF;
}