aboutsummaryrefslogtreecommitdiffstats
path: root/tools/firewire/nosy-dump.c
diff options
context:
space:
mode:
authorStefan Richter <stefanr@s5r6.in-berlin.de>2010-07-22 05:58:05 -0400
committerStefan Richter <stefanr@s5r6.in-berlin.de>2010-07-27 05:04:11 -0400
commit9f6d3c4b76314c40c866a935d78c80fd284768bd (patch)
treeccb994e8ebe7a36b8bed804c79ce9f117455f041 /tools/firewire/nosy-dump.c
parentaed69d2b79bb5af008526998e466da6d0eac7ae5 (diff)
tools/firewire: add userspace front-end of nosy
This adds nosy-dump, the userspace part of nosy, the IEEE 1394 traffic sniffer for Texas Instruments PCILynx/ PCILynx2 based cards. Author is Kristian Høgsberg. The files added here are taken from git://anongit.freedesktop.org/~krh/nosy commit ee29be97 (2009-11-10) with the following changes by Stefan Richter: - Parts pertaining to the kernel module removed from Makefile. - dist target removed from the Makefile. - Mentioned nosy-dump in the Kconfig help to nosy's kernel component. - Add copyright notice to nosy-dump.c. This is a duplicate of the respective notice in the kernel component nosy.c except for a time span of 2002 - 2006, according to Kristian's git log. "git shortlog decode-fcp.c list.h nosy-dump.[ch]" from nosy's git repository: Jonathan Woithe (1): Save logs on Ctrl-C Kristian Høgsberg (11): Pull over nosy from mercurial repo. Remove some fields from default view, add logging feature. Use infinite time out for poll(), mark more detail fields. Fix byte ordering macro. Add decoding of iso data and lock packets. Add flag to indicate data length field. Add cycle start packet decoding, add --iso and --cycle-start flags. Distinguish between phy-packets and 0-length iso data. Fix transaction and stats view. Add simple AV/C decoder. Don't break down on big payloads. Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> Acked-by: Kristian Høgsberg <krh@bitplanet.net>
Diffstat (limited to 'tools/firewire/nosy-dump.c')
-rw-r--r--tools/firewire/nosy-dump.c1037
1 files changed, 1037 insertions, 0 deletions
diff --git a/tools/firewire/nosy-dump.c b/tools/firewire/nosy-dump.c
new file mode 100644
index 000000000000..64e5ea438928
--- /dev/null
+++ b/tools/firewire/nosy-dump.c
@@ -0,0 +1,1037 @@
1/* -*- mode: c; c-basic-offset: 2 -*- */
2
3/*
4 * nosy-dump - Interface to snoop mode driver for TI PCILynx 1394 controllers
5 * Copyright (C) 2002-2006 Kristian Høgsberg
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software Foundation,
19 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21
22#include <stdlib.h>
23#include <stdio.h>
24#include <string.h>
25#include <unistd.h>
26#include <fcntl.h>
27#include <sys/ioctl.h>
28#include <sys/time.h>
29#include <endian.h>
30#include <popt.h>
31#include <poll.h>
32#include <byteswap.h>
33#include <termios.h>
34
35#include <signal.h>
36
37#include "list.h"
38#include "nosy-user.h"
39#include "nosy-dump.h"
40
41enum {
42 PACKET_FIELD_DETAIL = 0x01,
43 PACKET_FIELD_DATA_LENGTH = 0x02,
44 /* Marks the fields we print in transaction view. */
45 PACKET_FIELD_TRANSACTION = 0x04
46};
47
48static void
49print_packet(unsigned long *data, size_t length);
50static void
51decode_link_packet(struct link_packet *packet, size_t length,
52 int include_flags, int exclude_flags);
53
54static int run = 1;
55sig_t sys_sigint_handler;
56
57static char *option_nosy_device = "/dev/nosy";
58static char *option_view = "packet";
59static char *option_output = NULL;
60static char *option_input = NULL;
61static int option_hex;
62static int option_iso;
63static int option_cycle_start;
64static int option_version;
65static int option_verbose;
66
67enum {
68 VIEW_TRANSACTION,
69 VIEW_PACKET,
70 VIEW_STATS
71};
72
73static const struct poptOption options[] = {
74 {
75 longName: "device",
76 shortName: 'd',
77 argInfo: POPT_ARG_STRING,
78 arg: &option_nosy_device,
79 descrip: "Path to nosy device.",
80 argDescrip: "DEVICE"
81 },
82 {
83 longName: "view",
84 argInfo: POPT_ARG_STRING,
85 arg: &option_view,
86 descrip: "Specify view of bus traffic: packet, transaction or stats.",
87 argDescrip: "VIEW"
88 },
89 {
90 longName: "hex",
91 shortName: 'x',
92 argInfo: POPT_ARG_NONE,
93 arg: &option_hex,
94 descrip: "Print each packet in hex.",
95 },
96 {
97 longName: "iso",
98 argInfo: POPT_ARG_NONE,
99 arg: &option_iso,
100 descrip: "Print iso packets.",
101 },
102 {
103 longName: "cycle-start",
104 argInfo: POPT_ARG_NONE,
105 arg: &option_cycle_start,
106 descrip: "Print cycle start packets.",
107 },
108 {
109 longName: "verbose",
110 shortName: 'v',
111 argInfo: POPT_ARG_NONE,
112 arg: &option_verbose,
113 descrip: "Verbose packet view.",
114 },
115 {
116 longName: "output",
117 shortName: 'o',
118 argInfo: POPT_ARG_STRING,
119 arg: &option_output,
120 descrip: "Log to output file.",
121 argDescrip: "FILENAME"
122 },
123 {
124 longName: "input",
125 shortName: 'i',
126 argInfo: POPT_ARG_STRING,
127 arg: &option_input,
128 descrip: "Decode log from file.",
129 argDescrip: "FILENAME"
130 },
131 {
132 longName: "version",
133 argInfo: POPT_ARG_NONE,
134 arg: &option_version,
135 descrip: "Specify print version info.",
136 },
137 POPT_AUTOHELP
138 POPT_TABLEEND
139};
140
141void
142sigint_handler(int signal_num)
143{
144 if (run == 1) {
145 run = 0;
146 /* Allow all Ctrl-C's except the first to interrupt the program in
147 * the usual way.
148 */
149 signal(SIGINT, SIG_DFL);
150 }
151}
152
153struct subaction *
154subaction_create(unsigned long *data, size_t length)
155{
156 struct subaction *sa;
157
158 /* we put the ack in the subaction struct for easy access. */
159 sa = malloc(sizeof *sa - sizeof sa->packet + length);
160 sa->ack = data[length / 4 - 1];
161 sa->length = length;
162 memcpy (&sa->packet, data, length);
163
164 return sa;
165}
166
167void
168subaction_destroy(struct subaction *sa)
169{
170 free(sa);
171}
172
173struct list pending_transaction_list =
174 { &pending_transaction_list, &pending_transaction_list };
175
176struct link_transaction *
177link_transaction_lookup(int request_node, int response_node, int tlabel)
178{
179 struct link_transaction *t;
180
181 list_for_each_entry(t, &pending_transaction_list, link) {
182 if (t->request_node == request_node &&
183 t->response_node == response_node &&
184 t->tlabel == tlabel)
185 return t;
186 }
187
188 t = malloc(sizeof *t);
189 t->request_node = request_node;
190 t->response_node = response_node;
191 t->tlabel = tlabel;
192 list_init(&t->request_list);
193 list_init(&t->response_list);
194
195 list_append(&pending_transaction_list, &t->link);
196
197 return t;
198}
199
200void
201link_transaction_destroy(struct link_transaction *t)
202{
203 while (!list_empty(&t->request_list)) {
204 struct subaction *sa = list_head(&t->request_list, struct subaction, link);
205 list_remove(&sa->link);
206 subaction_destroy(sa);
207 }
208
209 while (!list_empty(&t->response_list)) {
210 struct subaction *sa = list_head(&t->response_list, struct subaction, link);
211 list_remove(&sa->link);
212 subaction_destroy(sa);
213 }
214
215 free(t);
216}
217
218struct protocol_decoder {
219 const char *name;
220 int (*decode)(struct link_transaction *t);
221};
222
223static struct protocol_decoder protocol_decoders[] = {
224 { "FCP", decode_fcp }
225};
226
227void
228handle_transaction(struct link_transaction *t)
229{
230 struct subaction *sa;
231 int i;
232
233 for (i = 0; i < array_length(protocol_decoders); i++)
234 if (protocol_decoders[i].decode(t))
235 break;
236
237 /* HACK: decode only fcp right now. */
238 return;
239
240 decode_link_packet(&t->request->packet, t->request->length,
241 PACKET_FIELD_TRANSACTION, 0);
242 if (t->response)
243 decode_link_packet(&t->response->packet, t->request->length,
244 PACKET_FIELD_TRANSACTION, 0);
245 else
246 printf("[no response]");
247
248 if (option_verbose) {
249 list_for_each_entry(sa, &t->request_list, link)
250 print_packet((unsigned long *) &sa->packet, sa->length);
251 list_for_each_entry(sa, &t->response_list, link)
252 print_packet((unsigned long *) &sa->packet, sa->length);
253 }
254 printf("\r\n");
255
256 link_transaction_destroy(t);
257}
258
259void
260clear_pending_transaction_list(void)
261{
262 struct link_transaction *t;
263
264 while (!list_empty(&pending_transaction_list)) {
265 t = list_head(&pending_transaction_list, struct link_transaction, link);
266 list_remove(&t->link);
267 link_transaction_destroy(t);
268 /* print unfinished transactions */
269 }
270}
271
272static const char * const tcode_names[] = {
273 "write_quadlet_request",
274 "write_block_request",
275 "write_response",
276 "reserved",
277 "read_quadlet_request",
278 "read_block_request",
279 "read_quadlet_response",
280 "read_block_response",
281 "cycle_start",
282 "lock_request",
283 "iso_data",
284 "lock_response"
285};
286
287static const char * const ack_names[] = {
288 "no ack",
289 "ack_complete",
290 "ack_pending",
291 "reserved (0x03)",
292 "ack_busy_x",
293 "ack_busy_a",
294 "ack_busy_b",
295 "reserved (0x07)",
296 "reserved (0x08)",
297 "reserved (0x09)",
298 "reserved (0x0a)",
299 "reserved (0x0b)",
300 "reserved (0x0c)",
301 "ack_data_error",
302 "ack_type_error",
303 "reserved (0x0f)",
304};
305
306static const char * const rcode_names[] = {
307 "complete",
308 "reserved (0x01)",
309 "reserved (0x02)",
310 "reserved (0x03)",
311 "conflict_error",
312 "data_error",
313 "type_error",
314 "address_error",
315};
316
317static const char * const retry_names[] = {
318 "retry_1",
319 "retry_x",
320 "retry_a",
321 "retry_b",
322};
323
324enum {
325 PACKET_RESERVED,
326 PACKET_REQUEST,
327 PACKET_RESPONSE,
328 PACKET_OTHER,
329};
330
331struct packet_info {
332 const char *name;
333 int type;
334 int response_tcode;
335 struct packet_field *fields;
336 int field_count;
337};
338
339struct packet_field {
340 const char *name; /* Short name for field. */
341 int offset; /* Location of field, specified in bits.
342 * Negative means from end of packet */
343 int width; /* Width of field, 0 means use data_length. */
344 int flags; /* Show options. */
345 const char * const *value_names;
346};
347
348#define COMMON_REQUEST_FIELDS \
349 { "dest", 0, 16, PACKET_FIELD_TRANSACTION }, \
350 { "tl", 16, 6 }, \
351 { "rt", 22, 2, PACKET_FIELD_DETAIL, retry_names }, \
352 { "tcode", 24, 4, PACKET_FIELD_TRANSACTION, tcode_names }, \
353 { "pri", 28, 4, PACKET_FIELD_DETAIL }, \
354 { "src", 32, 16, PACKET_FIELD_TRANSACTION }, \
355 { "offs", 48, 48, PACKET_FIELD_TRANSACTION }
356
357#define COMMON_RESPONSE_FIELDS \
358 { "dest", 0, 16 }, \
359 { "tl", 16, 6 }, \
360 { "rt", 22, 2, PACKET_FIELD_DETAIL, retry_names }, \
361 { "tcode", 24, 4, 0, tcode_names }, \
362 { "pri", 28, 4, PACKET_FIELD_DETAIL }, \
363 { "src", 32, 16 }, \
364 { "rcode", 48, 4, PACKET_FIELD_TRANSACTION, rcode_names }
365
366struct packet_field read_quadlet_request_fields[] = {
367 COMMON_REQUEST_FIELDS,
368 { "crc", 96, 32, PACKET_FIELD_DETAIL },
369 { "ack", 156, 4, 0, ack_names }
370};
371
372struct packet_field read_quadlet_response_fields[] = {
373 COMMON_RESPONSE_FIELDS,
374 { "data", 96, 32, PACKET_FIELD_TRANSACTION },
375 { "crc", 128, 32, PACKET_FIELD_DETAIL },
376 { "ack", 188, 4, 0, ack_names }
377};
378
379struct packet_field read_block_request_fields[] = {
380 COMMON_REQUEST_FIELDS,
381 { "data_length", 96, 16, PACKET_FIELD_TRANSACTION },
382 { "extended_tcode", 112, 16 },
383 { "crc", 128, 32, PACKET_FIELD_DETAIL },
384 { "ack", 188, 4, 0, ack_names },
385};
386
387struct packet_field block_response_fields[] = {
388 COMMON_RESPONSE_FIELDS,
389 { "data_length", 96, 16, PACKET_FIELD_DATA_LENGTH },
390 { "extended_tcode", 112, 16 },
391 { "crc", 128, 32, PACKET_FIELD_DETAIL },
392 { "data", 160, 0, PACKET_FIELD_TRANSACTION },
393 { "crc", -64, 32, PACKET_FIELD_DETAIL },
394 { "ack", -4, 4, 0, ack_names }
395};
396
397struct packet_field write_quadlet_request_fields[] = {
398 COMMON_REQUEST_FIELDS,
399 { "data", 96, 32, PACKET_FIELD_TRANSACTION },
400 { "ack", -4, 4, 0, ack_names }
401};
402
403struct packet_field block_request_fields[] = {
404 COMMON_REQUEST_FIELDS,
405 { "data_length", 96, 16, PACKET_FIELD_DATA_LENGTH | PACKET_FIELD_TRANSACTION },
406 { "extended_tcode", 112, 16, PACKET_FIELD_TRANSACTION },
407 { "crc", 128, 32, PACKET_FIELD_DETAIL },
408 { "data", 160, 0, PACKET_FIELD_TRANSACTION },
409 { "crc", -64, 32, PACKET_FIELD_DETAIL },
410 { "ack", -4, 4, 0, ack_names }
411};
412
413struct packet_field write_response_fields[] = {
414 COMMON_RESPONSE_FIELDS,
415 { "reserved", 64, 32, PACKET_FIELD_DETAIL },
416 { "ack", -4, 4, 0, ack_names }
417};
418
419struct packet_field iso_data_fields[] = {
420 { "data_length", 0, 16, PACKET_FIELD_DATA_LENGTH },
421 { "tag", 16, 2 },
422 { "channel", 18, 6 },
423 { "tcode", 24, 4, 0, tcode_names },
424 { "sy", 28, 4 },
425 { "crc", 32, 32, PACKET_FIELD_DETAIL },
426 { "data", 64, 0 },
427 { "crc", -64, 32, PACKET_FIELD_DETAIL },
428 { "ack", -4, 4, 0, ack_names }
429};
430
431static struct packet_info packet_info[] = {
432 {
433 .name = "write_quadlet_request",
434 .type = PACKET_REQUEST,
435 .response_tcode = TCODE_WRITE_RESPONSE,
436 .fields = write_quadlet_request_fields,
437 .field_count = array_length(write_quadlet_request_fields)
438 },
439 {
440 .name = "write_block_request",
441 .type = PACKET_REQUEST,
442 .response_tcode = TCODE_WRITE_RESPONSE,
443 .fields = block_request_fields,
444 .field_count = array_length(block_request_fields)
445 },
446 {
447 .name = "write_response",
448 .type = PACKET_RESPONSE,
449 .fields = write_response_fields,
450 .field_count = array_length(write_response_fields)
451 },
452 {
453 .name = "reserved",
454 .type = PACKET_RESERVED,
455 },
456 {
457 .name = "read_quadlet_request",
458 .type = PACKET_REQUEST,
459 .response_tcode = TCODE_READ_QUADLET_RESPONSE,
460 .fields = read_quadlet_request_fields,
461 .field_count = array_length(read_quadlet_request_fields)
462 },
463 {
464 .name = "read_block_request",
465 .type = PACKET_REQUEST,
466 .response_tcode = TCODE_READ_BLOCK_RESPONSE,
467 .fields = read_block_request_fields,
468 .field_count = array_length(read_block_request_fields)
469 },
470 {
471 .name = "read_quadlet_response",
472 .type = PACKET_RESPONSE,
473 .fields = read_quadlet_response_fields,
474 .field_count = array_length(read_quadlet_response_fields)
475 },
476 {
477 .name = "read_block_response",
478 .type = PACKET_RESPONSE,
479 .fields = block_response_fields,
480 .field_count = array_length(block_response_fields)
481 },
482 {
483 .name = "cycle_start",
484 .type = PACKET_OTHER,
485 .fields = write_quadlet_request_fields,
486 .field_count = array_length(write_quadlet_request_fields)
487 },
488 {
489 .name = "lock_request",
490 .type = PACKET_REQUEST,
491 .fields = block_request_fields,
492 .field_count = array_length(block_request_fields)
493 },
494 {
495 .name = "iso_data",
496 .type = PACKET_OTHER,
497 .fields = iso_data_fields,
498 .field_count = array_length(iso_data_fields)
499 },
500 {
501 .name = "lock_response",
502 .type = PACKET_RESPONSE,
503 .fields = block_response_fields,
504 .field_count = array_length(block_response_fields)
505 }
506};
507
508int
509handle_packet(unsigned long *data, size_t length)
510{
511 if (length == 0) {
512 printf("bus reset\r\n");
513 clear_pending_transaction_list();
514 }
515 else if (length > sizeof(struct phy_packet)) {
516 struct link_packet *p = (struct link_packet *) data;
517 struct subaction *sa, *prev;
518 struct link_transaction *t;
519
520 switch (packet_info[p->common.tcode].type) {
521 case PACKET_REQUEST:
522 t = link_transaction_lookup(p->common.source, p->common.destination,
523 p->common.tlabel);
524 sa = subaction_create(data, length);
525 t->request = sa;
526
527 if (!list_empty(&t->request_list)) {
528 prev = list_tail(&t->request_list, struct subaction, link);
529
530 if (!ACK_BUSY(prev->ack)) {
531 /* error, we should only see ack_busy_* before the
532 * ack_pending/ack_complete -- this is an ack_pending
533 * instead (ack_complete would have finished the
534 * transaction). */
535 }
536
537 if (prev->packet.common.tcode != sa->packet.common.tcode ||
538 prev->packet.common.tlabel != sa->packet.common.tlabel)
539 /* memcmp() ? */
540 /* error, these should match for retries. */;
541 }
542
543 list_append(&t->request_list, &sa->link);
544
545 switch (sa->ack) {
546 case ACK_COMPLETE:
547 if (p->common.tcode != TCODE_WRITE_QUADLET &&
548 p->common.tcode != TCODE_WRITE_BLOCK)
549 /* error, unified transactions only allowed for write */;
550 list_remove(&t->link);
551 handle_transaction(t);
552 break;
553
554 case ACK_NO_ACK:
555 case ACK_DATA_ERROR:
556 case ACK_TYPE_ERROR:
557 list_remove(&t->link);
558 handle_transaction(t);
559 break;
560
561 case ACK_PENDING:
562 /* request subaction phase over, wait for response. */
563 break;
564
565 case ACK_BUSY_X:
566 case ACK_BUSY_A:
567 case ACK_BUSY_B:
568 /* ok, wait for retry. */
569 /* check that retry protocol is respected. */
570 break;
571 }
572 break;
573
574 case PACKET_RESPONSE:
575 t = link_transaction_lookup(p->common.destination, p->common.source,
576 p->common.tlabel);
577 if (list_empty(&t->request_list)) {
578 /* unsolicited response */
579 }
580
581 sa = subaction_create(data, length);
582 t->response = sa;
583
584 if (!list_empty(&t->response_list)) {
585 prev = list_tail(&t->response_list, struct subaction, link);
586
587 if (!ACK_BUSY(prev->ack))
588 /* error, we should only see ack_busy_* before the
589 * ack_pending/ack_complete */;
590
591 if (prev->packet.common.tcode != sa->packet.common.tcode ||
592 prev->packet.common.tlabel != sa->packet.common.tlabel)
593 /* use memcmp() instead? */
594 /* error, these should match for retries. */;
595 }
596 else {
597 prev = list_tail(&t->request_list, struct subaction, link);
598 if (prev->ack != ACK_PENDING) {
599 /* error, should not get response unless last request got
600 * ack_pending. */
601 }
602
603 if (packet_info[prev->packet.common.tcode].response_tcode !=
604 sa->packet.common.tcode) {
605 /* error, tcode mismatch */
606 }
607 }
608
609 list_append(&t->response_list, &sa->link);
610
611 switch (sa->ack) {
612 case ACK_COMPLETE:
613 case ACK_NO_ACK:
614 case ACK_DATA_ERROR:
615 case ACK_TYPE_ERROR:
616 list_remove(&t->link);
617 handle_transaction(t);
618 /* transaction complete, remove t from pending list. */
619 break;
620
621 case ACK_PENDING:
622 /* error for responses. */
623 break;
624
625 case ACK_BUSY_X:
626 case ACK_BUSY_A:
627 case ACK_BUSY_B:
628 /* no problem, wait for next retry */
629 break;
630 }
631
632 break;
633
634 case PACKET_OTHER:
635 case PACKET_RESERVED:
636 return 0;
637 }
638 }
639
640 return 1;
641}
642
643unsigned int get_bits(struct link_packet *packet, int offset, int width)
644{
645 unsigned long *data = (unsigned long *) packet;
646 unsigned long index, shift, mask;
647
648 index = offset / 32 + 1;
649 shift = 32 - (offset & 31) - width;
650 mask = width == 32 ? ~0 : (1 << width) - 1;
651
652 return (data[index] >> shift) & mask;
653}
654
655#if __BYTE_ORDER == __LITTLE_ENDIAN
656#define byte_index(i) ((i) ^ 3)
657#elif __BYTE_ORDER == __BIG_ENDIAN
658#define byte_index(i) (i)
659#else
660#error unsupported byte order.
661#endif
662
663void dump_data(unsigned char *data, int length)
664{
665 int i, print_length;
666
667 if (length > 128)
668 print_length = 128;
669 else
670 print_length = length;
671
672 for (i = 0; i < print_length; i++)
673 printf("%s%02hhx",
674 (i % 4 == 0 && i != 0) ? " " : "",
675 data[byte_index(i)]);
676
677 if (print_length < length)
678 printf(" (%d more bytes)", length - print_length);
679}
680
681static void
682decode_link_packet(struct link_packet *packet, size_t length,
683 int include_flags, int exclude_flags)
684{
685 struct packet_info *pi;
686 int data_length = 0;
687 int i;
688
689 pi = &packet_info[packet->common.tcode];
690
691 for (i = 0; i < pi->field_count; i++) {
692 struct packet_field *f = &pi->fields[i];
693 int offset;
694
695 if (f->flags & exclude_flags)
696 continue;
697 if (include_flags && !(f->flags & include_flags))
698 continue;
699
700 if (f->offset < 0)
701 offset = length * 8 + f->offset - 32;
702 else
703 offset = f->offset;
704
705 if (f->value_names != NULL) {
706 unsigned long bits;
707
708 bits = get_bits(packet, offset, f->width);
709 printf("%s", f->value_names[bits]);
710 }
711 else if (f->width == 0) {
712 printf("%s=[", f->name);
713 dump_data((unsigned char *) packet + (offset / 8 + 4), data_length);
714 printf("]");
715 }
716 else {
717 unsigned long long bits;
718 int high_width, low_width;
719
720 if ((offset & ~31) != ((offset + f->width - 1) & ~31)) {
721 /* Bit field spans quadlet boundary. */
722 high_width = ((offset + 31) & ~31) - offset;
723 low_width = f->width - high_width;
724
725 bits = get_bits(packet, offset, high_width);
726 bits = (bits << low_width) |
727 get_bits(packet, offset + high_width, low_width);
728 }
729 else
730 bits = get_bits(packet, offset, f->width);
731
732 printf("%s=0x%0*llx", f->name, (f->width + 3) / 4, bits);
733
734 if (f->flags & PACKET_FIELD_DATA_LENGTH)
735 data_length = bits;
736 }
737
738 if (i < pi->field_count - 1)
739 printf(", ");
740 }
741}
742
743static void
744print_packet(unsigned long *data, size_t length)
745{
746 int i;
747
748 printf("%6lu ", data[0]);
749
750 if (length == 4)
751 printf("bus reset");
752 else if (length < sizeof(struct phy_packet)) {
753 printf("short packet: ");
754 for (i = 1; i < length / 4; i++)
755 printf("%s%08lx", i == 0 ? "[" : " ", data[i]);
756 printf("]");
757
758 }
759 else if (length == sizeof(struct phy_packet) && data[1] == ~data[2]) {
760 struct phy_packet *pp = (struct phy_packet *) data;
761
762 /* phy packet are 3 quadlets: the 1 quadlet payload,
763 * the bitwise inverse of the payload and the snoop
764 * mode ack */
765
766 switch (pp->common.identifier) {
767 case PHY_PACKET_CONFIGURATION:
768 if (!pp->phy_config.set_root && !pp->phy_config.set_gap_count) {
769 printf("ext phy config: phy_id=%02x", pp->phy_config.root_id);
770 }
771 else {
772 printf("phy config:");
773 if (pp->phy_config.set_root)
774 printf(" set_root_id=%02x", pp->phy_config.root_id);
775 if (pp->phy_config.set_gap_count)
776 printf(" set_gap_count=%d", pp->phy_config.gap_count);
777 }
778 break;
779
780 case PHY_PACKET_LINK_ON:
781 printf("link-on packet, phy_id=%02x", pp->link_on.phy_id);
782 break;
783
784 case PHY_PACKET_SELF_ID:
785 if (pp->self_id.extended) {
786 printf("extended self id: phy_id=%02x, seq=%d",
787 pp->ext_self_id.phy_id, pp->ext_self_id.sequence);
788 }
789 else {
790 static const char * const speed_names[] = {
791 "S100", "S200", "S400", "BETA"
792 };
793 printf("self id: phy_id=%02x, link %s, gap_count=%d, speed=%s%s%s",
794 pp->self_id.phy_id,
795 (pp->self_id.link_active ? "active" : "not active"),
796 pp->self_id.gap_count,
797 speed_names[pp->self_id.phy_speed],
798 (pp->self_id.contender ? ", irm contender" : ""),
799 (pp->self_id.initiated_reset ? ", initiator" : ""));
800
801 }
802 break;
803 default:
804 printf("unknown phy packet: ");
805 for (i = 1; i < length / 4; i++)
806 printf("%s%08lx", i == 0 ? "[" : " ", data[i]);
807 printf("]");
808 break;
809 }
810 }
811 else {
812 struct link_packet *packet = (struct link_packet *) data;
813
814 decode_link_packet(packet, length, 0,
815 option_verbose ? 0 : PACKET_FIELD_DETAIL);
816 }
817
818 if (option_hex) {
819 printf(" [");
820 dump_data((unsigned char *) data + 4, length - 4);
821 printf("]");
822 }
823
824 printf("\r\n");
825}
826
827#define HIDE_CURSOR "\033[?25l"
828#define SHOW_CURSOR "\033[?25h"
829#define CLEAR "\033[H\033[2J"
830
831static void
832print_stats(unsigned long *data, size_t length)
833{
834 static int bus_reset_count, short_packet_count, phy_packet_count;
835 static int tcode_count[16];
836 static struct timeval last_update;
837 struct timeval now;
838 int i;
839
840 if (length == 0)
841 bus_reset_count++;
842 else if (length < sizeof(struct phy_packet))
843 short_packet_count++;
844 else if (length == sizeof(struct phy_packet) && data[1] == ~data[2])
845 phy_packet_count++;
846 else {
847 struct link_packet *packet = (struct link_packet *) data;
848 tcode_count[packet->common.tcode]++;
849 }
850
851 gettimeofday(&now, NULL);
852 if (now.tv_sec <= last_update.tv_sec &&
853 now.tv_usec < last_update.tv_usec + 500000)
854 return;
855
856 last_update = now;
857 printf(CLEAR HIDE_CURSOR
858 " bus resets : %8d\n"
859 " short packets : %8d\n"
860 " phy packets : %8d\n",
861 bus_reset_count, short_packet_count, phy_packet_count);
862
863 for (i = 0; i < array_length(packet_info); i++)
864 if (packet_info[i].type != PACKET_RESERVED)
865 printf(" %-24s: %8d\n", packet_info[i].name, tcode_count[i]);
866 printf(SHOW_CURSOR "\n");
867}
868
869struct termios saved_attributes;
870
871void
872reset_input_mode (void)
873{
874 tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes);
875}
876
877void
878set_input_mode (void)
879{
880 struct termios tattr;
881
882 /* Make sure stdin is a terminal. */
883 if (!isatty(STDIN_FILENO)) {
884 fprintf(stderr, "Not a terminal.\n");
885 exit(EXIT_FAILURE);
886 }
887
888 /* Save the terminal attributes so we can restore them later. */
889 tcgetattr(STDIN_FILENO, &saved_attributes);
890 atexit(reset_input_mode);
891
892 /* Set the funny terminal modes. */
893 tcgetattr(STDIN_FILENO, &tattr);
894 tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
895 tattr.c_cc[VMIN] = 1;
896 tattr.c_cc[VTIME] = 0;
897 tcsetattr(STDIN_FILENO, TCSAFLUSH, &tattr);
898}
899
900int main(int argc, const char *argv[])
901{
902 int fd = -1;
903 FILE *output = NULL, *input = NULL;
904 poptContext con;
905 int retval;
906 int view;
907 char c;
908 struct pollfd pollfds[2];
909
910 sys_sigint_handler = signal(SIGINT, sigint_handler);
911
912 con = poptGetContext(NULL, argc, argv, options, 0);
913 retval = poptGetNextOpt(con);
914 if (retval < -1) {
915 poptPrintUsage(con, stdout, 0);
916 return -1;
917 }
918
919 if (option_version) {
920 printf("dump tool for nosy sniffer, version %s\n", VERSION);
921 return 0;
922 }
923
924 if (__BYTE_ORDER != __LITTLE_ENDIAN)
925 fprintf(stderr, "warning: nosy has only been tested on little "
926 "endian machines\n");
927
928 if (option_input != NULL) {
929 input = fopen(option_input, "r");
930 if (input == NULL) {
931 fprintf(stderr, "Could not open %s, %m\n", option_input);
932 return -1;
933 }
934 }
935 else {
936 fd = open(option_nosy_device, O_RDWR);
937 if (fd < 0) {
938 fprintf(stderr, "Could not open %s, %m\n", option_nosy_device);
939 return -1;
940 }
941 set_input_mode();
942 }
943
944 if (strcmp(option_view, "transaction") == 0)
945 view = VIEW_TRANSACTION;
946 else if (strcmp(option_view, "stats") == 0)
947 view = VIEW_STATS;
948 else
949 view = VIEW_PACKET;
950
951 if (option_output) {
952 output = fopen(option_output, "w");
953 if (output == NULL) {
954 fprintf(stderr, "Could not open %s, %m\n", option_output);
955 return -1;
956 }
957 }
958
959 setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
960
961 if (1) {
962 unsigned long buf[128 * 1024];
963 unsigned int filter;
964 int length;
965
966 filter = ~0;
967 if (!option_iso)
968 filter &= ~(1 <<TCODE_ISO_DATA);
969 if (!option_cycle_start)
970 filter &= ~(1 << TCODE_CYCLE_START);
971
972 if (view == VIEW_STATS)
973 ioctl(fd, NOSY_IOC_FILTER, ~(1 << TCODE_CYCLE_START));
974 else
975 ioctl(fd, NOSY_IOC_FILTER, filter);
976
977 ioctl(fd, NOSY_IOC_START);
978
979 pollfds[0].fd = fd;
980 pollfds[0].events = POLLIN;
981 pollfds[1].fd = STDIN_FILENO;
982 pollfds[1].events = POLLIN;
983
984 while (run) {
985 if (input != NULL) {
986 if (fread(&length, sizeof length, 1, input) != 1)
987 return 0;
988 fread(buf, 1, length, input);
989 }
990 else {
991 poll(pollfds, 2, -1);
992 if (pollfds[1].revents) {
993 read(STDIN_FILENO, &c, sizeof c);
994 switch (c) {
995 case 'q':
996 if (output != NULL)
997 fclose(output);
998 return 0;
999 }
1000 }
1001
1002 if (pollfds[0].revents)
1003 length = read(fd, buf, sizeof buf);
1004 else
1005 continue;
1006 }
1007
1008 if (output != NULL) {
1009 fwrite(&length, sizeof length, 1, output);
1010 fwrite(buf, 1, length, output);
1011 }
1012
1013 switch (view) {
1014 case VIEW_TRANSACTION:
1015 handle_packet(buf, length);
1016 break;
1017 case VIEW_PACKET:
1018 print_packet(buf, length);
1019 break;
1020 case VIEW_STATS:
1021 print_stats(buf, length);
1022 break;
1023 }
1024 }
1025 }
1026 else
1027 poptPrintUsage(con, stdout, 0);
1028
1029 if (output != NULL)
1030 fclose(output);
1031
1032 close(fd);
1033
1034 poptFreeContext(con);
1035
1036 return 0;
1037}