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