1022 lines · 24.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * nosy-dump - Interface to snoop mode driver for TI PCILynx 1394 controllers * Copyright (C) 2002-2006 Kristian Høgsberg */ #include <byteswap.h>#include <endian.h>#include <fcntl.h>#include <linux/firewire-constants.h>#include <poll.h>#include <popt.h>#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/ioctl.h>#include <sys/time.h>#include <termios.h>#include <unistd.h> #include "list.h"#include "nosy-dump.h"#include "nosy-user.h" enum {	PACKET_FIELD_DETAIL		= 0x01,	PACKET_FIELD_DATA_LENGTH	= 0x02,	/* Marks the fields we print in transaction view. */	PACKET_FIELD_TRANSACTION	= 0x04,}; static void print_packet(uint32_t *data, size_t length);static void decode_link_packet(struct link_packet *packet, size_t length,			       int include_flags, int exclude_flags);static int run = 1;sig_t sys_sigint_handler; static char *option_nosy_device = "/dev/nosy";static char *option_view = "packet";static char *option_output;static char *option_input;static int option_hex;static int option_iso;static int option_cycle_start;static int option_version;static int option_verbose; enum {	VIEW_TRANSACTION,	VIEW_PACKET,	VIEW_STATS,}; static const struct poptOption options[] = {	{		.longName	= "device",		.shortName	= 'd',		.argInfo	= POPT_ARG_STRING,		.arg		= &option_nosy_device,		.descrip	= "Path to nosy device.",		.argDescrip	= "DEVICE"	},	{		.longName	= "view",		.argInfo	= POPT_ARG_STRING,		.arg		= &option_view,		.descrip	= "Specify view of bus traffic: packet, transaction or stats.",		.argDescrip	= "VIEW"	},	{		.longName	= "hex",		.shortName	= 'x',		.argInfo	= POPT_ARG_NONE,		.arg		= &option_hex,		.descrip	= "Print each packet in hex.",	},	{		.longName	= "iso",		.argInfo	= POPT_ARG_NONE,		.arg		= &option_iso,		.descrip	= "Print iso packets.",	},	{		.longName	= "cycle-start",		.argInfo	= POPT_ARG_NONE,		.arg		= &option_cycle_start,		.descrip	= "Print cycle start packets.",	},	{		.longName	= "verbose",		.shortName	= 'v',		.argInfo	= POPT_ARG_NONE,		.arg		= &option_verbose,		.descrip	= "Verbose packet view.",	},	{		.longName	= "output",		.shortName	= 'o',		.argInfo	= POPT_ARG_STRING,		.arg		= &option_output,		.descrip	= "Log to output file.",		.argDescrip	= "FILENAME"	},	{		.longName	= "input",		.shortName	= 'i',		.argInfo	= POPT_ARG_STRING,		.arg		= &option_input,		.descrip	= "Decode log from file.",		.argDescrip	= "FILENAME"	},	{		.longName	= "version",		.argInfo	= POPT_ARG_NONE,		.arg		= &option_version,		.descrip	= "Specify print version info.",	},	POPT_AUTOHELP	POPT_TABLEEND}; /* Allow all ^C except the first to interrupt the program in the usual way. */static voidsigint_handler(int signal_num){	if (run == 1) {		run = 0;		signal(SIGINT, SIG_DFL);	}} static struct subaction *subaction_create(uint32_t *data, size_t length){	struct subaction *sa; 	/* we put the ack in the subaction struct for easy access. */	sa = malloc(sizeof *sa - sizeof sa->packet + length);	if (!sa)		exit(EXIT_FAILURE);	sa->ack = data[length / 4 - 1];	sa->length = length;	memcpy(&sa->packet, data, length); 	return sa;} static voidsubaction_destroy(struct subaction *sa){	free(sa);} static struct list pending_transaction_list = {	&pending_transaction_list, &pending_transaction_list}; static struct link_transaction *link_transaction_lookup(int request_node, int response_node, int tlabel){	struct link_transaction *t; 	list_for_each_entry(t, &pending_transaction_list, link) {		if (t->request_node == request_node &&		    t->response_node == response_node &&		    t->tlabel == tlabel)			return t;	} 	t = malloc(sizeof *t);	if (!t)		exit(EXIT_FAILURE);	t->request_node = request_node;	t->response_node = response_node;	t->tlabel = tlabel;	list_init(&t->request_list);	list_init(&t->response_list); 	list_append(&pending_transaction_list, &t->link); 	return t;} static voidlink_transaction_destroy(struct link_transaction *t){	struct subaction *sa; 	while (!list_empty(&t->request_list)) {		sa = list_head(&t->request_list, struct subaction, link);		list_remove(&sa->link);		subaction_destroy(sa);	}	while (!list_empty(&t->response_list)) {		sa = list_head(&t->response_list, struct subaction, link);		list_remove(&sa->link);		subaction_destroy(sa);	}	free(t);} struct protocol_decoder {	const char *name;	int (*decode)(struct link_transaction *t);}; static const struct protocol_decoder protocol_decoders[] = {	{ "FCP", decode_fcp }}; static voidhandle_transaction(struct link_transaction *t){	struct subaction *sa;	int i; 	if (!t->request) {		printf("BUG in handle_transaction\n");		return;	} 	for (i = 0; i < array_length(protocol_decoders); i++)		if (protocol_decoders[i].decode(t))			break; 	/* HACK: decode only fcp right now. */	return; 	decode_link_packet(&t->request->packet, t->request->length,			   PACKET_FIELD_TRANSACTION, 0);	if (t->response)		decode_link_packet(&t->response->packet, t->request->length,				   PACKET_FIELD_TRANSACTION, 0);	else		printf("[no response]"); 	if (option_verbose) {		list_for_each_entry(sa, &t->request_list, link)			print_packet((uint32_t *) &sa->packet, sa->length);		list_for_each_entry(sa, &t->response_list, link)			print_packet((uint32_t *) &sa->packet, sa->length);	}	printf("\r\n"); 	link_transaction_destroy(t);} static voidclear_pending_transaction_list(void){	struct link_transaction *t; 	while (!list_empty(&pending_transaction_list)) {		t = list_head(&pending_transaction_list,			      struct link_transaction, link);		list_remove(&t->link);		link_transaction_destroy(t);		/* print unfinished transactions */	}} static const char * const tcode_names[] = {	[0x0] = "write_quadlet_request",	[0x6] = "read_quadlet_response",	[0x1] = "write_block_request",		[0x7] = "read_block_response",	[0x2] = "write_response",		[0x8] = "cycle_start",	[0x3] = "reserved",			[0x9] = "lock_request",	[0x4] = "read_quadlet_request",		[0xa] = "iso_data",	[0x5] = "read_block_request",		[0xb] = "lock_response",}; static const char * const ack_names[] = {	[0x0] = "no ack",			[0x8] = "reserved (0x08)",	[0x1] = "ack_complete",			[0x9] = "reserved (0x09)",	[0x2] = "ack_pending",			[0xa] = "reserved (0x0a)",	[0x3] = "reserved (0x03)",		[0xb] = "reserved (0x0b)",	[0x4] = "ack_busy_x",			[0xc] = "reserved (0x0c)",	[0x5] = "ack_busy_a",			[0xd] = "ack_data_error",	[0x6] = "ack_busy_b",			[0xe] = "ack_type_error",	[0x7] = "reserved (0x07)",		[0xf] = "reserved (0x0f)",}; static const char * const rcode_names[] = {	[0x0] = "complete",			[0x4] = "conflict_error",	[0x1] = "reserved (0x01)",		[0x5] = "data_error",	[0x2] = "reserved (0x02)",		[0x6] = "type_error",	[0x3] = "reserved (0x03)",		[0x7] = "address_error",}; static const char * const retry_names[] = {	[0x0] = "retry_1",	[0x1] = "retry_x",	[0x2] = "retry_a",	[0x3] = "retry_b",}; enum {	PACKET_RESERVED,	PACKET_REQUEST,	PACKET_RESPONSE,	PACKET_OTHER,}; struct packet_info {	const char *name;	int type;	int response_tcode;	const struct packet_field *fields;	int field_count;}; struct packet_field {	const char *name; /* Short name for field. */	int offset;	/* Location of field, specified in bits; */			/* negative means from end of packet.    */	int width;	/* Width of field, 0 means use data_length. */	int flags;	/* Show options. */	const char * const *value_names;}; #define COMMON_REQUEST_FIELDS						\	{ "dest", 0, 16, PACKET_FIELD_TRANSACTION },			\	{ "tl", 16, 6 },						\	{ "rt", 22, 2, PACKET_FIELD_DETAIL, retry_names },		\	{ "tcode", 24, 4, PACKET_FIELD_TRANSACTION, tcode_names },	\	{ "pri", 28, 4, PACKET_FIELD_DETAIL },				\	{ "src", 32, 16, PACKET_FIELD_TRANSACTION },			\	{ "offs", 48, 48, PACKET_FIELD_TRANSACTION } #define COMMON_RESPONSE_FIELDS						\	{ "dest", 0, 16 },						\	{ "tl", 16, 6 },						\	{ "rt", 22, 2, PACKET_FIELD_DETAIL, retry_names },		\	{ "tcode", 24, 4, 0, tcode_names },				\	{ "pri", 28, 4, PACKET_FIELD_DETAIL },				\	{ "src", 32, 16 },						\	{ "rcode", 48, 4, PACKET_FIELD_TRANSACTION, rcode_names } static const struct packet_field read_quadlet_request_fields[] = {	COMMON_REQUEST_FIELDS,	{ "crc", 96, 32, PACKET_FIELD_DETAIL },	{ "ack", 156, 4, 0, ack_names },}; static const struct packet_field read_quadlet_response_fields[] = {	COMMON_RESPONSE_FIELDS,	{ "data", 96, 32, PACKET_FIELD_TRANSACTION },	{ "crc", 128, 32, PACKET_FIELD_DETAIL },	{ "ack", 188, 4, 0, ack_names },}; static const struct packet_field read_block_request_fields[] = {	COMMON_REQUEST_FIELDS,	{ "data_length", 96, 16, PACKET_FIELD_TRANSACTION },	{ "extended_tcode", 112, 16 },	{ "crc", 128, 32, PACKET_FIELD_DETAIL },	{ "ack", 188, 4, 0, ack_names },}; static const struct packet_field block_response_fields[] = {	COMMON_RESPONSE_FIELDS,	{ "data_length", 96, 16, PACKET_FIELD_DATA_LENGTH },	{ "extended_tcode", 112, 16 },	{ "crc", 128, 32, PACKET_FIELD_DETAIL },	{ "data", 160, 0, PACKET_FIELD_TRANSACTION },	{ "crc", -64, 32, PACKET_FIELD_DETAIL },	{ "ack", -4, 4, 0, ack_names },}; static const struct packet_field write_quadlet_request_fields[] = {	COMMON_REQUEST_FIELDS,	{ "data", 96, 32, PACKET_FIELD_TRANSACTION },	{ "ack", -4, 4, 0, ack_names },}; static const struct packet_field block_request_fields[] = {	COMMON_REQUEST_FIELDS,	{ "data_length", 96, 16, PACKET_FIELD_DATA_LENGTH | PACKET_FIELD_TRANSACTION },	{ "extended_tcode", 112, 16, PACKET_FIELD_TRANSACTION },	{ "crc", 128, 32, PACKET_FIELD_DETAIL },	{ "data", 160, 0, PACKET_FIELD_TRANSACTION },	{ "crc", -64, 32, PACKET_FIELD_DETAIL },	{ "ack", -4, 4, 0, ack_names },}; static const struct packet_field write_response_fields[] = {	COMMON_RESPONSE_FIELDS,	{ "reserved", 64, 32, PACKET_FIELD_DETAIL },	{ "ack", -4, 4, 0, ack_names },}; static const struct packet_field iso_data_fields[] = {	{ "data_length", 0, 16, PACKET_FIELD_DATA_LENGTH },	{ "tag", 16, 2 },	{ "channel", 18, 6 },	{ "tcode", 24, 4, 0, tcode_names },	{ "sy", 28, 4 },	{ "crc", 32, 32, PACKET_FIELD_DETAIL },	{ "data", 64, 0 },	{ "crc", -64, 32, PACKET_FIELD_DETAIL },	{ "ack", -4, 4, 0, ack_names },}; static const struct packet_info packet_info[] = {	{		.name		= "write_quadlet_request",		.type		= PACKET_REQUEST,		.response_tcode	= TCODE_WRITE_RESPONSE,		.fields		= write_quadlet_request_fields,		.field_count	= array_length(write_quadlet_request_fields)	},	{		.name		= "write_block_request",		.type		= PACKET_REQUEST,		.response_tcode	= TCODE_WRITE_RESPONSE,		.fields		= block_request_fields,		.field_count	= array_length(block_request_fields)	},	{		.name		= "write_response",		.type		= PACKET_RESPONSE,		.fields		= write_response_fields,		.field_count	= array_length(write_response_fields)	},	{		.name		= "reserved",		.type		= PACKET_RESERVED,	},	{		.name		= "read_quadlet_request",		.type		= PACKET_REQUEST,		.response_tcode	= TCODE_READ_QUADLET_RESPONSE,		.fields		= read_quadlet_request_fields,		.field_count	= array_length(read_quadlet_request_fields)	},	{		.name		= "read_block_request",		.type		= PACKET_REQUEST,		.response_tcode	= TCODE_READ_BLOCK_RESPONSE,		.fields		= read_block_request_fields,		.field_count	= array_length(read_block_request_fields)	},	{		.name		= "read_quadlet_response",		.type		= PACKET_RESPONSE,		.fields		= read_quadlet_response_fields,		.field_count	= array_length(read_quadlet_response_fields)	},	{		.name		= "read_block_response",		.type		= PACKET_RESPONSE,		.fields		= block_response_fields,		.field_count	= array_length(block_response_fields)	},	{		.name		= "cycle_start",		.type		= PACKET_OTHER,		.fields		= write_quadlet_request_fields,		.field_count	= array_length(write_quadlet_request_fields)	},	{		.name		= "lock_request",		.type		= PACKET_REQUEST,		.fields		= block_request_fields,		.field_count	= array_length(block_request_fields)	},	{		.name		= "iso_data",		.type		= PACKET_OTHER,		.fields		= iso_data_fields,		.field_count	= array_length(iso_data_fields)	},	{		.name		= "lock_response",		.type		= PACKET_RESPONSE,		.fields		= block_response_fields,		.field_count	= array_length(block_response_fields)	},}; static inthandle_request_packet(uint32_t *data, size_t length){	struct link_packet *p = (struct link_packet *) data;	struct subaction *sa, *prev;	struct link_transaction *t; 	t = link_transaction_lookup(p->common.source, p->common.destination,			p->common.tlabel);	sa = subaction_create(data, length);	t->request = sa; 	if (!list_empty(&t->request_list)) {		prev = list_tail(&t->request_list,				 struct subaction, link); 		if (!ACK_BUSY(prev->ack)) {			/*			 * error, we should only see ack_busy_* before the			 * ack_pending/ack_complete -- this is an ack_pending

Lines 1–500 of 1022.