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$");
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 <errno.h>
50#include <strings.h>
51#define EX_SOFTWARE 1
52#define EX_IOERR 1
53#else
54#include <sys/param.h>
55#include <sys/malloc.h>
56#include <sys/types.h>
57#include <sys/sysctl.h>
58#include <sys/socket.h>
59#include <sys/ioctl.h>
60#include <sys/errno.h>
61#include <sys/eui64.h>
62#include <dev/firewire/firewire.h>
63#include <dev/firewire/iec13213.h>
64#include <dev/firewire/fwphyreg.h>
65#include <dev/firewire/iec68113.h>
66#include <sysexits.h>
67#endif
68#include <stdio.h>
69#include <stdint.h>
70#include <stdbool.h>
71#include <fcntl.h>
72#include <err.h>
73#include <stdlib.h>
74#include <string.h>
75#include <unistd.h>
76#include <netinet/in.h>
77#include "fwmethods.h"
78
79#ifndef __HAIKU__
80static void sysctl_set_int(const char *, int);
81#endif
82
83static void
84usage(void)
85{
86	fprintf(stderr,
87		"%s [-u bus_num] [-prt] [-c node] [-d node] [-o node] [-s node]\n"
88		"\t  [-l file] [-g gap_count] [-f force_root ] [-b pri_req]\n"
89		"\t  [-M mode] [-R filename] [-S filename] "
90#ifndef __HAIKU__
91		"[-m EUI64 | hostname]\n"
92#else
93		"\n"
94#endif
95		"\t-u: specify bus number\n"
96		"\t-p: Display current PHY register settings\n"
97		"\t-r: bus reset\n"
98		"\t-t: read topology map\n"
99		"\t-c: read configuration ROM\n"
100		"\t-d: hex dump of configuration ROM\n"
101		"\t-o: send link-on packet to the node\n"
102		"\t-s: write RESET_START register on the node\n"
103		"\t-l: load and parse hex dump file of configuration ROM\n"
104		"\t-g: set gap count\n"
105		"\t-f: force root node\n"
106		"\t-b: set PRIORITY_BUDGET register on all supported nodes\n"
107		"\t-M: specify dv or mpeg\n"
108		"\t-R: Receive DV or MPEG TS stream\n"
109		"\t-S: Send DV stream\n"
110#ifndef __HAIKU__
111		"\t-m: set fwmem target\n"
112#endif
113		, getprogname() );
114	fprintf(stderr, "\n");
115}
116
117static void
118fweui2eui64(const struct fw_eui64 *fweui, struct eui64 *eui)
119{
120	*(u_int32_t*)&(eui->octet[0]) = htonl(fweui->hi);
121	*(u_int32_t*)&(eui->octet[4]) = htonl(fweui->lo);
122}
123
124static void
125get_dev(int fd, struct fw_devlstreq *data)
126{
127	if (data == NULL)
128		err(EX_SOFTWARE, "%s: data malloc", __func__);
129	if( ioctl(fd, FW_GDEVLST, data) < 0) {
130       			err(EX_IOERR, "%s: ioctl", __func__);
131	}
132}
133
134static int
135str2node(int fd, const char *nodestr)
136{
137	struct eui64 eui, tmpeui;
138	struct fw_devlstreq *data;
139	char *endptr;
140	int i, node;
141
142	if (nodestr == NULL || *nodestr == '\0')
143		return (-1);
144
145	/*
146	 * Deal with classic node specifications.
147	 */
148	node = strtol(nodestr, &endptr, 0);
149	if (*endptr == '\0')
150		goto gotnode;
151
152	/*
153	 * Try to get an eui and match it against available nodes.
154	 */
155#ifdef __HAIKU__
156	if (eui64_aton(nodestr, &eui) != 0)
157#else
158	if (eui64_hostton(nodestr, &eui) != 0 && eui64_aton(nodestr, &eui) != 0)
159#endif
160		return (-1);
161
162	data = (struct fw_devlstreq *)malloc(sizeof(struct fw_devlstreq));
163	if (data == NULL)
164		err(EX_SOFTWARE, "%s: data malloc", __func__);
165	get_dev(fd,data);
166
167	for (i = 0; i < data->info_len; i++) {
168		fweui2eui64(&data->dev[i].eui, &tmpeui);
169		if (memcmp(&eui, &tmpeui, sizeof(struct eui64)) == 0) {
170			node = data->dev[i].dst;
171			free(data);
172			goto gotnode;
173		}
174	}
175	if (i >= data->info_len) {
176		if (data != NULL)
177			free(data);
178		return (-1);
179	}
180
181gotnode:
182	if (node < 0 || node > 63)
183		return (-1);
184	else
185		return (node);
186}
187
188static void
189list_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 = (struct fw_devlstreq *)malloc(sizeof(struct fw_devlstreq));
198	if (data == NULL)
199		err(1, "%s: data malloc", __func__);
200	get_dev(fd, data);
201	printf("%d devices (info_len=%d)\n", data->n, data->info_len);
202	printf("node           EUI64          status\n");
203	for (i = 0; i < data->info_len; i++) {
204		devinfo = &data->dev[i];
205		fweui2eui64(&devinfo->eui, &eui);
206		eui64_ntoa(&eui, addr, sizeof(addr));
207		printf("%4d  %s %6d\n",
208			(devinfo->status || i == 0) ? devinfo->dst : -1,
209			addr,
210			devinfo->status
211		);
212	}
213	free((void *)data);
214}
215
216static u_int32_t
217read_write_quad(int fd, struct fw_eui64 eui, u_int32_t addr_lo, int readmode, u_int32_t data)
218{
219        struct fw_asyreq *asyreq;
220	u_int32_t *qld, res;
221
222        asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 16);
223	if (asyreq == NULL)
224		err(EX_SOFTWARE, "%s:asyreq malloc", __func__);
225	asyreq->req.len = 16;
226#if 0
227	asyreq->req.type = FWASREQNODE;
228	asyreq->pkt.mode.rreqq.dst = FWLOCALBUS | node;
229#else
230	asyreq->req.type = FWASREQEUI;
231	asyreq->req.dst.eui = eui;
232#endif
233	asyreq->pkt.mode.rreqq.tlrt = 0;
234	if (readmode)
235		asyreq->pkt.mode.rreqq.tcode = FWTCODE_RREQQ;
236	else
237		asyreq->pkt.mode.rreqq.tcode = FWTCODE_WREQQ;
238
239	asyreq->pkt.mode.rreqq.dest_hi = 0xffff;
240	asyreq->pkt.mode.rreqq.dest_lo = addr_lo;
241
242	qld = (u_int32_t *)&asyreq->pkt;
243	if (!readmode)
244		asyreq->pkt.mode.wreqq.data = htonl(data);
245
246	if (ioctl(fd, FW_ASYREQ, asyreq) < 0) {
247       		err(EX_IOERR, "%s: ioctl", __func__);
248	}
249	res = qld[3];
250	free(asyreq);
251	if (readmode)
252		return ntohl(res);
253	else
254		return 0;
255}
256
257/*
258 * Send a PHY Config Packet
259 * ieee 1394a-2005 4.3.4.3
260 *
261 * Message ID   Root ID    R  T   Gap Count
262 * 00(2 bits)   (6 bits)   1  1   (6 bits)
263 *
264 * if "R" is set, then Root ID will be the next
265 * root node upon the next bus reset.
266 * if "T" is set, then Gap Count will be the
267 * value that all nodes use for their Gap Count
268 * if "R" and "T" are not set, then this message
269 * is either ignored or interpreted as an extended
270 * PHY config Packet as per 1394a-2005 4.3.4.4
271 */
272static void
273send_phy_config(int fd, int root_node, int gap_count)
274{
275        struct fw_asyreq *asyreq;
276
277	asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 12);
278	if (asyreq == NULL)
279		err(EX_SOFTWARE, "%s:asyreq malloc", __func__);
280	asyreq->req.len = 12;
281	asyreq->req.type = FWASREQNODE;
282	asyreq->pkt.mode.ld[0] = 0;
283	asyreq->pkt.mode.ld[1] = 0;
284	asyreq->pkt.mode.common.tcode = FWTCODE_PHY;
285	if (root_node >= 0)
286		asyreq->pkt.mode.ld[1] |= ((root_node << 24) | (1 << 23));
287	if (gap_count >= 0)
288		asyreq->pkt.mode.ld[1] |= ((1 << 22) | (gap_count << 16));
289	asyreq->pkt.mode.ld[2] = ~asyreq->pkt.mode.ld[1];
290
291	printf("send phy_config root_node=%d gap_count=%d\n",
292						root_node, gap_count);
293
294	if (ioctl(fd, FW_ASYREQ, asyreq) < 0)
295       		err(EX_IOERR, "%s: ioctl", __func__);
296	free(asyreq);
297}
298
299static void
300link_on(int fd, int node)
301{
302        struct fw_asyreq *asyreq;
303
304	asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 12);
305	if (asyreq == NULL)
306		err(EX_SOFTWARE, "%s:asyreq malloc", __func__);
307	asyreq->req.len = 12;
308	asyreq->req.type = FWASREQNODE;
309	asyreq->pkt.mode.common.tcode = FWTCODE_PHY;
310	asyreq->pkt.mode.ld[1] |= (1 << 30) | ((node & 0x3f) << 24);
311	asyreq->pkt.mode.ld[2] = ~asyreq->pkt.mode.ld[1];
312
313	if (ioctl(fd, FW_ASYREQ, asyreq) < 0)
314       		err(EX_IOERR, "%s: ioctl", __func__);
315	free(asyreq);
316}
317
318static void
319reset_start(int fd, int node)
320{
321        struct fw_asyreq *asyreq;
322
323	asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 16);
324	if (asyreq == NULL)
325		err(EX_SOFTWARE, "%s:asyreq malloc", __func__);
326	asyreq->req.len = 16;
327	asyreq->req.type = FWASREQNODE;
328	asyreq->pkt.mode.wreqq.dst = FWLOCALBUS | (node & 0x3f);
329	asyreq->pkt.mode.wreqq.tlrt = 0;
330	asyreq->pkt.mode.wreqq.tcode = FWTCODE_WREQQ;
331
332	asyreq->pkt.mode.wreqq.dest_hi = 0xffff;
333	asyreq->pkt.mode.wreqq.dest_lo = 0xf0000000 | RESET_START;
334
335	asyreq->pkt.mode.wreqq.data = htonl(0x1);
336
337	if (ioctl(fd, FW_ASYREQ, asyreq) < 0)
338       		err(EX_IOERR, "%s: ioctl", __func__);
339	free(asyreq);
340}
341
342static void
343set_pri_req(int fd, u_int32_t pri_req)
344{
345	struct fw_devlstreq *data;
346	struct fw_devinfo *devinfo;
347	struct eui64 eui;
348	char addr[EUI64_SIZ];
349	u_int32_t max, reg, old;
350	int i;
351
352	data = (struct fw_devlstreq *)malloc(sizeof(struct fw_devlstreq));
353	if (data == NULL)
354		err(EX_SOFTWARE, "%s:data malloc", __func__);
355	get_dev(fd, data);
356#define BUGET_REG 0xf0000218
357	for (i = 0; i < data->info_len; i++) {
358		devinfo = &data->dev[i];
359		if (!devinfo->status)
360			continue;
361		reg = read_write_quad(fd, devinfo->eui, BUGET_REG, 1, 0);
362		fweui2eui64(&devinfo->eui, &eui);
363		eui64_ntoa(&eui, addr, sizeof(addr));
364		printf("%d %s, %08x",
365			devinfo->dst, addr, reg);
366		if (reg > 0) {
367			old = (reg & 0x3f);
368			max = (reg & 0x3f00) >> 8;
369			if (pri_req > max)
370				pri_req =  max;
371			printf(" 0x%x -> 0x%x\n", old, pri_req);
372			read_write_quad(fd, devinfo->eui, BUGET_REG, 0, pri_req);
373		} else {
374			printf("\n");
375		}
376	}
377	free((void *)data);
378}
379
380static void
381parse_bus_info_block(u_int32_t *p)
382{
383	char addr[EUI64_SIZ];
384	struct bus_info *bi;
385	struct eui64 eui;
386
387	bi = (struct bus_info *)p;
388	fweui2eui64(&bi->eui64, &eui);
389	eui64_ntoa(&eui, addr, sizeof(addr));
390	printf("bus_name: 0x%04x\n"
391		"irmc:%d cmc:%d isc:%d bmc:%d pmc:%d\n"
392		"cyc_clk_acc:%d max_rec:%d max_rom:%d\n"
393		"generation:%d link_spd:%d\n"
394		"EUI64: %s\n",
395		bi->bus_name,
396		bi->irmc, bi->cmc, bi->isc, bi->bmc, bi->pmc,
397		bi->cyc_clk_acc, bi->max_rec, bi->max_rom,
398		bi->generation, bi->link_spd,
399		addr);
400}
401
402static int
403get_crom(int fd, int node, void *crom_buf, int len)
404{
405	struct fw_crom_buf buf;
406	int i, error;
407	struct fw_devlstreq *data;
408
409	data = (struct fw_devlstreq *)malloc(sizeof(struct fw_devlstreq));
410	if (data == NULL)
411		err(EX_SOFTWARE, "%s:data malloc", __func__);
412	get_dev(fd, data);
413
414	for (i = 0; i < data->info_len; i++) {
415		if (data->dev[i].dst == node && data->dev[i].eui.lo != 0)
416			break;
417	}
418	if (i == data->info_len)
419		errx(1, "no such node %d.", node);
420	else
421		buf.eui = data->dev[i].eui;
422	free((void *)data);
423
424	buf.len = len;
425	buf.ptr = crom_buf;
426	bzero(crom_buf, len);
427	if ((error = ioctl(fd, FW_GCROM, &buf)) < 0) {
428       		err(EX_IOERR, "%s: ioctl", __func__);
429	}
430
431	return error;
432}
433
434static void
435show_crom(u_int32_t *crom_buf)
436{
437	int i;
438	struct crom_context cc;
439	char *desc, info[256];
440	static const char *key_types = "ICLD";
441	struct csrreg *reg;
442	struct csrdirectory *dir;
443	struct csrhdr *hdr;
444	u_int16_t crc;
445
446	printf("first quad: 0x%08x ", *crom_buf);
447	if (crom_buf[0] == 0) {
448		printf("(Invalid Configuration ROM)\n");
449		return;
450	}
451	hdr = (struct csrhdr *)crom_buf;
452	if (hdr->info_len == 1) {
453		/* minimum ROM */
454		reg = (struct csrreg *)hdr;
455		printf("verndor ID: 0x%06x\n",  reg->val);
456		return;
457	}
458	printf("info_len=%d crc_len=%d crc=0x%04x",
459		hdr->info_len, hdr->crc_len, hdr->crc);
460	crc = crom_crc(crom_buf+1, hdr->crc_len);
461	if (crc == hdr->crc)
462		printf("(OK)\n");
463	else
464		printf("(NG)\n");
465	parse_bus_info_block(crom_buf+1);
466
467	crom_init_context(&cc, crom_buf);
468	dir = cc.stack[0].dir;
469	if (!dir) {
470		printf("no root directory - giving up\n");
471		return;
472	}
473	printf("root_directory: len=0x%04x(%d) crc=0x%04x",
474			dir->crc_len, dir->crc_len, dir->crc);
475	crc = crom_crc((u_int32_t *)&dir->entry[0], dir->crc_len);
476	if (crc == dir->crc)
477		printf("(OK)\n");
478	else
479		printf("(NG)\n");
480	if (dir->crc_len < 1)
481		return;
482	while (cc.depth >= 0) {
483		desc = crom_desc(&cc, info, sizeof(info));
484		reg = crom_get(&cc);
485		for (i = 0; i < cc.depth; i++)
486			printf("\t");
487		printf("%02x(%c:%02x) %06x %s: %s\n",
488			reg->key,
489			key_types[(reg->key & CSRTYPE_MASK)>>6],
490			reg->key & CSRKEY_MASK, reg->val,
491			desc, info);
492		crom_next(&cc);
493	}
494}
495
496#define DUMP_FORMAT	"%08x %08x %08x %08x %08x %08x %08x %08x\n"
497
498static void
499dump_crom(u_int32_t *p)
500{
501	int len=1024, i;
502
503	for (i = 0; i < len/(4*8); i ++) {
504		printf(DUMP_FORMAT,
505			p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
506		p += 8;
507	}
508}
509
510static void
511load_crom(char *filename, u_int32_t *p)
512{
513	FILE *file;
514	int len=1024, i;
515
516	if ((file = fopen(filename, "r")) == NULL)
517		err(1, "load_crom %s", filename);
518	for (i = 0; i < len/(4*8); i ++) {
519		fscanf(file, DUMP_FORMAT,
520			p, p+1, p+2, p+3, p+4, p+5, p+6, p+7);
521		p += 8;
522	}
523	fclose(file);
524}
525
526static void
527show_topology_map(int fd)
528{
529	struct fw_topology_map *tmap;
530	union fw_self_id sid;
531	int i;
532	static const char *port_status[] = {" ", "-", "P", "C"};
533	static const char *pwr_class[] = {" 0W", "15W", "30W", "45W",
534					"-1W", "-2W", "-5W", "-9W"};
535	static const char *speed[] = {"S100", "S200", "S400", "S800"};
536	tmap = malloc(sizeof(struct fw_topology_map));
537	if (tmap == NULL)
538		err(EX_SOFTWARE, "%s:tmap malloc", __func__);
539	if (ioctl(fd, FW_GTPMAP, tmap) < 0) {
540       		err(EX_IOERR, "%s: ioctl", __func__);
541	}
542	printf("crc_len: %d generation:%d node_count:%d sid_count:%d\n",
543		tmap->crc_len, tmap->generation,
544		tmap->node_count, tmap->self_id_count);
545	printf("id link gap_cnt speed delay cIRM power port0 port1 port2"
546		" ini more\n");
547	for (i = 0; i < tmap->crc_len - 2; i++) {
548		sid = tmap->self_id[i];
549		if (sid.p0.sequel) {
550			printf("%02d sequel packet\n", sid.p0.phy_id);
551			continue;
552		}
553		printf("%02d   %2d      %2d  %4s     %d   %3s"
554				"     %s     %s     %s   %d    %d\n",
555			sid.p0.phy_id,
556			sid.p0.link_active,
557			sid.p0.gap_count,
558			speed[sid.p0.phy_speed],
559			sid.p0.contender,
560			pwr_class[sid.p0.power_class],
561			port_status[sid.p0.port0],
562			port_status[sid.p0.port1],
563			port_status[sid.p0.port2],
564			sid.p0.initiated_reset,
565			sid.p0.more_packets
566		);
567	}
568	free(tmap);
569}
570
571static void
572read_phy_registers(int fd, u_int8_t *buf, int offset, int len)
573{
574	struct fw_reg_req_t reg;
575	int i;
576
577	for (i = 0; i < len; i++) {
578		reg.addr = offset + i;
579		if (ioctl(fd, FWOHCI_RDPHYREG, &reg) < 0)
580       			err(EX_IOERR, "%s: ioctl", __func__);
581		buf[i] = (u_int8_t) reg.data;
582		printf("0x%02x ",  reg.data);
583	}
584	printf("\n");
585}
586
587static void
588read_phy_page(int fd, u_int8_t *buf, int page, int port)
589{
590	struct fw_reg_req_t reg;
591
592	reg.addr = 0x7;
593	reg.data = ((page & 7) << 5) | (port & 0xf);
594	if (ioctl(fd, FWOHCI_WRPHYREG, &reg) < 0)
595       		err(EX_IOERR, "%s: ioctl", __func__);
596	read_phy_registers(fd, buf, 8, 8);
597}
598
599static void
600dump_phy_registers(int fd)
601{
602	struct phyreg_base b;
603	struct phyreg_page0 p;
604	struct phyreg_page1 v;
605	int i;
606
607	printf("=== base register ===\n");
608	read_phy_registers(fd, (u_int8_t *)&b, 0, 8);
609	printf(
610	    "Physical_ID:%d  R:%d  CPS:%d\n"
611	    "RHB:%d  IBR:%d  Gap_Count:%d\n"
612	    "Extended:%d Num_Ports:%d\n"
613	    "PHY_Speed:%d Delay:%d\n"
614	    "LCtrl:%d C:%d Jitter:%d Pwr_Class:%d\n"
615	    "WDIE:%d ISBR:%d CTOI:%d CPSI:%d STOI:%d PEI:%d EAA:%d EMC:%d\n"
616	    "Max_Legacy_SPD:%d BLINK:%d Bridge:%d\n"
617	    "Page_Select:%d Port_Select%d\n",
618	    b.phy_id, b.r, b.cps,
619	    b.rhb, b.ibr, b.gap_count,
620	    b.extended, b.num_ports,
621	    b.phy_speed, b.delay,
622	    b.lctrl, b.c, b.jitter, b.pwr_class,
623	    b.wdie, b.isbr, b.ctoi, b.cpsi, b.stoi, b.pei, b.eaa, b.emc,
624	    b.legacy_spd, b.blink, b.bridge,
625	    b.page_select, b.port_select
626	);
627
628	for (i = 0; i < b.num_ports; i ++) {
629		printf("\n=== page 0 port %d ===\n", i);
630		read_phy_page(fd, (u_int8_t *)&p, 0, i);
631		printf(
632		    "Astat:%d BStat:%d Ch:%d Con:%d RXOK:%d Dis:%d\n"
633		    "Negotiated_speed:%d PIE:%d Fault:%d Stanby_fault:%d Disscrm:%d B_Only:%d\n"
634		    "DC_connected:%d Max_port_speed:%d LPP:%d Cable_speed:%d\n"
635		    "Connection_unreliable:%d Beta_mode:%d\n"
636		    "Port_error:0x%x\n"
637		    "Loop_disable:%d In_standby:%d Hard_disable:%d\n",
638		    p.astat, p.bstat, p.ch, p.con, p.rxok, p.dis,
639		    p.negotiated_speed, p.pie, p.fault, p.stanby_fault, p.disscrm, p.b_only,
640		    p.dc_connected, p.max_port_speed, p.lpp, p.cable_speed,
641		    p.connection_unreliable, p.beta_mode,
642		    p.port_error,
643		    p.loop_disable, p.in_standby, p.hard_disable
644		);
645	}
646	printf("\n=== page 1 ===\n");
647	read_phy_page(fd, (u_int8_t *)&v, 1, 0);
648	printf(
649	    "Compliance:%d\n"
650	    "Vendor_ID:0x%06x\n"
651	    "Product_ID:0x%06x\n",
652	    v.compliance,
653	    (v.vendor_id[0] << 16) | (v.vendor_id[1] << 8) | v.vendor_id[2],
654	    (v.product_id[0] << 16) | (v.product_id[1] << 8) | v.product_id[2]
655	);
656}
657
658static int
659open_dev(int *fd, char *devname)
660{
661	if (*fd < 0) {
662		*fd = open(devname, O_RDWR);
663		if (*fd < 0)
664			return(-1);
665
666	}
667	return(0);
668}
669
670#ifndef __HAIKU__
671static void
672sysctl_set_int(const char *name, int val)
673{
674	if (sysctlbyname(name, NULL, NULL, &val, sizeof(int)) < 0)
675		err(1, "sysctl %s failed.", name);
676}
677#endif
678
679static fwmethod *
680detect_recv_fn(int fd, char ich)
681{
682	char *buf;
683	struct fw_isochreq isoreq;
684	struct fw_isobufreq bufreq;
685	int len;
686	u_int32_t *ptr;
687	struct ciphdr *ciph;
688	fwmethod *retfn;
689#define RECV_NUM_PACKET 16
690#define RECV_PACKET_SZ  1024
691
692	bufreq.rx.nchunk = 8;
693	bufreq.rx.npacket = RECV_NUM_PACKET;
694	bufreq.rx.psize = RECV_PACKET_SZ;
695	bufreq.tx.nchunk = 0;
696	bufreq.tx.npacket = 0;
697	bufreq.tx.psize = 0;
698
699	if (ioctl(fd, FW_SSTBUF, &bufreq) < 0)
700		err(EX_IOERR, "%s: ioctl FW_SSTBUF", __func__);
701
702	isoreq.ch = ich & 0x3f;
703	isoreq.tag = (ich >> 6) & 3;
704
705	if (ioctl(fd, FW_SRSTREAM, &isoreq) < 0)
706		err(EX_IOERR, "%s: ioctl FW_SRSTREAM", __func__);
707
708	buf = (char *)malloc(RECV_NUM_PACKET * RECV_PACKET_SZ);
709	if (buf == NULL)
710		err(EX_SOFTWARE, "%s:buf malloc", __func__);
711	/*
712	 * fwdev.c seems to return EIO on error and
713	 * the return value of the last uiomove
714	 * on success.  For now, checking that the
715	 * return is not less than zero should be
716	 * sufficient.  fwdev.c::fw_read() should
717	 * return the total length read, not the value
718	 * of the last uiomove().
719	 */
720	len = read(fd, buf, RECV_NUM_PACKET * RECV_PACKET_SZ);
721	if (len < 0)
722		err(EX_IOERR, "%s: error reading from device", __func__);
723	ptr = (u_int32_t *) buf;
724	ciph = (struct ciphdr *)(ptr + 1);
725
726	switch(ciph->fmt) {
727		case CIP_FMT_DVCR:
728			fprintf(stderr, "Detected DV format on input.\n");
729			retfn = dvrecv;
730			break;
731		case CIP_FMT_MPEG:
732			fprintf(stderr, "Detected MPEG TS format on input.\n");
733			retfn = mpegtsrecv;
734			break;
735		default:
736			errx(1, "Unsupported format for receiving: fmt=0x%x", ciph->fmt);
737	}
738	free(buf);
739	return retfn;
740}
741
742int
743main(int argc, char **argv)
744{
745#define MAX_BOARDS 10
746	u_int32_t crom_buf[1024/4];
747	u_int32_t crom_buf_hex[1024/4];
748	char devbase[64];
749#ifdef __HAIKU__
750	const char *device_string = "/dev/bus/fw/";
751#else
752	const char *device_string = "/dev/fw";
753#endif
754	int fd = -1, ch, len=1024;
755	int32_t current_board = 0;
756 /*
757  * If !command_set, then -u will display the nodes for the board.
758  * This emulates the previous behavior when -u is passed by itself
759  */
760	bool command_set = false;
761	bool open_needed = false;
762	long tmp;
763	struct fw_eui64 eui;
764	struct eui64 target;
765	fwmethod *recvfn = NULL;
766/*
767 * Holders for which functions
768 * to iterate through
769 */
770	bool display_board_only = false;
771	bool display_crom = false;
772	bool send_bus_reset = false;
773	bool display_crom_hex = false;
774	bool load_crom_from_file = false;
775#ifndef __HAIKU__
776	bool set_fwmem_target = false;
777#endif
778	bool dump_topology = false;
779	bool dump_phy_reg = false;
780
781	int32_t priority_budget = -1;
782	int32_t set_root_node = -1;
783	int32_t set_gap_count = -1;
784	int32_t send_link_on = -1;
785	int32_t send_reset_start = -1;
786
787	char *crom_string = NULL;
788	char *crom_string_hex = NULL;
789	char *recv_data = NULL;
790	char *send_data = NULL;
791
792	if (argc < 2) {
793		for (current_board = 0; current_board < MAX_BOARDS; current_board++) {
794			snprintf(devbase, sizeof(devbase), "%s%d", device_string, current_board);
795			if (open_dev(&fd, devbase) < 0) {
796				if (current_board == 0) {
797					usage();
798		  			err(EX_IOERR, "%s: Error opening firewire controller #%d %s",
799						      __func__, current_board, devbase);
800				}
801				return(EIO);
802			}
803			list_dev(fd);
804			close(fd);
805			fd = -1;
806		}
807	}
808       /*
809	* Parse all command line options, then execute requested operations.
810	*/
811#ifdef __HAIKU__
812	while ((ch = getopt(argc, argv, "M:f:g:o:s:b:prtc:d:l:u:R:S:")) != -1) {
813#else
814	while ((ch = getopt(argc, argv, "M:f:g:m:o:s:b:prtc:d:l:u:R:S:")) != -1) {
815#endif
816
817		switch(ch) {
818		case 'b':
819			priority_budget = strtol(optarg, NULL, 0);
820			if (priority_budget < 0 || priority_budget > INT32_MAX)
821				errx(EX_USAGE, "%s: priority_budget out of range: %s", __func__, optarg);
822			command_set = true;
823			open_needed = true;
824			display_board_only = false;
825			break;
826		case 'c':
827			crom_string = malloc(strlen(optarg) + 1);
828			if (crom_string == NULL)
829				err(EX_SOFTWARE, "%s:crom_string malloc", __func__);
830			strcpy(crom_string, optarg);
831			if (strtol(crom_string, NULL, 0) < 0
832				|| strtol(crom_string, NULL, 0) > MAX_BOARDS)
833				errx(EX_USAGE, "%s:Invalid value for node", __func__);
834			display_crom = 1;
835			open_needed = true;
836			command_set = true;
837			display_board_only = false;
838			break;
839		case 'd':
840			crom_string_hex = malloc(strlen(optarg) + 1);
841			if (crom_string_hex == NULL)
842				err(EX_SOFTWARE, "%s:crom_string_hex malloc", __func__);
843			strcpy(crom_string_hex, optarg);
844			display_crom_hex = 1;
845			open_needed = true;
846			command_set = true;
847			display_board_only = false;
848			break;
849		case 'f':
850#define MAX_PHY_CONFIG 0x3f
851			set_root_node = strtol(optarg, NULL, 0);
852			if (set_root_node < 0 || set_root_node > MAX_PHY_CONFIG)
853				errx(EX_USAGE, "%s:set_root_node out of range", __func__);
854			open_needed = true;
855			command_set = true;
856			display_board_only = false;
857			break;
858		case 'g':
859			set_gap_count = strtol(optarg, NULL, 0);
860			if (set_gap_count < 0 || set_gap_count > MAX_PHY_CONFIG)
861				errx(EX_USAGE, "%s:set_gap_count out of range", __func__);
862			open_needed = true;
863			command_set = true;
864			display_board_only = false;
865			break;
866		case 'l':
867			load_crom_from_file = 1;
868			load_crom(optarg, crom_buf);
869			command_set = true;
870			display_board_only = false;
871			break;
872#ifndef __HAIKU__
873		case 'm':
874			set_fwmem_target = 1;
875			open_needed = 0;
876			command_set = true;
877			display_board_only = false;
878			if (eui64_hostton(optarg, &target) != 0 &&
879			    eui64_aton(optarg, &target) != 0)
880				errx(EX_USAGE, "%s: invalid target: %s", __func__, optarg);
881			break;
882#endif
883		case 'o':
884			send_link_on = str2node(fd, optarg);
885			if ( (send_link_on < 0) || (send_link_on > MAX_PHY_CONFIG) )
886				errx(EX_USAGE, "%s: node out of range: %s\n",__func__, optarg);
887			open_needed = true;
888			command_set = true;
889			display_board_only = false;
890			break;
891		case 'p':
892			dump_phy_reg = 1;
893			open_needed = true;
894			command_set = true;
895			display_board_only = false;
896			break;
897		case 'r':
898			send_bus_reset = 1;
899			open_needed = true;
900			command_set = true;
901			display_board_only = false;
902			break;
903		case 's':
904			send_reset_start  = str2node(fd, optarg);
905			if ( (send_reset_start < 0) || (send_reset_start > MAX_PHY_CONFIG) )
906				errx(EX_USAGE, "%s: node out of range: %s\n", __func__, optarg);
907			open_needed = true;
908			command_set = true;
909			display_board_only = false;
910			break;
911		case 't':
912			dump_topology = 1;
913			open_needed = true;
914			command_set = true;
915			display_board_only = false;
916			break;
917		case 'u':
918			if(!command_set)
919				display_board_only = true;
920			current_board = strtol(optarg, NULL, 0);
921			open_needed = true;
922			break;
923		case 'M':
924			switch (optarg[0]) {
925			case 'm':
926				recvfn = mpegtsrecv;
927				break;
928			case 'd':
929				recvfn = dvrecv;
930				break;
931			default:
932				errx(EX_USAGE, "unrecognized method: %s",
933				    optarg);
934			}
935			command_set = true;
936			display_board_only = false;
937			break;
938		case 'R':
939			recv_data = malloc(strlen(optarg)+1);
940			if (recv_data == NULL)
941				err(EX_SOFTWARE, "%s:recv_data malloc", __func__);
942			strcpy(recv_data, optarg);
943			open_needed = false;
944			command_set = true;
945			display_board_only = false;
946			break;
947		case 'S':
948			send_data = malloc(strlen(optarg)+1);
949			if (send_data == NULL)
950				err(EX_SOFTWARE, "%s:send_data malloc", __func__);
951			strcpy(send_data, optarg);
952			open_needed = true;
953			command_set = true;
954			display_board_only = false;
955			break;
956		case '?':
957		default:
958			usage();
959		        warnc(EINVAL, "%s: Unknown command line arguments", __func__);
960			return 0;
961		}
962	} /* end while */
963
964       /*
965	* Catch the error case when the user
966	* executes the command with non ''-''
967	* delimited arguments.
968	* Generate the usage() display and exit.
969	*/
970	if (!command_set && !display_board_only) {
971		usage();
972		warnc(EINVAL, "%s: Unknown command line arguments", __func__);
973		return 0;
974	}
975
976       /*
977	* If -u <bus_number> is passed, execute
978	* command for that card only.
979	*
980	* If -u <bus_number> is not passed, execute
981	* command for card 0 only.
982	*
983	*/
984	if(open_needed){
985		snprintf(devbase, sizeof(devbase), "%s%d", device_string, current_board);
986		if (open_dev(&fd, devbase) < 0) {
987		  err(EX_IOERR, "%s: Error opening firewire controller #%d %s", __func__, current_board, devbase);
988		}
989	}
990	/*
991	 * display the nodes on this board "-u"
992	 * only
993	 */
994	if (display_board_only)
995		list_dev(fd);
996
997	/*
998	 * dump_phy_reg "-p"
999	 */
1000	if (dump_phy_reg)
1001		dump_phy_registers(fd);
1002
1003	/*
1004	 * send a BUS_RESET Event "-r"
1005	 */
1006	if (send_bus_reset) {
1007		if(ioctl(fd, FW_IBUSRST, &tmp) < 0)
1008               		err(EX_IOERR, "%s: Ioctl of bus reset failed for %s", __func__, devbase);
1009	}
1010	/*
1011	 * Print out the CROM for this node "-c"
1012	 */
1013	if (display_crom) {
1014		tmp = str2node(fd, crom_string);
1015		get_crom(fd, tmp, crom_buf, len);
1016		show_crom(crom_buf);
1017		free(crom_string);
1018	}
1019	/*
1020	 * Hex Dump the CROM for this node "-d"
1021	 */
1022	if (display_crom_hex) {
1023		tmp = str2node(fd, crom_string_hex);
1024		get_crom(fd, tmp, crom_buf_hex, len);
1025		dump_crom(crom_buf_hex);
1026		free(crom_string_hex);
1027	}
1028	/*
1029	 * Set Priority Budget to value for this node "-b"
1030	 */
1031	if (priority_budget >= 0)
1032		set_pri_req(fd, priority_budget);
1033
1034	/*
1035	 * Explicitly set the root node of this bus to value "-f"
1036	 */
1037	if (set_root_node >= 0)
1038		send_phy_config(fd, set_root_node, -1);
1039
1040	/*
1041	 * Set the gap count for this card/bus  "-g"
1042	 */
1043	if (set_gap_count >= 0)
1044		send_phy_config(fd, -1, set_gap_count);
1045
1046	/*
1047	 * Load a CROM from a file "-l"
1048	 */
1049	if (load_crom_from_file)
1050		show_crom(crom_buf);
1051#ifndef __HAIKU__
1052	/*
1053	 * Set the fwmem target for a node to argument "-m"
1054	 */
1055	if (set_fwmem_target) {
1056		eui.hi = ntohl(*(u_int32_t*)&(target.octet[0]));
1057		eui.lo = ntohl(*(u_int32_t*)&(target.octet[4]));
1058#if defined(__FreeBSD__)
1059		sysctl_set_int("hw.firewire.fwmem.eui64_hi", eui.hi);
1060		sysctl_set_int("hw.firewire.fwmem.eui64_lo", eui.lo);
1061#elif defined(__NetBSD__)
1062		sysctl_set_int("hw.fwmem.eui64_hi", eui.hi);
1063		sysctl_set_int("hw.fwmem.eui64_lo", eui.lo);
1064#else
1065#warning "You need to add support for your OS"
1066#endif
1067	}
1068#endif
1069	/*
1070	 * Send a link on to this board/bus "-o"
1071	 */
1072	if (send_link_on >= 0)
1073		link_on(fd, send_link_on);
1074
1075	/*
1076	 * Send a reset start to this board/bus "-s"
1077	 */
1078	if (send_reset_start >= 0)
1079		reset_start(fd, send_reset_start);
1080
1081	/*
1082	 * Dump the node topology for this board/bus "-t"
1083	 */
1084	if (dump_topology)
1085		show_topology_map(fd);
1086
1087	/*
1088	 * Receive data file from node "-R"
1089	 */
1090#define TAG	(1<<6)
1091#define CHANNEL	63
1092	if (recv_data != NULL){
1093		if (recvfn == NULL) { /* guess... */
1094			recvfn = detect_recv_fn(fd, TAG | CHANNEL);
1095			close(fd);
1096			fd = -1;
1097		}
1098		snprintf(devbase, sizeof(devbase), "%s%d", device_string, current_board);
1099		if (open_dev(&fd, devbase) < 0)
1100		  err(EX_IOERR, "%s: Error opening firewire controller #%d %s in recv_data\n", __func__, current_board, devbase);
1101		(*recvfn)(fd, recv_data, TAG | CHANNEL, -1);
1102		free(recv_data);
1103	}
1104
1105	/*
1106	 * Send data file to node "-S"
1107	 */
1108	if (send_data != NULL){
1109		dvsend(fd, send_data, TAG | CHANNEL, -1);
1110		free(send_data);
1111	}
1112
1113	if (fd > 0) {
1114		close(fd);
1115		fd = -1;
1116	}
1117	return 0;
1118}
1119