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