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