Home | History | Annotate | Line # | Download | only in fwctl
fwcontrol.c revision 1.1
      1 /*	$NetBSD: fwcontrol.c,v 1.1 2005/07/11 15:35:25 kiyohara Exp $	*/
      2 /*
      3  * Copyright (C) 2002
      4  * 	Hidetoshi Shimokawa. All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. All advertising materials mentioning features or use of this software
     15  *    must display the following acknowledgement:
     16  *
     17  *	This product includes software developed by Hidetoshi Shimokawa.
     18  *
     19  * 4. Neither the name of the author nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 #if defined(__FreeBSD__)
     36 
     37 #include <sys/cdefs.h>
     38 __FBSDID("$FreeBSD: /repoman/r/ncvs/src/usr.sbin/fwcontrol/fwcontrol.c,v 1.22 2005/05/20 12:50:47 charnier Exp $");
     39 #endif
     40 
     41 #include <sys/param.h>
     42 #include <sys/malloc.h>
     43 #include <sys/types.h>
     44 #include <sys/sysctl.h>
     45 #include <sys/socket.h>
     46 #include <sys/ioctl.h>
     47 #include <sys/errno.h>
     48 #if defined(__FreeBSD__)
     49 #include <sys/eui64.h>
     50 #include <dev/firewire/firewire.h>
     51 #include <dev/firewire/iec13213.h>
     52 #include <dev/firewire/fwphyreg.h>
     53 #elif defined(__NetBSD__)
     54 #include "eui64.h"
     55 #include <dev/ieee1394/firewire.h>
     56 #include <dev/ieee1394/iec13213.h>
     57 #include <dev/ieee1394/fwphyreg.h>
     58 #endif
     59 
     60 #include <netinet/in.h>
     61 #include <fcntl.h>
     62 #include <stdio.h>
     63 #include <err.h>
     64 #include <stdlib.h>
     65 #include <string.h>
     66 #include <unistd.h>
     67 
     68 extern int dvrecv(int, char *, char, int);
     69 extern int dvsend(int, char *, char, int);
     70 
     71 int sysctl_set_int(const char *, int);
     72 
     73 static void
     74 usage(void)
     75 {
     76 	fprintf(stderr,
     77 		"fwctl [-u bus_num] [-rt] [-g gap_count] [-o node] "
     78 		    "[-b pri_req] [-c node] [-d node] [-l file] "
     79 		    "[-R file] [-S file] [-m target]\n"
     80 		"\t-u: specify bus number\n"
     81 		"\t-g: broadcast gap_count by phy_config packet\n"
     82 		"\t-o: send link-on packet to the node\n"
     83 		"\t-s: write RESET_START register on the node\n"
     84 		"\t-b: set PRIORITY_BUDGET register on all supported nodes\n"
     85 		"\t-c: read configuration ROM\n"
     86 		"\t-r: bus reset\n"
     87 		"\t-t: read topology map\n"
     88 		"\t-d: hex dump of configuration ROM\n"
     89 		"\t-l: load and parse hex dump file of configuration ROM\n"
     90 		"\t-R: Receive DV stream\n"
     91 		"\t-S: Send DV stream\n"
     92 		"\t-m: set fwmem target\n");
     93 	exit(0);
     94 }
     95 
     96 static void
     97 fweui2eui64(const struct fw_eui64 *fweui, struct eui64 *eui)
     98 {
     99 	*(u_int32_t*)&(eui->octet[0]) = htonl(fweui->hi);
    100 	*(u_int32_t*)&(eui->octet[4]) = htonl(fweui->lo);
    101 }
    102 
    103 static struct fw_devlstreq *
    104 get_dev(int fd)
    105 {
    106 	struct fw_devlstreq *data;
    107 
    108 	data = (struct fw_devlstreq *)malloc(sizeof(struct fw_devlstreq));
    109 	if (data == NULL)
    110 		err(1, "malloc");
    111 	if( ioctl(fd, FW_GDEVLST, data) < 0) {
    112        			err(1, "ioctl");
    113 	}
    114 	return data;
    115 }
    116 
    117 static int
    118 str2node(int fd, const char *nodestr)
    119 {
    120 	struct eui64 eui, tmpeui;
    121 	struct fw_devlstreq *data;
    122 	char *endptr;
    123 	int i, node;
    124 
    125 	if (nodestr == '\0')
    126 		return (-1);
    127 
    128 	/*
    129 	 * Deal with classic node specifications.
    130 	 */
    131 	node = strtol(nodestr, &endptr, 0);
    132 	if (*endptr == '\0')
    133 		goto gotnode;
    134 
    135 	/*
    136 	 * Try to get an eui and match it against available nodes.
    137 	 */
    138 	if (eui64_hostton(nodestr, &eui) != 0 && eui64_aton(nodestr, &eui) != 0)
    139 		return (-1);
    140 
    141 	data = get_dev(fd);
    142 
    143 	for (i = 0; i < data->info_len; i++) {
    144 		fweui2eui64(&data->dev[i].eui, &tmpeui);
    145 		if (memcmp(&eui, &tmpeui, sizeof(struct eui64)) == 0) {
    146 			node = data->dev[i].dst;
    147 			goto gotnode;
    148 		}
    149 	}
    150 	if (i >= data->info_len)
    151 		return (-1);
    152 
    153 gotnode:
    154 	if (node < 0 || node > 63)
    155 		return (-1);
    156 	else
    157 		return (node);
    158 }
    159 
    160 static void
    161 list_dev(int fd)
    162 {
    163 	struct fw_devlstreq *data;
    164 	struct fw_devinfo *devinfo;
    165 	struct eui64 eui;
    166 	char addr[EUI64_SIZ];
    167 	int i;
    168 
    169 	data = get_dev(fd);
    170 	printf("%d devices (info_len=%d)\n", data->n, data->info_len);
    171 	printf("node           EUI64          status\n");
    172 	for (i = 0; i < data->info_len; i++) {
    173 		devinfo = &data->dev[i];
    174 		fweui2eui64(&devinfo->eui, &eui);
    175 		eui64_ntoa(&eui, addr, sizeof(addr));
    176 		printf("%4d  %s %6d\n",
    177 			(devinfo->status || i == 0) ? devinfo->dst : -1,
    178 			addr,
    179 			devinfo->status
    180 		);
    181 	}
    182 	free((void *)data);
    183 }
    184 
    185 static u_int32_t
    186 read_write_quad(int fd, struct fw_eui64 eui, u_int32_t addr_lo, int readmode, u_int32_t data)
    187 {
    188         struct fw_asyreq *asyreq;
    189 	u_int32_t *qld, res;
    190 
    191         asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 16);
    192 	asyreq->req.len = 16;
    193 #if 0
    194 	asyreq->req.type = FWASREQNODE;
    195 	asyreq->pkt.mode.rreqq.dst = FWLOCALBUS | node;
    196 #else
    197 	asyreq->req.type = FWASREQEUI;
    198 	asyreq->req.dst.eui = eui;
    199 #endif
    200 	asyreq->pkt.mode.rreqq.tlrt = 0;
    201 	if (readmode)
    202 		asyreq->pkt.mode.rreqq.tcode = FWTCODE_RREQQ;
    203 	else
    204 		asyreq->pkt.mode.rreqq.tcode = FWTCODE_WREQQ;
    205 
    206 	asyreq->pkt.mode.rreqq.dest_hi = 0xffff;
    207 	asyreq->pkt.mode.rreqq.dest_lo = addr_lo;
    208 
    209 	qld = (u_int32_t *)&asyreq->pkt;
    210 	if (!readmode)
    211 		asyreq->pkt.mode.wreqq.data = data;
    212 
    213 	if (ioctl(fd, FW_ASYREQ, asyreq) < 0) {
    214        		err(1, "ioctl");
    215 	}
    216 	res = qld[3];
    217 	free(asyreq);
    218 	if (readmode)
    219 		return ntohl(res);
    220 	else
    221 		return 0;
    222 }
    223 
    224 static void
    225 send_phy_config(int fd, int root_node, int gap_count)
    226 {
    227         struct fw_asyreq *asyreq;
    228 
    229 	asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 12);
    230 	asyreq->req.len = 12;
    231 	asyreq->req.type = FWASREQNODE;
    232 	asyreq->pkt.mode.ld[0] = 0;
    233 	asyreq->pkt.mode.ld[1] = 0;
    234 	asyreq->pkt.mode.common.tcode = FWTCODE_PHY;
    235 	if (root_node >= 0)
    236 		asyreq->pkt.mode.ld[1] |= (root_node & 0x3f) << 24 | 1 << 23;
    237 	if (gap_count >= 0)
    238 		asyreq->pkt.mode.ld[1] |= 1 << 22 | (gap_count & 0x3f) << 16;
    239 	asyreq->pkt.mode.ld[2] = ~asyreq->pkt.mode.ld[1];
    240 
    241 	printf("send phy_config root_node=%d gap_count=%d\n",
    242 						root_node, gap_count);
    243 
    244 	if (ioctl(fd, FW_ASYREQ, asyreq) < 0)
    245        		err(1, "ioctl");
    246 	free(asyreq);
    247 }
    248 
    249 static void
    250 send_link_on(int fd, int node)
    251 {
    252         struct fw_asyreq *asyreq;
    253 
    254 	asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 12);
    255 	asyreq->req.len = 12;
    256 	asyreq->req.type = FWASREQNODE;
    257 	asyreq->pkt.mode.common.tcode = FWTCODE_PHY;
    258 	asyreq->pkt.mode.ld[1] |= (1 << 30) | ((node & 0x3f) << 24);
    259 	asyreq->pkt.mode.ld[2] = ~asyreq->pkt.mode.ld[1];
    260 
    261 	if (ioctl(fd, FW_ASYREQ, asyreq) < 0)
    262        		err(1, "ioctl");
    263 	free(asyreq);
    264 }
    265 
    266 static void
    267 reset_start(int fd, int node)
    268 {
    269         struct fw_asyreq *asyreq;
    270 
    271 	asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 16);
    272 	asyreq->req.len = 16;
    273 	asyreq->req.type = FWASREQNODE;
    274 	asyreq->pkt.mode.wreqq.dst = FWLOCALBUS | (node & 0x3f);
    275 	asyreq->pkt.mode.wreqq.tlrt = 0;
    276 	asyreq->pkt.mode.wreqq.tcode = FWTCODE_WREQQ;
    277 
    278 	asyreq->pkt.mode.wreqq.dest_hi = 0xffff;
    279 	asyreq->pkt.mode.wreqq.dest_lo = 0xf0000000 | RESET_START;
    280 
    281 	asyreq->pkt.mode.wreqq.data = htonl(0x1);
    282 
    283 	if (ioctl(fd, FW_ASYREQ, asyreq) < 0)
    284        		err(1, "ioctl");
    285 	free(asyreq);
    286 }
    287 
    288 static void
    289 set_pri_req(int fd, int pri_req)
    290 {
    291 	struct fw_devlstreq *data;
    292 	struct fw_devinfo *devinfo;
    293 	struct eui64 eui;
    294 	char addr[EUI64_SIZ];
    295 	u_int32_t max, reg, old;
    296 	int i;
    297 
    298 	data = get_dev(fd);
    299 #define BUGET_REG 0xf0000218
    300 	for (i = 0; i < data->info_len; i++) {
    301 		devinfo = &data->dev[i];
    302 		if (!devinfo->status)
    303 			continue;
    304 		reg = read_write_quad(fd, devinfo->eui, BUGET_REG, 1, 0);
    305 		fweui2eui64(&devinfo->eui, &eui);
    306 		eui64_ntoa(&eui, addr, sizeof(addr));
    307 		printf("%d %s, %08x",
    308 			devinfo->dst, addr, reg);
    309 		if (reg > 0 && pri_req >= 0) {
    310 			old = (reg & 0x3f);
    311 			max = (reg & 0x3f00) >> 8;
    312 			if (pri_req > max)
    313 				pri_req =  max;
    314 			printf(" 0x%x -> 0x%x\n", old, pri_req);
    315 			read_write_quad(fd, devinfo->eui, BUGET_REG, 0, pri_req);
    316 		} else {
    317 			printf("\n");
    318 		}
    319 	}
    320 	free((void *)data);
    321 }
    322 
    323 static void
    324 parse_bus_info_block(u_int32_t *p, int info_len)
    325 {
    326 	char addr[EUI64_SIZ];
    327 	struct bus_info *bi;
    328 	struct eui64 eui;
    329 
    330 	bi = (struct bus_info *)p;
    331 	fweui2eui64(&bi->eui64, &eui);
    332 	eui64_ntoa(&eui, addr, sizeof(addr));
    333 	printf("bus_name: 0x%04x\n"
    334 		"irmc:%d cmc:%d isc:%d bmc:%d pmc:%d\n"
    335 		"cyc_clk_acc:%d max_rec:%d max_rom:%d\n"
    336 		"generation:%d link_spd:%d\n"
    337 		"EUI64: %s\n",
    338 		bi->bus_name,
    339 		bi->irmc, bi->cmc, bi->isc, bi->bmc, bi->pmc,
    340 		bi->cyc_clk_acc, bi->max_rec, bi->max_rom,
    341 		bi->generation, bi->link_spd,
    342 		addr);
    343 }
    344 
    345 static int
    346 get_crom(int fd, int node, void *crom_buf, int len)
    347 {
    348 	struct fw_crom_buf buf;
    349 	int i, error;
    350 	struct fw_devlstreq *data;
    351 
    352 	data = get_dev(fd);
    353 
    354 	for (i = 0; i < data->info_len; i++) {
    355 		if (data->dev[i].dst == node && data->dev[i].eui.lo != 0)
    356 			break;
    357 	}
    358 	if (i == data->info_len)
    359 		errx(1, "no such node %d.", node);
    360 	else
    361 		buf.eui = data->dev[i].eui;
    362 	free((void *)data);
    363 
    364 	buf.len = len;
    365 	buf.ptr = crom_buf;
    366 	bzero(crom_buf, len);
    367 	if ((error = ioctl(fd, FW_GCROM, &buf)) < 0) {
    368        		err(1, "ioctl");
    369 	}
    370 
    371 	return error;
    372 }
    373 
    374 static void
    375 show_crom(u_int32_t *crom_buf)
    376 {
    377 	int i;
    378 	struct crom_context cc;
    379 	char *desc, info[256];
    380 	static const char *key_types = "ICLD";
    381 	struct csrreg *reg;
    382 	struct csrdirectory *dir;
    383 	struct csrhdr *hdr;
    384 	u_int16_t crc;
    385 
    386 	printf("first quad: 0x%08x ", *crom_buf);
    387 	if (crom_buf[0] == 0) {
    388 		printf("(Invalid Configuration ROM)\n");
    389 		return;
    390 	}
    391 	hdr = (struct csrhdr *)crom_buf;
    392 	if (hdr->info_len == 1) {
    393 		/* minimum ROM */
    394 		reg = (struct csrreg *)hdr;
    395 		printf("verndor ID: 0x%06x\n",  reg->val);
    396 		return;
    397 	}
    398 	printf("info_len=%d crc_len=%d crc=0x%04x",
    399 		hdr->info_len, hdr->crc_len, hdr->crc);
    400 	crc = crom_crc(crom_buf+1, hdr->crc_len);
    401 	if (crc == hdr->crc)
    402 		printf("(OK)\n");
    403 	else
    404 		printf("(NG)\n");
    405 	parse_bus_info_block(crom_buf+1, hdr->info_len);
    406 
    407 	crom_init_context(&cc, crom_buf);
    408 	dir = cc.stack[0].dir;
    409 	if (!dir) {
    410 		printf("no root directory - giving up\n");
    411 		return;
    412 	}
    413 	printf("root_directory: len=0x%04x(%d) crc=0x%04x",
    414 			dir->crc_len, dir->crc_len, dir->crc);
    415 	crc = crom_crc((u_int32_t *)&dir->entry[0], dir->crc_len);
    416 	if (crc == dir->crc)
    417 		printf("(OK)\n");
    418 	else
    419 		printf("(NG)\n");
    420 	if (dir->crc_len < 1)
    421 		return;
    422 	while (cc.depth >= 0) {
    423 		desc = crom_desc(&cc, info, sizeof(info));
    424 		reg = crom_get(&cc);
    425 		for (i = 0; i < cc.depth; i++)
    426 			printf("\t");
    427 		printf("%02x(%c:%02x) %06x %s: %s\n",
    428 			reg->key,
    429 			key_types[(reg->key & CSRTYPE_MASK)>>6],
    430 			reg->key & CSRKEY_MASK, reg->val,
    431 			desc, info);
    432 		crom_next(&cc);
    433 	}
    434 }
    435 
    436 #define DUMP_FORMAT	"%08x %08x %08x %08x %08x %08x %08x %08x\n"
    437 
    438 static void
    439 dump_crom(u_int32_t *p)
    440 {
    441 	int len=1024, i;
    442 
    443 	for (i = 0; i < len/(4*8); i ++) {
    444 		printf(DUMP_FORMAT,
    445 			p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
    446 		p += 8;
    447 	}
    448 }
    449 
    450 static void
    451 load_crom(char *filename, u_int32_t *p)
    452 {
    453 	FILE *file;
    454 	int len=1024, i;
    455 
    456 	if ((file = fopen(filename, "r")) == NULL)
    457 		err(1, "load_crom");
    458 	for (i = 0; i < len/(4*8); i ++) {
    459 		fscanf(file, DUMP_FORMAT,
    460 			p, p+1, p+2, p+3, p+4, p+5, p+6, p+7);
    461 		p += 8;
    462 	}
    463 }
    464 
    465 static void
    466 show_topology_map(int fd)
    467 {
    468 	struct fw_topology_map *tmap;
    469 	union fw_self_id sid;
    470 	int i;
    471 	static const char *port_status[] = {" ", "-", "P", "C"};
    472 	static const char *pwr_class[] = {" 0W", "15W", "30W", "45W",
    473 					"-1W", "-2W", "-5W", "-9W"};
    474 	static const char *speed[] = {"S100", "S200", "S400", "S800"};
    475 	tmap = malloc(sizeof(struct fw_topology_map));
    476 	if (tmap == NULL)
    477 		return;
    478 	if (ioctl(fd, FW_GTPMAP, tmap) < 0) {
    479        		err(1, "ioctl");
    480 	}
    481 	printf("crc_len: %d generation:%d node_count:%d sid_count:%d\n",
    482 		tmap->crc_len, tmap->generation,
    483 		tmap->node_count, tmap->self_id_count);
    484 	printf("id link gap_cnt speed delay cIRM power port0 port1 port2"
    485 		" ini more\n");
    486 	for (i = 0; i < tmap->crc_len - 2; i++) {
    487 		sid = tmap->self_id[i];
    488 		if (sid.p0.sequel) {
    489 			printf("%02d sequel packet\n", sid.p0.phy_id);
    490 			continue;
    491 		}
    492 		printf("%02d   %2d      %2d  %4s     %d    %d   %3s"
    493 				"     %s     %s     %s   %d    %d\n",
    494 			sid.p0.phy_id,
    495 			sid.p0.link_active,
    496 			sid.p0.gap_count,
    497 			speed[sid.p0.phy_speed],
    498 			sid.p0.phy_delay,
    499 			sid.p0.contender,
    500 			pwr_class[sid.p0.power_class],
    501 			port_status[sid.p0.port0],
    502 			port_status[sid.p0.port1],
    503 			port_status[sid.p0.port2],
    504 			sid.p0.initiated_reset,
    505 			sid.p0.more_packets
    506 		);
    507 	}
    508 	free(tmap);
    509 }
    510 
    511 static void
    512 read_phy_registers(int fd, u_int8_t *buf, int offset, int len)
    513 {
    514 	struct fw_reg_req_t reg;
    515 	int i;
    516 
    517 	for (i = 0; i < len; i++) {
    518 		reg.addr = offset + i;
    519 		if (ioctl(fd, FWOHCI_RDPHYREG, &reg) < 0)
    520        			err(1, "ioctl");
    521 		buf[i] = (u_int8_t) reg.data;
    522 		printf("0x%02x ",  reg.data);
    523 	}
    524 	printf("\n");
    525 }
    526 
    527 static void
    528 read_phy_page(int fd, u_int8_t *buf, int page, int port)
    529 {
    530 	struct fw_reg_req_t reg;
    531 
    532 	reg.addr = 0x7;
    533 	reg.data = ((page & 7) << 5) | (port & 0xf);
    534 	if (ioctl(fd, FWOHCI_WRPHYREG, &reg) < 0)
    535        		err(1, "ioctl");
    536 	read_phy_registers(fd, buf, 8, 8);
    537 }
    538 
    539 static void
    540 dump_phy_registers(int fd)
    541 {
    542 	struct phyreg_base b;
    543 	struct phyreg_page0 p;
    544 	struct phyreg_page1 v;
    545 	int i;
    546 
    547 	printf("=== base register ===\n");
    548 	read_phy_registers(fd, (u_int8_t *)&b, 0, 8);
    549 	printf(
    550 	    "Physical_ID:%d  R:%d  CPS:%d\n"
    551 	    "RHB:%d  IBR:%d  Gap_Count:%d\n"
    552 	    "Extended:%d Num_Ports:%d\n"
    553 	    "PHY_Speed:%d Delay:%d\n"
    554 	    "LCtrl:%d C:%d Jitter:%d Pwr_Class:%d\n"
    555 	    "WDIE:%d ISBR:%d CTOI:%d CPSI:%d STOI:%d PEI:%d EAA:%d EMC:%d\n"
    556 	    "Max_Legacy_SPD:%d BLINK:%d Bridge:%d\n"
    557 	    "Page_Select:%d Port_Select%d\n",
    558 	    b.phy_id, b.r, b.cps,
    559 	    b.rhb, b.ibr, b.gap_count,
    560 	    b.extended, b.num_ports,
    561 	    b.phy_speed, b.delay,
    562 	    b.lctrl, b.c, b.jitter, b.pwr_class,
    563 	    b.wdie, b.isbr, b.ctoi, b.cpsi, b.stoi, b.pei, b.eaa, b.emc,
    564 	    b.legacy_spd, b.blink, b.bridge,
    565 	    b.page_select, b.port_select
    566 	);
    567 
    568 	for (i = 0; i < b.num_ports; i ++) {
    569 		printf("\n=== page 0 port %d ===\n", i);
    570 		read_phy_page(fd, (u_int8_t *)&p, 0, i);
    571 		printf(
    572 		    "Astat:%d BStat:%d Ch:%d Con:%d RXOK:%d Dis:%d\n"
    573 		    "Negotiated_speed:%d PIE:%d Fault:%d Stanby_fault:%d Disscrm:%d B_Only:%d\n"
    574 		    "DC_connected:%d Max_port_speed:%d LPP:%d Cable_speed:%d\n"
    575 		    "Connection_unreliable:%d Beta_mode:%d\n"
    576 		    "Port_error:0x%x\n"
    577 		    "Loop_disable:%d In_standby:%d Hard_disable:%d\n",
    578 		    p.astat, p.bstat, p.ch, p.con, p.rxok, p.dis,
    579 		    p.negotiated_speed, p.pie, p.fault, p.stanby_fault, p.disscrm, p.b_only,
    580 		    p.dc_connected, p.max_port_speed, p.lpp, p.cable_speed,
    581 		    p.connection_unreliable, p.beta_mode,
    582 		    p.port_error,
    583 		    p.loop_disable, p.in_standby, p.hard_disable
    584 		);
    585 	}
    586 	printf("\n=== page 1 ===\n");
    587 	read_phy_page(fd, (u_int8_t *)&v, 1, 0);
    588 	printf(
    589 	    "Compliance:%d\n"
    590 	    "Vendor_ID:0x%06x\n"
    591 	    "Product_ID:0x%06x\n",
    592 	    v.compliance,
    593 	    (v.vendor_id[0] << 16) | (v.vendor_id[1] << 8) | v.vendor_id[2],
    594 	    (v.product_id[0] << 16) | (v.product_id[1] << 8) | v.product_id[2]
    595 	);
    596 }
    597 
    598 static void
    599 open_dev(int *fd, char *devbase)
    600 {
    601 	char name[256];
    602 	int i;
    603 
    604 	if (*fd < 0) {
    605 		for (i = 0; i < 4; i++) {
    606 			snprintf(name, sizeof(name), "%s.%d", devbase, i);
    607 			if ((*fd = open(name, O_RDWR)) >= 0)
    608 				break;
    609 		}
    610 		if (*fd < 0)
    611 			err(1, "open");
    612 
    613 	}
    614 }
    615 
    616 int
    617 sysctl_set_int(const char *name, int val)
    618 {
    619 	if (sysctlbyname(name, NULL, NULL, &val, sizeof(int)) < 0)
    620 		err(1, "sysctl %s failed.", name);
    621 	return (0);
    622 }
    623 
    624 int
    625 main(int argc, char **argv)
    626 {
    627 	u_int32_t crom_buf[1024/4];
    628 	char devbase[1024] = "/dev/fw0";
    629 	int fd, tmp, ch, len=1024;
    630 	struct fw_eui64 eui;
    631 	struct eui64 target;
    632 
    633 	fd = -1;
    634 
    635 	if (argc < 2) {
    636 		open_dev(&fd, devbase);
    637 		list_dev(fd);
    638 	}
    639 
    640 	while ((ch = getopt(argc, argv, "g:m:o:s:b:prtc:d:l:u:R:S:")) != -1)
    641 		switch(ch) {
    642 		case 'b':
    643 			tmp = strtol(optarg, NULL, 0);
    644 			open_dev(&fd, devbase);
    645 			set_pri_req(fd, tmp);
    646 			break;
    647 		case 'c':
    648 			open_dev(&fd, devbase);
    649 			tmp = str2node(fd, optarg);
    650 			get_crom(fd, tmp, crom_buf, len);
    651 			show_crom(crom_buf);
    652 			break;
    653 		case 'd':
    654 			open_dev(&fd, devbase);
    655 			tmp = str2node(fd, optarg);
    656 			get_crom(fd, tmp, crom_buf, len);
    657 			dump_crom(crom_buf);
    658 			break;
    659 		case 'g':
    660 			tmp = strtol(optarg, NULL, 0);
    661 			open_dev(&fd, devbase);
    662 			send_phy_config(fd, -1, tmp);
    663 			break;
    664 		case 'l':
    665 			load_crom(optarg, crom_buf);
    666 			show_crom(crom_buf);
    667 			break;
    668 		case 'm':
    669 		       if (eui64_hostton(optarg, &target) != 0 &&
    670 			   eui64_aton(optarg, &target) != 0)
    671 				errx(1, "invalid target: %s", optarg);
    672 			eui.hi = ntohl(*(u_int32_t*)&(target.octet[0]));
    673 			eui.lo = ntohl(*(u_int32_t*)&(target.octet[4]));
    674 			sysctl_set_int("hw.firewire.fwmem.eui64_hi", eui.hi);
    675 			sysctl_set_int("hw.firewire.fwmem.eui64_lo", eui.lo);
    676 			break;
    677 		case 'o':
    678 			open_dev(&fd, devbase);
    679 			tmp = str2node(fd, optarg);
    680 			send_link_on(fd, tmp);
    681 			break;
    682 		case 'p':
    683 			open_dev(&fd, devbase);
    684 			dump_phy_registers(fd);
    685 			break;
    686 		case 'r':
    687 			open_dev(&fd, devbase);
    688 			if(ioctl(fd, FW_IBUSRST, &tmp) < 0)
    689                        		err(1, "ioctl");
    690 			break;
    691 		case 's':
    692 			open_dev(&fd, devbase);
    693 			tmp = str2node(fd, optarg);
    694 			reset_start(fd, tmp);
    695 			break;
    696 		case 't':
    697 			open_dev(&fd, devbase);
    698 			show_topology_map(fd);
    699 			break;
    700 		case 'u':
    701 			tmp = strtol(optarg, NULL, 0);
    702 			snprintf(devbase, sizeof(devbase), "/dev/fw%d",  tmp);
    703 			if (fd > 0) {
    704 				close(fd);
    705 				fd = -1;
    706 			}
    707 			if (argc == optind) {
    708 				open_dev(&fd, devbase);
    709 				list_dev(fd);
    710 			}
    711 			break;
    712 #define TAG	(1<<6)
    713 #define CHANNEL	63
    714 		case 'R':
    715 			open_dev(&fd, devbase);
    716 			dvrecv(fd, optarg, TAG | CHANNEL, -1);
    717 			break;
    718 		case 'S':
    719 			open_dev(&fd, devbase);
    720 			dvsend(fd, optarg, TAG | CHANNEL, -1);
    721 			break;
    722 		default:
    723 			usage();
    724 		}
    725 	return 0;
    726 }
    727