xref: /freebsd/usr.sbin/fwcontrol/fwcontrol.c (revision ab543fbcee3776f432d6e574d39571b1d02d155f)
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  * $FreeBSD$
35  */
36 
37 #include <sys/param.h>
38 #include <sys/malloc.h>
39 #include <sys/socket.h>
40 #include <sys/ioctl.h>
41 #include <sys/errno.h>
42 #include <dev/firewire/firewire.h>
43 #include <dev/firewire/iec13213.h>
44 
45 #include <netinet/in.h>
46 #include <fcntl.h>
47 #include <stdio.h>
48 #include <err.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 
53 static void
54 usage(void)
55 {
56 	printf("fwcontrol [-g gap_count] [-b pri_req] [-c node]"
57 		" [-r] [-t] [-d node] [-l file]\n");
58 	printf("\t-g: broadcast gap_count by phy_config packet\n");
59 	printf("\t-b: set PRIORITY_BUDGET register on all supported nodes\n");
60 	printf("\t-c: read configuration ROM\n");
61 	printf("\t-r: bus reset\n");
62 	printf("\t-t: read topology map\n");
63 	printf("\t-d: hex dump of configuration ROM\n");
64 	printf("\t-l: load and parse hex dump file of configuration ROM\n");
65 	exit(0);
66 }
67 
68 static void
69 get_num_of_dev(int fd, struct fw_devlstreq *data)
70 {
71 	data->n = 64;
72 	if( ioctl(fd, FW_GDEVLST, data) < 0) {
73        			err(1, "ioctl");
74 	}
75 }
76 
77 static void
78 list_dev(int fd)
79 {
80 	struct fw_devlstreq data;
81 	int i;
82 
83 	get_num_of_dev(fd, &data);
84 	printf("%d devices\n", data.n);
85 	for (i = 0; i < data.n; i++) {
86 		printf("%d node %d eui:%08x%08x status:%d\n",
87 			i,
88 			data.dst[i],
89 			data.eui[i].hi,
90 			data.eui[i].lo,
91 			data.status[i]
92 		);
93 	}
94 }
95 
96 static u_int32_t
97 read_write_quad(int fd, struct fw_eui64 eui, u_int32_t addr_lo, int read, u_int32_t data)
98 {
99         struct fw_asyreq *asyreq;
100 	u_int32_t *qld, res;
101 
102         asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 16);
103 	asyreq->req.len = 16;
104 	asyreq->req.type = FWASREQEUI;
105 	asyreq->req.dst.eui = eui;
106 #if 0
107 	asyreq->pkt.mode.rreqq.dst = htons(FWLOCALBUS | node);
108 #endif
109 	asyreq->pkt.mode.rreqq.tlrt = 0;
110 	if (read)
111 		asyreq->pkt.mode.rreqq.tcode = FWTCODE_RREQQ;
112 	else
113 		asyreq->pkt.mode.rreqq.tcode = FWTCODE_WREQQ;
114 
115 	asyreq->pkt.mode.rreqq.dest_hi = htons(0xffff);
116 	asyreq->pkt.mode.rreqq.dest_lo = htonl(addr_lo);
117 
118 	qld = (u_int32_t *)&asyreq->pkt;
119 	if (!read)
120 		asyreq->pkt.mode.wreqq.data = data;
121 
122 	if (ioctl(fd, FW_ASYREQ, asyreq) < 0) {
123        		err(1, "ioctl");
124 	}
125 	res = qld[3];
126 	free(asyreq);
127 	if (read)
128 		return ntohl(res);
129 	else
130 		return 0;
131 }
132 
133 static void
134 send_phy_config(int fd, int root_node, int gap_count)
135 {
136         struct fw_asyreq *asyreq;
137 
138 	asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 12);
139 	asyreq->req.len = 12;
140 	asyreq->req.type = FWASREQNODE;
141 	asyreq->pkt.mode.ld[0] = 0;
142 	asyreq->pkt.mode.ld[1] = 0;
143 	asyreq->pkt.mode.common.tcode = FWTCODE_PHY;
144 	if (root_node >= 0)
145 		asyreq->pkt.mode.ld[1] |= htonl((root_node & 0x3f) << 24 | 1 << 23);
146 	if (gap_count >= 0)
147 		asyreq->pkt.mode.ld[1] |= htonl(1 << 22 | (gap_count & 0x3f) << 16);
148 	asyreq->pkt.mode.ld[2] = ~asyreq->pkt.mode.ld[1];
149 
150 	printf("send phy_config root_node=%d gap_count=%d\n",
151 						root_node, gap_count);
152 
153 	if (ioctl(fd, FW_ASYREQ, asyreq) < 0) {
154        		err(1, "ioctl");
155 	}
156 }
157 
158 static void
159 set_pri_req(int fd, int pri_req)
160 {
161 	struct fw_devlstreq data;
162 	u_int32_t max, reg, old;
163 	int i;
164 
165 	get_num_of_dev(fd, &data);
166 #define BUGET_REG 0xf0000218
167 	for (i = 0; i < data.n; i++) {
168 		if (!data.status[i])
169 			continue;
170 		reg = read_write_quad(fd, data.eui[i], BUGET_REG, 1, 0);
171 		printf("%d %08x:%08x, %08x",
172 			data.dst[i], data.eui[i].hi, data.eui[i].lo, reg);
173 		if (reg > 0 && pri_req >= 0) {
174 			old = (reg & 0x3f);
175 			max = (reg & 0x3f00) >> 8;
176 			if (pri_req > max)
177 				pri_req =  max;
178 			printf(" 0x%x -> 0x%x\n", old, pri_req);
179 			read_write_quad(fd, data.eui[i], BUGET_REG, 0, pri_req);
180 		} else {
181 			printf("\n");
182 		}
183 	}
184 }
185 
186 static void
187 parse_bus_info_block(u_int32_t *p, int info_len)
188 {
189 	int i;
190 
191 	for (i = 0; i < info_len; i++) {
192 		printf("bus_info%d: 0x%08x\n", i, *p++);
193 	}
194 }
195 
196 static int
197 get_crom(int fd, int node, void *crom_buf, int len)
198 {
199 	struct fw_crom_buf buf;
200 	int i, error;
201 	struct fw_devlstreq data;
202 
203 	get_num_of_dev(fd, &data);
204 
205 	for (i = 0; i < data.n; i++) {
206 		if (data.dst[i] == node && data.eui[i].lo != 0)
207 			break;
208 	}
209 	if (i != data.n) {
210 		buf.eui = data.eui[i];
211 	} else {
212 		err(1, "no such node: %d\n", node);
213 	}
214 
215 	buf.len = len;
216 	buf.ptr = crom_buf;
217 	if ((error = ioctl(fd, FW_GCROM, &buf)) < 0) {
218        		err(1, "ioctl");
219 	}
220 	return error;
221 }
222 
223 static void
224 show_crom(u_int32_t *crom_buf)
225 {
226 	int i;
227 	struct crom_context cc;
228 	char *desc, info[256];
229 	static char *key_types = "ICLD";
230 	struct csrreg *reg;
231 	struct csrdirectory *dir;
232 	struct csrhdr *hdr;
233 
234 	printf("first quad: 0x%08x\n", *crom_buf);
235 	hdr = (struct csrhdr *)crom_buf;
236 	if (hdr->info_len == 1) {
237 		/* minimum ROM */
238 		struct csrreg *reg;
239 		reg = (struct csrreg *)hdr;
240 		printf("verndor ID: 0x%06x\n",  reg->val);
241 		return;
242 	}
243 	printf("len: %d\n", hdr->crc_len);
244 	parse_bus_info_block(crom_buf+1, hdr->info_len);
245 
246 	crom_init_context(&cc, crom_buf);
247 	dir = cc.stack[0].dir;
248 	printf("root_directory: len=0x%04x(%d) crc=0x%04x\n",
249 			dir->crc_len, dir->crc_len, dir->crc);
250 	if (dir->crc_len < 1)
251 		return;
252 	while (cc.depth >= 0) {
253 		desc = crom_desc(&cc, info, sizeof(info));
254 		reg = crom_get(&cc);
255 		for (i = 0; i < cc.depth; i++)
256 			printf("\t");
257 		printf("%02x(%c:%02x) %06x %s: %s\n",
258 			reg->key,
259 			key_types[(reg->key & CSRTYPE_MASK)>>6],
260 			reg->key & CSRKEY_MASK, reg->val,
261 			desc, info);
262 		crom_next(&cc);
263 	}
264 }
265 
266 #define DUMP_FORMAT	"%08x %08x %08x %08x %08x %08x %08x %08x\n"
267 
268 static void
269 dump_crom(u_int32_t *p)
270 {
271 	int len=1024, i;
272 
273 	for (i = 0; i < len/(4*8); i ++) {
274 		printf(DUMP_FORMAT,
275 			p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
276 		p += 8;
277 	}
278 }
279 
280 static void
281 load_crom(char *filename, u_int32_t *p)
282 {
283 	FILE *file;
284 	int len=1024, i;
285 
286 	if ((file = fopen(filename, "r")) == NULL)
287 		err(1, "load_crom");
288 	for (i = 0; i < len/(4*8); i ++) {
289 		fscanf(file, DUMP_FORMAT,
290 			p, p+1, p+2, p+3, p+4, p+5, p+6, p+7);
291 		p += 8;
292 	}
293 }
294 
295 static void
296 show_topology_map(int fd)
297 {
298 	struct fw_topology_map *tmap;
299 	union fw_self_id sid;
300 	int i;
301 	static char *port_status[] = {" ", "-", "P", "C"};
302 	static char *pwr_class[] = {" 0W", "15W", "30W", "45W",
303 					"-1W", "-2W", "-5W", "-9W"};
304 	static char *speed[] = {"S100", "S200", "S400", "S800"};
305 	tmap = malloc(sizeof(struct fw_topology_map));
306 	if (tmap == NULL)
307 		return;
308 	if (ioctl(fd, FW_GTPMAP, tmap) < 0) {
309        		err(1, "ioctl");
310 	}
311 	printf("crc_len: %d generation:%d node_count:%d sid_count:%d\n",
312 		tmap->crc_len, tmap->generation,
313 		tmap->node_count, tmap->self_id_count);
314 	printf("id link gap_cnt speed delay cIRM power port0 port1 port2"
315 		" ini more\n");
316 	for (i = 0; i < tmap->crc_len - 2; i++) {
317 		sid = tmap->self_id[i];
318 		if (sid.p0.sequel) {
319 			printf("%02d sequel packet\n", sid.p0.phy_id);
320 			continue;
321 		}
322 		printf("%02d   %2d      %d  %4s     %d    %d   %3s"
323 				"     %s     %s     %s   %d    %d\n",
324 			sid.p0.phy_id,
325 			sid.p0.link_active,
326 			sid.p0.gap_count,
327 			speed[sid.p0.phy_speed],
328 			sid.p0.phy_delay,
329 			sid.p0.contender,
330 			pwr_class[sid.p0.power_class],
331 			port_status[sid.p0.port0],
332 			port_status[sid.p0.port1],
333 			port_status[sid.p0.port2],
334 			sid.p0.initiated_reset,
335 			sid.p0.more_packets
336 		);
337 	}
338 	free(tmap);
339 }
340 
341 int
342 main(int argc, char **argv)
343 {
344 	char devname[] = "/dev/fw1";
345 	u_int32_t crom_buf[1024/4];
346 	int fd, tmp, ch, len=1024;
347 
348 	if ((fd = open(devname, O_RDWR)) < 0)
349 		err(1, "open");
350 
351 	if (argc < 2) {
352 		list_dev(fd);
353 		usage();
354 	}
355 
356 	while ((ch = getopt(argc, argv, "g:b:rtc:d:l:")) != -1)
357 		switch(ch) {
358 		case 'g':
359 			/* gap count */
360 			tmp = strtol(optarg, NULL, 0);
361 			send_phy_config(fd, -1, tmp);
362 			break;
363 		case 'b':
364 			tmp = strtol(optarg, NULL, 0);
365 			set_pri_req(fd, tmp);
366 			break;
367 		case 'r':
368 			if(ioctl(fd, FW_IBUSRST, &tmp) < 0)
369                        		err(1, "ioctl");
370 			break;
371 		case 't':
372 			show_topology_map(fd);
373 			break;
374 		case 'c':
375 			tmp = strtol(optarg, NULL, 0);
376 			get_crom(fd, tmp, crom_buf, len);
377 			show_crom(crom_buf);
378 			break;
379 		case 'd':
380 			tmp = strtol(optarg, NULL, 0);
381 			get_crom(fd, tmp, crom_buf, len);
382 			dump_crom(crom_buf);
383 			break;
384 		case 'l':
385 			load_crom(optarg, crom_buf);
386 			show_crom(crom_buf);
387 			break;
388 		default:
389 			usage();
390 		}
391 	return 0;
392 }
393