brintos

brintos / linux-shallow public Read only

0
0
Text · 24.5 KiB · 156e035 Raw
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