1141 lines
37 KiB
C
1141 lines
37 KiB
C
|
/* $NetBSD: fwnode.c,v 1.1 2001/05/01 04:48:11 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/types.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);
|
||
|
|
||
|
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);
|
||
|
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_input = fwa->input;
|
||
|
sc->sc1394_output = fwa->output;
|
||
|
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_node = (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_input(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_node;
|
||
|
int i, len, infolen, crclen, newlen, offset, complete, val, *dirs, *tdirs;
|
||
|
int numdirs;
|
||
|
u_int32_t *t;
|
||
|
u_int8_t type;
|
||
|
|
||
|
dirs = NULL;
|
||
|
|
||
|
if (rcode != IEEE1394_RCODE_COMPLETE) {
|
||
|
#ifdef FW_DEBUG
|
||
|
printf ("Aborting configrom input, rcode: %d\n", rcode);
|
||
|
#endif
|
||
|
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) {
|
||
|
#ifdef FW_DEBUG
|
||
|
printf ("%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);
|
||
|
#endif
|
||
|
goto fail;
|
||
|
}
|
||
|
if (ab->ab_retlen % 4) {
|
||
|
#ifdef FW_DEBUG
|
||
|
printf ("%s: configrom read of invalid length: %d\n",
|
||
|
sc->sc_sc1394.sc1394_dev.dv_xname, ab->ab_retlen);
|
||
|
#endif
|
||
|
goto fail;
|
||
|
}
|
||
|
|
||
|
switch ((int)ab->ab_cbarg) {
|
||
|
case 1:
|
||
|
infolen = P1212_ROMFMT_GET_INFOLEN((ntohl(ab->ab_data[0])));
|
||
|
if (infolen <= 1) {
|
||
|
#ifdef FW_DEBUG
|
||
|
printf("%s: ROM not initialized or minimal ROM: Info length: "
|
||
|
"%d\n", sc->sc_sc1394.sc1394_dev.dv_xname, infolen);
|
||
|
#endif
|
||
|
goto fail;
|
||
|
}
|
||
|
crclen = P1212_ROMFMT_GET_CRCLEN((ntohl(ab->ab_data[0])));
|
||
|
if (crclen < infolen) {
|
||
|
#ifdef FW_DEBUG
|
||
|
printf("%s: CRC len less than info len. CRC len: %d, Info len: "
|
||
|
"%d\n", sc->sc_sc1394.sc1394_dev.dv_xname, crclen, infolen);
|
||
|
#endif
|
||
|
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, 0x00, 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_input(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:
|
||
|
|
||
|
/* Check to 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);
|
||
|
sc->sc_sc1394.sc1394_configrom = ab->ab_data;
|
||
|
sc->sc_sc1394.sc1394_configrom_len = 0;
|
||
|
memset(ab->ab_data, 0x00, newlen);
|
||
|
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_input(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;
|
||
|
int i, 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) {
|
||
|
#ifdef FW_DEBUG
|
||
|
printf("%s: Invalid signature found in rom. Expected: "
|
||
|
"IEEE1394_SIGNATURE, received: 0x%08x\n",
|
||
|
sc->sc_sc1394.sc1394_dev.dv_xname, ntohl(t[1]));
|
||
|
#endif
|
||
|
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) {
|
||
|
#ifdef FW_DEBUG
|
||
|
printf ("%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);
|
||
|
#endif
|
||
|
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) {
|
||
|
#ifdef FW_DEBUG
|
||
|
printf("Invalid ROM: CRC: 0x%04hx, Calculated CRC: 0x%04hx, CRC1:"
|
||
|
" 0x%04hx\n", (unsigned short)romcrc, (unsigned short)crc,
|
||
|
(unsigned short)crc1);
|
||
|
#endif
|
||
|
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) {
|
||
|
#ifdef FW_DEBUG
|
||
|
printf("Invalid ROM: CRC: 0x%04hx, Calculated CRC: "
|
||
|
"0x%04hx, CRC1: 0x%04hx\n",
|
||
|
(unsigned short)romcrc, (unsigned short)crc,
|
||
|
(unsigned short)crc1);
|
||
|
#endif
|
||
|
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);
|
||
|
memset(l, 0, sizeof(struct configrom_leafdata));
|
||
|
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) {
|
||
|
#ifdef FW_DEBUG
|
||
|
printf("Invalid text spec id 0x%08x in descriptor starting at: "
|
||
|
"0x%08x\n", l->spec_id, ntohl(addr[0]));
|
||
|
#endif
|
||
|
return 1;
|
||
|
}
|
||
|
l->char_width = (ntohl(addr[2]) >> 28) & 0xf;
|
||
|
|
||
|
if (l->char_width == 8) {
|
||
|
#ifdef FW_DEBUG
|
||
|
printf("Invalid character width 0x%04hx in text descriptor"
|
||
|
" starting at: 0x%08x\n", (unsigned short)l->char_width,
|
||
|
ntohl(addr[0]));
|
||
|
#endif
|
||
|
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))) {
|
||
|
#ifdef FW_DEBUG
|
||
|
printf("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]));
|
||
|
#endif
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* First 2 quads contain the data filled in above. Trim them off. */
|
||
|
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) {
|
||
|
#ifdef FW_DEBUG
|
||
|
printf("Invalid icon spec id 0x%08x in descriptor starting at: "
|
||
|
"0x%08x\n", l->spec_id, ntohl(addr[0]));
|
||
|
#endif
|
||
|
return 1;
|
||
|
}
|
||
|
version = ntohl(addr[2]) & 0xffffff;
|
||
|
if (version) {
|
||
|
#ifdef FW_DEBUG
|
||
|
printf("Invalid icon version 0x%08x in descriptor starting at: "
|
||
|
"0x%08x\n", version, ntohl(addr[0]));
|
||
|
#endif
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* First 2 quads contain the data filled in above. Trim them off. */
|
||
|
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 one exists and isn't valid one half was already found. */
|
||
|
if ((devcap->dev_type == DEVTYPE_SBP2) && found_sbp2) {
|
||
|
if (!devcap->dev_valid) {
|
||
|
done = 1;
|
||
|
devcap->dev_valid = 1;
|
||
|
} else {
|
||
|
#ifdef FW_DEBUG
|
||
|
printf("found_sbp2: 0x%08x\n", data->val);
|
||
|
#endif
|
||
|
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);
|
||
|
ab->ab_data = malloc(8, M_1394DATA, M_WAITOK);
|
||
|
ab2 = malloc(sizeof(struct ieee1394_abuf), M_1394DATA, M_WAITOK);
|
||
|
|
||
|
loc *= 4;
|
||
|
ab->ab_length = 8;
|
||
|
ab->ab_node = (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_node = (struct ieee1394_softc *)sc;
|
||
|
|
||
|
sc->sc1394_inreg(ab2, FALSE);
|
||
|
sc->sc1394_output(ab);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
sbp2_login(struct ieee1394_abuf *ab, int rcode)
|
||
|
{
|
||
|
struct fwnode_softc *sc = (struct fwnode_softc *)ab->ab_node;
|
||
|
/* 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);
|
||
|
statab = malloc(sizeof (struct ieee1394_abuf), M_1394DATA, M_WAITOK);
|
||
|
|
||
|
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_node = ab->ab_node;
|
||
|
|
||
|
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_node = ab->ab_node;
|
||
|
|
||
|
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_output(ab);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
sbp2_login_resp(struct ieee1394_abuf *ab, int rcode)
|
||
|
{
|
||
|
/* struct fwnode_device_cap *devcap = (struct fwnode_device_cap *)ab->ab_cbarg;*/
|
||
|
int i;
|
||
|
|
||
|
if (rcode) {
|
||
|
#ifdef FW_DEBUG
|
||
|
printf ("Bad return code: %d\n", rcode);
|
||
|
#endif
|
||
|
if (ab->ab_data)
|
||
|
free (ab->ab_data, M_1394DATA);
|
||
|
free(ab, M_1394DATA);
|
||
|
}
|
||
|
|
||
|
#ifdef FW_DEBUG
|
||
|
printf("csr: 0x%016qx\n", ab->ab_csr);
|
||
|
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:
|
||
|
#ifdef FW_DEBUG
|
||
|
printf ("%s: Unknown device %d\n", sc->sc_sc1394.sc1394_dev.dv_xname,
|
||
|
devcap->dev_spec);
|
||
|
#endif
|
||
|
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;
|
||
|
}
|