#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stropts.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <strings.h>
#include <sys/varargs.h>
#include <errno.h>
#include <sys/byteorder.h>
#include <inttypes.h>
#include <sys/sysmacros.h>
#include <err.h>
#include <libdevinfo.h>
#include "t4nex.h"
#include "osdep.h"
#include "t4fw_interface.h"
#include "cudbg.h"
#include "cudbg_lib_common.h"
#include "cudbg_entity.h"
#define CUDBG_SIZE (32 * 1024 * 1024)
#define CUDBG_MAX_ENTITY_STR_LEN 4096
#define MAX_PARAM_LEN 4096
#ifndef MAX
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#endif
#define T4_NEXUS_NAME "t4nex"
#define T4_PORT_NAME "cxgbe"
struct reg_info {
const char *name;
uint32_t addr;
uint32_t len;
};
struct mod_regs {
const char *name;
const struct reg_info *ri;
unsigned int offset;
};
#include "reg_defs.h"
static char cxgbetool_nexus[PATH_MAX];
char *option_list[] = {
"--collect",
"--view",
};
enum {
CUDBG_OPT_COLLECT,
CUDBG_OPT_VIEW,
CUDBG_OPT_VERSION,
};
static const char * const devlog_level_strings[] = {
[FW_DEVLOG_LEVEL_EMERG] = "EMERG",
[FW_DEVLOG_LEVEL_CRIT] = "CRIT",
[FW_DEVLOG_LEVEL_ERR] = "ERR",
[FW_DEVLOG_LEVEL_NOTICE] = "NOTICE",
[FW_DEVLOG_LEVEL_INFO] = "INFO",
[FW_DEVLOG_LEVEL_DEBUG] = "DEBUG"
};
static const char * const devlog_facility_strings[] = {
[FW_DEVLOG_FACILITY_CORE] = "CORE",
[FW_DEVLOG_FACILITY_CF] = "CF",
[FW_DEVLOG_FACILITY_SCHED] = "SCHED",
[FW_DEVLOG_FACILITY_TIMER] = "TIMER",
[FW_DEVLOG_FACILITY_RES] = "RES",
[FW_DEVLOG_FACILITY_HW] = "HW",
[FW_DEVLOG_FACILITY_FLR] = "FLR",
[FW_DEVLOG_FACILITY_DMAQ] = "DMAQ",
[FW_DEVLOG_FACILITY_PHY] = "PHY",
[FW_DEVLOG_FACILITY_MAC] = "MAC",
[FW_DEVLOG_FACILITY_PORT] = "PORT",
[FW_DEVLOG_FACILITY_VI] = "VI",
[FW_DEVLOG_FACILITY_FILTER] = "FILTER",
[FW_DEVLOG_FACILITY_ACL] = "ACL",
[FW_DEVLOG_FACILITY_TM] = "TM",
[FW_DEVLOG_FACILITY_QFC] = "QFC",
[FW_DEVLOG_FACILITY_DCB] = "DCB",
[FW_DEVLOG_FACILITY_ETH] = "ETH",
[FW_DEVLOG_FACILITY_OFLD] = "OFLD",
[FW_DEVLOG_FACILITY_RI] = "RI",
[FW_DEVLOG_FACILITY_ISCSI] = "ISCSI",
[FW_DEVLOG_FACILITY_FCOE] = "FCOE",
[FW_DEVLOG_FACILITY_FOISCSI] = "FOISCSI",
[FW_DEVLOG_FACILITY_FOFCOE] = "FOFCOE",
[FW_DEVLOG_FACILITY_CHNET] = "CHNET",
};
static const char *progname;
int set_dbg_entity(u8 *dbg_bitmap, char *dbg_entity_list);
static int check_option(char *opt)
{
int i;
for (i = 0; i < ARRAY_SIZE(option_list); i++) {
if (!strcmp(opt, option_list[i]))
return i;
}
return -1;
}
static void usage(FILE *fp)
{
fprintf(fp, "Usage: %s <t4nex# | cxgbe#> [operation]\n", progname);
fprintf(fp,
"\tdevlog show device log\n"
"\tloadfw <FW image> Flash the FW image\n"
"\tcudbg <option> [<args>] Chelsio Unified Debugger\n"
"\tregdump [<module>] Dump registers\n"
"\treg <address>[=<val>] Read or write the registers\n");
exit(fp == stderr ? 1 : 0);
}
static int
doit(const char *iff_name, unsigned long cmd, void *data)
{
int fd = 0;
int rc = 0;
if ((fd = open(iff_name, O_RDWR)) < 0)
return (-1);
rc = (ioctl(fd, cmd, data) < 0) ? errno : rc;
close(fd);
return (rc);
}
static void
get_devlog(int argc, char *argv[], int start_arg, const char *iff_name)
{
struct t4_devlog *devlog;
struct fw_devlog_e *entry, *buf;
int rc = 0, first = 0, nentries, i, j, len;
uint64_t ftstamp = UINT64_MAX;
devlog = malloc(T4_DEVLOG_SIZE + sizeof (struct t4_devlog));
if (!devlog)
err(1, "%s: can't allocate devlog buffer", __func__);
devlog->len = T4_DEVLOG_SIZE;
rc = doit(iff_name, T4_IOCTL_DEVLOG, devlog);
if (rc == ENOBUFS) {
len = devlog->len;
free(devlog);
if ((devlog = malloc(len + sizeof (struct t4_devlog))) == NULL)
err(1, "%s: can't reallocate devlog buffer", __func__);
rc = doit(iff_name, T4_IOCTL_DEVLOG, devlog);
}
if (rc) {
free(devlog);
errx(1, "%s: can't get device log", __func__);
}
nentries = (devlog->len / sizeof (struct fw_devlog_e));
buf = (struct fw_devlog_e *)devlog->data;
for (i = 0; i < nentries; i++) {
entry = &buf[i];
if (entry->timestamp == 0)
break;
entry->timestamp = BE_64(entry->timestamp);
entry->seqno = BE_32(entry->seqno);
for (j = 0; j < 8; j++)
entry->params[j] = BE_32(entry->params[j]);
if (entry->timestamp < ftstamp) {
ftstamp = entry->timestamp;
first = i;
}
}
printf("%10s %15s %8s %8s %s\n", "Seq#", "Tstamp", "Level",
"Facility", "Message");
i = first;
do {
entry = &buf[i];
if (entry->timestamp == 0)
break;
printf("%10d %15llu %8s %8s ", entry->seqno,
entry->timestamp,
(entry->level < ARRAY_SIZE(devlog_level_strings) ?
devlog_level_strings[entry->level] : "UNKNOWN"),
(entry->facility < ARRAY_SIZE(devlog_facility_strings) ?
devlog_facility_strings[entry->facility] : "UNKNOWN"));
printf((const char *)entry->fmt, entry->params[0],
entry->params[1], entry->params[2], entry->params[3],
entry->params[4], entry->params[5], entry->params[6],
entry->params[7]);
if (++i == nentries)
i = 0;
} while (i != first);
free(devlog);
}
static uint32_t xtract(uint32_t val, int shift, int len)
{
return (val >> shift) & ((1 << len) - 1);
}
int dump_block_regs(const struct reg_info *reg_array, const u32 *regs)
{
uint32_t reg_val = 0;
for ( ; reg_array->name; ++reg_array) {
if (!reg_array->len) {
reg_val = regs[reg_array->addr / 4];
printf("[%#7x] %-47s %#-10x %u\n", reg_array->addr,
reg_array->name, reg_val, reg_val);
} else {
uint32_t v = xtract(reg_val, reg_array->addr,
reg_array->len);
printf(" %*u:%u %-47s %#-10x %u\n",
reg_array->addr < 10 ? 3 : 2,
reg_array->addr + reg_array->len - 1,
reg_array->addr, reg_array->name, v, v);
}
}
return 1;
}
int dump_regs_table(int argc, char *argv[], int start_arg,
const u32 *regs, const struct mod_regs *modtab,
int nmodules, const char *modnames)
{
int match = 0;
const char *block_name = NULL;
if (argc == start_arg + 1)
block_name = argv[start_arg];
else if (argc != start_arg)
return -1;
for ( ; nmodules; nmodules--, modtab++) {
if (!block_name || !strcmp(block_name, modtab->name))
match += dump_block_regs(modtab->ri,
regs + modtab->offset);
}
if (!match)
errx(1, "unknown block \"%s\"\navailable: %s", block_name,
modnames);
return 0;
}
#define T5_MODREGS(name) { #name, t5_##name##_regs }
static int
dump_regs_t5(int argc, char *argv[], int start_arg, uint32_t *regs)
{
static struct mod_regs t5_mod[] = {
T5_MODREGS(sge),
{ "pci", t5_pcie_regs },
T5_MODREGS(dbg),
{ "mc0", t5_mc_0_regs },
{ "mc1", t5_mc_1_regs },
T5_MODREGS(ma),
{ "edc0", t5_edc_t50_regs },
{ "edc1", t5_edc_t51_regs },
T5_MODREGS(cim),
T5_MODREGS(tp),
{ "ulprx", t5_ulp_rx_regs },
{ "ulptx", t5_ulp_tx_regs },
{ "pmrx", t5_pm_rx_regs },
{ "pmtx", t5_pm_tx_regs },
T5_MODREGS(mps),
{ "cplsw", t5_cpl_switch_regs },
T5_MODREGS(smb),
{ "i2c", t5_i2cm_regs },
T5_MODREGS(mi),
T5_MODREGS(uart),
T5_MODREGS(pmu),
T5_MODREGS(sf),
T5_MODREGS(pl),
T5_MODREGS(le),
T5_MODREGS(ncsi),
T5_MODREGS(mac),
{ "hma", t5_hma_t5_regs }
};
return dump_regs_table(argc, argv, start_arg, regs, t5_mod,
ARRAY_SIZE(t5_mod),
"sge, pci, dbg, mc0, mc1, ma, edc0, edc1, cim, "
"tp, ulprx, ulptx, pmrx, pmtx, mps, cplsw, smb, "
"i2c, mi, uart, pmu, sf, pl, le, ncsi, "
"mac, hma");
}
#undef T5_MODREGS
#define T6_MODREGS(name) { #name, t6_##name##_regs }
static int dump_regs_t6(int argc, char *argv[], int start_arg, const u32 *regs)
{
static struct mod_regs t6_mod[] = {
T6_MODREGS(sge),
{ "pci", t6_pcie_regs },
T6_MODREGS(dbg),
{ "mc0", t6_mc_0_regs },
T6_MODREGS(ma),
{ "edc0", t6_edc_t60_regs },
{ "edc1", t6_edc_t61_regs },
T6_MODREGS(cim),
T6_MODREGS(tp),
{ "ulprx", t6_ulp_rx_regs },
{ "ulptx", t6_ulp_tx_regs },
{ "pmrx", t6_pm_rx_regs },
{ "pmtx", t6_pm_tx_regs },
T6_MODREGS(mps),
{ "cplsw", t6_cpl_switch_regs },
T6_MODREGS(smb),
{ "i2c", t6_i2cm_regs },
T6_MODREGS(mi),
T6_MODREGS(uart),
T6_MODREGS(pmu),
T6_MODREGS(sf),
T6_MODREGS(pl),
T6_MODREGS(le),
T6_MODREGS(ncsi),
T6_MODREGS(mac),
{ "hma", t6_hma_t6_regs }
};
return dump_regs_table(argc, argv, start_arg, regs, t6_mod,
ARRAY_SIZE(t6_mod),
"sge, pci, dbg, mc0, ma, edc0, edc1, cim, "
"tp, ulprx, ulptx, pmrx, pmtx, mps, cplsw, smb, "
"i2c, mi, uart, pmu, sf, pl, le, ncsi, "
"mac, hma");
}
#undef T6_MODREGS
static int
get_regdump(int argc, char *argv[], int start_arg, const char *iff_name)
{
int rc, vers, revision, is_pcie;
uint32_t len, length;
struct t4_regdump *regs;
len = MAX(T5_REGDUMP_SIZE, T6_REGDUMP_SIZE);
regs = malloc(len + sizeof(struct t4_regdump));
if (!regs)
err(1, "%s: can't allocate reg dump buffer", __func__);
regs->len = len;
rc = doit(iff_name, T4_IOCTL_REGDUMP, regs);
if (rc == ENOBUFS) {
length = regs->len;
free(regs);
regs = malloc(length + sizeof(struct t4_regdump));
if (regs == NULL)
err(1, "%s: can't reallocate regs buffer", __func__);
rc = doit(iff_name, T4_IOCTL_REGDUMP, regs);
}
if (rc) {
free(regs);
errx(1, "%s: can't get register dumps", __func__);
}
vers = regs->version & 0x3ff;
revision = (regs->version >> 10) & 0x3f;
is_pcie = (regs->version & 0x80000000) != 0;
if (vers == 5) {
return dump_regs_t5(argc, argv, start_arg, regs->data);
} else if (vers == 6) {
return dump_regs_t6(argc, argv, start_arg, regs->data);
} else {
errx(1, "unknown card type %d.%d.%d", vers, revision, is_pcie);
}
return 0;
}
static void
write_reg(const char *iff_name, uint32_t addr, uint32_t val)
{
struct t4_reg32_cmd reg;
reg.reg = addr;
reg.value = val;
if (doit(iff_name, T4_IOCTL_PUT32, ®) < 0)
err(1, "register write");
}
static uint32_t
read_reg(const char *iff_name, uint32_t addr)
{
struct t4_reg32_cmd reg;
reg.reg = addr;
if (doit(iff_name, T4_IOCTL_GET32, ®) < 0)
err(1, "register read");
return reg.value;
}
static void register_io(int argc, char *argv[], int start_arg,
const char *iff_name)
{
char *p;
uint32_t addr = 0, val = 0, write = 0;
if (argc != start_arg + 1) {
errx(1, "incorrect number of arguments");
}
errno = 0;
addr = strtoul(argv[start_arg], &p, 0);
if (addr == 0 || errno != 0) {
errx(1, "invalid arguments");
}
if (p == argv[start_arg])
return;
if (*p == '=' && p[1]) {
val = strtoul(p + 1, &p, 0);
write = 1;
}
if (*p) {
errx(1, "bad parameter \"%s\"", argv[start_arg]);
}
if (write) {
write_reg(iff_name, addr, val);
} else {
val = read_reg(iff_name, addr);
printf("%#x [%u]\n", val, val);
}
}
static void
load_fw(int argc, char *argv[], int start_arg, const char *iff_name)
{
const char *fname = argv[start_arg];
struct t4_ldfw *fw;
struct stat sb;
size_t len;
int fd;
if (argc != 4)
errx(1, "incorrect number of arguments");
fd = open(fname, O_RDONLY);
if (fd < 0)
err(1, "%s: opening %s failed", __func__, fname);
if (fstat(fd, &sb) < 0) {
warn("%s: fstat %s failed", __func__, fname);
close(fd);
exit(1);
}
len = (size_t)sb.st_size;
fw = malloc(sizeof (struct t4_ldfw) + len);
if (!fw) {
warn("%s: %s allocate %zu bytes failed",
__func__, fname, sizeof (struct t4_ldfw) + len);
close(fd);
exit(1);
}
if (read(fd, fw->data, len) < len) {
warn("%s: %s read failed", __func__, fname);
close(fd);
free(fw);
exit(1);
}
close(fd);
fw->len = len;
if (doit(iff_name, T4_IOCTL_LOAD_FW, fw)) {
free(fw);
err(1, "%s: IOCTL failed", __func__);
} else {
printf("FW flash success, reload driver/reboot to take "
"effect\n");
}
free(fw);
}
int read_input_file(char *in_file, void **buf, int *buf_size)
{
FILE *fptr = NULL;
size_t count;
int rc = 0;
fptr = fopen(in_file, "rb");
if (!fptr) {
perror("error in opening file ");
rc = -1;
goto out;
}
rc = fseek(fptr, 0, SEEK_END);
if (rc < 0) {
perror("error in seeking file ");
rc = -1;
goto out;
}
*buf_size = ftell(fptr);
rc = fseek(fptr, 0, SEEK_SET);
if (rc < 0) {
perror("error in seeking file ");
rc = -1;
goto out;
}
*buf = (void *) malloc(*buf_size);
if (*buf == NULL) {
rc = CUDBG_STATUS_NOSPACE;
goto out;
}
memset(*buf, 0, *buf_size);
count = fread(*buf, 1, *buf_size, fptr);
if (count != *buf_size) {
perror("error in reading from file ");
goto out;
}
out:
if (fptr)
fclose(fptr);
return rc;
}
static void
do_collect(char *dbg_entity_list, const char *iff_name, const char *fname)
{
struct t4_cudbg_dump *cudbg;
int fd;
cudbg = malloc(sizeof(struct t4_cudbg_dump) + CUDBG_SIZE);
if (!cudbg) {
err(1, "%s:allocate %d bytes failed", __func__, CUDBG_SIZE);
}
memset(cudbg, 0, sizeof(struct t4_cudbg_dump) + CUDBG_SIZE);
cudbg->len = CUDBG_SIZE;
set_dbg_entity(cudbg->bitmap, dbg_entity_list);
if (doit(iff_name, T4_IOCTL_GET_CUDBG, cudbg)) {
free(cudbg);
err(1, "%s: IOCTL failed", __func__);
}
fd = open(fname, O_CREAT | O_TRUNC | O_EXCL | O_WRONLY,
S_IRUSR | S_IRGRP | S_IROTH);
if (fd < 0) {
err(1, "%s: file open failed", __func__);
}
write(fd, cudbg->data, cudbg->len);
close(fd);
free(cudbg);
}
static void
do_view(char *dbg_entity_list, char *in_file)
{
void *handle = NULL;
void *buf = NULL;
int buf_size = 32 * 1024 * 1024;
int next_offset = 0;
int data_len;
int rc = 0;
handle = cudbg_alloc_handle();
if (!handle)
goto out;
rc = read_input_file(in_file, &buf, &buf_size);
if (rc < 0) {
goto out;
}
set_dbg_entity(((struct cudbg_private *)handle)->dbg_init.dbg_bitmap,
dbg_entity_list);
do {
if (buf_size - next_offset <= 0)
break;
data_len = cudbg_view(handle, buf+next_offset,
buf_size-next_offset, NULL, 0);
next_offset += data_len;
if (data_len > 0)
printf("\n\t\t<========================END============="\
"===========>\t\t\n\n\n");
} while (data_len > 0);
out:
if (buf)
free(buf);
if (handle)
cudbg_free_handle(handle);
return;
}
typedef void (*cudbg_alias_get_entities_cb)(char *dst, u32 dst_size);
struct entity_access_list {
const char *name;
cudbg_alias_get_entities_cb get_entities_cb;
};
void
cudbg_append_string(char *dst, u32 dst_size, char *src)
{
strlcat(dst, src, dst_size);
strlcat(dst, ",", dst_size);
}
static void
cudbg_alias_get_allregs(char *dst, u32 dst_size)
{
u32 i;
for (i = 0; i < ARRAY_SIZE(entity_list); i++)
if (entity_list[i].flag & (1 << ENTITY_FLAG_REGISTER))
cudbg_append_string(dst, dst_size, entity_list[i].name);
}
static struct entity_access_list ATTRIBUTE_UNUSED entity_alias_list[] = {
{"allregs", cudbg_alias_get_allregs},
};
static int
check_dbg_entity(char *entity)
{
u32 i;
for (i = 0; i < ARRAY_SIZE(entity_list); i++)
if (!strcmp(entity, entity_list[i].name))
return entity_list[i].bit;
return -1;
}
static
int get_alias(const char *entity)
{
u32 i;
for (i = 0; i < ARRAY_SIZE(entity_alias_list); i++)
if (!strcmp(entity, entity_alias_list[i].name))
return i;
return -1;
}
static int
parse_entity_list(const char *dbg_entity_list, char *dst,
u32 dst_size)
{
char *tmp_dbg_entity_list;
char *dbg_entity;
int rc, i;
tmp_dbg_entity_list = malloc(CUDBG_MAX_ENTITY_STR_LEN);
if (!tmp_dbg_entity_list)
return ENOMEM;
strlcpy(tmp_dbg_entity_list, dbg_entity_list, CUDBG_MAX_ENTITY_STR_LEN);
dbg_entity = strtok(tmp_dbg_entity_list, ",");
while (dbg_entity != NULL) {
rc = check_dbg_entity(dbg_entity);
if (rc < 0) {
i = get_alias(dbg_entity);
if (i < 0) {
printf("\nUnknown entity: %s\n", dbg_entity);
rc = CUDBG_STATUS_ENTITY_NOT_FOUND;
goto out_err;
} else {
entity_alias_list[i].get_entities_cb(dst, dst_size);
}
} else {
cudbg_append_string(dst, dst_size, entity_list[rc].name);
}
dbg_entity = strtok(NULL, ",");
}
rc = 0;
out_err:
free(tmp_dbg_entity_list);
return rc;
}
static
int get_entity_list(const char *in_buff, char **out_buff)
{
char *dbg_entity_list;
int rc;
dbg_entity_list = malloc(CUDBG_MAX_ENTITY_STR_LEN);
if (!dbg_entity_list)
return ENOMEM;
memset(dbg_entity_list, 0, CUDBG_MAX_ENTITY_STR_LEN);
rc = parse_entity_list(in_buff, dbg_entity_list,
CUDBG_MAX_ENTITY_STR_LEN);
if (rc) {
free(dbg_entity_list);
return rc;
}
dbg_entity_list[strlen(dbg_entity_list) - 1] = '\0';
*out_buff = dbg_entity_list;
return 0;
}
static void
put_entity_list(char *buf)
{
if (buf)
free(buf);
}
int
set_dbg_entity(u8 *dbg_bitmap, char *dbg_entity_list)
{
int i, dbg_entity_bit, rc = 0;
char *dbg_entity;
char *dbg_entity_list_tmp;
dbg_entity_list_tmp = malloc(MAX_PARAM_LEN);
if (!dbg_entity_list_tmp) {
rc = CUDBG_STATUS_NOSPACE;
return rc;
}
if (dbg_entity_list != NULL) {
strlcpy(dbg_entity_list_tmp, dbg_entity_list, MAX_PARAM_LEN);
dbg_entity = strtok(dbg_entity_list_tmp, ",");
}
else
dbg_entity = NULL;
while (dbg_entity != NULL) {
rc = check_dbg_entity(dbg_entity);
if (rc < 0) {
printf("\n\tInvalid debug entity: %s\n", dbg_entity);
goto out_free;
}
dbg_entity_bit = rc;
if (dbg_entity_bit == CUDBG_ALL) {
for (i = 1; i < CUDBG_MAX_ENTITY; i++)
set_dbg_bitmap(dbg_bitmap, i);
set_dbg_bitmap(dbg_bitmap, CUDBG_ALL);
break;
} else {
set_dbg_bitmap(dbg_bitmap, dbg_entity_bit);
}
dbg_entity = strtok(NULL, ",");
}
rc = 0;
out_free:
free(dbg_entity_list_tmp);
return rc;
}
static void
get_cudbg(int argc, char *argv[], int start_arg, const char *iff_name)
{
char *dbg_entity_list = NULL;
int rc = 0, option;
if (start_arg >= argc)
errx(1, "no option provided");
rc = check_option(argv[start_arg++]);
if (rc < 0) {
errx(1, "%s:Invalid option provided", __func__);
}
option = rc;
if (option == CUDBG_OPT_VERSION) {
printf("Library Version %d.%d.%d\n", CUDBG_MAJOR_VERSION,
CUDBG_MINOR_VERSION, CUDBG_BUILD_VERSION);
return;
}
if (argc < 5) {
errx(1, "Invalid number of arguments\n");
}
rc = get_entity_list(argv[start_arg++],
&dbg_entity_list);
if (rc) {
errx(1, "Error in parsing entity\n");
}
if (argc < 6) {
errx(1, "File name is missing\n");
}
switch (option) {
case CUDBG_OPT_COLLECT:
do_collect(dbg_entity_list, iff_name, argv[start_arg]);
break;
case CUDBG_OPT_VIEW:
do_view(dbg_entity_list, argv[start_arg]);
break;
default:
errx(1, "Wrong option provided\n");
}
put_entity_list(dbg_entity_list);
}
static void
run_cmd(int argc, char *argv[], const char *iff_name)
{
if (strcmp(argv[2], "devlog") == 0)
get_devlog(argc, argv, 3, iff_name);
else if (strcmp(argv[2], "loadfw") == 0)
load_fw(argc, argv, 3, iff_name);
else if (strcmp(argv[2], "cudbg") == 0)
get_cudbg(argc, argv, 3, iff_name);
else if (strcmp(argv[2], "regdump") == 0)
get_regdump(argc, argv, 3, iff_name);
else if (strcmp(argv[2], "reg") == 0)
register_io(argc, argv, 3, iff_name);
else
usage(stderr);
}
static const char *
cxgbetool_parse_path(char *arg)
{
struct stat st;
di_node_t root, node;
const char *numptr, *errstr;
size_t drvlen;
int inst;
boolean_t is_t4nex = B_TRUE;
char mname[64];
if (stat(arg, &st) == 0) {
if (S_ISCHR(st.st_mode)) {
return (arg);
}
}
if (strncmp(arg, T4_NEXUS_NAME, sizeof (T4_NEXUS_NAME) - 1) == 0) {
drvlen = sizeof (T4_NEXUS_NAME) - 1;
} else if (strncmp(arg, T4_PORT_NAME, sizeof (T4_PORT_NAME) - 1) == 0) {
is_t4nex = B_FALSE;
drvlen = sizeof (T4_PORT_NAME) - 1;
} else {
errx(EXIT_FAILURE, "cannot use device %s: not a character "
"device or a %s/%s device instance", arg, T4_PORT_NAME,
T4_NEXUS_NAME);
}
numptr = arg + drvlen;
inst = (int)strtonum(numptr, 0, INT_MAX, &errstr);
if (errstr != NULL) {
errx(EXIT_FAILURE, "failed to parse instance number '%s': %s",
numptr, errstr);
}
arg[drvlen] = '\0';
root = di_init("/", DINFOCPYALL);
if (root == DI_NODE_NIL) {
err(EXIT_FAILURE, "failed to initialize libdevinfo while "
"trying to map device name %s", arg);
}
for (node = di_drv_first_node(arg, root); node != DI_NODE_NIL;
node = di_drv_next_node(node)) {
char *bpath;
di_minor_t minor = DI_MINOR_NIL;
if (di_instance(node) != inst) {
continue;
}
if (!is_t4nex) {
const char *pdrv;
node = di_parent_node(node);
pdrv = di_driver_name(node);
if (pdrv == NULL || strcmp(pdrv, T4_NEXUS_NAME) != 0) {
errx(EXIT_FAILURE, "%s does not have %s "
"parent, found %s%d", arg, T4_NEXUS_NAME,
pdrv != NULL ? pdrv : "unknown",
pdrv != NULL ? di_instance(node) : -1);
}
}
(void) snprintf(mname, sizeof (mname), "%s,%d", T4_NEXUS_NAME,
di_instance(node));
while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
if (strcmp(di_minor_name(minor), mname) == 0) {
break;
}
}
if (minor == DI_MINOR_NIL) {
errx(EXIT_FAILURE, "failed to find minor %s on %s%d",
mname, di_driver_name(node), di_instance(node));
}
bpath = di_devfs_minor_path(minor);
if (bpath == NULL) {
err(EXIT_FAILURE, "failed to get minor path for "
"%s%d:%s", di_driver_name(node), di_instance(node),
di_minor_name(minor));
}
if (snprintf(cxgbetool_nexus, sizeof (cxgbetool_nexus),
"/devices%s", bpath) >= sizeof (cxgbetool_nexus)) {
errx(EXIT_FAILURE, "failed to construct full /devices "
"path for %s: internal path buffer would have "
"overflowed", bpath);
}
di_devfs_path_free(bpath);
di_fini(root);
return (cxgbetool_nexus);
}
errx(EXIT_FAILURE, "failed to map %s%d to a %s or %s instance",
arg, inst, T4_PORT_NAME, T4_NEXUS_NAME);
}
int
main(int argc, char *argv[])
{
const char *iff_name;
progname = argv[0];
if (argc == 2) {
if (strcmp(argv[1], "-h") == 0 ||
strcmp(argv[1], "--help") == 0) {
usage(stdout);
}
}
if (argc < 3)
usage(stderr);
iff_name = cxgbetool_parse_path(argv[1]);
run_cmd(argc, argv, iff_name);
return (0);
}