18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * nosy-dump - Interface to snoop mode driver for TI PCILynx 1394 controllers
48c2ecf20Sopenharmony_ci * Copyright (C) 2002-2006 Kristian Høgsberg
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <byteswap.h>
88c2ecf20Sopenharmony_ci#include <endian.h>
98c2ecf20Sopenharmony_ci#include <fcntl.h>
108c2ecf20Sopenharmony_ci#include <linux/firewire-constants.h>
118c2ecf20Sopenharmony_ci#include <poll.h>
128c2ecf20Sopenharmony_ci#include <popt.h>
138c2ecf20Sopenharmony_ci#include <signal.h>
148c2ecf20Sopenharmony_ci#include <stdio.h>
158c2ecf20Sopenharmony_ci#include <stdlib.h>
168c2ecf20Sopenharmony_ci#include <string.h>
178c2ecf20Sopenharmony_ci#include <sys/ioctl.h>
188c2ecf20Sopenharmony_ci#include <sys/time.h>
198c2ecf20Sopenharmony_ci#include <termios.h>
208c2ecf20Sopenharmony_ci#include <unistd.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include "list.h"
238c2ecf20Sopenharmony_ci#include "nosy-dump.h"
248c2ecf20Sopenharmony_ci#include "nosy-user.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cienum {
278c2ecf20Sopenharmony_ci	PACKET_FIELD_DETAIL		= 0x01,
288c2ecf20Sopenharmony_ci	PACKET_FIELD_DATA_LENGTH	= 0x02,
298c2ecf20Sopenharmony_ci	/* Marks the fields we print in transaction view. */
308c2ecf20Sopenharmony_ci	PACKET_FIELD_TRANSACTION	= 0x04,
318c2ecf20Sopenharmony_ci};
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic void print_packet(uint32_t *data, size_t length);
348c2ecf20Sopenharmony_cistatic void decode_link_packet(struct link_packet *packet, size_t length,
358c2ecf20Sopenharmony_ci			       int include_flags, int exclude_flags);
368c2ecf20Sopenharmony_cistatic int run = 1;
378c2ecf20Sopenharmony_cisig_t sys_sigint_handler;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic char *option_nosy_device = "/dev/nosy";
408c2ecf20Sopenharmony_cistatic char *option_view = "packet";
418c2ecf20Sopenharmony_cistatic char *option_output;
428c2ecf20Sopenharmony_cistatic char *option_input;
438c2ecf20Sopenharmony_cistatic int option_hex;
448c2ecf20Sopenharmony_cistatic int option_iso;
458c2ecf20Sopenharmony_cistatic int option_cycle_start;
468c2ecf20Sopenharmony_cistatic int option_version;
478c2ecf20Sopenharmony_cistatic int option_verbose;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cienum {
508c2ecf20Sopenharmony_ci	VIEW_TRANSACTION,
518c2ecf20Sopenharmony_ci	VIEW_PACKET,
528c2ecf20Sopenharmony_ci	VIEW_STATS,
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic const struct poptOption options[] = {
568c2ecf20Sopenharmony_ci	{
578c2ecf20Sopenharmony_ci		.longName	= "device",
588c2ecf20Sopenharmony_ci		.shortName	= 'd',
598c2ecf20Sopenharmony_ci		.argInfo	= POPT_ARG_STRING,
608c2ecf20Sopenharmony_ci		.arg		= &option_nosy_device,
618c2ecf20Sopenharmony_ci		.descrip	= "Path to nosy device.",
628c2ecf20Sopenharmony_ci		.argDescrip	= "DEVICE"
638c2ecf20Sopenharmony_ci	},
648c2ecf20Sopenharmony_ci	{
658c2ecf20Sopenharmony_ci		.longName	= "view",
668c2ecf20Sopenharmony_ci		.argInfo	= POPT_ARG_STRING,
678c2ecf20Sopenharmony_ci		.arg		= &option_view,
688c2ecf20Sopenharmony_ci		.descrip	= "Specify view of bus traffic: packet, transaction or stats.",
698c2ecf20Sopenharmony_ci		.argDescrip	= "VIEW"
708c2ecf20Sopenharmony_ci	},
718c2ecf20Sopenharmony_ci	{
728c2ecf20Sopenharmony_ci		.longName	= "hex",
738c2ecf20Sopenharmony_ci		.shortName	= 'x',
748c2ecf20Sopenharmony_ci		.argInfo	= POPT_ARG_NONE,
758c2ecf20Sopenharmony_ci		.arg		= &option_hex,
768c2ecf20Sopenharmony_ci		.descrip	= "Print each packet in hex.",
778c2ecf20Sopenharmony_ci	},
788c2ecf20Sopenharmony_ci	{
798c2ecf20Sopenharmony_ci		.longName	= "iso",
808c2ecf20Sopenharmony_ci		.argInfo	= POPT_ARG_NONE,
818c2ecf20Sopenharmony_ci		.arg		= &option_iso,
828c2ecf20Sopenharmony_ci		.descrip	= "Print iso packets.",
838c2ecf20Sopenharmony_ci	},
848c2ecf20Sopenharmony_ci	{
858c2ecf20Sopenharmony_ci		.longName	= "cycle-start",
868c2ecf20Sopenharmony_ci		.argInfo	= POPT_ARG_NONE,
878c2ecf20Sopenharmony_ci		.arg		= &option_cycle_start,
888c2ecf20Sopenharmony_ci		.descrip	= "Print cycle start packets.",
898c2ecf20Sopenharmony_ci	},
908c2ecf20Sopenharmony_ci	{
918c2ecf20Sopenharmony_ci		.longName	= "verbose",
928c2ecf20Sopenharmony_ci		.shortName	= 'v',
938c2ecf20Sopenharmony_ci		.argInfo	= POPT_ARG_NONE,
948c2ecf20Sopenharmony_ci		.arg		= &option_verbose,
958c2ecf20Sopenharmony_ci		.descrip	= "Verbose packet view.",
968c2ecf20Sopenharmony_ci	},
978c2ecf20Sopenharmony_ci	{
988c2ecf20Sopenharmony_ci		.longName	= "output",
998c2ecf20Sopenharmony_ci		.shortName	= 'o',
1008c2ecf20Sopenharmony_ci		.argInfo	= POPT_ARG_STRING,
1018c2ecf20Sopenharmony_ci		.arg		= &option_output,
1028c2ecf20Sopenharmony_ci		.descrip	= "Log to output file.",
1038c2ecf20Sopenharmony_ci		.argDescrip	= "FILENAME"
1048c2ecf20Sopenharmony_ci	},
1058c2ecf20Sopenharmony_ci	{
1068c2ecf20Sopenharmony_ci		.longName	= "input",
1078c2ecf20Sopenharmony_ci		.shortName	= 'i',
1088c2ecf20Sopenharmony_ci		.argInfo	= POPT_ARG_STRING,
1098c2ecf20Sopenharmony_ci		.arg		= &option_input,
1108c2ecf20Sopenharmony_ci		.descrip	= "Decode log from file.",
1118c2ecf20Sopenharmony_ci		.argDescrip	= "FILENAME"
1128c2ecf20Sopenharmony_ci	},
1138c2ecf20Sopenharmony_ci	{
1148c2ecf20Sopenharmony_ci		.longName	= "version",
1158c2ecf20Sopenharmony_ci		.argInfo	= POPT_ARG_NONE,
1168c2ecf20Sopenharmony_ci		.arg		= &option_version,
1178c2ecf20Sopenharmony_ci		.descrip	= "Specify print version info.",
1188c2ecf20Sopenharmony_ci	},
1198c2ecf20Sopenharmony_ci	POPT_AUTOHELP
1208c2ecf20Sopenharmony_ci	POPT_TABLEEND
1218c2ecf20Sopenharmony_ci};
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/* Allow all ^C except the first to interrupt the program in the usual way. */
1248c2ecf20Sopenharmony_cistatic void
1258c2ecf20Sopenharmony_cisigint_handler(int signal_num)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	if (run == 1) {
1288c2ecf20Sopenharmony_ci		run = 0;
1298c2ecf20Sopenharmony_ci		signal(SIGINT, SIG_DFL);
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic struct subaction *
1348c2ecf20Sopenharmony_cisubaction_create(uint32_t *data, size_t length)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	struct subaction *sa;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	/* we put the ack in the subaction struct for easy access. */
1398c2ecf20Sopenharmony_ci	sa = malloc(sizeof *sa - sizeof sa->packet + length);
1408c2ecf20Sopenharmony_ci	if (!sa)
1418c2ecf20Sopenharmony_ci		exit(EXIT_FAILURE);
1428c2ecf20Sopenharmony_ci	sa->ack = data[length / 4 - 1];
1438c2ecf20Sopenharmony_ci	sa->length = length;
1448c2ecf20Sopenharmony_ci	memcpy(&sa->packet, data, length);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	return sa;
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic void
1508c2ecf20Sopenharmony_cisubaction_destroy(struct subaction *sa)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	free(sa);
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic struct list pending_transaction_list = {
1568c2ecf20Sopenharmony_ci	&pending_transaction_list, &pending_transaction_list
1578c2ecf20Sopenharmony_ci};
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic struct link_transaction *
1608c2ecf20Sopenharmony_cilink_transaction_lookup(int request_node, int response_node, int tlabel)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	struct link_transaction *t;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	list_for_each_entry(t, &pending_transaction_list, link) {
1658c2ecf20Sopenharmony_ci		if (t->request_node == request_node &&
1668c2ecf20Sopenharmony_ci		    t->response_node == response_node &&
1678c2ecf20Sopenharmony_ci		    t->tlabel == tlabel)
1688c2ecf20Sopenharmony_ci			return t;
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	t = malloc(sizeof *t);
1728c2ecf20Sopenharmony_ci	if (!t)
1738c2ecf20Sopenharmony_ci		exit(EXIT_FAILURE);
1748c2ecf20Sopenharmony_ci	t->request_node = request_node;
1758c2ecf20Sopenharmony_ci	t->response_node = response_node;
1768c2ecf20Sopenharmony_ci	t->tlabel = tlabel;
1778c2ecf20Sopenharmony_ci	list_init(&t->request_list);
1788c2ecf20Sopenharmony_ci	list_init(&t->response_list);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	list_append(&pending_transaction_list, &t->link);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	return t;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic void
1868c2ecf20Sopenharmony_cilink_transaction_destroy(struct link_transaction *t)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct subaction *sa;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	while (!list_empty(&t->request_list)) {
1918c2ecf20Sopenharmony_ci		sa = list_head(&t->request_list, struct subaction, link);
1928c2ecf20Sopenharmony_ci		list_remove(&sa->link);
1938c2ecf20Sopenharmony_ci		subaction_destroy(sa);
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci	while (!list_empty(&t->response_list)) {
1968c2ecf20Sopenharmony_ci		sa = list_head(&t->response_list, struct subaction, link);
1978c2ecf20Sopenharmony_ci		list_remove(&sa->link);
1988c2ecf20Sopenharmony_ci		subaction_destroy(sa);
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci	free(t);
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistruct protocol_decoder {
2048c2ecf20Sopenharmony_ci	const char *name;
2058c2ecf20Sopenharmony_ci	int (*decode)(struct link_transaction *t);
2068c2ecf20Sopenharmony_ci};
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic const struct protocol_decoder protocol_decoders[] = {
2098c2ecf20Sopenharmony_ci	{ "FCP", decode_fcp }
2108c2ecf20Sopenharmony_ci};
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic void
2138c2ecf20Sopenharmony_cihandle_transaction(struct link_transaction *t)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	struct subaction *sa;
2168c2ecf20Sopenharmony_ci	int i;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	if (!t->request) {
2198c2ecf20Sopenharmony_ci		printf("BUG in handle_transaction\n");
2208c2ecf20Sopenharmony_ci		return;
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	for (i = 0; i < array_length(protocol_decoders); i++)
2248c2ecf20Sopenharmony_ci		if (protocol_decoders[i].decode(t))
2258c2ecf20Sopenharmony_ci			break;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	/* HACK: decode only fcp right now. */
2288c2ecf20Sopenharmony_ci	return;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	decode_link_packet(&t->request->packet, t->request->length,
2318c2ecf20Sopenharmony_ci			   PACKET_FIELD_TRANSACTION, 0);
2328c2ecf20Sopenharmony_ci	if (t->response)
2338c2ecf20Sopenharmony_ci		decode_link_packet(&t->response->packet, t->request->length,
2348c2ecf20Sopenharmony_ci				   PACKET_FIELD_TRANSACTION, 0);
2358c2ecf20Sopenharmony_ci	else
2368c2ecf20Sopenharmony_ci		printf("[no response]");
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	if (option_verbose) {
2398c2ecf20Sopenharmony_ci		list_for_each_entry(sa, &t->request_list, link)
2408c2ecf20Sopenharmony_ci			print_packet((uint32_t *) &sa->packet, sa->length);
2418c2ecf20Sopenharmony_ci		list_for_each_entry(sa, &t->response_list, link)
2428c2ecf20Sopenharmony_ci			print_packet((uint32_t *) &sa->packet, sa->length);
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci	printf("\r\n");
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	link_transaction_destroy(t);
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic void
2508c2ecf20Sopenharmony_ciclear_pending_transaction_list(void)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	struct link_transaction *t;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	while (!list_empty(&pending_transaction_list)) {
2558c2ecf20Sopenharmony_ci		t = list_head(&pending_transaction_list,
2568c2ecf20Sopenharmony_ci			      struct link_transaction, link);
2578c2ecf20Sopenharmony_ci		list_remove(&t->link);
2588c2ecf20Sopenharmony_ci		link_transaction_destroy(t);
2598c2ecf20Sopenharmony_ci		/* print unfinished transactions */
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic const char * const tcode_names[] = {
2648c2ecf20Sopenharmony_ci	[0x0] = "write_quadlet_request",	[0x6] = "read_quadlet_response",
2658c2ecf20Sopenharmony_ci	[0x1] = "write_block_request",		[0x7] = "read_block_response",
2668c2ecf20Sopenharmony_ci	[0x2] = "write_response",		[0x8] = "cycle_start",
2678c2ecf20Sopenharmony_ci	[0x3] = "reserved",			[0x9] = "lock_request",
2688c2ecf20Sopenharmony_ci	[0x4] = "read_quadlet_request",		[0xa] = "iso_data",
2698c2ecf20Sopenharmony_ci	[0x5] = "read_block_request",		[0xb] = "lock_response",
2708c2ecf20Sopenharmony_ci};
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic const char * const ack_names[] = {
2738c2ecf20Sopenharmony_ci	[0x0] = "no ack",			[0x8] = "reserved (0x08)",
2748c2ecf20Sopenharmony_ci	[0x1] = "ack_complete",			[0x9] = "reserved (0x09)",
2758c2ecf20Sopenharmony_ci	[0x2] = "ack_pending",			[0xa] = "reserved (0x0a)",
2768c2ecf20Sopenharmony_ci	[0x3] = "reserved (0x03)",		[0xb] = "reserved (0x0b)",
2778c2ecf20Sopenharmony_ci	[0x4] = "ack_busy_x",			[0xc] = "reserved (0x0c)",
2788c2ecf20Sopenharmony_ci	[0x5] = "ack_busy_a",			[0xd] = "ack_data_error",
2798c2ecf20Sopenharmony_ci	[0x6] = "ack_busy_b",			[0xe] = "ack_type_error",
2808c2ecf20Sopenharmony_ci	[0x7] = "reserved (0x07)",		[0xf] = "reserved (0x0f)",
2818c2ecf20Sopenharmony_ci};
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic const char * const rcode_names[] = {
2848c2ecf20Sopenharmony_ci	[0x0] = "complete",			[0x4] = "conflict_error",
2858c2ecf20Sopenharmony_ci	[0x1] = "reserved (0x01)",		[0x5] = "data_error",
2868c2ecf20Sopenharmony_ci	[0x2] = "reserved (0x02)",		[0x6] = "type_error",
2878c2ecf20Sopenharmony_ci	[0x3] = "reserved (0x03)",		[0x7] = "address_error",
2888c2ecf20Sopenharmony_ci};
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic const char * const retry_names[] = {
2918c2ecf20Sopenharmony_ci	[0x0] = "retry_1",
2928c2ecf20Sopenharmony_ci	[0x1] = "retry_x",
2938c2ecf20Sopenharmony_ci	[0x2] = "retry_a",
2948c2ecf20Sopenharmony_ci	[0x3] = "retry_b",
2958c2ecf20Sopenharmony_ci};
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cienum {
2988c2ecf20Sopenharmony_ci	PACKET_RESERVED,
2998c2ecf20Sopenharmony_ci	PACKET_REQUEST,
3008c2ecf20Sopenharmony_ci	PACKET_RESPONSE,
3018c2ecf20Sopenharmony_ci	PACKET_OTHER,
3028c2ecf20Sopenharmony_ci};
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_cistruct packet_info {
3058c2ecf20Sopenharmony_ci	const char *name;
3068c2ecf20Sopenharmony_ci	int type;
3078c2ecf20Sopenharmony_ci	int response_tcode;
3088c2ecf20Sopenharmony_ci	const struct packet_field *fields;
3098c2ecf20Sopenharmony_ci	int field_count;
3108c2ecf20Sopenharmony_ci};
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistruct packet_field {
3138c2ecf20Sopenharmony_ci	const char *name; /* Short name for field. */
3148c2ecf20Sopenharmony_ci	int offset;	/* Location of field, specified in bits; */
3158c2ecf20Sopenharmony_ci			/* negative means from end of packet.    */
3168c2ecf20Sopenharmony_ci	int width;	/* Width of field, 0 means use data_length. */
3178c2ecf20Sopenharmony_ci	int flags;	/* Show options. */
3188c2ecf20Sopenharmony_ci	const char * const *value_names;
3198c2ecf20Sopenharmony_ci};
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci#define COMMON_REQUEST_FIELDS						\
3228c2ecf20Sopenharmony_ci	{ "dest", 0, 16, PACKET_FIELD_TRANSACTION },			\
3238c2ecf20Sopenharmony_ci	{ "tl", 16, 6 },						\
3248c2ecf20Sopenharmony_ci	{ "rt", 22, 2, PACKET_FIELD_DETAIL, retry_names },		\
3258c2ecf20Sopenharmony_ci	{ "tcode", 24, 4, PACKET_FIELD_TRANSACTION, tcode_names },	\
3268c2ecf20Sopenharmony_ci	{ "pri", 28, 4, PACKET_FIELD_DETAIL },				\
3278c2ecf20Sopenharmony_ci	{ "src", 32, 16, PACKET_FIELD_TRANSACTION },			\
3288c2ecf20Sopenharmony_ci	{ "offs", 48, 48, PACKET_FIELD_TRANSACTION }
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci#define COMMON_RESPONSE_FIELDS						\
3318c2ecf20Sopenharmony_ci	{ "dest", 0, 16 },						\
3328c2ecf20Sopenharmony_ci	{ "tl", 16, 6 },						\
3338c2ecf20Sopenharmony_ci	{ "rt", 22, 2, PACKET_FIELD_DETAIL, retry_names },		\
3348c2ecf20Sopenharmony_ci	{ "tcode", 24, 4, 0, tcode_names },				\
3358c2ecf20Sopenharmony_ci	{ "pri", 28, 4, PACKET_FIELD_DETAIL },				\
3368c2ecf20Sopenharmony_ci	{ "src", 32, 16 },						\
3378c2ecf20Sopenharmony_ci	{ "rcode", 48, 4, PACKET_FIELD_TRANSACTION, rcode_names }
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cistatic const struct packet_field read_quadlet_request_fields[] = {
3408c2ecf20Sopenharmony_ci	COMMON_REQUEST_FIELDS,
3418c2ecf20Sopenharmony_ci	{ "crc", 96, 32, PACKET_FIELD_DETAIL },
3428c2ecf20Sopenharmony_ci	{ "ack", 156, 4, 0, ack_names },
3438c2ecf20Sopenharmony_ci};
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_cistatic const struct packet_field read_quadlet_response_fields[] = {
3468c2ecf20Sopenharmony_ci	COMMON_RESPONSE_FIELDS,
3478c2ecf20Sopenharmony_ci	{ "data", 96, 32, PACKET_FIELD_TRANSACTION },
3488c2ecf20Sopenharmony_ci	{ "crc", 128, 32, PACKET_FIELD_DETAIL },
3498c2ecf20Sopenharmony_ci	{ "ack", 188, 4, 0, ack_names },
3508c2ecf20Sopenharmony_ci};
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic const struct packet_field read_block_request_fields[] = {
3538c2ecf20Sopenharmony_ci	COMMON_REQUEST_FIELDS,
3548c2ecf20Sopenharmony_ci	{ "data_length", 96, 16, PACKET_FIELD_TRANSACTION },
3558c2ecf20Sopenharmony_ci	{ "extended_tcode", 112, 16 },
3568c2ecf20Sopenharmony_ci	{ "crc", 128, 32, PACKET_FIELD_DETAIL },
3578c2ecf20Sopenharmony_ci	{ "ack", 188, 4, 0, ack_names },
3588c2ecf20Sopenharmony_ci};
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic const struct packet_field block_response_fields[] = {
3618c2ecf20Sopenharmony_ci	COMMON_RESPONSE_FIELDS,
3628c2ecf20Sopenharmony_ci	{ "data_length", 96, 16, PACKET_FIELD_DATA_LENGTH },
3638c2ecf20Sopenharmony_ci	{ "extended_tcode", 112, 16 },
3648c2ecf20Sopenharmony_ci	{ "crc", 128, 32, PACKET_FIELD_DETAIL },
3658c2ecf20Sopenharmony_ci	{ "data", 160, 0, PACKET_FIELD_TRANSACTION },
3668c2ecf20Sopenharmony_ci	{ "crc", -64, 32, PACKET_FIELD_DETAIL },
3678c2ecf20Sopenharmony_ci	{ "ack", -4, 4, 0, ack_names },
3688c2ecf20Sopenharmony_ci};
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_cistatic const struct packet_field write_quadlet_request_fields[] = {
3718c2ecf20Sopenharmony_ci	COMMON_REQUEST_FIELDS,
3728c2ecf20Sopenharmony_ci	{ "data", 96, 32, PACKET_FIELD_TRANSACTION },
3738c2ecf20Sopenharmony_ci	{ "ack", -4, 4, 0, ack_names },
3748c2ecf20Sopenharmony_ci};
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_cistatic const struct packet_field block_request_fields[] = {
3778c2ecf20Sopenharmony_ci	COMMON_REQUEST_FIELDS,
3788c2ecf20Sopenharmony_ci	{ "data_length", 96, 16, PACKET_FIELD_DATA_LENGTH | PACKET_FIELD_TRANSACTION },
3798c2ecf20Sopenharmony_ci	{ "extended_tcode", 112, 16, PACKET_FIELD_TRANSACTION },
3808c2ecf20Sopenharmony_ci	{ "crc", 128, 32, PACKET_FIELD_DETAIL },
3818c2ecf20Sopenharmony_ci	{ "data", 160, 0, PACKET_FIELD_TRANSACTION },
3828c2ecf20Sopenharmony_ci	{ "crc", -64, 32, PACKET_FIELD_DETAIL },
3838c2ecf20Sopenharmony_ci	{ "ack", -4, 4, 0, ack_names },
3848c2ecf20Sopenharmony_ci};
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic const struct packet_field write_response_fields[] = {
3878c2ecf20Sopenharmony_ci	COMMON_RESPONSE_FIELDS,
3888c2ecf20Sopenharmony_ci	{ "reserved", 64, 32, PACKET_FIELD_DETAIL },
3898c2ecf20Sopenharmony_ci	{ "ack", -4, 4, 0, ack_names },
3908c2ecf20Sopenharmony_ci};
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic const struct packet_field iso_data_fields[] = {
3938c2ecf20Sopenharmony_ci	{ "data_length", 0, 16, PACKET_FIELD_DATA_LENGTH },
3948c2ecf20Sopenharmony_ci	{ "tag", 16, 2 },
3958c2ecf20Sopenharmony_ci	{ "channel", 18, 6 },
3968c2ecf20Sopenharmony_ci	{ "tcode", 24, 4, 0, tcode_names },
3978c2ecf20Sopenharmony_ci	{ "sy", 28, 4 },
3988c2ecf20Sopenharmony_ci	{ "crc", 32, 32, PACKET_FIELD_DETAIL },
3998c2ecf20Sopenharmony_ci	{ "data", 64, 0 },
4008c2ecf20Sopenharmony_ci	{ "crc", -64, 32, PACKET_FIELD_DETAIL },
4018c2ecf20Sopenharmony_ci	{ "ack", -4, 4, 0, ack_names },
4028c2ecf20Sopenharmony_ci};
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic const struct packet_info packet_info[] = {
4058c2ecf20Sopenharmony_ci	{
4068c2ecf20Sopenharmony_ci		.name		= "write_quadlet_request",
4078c2ecf20Sopenharmony_ci		.type		= PACKET_REQUEST,
4088c2ecf20Sopenharmony_ci		.response_tcode	= TCODE_WRITE_RESPONSE,
4098c2ecf20Sopenharmony_ci		.fields		= write_quadlet_request_fields,
4108c2ecf20Sopenharmony_ci		.field_count	= array_length(write_quadlet_request_fields)
4118c2ecf20Sopenharmony_ci	},
4128c2ecf20Sopenharmony_ci	{
4138c2ecf20Sopenharmony_ci		.name		= "write_block_request",
4148c2ecf20Sopenharmony_ci		.type		= PACKET_REQUEST,
4158c2ecf20Sopenharmony_ci		.response_tcode	= TCODE_WRITE_RESPONSE,
4168c2ecf20Sopenharmony_ci		.fields		= block_request_fields,
4178c2ecf20Sopenharmony_ci		.field_count	= array_length(block_request_fields)
4188c2ecf20Sopenharmony_ci	},
4198c2ecf20Sopenharmony_ci	{
4208c2ecf20Sopenharmony_ci		.name		= "write_response",
4218c2ecf20Sopenharmony_ci		.type		= PACKET_RESPONSE,
4228c2ecf20Sopenharmony_ci		.fields		= write_response_fields,
4238c2ecf20Sopenharmony_ci		.field_count	= array_length(write_response_fields)
4248c2ecf20Sopenharmony_ci	},
4258c2ecf20Sopenharmony_ci	{
4268c2ecf20Sopenharmony_ci		.name		= "reserved",
4278c2ecf20Sopenharmony_ci		.type		= PACKET_RESERVED,
4288c2ecf20Sopenharmony_ci	},
4298c2ecf20Sopenharmony_ci	{
4308c2ecf20Sopenharmony_ci		.name		= "read_quadlet_request",
4318c2ecf20Sopenharmony_ci		.type		= PACKET_REQUEST,
4328c2ecf20Sopenharmony_ci		.response_tcode	= TCODE_READ_QUADLET_RESPONSE,
4338c2ecf20Sopenharmony_ci		.fields		= read_quadlet_request_fields,
4348c2ecf20Sopenharmony_ci		.field_count	= array_length(read_quadlet_request_fields)
4358c2ecf20Sopenharmony_ci	},
4368c2ecf20Sopenharmony_ci	{
4378c2ecf20Sopenharmony_ci		.name		= "read_block_request",
4388c2ecf20Sopenharmony_ci		.type		= PACKET_REQUEST,
4398c2ecf20Sopenharmony_ci		.response_tcode	= TCODE_READ_BLOCK_RESPONSE,
4408c2ecf20Sopenharmony_ci		.fields		= read_block_request_fields,
4418c2ecf20Sopenharmony_ci		.field_count	= array_length(read_block_request_fields)
4428c2ecf20Sopenharmony_ci	},
4438c2ecf20Sopenharmony_ci	{
4448c2ecf20Sopenharmony_ci		.name		= "read_quadlet_response",
4458c2ecf20Sopenharmony_ci		.type		= PACKET_RESPONSE,
4468c2ecf20Sopenharmony_ci		.fields		= read_quadlet_response_fields,
4478c2ecf20Sopenharmony_ci		.field_count	= array_length(read_quadlet_response_fields)
4488c2ecf20Sopenharmony_ci	},
4498c2ecf20Sopenharmony_ci	{
4508c2ecf20Sopenharmony_ci		.name		= "read_block_response",
4518c2ecf20Sopenharmony_ci		.type		= PACKET_RESPONSE,
4528c2ecf20Sopenharmony_ci		.fields		= block_response_fields,
4538c2ecf20Sopenharmony_ci		.field_count	= array_length(block_response_fields)
4548c2ecf20Sopenharmony_ci	},
4558c2ecf20Sopenharmony_ci	{
4568c2ecf20Sopenharmony_ci		.name		= "cycle_start",
4578c2ecf20Sopenharmony_ci		.type		= PACKET_OTHER,
4588c2ecf20Sopenharmony_ci		.fields		= write_quadlet_request_fields,
4598c2ecf20Sopenharmony_ci		.field_count	= array_length(write_quadlet_request_fields)
4608c2ecf20Sopenharmony_ci	},
4618c2ecf20Sopenharmony_ci	{
4628c2ecf20Sopenharmony_ci		.name		= "lock_request",
4638c2ecf20Sopenharmony_ci		.type		= PACKET_REQUEST,
4648c2ecf20Sopenharmony_ci		.fields		= block_request_fields,
4658c2ecf20Sopenharmony_ci		.field_count	= array_length(block_request_fields)
4668c2ecf20Sopenharmony_ci	},
4678c2ecf20Sopenharmony_ci	{
4688c2ecf20Sopenharmony_ci		.name		= "iso_data",
4698c2ecf20Sopenharmony_ci		.type		= PACKET_OTHER,
4708c2ecf20Sopenharmony_ci		.fields		= iso_data_fields,
4718c2ecf20Sopenharmony_ci		.field_count	= array_length(iso_data_fields)
4728c2ecf20Sopenharmony_ci	},
4738c2ecf20Sopenharmony_ci	{
4748c2ecf20Sopenharmony_ci		.name		= "lock_response",
4758c2ecf20Sopenharmony_ci		.type		= PACKET_RESPONSE,
4768c2ecf20Sopenharmony_ci		.fields		= block_response_fields,
4778c2ecf20Sopenharmony_ci		.field_count	= array_length(block_response_fields)
4788c2ecf20Sopenharmony_ci	},
4798c2ecf20Sopenharmony_ci};
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_cistatic int
4828c2ecf20Sopenharmony_cihandle_request_packet(uint32_t *data, size_t length)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	struct link_packet *p = (struct link_packet *) data;
4858c2ecf20Sopenharmony_ci	struct subaction *sa, *prev;
4868c2ecf20Sopenharmony_ci	struct link_transaction *t;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	t = link_transaction_lookup(p->common.source, p->common.destination,
4898c2ecf20Sopenharmony_ci			p->common.tlabel);
4908c2ecf20Sopenharmony_ci	sa = subaction_create(data, length);
4918c2ecf20Sopenharmony_ci	t->request = sa;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	if (!list_empty(&t->request_list)) {
4948c2ecf20Sopenharmony_ci		prev = list_tail(&t->request_list,
4958c2ecf20Sopenharmony_ci				 struct subaction, link);
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci		if (!ACK_BUSY(prev->ack)) {
4988c2ecf20Sopenharmony_ci			/*
4998c2ecf20Sopenharmony_ci			 * error, we should only see ack_busy_* before the
5008c2ecf20Sopenharmony_ci			 * ack_pending/ack_complete -- this is an ack_pending
5018c2ecf20Sopenharmony_ci			 * instead (ack_complete would have finished the
5028c2ecf20Sopenharmony_ci			 * transaction).
5038c2ecf20Sopenharmony_ci			 */
5048c2ecf20Sopenharmony_ci		}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci		if (prev->packet.common.tcode != sa->packet.common.tcode ||
5078c2ecf20Sopenharmony_ci		    prev->packet.common.tlabel != sa->packet.common.tlabel) {
5088c2ecf20Sopenharmony_ci			/* memcmp() ? */
5098c2ecf20Sopenharmony_ci			/* error, these should match for retries. */
5108c2ecf20Sopenharmony_ci		}
5118c2ecf20Sopenharmony_ci	}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	list_append(&t->request_list, &sa->link);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	switch (sa->ack) {
5168c2ecf20Sopenharmony_ci	case ACK_COMPLETE:
5178c2ecf20Sopenharmony_ci		if (p->common.tcode != TCODE_WRITE_QUADLET_REQUEST &&
5188c2ecf20Sopenharmony_ci		    p->common.tcode != TCODE_WRITE_BLOCK_REQUEST)
5198c2ecf20Sopenharmony_ci			/* error, unified transactions only allowed for write */;
5208c2ecf20Sopenharmony_ci		list_remove(&t->link);
5218c2ecf20Sopenharmony_ci		handle_transaction(t);
5228c2ecf20Sopenharmony_ci		break;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	case ACK_NO_ACK:
5258c2ecf20Sopenharmony_ci	case ACK_DATA_ERROR:
5268c2ecf20Sopenharmony_ci	case ACK_TYPE_ERROR:
5278c2ecf20Sopenharmony_ci		list_remove(&t->link);
5288c2ecf20Sopenharmony_ci		handle_transaction(t);
5298c2ecf20Sopenharmony_ci		break;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	case ACK_PENDING:
5328c2ecf20Sopenharmony_ci		/* request subaction phase over, wait for response. */
5338c2ecf20Sopenharmony_ci		break;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	case ACK_BUSY_X:
5368c2ecf20Sopenharmony_ci	case ACK_BUSY_A:
5378c2ecf20Sopenharmony_ci	case ACK_BUSY_B:
5388c2ecf20Sopenharmony_ci		/* ok, wait for retry. */
5398c2ecf20Sopenharmony_ci		/* check that retry protocol is respected. */
5408c2ecf20Sopenharmony_ci		break;
5418c2ecf20Sopenharmony_ci	}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	return 1;
5448c2ecf20Sopenharmony_ci}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cistatic int
5478c2ecf20Sopenharmony_cihandle_response_packet(uint32_t *data, size_t length)
5488c2ecf20Sopenharmony_ci{
5498c2ecf20Sopenharmony_ci	struct link_packet *p = (struct link_packet *) data;
5508c2ecf20Sopenharmony_ci	struct subaction *sa, *prev;
5518c2ecf20Sopenharmony_ci	struct link_transaction *t;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	t = link_transaction_lookup(p->common.destination, p->common.source,
5548c2ecf20Sopenharmony_ci			p->common.tlabel);
5558c2ecf20Sopenharmony_ci	if (list_empty(&t->request_list)) {
5568c2ecf20Sopenharmony_ci		/* unsolicited response */
5578c2ecf20Sopenharmony_ci	}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	sa = subaction_create(data, length);
5608c2ecf20Sopenharmony_ci	t->response = sa;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	if (!list_empty(&t->response_list)) {
5638c2ecf20Sopenharmony_ci		prev = list_tail(&t->response_list, struct subaction, link);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci		if (!ACK_BUSY(prev->ack)) {
5668c2ecf20Sopenharmony_ci			/*
5678c2ecf20Sopenharmony_ci			 * error, we should only see ack_busy_* before the
5688c2ecf20Sopenharmony_ci			 * ack_pending/ack_complete
5698c2ecf20Sopenharmony_ci			 */
5708c2ecf20Sopenharmony_ci		}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci		if (prev->packet.common.tcode != sa->packet.common.tcode ||
5738c2ecf20Sopenharmony_ci		    prev->packet.common.tlabel != sa->packet.common.tlabel) {
5748c2ecf20Sopenharmony_ci			/* use memcmp() instead? */
5758c2ecf20Sopenharmony_ci			/* error, these should match for retries. */
5768c2ecf20Sopenharmony_ci		}
5778c2ecf20Sopenharmony_ci	} else {
5788c2ecf20Sopenharmony_ci		prev = list_tail(&t->request_list, struct subaction, link);
5798c2ecf20Sopenharmony_ci		if (prev->ack != ACK_PENDING) {
5808c2ecf20Sopenharmony_ci			/*
5818c2ecf20Sopenharmony_ci			 * error, should not get response unless last request got
5828c2ecf20Sopenharmony_ci			 * ack_pending.
5838c2ecf20Sopenharmony_ci			 */
5848c2ecf20Sopenharmony_ci		}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci		if (packet_info[prev->packet.common.tcode].response_tcode !=
5878c2ecf20Sopenharmony_ci		    sa->packet.common.tcode) {
5888c2ecf20Sopenharmony_ci			/* error, tcode mismatch */
5898c2ecf20Sopenharmony_ci		}
5908c2ecf20Sopenharmony_ci	}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	list_append(&t->response_list, &sa->link);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	switch (sa->ack) {
5958c2ecf20Sopenharmony_ci	case ACK_COMPLETE:
5968c2ecf20Sopenharmony_ci	case ACK_NO_ACK:
5978c2ecf20Sopenharmony_ci	case ACK_DATA_ERROR:
5988c2ecf20Sopenharmony_ci	case ACK_TYPE_ERROR:
5998c2ecf20Sopenharmony_ci		list_remove(&t->link);
6008c2ecf20Sopenharmony_ci		handle_transaction(t);
6018c2ecf20Sopenharmony_ci		/* transaction complete, remove t from pending list. */
6028c2ecf20Sopenharmony_ci		break;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	case ACK_PENDING:
6058c2ecf20Sopenharmony_ci		/* error for responses. */
6068c2ecf20Sopenharmony_ci		break;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	case ACK_BUSY_X:
6098c2ecf20Sopenharmony_ci	case ACK_BUSY_A:
6108c2ecf20Sopenharmony_ci	case ACK_BUSY_B:
6118c2ecf20Sopenharmony_ci		/* no problem, wait for next retry */
6128c2ecf20Sopenharmony_ci		break;
6138c2ecf20Sopenharmony_ci	}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	return 1;
6168c2ecf20Sopenharmony_ci}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_cistatic int
6198c2ecf20Sopenharmony_cihandle_packet(uint32_t *data, size_t length)
6208c2ecf20Sopenharmony_ci{
6218c2ecf20Sopenharmony_ci	if (length == 0) {
6228c2ecf20Sopenharmony_ci		printf("bus reset\r\n");
6238c2ecf20Sopenharmony_ci		clear_pending_transaction_list();
6248c2ecf20Sopenharmony_ci	} else if (length > sizeof(struct phy_packet)) {
6258c2ecf20Sopenharmony_ci		struct link_packet *p = (struct link_packet *) data;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci		switch (packet_info[p->common.tcode].type) {
6288c2ecf20Sopenharmony_ci		case PACKET_REQUEST:
6298c2ecf20Sopenharmony_ci			return handle_request_packet(data, length);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci		case PACKET_RESPONSE:
6328c2ecf20Sopenharmony_ci			return handle_response_packet(data, length);
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci		case PACKET_OTHER:
6358c2ecf20Sopenharmony_ci		case PACKET_RESERVED:
6368c2ecf20Sopenharmony_ci			return 0;
6378c2ecf20Sopenharmony_ci		}
6388c2ecf20Sopenharmony_ci	}
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	return 1;
6418c2ecf20Sopenharmony_ci}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_cistatic unsigned int
6448c2ecf20Sopenharmony_ciget_bits(struct link_packet *packet, int offset, int width)
6458c2ecf20Sopenharmony_ci{
6468c2ecf20Sopenharmony_ci	uint32_t *data = (uint32_t *) packet;
6478c2ecf20Sopenharmony_ci	uint32_t index, shift, mask;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	index = offset / 32 + 1;
6508c2ecf20Sopenharmony_ci	shift = 32 - (offset & 31) - width;
6518c2ecf20Sopenharmony_ci	mask = width == 32 ? ~0 : (1 << width) - 1;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	return (data[index] >> shift) & mask;
6548c2ecf20Sopenharmony_ci}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci#if __BYTE_ORDER == __LITTLE_ENDIAN
6578c2ecf20Sopenharmony_ci#define byte_index(i) ((i) ^ 3)
6588c2ecf20Sopenharmony_ci#elif __BYTE_ORDER == __BIG_ENDIAN
6598c2ecf20Sopenharmony_ci#define byte_index(i) (i)
6608c2ecf20Sopenharmony_ci#else
6618c2ecf20Sopenharmony_ci#error unsupported byte order.
6628c2ecf20Sopenharmony_ci#endif
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_cistatic void
6658c2ecf20Sopenharmony_cidump_data(unsigned char *data, int length)
6668c2ecf20Sopenharmony_ci{
6678c2ecf20Sopenharmony_ci	int i, print_length;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	if (length > 128)
6708c2ecf20Sopenharmony_ci		print_length = 128;
6718c2ecf20Sopenharmony_ci	else
6728c2ecf20Sopenharmony_ci		print_length = length;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	for (i = 0; i < print_length; i++)
6758c2ecf20Sopenharmony_ci		printf("%s%02hhx",
6768c2ecf20Sopenharmony_ci		       (i % 4 == 0 && i != 0) ? " " : "",
6778c2ecf20Sopenharmony_ci		       data[byte_index(i)]);
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	if (print_length < length)
6808c2ecf20Sopenharmony_ci		printf(" (%d more bytes)", length - print_length);
6818c2ecf20Sopenharmony_ci}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_cistatic void
6848c2ecf20Sopenharmony_cidecode_link_packet(struct link_packet *packet, size_t length,
6858c2ecf20Sopenharmony_ci		   int include_flags, int exclude_flags)
6868c2ecf20Sopenharmony_ci{
6878c2ecf20Sopenharmony_ci	const struct packet_info *pi;
6888c2ecf20Sopenharmony_ci	int data_length = 0;
6898c2ecf20Sopenharmony_ci	int i;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	pi = &packet_info[packet->common.tcode];
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	for (i = 0; i < pi->field_count; i++) {
6948c2ecf20Sopenharmony_ci		const struct packet_field *f = &pi->fields[i];
6958c2ecf20Sopenharmony_ci		int offset;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci		if (f->flags & exclude_flags)
6988c2ecf20Sopenharmony_ci			continue;
6998c2ecf20Sopenharmony_ci		if (include_flags && !(f->flags & include_flags))
7008c2ecf20Sopenharmony_ci			continue;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci		if (f->offset < 0)
7038c2ecf20Sopenharmony_ci			offset = length * 8 + f->offset - 32;
7048c2ecf20Sopenharmony_ci		else
7058c2ecf20Sopenharmony_ci			offset = f->offset;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci		if (f->value_names != NULL) {
7088c2ecf20Sopenharmony_ci			uint32_t bits;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci			bits = get_bits(packet, offset, f->width);
7118c2ecf20Sopenharmony_ci			printf("%s", f->value_names[bits]);
7128c2ecf20Sopenharmony_ci		} else if (f->width == 0) {
7138c2ecf20Sopenharmony_ci			printf("%s=[", f->name);
7148c2ecf20Sopenharmony_ci			dump_data((unsigned char *) packet + (offset / 8 + 4), data_length);
7158c2ecf20Sopenharmony_ci			printf("]");
7168c2ecf20Sopenharmony_ci		} else {
7178c2ecf20Sopenharmony_ci			unsigned long long bits;
7188c2ecf20Sopenharmony_ci			int high_width, low_width;
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci			if ((offset & ~31) != ((offset + f->width - 1) & ~31)) {
7218c2ecf20Sopenharmony_ci				/* Bit field spans quadlet boundary. */
7228c2ecf20Sopenharmony_ci				high_width = ((offset + 31) & ~31) - offset;
7238c2ecf20Sopenharmony_ci				low_width = f->width - high_width;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci				bits = get_bits(packet, offset, high_width);
7268c2ecf20Sopenharmony_ci				bits = (bits << low_width) |
7278c2ecf20Sopenharmony_ci					get_bits(packet, offset + high_width, low_width);
7288c2ecf20Sopenharmony_ci			} else {
7298c2ecf20Sopenharmony_ci				bits = get_bits(packet, offset, f->width);
7308c2ecf20Sopenharmony_ci			}
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci			printf("%s=0x%0*llx", f->name, (f->width + 3) / 4, bits);
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci			if (f->flags & PACKET_FIELD_DATA_LENGTH)
7358c2ecf20Sopenharmony_ci				data_length = bits;
7368c2ecf20Sopenharmony_ci		}
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci		if (i < pi->field_count - 1)
7398c2ecf20Sopenharmony_ci			printf(", ");
7408c2ecf20Sopenharmony_ci	}
7418c2ecf20Sopenharmony_ci}
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_cistatic void
7448c2ecf20Sopenharmony_ciprint_packet(uint32_t *data, size_t length)
7458c2ecf20Sopenharmony_ci{
7468c2ecf20Sopenharmony_ci	int i;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	printf("%6u  ", data[0]);
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	if (length == 4) {
7518c2ecf20Sopenharmony_ci		printf("bus reset");
7528c2ecf20Sopenharmony_ci	} else if (length < sizeof(struct phy_packet)) {
7538c2ecf20Sopenharmony_ci		printf("short packet: ");
7548c2ecf20Sopenharmony_ci		for (i = 1; i < length / 4; i++)
7558c2ecf20Sopenharmony_ci			printf("%s%08x", i == 0 ? "[" : " ", data[i]);
7568c2ecf20Sopenharmony_ci		printf("]");
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	} else if (length == sizeof(struct phy_packet) && data[1] == ~data[2]) {
7598c2ecf20Sopenharmony_ci		struct phy_packet *pp = (struct phy_packet *) data;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci		/* phy packet are 3 quadlets: the 1 quadlet payload,
7628c2ecf20Sopenharmony_ci		 * the bitwise inverse of the payload and the snoop
7638c2ecf20Sopenharmony_ci		 * mode ack */
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci		switch (pp->common.identifier) {
7668c2ecf20Sopenharmony_ci		case PHY_PACKET_CONFIGURATION:
7678c2ecf20Sopenharmony_ci			if (!pp->phy_config.set_root && !pp->phy_config.set_gap_count) {
7688c2ecf20Sopenharmony_ci				printf("ext phy config: phy_id=%02x", pp->phy_config.root_id);
7698c2ecf20Sopenharmony_ci			} else {
7708c2ecf20Sopenharmony_ci				printf("phy config:");
7718c2ecf20Sopenharmony_ci				if (pp->phy_config.set_root)
7728c2ecf20Sopenharmony_ci					printf(" set_root_id=%02x", pp->phy_config.root_id);
7738c2ecf20Sopenharmony_ci				if (pp->phy_config.set_gap_count)
7748c2ecf20Sopenharmony_ci					printf(" set_gap_count=%d", pp->phy_config.gap_count);
7758c2ecf20Sopenharmony_ci			}
7768c2ecf20Sopenharmony_ci			break;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci		case PHY_PACKET_LINK_ON:
7798c2ecf20Sopenharmony_ci			printf("link-on packet, phy_id=%02x", pp->link_on.phy_id);
7808c2ecf20Sopenharmony_ci			break;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci		case PHY_PACKET_SELF_ID:
7838c2ecf20Sopenharmony_ci			if (pp->self_id.extended) {
7848c2ecf20Sopenharmony_ci				printf("extended self id: phy_id=%02x, seq=%d",
7858c2ecf20Sopenharmony_ci				       pp->ext_self_id.phy_id, pp->ext_self_id.sequence);
7868c2ecf20Sopenharmony_ci			} else {
7878c2ecf20Sopenharmony_ci				static const char * const speed_names[] = {
7888c2ecf20Sopenharmony_ci					"S100", "S200", "S400", "BETA"
7898c2ecf20Sopenharmony_ci				};
7908c2ecf20Sopenharmony_ci				printf("self id: phy_id=%02x, link %s, gap_count=%d, speed=%s%s%s",
7918c2ecf20Sopenharmony_ci				       pp->self_id.phy_id,
7928c2ecf20Sopenharmony_ci				       (pp->self_id.link_active ? "active" : "not active"),
7938c2ecf20Sopenharmony_ci				       pp->self_id.gap_count,
7948c2ecf20Sopenharmony_ci				       speed_names[pp->self_id.phy_speed],
7958c2ecf20Sopenharmony_ci				       (pp->self_id.contender ? ", irm contender" : ""),
7968c2ecf20Sopenharmony_ci				       (pp->self_id.initiated_reset ? ", initiator" : ""));
7978c2ecf20Sopenharmony_ci			}
7988c2ecf20Sopenharmony_ci			break;
7998c2ecf20Sopenharmony_ci		default:
8008c2ecf20Sopenharmony_ci			printf("unknown phy packet: ");
8018c2ecf20Sopenharmony_ci			for (i = 1; i < length / 4; i++)
8028c2ecf20Sopenharmony_ci				printf("%s%08x", i == 0 ? "[" : " ", data[i]);
8038c2ecf20Sopenharmony_ci			printf("]");
8048c2ecf20Sopenharmony_ci			break;
8058c2ecf20Sopenharmony_ci		}
8068c2ecf20Sopenharmony_ci	} else {
8078c2ecf20Sopenharmony_ci		struct link_packet *packet = (struct link_packet *) data;
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci		decode_link_packet(packet, length, 0,
8108c2ecf20Sopenharmony_ci				   option_verbose ? 0 : PACKET_FIELD_DETAIL);
8118c2ecf20Sopenharmony_ci	}
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	if (option_hex) {
8148c2ecf20Sopenharmony_ci		printf("  [");
8158c2ecf20Sopenharmony_ci		dump_data((unsigned char *) data + 4, length - 4);
8168c2ecf20Sopenharmony_ci		printf("]");
8178c2ecf20Sopenharmony_ci	}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	printf("\r\n");
8208c2ecf20Sopenharmony_ci}
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci#define HIDE_CURSOR	"\033[?25l"
8238c2ecf20Sopenharmony_ci#define SHOW_CURSOR	"\033[?25h"
8248c2ecf20Sopenharmony_ci#define CLEAR		"\033[H\033[2J"
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_cistatic void
8278c2ecf20Sopenharmony_ciprint_stats(uint32_t *data, size_t length)
8288c2ecf20Sopenharmony_ci{
8298c2ecf20Sopenharmony_ci	static int bus_reset_count, short_packet_count, phy_packet_count;
8308c2ecf20Sopenharmony_ci	static int tcode_count[16];
8318c2ecf20Sopenharmony_ci	static struct timeval last_update;
8328c2ecf20Sopenharmony_ci	struct timeval now;
8338c2ecf20Sopenharmony_ci	int i;
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	if (length == 0)
8368c2ecf20Sopenharmony_ci		bus_reset_count++;
8378c2ecf20Sopenharmony_ci	else if (length < sizeof(struct phy_packet))
8388c2ecf20Sopenharmony_ci		short_packet_count++;
8398c2ecf20Sopenharmony_ci	else if (length == sizeof(struct phy_packet) && data[1] == ~data[2])
8408c2ecf20Sopenharmony_ci		phy_packet_count++;
8418c2ecf20Sopenharmony_ci	else {
8428c2ecf20Sopenharmony_ci		struct link_packet *packet = (struct link_packet *) data;
8438c2ecf20Sopenharmony_ci		tcode_count[packet->common.tcode]++;
8448c2ecf20Sopenharmony_ci	}
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	gettimeofday(&now, NULL);
8478c2ecf20Sopenharmony_ci	if (now.tv_sec <= last_update.tv_sec &&
8488c2ecf20Sopenharmony_ci	    now.tv_usec < last_update.tv_usec + 500000)
8498c2ecf20Sopenharmony_ci		return;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	last_update = now;
8528c2ecf20Sopenharmony_ci	printf(CLEAR HIDE_CURSOR
8538c2ecf20Sopenharmony_ci	       "  bus resets              : %8d\n"
8548c2ecf20Sopenharmony_ci	       "  short packets           : %8d\n"
8558c2ecf20Sopenharmony_ci	       "  phy packets             : %8d\n",
8568c2ecf20Sopenharmony_ci	       bus_reset_count, short_packet_count, phy_packet_count);
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	for (i = 0; i < array_length(packet_info); i++)
8598c2ecf20Sopenharmony_ci		if (packet_info[i].type != PACKET_RESERVED)
8608c2ecf20Sopenharmony_ci			printf("  %-24s: %8d\n", packet_info[i].name, tcode_count[i]);
8618c2ecf20Sopenharmony_ci	printf(SHOW_CURSOR "\n");
8628c2ecf20Sopenharmony_ci}
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_cistatic struct termios saved_attributes;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_cistatic void
8678c2ecf20Sopenharmony_cireset_input_mode(void)
8688c2ecf20Sopenharmony_ci{
8698c2ecf20Sopenharmony_ci	tcsetattr(STDIN_FILENO, TCSANOW, &saved_attributes);
8708c2ecf20Sopenharmony_ci}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_cistatic void
8738c2ecf20Sopenharmony_ciset_input_mode(void)
8748c2ecf20Sopenharmony_ci{
8758c2ecf20Sopenharmony_ci	struct termios tattr;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	/* Make sure stdin is a terminal. */
8788c2ecf20Sopenharmony_ci	if (!isatty(STDIN_FILENO)) {
8798c2ecf20Sopenharmony_ci		fprintf(stderr, "Not a terminal.\n");
8808c2ecf20Sopenharmony_ci		exit(EXIT_FAILURE);
8818c2ecf20Sopenharmony_ci	}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	/* Save the terminal attributes so we can restore them later. */
8848c2ecf20Sopenharmony_ci	tcgetattr(STDIN_FILENO, &saved_attributes);
8858c2ecf20Sopenharmony_ci	atexit(reset_input_mode);
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	/* Set the funny terminal modes. */
8888c2ecf20Sopenharmony_ci	tcgetattr(STDIN_FILENO, &tattr);
8898c2ecf20Sopenharmony_ci	tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
8908c2ecf20Sopenharmony_ci	tattr.c_cc[VMIN] = 1;
8918c2ecf20Sopenharmony_ci	tattr.c_cc[VTIME] = 0;
8928c2ecf20Sopenharmony_ci	tcsetattr(STDIN_FILENO, TCSAFLUSH, &tattr);
8938c2ecf20Sopenharmony_ci}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ciint main(int argc, const char *argv[])
8968c2ecf20Sopenharmony_ci{
8978c2ecf20Sopenharmony_ci	uint32_t buf[128 * 1024];
8988c2ecf20Sopenharmony_ci	uint32_t filter;
8998c2ecf20Sopenharmony_ci	int length, retval, view;
9008c2ecf20Sopenharmony_ci	int fd = -1;
9018c2ecf20Sopenharmony_ci	FILE *output = NULL, *input = NULL;
9028c2ecf20Sopenharmony_ci	poptContext con;
9038c2ecf20Sopenharmony_ci	char c;
9048c2ecf20Sopenharmony_ci	struct pollfd pollfds[2];
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	sys_sigint_handler = signal(SIGINT, sigint_handler);
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	con = poptGetContext(NULL, argc, argv, options, 0);
9098c2ecf20Sopenharmony_ci	retval = poptGetNextOpt(con);
9108c2ecf20Sopenharmony_ci	if (retval < -1) {
9118c2ecf20Sopenharmony_ci		poptPrintUsage(con, stdout, 0);
9128c2ecf20Sopenharmony_ci		return -1;
9138c2ecf20Sopenharmony_ci	}
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	if (option_version) {
9168c2ecf20Sopenharmony_ci		printf("dump tool for nosy sniffer, version %s\n", VERSION);
9178c2ecf20Sopenharmony_ci		return 0;
9188c2ecf20Sopenharmony_ci	}
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	if (__BYTE_ORDER != __LITTLE_ENDIAN)
9218c2ecf20Sopenharmony_ci		fprintf(stderr, "warning: nosy has only been tested on little "
9228c2ecf20Sopenharmony_ci			"endian machines\n");
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	if (option_input != NULL) {
9258c2ecf20Sopenharmony_ci		input = fopen(option_input, "r");
9268c2ecf20Sopenharmony_ci		if (input == NULL) {
9278c2ecf20Sopenharmony_ci			fprintf(stderr, "Could not open %s, %m\n", option_input);
9288c2ecf20Sopenharmony_ci			return -1;
9298c2ecf20Sopenharmony_ci		}
9308c2ecf20Sopenharmony_ci	} else {
9318c2ecf20Sopenharmony_ci		fd = open(option_nosy_device, O_RDWR);
9328c2ecf20Sopenharmony_ci		if (fd < 0) {
9338c2ecf20Sopenharmony_ci			fprintf(stderr, "Could not open %s, %m\n", option_nosy_device);
9348c2ecf20Sopenharmony_ci			return -1;
9358c2ecf20Sopenharmony_ci		}
9368c2ecf20Sopenharmony_ci		set_input_mode();
9378c2ecf20Sopenharmony_ci	}
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	if (strcmp(option_view, "transaction") == 0)
9408c2ecf20Sopenharmony_ci		view = VIEW_TRANSACTION;
9418c2ecf20Sopenharmony_ci	else if (strcmp(option_view, "stats") == 0)
9428c2ecf20Sopenharmony_ci		view = VIEW_STATS;
9438c2ecf20Sopenharmony_ci	else
9448c2ecf20Sopenharmony_ci		view = VIEW_PACKET;
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	if (option_output) {
9478c2ecf20Sopenharmony_ci		output = fopen(option_output, "w");
9488c2ecf20Sopenharmony_ci		if (output == NULL) {
9498c2ecf20Sopenharmony_ci			fprintf(stderr, "Could not open %s, %m\n", option_output);
9508c2ecf20Sopenharmony_ci			return -1;
9518c2ecf20Sopenharmony_ci		}
9528c2ecf20Sopenharmony_ci	}
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	filter = ~0;
9578c2ecf20Sopenharmony_ci	if (!option_iso)
9588c2ecf20Sopenharmony_ci		filter &= ~(1 << TCODE_STREAM_DATA);
9598c2ecf20Sopenharmony_ci	if (!option_cycle_start)
9608c2ecf20Sopenharmony_ci		filter &= ~(1 << TCODE_CYCLE_START);
9618c2ecf20Sopenharmony_ci	if (view == VIEW_STATS)
9628c2ecf20Sopenharmony_ci		filter = ~(1 << TCODE_CYCLE_START);
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	ioctl(fd, NOSY_IOC_FILTER, filter);
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	ioctl(fd, NOSY_IOC_START);
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	pollfds[0].fd = fd;
9698c2ecf20Sopenharmony_ci	pollfds[0].events = POLLIN;
9708c2ecf20Sopenharmony_ci	pollfds[1].fd = STDIN_FILENO;
9718c2ecf20Sopenharmony_ci	pollfds[1].events = POLLIN;
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	while (run) {
9748c2ecf20Sopenharmony_ci		if (input != NULL) {
9758c2ecf20Sopenharmony_ci			if (fread(&length, sizeof length, 1, input) != 1)
9768c2ecf20Sopenharmony_ci				return 0;
9778c2ecf20Sopenharmony_ci			fread(buf, 1, length, input);
9788c2ecf20Sopenharmony_ci		} else {
9798c2ecf20Sopenharmony_ci			poll(pollfds, 2, -1);
9808c2ecf20Sopenharmony_ci			if (pollfds[1].revents) {
9818c2ecf20Sopenharmony_ci				read(STDIN_FILENO, &c, sizeof c);
9828c2ecf20Sopenharmony_ci				switch (c) {
9838c2ecf20Sopenharmony_ci				case 'q':
9848c2ecf20Sopenharmony_ci					if (output != NULL)
9858c2ecf20Sopenharmony_ci						fclose(output);
9868c2ecf20Sopenharmony_ci					return 0;
9878c2ecf20Sopenharmony_ci				}
9888c2ecf20Sopenharmony_ci			}
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci			if (pollfds[0].revents)
9918c2ecf20Sopenharmony_ci				length = read(fd, buf, sizeof buf);
9928c2ecf20Sopenharmony_ci			else
9938c2ecf20Sopenharmony_ci				continue;
9948c2ecf20Sopenharmony_ci		}
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci		if (output != NULL) {
9978c2ecf20Sopenharmony_ci			fwrite(&length, sizeof length, 1, output);
9988c2ecf20Sopenharmony_ci			fwrite(buf, 1, length, output);
9998c2ecf20Sopenharmony_ci		}
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci		switch (view) {
10028c2ecf20Sopenharmony_ci		case VIEW_TRANSACTION:
10038c2ecf20Sopenharmony_ci			handle_packet(buf, length);
10048c2ecf20Sopenharmony_ci			break;
10058c2ecf20Sopenharmony_ci		case VIEW_PACKET:
10068c2ecf20Sopenharmony_ci			print_packet(buf, length);
10078c2ecf20Sopenharmony_ci			break;
10088c2ecf20Sopenharmony_ci		case VIEW_STATS:
10098c2ecf20Sopenharmony_ci			print_stats(buf, length);
10108c2ecf20Sopenharmony_ci			break;
10118c2ecf20Sopenharmony_ci		}
10128c2ecf20Sopenharmony_ci	}
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	if (output != NULL)
10158c2ecf20Sopenharmony_ci		fclose(output);
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	close(fd);
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	poptFreeContext(con);
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	return 0;
10228c2ecf20Sopenharmony_ci}
1023