1108441Ssimokawa/*
2108441Ssimokawa * Copyright (C) 2002
3108441Ssimokawa * 	Hidetoshi Shimokawa. All rights reserved.
4108441Ssimokawa *
5108441Ssimokawa * Redistribution and use in source and binary forms, with or without
6108441Ssimokawa * modification, are permitted provided that the following conditions
7108441Ssimokawa * are met:
8108441Ssimokawa * 1. Redistributions of source code must retain the above copyright
9108441Ssimokawa *    notice, this list of conditions and the following disclaimer.
10108441Ssimokawa * 2. Redistributions in binary form must reproduce the above copyright
11108441Ssimokawa *    notice, this list of conditions and the following disclaimer in the
12108441Ssimokawa *    documentation and/or other materials provided with the distribution.
13108441Ssimokawa * 3. All advertising materials mentioning features or use of this software
14108441Ssimokawa *    must display the following acknowledgement:
15108441Ssimokawa *
16108441Ssimokawa *	This product includes software developed by Hidetoshi Shimokawa.
17108441Ssimokawa *
18108441Ssimokawa * 4. Neither the name of the author nor the names of its contributors
19108441Ssimokawa *    may be used to endorse or promote products derived from this software
20108441Ssimokawa *    without specific prior written permission.
21108441Ssimokawa *
22108441Ssimokawa * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23108441Ssimokawa * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24108441Ssimokawa * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25108441Ssimokawa * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26108441Ssimokawa * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27108441Ssimokawa * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28108441Ssimokawa * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29108441Ssimokawa * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30108441Ssimokawa * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31108441Ssimokawa * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32108441Ssimokawa * SUCH DAMAGE.
33108441Ssimokawa */
34108441Ssimokawa
35185996Ssbruno#if defined(__FreeBSD__)
36146442Scharnier#include <sys/cdefs.h>
37146442Scharnier__FBSDID("$FreeBSD$");
38185996Ssbruno#endif
39146442Scharnier
40108441Ssimokawa#include <sys/param.h>
41108441Ssimokawa#include <sys/malloc.h>
42146442Scharnier#include <sys/types.h>
43146442Scharnier#include <sys/sysctl.h>
44108441Ssimokawa#include <sys/socket.h>
45108441Ssimokawa#include <sys/ioctl.h>
46108441Ssimokawa#include <sys/errno.h>
47185996Ssbruno#if defined(__FreeBSD__)
48129760Sbrooks#include <sys/eui64.h>
49108441Ssimokawa#include <dev/firewire/firewire.h>
50108441Ssimokawa#include <dev/firewire/iec13213.h>
51120432Ssimokawa#include <dev/firewire/fwphyreg.h>
52163712Simp#include <dev/firewire/iec68113.h>
53185996Ssbruno#elif defined(__NetBSD__)
54185996Ssbruno#include "eui64.h"
55185996Ssbruno#include <dev/ieee1394/firewire.h>
56185996Ssbruno#include <dev/ieee1394/iec13213.h>
57185996Ssbruno#include <dev/ieee1394/fwphyreg.h>
58185996Ssbruno#include <dev/ieee1394/iec68113.h>
59185996Ssbruno#else
60185996Ssbruno#warning "You need to add support for your OS"
61185996Ssbruno#endif
62108441Ssimokawa
63185996Ssbruno
64108441Ssimokawa#include <netinet/in.h>
65108441Ssimokawa#include <fcntl.h>
66108441Ssimokawa#include <stdio.h>
67108441Ssimokawa#include <err.h>
68108441Ssimokawa#include <stdlib.h>
69108441Ssimokawa#include <string.h>
70163712Simp#include <sysexits.h>
71108441Ssimokawa#include <unistd.h>
72182911Ssbruno#include <stdint.h>
73182911Ssbruno#include <stdbool.h>
74163712Simp#include "fwmethods.h"
75108441Ssimokawa
76163712Simpstatic void sysctl_set_int(const char *, int);
77109737Ssimokawa
78108657Ssimokawastatic void
79108441Ssimokawausage(void)
80108441Ssimokawa{
81109787Ssimokawa	fprintf(stderr,
82185996Ssbruno		"%s [-u bus_num] [-prt] [-c node] [-d node] [-o node] [-s node]\n"
83182911Ssbruno		"\t  [-l file] [-g gap_count] [-f force_root ] [-b pri_req]\n"
84182911Ssbruno		"\t  [-M mode] [-R filename] [-S filename] [-m EUI64 | hostname]\n"
85118457Ssimokawa		"\t-u: specify bus number\n"
86182911Ssbruno		"\t-p: Display current PHY register settings\n"
87114217Ssimokawa		"\t-r: bus reset\n"
88114217Ssimokawa		"\t-t: read topology map\n"
89182911Ssbruno		"\t-c: read configuration ROM\n"
90114217Ssimokawa		"\t-d: hex dump of configuration ROM\n"
91182911Ssbruno		"\t-o: send link-on packet to the node\n"
92182911Ssbruno		"\t-s: write RESET_START register on the node\n"
93114217Ssimokawa		"\t-l: load and parse hex dump file of configuration ROM\n"
94185996Ssbruno		"\t-g: set gap count\n"
95185996Ssbruno		"\t-f: force root node\n"
96182911Ssbruno		"\t-b: set PRIORITY_BUDGET register on all supported nodes\n"
97182911Ssbruno		"\t-M: specify dv or mpeg\n"
98163712Simp		"\t-R: Receive DV or MPEG TS stream\n"
99137028Ssimokawa		"\t-S: Send DV stream\n"
100185996Ssbruno		"\t-m: set fwmem target\n"
101185996Ssbruno		, getprogname() );
102185996Ssbruno	fprintf(stderr, "\n");
103108441Ssimokawa}
104108441Ssimokawa
105129760Sbrooksstatic void
106129760Sbrooksfweui2eui64(const struct fw_eui64 *fweui, struct eui64 *eui)
107129760Sbrooks{
108129760Sbrooks	*(u_int32_t*)&(eui->octet[0]) = htonl(fweui->hi);
109129760Sbrooks	*(u_int32_t*)&(eui->octet[4]) = htonl(fweui->lo);
110129760Sbrooks}
111129760Sbrooks
112182911Ssbrunostatic void
113182911Ssbrunoget_dev(int fd, struct fw_devlstreq *data)
114108441Ssimokawa{
115109814Ssimokawa	if (data == NULL)
116182911Ssbruno		err(EX_SOFTWARE, "%s: data malloc", __func__);
117108441Ssimokawa	if( ioctl(fd, FW_GDEVLST, data) < 0) {
118182911Ssbruno       			err(EX_IOERR, "%s: ioctl", __func__);
119108441Ssimokawa	}
120108441Ssimokawa}
121108441Ssimokawa
122129760Sbrooksstatic int
123129760Sbrooksstr2node(int fd, const char *nodestr)
124129760Sbrooks{
125129760Sbrooks	struct eui64 eui, tmpeui;
126129760Sbrooks	struct fw_devlstreq *data;
127129760Sbrooks	char *endptr;
128129760Sbrooks	int i, node;
129129760Sbrooks
130129760Sbrooks	if (nodestr == '\0')
131129760Sbrooks		return (-1);
132129760Sbrooks
133129760Sbrooks	/*
134129760Sbrooks	 * Deal with classic node specifications.
135129760Sbrooks	 */
136129760Sbrooks	node = strtol(nodestr, &endptr, 0);
137129760Sbrooks	if (*endptr == '\0')
138129760Sbrooks		goto gotnode;
139129760Sbrooks
140129760Sbrooks	/*
141129760Sbrooks	 * Try to get an eui and match it against available nodes.
142129760Sbrooks	 */
143129760Sbrooks	if (eui64_hostton(nodestr, &eui) != 0 && eui64_aton(nodestr, &eui) != 0)
144129760Sbrooks		return (-1);
145129760Sbrooks
146182911Ssbruno	data = (struct fw_devlstreq *)malloc(sizeof(struct fw_devlstreq));
147182911Ssbruno	if (data == NULL)
148182911Ssbruno		err(EX_SOFTWARE, "%s: data malloc", __func__);
149182911Ssbruno	get_dev(fd,data);
150129760Sbrooks
151129760Sbrooks	for (i = 0; i < data->info_len; i++) {
152129760Sbrooks		fweui2eui64(&data->dev[i].eui, &tmpeui);
153129760Sbrooks		if (memcmp(&eui, &tmpeui, sizeof(struct eui64)) == 0) {
154129760Sbrooks			node = data->dev[i].dst;
155228790Seadler			free(data);
156129760Sbrooks			goto gotnode;
157129760Sbrooks		}
158129760Sbrooks	}
159182911Ssbruno	if (i >= data->info_len) {
160182911Ssbruno		if (data != NULL)
161182911Ssbruno			free(data);
162129760Sbrooks		return (-1);
163182911Ssbruno	}
164129760Sbrooks
165129760Sbrooksgotnode:
166129760Sbrooks	if (node < 0 || node > 63)
167129760Sbrooks		return (-1);
168129760Sbrooks	else
169129760Sbrooks		return (node);
170129760Sbrooks}
171129760Sbrooks
172108657Ssimokawastatic void
173108441Ssimokawalist_dev(int fd)
174108441Ssimokawa{
175109814Ssimokawa	struct fw_devlstreq *data;
176109814Ssimokawa	struct fw_devinfo *devinfo;
177129760Sbrooks	struct eui64 eui;
178176810Ssimokawa	char addr[EUI64_SIZ], hostname[40];
179108441Ssimokawa	int i;
180108441Ssimokawa
181182911Ssbruno	data = (struct fw_devlstreq *)malloc(sizeof(struct fw_devlstreq));
182182911Ssbruno	if (data == NULL)
183182911Ssbruno		err(EX_SOFTWARE, "%s:data malloc", __func__);
184182911Ssbruno	get_dev(fd, data);
185109814Ssimokawa	printf("%d devices (info_len=%d)\n", data->n, data->info_len);
186176810Ssimokawa	printf("node           EUI64          status    hostname\n");
187109814Ssimokawa	for (i = 0; i < data->info_len; i++) {
188109814Ssimokawa		devinfo = &data->dev[i];
189129760Sbrooks		fweui2eui64(&devinfo->eui, &eui);
190129760Sbrooks		eui64_ntoa(&eui, addr, sizeof(addr));
191176810Ssimokawa	        if (eui64_ntohost(hostname, sizeof(hostname), &eui))
192176810Ssimokawa			hostname[0] = 0;
193176810Ssimokawa		printf("%4d  %s %6d    %s\n",
194110578Ssimokawa			(devinfo->status || i == 0) ? devinfo->dst : -1,
195129760Sbrooks			addr,
196176810Ssimokawa			devinfo->status,
197176810Ssimokawa			hostname
198108441Ssimokawa		);
199108441Ssimokawa	}
200109814Ssimokawa	free((void *)data);
201108441Ssimokawa}
202108441Ssimokawa
203108657Ssimokawastatic u_int32_t
204146442Scharnierread_write_quad(int fd, struct fw_eui64 eui, u_int32_t addr_lo, int readmode, u_int32_t data)
205108441Ssimokawa{
206108441Ssimokawa        struct fw_asyreq *asyreq;
207108441Ssimokawa	u_int32_t *qld, res;
208108441Ssimokawa
209108441Ssimokawa        asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 16);
210182911Ssbruno	if (asyreq == NULL)
211182911Ssbruno		err(EX_SOFTWARE, "%s:asyreq malloc", __func__);
212108441Ssimokawa	asyreq->req.len = 16;
213114217Ssimokawa#if 0
214114217Ssimokawa	asyreq->req.type = FWASREQNODE;
215114217Ssimokawa	asyreq->pkt.mode.rreqq.dst = FWLOCALBUS | node;
216114217Ssimokawa#else
217108441Ssimokawa	asyreq->req.type = FWASREQEUI;
218108441Ssimokawa	asyreq->req.dst.eui = eui;
219108441Ssimokawa#endif
220108441Ssimokawa	asyreq->pkt.mode.rreqq.tlrt = 0;
221146442Scharnier	if (readmode)
222108441Ssimokawa		asyreq->pkt.mode.rreqq.tcode = FWTCODE_RREQQ;
223108441Ssimokawa	else
224108441Ssimokawa		asyreq->pkt.mode.rreqq.tcode = FWTCODE_WREQQ;
225108441Ssimokawa
226113584Ssimokawa	asyreq->pkt.mode.rreqq.dest_hi = 0xffff;
227113584Ssimokawa	asyreq->pkt.mode.rreqq.dest_lo = addr_lo;
228108441Ssimokawa
229108441Ssimokawa	qld = (u_int32_t *)&asyreq->pkt;
230146442Scharnier	if (!readmode)
231176810Ssimokawa		asyreq->pkt.mode.wreqq.data = htonl(data);
232108441Ssimokawa
233108441Ssimokawa	if (ioctl(fd, FW_ASYREQ, asyreq) < 0) {
234182911Ssbruno       		err(EX_IOERR, "%s: ioctl", __func__);
235108441Ssimokawa	}
236108441Ssimokawa	res = qld[3];
237108441Ssimokawa	free(asyreq);
238146442Scharnier	if (readmode)
239108441Ssimokawa		return ntohl(res);
240108441Ssimokawa	else
241108441Ssimokawa		return 0;
242108441Ssimokawa}
243108657Ssimokawa
244182911Ssbruno/*
245182911Ssbruno * Send a PHY Config Packet
246182911Ssbruno * ieee 1394a-2005 4.3.4.3
247182911Ssbruno *
248182911Ssbruno * Message ID   Root ID    R  T   Gap Count
249182911Ssbruno * 00(2 bits)   (6 bits)   1  1   (6 bits)
250182911Ssbruno *
251182911Ssbruno * if "R" is set, then Root ID will be the next
252182911Ssbruno * root node upon the next bus reset.
253182911Ssbruno * if "T" is set, then Gap Count will be the
254182911Ssbruno * value that all nodes use for their Gap Count
255182911Ssbruno * if "R" and "T" are not set, then this message
256182911Ssbruno * is either ignored or interpreted as an extended
257182911Ssbruno * PHY config Packet as per 1394a-2005 4.3.4.4
258182911Ssbruno */
259108657Ssimokawastatic void
260108441Ssimokawasend_phy_config(int fd, int root_node, int gap_count)
261108441Ssimokawa{
262108441Ssimokawa        struct fw_asyreq *asyreq;
263108441Ssimokawa
264108441Ssimokawa	asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 12);
265182911Ssbruno	if (asyreq == NULL)
266182911Ssbruno		err(EX_SOFTWARE, "%s:asyreq malloc", __func__);
267108441Ssimokawa	asyreq->req.len = 12;
268108441Ssimokawa	asyreq->req.type = FWASREQNODE;
269108441Ssimokawa	asyreq->pkt.mode.ld[0] = 0;
270108441Ssimokawa	asyreq->pkt.mode.ld[1] = 0;
271108441Ssimokawa	asyreq->pkt.mode.common.tcode = FWTCODE_PHY;
272108441Ssimokawa	if (root_node >= 0)
273182911Ssbruno		asyreq->pkt.mode.ld[1] |= ((root_node << 24) | (1 << 23));
274108441Ssimokawa	if (gap_count >= 0)
275182911Ssbruno		asyreq->pkt.mode.ld[1] |= ((1 << 22) | (gap_count << 16));
276108441Ssimokawa	asyreq->pkt.mode.ld[2] = ~asyreq->pkt.mode.ld[1];
277108441Ssimokawa
278108441Ssimokawa	printf("send phy_config root_node=%d gap_count=%d\n",
279108441Ssimokawa						root_node, gap_count);
280108441Ssimokawa
281114274Ssimokawa	if (ioctl(fd, FW_ASYREQ, asyreq) < 0)
282182911Ssbruno       		err(EX_IOERR, "%s: ioctl", __func__);
283114274Ssimokawa	free(asyreq);
284108441Ssimokawa}
285108441Ssimokawa
286108657Ssimokawastatic void
287182911Ssbrunolink_on(int fd, int node)
288114217Ssimokawa{
289114217Ssimokawa        struct fw_asyreq *asyreq;
290114217Ssimokawa
291114217Ssimokawa	asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 12);
292182911Ssbruno	if (asyreq == NULL)
293182911Ssbruno		err(EX_SOFTWARE, "%s:asyreq malloc", __func__);
294114217Ssimokawa	asyreq->req.len = 12;
295114217Ssimokawa	asyreq->req.type = FWASREQNODE;
296114217Ssimokawa	asyreq->pkt.mode.common.tcode = FWTCODE_PHY;
297114217Ssimokawa	asyreq->pkt.mode.ld[1] |= (1 << 30) | ((node & 0x3f) << 24);
298114217Ssimokawa	asyreq->pkt.mode.ld[2] = ~asyreq->pkt.mode.ld[1];
299114217Ssimokawa
300114274Ssimokawa	if (ioctl(fd, FW_ASYREQ, asyreq) < 0)
301182911Ssbruno       		err(EX_IOERR, "%s: ioctl", __func__);
302114274Ssimokawa	free(asyreq);
303114217Ssimokawa}
304114217Ssimokawa
305114217Ssimokawastatic void
306114217Ssimokawareset_start(int fd, int node)
307114217Ssimokawa{
308114217Ssimokawa        struct fw_asyreq *asyreq;
309114217Ssimokawa
310114217Ssimokawa	asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 16);
311182911Ssbruno	if (asyreq == NULL)
312182911Ssbruno		err(EX_SOFTWARE, "%s:asyreq malloc", __func__);
313114217Ssimokawa	asyreq->req.len = 16;
314114217Ssimokawa	asyreq->req.type = FWASREQNODE;
315114217Ssimokawa	asyreq->pkt.mode.wreqq.dst = FWLOCALBUS | (node & 0x3f);
316114217Ssimokawa	asyreq->pkt.mode.wreqq.tlrt = 0;
317114217Ssimokawa	asyreq->pkt.mode.wreqq.tcode = FWTCODE_WREQQ;
318114217Ssimokawa
319114217Ssimokawa	asyreq->pkt.mode.wreqq.dest_hi = 0xffff;
320114217Ssimokawa	asyreq->pkt.mode.wreqq.dest_lo = 0xf0000000 | RESET_START;
321114217Ssimokawa
322114217Ssimokawa	asyreq->pkt.mode.wreqq.data = htonl(0x1);
323114217Ssimokawa
324114274Ssimokawa	if (ioctl(fd, FW_ASYREQ, asyreq) < 0)
325182911Ssbruno       		err(EX_IOERR, "%s: ioctl", __func__);
326114274Ssimokawa	free(asyreq);
327114217Ssimokawa}
328114217Ssimokawa
329114217Ssimokawastatic void
330163712Simpset_pri_req(int fd, u_int32_t pri_req)
331108441Ssimokawa{
332109814Ssimokawa	struct fw_devlstreq *data;
333109814Ssimokawa	struct fw_devinfo *devinfo;
334129760Sbrooks	struct eui64 eui;
335129760Sbrooks	char addr[EUI64_SIZ];
336108441Ssimokawa	u_int32_t max, reg, old;
337108441Ssimokawa	int i;
338108441Ssimokawa
339182911Ssbruno	data = (struct fw_devlstreq *)malloc(sizeof(struct fw_devlstreq));
340182911Ssbruno	if (data == NULL)
341182911Ssbruno		err(EX_SOFTWARE, "%s:data malloc", __func__);
342182911Ssbruno	get_dev(fd, data);
343108441Ssimokawa#define BUGET_REG 0xf0000218
344109814Ssimokawa	for (i = 0; i < data->info_len; i++) {
345109814Ssimokawa		devinfo = &data->dev[i];
346109814Ssimokawa		if (!devinfo->status)
347108441Ssimokawa			continue;
348109814Ssimokawa		reg = read_write_quad(fd, devinfo->eui, BUGET_REG, 1, 0);
349129760Sbrooks		fweui2eui64(&devinfo->eui, &eui);
350129760Sbrooks		eui64_ntoa(&eui, addr, sizeof(addr));
351129760Sbrooks		printf("%d %s, %08x",
352129760Sbrooks			devinfo->dst, addr, reg);
353163712Simp		if (reg > 0) {
354108441Ssimokawa			old = (reg & 0x3f);
355108441Ssimokawa			max = (reg & 0x3f00) >> 8;
356108441Ssimokawa			if (pri_req > max)
357108441Ssimokawa				pri_req =  max;
358108441Ssimokawa			printf(" 0x%x -> 0x%x\n", old, pri_req);
359109814Ssimokawa			read_write_quad(fd, devinfo->eui, BUGET_REG, 0, pri_req);
360108441Ssimokawa		} else {
361108441Ssimokawa			printf("\n");
362108441Ssimokawa		}
363108441Ssimokawa	}
364109814Ssimokawa	free((void *)data);
365108441Ssimokawa}
366108441Ssimokawa
367108657Ssimokawastatic void
368163712Simpparse_bus_info_block(u_int32_t *p)
369108441Ssimokawa{
370129760Sbrooks	char addr[EUI64_SIZ];
371116141Ssimokawa	struct bus_info *bi;
372129760Sbrooks	struct eui64 eui;
373108441Ssimokawa
374116141Ssimokawa	bi = (struct bus_info *)p;
375129760Sbrooks	fweui2eui64(&bi->eui64, &eui);
376129760Sbrooks	eui64_ntoa(&eui, addr, sizeof(addr));
377116141Ssimokawa	printf("bus_name: 0x%04x\n"
378116141Ssimokawa		"irmc:%d cmc:%d isc:%d bmc:%d pmc:%d\n"
379116141Ssimokawa		"cyc_clk_acc:%d max_rec:%d max_rom:%d\n"
380116141Ssimokawa		"generation:%d link_spd:%d\n"
381129760Sbrooks		"EUI64: %s\n",
382116141Ssimokawa		bi->bus_name,
383116141Ssimokawa		bi->irmc, bi->cmc, bi->isc, bi->bmc, bi->pmc,
384116141Ssimokawa		bi->cyc_clk_acc, bi->max_rec, bi->max_rom,
385116141Ssimokawa		bi->generation, bi->link_spd,
386129760Sbrooks		addr);
387108441Ssimokawa}
388108441Ssimokawa
389108657Ssimokawastatic int
390108441Ssimokawaget_crom(int fd, int node, void *crom_buf, int len)
391108441Ssimokawa{
392108441Ssimokawa	struct fw_crom_buf buf;
393109991Ssimokawa	int i, error;
394109814Ssimokawa	struct fw_devlstreq *data;
395108441Ssimokawa
396182911Ssbruno	data = (struct fw_devlstreq *)malloc(sizeof(struct fw_devlstreq));
397182911Ssbruno	if (data == NULL)
398182911Ssbruno		err(EX_SOFTWARE, "%s:data malloc", __func__);
399182911Ssbruno	get_dev(fd, data);
400108441Ssimokawa
401109814Ssimokawa	for (i = 0; i < data->info_len; i++) {
402109814Ssimokawa		if (data->dev[i].dst == node && data->dev[i].eui.lo != 0)
403108441Ssimokawa			break;
404108441Ssimokawa	}
405109814Ssimokawa	if (i == data->info_len)
406109814Ssimokawa		errx(1, "no such node %d.", node);
407109814Ssimokawa	else
408109814Ssimokawa		buf.eui = data->dev[i].eui;
409109814Ssimokawa	free((void *)data);
410108441Ssimokawa
411108441Ssimokawa	buf.len = len;
412108441Ssimokawa	buf.ptr = crom_buf;
413117474Ssimokawa	bzero(crom_buf, len);
414108441Ssimokawa	if ((error = ioctl(fd, FW_GCROM, &buf)) < 0) {
415182911Ssbruno       		err(EX_IOERR, "%s: ioctl", __func__);
416108441Ssimokawa	}
417109814Ssimokawa
418108441Ssimokawa	return error;
419108441Ssimokawa}
420108441Ssimokawa
421108657Ssimokawastatic void
422108441Ssimokawashow_crom(u_int32_t *crom_buf)
423108441Ssimokawa{
424108441Ssimokawa	int i;
425108441Ssimokawa	struct crom_context cc;
426108441Ssimokawa	char *desc, info[256];
427146442Scharnier	static const char *key_types = "ICLD";
428108441Ssimokawa	struct csrreg *reg;
429108441Ssimokawa	struct csrdirectory *dir;
430108441Ssimokawa	struct csrhdr *hdr;
431113584Ssimokawa	u_int16_t crc;
432108441Ssimokawa
433113584Ssimokawa	printf("first quad: 0x%08x ", *crom_buf);
434136845Ssimokawa	if (crom_buf[0] == 0) {
435136845Ssimokawa		printf("(Invalid Configuration ROM)\n");
436136845Ssimokawa		return;
437136845Ssimokawa	}
438108441Ssimokawa	hdr = (struct csrhdr *)crom_buf;
439108441Ssimokawa	if (hdr->info_len == 1) {
440108441Ssimokawa		/* minimum ROM */
441108441Ssimokawa		reg = (struct csrreg *)hdr;
442108441Ssimokawa		printf("verndor ID: 0x%06x\n",  reg->val);
443108441Ssimokawa		return;
444108441Ssimokawa	}
445113584Ssimokawa	printf("info_len=%d crc_len=%d crc=0x%04x",
446113584Ssimokawa		hdr->info_len, hdr->crc_len, hdr->crc);
447113584Ssimokawa	crc = crom_crc(crom_buf+1, hdr->crc_len);
448113584Ssimokawa	if (crc == hdr->crc)
449113584Ssimokawa		printf("(OK)\n");
450113584Ssimokawa	else
451113584Ssimokawa		printf("(NG)\n");
452163712Simp	parse_bus_info_block(crom_buf+1);
453108441Ssimokawa
454108441Ssimokawa	crom_init_context(&cc, crom_buf);
455108441Ssimokawa	dir = cc.stack[0].dir;
456129604Sdfr	if (!dir) {
457129604Sdfr		printf("no root directory - giving up\n");
458129604Sdfr		return;
459129604Sdfr	}
460113584Ssimokawa	printf("root_directory: len=0x%04x(%d) crc=0x%04x",
461108441Ssimokawa			dir->crc_len, dir->crc_len, dir->crc);
462113584Ssimokawa	crc = crom_crc((u_int32_t *)&dir->entry[0], dir->crc_len);
463113584Ssimokawa	if (crc == dir->crc)
464113584Ssimokawa		printf("(OK)\n");
465113584Ssimokawa	else
466113584Ssimokawa		printf("(NG)\n");
467108657Ssimokawa	if (dir->crc_len < 1)
468108657Ssimokawa		return;
469108441Ssimokawa	while (cc.depth >= 0) {
470108441Ssimokawa		desc = crom_desc(&cc, info, sizeof(info));
471108441Ssimokawa		reg = crom_get(&cc);
472108441Ssimokawa		for (i = 0; i < cc.depth; i++)
473108441Ssimokawa			printf("\t");
474108441Ssimokawa		printf("%02x(%c:%02x) %06x %s: %s\n",
475108441Ssimokawa			reg->key,
476108441Ssimokawa			key_types[(reg->key & CSRTYPE_MASK)>>6],
477108441Ssimokawa			reg->key & CSRKEY_MASK, reg->val,
478108441Ssimokawa			desc, info);
479108441Ssimokawa		crom_next(&cc);
480108441Ssimokawa	}
481108441Ssimokawa}
482108441Ssimokawa
483108441Ssimokawa#define DUMP_FORMAT	"%08x %08x %08x %08x %08x %08x %08x %08x\n"
484108441Ssimokawa
485108657Ssimokawastatic void
486108441Ssimokawadump_crom(u_int32_t *p)
487108441Ssimokawa{
488108441Ssimokawa	int len=1024, i;
489108441Ssimokawa
490108441Ssimokawa	for (i = 0; i < len/(4*8); i ++) {
491108441Ssimokawa		printf(DUMP_FORMAT,
492108441Ssimokawa			p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
493108441Ssimokawa		p += 8;
494108441Ssimokawa	}
495108441Ssimokawa}
496108441Ssimokawa
497108657Ssimokawastatic void
498108441Ssimokawaload_crom(char *filename, u_int32_t *p)
499108441Ssimokawa{
500108441Ssimokawa	FILE *file;
501108441Ssimokawa	int len=1024, i;
502108441Ssimokawa
503108441Ssimokawa	if ((file = fopen(filename, "r")) == NULL)
504185996Ssbruno	  err(1, "load_crom %s", filename);
505108441Ssimokawa	for (i = 0; i < len/(4*8); i ++) {
506108441Ssimokawa		fscanf(file, DUMP_FORMAT,
507108441Ssimokawa			p, p+1, p+2, p+3, p+4, p+5, p+6, p+7);
508108441Ssimokawa		p += 8;
509108441Ssimokawa	}
510216321Skevlo	fclose(file);
511108441Ssimokawa}
512108441Ssimokawa
513108441Ssimokawastatic void
514108441Ssimokawashow_topology_map(int fd)
515108441Ssimokawa{
516108441Ssimokawa	struct fw_topology_map *tmap;
517108441Ssimokawa	union fw_self_id sid;
518108441Ssimokawa	int i;
519146442Scharnier	static const char *port_status[] = {" ", "-", "P", "C"};
520146442Scharnier	static const char *pwr_class[] = {" 0W", "15W", "30W", "45W",
521108441Ssimokawa					"-1W", "-2W", "-5W", "-9W"};
522146442Scharnier	static const char *speed[] = {"S100", "S200", "S400", "S800"};
523108441Ssimokawa	tmap = malloc(sizeof(struct fw_topology_map));
524108441Ssimokawa	if (tmap == NULL)
525182911Ssbruno		err(EX_SOFTWARE, "%s:tmap malloc", __func__);
526108441Ssimokawa	if (ioctl(fd, FW_GTPMAP, tmap) < 0) {
527182911Ssbruno       		err(EX_IOERR, "%s: ioctl", __func__);
528108441Ssimokawa	}
529108441Ssimokawa	printf("crc_len: %d generation:%d node_count:%d sid_count:%d\n",
530108441Ssimokawa		tmap->crc_len, tmap->generation,
531108441Ssimokawa		tmap->node_count, tmap->self_id_count);
532108441Ssimokawa	printf("id link gap_cnt speed delay cIRM power port0 port1 port2"
533108441Ssimokawa		" ini more\n");
534108441Ssimokawa	for (i = 0; i < tmap->crc_len - 2; i++) {
535108441Ssimokawa		sid = tmap->self_id[i];
536108441Ssimokawa		if (sid.p0.sequel) {
537108441Ssimokawa			printf("%02d sequel packet\n", sid.p0.phy_id);
538108441Ssimokawa			continue;
539108441Ssimokawa		}
540188742Ssbruno		printf("%02d   %2d      %2d  %4s     %d   %3s"
541108441Ssimokawa				"     %s     %s     %s   %d    %d\n",
542108441Ssimokawa			sid.p0.phy_id,
543108441Ssimokawa			sid.p0.link_active,
544108441Ssimokawa			sid.p0.gap_count,
545108441Ssimokawa			speed[sid.p0.phy_speed],
546108441Ssimokawa			sid.p0.contender,
547108441Ssimokawa			pwr_class[sid.p0.power_class],
548108441Ssimokawa			port_status[sid.p0.port0],
549108441Ssimokawa			port_status[sid.p0.port1],
550108441Ssimokawa			port_status[sid.p0.port2],
551108441Ssimokawa			sid.p0.initiated_reset,
552108441Ssimokawa			sid.p0.more_packets
553108441Ssimokawa		);
554108441Ssimokawa	}
555108441Ssimokawa	free(tmap);
556108441Ssimokawa}
557108441Ssimokawa
558118457Ssimokawastatic void
559120432Ssimokawaread_phy_registers(int fd, u_int8_t *buf, int offset, int len)
560120432Ssimokawa{
561120432Ssimokawa	struct fw_reg_req_t reg;
562120432Ssimokawa	int i;
563120432Ssimokawa
564120432Ssimokawa	for (i = 0; i < len; i++) {
565120432Ssimokawa		reg.addr = offset + i;
566120432Ssimokawa		if (ioctl(fd, FWOHCI_RDPHYREG, &reg) < 0)
567182911Ssbruno       			err(EX_IOERR, "%s: ioctl", __func__);
568120432Ssimokawa		buf[i] = (u_int8_t) reg.data;
569120432Ssimokawa		printf("0x%02x ",  reg.data);
570120432Ssimokawa	}
571120432Ssimokawa	printf("\n");
572120432Ssimokawa}
573120432Ssimokawa
574120432Ssimokawastatic void
575120432Ssimokawaread_phy_page(int fd, u_int8_t *buf, int page, int port)
576120432Ssimokawa{
577120432Ssimokawa	struct fw_reg_req_t reg;
578120432Ssimokawa
579120432Ssimokawa	reg.addr = 0x7;
580120432Ssimokawa	reg.data = ((page & 7) << 5) | (port & 0xf);
581120432Ssimokawa	if (ioctl(fd, FWOHCI_WRPHYREG, &reg) < 0)
582182911Ssbruno       		err(EX_IOERR, "%s: ioctl", __func__);
583120432Ssimokawa	read_phy_registers(fd, buf, 8, 8);
584120432Ssimokawa}
585120432Ssimokawa
586120432Ssimokawastatic void
587120432Ssimokawadump_phy_registers(int fd)
588120432Ssimokawa{
589120432Ssimokawa	struct phyreg_base b;
590120432Ssimokawa	struct phyreg_page0 p;
591120432Ssimokawa	struct phyreg_page1 v;
592120432Ssimokawa	int i;
593120432Ssimokawa
594120432Ssimokawa	printf("=== base register ===\n");
595120432Ssimokawa	read_phy_registers(fd, (u_int8_t *)&b, 0, 8);
596120432Ssimokawa	printf(
597120432Ssimokawa	    "Physical_ID:%d  R:%d  CPS:%d\n"
598120432Ssimokawa	    "RHB:%d  IBR:%d  Gap_Count:%d\n"
599120432Ssimokawa	    "Extended:%d Num_Ports:%d\n"
600120432Ssimokawa	    "PHY_Speed:%d Delay:%d\n"
601120432Ssimokawa	    "LCtrl:%d C:%d Jitter:%d Pwr_Class:%d\n"
602120432Ssimokawa	    "WDIE:%d ISBR:%d CTOI:%d CPSI:%d STOI:%d PEI:%d EAA:%d EMC:%d\n"
603120432Ssimokawa	    "Max_Legacy_SPD:%d BLINK:%d Bridge:%d\n"
604120432Ssimokawa	    "Page_Select:%d Port_Select%d\n",
605120432Ssimokawa	    b.phy_id, b.r, b.cps,
606120432Ssimokawa	    b.rhb, b.ibr, b.gap_count,
607120432Ssimokawa	    b.extended, b.num_ports,
608120432Ssimokawa	    b.phy_speed, b.delay,
609120432Ssimokawa	    b.lctrl, b.c, b.jitter, b.pwr_class,
610120432Ssimokawa	    b.wdie, b.isbr, b.ctoi, b.cpsi, b.stoi, b.pei, b.eaa, b.emc,
611120432Ssimokawa	    b.legacy_spd, b.blink, b.bridge,
612120432Ssimokawa	    b.page_select, b.port_select
613120432Ssimokawa	);
614120432Ssimokawa
615120432Ssimokawa	for (i = 0; i < b.num_ports; i ++) {
616120432Ssimokawa		printf("\n=== page 0 port %d ===\n", i);
617120432Ssimokawa		read_phy_page(fd, (u_int8_t *)&p, 0, i);
618120432Ssimokawa		printf(
619120432Ssimokawa		    "Astat:%d BStat:%d Ch:%d Con:%d RXOK:%d Dis:%d\n"
620120432Ssimokawa		    "Negotiated_speed:%d PIE:%d Fault:%d Stanby_fault:%d Disscrm:%d B_Only:%d\n"
621120432Ssimokawa		    "DC_connected:%d Max_port_speed:%d LPP:%d Cable_speed:%d\n"
622120432Ssimokawa		    "Connection_unreliable:%d Beta_mode:%d\n"
623120432Ssimokawa		    "Port_error:0x%x\n"
624120432Ssimokawa		    "Loop_disable:%d In_standby:%d Hard_disable:%d\n",
625120432Ssimokawa		    p.astat, p.bstat, p.ch, p.con, p.rxok, p.dis,
626120432Ssimokawa		    p.negotiated_speed, p.pie, p.fault, p.stanby_fault, p.disscrm, p.b_only,
627120432Ssimokawa		    p.dc_connected, p.max_port_speed, p.lpp, p.cable_speed,
628120432Ssimokawa		    p.connection_unreliable, p.beta_mode,
629120432Ssimokawa		    p.port_error,
630120432Ssimokawa		    p.loop_disable, p.in_standby, p.hard_disable
631120432Ssimokawa		);
632120432Ssimokawa	}
633120432Ssimokawa	printf("\n=== page 1 ===\n");
634120432Ssimokawa	read_phy_page(fd, (u_int8_t *)&v, 1, 0);
635120432Ssimokawa	printf(
636120432Ssimokawa	    "Compliance:%d\n"
637120432Ssimokawa	    "Vendor_ID:0x%06x\n"
638120432Ssimokawa	    "Product_ID:0x%06x\n",
639120432Ssimokawa	    v.compliance,
640120432Ssimokawa	    (v.vendor_id[0] << 16) | (v.vendor_id[1] << 8) | v.vendor_id[2],
641120432Ssimokawa	    (v.product_id[0] << 16) | (v.product_id[1] << 8) | v.product_id[2]
642120432Ssimokawa	);
643120432Ssimokawa}
644120432Ssimokawa
645182911Ssbrunostatic int
646182911Ssbrunoopen_dev(int *fd, char *devname)
647118457Ssimokawa{
648118457Ssimokawa	if (*fd < 0) {
649182911Ssbruno		*fd = open(devname, O_RDWR);
650118457Ssimokawa		if (*fd < 0)
651182911Ssbruno			return(-1);
652118457Ssimokawa
653118457Ssimokawa	}
654182911Ssbruno	return(0);
655118457Ssimokawa}
656118457Ssimokawa
657163712Simpstatic void
658146442Scharniersysctl_set_int(const char *name, int val)
659136845Ssimokawa{
660136845Ssimokawa	if (sysctlbyname(name, NULL, NULL, &val, sizeof(int)) < 0)
661136845Ssimokawa		err(1, "sysctl %s failed.", name);
662136845Ssimokawa}
663136845Ssimokawa
664163712Simpstatic fwmethod *
665163712Simpdetect_recv_fn(int fd, char ich)
666163712Simp{
667163712Simp	char *buf;
668163712Simp	struct fw_isochreq isoreq;
669163712Simp	struct fw_isobufreq bufreq;
670163712Simp	int len;
671163712Simp	u_int32_t *ptr;
672163712Simp	struct ciphdr *ciph;
673163712Simp	fwmethod *retfn;
674182911Ssbruno#define RECV_NUM_PACKET 16
675182911Ssbruno#define RECV_PACKET_SZ  1024
676163712Simp
677163712Simp	bufreq.rx.nchunk = 8;
678182911Ssbruno	bufreq.rx.npacket = RECV_NUM_PACKET;
679182911Ssbruno	bufreq.rx.psize = RECV_PACKET_SZ;
680163712Simp	bufreq.tx.nchunk = 0;
681163712Simp	bufreq.tx.npacket = 0;
682163712Simp	bufreq.tx.psize = 0;
683163712Simp
684163712Simp	if (ioctl(fd, FW_SSTBUF, &bufreq) < 0)
685182911Ssbruno		err(EX_IOERR, "%s: ioctl FW_SSTBUF", __func__);
686163712Simp
687163712Simp	isoreq.ch = ich & 0x3f;
688163712Simp	isoreq.tag = (ich >> 6) & 3;
689163712Simp
690163712Simp	if (ioctl(fd, FW_SRSTREAM, &isoreq) < 0)
691182911Ssbruno		err(EX_IOERR, "%s: ioctl FW_SRSTREAM", __func__);
692163712Simp
693182911Ssbruno	buf = (char *)malloc(RECV_NUM_PACKET * RECV_PACKET_SZ);
694182911Ssbruno	if (buf == NULL)
695182911Ssbruno		err(EX_SOFTWARE, "%s:buf malloc", __func__);
696182911Ssbruno	/*
697182911Ssbruno	 * fwdev.c seems to return EIO on error and
698182911Ssbruno	 * the return value of the last uiomove
699182911Ssbruno	 * on success.  For now, checking that the
700182911Ssbruno	 * return is not less than zero should be
701182911Ssbruno	 * sufficient.  fwdev.c::fw_read() should
702182911Ssbruno	 * return the total length read, not the value
703182911Ssbruno	 * of the last uiomove().
704182911Ssbruno	 */
705182911Ssbruno	len = read(fd, buf, RECV_NUM_PACKET * RECV_PACKET_SZ);
706182911Ssbruno	if (len < 0)
707185996Ssbruno		err(EX_IOERR, "%s: error reading from device", __func__);
708163712Simp	ptr = (u_int32_t *) buf;
709163712Simp	ciph = (struct ciphdr *)(ptr + 1);
710163712Simp
711163712Simp	switch(ciph->fmt) {
712163712Simp		case CIP_FMT_DVCR:
713163712Simp			fprintf(stderr, "Detected DV format on input.\n");
714163712Simp			retfn = dvrecv;
715163712Simp			break;
716163712Simp		case CIP_FMT_MPEG:
717163712Simp			fprintf(stderr, "Detected MPEG TS format on input.\n");
718163712Simp			retfn = mpegtsrecv;
719163712Simp			break;
720163712Simp		default:
721163712Simp			errx(1, "Unsupported format for receiving: fmt=0x%x", ciph->fmt);
722163712Simp	}
723163712Simp	free(buf);
724163712Simp	return retfn;
725163712Simp}
726163712Simp
727136845Ssimokawaint
728108441Ssimokawamain(int argc, char **argv)
729108441Ssimokawa{
730182911Ssbruno#define MAX_BOARDS 10
731108441Ssimokawa	u_int32_t crom_buf[1024/4];
732182911Ssbruno	u_int32_t crom_buf_hex[1024/4];
733182911Ssbruno	char devbase[64];
734182911Ssbruno	const char *device_string = "/dev/fw";
735182911Ssbruno	int fd = -1, ch, len=1024;
736182911Ssbruno	int32_t current_board = 0;
737182911Ssbruno /*
738182911Ssbruno  * If !command_set, then -u will display the nodes for the board.
739182911Ssbruno  * This emulates the previous behavior when -u is passed by itself
740182911Ssbruno  */
741182911Ssbruno	bool command_set = false;
742182911Ssbruno	bool open_needed = false;
743163712Simp	long tmp;
744136845Ssimokawa	struct fw_eui64 eui;
745136845Ssimokawa	struct eui64 target;
746163712Simp	fwmethod *recvfn = NULL;
747182911Ssbruno/*
748182911Ssbruno * Holders for which functions
749182911Ssbruno * to iterate through
750182911Ssbruno */
751182911Ssbruno	bool display_board_only = false;
752182911Ssbruno	bool display_crom = false;
753182911Ssbruno	bool send_bus_reset = false;
754182911Ssbruno	bool display_crom_hex = false;
755182911Ssbruno	bool load_crom_from_file = false;
756182911Ssbruno	bool set_fwmem_target = false;
757182911Ssbruno	bool dump_topology = false;
758182911Ssbruno	bool dump_phy_reg = false;
759108441Ssimokawa
760182911Ssbruno	int32_t priority_budget = -1;
761182911Ssbruno	int32_t set_root_node = -1;
762182911Ssbruno	int32_t set_gap_count = -1;
763182911Ssbruno	int32_t send_link_on = -1;
764182911Ssbruno	int32_t send_reset_start = -1;
765108441Ssimokawa
766182911Ssbruno	char *crom_string = NULL;
767182911Ssbruno	char *crom_string_hex = NULL;
768182911Ssbruno	char *recv_data = NULL;
769182911Ssbruno	char *send_data = NULL;
770182911Ssbruno
771108441Ssimokawa	if (argc < 2) {
772182911Ssbruno		for (current_board = 0; current_board < MAX_BOARDS; current_board++) {
773185996Ssbruno			snprintf(devbase, sizeof(devbase), "%s%d.0", device_string, current_board);
774182911Ssbruno			if (open_dev(&fd, devbase) < 0) {
775182911Ssbruno				if (current_board == 0) {
776182911Ssbruno					usage();
777185996Ssbruno		  			err(EX_IOERR, "%s: Error opening firewire controller #%d %s",
778185996Ssbruno						      __func__, current_board, devbase);
779182911Ssbruno				}
780182911Ssbruno				return(EIO);
781182911Ssbruno			}
782182911Ssbruno			list_dev(fd);
783182911Ssbruno			close(fd);
784182911Ssbruno			fd = -1;
785182911Ssbruno		}
786108441Ssimokawa	}
787182911Ssbruno       /*
788182911Ssbruno	* Parse all command line options, then execute requested operations.
789182911Ssbruno	*/
790182911Ssbruno	while ((ch = getopt(argc, argv, "M:f:g:m:o:s:b:prtc:d:l:u:R:S:")) != -1) {
791108441Ssimokawa		switch(ch) {
792108441Ssimokawa		case 'b':
793182911Ssbruno			priority_budget = strtol(optarg, NULL, 0);
794182911Ssbruno			if (priority_budget < 0 || priority_budget > INT32_MAX)
795185996Ssbruno				errx(EX_USAGE, "%s: priority_budget out of range: %s", __func__, optarg);
796182911Ssbruno			command_set = true;
797182911Ssbruno			open_needed = true;
798182911Ssbruno			display_board_only = false;
799108441Ssimokawa			break;
800108441Ssimokawa		case 'c':
801182911Ssbruno			crom_string = malloc(strlen(optarg)+1);
802182911Ssbruno			if (crom_string == NULL)
803182911Ssbruno				err(EX_SOFTWARE, "%s:crom_string malloc", __func__);
804182911Ssbruno			if ( (strtol(crom_string, NULL, 0) < 0) || strtol(crom_string, NULL, 0) > MAX_BOARDS)
805185996Ssbruno				errx(EX_USAGE, "%s:Invalid value for node", __func__);
806182911Ssbruno			strcpy(crom_string, optarg);
807182911Ssbruno			display_crom = 1;
808182911Ssbruno			open_needed = true;
809182911Ssbruno			command_set = true;
810182911Ssbruno			display_board_only = false;
811108441Ssimokawa			break;
812108441Ssimokawa		case 'd':
813182911Ssbruno			crom_string_hex = malloc(strlen(optarg)+1);
814182911Ssbruno			if (crom_string_hex == NULL)
815182911Ssbruno				err(EX_SOFTWARE, "%s:crom_string_hex malloc", __func__);
816182911Ssbruno			strcpy(crom_string_hex, optarg);
817182911Ssbruno			display_crom_hex = 1;
818182911Ssbruno			open_needed = true;
819182911Ssbruno			command_set = true;
820182911Ssbruno			display_board_only = false;
821108441Ssimokawa			break;
822176810Ssimokawa		case 'f':
823182911Ssbruno#define MAX_PHY_CONFIG 0x3f
824182911Ssbruno			set_root_node = strtol(optarg, NULL, 0);
825182911Ssbruno			if ( (set_root_node < 0) || (set_root_node > MAX_PHY_CONFIG) )
826185996Ssbruno				errx(EX_USAGE, "%s:set_root_node out of range", __func__);
827182911Ssbruno			open_needed = true;
828182911Ssbruno			command_set = true;
829182911Ssbruno			display_board_only = false;
830176810Ssimokawa			break;
831118457Ssimokawa		case 'g':
832182911Ssbruno			set_gap_count = strtol(optarg, NULL, 0);
833182911Ssbruno			if ( (set_gap_count < 0) || (set_gap_count > MAX_PHY_CONFIG) )
834185996Ssbruno				errx(EX_USAGE, "%s:set_gap_count out of range", __func__);
835182911Ssbruno			open_needed = true;
836182911Ssbruno			command_set = true;
837182911Ssbruno			display_board_only = false;
838118457Ssimokawa			break;
839108441Ssimokawa		case 'l':
840182911Ssbruno			load_crom_from_file = 1;
841108441Ssimokawa			load_crom(optarg, crom_buf);
842182911Ssbruno			command_set = true;
843182911Ssbruno			display_board_only = false;
844108441Ssimokawa			break;
845136845Ssimokawa		case 'm':
846182911Ssbruno			set_fwmem_target = 1;
847182911Ssbruno			open_needed = 0;
848182911Ssbruno			command_set = true;
849182911Ssbruno			display_board_only = false;
850182911Ssbruno			if (eui64_hostton(optarg, &target) != 0 &&
851182911Ssbruno			    eui64_aton(optarg, &target) != 0)
852185996Ssbruno				errx(EX_USAGE, "%s: invalid target: %s", __func__, optarg);
853136845Ssimokawa			break;
854118457Ssimokawa		case 'o':
855182911Ssbruno			send_link_on = str2node(fd, optarg);
856185996Ssbruno			if ( (send_link_on < 0) || (send_link_on > MAX_PHY_CONFIG) )
857185996Ssbruno				errx(EX_USAGE, "%s: node out of range: %s\n",__func__, optarg);
858182911Ssbruno			open_needed = true;
859182911Ssbruno			command_set = true;
860182911Ssbruno			display_board_only = false;
861118457Ssimokawa			break;
862120432Ssimokawa		case 'p':
863182911Ssbruno			dump_phy_reg = 1;
864182911Ssbruno			open_needed = true;
865182911Ssbruno			command_set = true;
866182911Ssbruno			display_board_only = false;
867120432Ssimokawa			break;
868118457Ssimokawa		case 'r':
869182911Ssbruno			send_bus_reset = 1;
870182911Ssbruno			open_needed = true;
871182911Ssbruno			command_set = true;
872182911Ssbruno			display_board_only = false;
873118457Ssimokawa			break;
874118457Ssimokawa		case 's':
875182911Ssbruno			send_reset_start  = str2node(fd, optarg);
876185996Ssbruno			if ( (send_reset_start < 0) || (send_reset_start > MAX_PHY_CONFIG) )
877185996Ssbruno				errx(EX_USAGE, "%s: node out of range: %s\n", __func__, optarg);
878182911Ssbruno			open_needed = true;
879182911Ssbruno			command_set = true;
880182911Ssbruno			display_board_only = false;
881118457Ssimokawa			break;
882118457Ssimokawa		case 't':
883182911Ssbruno			dump_topology = 1;
884182911Ssbruno			open_needed = true;
885182911Ssbruno			command_set = true;
886182911Ssbruno			display_board_only = false;
887118457Ssimokawa			break;
888118457Ssimokawa		case 'u':
889182911Ssbruno			if(!command_set)
890182911Ssbruno				display_board_only = true;
891182911Ssbruno			current_board = strtol(optarg, NULL, 0);
892182911Ssbruno			open_needed = true;
893118457Ssimokawa			break;
894163712Simp		case 'M':
895163712Simp			switch (optarg[0]) {
896163712Simp			case 'm':
897163712Simp				recvfn = mpegtsrecv;
898163712Simp				break;
899163712Simp			case 'd':
900163712Simp				recvfn = dvrecv;
901163712Simp				break;
902163712Simp			default:
903163712Simp				errx(EX_USAGE, "unrecognized method: %s",
904163712Simp				    optarg);
905163712Simp			}
906182911Ssbruno			command_set = true;
907182911Ssbruno			display_board_only = false;
908163712Simp			break;
909109737Ssimokawa		case 'R':
910182911Ssbruno			recv_data = malloc(strlen(optarg)+1);
911182911Ssbruno			if (recv_data == NULL)
912182911Ssbruno				err(EX_SOFTWARE, "%s:recv_data malloc", __func__);
913182911Ssbruno			strcpy(recv_data, optarg);
914182911Ssbruno			open_needed = false;
915182911Ssbruno			command_set = true;
916182911Ssbruno			display_board_only = false;
917109737Ssimokawa			break;
918109737Ssimokawa		case 'S':
919182911Ssbruno			send_data = malloc(strlen(optarg)+1);
920182911Ssbruno			if (send_data == NULL)
921182911Ssbruno				err(EX_SOFTWARE, "%s:send_data malloc", __func__);
922182911Ssbruno			strcpy(send_data, optarg);
923182911Ssbruno			open_needed = true;
924182911Ssbruno			command_set = true;
925182911Ssbruno			display_board_only = false;
926109737Ssimokawa			break;
927185996Ssbruno		case '?':
928108441Ssimokawa		default:
929108441Ssimokawa			usage();
930185996Ssbruno		        warnc(EINVAL, "%s: Unknown command line arguments", __func__);
931182911Ssbruno			return 0;
932108441Ssimokawa		}
933182911Ssbruno	} /* end while */
934182911Ssbruno
935182911Ssbruno       /*
936185996Ssbruno	* Catch the error case when the user
937185996Ssbruno	* executes the command with non ''-''
938185996Ssbruno	* delimited arguments.
939185996Ssbruno	* Generate the usage() display and exit.
940185996Ssbruno	*/
941185996Ssbruno	if (!command_set && !display_board_only) {
942185996Ssbruno		usage();
943185996Ssbruno		warnc(EINVAL, "%s: Unknown command line arguments", __func__);
944185996Ssbruno		return 0;
945185996Ssbruno	}
946185996Ssbruno
947185996Ssbruno       /*
948182911Ssbruno	* If -u <bus_number> is passed, execute
949182911Ssbruno	* command for that card only.
950182911Ssbruno	*
951182911Ssbruno	* If -u <bus_number> is not passed, execute
952182911Ssbruno	* command for card 0 only.
953182911Ssbruno	*
954182911Ssbruno	*/
955182911Ssbruno	if(open_needed){
956185996Ssbruno		snprintf(devbase, sizeof(devbase), "%s%d.0", device_string, current_board);
957182911Ssbruno		if (open_dev(&fd, devbase) < 0) {
958185996Ssbruno		  err(EX_IOERR, "%s: Error opening firewire controller #%d %s", __func__, current_board, devbase);
959182911Ssbruno		}
960182911Ssbruno	}
961182911Ssbruno	/*
962182911Ssbruno	 * display the nodes on this board "-u"
963182911Ssbruno	 * only
964182911Ssbruno	 */
965182911Ssbruno	if (display_board_only)
966182911Ssbruno		list_dev(fd);
967182911Ssbruno
968182911Ssbruno	/*
969182911Ssbruno	 * dump_phy_reg "-p"
970182911Ssbruno	 */
971182911Ssbruno	if (dump_phy_reg)
972182911Ssbruno		dump_phy_registers(fd);
973182911Ssbruno
974182911Ssbruno	/*
975182911Ssbruno	 * send a BUS_RESET Event "-r"
976182911Ssbruno	 */
977182911Ssbruno	if (send_bus_reset) {
978182911Ssbruno		if(ioctl(fd, FW_IBUSRST, &tmp) < 0)
979185996Ssbruno               		err(EX_IOERR, "%s: Ioctl of bus reset failed for %s", __func__, devbase);
980182911Ssbruno	}
981182911Ssbruno	/*
982182911Ssbruno	 * Print out the CROM for this node "-c"
983182911Ssbruno	 */
984182911Ssbruno	if (display_crom) {
985182911Ssbruno		tmp = str2node(fd, crom_string);
986182911Ssbruno		get_crom(fd, tmp, crom_buf, len);
987182911Ssbruno		show_crom(crom_buf);
988182911Ssbruno		free(crom_string);
989182911Ssbruno	}
990182911Ssbruno	/*
991182911Ssbruno	 * Hex Dump the CROM for this node "-d"
992182911Ssbruno	 */
993182911Ssbruno	if (display_crom_hex) {
994182911Ssbruno		tmp = str2node(fd, crom_string_hex);
995182911Ssbruno		get_crom(fd, tmp, crom_buf_hex, len);
996182911Ssbruno		dump_crom(crom_buf_hex);
997182911Ssbruno		free(crom_string_hex);
998182911Ssbruno	}
999182911Ssbruno	/*
1000182911Ssbruno	 * Set Priority Budget to value for this node "-b"
1001182911Ssbruno	 */
1002182911Ssbruno	if (priority_budget >= 0)
1003182911Ssbruno		set_pri_req(fd, priority_budget);
1004182911Ssbruno
1005182911Ssbruno	/*
1006182911Ssbruno	 * Explicitly set the root node of this bus to value "-f"
1007182911Ssbruno	 */
1008182911Ssbruno	if (set_root_node >= 0)
1009182911Ssbruno		send_phy_config(fd, set_root_node, -1);
1010182911Ssbruno
1011182911Ssbruno	/*
1012182911Ssbruno	 * Set the gap count for this card/bus  "-g"
1013182911Ssbruno	 */
1014182911Ssbruno	if (set_gap_count >= 0)
1015182911Ssbruno		send_phy_config(fd, -1, set_gap_count);
1016182911Ssbruno
1017182911Ssbruno	/*
1018182911Ssbruno	 * Load a CROM from a file "-l"
1019182911Ssbruno	 */
1020182911Ssbruno	if (load_crom_from_file)
1021182911Ssbruno		show_crom(crom_buf);
1022182911Ssbruno	/*
1023182911Ssbruno	 * Set the fwmem target for a node to argument "-m"
1024182911Ssbruno	 */
1025182911Ssbruno	if (set_fwmem_target) {
1026182911Ssbruno		eui.hi = ntohl(*(u_int32_t*)&(target.octet[0]));
1027182911Ssbruno		eui.lo = ntohl(*(u_int32_t*)&(target.octet[4]));
1028185996Ssbruno#if defined(__FreeBSD__)
1029182911Ssbruno		sysctl_set_int("hw.firewire.fwmem.eui64_hi", eui.hi);
1030182911Ssbruno		sysctl_set_int("hw.firewire.fwmem.eui64_lo", eui.lo);
1031185996Ssbruno#elif defined(__NetBSD__)
1032185996Ssbruno		sysctl_set_int("hw.fwmem.eui64_hi", eui.hi);
1033185996Ssbruno		sysctl_set_int("hw.fwmem.eui64_lo", eui.lo);
1034185996Ssbruno#else
1035185996Ssbruno#warning "You need to add support for your OS"
1036185996Ssbruno#endif
1037185996Ssbruno
1038182911Ssbruno	}
1039182911Ssbruno
1040182911Ssbruno	/*
1041182911Ssbruno	 * Send a link on to this board/bus "-o"
1042182911Ssbruno	 */
1043182911Ssbruno	if (send_link_on >= 0)
1044182911Ssbruno		link_on(fd, send_link_on);
1045182911Ssbruno
1046182911Ssbruno	/*
1047182911Ssbruno	 * Send a reset start to this board/bus "-s"
1048182911Ssbruno	 */
1049182911Ssbruno	if (send_reset_start >= 0)
1050182911Ssbruno		reset_start(fd, send_reset_start);
1051182911Ssbruno
1052182911Ssbruno	/*
1053182911Ssbruno	 * Dump the node topology for this board/bus "-t"
1054182911Ssbruno	 */
1055182911Ssbruno	if (dump_topology)
1056182911Ssbruno		show_topology_map(fd);
1057182911Ssbruno
1058182911Ssbruno	/*
1059228990Suqs	 * Receive data file from node "-R"
1060182911Ssbruno	 */
1061182911Ssbruno#define TAG	(1<<6)
1062182911Ssbruno#define CHANNEL	63
1063182911Ssbruno	if (recv_data != NULL){
1064182911Ssbruno		if (recvfn == NULL) { /* guess... */
1065182911Ssbruno			recvfn = detect_recv_fn(fd, TAG | CHANNEL);
1066182911Ssbruno			close(fd);
1067186696Simp			fd = -1;
1068182911Ssbruno		}
1069185996Ssbruno		snprintf(devbase, sizeof(devbase), "%s%d.0", device_string, current_board);
1070182911Ssbruno		if (open_dev(&fd, devbase) < 0)
1071185996Ssbruno		  err(EX_IOERR, "%s: Error opening firewire controller #%d %s in recv_data\n", __func__, current_board, devbase);
1072182911Ssbruno		(*recvfn)(fd, recv_data, TAG | CHANNEL, -1);
1073182911Ssbruno		free(recv_data);
1074182911Ssbruno	}
1075182911Ssbruno
1076182911Ssbruno	/*
1077182911Ssbruno	 * Send data file to node "-S"
1078182911Ssbruno	 */
1079182911Ssbruno	if (send_data != NULL){
1080182911Ssbruno		dvsend(fd, send_data, TAG | CHANNEL, -1);
1081182911Ssbruno		free(send_data);
1082182911Ssbruno	}
1083182911Ssbruno
1084182911Ssbruno	if (fd > 0) {
1085182911Ssbruno		close(fd);
1086182911Ssbruno		fd = -1;
1087182911Ssbruno	}
1088108441Ssimokawa	return 0;
1089108441Ssimokawa}
1090