diff --git a/cli/diag.c b/cli/diag.c index 9644da7d..f02ec339 100644 --- a/cli/diag.c +++ b/cli/diag.c @@ -2026,6 +2026,109 @@ static int refclk(int argc, char **argv) return 0; } +static int convert_str_to_dwords(char *str, uint32_t **dwords, int *num_dwords) +{ + *num_dwords = 0; + const char *ptr = str; + int dword_len = 0; + while (*ptr != '\0') { + if (*ptr == '0' && *(ptr + 1) == 'x') { + (*num_dwords)++; + ptr += 2; + dword_len = 0; + } + while (*ptr != ' ' && *ptr != '\0') { + ptr++; + dword_len++; + } + if (dword_len > 8) { + printf("Entered dword longer than allowed\n"); + return -1; + } + if (*ptr == ' ') + ptr++; + } + + *dwords = (uint32_t *)malloc(*num_dwords * sizeof(uint32_t)); + if (*dwords == NULL) + return -1; + + ptr = str; + for (int i = 0; i < *num_dwords; i++) { + char *endptr; + (*dwords)[i] = (uint32_t)strtoul(ptr, &endptr, 0); + if (endptr == ptr || (*endptr != ' ' && *endptr != '\0')) { + free(*dwords); + return -1; + } + ptr = endptr; + while (*ptr == ' ') + ptr++; + } + + return 0; +} + +#define CMD_TLP_INJECT "Inject a raw TLP" + +static int tlp_inject (int argc, char **argv) +{ + int ret = 0; + uint32_t * raw_tlp_dwords = NULL; + int num_dwords = 0; + static struct { + struct switchtec_dev *dev; + int port_id; + int tlp_type; + int ecrc; + char * raw_tlp_data; + } cfg = { + .tlp_type = 0 + }; + + const struct argconfig_options opts[] = { + DEVICE_OPTION, + {"port", 'p', "PORT_ID", CFG_NONNEGATIVE, &cfg.port_id, + required_argument, "destination port ID"}, + {"tlp_type", 't', "TYPE", CFG_NONNEGATIVE, &cfg.tlp_type, + required_argument, "tlp type:\n0: P - Posted\n1: NP - Non-posted\n2: CP - Completion\n(default 0)"}, + {"enable_ecrc", 'e', "", CFG_NONE, &cfg.ecrc, no_argument, + "Enable the ecrc to be included at the end of the input data (Default: disabled)"}, + {"tlp_data", 'd', "\"DW0 DW1 ... DW131\"", CFG_STRING, + &cfg.raw_tlp_data, required_argument, + "DWs to be sent as part of the raw TLP (Maximum 132 DWs). Every DW must start with \'0x\'"}, + {NULL} + }; + + argconfig_parse(argc, argv, CMD_TLP_INJECT, opts, &cfg, sizeof(cfg)); + + if (cfg.raw_tlp_data == NULL) { + fprintf(stderr, "Must set tlp data --tlp_data -d \n"); + return -1; + } + ret = convert_str_to_dwords(cfg.raw_tlp_data, &raw_tlp_dwords, + &num_dwords); + if (ret) { + fprintf(stderr, "Error with tlp data provided \n"); + return -1; + } + if (num_dwords > SWITCHTEC_DIAG_MAX_TLP_DWORDS) { + fprintf(stderr, "TLP data cannot exceed %d dwords \n", + SWITCHTEC_DIAG_MAX_TLP_DWORDS); + free(raw_tlp_dwords); + return -1; + } + + ret = switchtec_tlp_inject(cfg.dev, cfg.port_id, cfg.tlp_type, + num_dwords, cfg.ecrc, raw_tlp_dwords); + if (ret != 0) { + switchtec_perror("tlp_inject"); + return -1; + } + + return 0; +} + static const struct cmd commands[] = { CMD(crosshair, CMD_DESC_CROSS_HAIR), CMD(eye, CMD_DESC_EYE), @@ -2039,6 +2142,7 @@ static const struct cmd commands[] = { CMD(rcvr_obj, CMD_DESC_RCVR_OBJ), CMD(refclk, CMD_DESC_REF_CLK), CMD(ltssm_log, CMD_DESC_LTSSM_LOG), + CMD(tlp_inject, CMD_TLP_INJECT), {} }; diff --git a/inc/switchtec/diag.h b/inc/switchtec/diag.h index 5e137d5d..e2f25b8c 100644 --- a/inc/switchtec/diag.h +++ b/inc/switchtec/diag.h @@ -25,6 +25,8 @@ #ifndef LIBSWITCHTEC_DIAG_H #define LIBSWITCHTEC_DIAG_H +#include "switchtec.h" + #include /** @@ -279,5 +281,13 @@ struct switchtec_diag_ltssm_log_dmp_out { uint32_t arc; }; +struct switchtec_tlp_inject_in { + uint32_t dest_port; + uint32_t tlp_type; + uint32_t tlp_length; + uint32_t ecrc; + uint32_t raw_tlp_data[SWITCHTEC_DIAG_MAX_TLP_DWORDS]; +}; + #endif /**@}*/ diff --git a/inc/switchtec/switchtec.h b/inc/switchtec/switchtec.h index a41f6ba0..b8454221 100644 --- a/inc/switchtec/switchtec.h +++ b/inc/switchtec/switchtec.h @@ -66,6 +66,8 @@ struct switchtec_dev; #define SWITCHTEC_PAX_ID_MASK 0x1f #define SWITCHTEC_PAX_ID_LOCAL SWITCHTEC_PAX_ID_MASK +#define SWITCHTEC_DIAG_MAX_TLP_DWORDS 132 + #ifdef __CHECKER__ #define __gas __attribute__((noderef, address_space(1))) #else @@ -1243,7 +1245,8 @@ int switchtec_diag_refclk_ctl(struct switchtec_dev *dev, int stack_id, bool en); int switchtec_diag_ltssm_log(struct switchtec_dev *dev, int port, int *log_count, struct switchtec_diag_ltssm_log *log_data); - +int switchtec_tlp_inject(struct switchtec_dev * dev, int port_id, int tlp_type, + int tlp_length, int ecrc, uint32_t * raw_tlp_data); #ifdef __cplusplus } #endif diff --git a/lib/diag.c b/lib/diag.c index eb9e8157..22879f50 100644 --- a/lib/diag.c +++ b/lib/diag.c @@ -1214,4 +1214,25 @@ int switchtec_diag_ltssm_log(struct switchtec_dev *dev, return ret; } +int switchtec_tlp_inject(struct switchtec_dev * dev, int port_id, int tlp_type, + int tlp_length, int ecrc, uint32_t * raw_tlp_data) +{ + uint32_t tlp_out; + int ret = 1; + struct switchtec_tlp_inject_in tlp_in = { + .dest_port = port_id, + .tlp_type = tlp_type, + .tlp_length = tlp_length, + .ecrc = ecrc + }; + for (int i = 0; i < tlp_in.tlp_length; i++) { + tlp_in.raw_tlp_data[i] = htole32(*(raw_tlp_data + i)); + } + free(raw_tlp_data); + + ret = switchtec_cmd(dev, MRPC_DIAG_TLP_INJECT, &tlp_in, sizeof(tlp_in), + &tlp_out, sizeof(tlp_out)); + return ret; +} + /**@}*/