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