cxgbetool.c revision 284984
1222900Snp/*-
2222900Snp * Copyright (c) 2011 Chelsio Communications, Inc.
3222900Snp * All rights reserved.
4222900Snp * Written by: Navdeep Parhar <np@FreeBSD.org>
5222900Snp *
6222900Snp * Redistribution and use in source and binary forms, with or without
7222900Snp * modification, are permitted provided that the following conditions
8222900Snp * are met:
9222900Snp * 1. Redistributions of source code must retain the above copyright
10222900Snp *    notice, this list of conditions and the following disclaimer.
11222900Snp * 2. Redistributions in binary form must reproduce the above copyright
12222900Snp *    notice, this list of conditions and the following disclaimer in the
13222900Snp *    documentation and/or other materials provided with the distribution.
14222900Snp *
15222900Snp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16222900Snp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17222900Snp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18222900Snp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19222900Snp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20222900Snp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21222900Snp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22222900Snp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23222900Snp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24222900Snp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25222900Snp * SUCH DAMAGE.
26222900Snp */
27222900Snp
28222900Snp#include <sys/cdefs.h>
29222900Snp__FBSDID("$FreeBSD: head/tools/tools/cxgbetool/cxgbetool.c 284984 2015-06-30 22:30:21Z np $");
30222900Snp
31222900Snp#include <stdint.h>
32222900Snp#include <stdlib.h>
33228594Snp#include <unistd.h>
34247854Snp#include <ctype.h>
35222900Snp#include <errno.h>
36222900Snp#include <err.h>
37222900Snp#include <fcntl.h>
38222900Snp#include <string.h>
39222900Snp#include <stdio.h>
40222900Snp#include <sys/ioctl.h>
41241401Snp#include <limits.h>
42228594Snp#include <sys/mman.h>
43222900Snp#include <sys/types.h>
44222900Snp#include <sys/socket.h>
45228594Snp#include <sys/stat.h>
46222900Snp#include <net/ethernet.h>
47222900Snp#include <netinet/in.h>
48222900Snp#include <arpa/inet.h>
49258698Snp#include <net/sff8472.h>
50222900Snp
51222900Snp#include "t4_ioctl.h"
52222900Snp
53222900Snp#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
54259048Snp#define in_range(val, lo, hi) ( val < 0 || (val <= hi && val >= lo))
55222974Snp#define	max(x, y) ((x) > (y) ? (x) : (y))
56222974Snp
57222900Snpstatic const char *progname, *nexus;
58253870Snpstatic int chip_id;	/* 4 for T4, 5 for T5 */
59222900Snp
60222900Snpstruct reg_info {
61222900Snp	const char *name;
62222900Snp	uint32_t addr;
63222900Snp	uint32_t len;
64222900Snp};
65222900Snp
66222900Snpstruct mod_regs {
67222900Snp	const char *name;
68222900Snp	const struct reg_info *ri;
69222900Snp};
70222900Snp
71222974Snpstruct field_desc {
72222974Snp	const char *name;     /* Field name */
73222974Snp	unsigned short start; /* Start bit position */
74222974Snp	unsigned short end;   /* End bit position */
75222974Snp	unsigned char shift;  /* # of low order bits omitted and implicitly 0 */
76222974Snp	unsigned char hex;    /* Print field in hex instead of decimal */
77222974Snp	unsigned char islog2; /* Field contains the base-2 log of the value */
78222974Snp};
79222974Snp
80222900Snp#include "reg_defs_t4.c"
81222900Snp#include "reg_defs_t4vf.c"
82248925Snp#include "reg_defs_t5.c"
83222900Snp
84222900Snpstatic void
85222900Snpusage(FILE *fp)
86222900Snp{
87222900Snp	fprintf(fp, "Usage: %s <nexus> [operation]\n", progname);
88222900Snp	fprintf(fp,
89241416Snp	    "\tclearstats <port>                   clear port statistics\n"
90222974Snp	    "\tcontext <type> <id>                 show an SGE context\n"
91222900Snp	    "\tfilter <idx> [<param> <val>] ...    set a filter\n"
92222900Snp	    "\tfilter <idx> delete|clear           delete a filter\n"
93222900Snp	    "\tfilter list                         list all filters\n"
94222900Snp	    "\tfilter mode [<match>] ...           get/set global filter mode\n"
95241401Snp	    "\ti2c <port> <devaddr> <addr> [<len>] read from i2c device\n"
96228594Snp	    "\tloadfw <fw-image.bin>               install firmware\n"
97228594Snp	    "\tmemdump <addr> <len>                dump a memory range\n"
98269106Snp	    "\tmodinfo <port> [raw]                optics/cable information\n"
99222900Snp	    "\treg <address>[=<val>]               read/write register\n"
100222900Snp	    "\treg64 <address>[=<val>]             read/write 64 bit register\n"
101222900Snp	    "\tregdump [<module>] ...              dump registers\n"
102259048Snp	    "\tsched-class params <param> <val> .. configure TX scheduler class\n"
103259048Snp	    "\tsched-queue <port> <queue> <class>  bind NIC queues to TX Scheduling class\n"
104222900Snp	    "\tstdio                               interactive mode\n"
105228594Snp	    "\ttcb <tid>                           read TCB\n"
106259048Snp	    "\ttracer <idx> tx<n>|rx<n>            set and enable a tracer\n"
107253691Snp	    "\ttracer <idx> disable|enable         disable or enable a tracer\n"
108253691Snp	    "\ttracer list                         list all tracers\n"
109222900Snp	    );
110222900Snp}
111222900Snp
112222900Snpstatic inline unsigned int
113222900Snpget_card_vers(unsigned int version)
114222900Snp{
115222900Snp	return (version & 0x3ff);
116222900Snp}
117222900Snp
118222900Snpstatic int
119222900Snpreal_doit(unsigned long cmd, void *data, const char *cmdstr)
120222900Snp{
121222900Snp	static int fd = -1;
122222900Snp	int rc = 0;
123222900Snp
124222900Snp	if (fd == -1) {
125222900Snp		char buf[64];
126222900Snp
127222900Snp		snprintf(buf, sizeof(buf), "/dev/%s", nexus);
128222900Snp		if ((fd = open(buf, O_RDWR)) < 0) {
129222900Snp			warn("open(%s)", nexus);
130222900Snp			rc = errno;
131222900Snp			return (rc);
132222900Snp		}
133253870Snp		chip_id = nexus[1] - '0';
134222900Snp	}
135222900Snp
136222900Snp	rc = ioctl(fd, cmd, data);
137222900Snp	if (rc < 0) {
138222900Snp		warn("%s", cmdstr);
139222900Snp		rc = errno;
140222900Snp	}
141222900Snp
142222900Snp	return (rc);
143222900Snp}
144222900Snp#define doit(x, y) real_doit(x, y, #x)
145222900Snp
146222900Snpstatic char *
147222900Snpstr_to_number(const char *s, long *val, long long *vall)
148222900Snp{
149222900Snp	char *p;
150222900Snp
151222900Snp	if (vall)
152222900Snp		*vall = strtoll(s, &p, 0);
153222900Snp	else if (val)
154222900Snp		*val = strtol(s, &p, 0);
155222900Snp	else
156222900Snp		p = NULL;
157222900Snp
158222900Snp	return (p);
159222900Snp}
160222900Snp
161222900Snpstatic int
162222900Snpread_reg(long addr, int size, long long *val)
163222900Snp{
164222900Snp	struct t4_reg reg;
165222900Snp	int rc;
166222900Snp
167222900Snp	reg.addr = (uint32_t) addr;
168222900Snp	reg.size = (uint32_t) size;
169222900Snp	reg.val = 0;
170222900Snp
171222900Snp	rc = doit(CHELSIO_T4_GETREG, &reg);
172222900Snp
173222900Snp	*val = reg.val;
174222900Snp
175222900Snp	return (rc);
176222900Snp}
177222900Snp
178222900Snpstatic int
179222900Snpwrite_reg(long addr, int size, long long val)
180222900Snp{
181222900Snp	struct t4_reg reg;
182222900Snp
183222900Snp	reg.addr = (uint32_t) addr;
184222900Snp	reg.size = (uint32_t) size;
185222900Snp	reg.val = (uint64_t) val;
186222900Snp
187222900Snp	return doit(CHELSIO_T4_SETREG, &reg);
188222900Snp}
189222900Snp
190222900Snpstatic int
191222900Snpregister_io(int argc, const char *argv[], int size)
192222900Snp{
193222900Snp	char *p, *v;
194222900Snp	long addr;
195222900Snp	long long val;
196222900Snp	int w = 0, rc;
197222900Snp
198222900Snp	if (argc == 1) {
199222900Snp		/* <reg> OR <reg>=<value> */
200222900Snp
201222900Snp		p = str_to_number(argv[0], &addr, NULL);
202222900Snp		if (*p) {
203222900Snp			if (*p != '=') {
204222900Snp				warnx("invalid register \"%s\"", argv[0]);
205222900Snp				return (EINVAL);
206222900Snp			}
207222900Snp
208222900Snp			w = 1;
209222900Snp			v = p + 1;
210222900Snp			p = str_to_number(v, NULL, &val);
211222900Snp
212222900Snp			if (*p) {
213222900Snp				warnx("invalid value \"%s\"", v);
214222900Snp				return (EINVAL);
215222900Snp			}
216222900Snp		}
217222900Snp
218222900Snp	} else if (argc == 2) {
219222900Snp		/* <reg> <value> */
220222900Snp
221222900Snp		w = 1;
222222900Snp
223222900Snp		p = str_to_number(argv[0], &addr, NULL);
224222900Snp		if (*p) {
225222900Snp			warnx("invalid register \"%s\"", argv[0]);
226222900Snp			return (EINVAL);
227222900Snp		}
228222900Snp
229222900Snp		p = str_to_number(argv[1], NULL, &val);
230222900Snp		if (*p) {
231222900Snp			warnx("invalid value \"%s\"", argv[1]);
232222900Snp			return (EINVAL);
233222900Snp		}
234222900Snp	} else {
235222900Snp		warnx("reg: invalid number of arguments (%d)", argc);
236222900Snp		return (EINVAL);
237222900Snp	}
238222900Snp
239222900Snp	if (w)
240222900Snp		rc = write_reg(addr, size, val);
241222900Snp	else {
242222900Snp		rc = read_reg(addr, size, &val);
243222900Snp		if (rc == 0)
244222900Snp			printf("0x%llx [%llu]\n", val, val);
245222900Snp	}
246222900Snp
247222900Snp	return (rc);
248222900Snp}
249222900Snp
250222900Snpstatic inline uint32_t
251222900Snpxtract(uint32_t val, int shift, int len)
252222900Snp{
253222900Snp	return (val >> shift) & ((1 << len) - 1);
254222900Snp}
255222900Snp
256222900Snpstatic int
257222900Snpdump_block_regs(const struct reg_info *reg_array, const uint32_t *regs)
258222900Snp{
259222900Snp	uint32_t reg_val = 0;
260222900Snp
261222900Snp	for ( ; reg_array->name; ++reg_array)
262222900Snp		if (!reg_array->len) {
263222900Snp			reg_val = regs[reg_array->addr / 4];
264222900Snp			printf("[%#7x] %-47s %#-10x %u\n", reg_array->addr,
265222900Snp			       reg_array->name, reg_val, reg_val);
266222900Snp		} else {
267222900Snp			uint32_t v = xtract(reg_val, reg_array->addr,
268222900Snp					    reg_array->len);
269222900Snp
270222900Snp			printf("    %*u:%u %-47s %#-10x %u\n",
271222900Snp			       reg_array->addr < 10 ? 3 : 2,
272222900Snp			       reg_array->addr + reg_array->len - 1,
273222900Snp			       reg_array->addr, reg_array->name, v, v);
274222900Snp		}
275222900Snp
276222900Snp	return (1);
277222900Snp}
278222900Snp
279222900Snpstatic int
280222900Snpdump_regs_table(int argc, const char *argv[], const uint32_t *regs,
281222900Snp    const struct mod_regs *modtab, int nmodules)
282222900Snp{
283222900Snp	int i, j, match;
284222900Snp
285222900Snp	for (i = 0; i < argc; i++) {
286222900Snp		for (j = 0; j < nmodules; j++) {
287222900Snp			if (!strcmp(argv[i], modtab[j].name))
288222900Snp				break;
289222900Snp		}
290222900Snp
291222900Snp		if (j == nmodules) {
292222900Snp			warnx("invalid register block \"%s\"", argv[i]);
293222900Snp			fprintf(stderr, "\nAvailable blocks:");
294222900Snp			for ( ; nmodules; nmodules--, modtab++)
295222900Snp				fprintf(stderr, " %s", modtab->name);
296222900Snp			fprintf(stderr, "\n");
297222900Snp			return (EINVAL);
298222900Snp		}
299222900Snp	}
300222900Snp
301222900Snp	for ( ; nmodules; nmodules--, modtab++) {
302222900Snp
303222900Snp		match = argc == 0 ? 1 : 0;
304222900Snp		for (i = 0; !match && i < argc; i++) {
305222900Snp			if (!strcmp(argv[i], modtab->name))
306222900Snp				match = 1;
307222900Snp		}
308222900Snp
309222900Snp		if (match)
310222900Snp			dump_block_regs(modtab->ri, regs);
311222900Snp	}
312222900Snp
313222900Snp	return (0);
314222900Snp}
315222900Snp
316222900Snp#define T4_MODREGS(name) { #name, t4_##name##_regs }
317222900Snpstatic int
318222900Snpdump_regs_t4(int argc, const char *argv[], const uint32_t *regs)
319222900Snp{
320222900Snp	static struct mod_regs t4_mod[] = {
321222900Snp		T4_MODREGS(sge),
322222900Snp		{ "pci", t4_pcie_regs },
323222900Snp		T4_MODREGS(dbg),
324222900Snp		T4_MODREGS(mc),
325222900Snp		T4_MODREGS(ma),
326222900Snp		{ "edc0", t4_edc_0_regs },
327222900Snp		{ "edc1", t4_edc_1_regs },
328259048Snp		T4_MODREGS(cim),
329222900Snp		T4_MODREGS(tp),
330222900Snp		T4_MODREGS(ulp_rx),
331222900Snp		T4_MODREGS(ulp_tx),
332222900Snp		{ "pmrx", t4_pm_rx_regs },
333222900Snp		{ "pmtx", t4_pm_tx_regs },
334222900Snp		T4_MODREGS(mps),
335222900Snp		{ "cplsw", t4_cpl_switch_regs },
336222900Snp		T4_MODREGS(smb),
337222900Snp		{ "i2c", t4_i2cm_regs },
338222900Snp		T4_MODREGS(mi),
339222900Snp		T4_MODREGS(uart),
340259048Snp		T4_MODREGS(pmu),
341222900Snp		T4_MODREGS(sf),
342222900Snp		T4_MODREGS(pl),
343222900Snp		T4_MODREGS(le),
344222900Snp		T4_MODREGS(ncsi),
345222900Snp		T4_MODREGS(xgmac)
346222900Snp	};
347222900Snp
348222900Snp	return dump_regs_table(argc, argv, regs, t4_mod, ARRAY_SIZE(t4_mod));
349222900Snp}
350222900Snp#undef T4_MODREGS
351222900Snp
352222900Snpstatic int
353222900Snpdump_regs_t4vf(int argc, const char *argv[], const uint32_t *regs)
354222900Snp{
355222900Snp	static struct mod_regs t4vf_mod[] = {
356222900Snp		{ "sge", t4vf_sge_regs },
357222900Snp		{ "mps", t4vf_mps_regs },
358222900Snp		{ "pl", t4vf_pl_regs },
359222900Snp		{ "mbdata", t4vf_mbdata_regs },
360222900Snp		{ "cim", t4vf_cim_regs },
361222900Snp	};
362222900Snp
363222900Snp	return dump_regs_table(argc, argv, regs, t4vf_mod,
364222900Snp	    ARRAY_SIZE(t4vf_mod));
365222900Snp}
366222900Snp
367248925Snp#define T5_MODREGS(name) { #name, t5_##name##_regs }
368222900Snpstatic int
369248925Snpdump_regs_t5(int argc, const char *argv[], const uint32_t *regs)
370248925Snp{
371248925Snp	static struct mod_regs t5_mod[] = {
372248925Snp		T5_MODREGS(sge),
373248925Snp		{ "pci", t5_pcie_regs },
374248925Snp		T5_MODREGS(dbg),
375248925Snp		{ "mc0", t5_mc_0_regs },
376248925Snp		{ "mc1", t5_mc_1_regs },
377248925Snp		T5_MODREGS(ma),
378248925Snp		{ "edc0", t5_edc_t50_regs },
379248925Snp		{ "edc1", t5_edc_t51_regs },
380248925Snp		T5_MODREGS(cim),
381248925Snp		T5_MODREGS(tp),
382248925Snp		{ "ulprx", t5_ulp_rx_regs },
383248925Snp		{ "ulptx", t5_ulp_tx_regs },
384248925Snp		{ "pmrx", t5_pm_rx_regs },
385248925Snp		{ "pmtx", t5_pm_tx_regs },
386248925Snp		T5_MODREGS(mps),
387248925Snp		{ "cplsw", t5_cpl_switch_regs },
388248925Snp		T5_MODREGS(smb),
389248925Snp		{ "i2c", t5_i2cm_regs },
390248925Snp		T5_MODREGS(mi),
391248925Snp		T5_MODREGS(uart),
392248925Snp		T5_MODREGS(pmu),
393248925Snp		T5_MODREGS(sf),
394248925Snp		T5_MODREGS(pl),
395248925Snp		T5_MODREGS(le),
396248925Snp		T5_MODREGS(ncsi),
397248925Snp		T5_MODREGS(mac),
398248925Snp		{ "hma", t5_hma_t5_regs }
399248925Snp	};
400248925Snp
401248925Snp	return dump_regs_table(argc, argv, regs, t5_mod, ARRAY_SIZE(t5_mod));
402248925Snp}
403248925Snp#undef T5_MODREGS
404248925Snp
405248925Snpstatic int
406222900Snpdump_regs(int argc, const char *argv[])
407222900Snp{
408248925Snp	int vers, revision, rc;
409222900Snp	struct t4_regdump regs;
410248925Snp	uint32_t len;
411222900Snp
412248925Snp	len = max(T4_REGDUMP_SIZE, T5_REGDUMP_SIZE);
413248925Snp	regs.data = calloc(1, len);
414222900Snp	if (regs.data == NULL) {
415222900Snp		warnc(ENOMEM, "regdump");
416222900Snp		return (ENOMEM);
417222900Snp	}
418222900Snp
419248925Snp	regs.len = len;
420222900Snp	rc = doit(CHELSIO_T4_REGDUMP, &regs);
421222900Snp	if (rc != 0)
422222900Snp		return (rc);
423222900Snp
424222900Snp	vers = get_card_vers(regs.version);
425222900Snp	revision = (regs.version >> 10) & 0x3f;
426222900Snp
427222900Snp	if (vers == 4) {
428222900Snp		if (revision == 0x3f)
429222900Snp			rc = dump_regs_t4vf(argc, argv, regs.data);
430222900Snp		else
431222900Snp			rc = dump_regs_t4(argc, argv, regs.data);
432248925Snp	} else if (vers == 5)
433248925Snp		rc = dump_regs_t5(argc, argv, regs.data);
434248925Snp	else {
435248925Snp		warnx("%s (type %d, rev %d) is not a known card.",
436222900Snp		    nexus, vers, revision);
437222900Snp		return (ENOTSUP);
438222900Snp	}
439222900Snp
440222900Snp	free(regs.data);
441222900Snp	return (rc);
442222900Snp}
443222900Snp
444222900Snpstatic void
445222900Snpdo_show_info_header(uint32_t mode)
446222900Snp{
447222900Snp	uint32_t i;
448222900Snp
449222900Snp	printf ("%4s %8s", "Idx", "Hits");
450222900Snp	for (i = T4_FILTER_FCoE; i <= T4_FILTER_IP_FRAGMENT; i <<= 1) {
451222900Snp		switch (mode & i) {
452222900Snp		case T4_FILTER_FCoE:
453222900Snp			printf (" FCoE");
454222900Snp			break;
455222900Snp
456222900Snp		case T4_FILTER_PORT:
457222900Snp			printf (" Port");
458222900Snp			break;
459222900Snp
460228561Snp		case T4_FILTER_VNIC:
461228561Snp			printf ("      vld:VNIC");
462222900Snp			break;
463222900Snp
464228561Snp		case T4_FILTER_VLAN:
465228561Snp			printf ("      vld:VLAN");
466222900Snp			break;
467222900Snp
468222900Snp		case T4_FILTER_IP_TOS:
469222900Snp			printf ("   TOS");
470222900Snp			break;
471222900Snp
472222900Snp		case T4_FILTER_IP_PROTO:
473222900Snp			printf ("  Prot");
474222900Snp			break;
475222900Snp
476222900Snp		case T4_FILTER_ETH_TYPE:
477222900Snp			printf ("   EthType");
478222900Snp			break;
479222900Snp
480222900Snp		case T4_FILTER_MAC_IDX:
481222900Snp			printf ("  MACIdx");
482222900Snp			break;
483222900Snp
484222900Snp		case T4_FILTER_MPS_HIT_TYPE:
485222900Snp			printf (" MPS");
486222900Snp			break;
487222900Snp
488222900Snp		case T4_FILTER_IP_FRAGMENT:
489222900Snp			printf (" Frag");
490222900Snp			break;
491222900Snp
492222900Snp		default:
493222900Snp			/* compressed filter field not enabled */
494222900Snp			break;
495222900Snp		}
496222900Snp	}
497222900Snp	printf(" %20s %20s %9s %9s %s\n",
498222900Snp	    "DIP", "SIP", "DPORT", "SPORT", "Action");
499222900Snp}
500222900Snp
501222900Snp/*
502222900Snp * Parse an argument sub-vector as a { <parameter name> <value>[:<mask>] }
503222900Snp * ordered tuple.  If the parameter name in the argument sub-vector does not
504222900Snp * match the passed in parameter name, then a zero is returned for the
505222900Snp * function and no parsing is performed.  If there is a match, then the value
506222900Snp * and optional mask are parsed and returned in the provided return value
507222900Snp * pointers.  If no optional mask is specified, then a default mask of all 1s
508222900Snp * will be returned.
509222900Snp *
510222900Snp * An error in parsing the value[:mask] will result in an error message and
511222900Snp * program termination.
512222900Snp */
513222900Snpstatic int
514222900Snpparse_val_mask(const char *param, const char *args[], uint32_t *val,
515222900Snp    uint32_t *mask)
516222900Snp{
517222900Snp	char *p;
518222900Snp
519222900Snp	if (strcmp(param, args[0]) != 0)
520222900Snp		return (EINVAL);
521222900Snp
522222900Snp	*val = strtoul(args[1], &p, 0);
523222900Snp	if (p > args[1]) {
524222900Snp		if (p[0] == 0) {
525222900Snp			*mask = ~0;
526222900Snp			return (0);
527222900Snp		}
528222900Snp
529222900Snp		if (p[0] == ':' && p[1] != 0) {
530222900Snp			*mask = strtoul(p+1, &p, 0);
531222900Snp			if (p[0] == 0)
532222900Snp				return (0);
533222900Snp		}
534222900Snp	}
535222900Snp
536222900Snp	warnx("parameter \"%s\" has bad \"value[:mask]\" %s",
537222900Snp	    args[0], args[1]);
538222900Snp
539222900Snp	return (EINVAL);
540222900Snp}
541222900Snp
542222900Snp/*
543222900Snp * Parse an argument sub-vector as a { <parameter name> <addr>[/<mask>] }
544222900Snp * ordered tuple.  If the parameter name in the argument sub-vector does not
545222900Snp * match the passed in parameter name, then a zero is returned for the
546222900Snp * function and no parsing is performed.  If there is a match, then the value
547222900Snp * and optional mask are parsed and returned in the provided return value
548222900Snp * pointers.  If no optional mask is specified, then a default mask of all 1s
549222900Snp * will be returned.
550222900Snp *
551222900Snp * The value return parameter "afp" is used to specify the expected address
552222900Snp * family -- IPv4 or IPv6 -- of the address[/mask] and return its actual
553222900Snp * format.  A passed in value of AF_UNSPEC indicates that either IPv4 or IPv6
554222900Snp * is acceptable; AF_INET means that only IPv4 addresses are acceptable; and
555222900Snp * AF_INET6 means that only IPv6 are acceptable.  AF_INET is returned for IPv4
556222900Snp * and AF_INET6 for IPv6 addresses, respectively.  IPv4 address/mask pairs are
557222900Snp * returned in the first four bytes of the address and mask return values with
558222900Snp * the address A.B.C.D returned with { A, B, C, D } returned in addresses { 0,
559222900Snp * 1, 2, 3}, respectively.
560222900Snp *
561222900Snp * An error in parsing the value[:mask] will result in an error message and
562222900Snp * program termination.
563222900Snp */
564222900Snpstatic int
565222900Snpparse_ipaddr(const char *param, const char *args[], int *afp, uint8_t addr[],
566222900Snp    uint8_t mask[])
567222900Snp{
568222900Snp	const char *colon, *afn;
569222900Snp	char *slash;
570222900Snp	uint8_t *m;
571222900Snp	int af, ret;
572222900Snp	unsigned int masksize;
573222900Snp
574222900Snp	/*
575222900Snp	 * Is this our parameter?
576222900Snp	 */
577222900Snp	if (strcmp(param, args[0]) != 0)
578222900Snp		return (EINVAL);
579222900Snp
580222900Snp	/*
581222900Snp	 * Fundamental IPv4 versus IPv6 selection.
582222900Snp	 */
583222900Snp	colon = strchr(args[1], ':');
584222900Snp	if (!colon) {
585222900Snp		afn = "IPv4";
586222900Snp		af = AF_INET;
587222900Snp		masksize = 32;
588222900Snp	} else {
589222900Snp		afn = "IPv6";
590222900Snp		af = AF_INET6;
591222900Snp		masksize = 128;
592222900Snp	}
593222900Snp	if (*afp == AF_UNSPEC)
594222900Snp		*afp = af;
595222900Snp	else if (*afp != af) {
596222900Snp		warnx("address %s is not of expected family %s",
597222900Snp		    args[1], *afp == AF_INET ? "IP" : "IPv6");
598222900Snp		return (EINVAL);
599222900Snp	}
600222900Snp
601222900Snp	/*
602222900Snp	 * Parse address (temporarily stripping off any "/mask"
603222900Snp	 * specification).
604222900Snp	 */
605222900Snp	slash = strchr(args[1], '/');
606222900Snp	if (slash)
607222900Snp		*slash = 0;
608222900Snp	ret = inet_pton(af, args[1], addr);
609222900Snp	if (slash)
610222900Snp		*slash = '/';
611222900Snp	if (ret <= 0) {
612222900Snp		warnx("Cannot parse %s %s address %s", param, afn, args[1]);
613222900Snp		return (EINVAL);
614222900Snp	}
615222900Snp
616222900Snp	/*
617222900Snp	 * Parse optional mask specification.
618222900Snp	 */
619222900Snp	if (slash) {
620222900Snp		char *p;
621222900Snp		unsigned int prefix = strtoul(slash + 1, &p, 10);
622222900Snp
623222900Snp		if (p == slash + 1) {
624222900Snp			warnx("missing address prefix for %s", param);
625222900Snp			return (EINVAL);
626222900Snp		}
627222900Snp		if (*p) {
628222900Snp			warnx("%s is not a valid address prefix", slash + 1);
629222900Snp			return (EINVAL);
630222900Snp		}
631222900Snp		if (prefix > masksize) {
632222900Snp			warnx("prefix %u is too long for an %s address",
633222900Snp			     prefix, afn);
634222900Snp			return (EINVAL);
635222900Snp		}
636222900Snp		memset(mask, 0, masksize / 8);
637222900Snp		masksize = prefix;
638222900Snp	}
639222900Snp
640222900Snp	/*
641222900Snp	 * Fill in mask.
642222900Snp	 */
643222900Snp	for (m = mask; masksize >= 8; m++, masksize -= 8)
644222900Snp		*m = ~0;
645222900Snp	if (masksize)
646222900Snp		*m = ~0 << (8 - masksize);
647222900Snp
648222900Snp	return (0);
649222900Snp}
650222900Snp
651222900Snp/*
652222900Snp * Parse an argument sub-vector as a { <parameter name> <value> } ordered
653222900Snp * tuple.  If the parameter name in the argument sub-vector does not match the
654222900Snp * passed in parameter name, then a zero is returned for the function and no
655222900Snp * parsing is performed.  If there is a match, then the value is parsed and
656222900Snp * returned in the provided return value pointer.
657222900Snp */
658222900Snpstatic int
659222900Snpparse_val(const char *param, const char *args[], uint32_t *val)
660222900Snp{
661222900Snp	char *p;
662222900Snp
663222900Snp	if (strcmp(param, args[0]) != 0)
664222900Snp		return (EINVAL);
665222900Snp
666222900Snp	*val = strtoul(args[1], &p, 0);
667222900Snp	if (p > args[1] && p[0] == 0)
668222900Snp		return (0);
669222900Snp
670222900Snp	warnx("parameter \"%s\" has bad \"value\" %s", args[0], args[1]);
671222900Snp	return (EINVAL);
672222900Snp}
673222900Snp
674222900Snpstatic void
675222900Snpfilters_show_ipaddr(int type, uint8_t *addr, uint8_t *addrm)
676222900Snp{
677222900Snp	int noctets, octet;
678222900Snp
679222900Snp	printf(" ");
680222900Snp	if (type == 0) {
681222900Snp		noctets = 4;
682222900Snp		printf("%3s", " ");
683222900Snp	} else
684222900Snp	noctets = 16;
685222900Snp
686222900Snp	for (octet = 0; octet < noctets; octet++)
687222900Snp		printf("%02x", addr[octet]);
688222900Snp	printf("/");
689222900Snp	for (octet = 0; octet < noctets; octet++)
690222900Snp		printf("%02x", addrm[octet]);
691222900Snp}
692222900Snp
693222900Snpstatic void
694222900Snpdo_show_one_filter_info(struct t4_filter *t, uint32_t mode)
695222900Snp{
696222900Snp	uint32_t i;
697222900Snp
698222900Snp	printf("%4d", t->idx);
699222900Snp	if (t->hits == UINT64_MAX)
700222900Snp		printf(" %8s", "-");
701222900Snp	else
702222900Snp		printf(" %8ju", t->hits);
703222900Snp
704222900Snp	/*
705222900Snp	 * Compressed header portion of filter.
706222900Snp	 */
707222900Snp	for (i = T4_FILTER_FCoE; i <= T4_FILTER_IP_FRAGMENT; i <<= 1) {
708222900Snp		switch (mode & i) {
709222900Snp		case T4_FILTER_FCoE:
710222900Snp			printf("  %1d/%1d", t->fs.val.fcoe, t->fs.mask.fcoe);
711222900Snp			break;
712222900Snp
713222900Snp		case T4_FILTER_PORT:
714222900Snp			printf("  %1d/%1d", t->fs.val.iport, t->fs.mask.iport);
715222900Snp			break;
716222900Snp
717228561Snp		case T4_FILTER_VNIC:
718222900Snp			printf(" %1d:%1x:%02x/%1d:%1x:%02x",
719228561Snp			    t->fs.val.vnic_vld, (t->fs.val.vnic >> 7) & 0x7,
720228561Snp			    t->fs.val.vnic & 0x7f, t->fs.mask.vnic_vld,
721228561Snp			    (t->fs.mask.vnic >> 7) & 0x7,
722228561Snp			    t->fs.mask.vnic & 0x7f);
723222900Snp			break;
724222900Snp
725228561Snp		case T4_FILTER_VLAN:
726222900Snp			printf(" %1d:%04x/%1d:%04x",
727228561Snp			    t->fs.val.vlan_vld, t->fs.val.vlan,
728228561Snp			    t->fs.mask.vlan_vld, t->fs.mask.vlan);
729222900Snp			break;
730222900Snp
731222900Snp		case T4_FILTER_IP_TOS:
732222900Snp			printf(" %02x/%02x", t->fs.val.tos, t->fs.mask.tos);
733222900Snp			break;
734222900Snp
735222900Snp		case T4_FILTER_IP_PROTO:
736222900Snp			printf(" %02x/%02x", t->fs.val.proto, t->fs.mask.proto);
737222900Snp			break;
738222900Snp
739222900Snp		case T4_FILTER_ETH_TYPE:
740222900Snp			printf(" %04x/%04x", t->fs.val.ethtype,
741222900Snp			    t->fs.mask.ethtype);
742222900Snp			break;
743222900Snp
744222900Snp		case T4_FILTER_MAC_IDX:
745222900Snp			printf(" %03x/%03x", t->fs.val.macidx,
746222900Snp			    t->fs.mask.macidx);
747222900Snp			break;
748222900Snp
749222900Snp		case T4_FILTER_MPS_HIT_TYPE:
750222900Snp			printf(" %1x/%1x", t->fs.val.matchtype,
751222900Snp			    t->fs.mask.matchtype);
752222900Snp			break;
753222900Snp
754222900Snp		case T4_FILTER_IP_FRAGMENT:
755222900Snp			printf("  %1d/%1d", t->fs.val.frag, t->fs.mask.frag);
756222900Snp			break;
757222900Snp
758222900Snp		default:
759222900Snp			/* compressed filter field not enabled */
760222900Snp			break;
761222900Snp		}
762222900Snp	}
763222900Snp
764222900Snp	/*
765222900Snp	 * Fixed portion of filter.
766222900Snp	 */
767222900Snp	filters_show_ipaddr(t->fs.type, t->fs.val.dip, t->fs.mask.dip);
768222900Snp	filters_show_ipaddr(t->fs.type, t->fs.val.sip, t->fs.mask.sip);
769222900Snp	printf(" %04x/%04x %04x/%04x",
770222900Snp		 t->fs.val.dport, t->fs.mask.dport,
771222900Snp		 t->fs.val.sport, t->fs.mask.sport);
772222900Snp
773222900Snp	/*
774222900Snp	 * Variable length filter action.
775222900Snp	 */
776222900Snp	if (t->fs.action == FILTER_DROP)
777222900Snp		printf(" Drop");
778222900Snp	else if (t->fs.action == FILTER_SWITCH) {
779222900Snp		printf(" Switch: port=%d", t->fs.eport);
780222900Snp	if (t->fs.newdmac)
781222900Snp		printf(
782222900Snp			", dmac=%02x:%02x:%02x:%02x:%02x:%02x "
783222900Snp			", l2tidx=%d",
784222900Snp			t->fs.dmac[0], t->fs.dmac[1],
785222900Snp			t->fs.dmac[2], t->fs.dmac[3],
786222900Snp			t->fs.dmac[4], t->fs.dmac[5],
787222900Snp			t->l2tidx);
788222900Snp	if (t->fs.newsmac)
789222900Snp		printf(
790222900Snp			", smac=%02x:%02x:%02x:%02x:%02x:%02x "
791222900Snp			", smtidx=%d",
792222900Snp			t->fs.smac[0], t->fs.smac[1],
793222900Snp			t->fs.smac[2], t->fs.smac[3],
794222900Snp			t->fs.smac[4], t->fs.smac[5],
795222900Snp			t->smtidx);
796222900Snp	if (t->fs.newvlan == VLAN_REMOVE)
797222900Snp		printf(", vlan=none");
798222900Snp	else if (t->fs.newvlan == VLAN_INSERT)
799222900Snp		printf(", vlan=insert(%x)", t->fs.vlan);
800222900Snp	else if (t->fs.newvlan == VLAN_REWRITE)
801222900Snp		printf(", vlan=rewrite(%x)", t->fs.vlan);
802222900Snp	} else {
803222900Snp		printf(" Pass: Q=");
804222900Snp		if (t->fs.dirsteer == 0) {
805222900Snp			printf("RSS");
806222900Snp			if (t->fs.maskhash)
807222900Snp				printf("(TCB=hash)");
808222900Snp		} else {
809222900Snp			printf("%d", t->fs.iq);
810222900Snp			if (t->fs.dirsteerhash == 0)
811222900Snp				printf("(QID)");
812222900Snp			else
813222900Snp				printf("(hash)");
814222900Snp		}
815222900Snp	}
816222900Snp	if (t->fs.prio)
817222900Snp		printf(" Prio");
818222900Snp	if (t->fs.rpttid)
819222900Snp		printf(" RptTID");
820222900Snp	printf("\n");
821222900Snp}
822222900Snp
823222900Snpstatic int
824222900Snpshow_filters(void)
825222900Snp{
826222900Snp	uint32_t mode = 0, header = 0;
827222900Snp	struct t4_filter t;
828222900Snp	int rc;
829222900Snp
830222900Snp	/* Get the global filter mode first */
831222900Snp	rc = doit(CHELSIO_T4_GET_FILTER_MODE, &mode);
832222900Snp	if (rc != 0)
833222900Snp		return (rc);
834222900Snp
835222900Snp	t.idx = 0;
836222900Snp	for (t.idx = 0; ; t.idx++) {
837222900Snp		rc = doit(CHELSIO_T4_GET_FILTER, &t);
838222900Snp		if (rc != 0 || t.idx == 0xffffffff)
839222900Snp			break;
840222900Snp
841222900Snp		if (!header) {
842222900Snp			do_show_info_header(mode);
843222900Snp			header = 1;
844222900Snp		}
845222900Snp		do_show_one_filter_info(&t, mode);
846222900Snp	};
847222900Snp
848222900Snp	return (rc);
849222900Snp}
850222900Snp
851222900Snpstatic int
852222900Snpget_filter_mode(void)
853222900Snp{
854222900Snp	uint32_t mode = 0;
855222900Snp	int rc;
856222900Snp
857222900Snp	rc = doit(CHELSIO_T4_GET_FILTER_MODE, &mode);
858222900Snp	if (rc != 0)
859222900Snp		return (rc);
860222900Snp
861222900Snp	if (mode & T4_FILTER_IPv4)
862222900Snp		printf("ipv4 ");
863222900Snp
864222900Snp	if (mode & T4_FILTER_IPv6)
865222900Snp		printf("ipv6 ");
866222900Snp
867222900Snp	if (mode & T4_FILTER_IP_SADDR)
868222900Snp		printf("sip ");
869222900Snp
870222900Snp	if (mode & T4_FILTER_IP_DADDR)
871222900Snp		printf("dip ");
872222900Snp
873222900Snp	if (mode & T4_FILTER_IP_SPORT)
874222900Snp		printf("sport ");
875222900Snp
876222900Snp	if (mode & T4_FILTER_IP_DPORT)
877222900Snp		printf("dport ");
878222900Snp
879249368Snp	if (mode & T4_FILTER_IP_FRAGMENT)
880249368Snp		printf("frag ");
881249368Snp
882222900Snp	if (mode & T4_FILTER_MPS_HIT_TYPE)
883222900Snp		printf("matchtype ");
884222900Snp
885222900Snp	if (mode & T4_FILTER_MAC_IDX)
886222900Snp		printf("macidx ");
887222900Snp
888222900Snp	if (mode & T4_FILTER_ETH_TYPE)
889222900Snp		printf("ethtype ");
890222900Snp
891222900Snp	if (mode & T4_FILTER_IP_PROTO)
892222900Snp		printf("proto ");
893222900Snp
894222900Snp	if (mode & T4_FILTER_IP_TOS)
895222900Snp		printf("tos ");
896222900Snp
897228561Snp	if (mode & T4_FILTER_VLAN)
898228561Snp		printf("vlan ");
899222900Snp
900228561Snp	if (mode & T4_FILTER_VNIC)
901249368Snp		printf("vnic/ovlan ");
902222900Snp
903222900Snp	if (mode & T4_FILTER_PORT)
904222900Snp		printf("iport ");
905222900Snp
906222900Snp	if (mode & T4_FILTER_FCoE)
907222900Snp		printf("fcoe ");
908222900Snp
909222900Snp	printf("\n");
910222900Snp
911222900Snp	return (0);
912222900Snp}
913222900Snp
914222900Snpstatic int
915222900Snpset_filter_mode(int argc, const char *argv[])
916222900Snp{
917222900Snp	uint32_t mode = 0;
918222900Snp
919222900Snp	for (; argc; argc--, argv++) {
920249368Snp		if (!strcmp(argv[0], "frag"))
921249368Snp			mode |= T4_FILTER_IP_FRAGMENT;
922249368Snp
923222900Snp		if (!strcmp(argv[0], "matchtype"))
924222900Snp			mode |= T4_FILTER_MPS_HIT_TYPE;
925222900Snp
926222900Snp		if (!strcmp(argv[0], "macidx"))
927222900Snp			mode |= T4_FILTER_MAC_IDX;
928222900Snp
929222900Snp		if (!strcmp(argv[0], "ethtype"))
930222900Snp			mode |= T4_FILTER_ETH_TYPE;
931222900Snp
932222900Snp		if (!strcmp(argv[0], "proto"))
933222900Snp			mode |= T4_FILTER_IP_PROTO;
934222900Snp
935222900Snp		if (!strcmp(argv[0], "tos"))
936222900Snp			mode |= T4_FILTER_IP_TOS;
937222900Snp
938228561Snp		if (!strcmp(argv[0], "vlan"))
939228561Snp			mode |= T4_FILTER_VLAN;
940222900Snp
941228561Snp		if (!strcmp(argv[0], "ovlan") ||
942228561Snp		    !strcmp(argv[0], "vnic"))
943228561Snp			mode |= T4_FILTER_VNIC;
944222900Snp
945222900Snp		if (!strcmp(argv[0], "iport"))
946222900Snp			mode |= T4_FILTER_PORT;
947222900Snp
948222900Snp		if (!strcmp(argv[0], "fcoe"))
949222900Snp			mode |= T4_FILTER_FCoE;
950222900Snp	}
951222900Snp
952222900Snp	return doit(CHELSIO_T4_SET_FILTER_MODE, &mode);
953222900Snp}
954222900Snp
955222900Snpstatic int
956222900Snpdel_filter(uint32_t idx)
957222900Snp{
958222900Snp	struct t4_filter t;
959222900Snp
960222900Snp	t.idx = idx;
961222900Snp
962222900Snp	return doit(CHELSIO_T4_DEL_FILTER, &t);
963222900Snp}
964222900Snp
965222900Snpstatic int
966222900Snpset_filter(uint32_t idx, int argc, const char *argv[])
967222900Snp{
968222900Snp	int af = AF_UNSPEC, start_arg = 0;
969222900Snp	struct t4_filter t;
970222900Snp
971222900Snp	if (argc < 2) {
972222900Snp		warnc(EINVAL, "%s", __func__);
973222900Snp		return (EINVAL);
974222900Snp	};
975222900Snp	bzero(&t, sizeof (t));
976222900Snp	t.idx = idx;
977252470Snp	t.fs.hitcnts = 1;
978222900Snp
979222900Snp	for (start_arg = 0; start_arg + 2 <= argc; start_arg += 2) {
980222900Snp		const char **args = &argv[start_arg];
981222900Snp		uint32_t val, mask;
982222900Snp
983222900Snp		if (!strcmp(argv[start_arg], "type")) {
984222900Snp			int newaf;
985222900Snp			if (!strcasecmp(argv[start_arg + 1], "ipv4"))
986222900Snp				newaf = AF_INET;
987222900Snp			else if (!strcasecmp(argv[start_arg + 1], "ipv6"))
988222900Snp				newaf = AF_INET6;
989222900Snp			else {
990222900Snp				warnx("invalid type \"%s\"; "
991222900Snp				    "must be one of \"ipv4\" or \"ipv6\"",
992222900Snp				    argv[start_arg + 1]);
993222900Snp				return (EINVAL);
994222900Snp			}
995222900Snp
996222900Snp			if (af != AF_UNSPEC && af != newaf) {
997222900Snp				warnx("conflicting IPv4/IPv6 specifications.");
998222900Snp				return (EINVAL);
999222900Snp			}
1000222900Snp			af = newaf;
1001222900Snp		} else if (!parse_val_mask("fcoe", args, &val, &mask)) {
1002222900Snp			t.fs.val.fcoe = val;
1003222900Snp			t.fs.mask.fcoe = mask;
1004222900Snp		} else if (!parse_val_mask("iport", args, &val, &mask)) {
1005222900Snp			t.fs.val.iport = val;
1006222900Snp			t.fs.mask.iport = mask;
1007222900Snp		} else if (!parse_val_mask("ovlan", args, &val, &mask)) {
1008228561Snp			t.fs.val.vnic = val;
1009228561Snp			t.fs.mask.vnic = mask;
1010228561Snp			t.fs.val.vnic_vld = 1;
1011228561Snp			t.fs.mask.vnic_vld = 1;
1012228561Snp		} else if (!parse_val_mask("vnic", args, &val, &mask)) {
1013228561Snp			t.fs.val.vnic = val;
1014228561Snp			t.fs.mask.vnic = mask;
1015228561Snp			t.fs.val.vnic_vld = 1;
1016228561Snp			t.fs.mask.vnic_vld = 1;
1017245520Snp		} else if (!parse_val_mask("ivlan", args, &val, &mask)) {
1018228561Snp			t.fs.val.vlan = val;
1019228561Snp			t.fs.mask.vlan = mask;
1020228561Snp			t.fs.val.vlan_vld = 1;
1021228561Snp			t.fs.mask.vlan_vld = 1;
1022222900Snp		} else if (!parse_val_mask("tos", args, &val, &mask)) {
1023222900Snp			t.fs.val.tos = val;
1024222900Snp			t.fs.mask.tos = mask;
1025222900Snp		} else if (!parse_val_mask("proto", args, &val, &mask)) {
1026222900Snp			t.fs.val.proto = val;
1027222900Snp			t.fs.mask.proto = mask;
1028222900Snp		} else if (!parse_val_mask("ethtype", args, &val, &mask)) {
1029222900Snp			t.fs.val.ethtype = val;
1030222900Snp			t.fs.mask.ethtype = mask;
1031222900Snp		} else if (!parse_val_mask("macidx", args, &val, &mask)) {
1032222900Snp			t.fs.val.macidx = val;
1033222900Snp			t.fs.mask.macidx = mask;
1034222900Snp		} else if (!parse_val_mask("matchtype", args, &val, &mask)) {
1035222900Snp			t.fs.val.matchtype = val;
1036222900Snp			t.fs.mask.matchtype = mask;
1037222900Snp		} else if (!parse_val_mask("frag", args, &val, &mask)) {
1038222900Snp			t.fs.val.frag = val;
1039222900Snp			t.fs.mask.frag = mask;
1040222900Snp		} else if (!parse_val_mask("dport", args, &val, &mask)) {
1041222900Snp			t.fs.val.dport = val;
1042222900Snp			t.fs.mask.dport = mask;
1043222900Snp		} else if (!parse_val_mask("sport", args, &val, &mask)) {
1044222900Snp			t.fs.val.sport = val;
1045222900Snp			t.fs.mask.sport = mask;
1046222900Snp		} else if (!parse_ipaddr("dip", args, &af, t.fs.val.dip,
1047222900Snp		    t.fs.mask.dip)) {
1048222900Snp			/* nada */;
1049222900Snp		} else if (!parse_ipaddr("sip", args, &af, t.fs.val.sip,
1050222900Snp		    t.fs.mask.sip)) {
1051222900Snp			/* nada */;
1052222900Snp		} else if (!strcmp(argv[start_arg], "action")) {
1053222900Snp			if (!strcmp(argv[start_arg + 1], "pass"))
1054222900Snp				t.fs.action = FILTER_PASS;
1055222900Snp			else if (!strcmp(argv[start_arg + 1], "drop"))
1056222900Snp				t.fs.action = FILTER_DROP;
1057222900Snp			else if (!strcmp(argv[start_arg + 1], "switch"))
1058222900Snp				t.fs.action = FILTER_SWITCH;
1059222900Snp			else {
1060222900Snp				warnx("invalid action \"%s\"; must be one of"
1061222900Snp				     " \"pass\", \"drop\" or \"switch\"",
1062222900Snp				     argv[start_arg + 1]);
1063222900Snp				return (EINVAL);
1064222900Snp			}
1065222900Snp		} else if (!parse_val("hitcnts", args, &val)) {
1066222900Snp			t.fs.hitcnts = val;
1067222900Snp		} else if (!parse_val("prio", args, &val)) {
1068222900Snp			t.fs.prio = val;
1069222900Snp		} else if (!parse_val("rpttid", args, &val)) {
1070222900Snp			t.fs.rpttid = 1;
1071222900Snp		} else if (!parse_val("queue", args, &val)) {
1072222900Snp			t.fs.dirsteer = 1;
1073222900Snp			t.fs.iq = val;
1074222900Snp		} else if (!parse_val("tcbhash", args, &val)) {
1075222900Snp			t.fs.maskhash = 1;
1076222900Snp			t.fs.dirsteerhash = 1;
1077222900Snp		} else if (!parse_val("eport", args, &val)) {
1078222900Snp			t.fs.eport = val;
1079222900Snp		} else if (!strcmp(argv[start_arg], "dmac")) {
1080222900Snp			struct ether_addr *daddr;
1081222900Snp
1082222900Snp			daddr = ether_aton(argv[start_arg + 1]);
1083222900Snp			if (daddr == NULL) {
1084222900Snp				warnx("invalid dmac address \"%s\"",
1085222900Snp				    argv[start_arg + 1]);
1086222900Snp				return (EINVAL);
1087222900Snp			}
1088222900Snp			memcpy(t.fs.dmac, daddr, ETHER_ADDR_LEN);
1089222900Snp			t.fs.newdmac = 1;
1090222900Snp		} else if (!strcmp(argv[start_arg], "smac")) {
1091222900Snp			struct ether_addr *saddr;
1092222900Snp
1093222900Snp			saddr = ether_aton(argv[start_arg + 1]);
1094222900Snp			if (saddr == NULL) {
1095222900Snp				warnx("invalid smac address \"%s\"",
1096222900Snp				    argv[start_arg + 1]);
1097222900Snp				return (EINVAL);
1098222900Snp			}
1099222900Snp			memcpy(t.fs.smac, saddr, ETHER_ADDR_LEN);
1100222900Snp			t.fs.newsmac = 1;
1101222900Snp		} else if (!strcmp(argv[start_arg], "vlan")) {
1102222900Snp			char *p;
1103222900Snp			if (!strcmp(argv[start_arg + 1], "none")) {
1104222900Snp				t.fs.newvlan = VLAN_REMOVE;
1105222900Snp			} else if (argv[start_arg + 1][0] == '=') {
1106222900Snp				t.fs.newvlan = VLAN_REWRITE;
1107222900Snp			} else if (argv[start_arg + 1][0] == '+') {
1108222900Snp				t.fs.newvlan = VLAN_INSERT;
1109245520Snp			} else if (isdigit(argv[start_arg + 1][0]) &&
1110245520Snp			    !parse_val_mask("vlan", args, &val, &mask)) {
1111245520Snp				t.fs.val.vlan = val;
1112245520Snp				t.fs.mask.vlan = mask;
1113245520Snp				t.fs.val.vlan_vld = 1;
1114245520Snp				t.fs.mask.vlan_vld = 1;
1115222900Snp			} else {
1116222900Snp				warnx("unknown vlan parameter \"%s\"; must"
1117245520Snp				     " be one of \"none\", \"=<vlan>\", "
1118245520Snp				     " \"+<vlan>\", or \"<vlan>\"",
1119245520Snp				     argv[start_arg + 1]);
1120222900Snp				return (EINVAL);
1121222900Snp			}
1122222900Snp			if (t.fs.newvlan == VLAN_REWRITE ||
1123222900Snp			    t.fs.newvlan == VLAN_INSERT) {
1124222900Snp				t.fs.vlan = strtoul(argv[start_arg + 1] + 1,
1125222900Snp				    &p, 0);
1126222900Snp				if (p == argv[start_arg + 1] + 1 || p[0] != 0) {
1127222900Snp					warnx("invalid vlan \"%s\"",
1128222900Snp					     argv[start_arg + 1]);
1129222900Snp					return (EINVAL);
1130222900Snp				}
1131222900Snp			}
1132222900Snp		} else {
1133222900Snp			warnx("invalid parameter \"%s\"", argv[start_arg]);
1134222900Snp			return (EINVAL);
1135222900Snp		}
1136222900Snp	}
1137222900Snp	if (start_arg != argc) {
1138222900Snp		warnx("no value for \"%s\"", argv[start_arg]);
1139222900Snp		return (EINVAL);
1140222900Snp	}
1141222900Snp
1142222900Snp	/*
1143222900Snp	 * Check basic sanity of option combinations.
1144222900Snp	 */
1145222900Snp	if (t.fs.action != FILTER_SWITCH &&
1146222900Snp	    (t.fs.eport || t.fs.newdmac || t.fs.newsmac || t.fs.newvlan)) {
1147222900Snp		warnx("prio, port dmac, smac and vlan only make sense with"
1148222900Snp		     " \"action switch\"");
1149222900Snp		return (EINVAL);
1150222900Snp	}
1151222900Snp	if (t.fs.action != FILTER_PASS &&
1152222900Snp	    (t.fs.rpttid || t.fs.dirsteer || t.fs.maskhash)) {
1153222900Snp		warnx("rpttid, queue and tcbhash don't make sense with"
1154222900Snp		     " action \"drop\" or \"switch\"");
1155222900Snp		return (EINVAL);
1156222900Snp	}
1157222900Snp
1158222900Snp	t.fs.type = (af == AF_INET6 ? 1 : 0); /* default IPv4 */
1159222900Snp	return doit(CHELSIO_T4_SET_FILTER, &t);
1160222900Snp}
1161222900Snp
1162222900Snpstatic int
1163222900Snpfilter_cmd(int argc, const char *argv[])
1164222900Snp{
1165222900Snp	long long val;
1166222900Snp	uint32_t idx;
1167222900Snp	char *s;
1168222900Snp
1169222900Snp	if (argc == 0) {
1170222900Snp		warnx("filter: no arguments.");
1171222900Snp		return (EINVAL);
1172222900Snp	};
1173222900Snp
1174222900Snp	/* list */
1175222900Snp	if (strcmp(argv[0], "list") == 0) {
1176222900Snp		if (argc != 1)
1177222900Snp			warnx("trailing arguments after \"list\" ignored.");
1178222900Snp
1179222900Snp		return show_filters();
1180222900Snp	}
1181222900Snp
1182222900Snp	/* mode */
1183222900Snp	if (argc == 1 && strcmp(argv[0], "mode") == 0)
1184222900Snp		return get_filter_mode();
1185222900Snp
1186222900Snp	/* mode <mode> */
1187222900Snp	if (strcmp(argv[0], "mode") == 0)
1188222900Snp		return set_filter_mode(argc - 1, argv + 1);
1189222900Snp
1190222900Snp	/* <idx> ... */
1191222900Snp	s = str_to_number(argv[0], NULL, &val);
1192222900Snp	if (*s || val > 0xffffffffU) {
1193222900Snp		warnx("\"%s\" is neither an index nor a filter subcommand.",
1194222900Snp		    argv[0]);
1195222900Snp		return (EINVAL);
1196222900Snp	}
1197222900Snp	idx = (uint32_t) val;
1198222900Snp
1199222900Snp	/* <idx> delete|clear */
1200222900Snp	if (argc == 2 &&
1201222900Snp	    (strcmp(argv[1], "delete") == 0 || strcmp(argv[1], "clear") == 0)) {
1202222900Snp		return del_filter(idx);
1203222900Snp	}
1204222900Snp
1205222900Snp	/* <idx> [<param> <val>] ... */
1206222900Snp	return set_filter(idx, argc - 1, argv + 1);
1207222900Snp}
1208222900Snp
1209222974Snp/*
1210222974Snp * Shows the fields of a multi-word structure.  The structure is considered to
1211222974Snp * consist of @nwords 32-bit words (i.e, it's an (@nwords * 32)-bit structure)
1212222974Snp * whose fields are described by @fd.  The 32-bit words are given in @words
1213222974Snp * starting with the least significant 32-bit word.
1214222974Snp */
1215222974Snpstatic void
1216222974Snpshow_struct(const uint32_t *words, int nwords, const struct field_desc *fd)
1217222974Snp{
1218222974Snp	unsigned int w = 0;
1219222974Snp	const struct field_desc *p;
1220222974Snp
1221222974Snp	for (p = fd; p->name; p++)
1222222974Snp		w = max(w, strlen(p->name));
1223222974Snp
1224222974Snp	while (fd->name) {
1225222974Snp		unsigned long long data;
1226222974Snp		int first_word = fd->start / 32;
1227222974Snp		int shift = fd->start % 32;
1228222974Snp		int width = fd->end - fd->start + 1;
1229222974Snp		unsigned long long mask = (1ULL << width) - 1;
1230222974Snp
1231222974Snp		data = (words[first_word] >> shift) |
1232222974Snp		       ((uint64_t)words[first_word + 1] << (32 - shift));
1233222974Snp		if (shift)
1234222974Snp		       data |= ((uint64_t)words[first_word + 2] << (64 - shift));
1235222974Snp		data &= mask;
1236222974Snp		if (fd->islog2)
1237222974Snp			data = 1 << data;
1238222974Snp		printf("%-*s ", w, fd->name);
1239222974Snp		printf(fd->hex ? "%#llx\n" : "%llu\n", data << fd->shift);
1240222974Snp		fd++;
1241222974Snp	}
1242222974Snp}
1243222974Snp
1244222974Snp#define FIELD(name, start, end) { name, start, end, 0, 0, 0 }
1245222974Snp#define FIELD1(name, start) FIELD(name, start, start)
1246222974Snp
1247222974Snpstatic void
1248284984Snpshow_t5_ctxt(const struct t4_sge_context *p)
1249222974Snp{
1250284984Snp	static struct field_desc egress_t5[] = {
1251284984Snp		FIELD("DCA_ST:", 181, 191),
1252222974Snp		FIELD1("StatusPgNS:", 180),
1253222974Snp		FIELD1("StatusPgRO:", 179),
1254222974Snp		FIELD1("FetchNS:", 178),
1255222974Snp		FIELD1("FetchRO:", 177),
1256222974Snp		FIELD1("Valid:", 176),
1257222974Snp		FIELD("PCIeDataChannel:", 174, 175),
1258284984Snp		FIELD1("StatusPgTPHintEn:", 173),
1259284984Snp		FIELD("StatusPgTPHint:", 171, 172),
1260284984Snp		FIELD1("FetchTPHintEn:", 170),
1261284984Snp		FIELD("FetchTPHint:", 168, 169),
1262284984Snp		FIELD1("FCThreshOverride:", 167),
1263284984Snp		{ "WRLength:", 162, 166, 9, 0, 1 },
1264284984Snp		FIELD1("WRLengthKnown:", 161),
1265284984Snp		FIELD1("ReschedulePending:", 160),
1266284984Snp		FIELD1("OnChipQueue:", 159),
1267284984Snp		FIELD1("FetchSizeMode:", 158),
1268284984Snp		{ "FetchBurstMin:", 156, 157, 4, 0, 1 },
1269284984Snp		FIELD1("FLMPacking:", 155),
1270284984Snp		FIELD("FetchBurstMax:", 153, 154),
1271284984Snp		FIELD("uPToken:", 133, 152),
1272284984Snp		FIELD1("uPTokenEn:", 132),
1273284984Snp		FIELD1("UserModeIO:", 131),
1274284984Snp		FIELD("uPFLCredits:", 123, 130),
1275284984Snp		FIELD1("uPFLCreditEn:", 122),
1276284984Snp		FIELD("FID:", 111, 121),
1277284984Snp		FIELD("HostFCMode:", 109, 110),
1278284984Snp		FIELD1("HostFCOwner:", 108),
1279284984Snp		{ "CIDXFlushThresh:", 105, 107, 0, 0, 1 },
1280284984Snp		FIELD("CIDX:", 89, 104),
1281284984Snp		FIELD("PIDX:", 73, 88),
1282284984Snp		{ "BaseAddress:", 18, 72, 9, 1 },
1283284984Snp		FIELD("QueueSize:", 2, 17),
1284284984Snp		FIELD1("QueueType:", 1),
1285284984Snp		FIELD1("CachePriority:", 0),
1286284984Snp		{ NULL }
1287284984Snp	};
1288284984Snp	static struct field_desc fl_t5[] = {
1289284984Snp		FIELD("DCA_ST:", 181, 191),
1290284984Snp		FIELD1("StatusPgNS:", 180),
1291284984Snp		FIELD1("StatusPgRO:", 179),
1292284984Snp		FIELD1("FetchNS:", 178),
1293284984Snp		FIELD1("FetchRO:", 177),
1294284984Snp		FIELD1("Valid:", 176),
1295284984Snp		FIELD("PCIeDataChannel:", 174, 175),
1296284984Snp		FIELD1("StatusPgTPHintEn:", 173),
1297284984Snp		FIELD("StatusPgTPHint:", 171, 172),
1298284984Snp		FIELD1("FetchTPHintEn:", 170),
1299284984Snp		FIELD("FetchTPHint:", 168, 169),
1300284984Snp		FIELD1("FCThreshOverride:", 167),
1301284984Snp		FIELD1("ReschedulePending:", 160),
1302284984Snp		FIELD1("OnChipQueue:", 159),
1303284984Snp		FIELD1("FetchSizeMode:", 158),
1304284984Snp		{ "FetchBurstMin:", 156, 157, 4, 0, 1 },
1305284984Snp		FIELD1("FLMPacking:", 155),
1306284984Snp		FIELD("FetchBurstMax:", 153, 154),
1307284984Snp		FIELD1("FLMcongMode:", 152),
1308284984Snp		FIELD("MaxuPFLCredits:", 144, 151),
1309284984Snp		FIELD("FLMcontextID:", 133, 143),
1310284984Snp		FIELD1("uPTokenEn:", 132),
1311284984Snp		FIELD1("UserModeIO:", 131),
1312284984Snp		FIELD("uPFLCredits:", 123, 130),
1313284984Snp		FIELD1("uPFLCreditEn:", 122),
1314284984Snp		FIELD("FID:", 111, 121),
1315284984Snp		FIELD("HostFCMode:", 109, 110),
1316284984Snp		FIELD1("HostFCOwner:", 108),
1317284984Snp		{ "CIDXFlushThresh:", 105, 107, 0, 0, 1 },
1318284984Snp		FIELD("CIDX:", 89, 104),
1319284984Snp		FIELD("PIDX:", 73, 88),
1320284984Snp		{ "BaseAddress:", 18, 72, 9, 1 },
1321284984Snp		FIELD("QueueSize:", 2, 17),
1322284984Snp		FIELD1("QueueType:", 1),
1323284984Snp		FIELD1("CachePriority:", 0),
1324284984Snp		{ NULL }
1325284984Snp	};
1326284984Snp	static struct field_desc ingress_t5[] = {
1327284984Snp		FIELD("DCA_ST:", 143, 153),
1328284984Snp		FIELD1("ISCSICoalescing:", 142),
1329284984Snp		FIELD1("Queue_Valid:", 141),
1330284984Snp		FIELD1("TimerPending:", 140),
1331284984Snp		FIELD1("DropRSS:", 139),
1332284984Snp		FIELD("PCIeChannel:", 137, 138),
1333284984Snp		FIELD1("SEInterruptArmed:", 136),
1334284984Snp		FIELD1("CongestionMgtEnable:", 135),
1335284984Snp		FIELD1("NoSnoop:", 134),
1336284984Snp		FIELD1("RelaxedOrdering:", 133),
1337284984Snp		FIELD1("GTSmode:", 132),
1338284984Snp		FIELD1("TPHintEn:", 131),
1339284984Snp		FIELD("TPHint:", 129, 130),
1340284984Snp		FIELD1("UpdateScheduling:", 128),
1341284984Snp		FIELD("UpdateDelivery:", 126, 127),
1342284984Snp		FIELD1("InterruptSent:", 125),
1343284984Snp		FIELD("InterruptIDX:", 114, 124),
1344284984Snp		FIELD1("InterruptDestination:", 113),
1345284984Snp		FIELD1("InterruptArmed:", 112),
1346284984Snp		FIELD("RxIntCounter:", 106, 111),
1347284984Snp		FIELD("RxIntCounterThreshold:", 104, 105),
1348284984Snp		FIELD1("Generation:", 103),
1349284984Snp		{ "BaseAddress:", 48, 102, 9, 1 },
1350284984Snp		FIELD("PIDX:", 32, 47),
1351284984Snp		FIELD("CIDX:", 16, 31),
1352284984Snp		{ "QueueSize:", 4, 15, 4, 0 },
1353284984Snp		{ "QueueEntrySize:", 2, 3, 4, 0, 1 },
1354284984Snp		FIELD1("QueueEntryOverride:", 1),
1355284984Snp		FIELD1("CachePriority:", 0),
1356284984Snp		{ NULL }
1357284984Snp	};
1358284984Snp	static struct field_desc flm_t5[] = {
1359284984Snp		FIELD1("Valid:", 89),
1360284984Snp		FIELD("SplitLenMode:", 87, 88),
1361284984Snp		FIELD1("TPHintEn:", 86),
1362284984Snp		FIELD("TPHint:", 84, 85),
1363284984Snp		FIELD1("NoSnoop:", 83),
1364284984Snp		FIELD1("RelaxedOrdering:", 82),
1365284984Snp		FIELD("DCA_ST:", 71, 81),
1366284984Snp		FIELD("EQid:", 54, 70),
1367284984Snp		FIELD("SplitEn:", 52, 53),
1368284984Snp		FIELD1("PadEn:", 51),
1369284984Snp		FIELD1("PackEn:", 50),
1370284984Snp		FIELD1("Cache_Lock :", 49),
1371284984Snp		FIELD1("CongDrop:", 48),
1372284984Snp		FIELD("PackOffset:", 16, 47),
1373284984Snp		FIELD("CIDX:", 8, 15),
1374284984Snp		FIELD("PIDX:", 0, 7),
1375284984Snp		{ NULL }
1376284984Snp	};
1377284984Snp	static struct field_desc conm_t5[] = {
1378284984Snp		FIELD1("CngMPSEnable:", 21),
1379284984Snp		FIELD("CngTPMode:", 19, 20),
1380284984Snp		FIELD1("CngDBPHdr:", 18),
1381284984Snp		FIELD1("CngDBPData:", 17),
1382284984Snp		FIELD1("CngIMSG:", 16),
1383284984Snp		{ "CngChMap:", 0, 15, 0, 1, 0 },
1384284984Snp		{ NULL }
1385284984Snp	};
1386284984Snp
1387284984Snp	if (p->mem_id == SGE_CONTEXT_EGRESS)
1388284984Snp		show_struct(p->data, 6, (p->data[0] & 2) ? fl_t5 : egress_t5);
1389284984Snp	else if (p->mem_id == SGE_CONTEXT_FLM)
1390284984Snp		show_struct(p->data, 3, flm_t5);
1391284984Snp	else if (p->mem_id == SGE_CONTEXT_INGRESS)
1392284984Snp		show_struct(p->data, 5, ingress_t5);
1393284984Snp	else if (p->mem_id == SGE_CONTEXT_CNM)
1394284984Snp		show_struct(p->data, 1, conm_t5);
1395284984Snp}
1396284984Snp
1397284984Snpstatic void
1398284984Snpshow_t4_ctxt(const struct t4_sge_context *p)
1399284984Snp{
1400284984Snp	static struct field_desc egress_t4[] = {
1401284984Snp		FIELD1("StatusPgNS:", 180),
1402284984Snp		FIELD1("StatusPgRO:", 179),
1403284984Snp		FIELD1("FetchNS:", 178),
1404284984Snp		FIELD1("FetchRO:", 177),
1405284984Snp		FIELD1("Valid:", 176),
1406284984Snp		FIELD("PCIeDataChannel:", 174, 175),
1407222974Snp		FIELD1("DCAEgrQEn:", 173),
1408222974Snp		FIELD("DCACPUID:", 168, 172),
1409222974Snp		FIELD1("FCThreshOverride:", 167),
1410222974Snp		FIELD("WRLength:", 162, 166),
1411222974Snp		FIELD1("WRLengthKnown:", 161),
1412222974Snp		FIELD1("ReschedulePending:", 160),
1413222974Snp		FIELD1("OnChipQueue:", 159),
1414222974Snp		FIELD1("FetchSizeMode", 158),
1415222974Snp		{ "FetchBurstMin:", 156, 157, 4, 0, 1 },
1416222974Snp		{ "FetchBurstMax:", 153, 154, 6, 0, 1 },
1417222974Snp		FIELD("uPToken:", 133, 152),
1418222974Snp		FIELD1("uPTokenEn:", 132),
1419222974Snp		FIELD1("UserModeIO:", 131),
1420222974Snp		FIELD("uPFLCredits:", 123, 130),
1421222974Snp		FIELD1("uPFLCreditEn:", 122),
1422222974Snp		FIELD("FID:", 111, 121),
1423222974Snp		FIELD("HostFCMode:", 109, 110),
1424222974Snp		FIELD1("HostFCOwner:", 108),
1425222974Snp		{ "CIDXFlushThresh:", 105, 107, 0, 0, 1 },
1426222974Snp		FIELD("CIDX:", 89, 104),
1427222974Snp		FIELD("PIDX:", 73, 88),
1428222974Snp		{ "BaseAddress:", 18, 72, 9, 1 },
1429222974Snp		FIELD("QueueSize:", 2, 17),
1430222974Snp		FIELD1("QueueType:", 1),
1431222974Snp		FIELD1("CachePriority:", 0),
1432222974Snp		{ NULL }
1433222974Snp	};
1434284984Snp	static struct field_desc fl_t4[] = {
1435222974Snp		FIELD1("StatusPgNS:", 180),
1436222974Snp		FIELD1("StatusPgRO:", 179),
1437222974Snp		FIELD1("FetchNS:", 178),
1438222974Snp		FIELD1("FetchRO:", 177),
1439222974Snp		FIELD1("Valid:", 176),
1440222974Snp		FIELD("PCIeDataChannel:", 174, 175),
1441222974Snp		FIELD1("DCAEgrQEn:", 173),
1442222974Snp		FIELD("DCACPUID:", 168, 172),
1443222974Snp		FIELD1("FCThreshOverride:", 167),
1444222974Snp		FIELD1("ReschedulePending:", 160),
1445222974Snp		FIELD1("OnChipQueue:", 159),
1446222974Snp		FIELD1("FetchSizeMode", 158),
1447222974Snp		{ "FetchBurstMin:", 156, 157, 4, 0, 1 },
1448222974Snp		{ "FetchBurstMax:", 153, 154, 6, 0, 1 },
1449222974Snp		FIELD1("FLMcongMode:", 152),
1450222974Snp		FIELD("MaxuPFLCredits:", 144, 151),
1451222974Snp		FIELD("FLMcontextID:", 133, 143),
1452222974Snp		FIELD1("uPTokenEn:", 132),
1453222974Snp		FIELD1("UserModeIO:", 131),
1454222974Snp		FIELD("uPFLCredits:", 123, 130),
1455222974Snp		FIELD1("uPFLCreditEn:", 122),
1456222974Snp		FIELD("FID:", 111, 121),
1457222974Snp		FIELD("HostFCMode:", 109, 110),
1458222974Snp		FIELD1("HostFCOwner:", 108),
1459222974Snp		{ "CIDXFlushThresh:", 105, 107, 0, 0, 1 },
1460222974Snp		FIELD("CIDX:", 89, 104),
1461222974Snp		FIELD("PIDX:", 73, 88),
1462222974Snp		{ "BaseAddress:", 18, 72, 9, 1 },
1463222974Snp		FIELD("QueueSize:", 2, 17),
1464222974Snp		FIELD1("QueueType:", 1),
1465222974Snp		FIELD1("CachePriority:", 0),
1466222974Snp		{ NULL }
1467222974Snp	};
1468284984Snp	static struct field_desc ingress_t4[] = {
1469222974Snp		FIELD1("NoSnoop:", 145),
1470222974Snp		FIELD1("RelaxedOrdering:", 144),
1471222974Snp		FIELD1("GTSmode:", 143),
1472222974Snp		FIELD1("ISCSICoalescing:", 142),
1473222974Snp		FIELD1("Valid:", 141),
1474222974Snp		FIELD1("TimerPending:", 140),
1475222974Snp		FIELD1("DropRSS:", 139),
1476222974Snp		FIELD("PCIeChannel:", 137, 138),
1477222974Snp		FIELD1("SEInterruptArmed:", 136),
1478222974Snp		FIELD1("CongestionMgtEnable:", 135),
1479222974Snp		FIELD1("DCAIngQEnable:", 134),
1480222974Snp		FIELD("DCACPUID:", 129, 133),
1481222974Snp		FIELD1("UpdateScheduling:", 128),
1482222974Snp		FIELD("UpdateDelivery:", 126, 127),
1483222974Snp		FIELD1("InterruptSent:", 125),
1484222974Snp		FIELD("InterruptIDX:", 114, 124),
1485222974Snp		FIELD1("InterruptDestination:", 113),
1486222974Snp		FIELD1("InterruptArmed:", 112),
1487222974Snp		FIELD("RxIntCounter:", 106, 111),
1488222974Snp		FIELD("RxIntCounterThreshold:", 104, 105),
1489222974Snp		FIELD1("Generation:", 103),
1490222974Snp		{ "BaseAddress:", 48, 102, 9, 1 },
1491222974Snp		FIELD("PIDX:", 32, 47),
1492222974Snp		FIELD("CIDX:", 16, 31),
1493222974Snp		{ "QueueSize:", 4, 15, 4, 0 },
1494222974Snp		{ "QueueEntrySize:", 2, 3, 4, 0, 1 },
1495222974Snp		FIELD1("QueueEntryOverride:", 1),
1496222974Snp		FIELD1("CachePriority:", 0),
1497222974Snp		{ NULL }
1498222974Snp	};
1499284984Snp	static struct field_desc flm_t4[] = {
1500222974Snp		FIELD1("NoSnoop:", 79),
1501222974Snp		FIELD1("RelaxedOrdering:", 78),
1502222974Snp		FIELD1("Valid:", 77),
1503222974Snp		FIELD("DCACPUID:", 72, 76),
1504222974Snp		FIELD1("DCAFLEn:", 71),
1505222974Snp		FIELD("EQid:", 54, 70),
1506222974Snp		FIELD("SplitEn:", 52, 53),
1507222974Snp		FIELD1("PadEn:", 51),
1508222974Snp		FIELD1("PackEn:", 50),
1509222974Snp		FIELD1("DBpriority:", 48),
1510222974Snp		FIELD("PackOffset:", 16, 47),
1511222974Snp		FIELD("CIDX:", 8, 15),
1512222974Snp		FIELD("PIDX:", 0, 7),
1513222974Snp		{ NULL }
1514222974Snp	};
1515284984Snp	static struct field_desc conm_t4[] = {
1516222974Snp		FIELD1("CngDBPHdr:", 6),
1517222974Snp		FIELD1("CngDBPData:", 5),
1518222974Snp		FIELD1("CngIMSG:", 4),
1519261534Snp		{ "CngChMap:", 0, 3, 0, 1, 0},
1520222974Snp		{ NULL }
1521222974Snp	};
1522222974Snp
1523222974Snp	if (p->mem_id == SGE_CONTEXT_EGRESS)
1524284984Snp		show_struct(p->data, 6, (p->data[0] & 2) ? fl_t4 : egress_t4);
1525222974Snp	else if (p->mem_id == SGE_CONTEXT_FLM)
1526284984Snp		show_struct(p->data, 3, flm_t4);
1527222974Snp	else if (p->mem_id == SGE_CONTEXT_INGRESS)
1528284984Snp		show_struct(p->data, 5, ingress_t4);
1529222974Snp	else if (p->mem_id == SGE_CONTEXT_CNM)
1530284984Snp		show_struct(p->data, 1, conm_t4);
1531222974Snp}
1532222974Snp
1533222974Snp#undef FIELD
1534222974Snp#undef FIELD1
1535222974Snp
1536222900Snpstatic int
1537222974Snpget_sge_context(int argc, const char *argv[])
1538222974Snp{
1539222974Snp	int rc;
1540222974Snp	char *p;
1541222974Snp	long cid;
1542222974Snp	struct t4_sge_context cntxt = {0};
1543222974Snp
1544222974Snp	if (argc != 2) {
1545222974Snp		warnx("sge_context: incorrect number of arguments.");
1546222974Snp		return (EINVAL);
1547222974Snp	}
1548222974Snp
1549222974Snp	if (!strcmp(argv[0], "egress"))
1550222974Snp		cntxt.mem_id = SGE_CONTEXT_EGRESS;
1551222974Snp	else if (!strcmp(argv[0], "ingress"))
1552222974Snp		cntxt.mem_id = SGE_CONTEXT_INGRESS;
1553222974Snp	else if (!strcmp(argv[0], "fl"))
1554222974Snp		cntxt.mem_id = SGE_CONTEXT_FLM;
1555222974Snp	else if (!strcmp(argv[0], "cong"))
1556222974Snp		cntxt.mem_id = SGE_CONTEXT_CNM;
1557222974Snp	else {
1558222974Snp		warnx("unknown context type \"%s\"; known types are egress, "
1559222974Snp		    "ingress, fl, and cong.", argv[0]);
1560222974Snp		return (EINVAL);
1561222974Snp	}
1562222974Snp
1563222974Snp	p = str_to_number(argv[1], &cid, NULL);
1564222974Snp	if (*p) {
1565222974Snp		warnx("invalid context id \"%s\"", argv[1]);
1566222974Snp		return (EINVAL);
1567222974Snp	}
1568222974Snp	cntxt.cid = cid;
1569222974Snp
1570222974Snp	rc = doit(CHELSIO_T4_GET_SGE_CONTEXT, &cntxt);
1571222974Snp	if (rc != 0)
1572222974Snp		return (rc);
1573222974Snp
1574284984Snp	if (chip_id == 4)
1575284984Snp		show_t4_ctxt(&cntxt);
1576284984Snp	else
1577284984Snp		show_t5_ctxt(&cntxt);
1578284984Snp
1579222974Snp	return (0);
1580222974Snp}
1581222974Snp
1582222974Snpstatic int
1583228594Snploadfw(int argc, const char *argv[])
1584228594Snp{
1585228594Snp	int rc, fd;
1586228594Snp	struct t4_data data = {0};
1587228594Snp	const char *fname = argv[0];
1588228594Snp	struct stat st = {0};
1589228594Snp
1590228594Snp	if (argc != 1) {
1591228594Snp		warnx("loadfw: incorrect number of arguments.");
1592228594Snp		return (EINVAL);
1593228594Snp	}
1594228594Snp
1595228594Snp	fd = open(fname, O_RDONLY);
1596228594Snp	if (fd < 0) {
1597228594Snp		warn("open(%s)", fname);
1598228594Snp		return (errno);
1599228594Snp	}
1600228594Snp
1601228594Snp	if (fstat(fd, &st) < 0) {
1602228594Snp		warn("fstat");
1603228594Snp		close(fd);
1604228594Snp		return (errno);
1605228594Snp	}
1606228594Snp
1607228594Snp	data.len = st.st_size;
1608273360Snp	data.data = mmap(0, data.len, PROT_READ, MAP_PRIVATE, fd, 0);
1609228594Snp	if (data.data == MAP_FAILED) {
1610228594Snp		warn("mmap");
1611228594Snp		close(fd);
1612228594Snp		return (errno);
1613228594Snp	}
1614228594Snp
1615228594Snp	rc = doit(CHELSIO_T4_LOAD_FW, &data);
1616228594Snp	munmap(data.data, data.len);
1617228594Snp	close(fd);
1618228594Snp	return (rc);
1619228594Snp}
1620228594Snp
1621228594Snpstatic int
1622228594Snpread_mem(uint32_t addr, uint32_t len, void (*output)(uint32_t *, uint32_t))
1623228594Snp{
1624228594Snp	int rc;
1625228594Snp	struct t4_mem_range mr;
1626228594Snp
1627228594Snp	mr.addr = addr;
1628228594Snp	mr.len = len;
1629228594Snp	mr.data = malloc(mr.len);
1630228594Snp
1631228594Snp	if (mr.data == 0) {
1632228594Snp		warn("read_mem: malloc");
1633228594Snp		return (errno);
1634228594Snp	}
1635228594Snp
1636228594Snp	rc = doit(CHELSIO_T4_GET_MEM, &mr);
1637228594Snp	if (rc != 0)
1638228594Snp		goto done;
1639228594Snp
1640228594Snp	if (output)
1641228594Snp		(*output)(mr.data, mr.len);
1642228594Snpdone:
1643228594Snp	free(mr.data);
1644228594Snp	return (rc);
1645228594Snp}
1646228594Snp
1647228594Snp/*
1648228594Snp * Display memory as list of 'n' 4-byte values per line.
1649228594Snp */
1650228594Snpstatic void
1651228594Snpshow_mem(uint32_t *buf, uint32_t len)
1652228594Snp{
1653228594Snp	const char *s;
1654228594Snp	int i, n = 8;
1655228594Snp
1656228594Snp	while (len) {
1657228594Snp		for (i = 0; len && i < n; i++, buf++, len -= 4) {
1658228594Snp			s = i ? " " : "";
1659228594Snp			printf("%s%08x", s, htonl(*buf));
1660228594Snp		}
1661228594Snp		printf("\n");
1662228594Snp	}
1663228594Snp}
1664228594Snp
1665228594Snpstatic int
1666228594Snpmemdump(int argc, const char *argv[])
1667228594Snp{
1668228594Snp	char *p;
1669228594Snp	long l;
1670228594Snp	uint32_t addr, len;
1671228594Snp
1672228594Snp	if (argc != 2) {
1673228594Snp		warnx("incorrect number of arguments.");
1674228594Snp		return (EINVAL);
1675228594Snp	}
1676228594Snp
1677228594Snp	p = str_to_number(argv[0], &l, NULL);
1678228594Snp	if (*p) {
1679228594Snp		warnx("invalid address \"%s\"", argv[0]);
1680228594Snp		return (EINVAL);
1681228594Snp	}
1682228594Snp	addr = l;
1683228594Snp
1684228594Snp	p = str_to_number(argv[1], &l, NULL);
1685228594Snp	if (*p) {
1686228594Snp		warnx("memdump: invalid length \"%s\"", argv[1]);
1687228594Snp		return (EINVAL);
1688228594Snp	}
1689228594Snp	len = l;
1690228594Snp
1691228594Snp	return (read_mem(addr, len, show_mem));
1692228594Snp}
1693228594Snp
1694228594Snp/*
1695228594Snp * Display TCB as list of 'n' 4-byte values per line.
1696228594Snp */
1697228594Snpstatic void
1698228594Snpshow_tcb(uint32_t *buf, uint32_t len)
1699228594Snp{
1700228594Snp	const char *s;
1701228594Snp	int i, n = 8;
1702228594Snp
1703228594Snp	while (len) {
1704228594Snp		for (i = 0; len && i < n; i++, buf++, len -= 4) {
1705228594Snp			s = i ? " " : "";
1706228594Snp			printf("%s%08x", s, htonl(*buf));
1707228594Snp		}
1708228594Snp		printf("\n");
1709228594Snp	}
1710228594Snp}
1711228594Snp
1712228594Snp#define A_TP_CMM_TCB_BASE 0x7d10
1713228594Snp#define TCB_SIZE 128
1714228594Snpstatic int
1715228594Snpread_tcb(int argc, const char *argv[])
1716228594Snp{
1717228594Snp	char *p;
1718228594Snp	long l;
1719228594Snp	long long val;
1720228594Snp	unsigned int tid;
1721228594Snp	uint32_t addr;
1722228594Snp	int rc;
1723228594Snp
1724228594Snp	if (argc != 1) {
1725228594Snp		warnx("incorrect number of arguments.");
1726228594Snp		return (EINVAL);
1727228594Snp	}
1728228594Snp
1729228594Snp	p = str_to_number(argv[0], &l, NULL);
1730228594Snp	if (*p) {
1731228594Snp		warnx("invalid tid \"%s\"", argv[0]);
1732228594Snp		return (EINVAL);
1733228594Snp	}
1734228594Snp	tid = l;
1735228594Snp
1736228594Snp	rc = read_reg(A_TP_CMM_TCB_BASE, 4, &val);
1737228594Snp	if (rc != 0)
1738228594Snp		return (rc);
1739228594Snp
1740228594Snp	addr = val + tid * TCB_SIZE;
1741228594Snp
1742228594Snp	return (read_mem(addr, TCB_SIZE, show_tcb));
1743228594Snp}
1744228594Snp
1745228594Snpstatic int
1746241401Snpread_i2c(int argc, const char *argv[])
1747241401Snp{
1748241401Snp	char *p;
1749241401Snp	long l;
1750241401Snp	struct t4_i2c_data i2cd;
1751241401Snp	int rc, i;
1752241401Snp
1753241401Snp	if (argc < 3 || argc > 4) {
1754241401Snp		warnx("incorrect number of arguments.");
1755241401Snp		return (EINVAL);
1756241401Snp	}
1757241401Snp
1758241401Snp	p = str_to_number(argv[0], &l, NULL);
1759241401Snp	if (*p || l > UCHAR_MAX) {
1760241401Snp		warnx("invalid port id \"%s\"", argv[0]);
1761241401Snp		return (EINVAL);
1762241401Snp	}
1763241401Snp	i2cd.port_id = l;
1764241401Snp
1765241401Snp	p = str_to_number(argv[1], &l, NULL);
1766241401Snp	if (*p || l > UCHAR_MAX) {
1767241401Snp		warnx("invalid i2c device address \"%s\"", argv[1]);
1768241401Snp		return (EINVAL);
1769241401Snp	}
1770241401Snp	i2cd.dev_addr = l;
1771241401Snp
1772241401Snp	p = str_to_number(argv[2], &l, NULL);
1773241401Snp	if (*p || l > UCHAR_MAX) {
1774241401Snp		warnx("invalid byte offset \"%s\"", argv[2]);
1775241401Snp		return (EINVAL);
1776241401Snp	}
1777241401Snp	i2cd.offset = l;
1778241401Snp
1779241401Snp	if (argc == 4) {
1780241401Snp		p = str_to_number(argv[3], &l, NULL);
1781241401Snp		if (*p || l > sizeof(i2cd.data)) {
1782241401Snp			warnx("invalid number of bytes \"%s\"", argv[3]);
1783241401Snp			return (EINVAL);
1784241401Snp		}
1785241401Snp		i2cd.len = l;
1786241401Snp	} else
1787241401Snp		i2cd.len = 1;
1788241401Snp
1789241401Snp	rc = doit(CHELSIO_T4_GET_I2C, &i2cd);
1790241401Snp	if (rc != 0)
1791241401Snp		return (rc);
1792241401Snp
1793241401Snp	for (i = 0; i < i2cd.len; i++)
1794241401Snp		printf("0x%x [%u]\n", i2cd.data[i], i2cd.data[i]);
1795241401Snp
1796241401Snp	return (0);
1797241401Snp}
1798241401Snp
1799241401Snpstatic int
1800241416Snpclearstats(int argc, const char *argv[])
1801241416Snp{
1802241416Snp	char *p;
1803241416Snp	long l;
1804241416Snp	uint32_t port;
1805241416Snp
1806241416Snp	if (argc != 1) {
1807241416Snp		warnx("incorrect number of arguments.");
1808241416Snp		return (EINVAL);
1809241416Snp	}
1810241416Snp
1811241416Snp	p = str_to_number(argv[0], &l, NULL);
1812241416Snp	if (*p) {
1813241416Snp		warnx("invalid port id \"%s\"", argv[0]);
1814241416Snp		return (EINVAL);
1815241416Snp	}
1816241416Snp	port = l;
1817241416Snp
1818241416Snp	return doit(CHELSIO_T4_CLEAR_STATS, &port);
1819241416Snp}
1820241416Snp
1821241416Snpstatic int
1822253691Snpshow_tracers(void)
1823253691Snp{
1824253691Snp	struct t4_tracer t;
1825253691Snp	char *s;
1826253691Snp	int rc, port_idx, i;
1827253691Snp	long long val;
1828253691Snp
1829253691Snp	/* Magic values: MPS_TRC_CFG = 0x9800. MPS_TRC_CFG[1:1] = TrcEn */
1830253691Snp	rc = read_reg(0x9800, 4, &val);
1831253691Snp	if (rc != 0)
1832253691Snp		return (rc);
1833253691Snp	printf("tracing is %s\n", val & 2 ? "ENABLED" : "DISABLED");
1834253691Snp
1835253691Snp	t.idx = 0;
1836253691Snp	for (t.idx = 0; ; t.idx++) {
1837253691Snp		rc = doit(CHELSIO_T4_GET_TRACER, &t);
1838253691Snp		if (rc != 0 || t.idx == 0xff)
1839253691Snp			break;
1840253691Snp
1841253691Snp		if (t.tp.port < 4) {
1842253691Snp			s = "Rx";
1843253691Snp			port_idx = t.tp.port;
1844253691Snp		} else if (t.tp.port < 8) {
1845253691Snp			s = "Tx";
1846253691Snp			port_idx = t.tp.port - 4;
1847253691Snp		} else if (t.tp.port < 12) {
1848253691Snp			s = "loopback";
1849253691Snp			port_idx = t.tp.port - 8;
1850253691Snp		} else if (t.tp.port < 16) {
1851253691Snp			s = "MPS Rx";
1852253691Snp			port_idx = t.tp.port - 12;
1853253691Snp		} else if (t.tp.port < 20) {
1854253691Snp			s = "MPS Tx";
1855253691Snp			port_idx = t.tp.port - 16;
1856253691Snp		} else {
1857253691Snp			s = "unknown";
1858253691Snp			port_idx = t.tp.port;
1859253691Snp		}
1860253691Snp
1861253691Snp		printf("\ntracer %u (currently %s) captures ", t.idx,
1862253691Snp		    t.enabled ? "ENABLED" : "DISABLED");
1863253691Snp		if (t.tp.port < 8)
1864253691Snp			printf("port %u %s, ", port_idx, s);
1865253691Snp		else
1866253691Snp			printf("%s %u, ", s, port_idx);
1867253691Snp		printf("snap length: %u, min length: %u\n", t.tp.snap_len,
1868253691Snp		    t.tp.min_len);
1869253691Snp		printf("packets captured %smatch filter\n",
1870253691Snp		    t.tp.invert ? "do not " : "");
1871253691Snp		if (t.tp.skip_ofst) {
1872253691Snp			printf("filter pattern: ");
1873253691Snp			for (i = 0; i < t.tp.skip_ofst * 2; i += 2)
1874253691Snp				printf("%08x%08x", t.tp.data[i],
1875253691Snp				    t.tp.data[i + 1]);
1876253691Snp			printf("/");
1877253691Snp			for (i = 0; i < t.tp.skip_ofst * 2; i += 2)
1878253691Snp				printf("%08x%08x", t.tp.mask[i],
1879253691Snp				    t.tp.mask[i + 1]);
1880253691Snp			printf("@0\n");
1881253691Snp		}
1882253691Snp		printf("filter pattern: ");
1883253691Snp		for (i = t.tp.skip_ofst * 2; i < T4_TRACE_LEN / 4; i += 2)
1884253691Snp			printf("%08x%08x", t.tp.data[i], t.tp.data[i + 1]);
1885253691Snp		printf("/");
1886253691Snp		for (i = t.tp.skip_ofst * 2; i < T4_TRACE_LEN / 4; i += 2)
1887253691Snp			printf("%08x%08x", t.tp.mask[i], t.tp.mask[i + 1]);
1888253691Snp		printf("@%u\n", (t.tp.skip_ofst + t.tp.skip_len) * 8);
1889253691Snp	}
1890253691Snp
1891253691Snp	return (rc);
1892253691Snp}
1893253691Snp
1894253691Snpstatic int
1895253691Snptracer_onoff(uint8_t idx, int enabled)
1896253691Snp{
1897253691Snp	struct t4_tracer t;
1898253691Snp
1899253691Snp	t.idx = idx;
1900253691Snp	t.enabled = enabled;
1901253691Snp	t.valid = 0;
1902253691Snp
1903253691Snp	return doit(CHELSIO_T4_SET_TRACER, &t);
1904253691Snp}
1905253691Snp
1906253691Snpstatic void
1907253691Snpcreate_tracing_ifnet()
1908253691Snp{
1909253691Snp	char *cmd[] = {
1910253691Snp		"/sbin/ifconfig", __DECONST(char *, nexus), "create", NULL
1911253691Snp	};
1912253691Snp	char *env[] = {NULL};
1913253691Snp
1914253691Snp	if (vfork() == 0) {
1915253691Snp		close(STDERR_FILENO);
1916253691Snp		execve(cmd[0], cmd, env);
1917253691Snp		_exit(0);
1918253691Snp	}
1919253691Snp}
1920253691Snp
1921253691Snp/*
1922253691Snp * XXX: Allow user to specify snaplen, minlen, and pattern (including inverted
1923253691Snp * matching).  Right now this is a quick-n-dirty implementation that traces the
1924253691Snp * first 128B of all tx or rx on a port
1925253691Snp */
1926253691Snpstatic int
1927253691Snpset_tracer(uint8_t idx, int argc, const char *argv[])
1928253691Snp{
1929253691Snp	struct t4_tracer t;
1930253691Snp	int len, port;
1931253691Snp
1932253691Snp	bzero(&t, sizeof (t));
1933253691Snp	t.idx = idx;
1934253691Snp	t.enabled = 1;
1935253691Snp	t.valid = 1;
1936253691Snp
1937253691Snp	if (argc != 1) {
1938253691Snp		warnx("must specify tx<n> or rx<n>.");
1939253691Snp		return (EINVAL);
1940253691Snp	}
1941253691Snp
1942253691Snp	len = strlen(argv[0]);
1943253691Snp	if (len != 3) {
1944253691Snp		warnx("argument must be 3 characters (tx<n> or rx<n>)");
1945253691Snp		return (EINVAL);
1946253691Snp	}
1947253691Snp
1948253691Snp	if (strncmp(argv[0], "tx", 2) == 0) {
1949253691Snp		port = argv[0][2] - '0';
1950253691Snp		if (port < 0 || port > 3) {
1951253691Snp			warnx("'%c' in %s is invalid", argv[0][2], argv[0]);
1952253691Snp			return (EINVAL);
1953253691Snp		}
1954253691Snp		port += 4;
1955253691Snp	} else if (strncmp(argv[0], "rx", 2) == 0) {
1956253691Snp		port = argv[0][2] - '0';
1957253691Snp		if (port < 0 || port > 3) {
1958253691Snp			warnx("'%c' in %s is invalid", argv[0][2], argv[0]);
1959253691Snp			return (EINVAL);
1960253691Snp		}
1961253691Snp	} else {
1962253691Snp		warnx("argument '%s' isn't tx<n> or rx<n>", argv[0]);
1963253691Snp		return (EINVAL);
1964253691Snp	}
1965253691Snp
1966253691Snp	t.tp.snap_len = 128;
1967253691Snp	t.tp.min_len = 0;
1968253691Snp	t.tp.skip_ofst = 0;
1969253691Snp	t.tp.skip_len = 0;
1970253691Snp	t.tp.invert = 0;
1971253691Snp	t.tp.port = port;
1972253691Snp
1973253691Snp	create_tracing_ifnet();
1974253691Snp	return doit(CHELSIO_T4_SET_TRACER, &t);
1975253691Snp}
1976253691Snp
1977253691Snpstatic int
1978259048Snptracer_cmd(int argc, const char *argv[])
1979259048Snp{
1980259048Snp	long long val;
1981259048Snp	uint8_t idx;
1982259048Snp	char *s;
1983259048Snp
1984259048Snp	if (argc == 0) {
1985259048Snp		warnx("tracer: no arguments.");
1986259048Snp		return (EINVAL);
1987259048Snp	};
1988259048Snp
1989259048Snp	/* list */
1990259048Snp	if (strcmp(argv[0], "list") == 0) {
1991259048Snp		if (argc != 1)
1992259048Snp			warnx("trailing arguments after \"list\" ignored.");
1993259048Snp
1994259048Snp		return show_tracers();
1995259048Snp	}
1996259048Snp
1997259048Snp	/* <idx> ... */
1998259048Snp	s = str_to_number(argv[0], NULL, &val);
1999259048Snp	if (*s || val > 0xff) {
2000259048Snp		warnx("\"%s\" is neither an index nor a tracer subcommand.",
2001259048Snp		    argv[0]);
2002259048Snp		return (EINVAL);
2003259048Snp	}
2004259048Snp	idx = (int8_t)val;
2005259048Snp
2006259048Snp	/* <idx> disable */
2007259048Snp	if (argc == 2 && strcmp(argv[1], "disable") == 0)
2008259048Snp		return tracer_onoff(idx, 0);
2009259048Snp
2010259048Snp	/* <idx> enable */
2011259048Snp	if (argc == 2 && strcmp(argv[1], "enable") == 0)
2012259048Snp		return tracer_onoff(idx, 1);
2013259048Snp
2014259048Snp	/* <idx> ... */
2015259048Snp	return set_tracer(idx, argc - 1, argv + 1);
2016259048Snp}
2017259048Snp
2018259048Snpstatic int
2019269106Snpmodinfo_raw(int port_id)
2020269106Snp{
2021269106Snp	uint8_t offset;
2022269106Snp	struct t4_i2c_data i2cd;
2023269106Snp	int rc;
2024269106Snp
2025269106Snp	for (offset = 0; offset < 96; offset += sizeof(i2cd.data)) {
2026269106Snp		bzero(&i2cd, sizeof(i2cd));
2027269106Snp		i2cd.port_id = port_id;
2028269106Snp		i2cd.dev_addr = 0xa0;
2029269106Snp		i2cd.offset = offset;
2030269106Snp		i2cd.len = sizeof(i2cd.data);
2031269106Snp		rc = doit(CHELSIO_T4_GET_I2C, &i2cd);
2032269106Snp		if (rc != 0)
2033269106Snp			return (rc);
2034269106Snp		printf("%02x:  %02x %02x %02x %02x  %02x %02x %02x %02x",
2035269106Snp		    offset, i2cd.data[0], i2cd.data[1], i2cd.data[2],
2036269106Snp		    i2cd.data[3], i2cd.data[4], i2cd.data[5], i2cd.data[6],
2037269106Snp		    i2cd.data[7]);
2038269106Snp
2039269106Snp		printf("  %c%c%c%c %c%c%c%c\n",
2040269106Snp		    isprint(i2cd.data[0]) ? i2cd.data[0] : '.',
2041269106Snp		    isprint(i2cd.data[1]) ? i2cd.data[1] : '.',
2042269106Snp		    isprint(i2cd.data[2]) ? i2cd.data[2] : '.',
2043269106Snp		    isprint(i2cd.data[3]) ? i2cd.data[3] : '.',
2044269106Snp		    isprint(i2cd.data[4]) ? i2cd.data[4] : '.',
2045269106Snp		    isprint(i2cd.data[5]) ? i2cd.data[5] : '.',
2046269106Snp		    isprint(i2cd.data[6]) ? i2cd.data[6] : '.',
2047269106Snp		    isprint(i2cd.data[7]) ? i2cd.data[7] : '.');
2048269106Snp	}
2049269106Snp
2050269106Snp	return (0);
2051269106Snp}
2052269106Snp
2053269106Snpstatic int
2054258698Snpmodinfo(int argc, const char *argv[])
2055258698Snp{
2056258698Snp	long port;
2057258698Snp	char string[16], *p;
2058258698Snp	struct t4_i2c_data i2cd;
2059258698Snp	int rc, i;
2060258698Snp	uint16_t temp, vcc, tx_bias, tx_power, rx_power;
2061258698Snp
2062269106Snp	if (argc < 1) {
2063258698Snp		warnx("must supply a port");
2064258698Snp		return (EINVAL);
2065258698Snp	}
2066258698Snp
2067269106Snp	if (argc > 2) {
2068269106Snp		warnx("too many arguments");
2069269106Snp		return (EINVAL);
2070269106Snp	}
2071269106Snp
2072258698Snp	p = str_to_number(argv[0], &port, NULL);
2073258698Snp	if (*p || port > UCHAR_MAX) {
2074258698Snp		warnx("invalid port id \"%s\"", argv[0]);
2075258698Snp		return (EINVAL);
2076258698Snp	}
2077258698Snp
2078269106Snp	if (argc == 2) {
2079269106Snp		if (!strcmp(argv[1], "raw"))
2080269106Snp			return (modinfo_raw(port));
2081269106Snp		else {
2082269106Snp			warnx("second argument can only be \"raw\"");
2083269106Snp			return (EINVAL);
2084269106Snp		}
2085269106Snp	}
2086269106Snp
2087258698Snp	bzero(&i2cd, sizeof(i2cd));
2088258698Snp	i2cd.len = 1;
2089258698Snp	i2cd.port_id = port;
2090258698Snp	i2cd.dev_addr = SFF_8472_BASE;
2091258698Snp
2092258698Snp	i2cd.offset = SFF_8472_ID;
2093258698Snp	if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
2094258698Snp		goto fail;
2095258698Snp
2096258698Snp	if (i2cd.data[0] > SFF_8472_ID_LAST)
2097258698Snp		printf("Unknown ID\n");
2098258698Snp	else
2099258698Snp		printf("ID: %s\n", sff_8472_id[i2cd.data[0]]);
2100258698Snp
2101258698Snp	bzero(&string, sizeof(string));
2102258698Snp	for (i = SFF_8472_VENDOR_START; i < SFF_8472_VENDOR_END; i++) {
2103258698Snp		i2cd.offset = i;
2104258698Snp		if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
2105258698Snp			goto fail;
2106258698Snp		string[i - SFF_8472_VENDOR_START] = i2cd.data[0];
2107258698Snp	}
2108258698Snp	printf("Vendor %s\n", string);
2109258698Snp
2110258698Snp	bzero(&string, sizeof(string));
2111258698Snp	for (i = SFF_8472_SN_START; i < SFF_8472_SN_END; i++) {
2112258698Snp		i2cd.offset = i;
2113258698Snp		if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
2114258698Snp			goto fail;
2115258698Snp		string[i - SFF_8472_SN_START] = i2cd.data[0];
2116258698Snp	}
2117258698Snp	printf("SN %s\n", string);
2118258698Snp
2119258698Snp	bzero(&string, sizeof(string));
2120258698Snp	for (i = SFF_8472_PN_START; i < SFF_8472_PN_END; i++) {
2121258698Snp		i2cd.offset = i;
2122258698Snp		if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
2123258698Snp			goto fail;
2124258698Snp		string[i - SFF_8472_PN_START] = i2cd.data[0];
2125258698Snp	}
2126258698Snp	printf("PN %s\n", string);
2127258698Snp
2128258698Snp	bzero(&string, sizeof(string));
2129258698Snp	for (i = SFF_8472_REV_START; i < SFF_8472_REV_END; i++) {
2130258698Snp		i2cd.offset = i;
2131258698Snp		if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
2132258698Snp			goto fail;
2133258698Snp		string[i - SFF_8472_REV_START] = i2cd.data[0];
2134258698Snp	}
2135258698Snp	printf("Rev %s\n", string);
2136258698Snp
2137258698Snp	i2cd.offset = SFF_8472_DIAG_TYPE;
2138258698Snp	if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
2139258698Snp		goto fail;
2140258698Snp
2141258698Snp	if ((char )i2cd.data[0] & (SFF_8472_DIAG_IMPL |
2142258698Snp				   SFF_8472_DIAG_INTERNAL)) {
2143258698Snp
2144258698Snp		/* Switch to reading from the Diagnostic address. */
2145258698Snp		i2cd.dev_addr = SFF_8472_DIAG;
2146258698Snp		i2cd.len = 1;
2147258698Snp
2148258698Snp		i2cd.offset = SFF_8472_TEMP;
2149258698Snp		if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
2150258698Snp			goto fail;
2151258698Snp		temp = i2cd.data[0] << 8;
2152258698Snp		printf("Temp: ");
2153258698Snp		if ((temp & SFF_8472_TEMP_SIGN) == SFF_8472_TEMP_SIGN)
2154258698Snp			printf("-");
2155258698Snp		else
2156258698Snp			printf("+");
2157258698Snp		printf("%dC\n", (temp & SFF_8472_TEMP_MSK) >>
2158258698Snp		    SFF_8472_TEMP_SHIFT);
2159258698Snp
2160258698Snp		i2cd.offset = SFF_8472_VCC;
2161258698Snp		if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
2162258698Snp			goto fail;
2163258698Snp		vcc = i2cd.data[0] << 8;
2164258698Snp		printf("Vcc %fV\n", vcc / SFF_8472_VCC_FACTOR);
2165258698Snp
2166258698Snp		i2cd.offset = SFF_8472_TX_BIAS;
2167258698Snp		if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
2168258698Snp			goto fail;
2169258698Snp		tx_bias = i2cd.data[0] << 8;
2170258698Snp		printf("TX Bias %fuA\n", tx_bias / SFF_8472_BIAS_FACTOR);
2171258698Snp
2172258698Snp		i2cd.offset = SFF_8472_TX_POWER;
2173258698Snp		if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
2174258698Snp			goto fail;
2175258698Snp		tx_power = i2cd.data[0] << 8;
2176258698Snp		printf("TX Power %fmW\n", tx_power / SFF_8472_POWER_FACTOR);
2177258698Snp
2178258698Snp		i2cd.offset = SFF_8472_RX_POWER;
2179258698Snp		if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
2180258698Snp			goto fail;
2181258698Snp		rx_power = i2cd.data[0] << 8;
2182258698Snp		printf("RX Power %fmW\n", rx_power / SFF_8472_POWER_FACTOR);
2183258698Snp
2184258698Snp	} else
2185258698Snp		printf("Diagnostics not supported.\n");
2186258698Snp
2187258698Snp	return(0);
2188258698Snp
2189258698Snpfail:
2190258698Snp	if (rc == EPERM)
2191258698Snp		warnx("No module/cable in port %ld", port);
2192258698Snp	return (rc);
2193258698Snp
2194258698Snp}
2195258698Snp
2196259048Snp/* XXX: pass in a low/high and do range checks as well */
2197258698Snpstatic int
2198259048Snpget_sched_param(const char *param, const char *args[], long *val)
2199253691Snp{
2200259048Snp	char *p;
2201253691Snp
2202259048Snp	if (strcmp(param, args[0]) != 0)
2203259048Snp		return (EINVAL);
2204259048Snp
2205259048Snp	p = str_to_number(args[1], val, NULL);
2206259048Snp	if (*p) {
2207259048Snp		warnx("parameter \"%s\" has bad value \"%s\"", args[0],
2208259048Snp		    args[1]);
2209259048Snp		return (EINVAL);
2210259048Snp	}
2211259048Snp
2212259048Snp	return (0);
2213259048Snp}
2214259048Snp
2215259048Snpstatic int
2216259048Snpsched_class(int argc, const char *argv[])
2217259048Snp{
2218259048Snp	struct t4_sched_params op;
2219259048Snp	int errs, i;
2220259048Snp
2221259048Snp	memset(&op, 0xff, sizeof(op));
2222259048Snp	op.subcmd = -1;
2223259048Snp	op.type = -1;
2224253691Snp	if (argc == 0) {
2225259048Snp		warnx("missing scheduling sub-command");
2226253691Snp		return (EINVAL);
2227259048Snp	}
2228259048Snp	if (!strcmp(argv[0], "config")) {
2229259048Snp		op.subcmd = SCHED_CLASS_SUBCMD_CONFIG;
2230259048Snp		op.u.config.minmax = -1;
2231259048Snp	} else if (!strcmp(argv[0], "params")) {
2232259048Snp		op.subcmd = SCHED_CLASS_SUBCMD_PARAMS;
2233259048Snp		op.u.params.level = op.u.params.mode = op.u.params.rateunit =
2234259048Snp		    op.u.params.ratemode = op.u.params.channel =
2235259048Snp		    op.u.params.cl = op.u.params.minrate = op.u.params.maxrate =
2236259048Snp		    op.u.params.weight = op.u.params.pktsize = -1;
2237259048Snp	} else {
2238259048Snp		warnx("invalid scheduling sub-command \"%s\"", argv[0]);
2239259048Snp		return (EINVAL);
2240259048Snp	}
2241253691Snp
2242259048Snp	/* Decode remaining arguments ... */
2243259048Snp	errs = 0;
2244259048Snp	for (i = 1; i < argc; i += 2) {
2245259048Snp		const char **args = &argv[i];
2246259048Snp		long l;
2247253691Snp
2248259048Snp		if (i + 1 == argc) {
2249259048Snp			warnx("missing argument for \"%s\"", args[0]);
2250259048Snp			errs++;
2251259048Snp			break;
2252259048Snp		}
2253259048Snp
2254259048Snp		if (!strcmp(args[0], "type")) {
2255259048Snp			if (!strcmp(args[1], "packet"))
2256259048Snp				op.type = SCHED_CLASS_TYPE_PACKET;
2257259048Snp			else {
2258259048Snp				warnx("invalid type parameter \"%s\"", args[1]);
2259259048Snp				errs++;
2260259048Snp			}
2261259048Snp
2262259048Snp			continue;
2263259048Snp		}
2264259048Snp
2265259048Snp		if (op.subcmd == SCHED_CLASS_SUBCMD_CONFIG) {
2266259048Snp			if(!get_sched_param("minmax", args, &l))
2267259048Snp				op.u.config.minmax = (int8_t)l;
2268259048Snp			else {
2269259048Snp				warnx("unknown scheduler config parameter "
2270259048Snp				    "\"%s\"", args[0]);
2271259048Snp				errs++;
2272259048Snp			}
2273259048Snp
2274259048Snp			continue;
2275259048Snp		}
2276259048Snp
2277259048Snp		/* Rest applies only to SUBCMD_PARAMS */
2278259048Snp		if (op.subcmd != SCHED_CLASS_SUBCMD_PARAMS)
2279259048Snp			continue;
2280259048Snp
2281259048Snp		if (!strcmp(args[0], "level")) {
2282259048Snp			if (!strcmp(args[1], "cl-rl"))
2283259048Snp				op.u.params.level = SCHED_CLASS_LEVEL_CL_RL;
2284259048Snp			else if (!strcmp(args[1], "cl-wrr"))
2285259048Snp				op.u.params.level = SCHED_CLASS_LEVEL_CL_WRR;
2286259048Snp			else if (!strcmp(args[1], "ch-rl"))
2287259048Snp				op.u.params.level = SCHED_CLASS_LEVEL_CH_RL;
2288259048Snp			else {
2289259048Snp				warnx("invalid level parameter \"%s\"",
2290259048Snp				    args[1]);
2291259048Snp				errs++;
2292259048Snp			}
2293259048Snp		} else if (!strcmp(args[0], "mode")) {
2294259048Snp			if (!strcmp(args[1], "class"))
2295259048Snp				op.u.params.mode = SCHED_CLASS_MODE_CLASS;
2296259048Snp			else if (!strcmp(args[1], "flow"))
2297259048Snp				op.u.params.mode = SCHED_CLASS_MODE_FLOW;
2298259048Snp			else {
2299259048Snp				warnx("invalid mode parameter \"%s\"", args[1]);
2300259048Snp				errs++;
2301259048Snp			}
2302259048Snp		} else if (!strcmp(args[0], "rate-unit")) {
2303259048Snp			if (!strcmp(args[1], "bits"))
2304259048Snp				op.u.params.rateunit = SCHED_CLASS_RATEUNIT_BITS;
2305259048Snp			else if (!strcmp(args[1], "pkts"))
2306259048Snp				op.u.params.rateunit = SCHED_CLASS_RATEUNIT_PKTS;
2307259048Snp			else {
2308259048Snp				warnx("invalid rate-unit parameter \"%s\"",
2309259048Snp				    args[1]);
2310259048Snp				errs++;
2311259048Snp			}
2312259048Snp		} else if (!strcmp(args[0], "rate-mode")) {
2313259048Snp			if (!strcmp(args[1], "relative"))
2314259048Snp				op.u.params.ratemode = SCHED_CLASS_RATEMODE_REL;
2315259048Snp			else if (!strcmp(args[1], "absolute"))
2316259048Snp				op.u.params.ratemode = SCHED_CLASS_RATEMODE_ABS;
2317259048Snp			else {
2318259048Snp				warnx("invalid rate-mode parameter \"%s\"",
2319259048Snp				    args[1]);
2320259048Snp				errs++;
2321259048Snp			}
2322259048Snp		} else if (!get_sched_param("channel", args, &l))
2323259048Snp			op.u.params.channel = (int8_t)l;
2324259048Snp		else if (!get_sched_param("class", args, &l))
2325259048Snp			op.u.params.cl = (int8_t)l;
2326259048Snp		else if (!get_sched_param("min-rate", args, &l))
2327259048Snp			op.u.params.minrate = (int32_t)l;
2328259048Snp		else if (!get_sched_param("max-rate", args, &l))
2329259048Snp			op.u.params.maxrate = (int32_t)l;
2330259048Snp		else if (!get_sched_param("weight", args, &l))
2331259048Snp			op.u.params.weight = (int16_t)l;
2332259048Snp		else if (!get_sched_param("pkt-size", args, &l))
2333259048Snp			op.u.params.pktsize = (int16_t)l;
2334259048Snp		else {
2335259048Snp			warnx("unknown scheduler parameter \"%s\"", args[0]);
2336259048Snp			errs++;
2337259048Snp		}
2338253691Snp	}
2339253691Snp
2340259048Snp	/*
2341259048Snp	 * Catch some logical fallacies in terms of argument combinations here
2342259048Snp	 * so we can offer more than just the EINVAL return from the driver.
2343259048Snp	 * The driver will be able to catch a lot more issues since it knows
2344259048Snp	 * the specifics of the device hardware capabilities like how many
2345259048Snp	 * channels, classes, etc. the device supports.
2346259048Snp	 */
2347259048Snp	if (op.type < 0) {
2348259048Snp		warnx("sched \"type\" parameter missing");
2349259048Snp		errs++;
2350259048Snp	}
2351259048Snp	if (op.subcmd == SCHED_CLASS_SUBCMD_CONFIG) {
2352259048Snp		if (op.u.config.minmax < 0) {
2353259048Snp			warnx("sched config \"minmax\" parameter missing");
2354259048Snp			errs++;
2355259048Snp		}
2356259048Snp	}
2357259048Snp	if (op.subcmd == SCHED_CLASS_SUBCMD_PARAMS) {
2358259048Snp		if (op.u.params.level < 0) {
2359259048Snp			warnx("sched params \"level\" parameter missing");
2360259048Snp			errs++;
2361259048Snp		}
2362259048Snp		if (op.u.params.mode < 0) {
2363259048Snp			warnx("sched params \"mode\" parameter missing");
2364259048Snp			errs++;
2365259048Snp		}
2366259048Snp		if (op.u.params.rateunit < 0) {
2367259048Snp			warnx("sched params \"rate-unit\" parameter missing");
2368259048Snp			errs++;
2369259048Snp		}
2370259048Snp		if (op.u.params.ratemode < 0) {
2371259048Snp			warnx("sched params \"rate-mode\" parameter missing");
2372259048Snp			errs++;
2373259048Snp		}
2374259048Snp		if (op.u.params.channel < 0) {
2375259048Snp			warnx("sched params \"channel\" missing");
2376259048Snp			errs++;
2377259048Snp		}
2378259048Snp		if (op.u.params.cl < 0) {
2379259048Snp			warnx("sched params \"class\" missing");
2380259048Snp			errs++;
2381259048Snp		}
2382259048Snp		if (op.u.params.maxrate < 0 &&
2383259048Snp		    (op.u.params.level == SCHED_CLASS_LEVEL_CL_RL ||
2384259048Snp		    op.u.params.level == SCHED_CLASS_LEVEL_CH_RL)) {
2385259048Snp			warnx("sched params \"max-rate\" missing for "
2386259048Snp			    "rate-limit level");
2387259048Snp			errs++;
2388259048Snp		}
2389259048Snp		if (op.u.params.weight < 0 &&
2390259048Snp		    op.u.params.level == SCHED_CLASS_LEVEL_CL_WRR) {
2391259048Snp			warnx("sched params \"weight\" missing for "
2392259048Snp			    "weighted-round-robin level");
2393259048Snp			errs++;
2394259048Snp		}
2395259048Snp		if (op.u.params.pktsize < 0 &&
2396259048Snp		    (op.u.params.level == SCHED_CLASS_LEVEL_CL_RL ||
2397259048Snp		    op.u.params.level == SCHED_CLASS_LEVEL_CH_RL)) {
2398259048Snp			warnx("sched params \"pkt-size\" missing for "
2399259048Snp			    "rate-limit level");
2400259048Snp			errs++;
2401259048Snp		}
2402259048Snp		if (op.u.params.mode == SCHED_CLASS_MODE_FLOW &&
2403259048Snp		    op.u.params.ratemode != SCHED_CLASS_RATEMODE_ABS) {
2404259048Snp			warnx("sched params mode flow needs rate-mode absolute");
2405259048Snp			errs++;
2406259048Snp		}
2407259048Snp		if (op.u.params.ratemode == SCHED_CLASS_RATEMODE_REL &&
2408259048Snp		    !in_range(op.u.params.maxrate, 1, 100)) {
2409259048Snp                        warnx("sched params \"max-rate\" takes "
2410259048Snp			    "percentage value(1-100) for rate-mode relative");
2411259048Snp                        errs++;
2412259048Snp                }
2413259048Snp                if (op.u.params.ratemode == SCHED_CLASS_RATEMODE_ABS &&
2414259048Snp		    !in_range(op.u.params.maxrate, 1, 10000000)) {
2415259048Snp                        warnx("sched params \"max-rate\" takes "
2416259048Snp			    "value(1-10000000) for rate-mode absolute");
2417259048Snp                        errs++;
2418259048Snp                }
2419259048Snp                if (op.u.params.maxrate > 0 &&
2420259048Snp		    op.u.params.maxrate < op.u.params.minrate) {
2421259048Snp                        warnx("sched params \"max-rate\" is less than "
2422259048Snp			    "\"min-rate\"");
2423259048Snp                        errs++;
2424259048Snp                }
2425259048Snp	}
2426259048Snp
2427259048Snp	if (errs > 0) {
2428259048Snp		warnx("%d error%s in sched-class command", errs,
2429259048Snp		    errs == 1 ? "" : "s");
2430253691Snp		return (EINVAL);
2431253691Snp	}
2432253691Snp
2433259048Snp	return doit(CHELSIO_T4_SCHED_CLASS, &op);
2434259048Snp}
2435253691Snp
2436259048Snpstatic int
2437259048Snpsched_queue(int argc, const char *argv[])
2438259048Snp{
2439259048Snp	struct t4_sched_queue op = {0};
2440259048Snp	char *p;
2441259048Snp	long val;
2442253691Snp
2443259048Snp	if (argc != 3) {
2444259048Snp		/* need "<port> <queue> <class> */
2445259048Snp		warnx("incorrect number of arguments.");
2446259048Snp		return (EINVAL);
2447259048Snp	}
2448259048Snp
2449259048Snp	p = str_to_number(argv[0], &val, NULL);
2450259048Snp	if (*p || val > UCHAR_MAX) {
2451259048Snp		warnx("invalid port id \"%s\"", argv[0]);
2452259048Snp		return (EINVAL);
2453259048Snp	}
2454259048Snp	op.port = (uint8_t)val;
2455259048Snp
2456259048Snp	if (!strcmp(argv[1], "all") || !strcmp(argv[1], "*"))
2457259048Snp		op.queue = -1;
2458259048Snp	else {
2459259048Snp		p = str_to_number(argv[1], &val, NULL);
2460259048Snp		if (*p || val < -1) {
2461259048Snp			warnx("invalid queue \"%s\"", argv[1]);
2462259048Snp			return (EINVAL);
2463259048Snp		}
2464259048Snp		op.queue = (int8_t)val;
2465259048Snp	}
2466259048Snp
2467259048Snp	if (!strcmp(argv[2], "unbind") || !strcmp(argv[2], "clear"))
2468259048Snp		op.cl = -1;
2469259048Snp	else {
2470259048Snp		p = str_to_number(argv[2], &val, NULL);
2471259048Snp		if (*p || val < -1) {
2472259048Snp			warnx("invalid class \"%s\"", argv[2]);
2473259048Snp			return (EINVAL);
2474259048Snp		}
2475259048Snp		op.cl = (int8_t)val;
2476259048Snp	}
2477259048Snp
2478259048Snp	return doit(CHELSIO_T4_SCHED_QUEUE, &op);
2479253691Snp}
2480253691Snp
2481253691Snpstatic int
2482222900Snprun_cmd(int argc, const char *argv[])
2483222900Snp{
2484222900Snp	int rc = -1;
2485222900Snp	const char *cmd = argv[0];
2486222900Snp
2487222900Snp	/* command */
2488222900Snp	argc--;
2489222900Snp	argv++;
2490222900Snp
2491222900Snp	if (!strcmp(cmd, "reg") || !strcmp(cmd, "reg32"))
2492222900Snp		rc = register_io(argc, argv, 4);
2493222900Snp	else if (!strcmp(cmd, "reg64"))
2494222900Snp		rc = register_io(argc, argv, 8);
2495222900Snp	else if (!strcmp(cmd, "regdump"))
2496222900Snp		rc = dump_regs(argc, argv);
2497222900Snp	else if (!strcmp(cmd, "filter"))
2498222900Snp		rc = filter_cmd(argc, argv);
2499222974Snp	else if (!strcmp(cmd, "context"))
2500222974Snp		rc = get_sge_context(argc, argv);
2501228594Snp	else if (!strcmp(cmd, "loadfw"))
2502228594Snp		rc = loadfw(argc, argv);
2503228594Snp	else if (!strcmp(cmd, "memdump"))
2504228594Snp		rc = memdump(argc, argv);
2505228594Snp	else if (!strcmp(cmd, "tcb"))
2506228594Snp		rc = read_tcb(argc, argv);
2507241401Snp	else if (!strcmp(cmd, "i2c"))
2508241401Snp		rc = read_i2c(argc, argv);
2509241416Snp	else if (!strcmp(cmd, "clearstats"))
2510241416Snp		rc = clearstats(argc, argv);
2511253691Snp	else if (!strcmp(cmd, "tracer"))
2512253691Snp		rc = tracer_cmd(argc, argv);
2513258698Snp	else if (!strcmp(cmd, "modinfo"))
2514258698Snp		rc = modinfo(argc, argv);
2515259048Snp	else if (!strcmp(cmd, "sched-class"))
2516259048Snp		rc = sched_class(argc, argv);
2517259048Snp	else if (!strcmp(cmd, "sched-queue"))
2518259048Snp		rc = sched_queue(argc, argv);
2519222900Snp	else {
2520222900Snp		rc = EINVAL;
2521222900Snp		warnx("invalid command \"%s\"", cmd);
2522222900Snp	}
2523222900Snp
2524222900Snp	return (rc);
2525222900Snp}
2526222900Snp
2527222900Snp#define MAX_ARGS 15
2528222900Snpstatic int
2529222900Snprun_cmd_loop(void)
2530222900Snp{
2531222900Snp	int i, rc = 0;
2532222900Snp	char buffer[128], *buf;
2533222900Snp	const char *args[MAX_ARGS + 1];
2534222900Snp
2535222900Snp	/*
2536222900Snp	 * Simple loop: displays a "> " prompt and processes any input as a
2537222900Snp	 * cxgbetool command.  You're supposed to enter only the part after
2538222900Snp	 * "cxgbetool t4nexX".  Use "quit" or "exit" to exit.
2539222900Snp	 */
2540222900Snp	for (;;) {
2541222900Snp		fprintf(stdout, "> ");
2542222900Snp		fflush(stdout);
2543222900Snp		buf = fgets(buffer, sizeof(buffer), stdin);
2544222900Snp		if (buf == NULL) {
2545222900Snp			if (ferror(stdin)) {
2546222900Snp				warn("stdin error");
2547222900Snp				rc = errno;	/* errno from fgets */
2548222900Snp			}
2549222900Snp			break;
2550222900Snp		}
2551222900Snp
2552222900Snp		i = 0;
2553222900Snp		while ((args[i] = strsep(&buf, " \t\n")) != NULL) {
2554222900Snp			if (args[i][0] != 0 && ++i == MAX_ARGS)
2555222900Snp				break;
2556222900Snp		}
2557222900Snp		args[i] = 0;
2558222900Snp
2559222900Snp		if (i == 0)
2560222900Snp			continue;	/* skip empty line */
2561222900Snp
2562222900Snp		if (!strcmp(args[0], "quit") || !strcmp(args[0], "exit"))
2563222900Snp			break;
2564222900Snp
2565222900Snp		rc = run_cmd(i, args);
2566222900Snp	}
2567222900Snp
2568222900Snp	/* rc normally comes from the last command (not including quit/exit) */
2569222900Snp	return (rc);
2570222900Snp}
2571222900Snp
2572222900Snpint
2573222900Snpmain(int argc, const char *argv[])
2574222900Snp{
2575222900Snp	int rc = -1;
2576222900Snp
2577222900Snp	progname = argv[0];
2578222900Snp
2579222900Snp	if (argc == 2) {
2580222900Snp		if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
2581222900Snp			usage(stdout);
2582222900Snp			exit(0);
2583222900Snp		}
2584222900Snp	}
2585222900Snp
2586222900Snp	if (argc < 3) {
2587222900Snp		usage(stderr);
2588222900Snp		exit(EINVAL);
2589222900Snp	}
2590222900Snp
2591222900Snp	nexus = argv[1];
2592222900Snp
2593222900Snp	/* progname and nexus */
2594222900Snp	argc -= 2;
2595222900Snp	argv += 2;
2596222900Snp
2597222900Snp	if (argc == 1 && !strcmp(argv[0], "stdio"))
2598222900Snp		rc = run_cmd_loop();
2599222900Snp	else
2600222900Snp		rc = run_cmd(argc, argv);
2601222900Snp
2602222900Snp	return (rc);
2603222900Snp}
2604