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: stable/10/usr.sbin/cxgbetool/cxgbetool.c 306823 2016-10-07 19:13:29Z np $");
30222900Snp
31287297Srodrigc#include <sys/param.h>
32222900Snp#include <sys/ioctl.h>
33228594Snp#include <sys/mman.h>
34222900Snp#include <sys/socket.h>
35228594Snp#include <sys/stat.h>
36287297Srodrigc
37287297Srodrigc#include <arpa/inet.h>
38222900Snp#include <net/ethernet.h>
39287297Srodrigc#include <net/sff8472.h>
40222900Snp#include <netinet/in.h>
41222900Snp
42287297Srodrigc#include <ctype.h>
43287297Srodrigc#include <err.h>
44287297Srodrigc#include <errno.h>
45287297Srodrigc#include <fcntl.h>
46287297Srodrigc#include <limits.h>
47287297Srodrigc#include <stdint.h>
48287297Srodrigc#include <stdio.h>
49287297Srodrigc#include <stdlib.h>
50287297Srodrigc#include <string.h>
51287297Srodrigc#include <unistd.h>
52287297Srodrigc
53222900Snp#include "t4_ioctl.h"
54222900Snp
55259048Snp#define in_range(val, lo, hi) ( val < 0 || (val <= hi && val >= lo))
56222974Snp#define	max(x, y) ((x) > (y) ? (x) : (y))
57222974Snp
58222900Snpstatic const char *progname, *nexus;
59253870Snpstatic int chip_id;	/* 4 for T4, 5 for T5 */
60222900Snp
61222900Snpstruct reg_info {
62222900Snp	const char *name;
63222900Snp	uint32_t addr;
64222900Snp	uint32_t len;
65222900Snp};
66222900Snp
67222900Snpstruct mod_regs {
68222900Snp	const char *name;
69222900Snp	const struct reg_info *ri;
70222900Snp};
71222900Snp
72222974Snpstruct field_desc {
73222974Snp	const char *name;     /* Field name */
74222974Snp	unsigned short start; /* Start bit position */
75222974Snp	unsigned short end;   /* End bit position */
76222974Snp	unsigned char shift;  /* # of low order bits omitted and implicitly 0 */
77222974Snp	unsigned char hex;    /* Print field in hex instead of decimal */
78222974Snp	unsigned char islog2; /* Field contains the base-2 log of the value */
79222974Snp};
80222974Snp
81222900Snp#include "reg_defs_t4.c"
82296471Snp#include "reg_defs_t5.c"
83296471Snp#include "reg_defs_t6.c"
84222900Snp#include "reg_defs_t4vf.c"
85222900Snp
86222900Snpstatic void
87222900Snpusage(FILE *fp)
88222900Snp{
89222900Snp	fprintf(fp, "Usage: %s <nexus> [operation]\n", progname);
90222900Snp	fprintf(fp,
91241416Snp	    "\tclearstats <port>                   clear port statistics\n"
92222974Snp	    "\tcontext <type> <id>                 show an SGE context\n"
93222900Snp	    "\tfilter <idx> [<param> <val>] ...    set a filter\n"
94222900Snp	    "\tfilter <idx> delete|clear           delete a filter\n"
95222900Snp	    "\tfilter list                         list all filters\n"
96222900Snp	    "\tfilter mode [<match>] ...           get/set global filter mode\n"
97241401Snp	    "\ti2c <port> <devaddr> <addr> [<len>] read from i2c device\n"
98306823Snp	    "\tloadcfg <fw-config.txt>             install configuration file\n"
99306823Snp	    "\tloadcfg clear                       remove configuration file\n"
100228594Snp	    "\tloadfw <fw-image.bin>               install firmware\n"
101228594Snp	    "\tmemdump <addr> <len>                dump a memory range\n"
102269106Snp	    "\tmodinfo <port> [raw]                optics/cable information\n"
103222900Snp	    "\treg <address>[=<val>]               read/write register\n"
104222900Snp	    "\treg64 <address>[=<val>]             read/write 64 bit register\n"
105222900Snp	    "\tregdump [<module>] ...              dump registers\n"
106259048Snp	    "\tsched-class params <param> <val> .. configure TX scheduler class\n"
107259048Snp	    "\tsched-queue <port> <queue> <class>  bind NIC queues to TX Scheduling class\n"
108222900Snp	    "\tstdio                               interactive mode\n"
109228594Snp	    "\ttcb <tid>                           read TCB\n"
110259048Snp	    "\ttracer <idx> tx<n>|rx<n>            set and enable a tracer\n"
111253691Snp	    "\ttracer <idx> disable|enable         disable or enable a tracer\n"
112253691Snp	    "\ttracer list                         list all tracers\n"
113222900Snp	    );
114222900Snp}
115222900Snp
116222900Snpstatic inline unsigned int
117222900Snpget_card_vers(unsigned int version)
118222900Snp{
119222900Snp	return (version & 0x3ff);
120222900Snp}
121222900Snp
122222900Snpstatic int
123222900Snpreal_doit(unsigned long cmd, void *data, const char *cmdstr)
124222900Snp{
125222900Snp	static int fd = -1;
126222900Snp	int rc = 0;
127222900Snp
128222900Snp	if (fd == -1) {
129222900Snp		char buf[64];
130222900Snp
131222900Snp		snprintf(buf, sizeof(buf), "/dev/%s", nexus);
132222900Snp		if ((fd = open(buf, O_RDWR)) < 0) {
133222900Snp			warn("open(%s)", nexus);
134222900Snp			rc = errno;
135222900Snp			return (rc);
136222900Snp		}
137253870Snp		chip_id = nexus[1] - '0';
138222900Snp	}
139222900Snp
140222900Snp	rc = ioctl(fd, cmd, data);
141222900Snp	if (rc < 0) {
142222900Snp		warn("%s", cmdstr);
143222900Snp		rc = errno;
144222900Snp	}
145222900Snp
146222900Snp	return (rc);
147222900Snp}
148222900Snp#define doit(x, y) real_doit(x, y, #x)
149222900Snp
150222900Snpstatic char *
151222900Snpstr_to_number(const char *s, long *val, long long *vall)
152222900Snp{
153222900Snp	char *p;
154222900Snp
155222900Snp	if (vall)
156222900Snp		*vall = strtoll(s, &p, 0);
157222900Snp	else if (val)
158222900Snp		*val = strtol(s, &p, 0);
159222900Snp	else
160222900Snp		p = NULL;
161222900Snp
162222900Snp	return (p);
163222900Snp}
164222900Snp
165222900Snpstatic int
166222900Snpread_reg(long addr, int size, long long *val)
167222900Snp{
168222900Snp	struct t4_reg reg;
169222900Snp	int rc;
170222900Snp
171222900Snp	reg.addr = (uint32_t) addr;
172222900Snp	reg.size = (uint32_t) size;
173222900Snp	reg.val = 0;
174222900Snp
175222900Snp	rc = doit(CHELSIO_T4_GETREG, &reg);
176222900Snp
177222900Snp	*val = reg.val;
178222900Snp
179222900Snp	return (rc);
180222900Snp}
181222900Snp
182222900Snpstatic int
183222900Snpwrite_reg(long addr, int size, long long val)
184222900Snp{
185222900Snp	struct t4_reg reg;
186222900Snp
187222900Snp	reg.addr = (uint32_t) addr;
188222900Snp	reg.size = (uint32_t) size;
189222900Snp	reg.val = (uint64_t) val;
190222900Snp
191222900Snp	return doit(CHELSIO_T4_SETREG, &reg);
192222900Snp}
193222900Snp
194222900Snpstatic int
195222900Snpregister_io(int argc, const char *argv[], int size)
196222900Snp{
197222900Snp	char *p, *v;
198222900Snp	long addr;
199222900Snp	long long val;
200222900Snp	int w = 0, rc;
201222900Snp
202222900Snp	if (argc == 1) {
203222900Snp		/* <reg> OR <reg>=<value> */
204222900Snp
205222900Snp		p = str_to_number(argv[0], &addr, NULL);
206222900Snp		if (*p) {
207222900Snp			if (*p != '=') {
208222900Snp				warnx("invalid register \"%s\"", argv[0]);
209222900Snp				return (EINVAL);
210222900Snp			}
211222900Snp
212222900Snp			w = 1;
213222900Snp			v = p + 1;
214222900Snp			p = str_to_number(v, NULL, &val);
215222900Snp
216222900Snp			if (*p) {
217222900Snp				warnx("invalid value \"%s\"", v);
218222900Snp				return (EINVAL);
219222900Snp			}
220222900Snp		}
221222900Snp
222222900Snp	} else if (argc == 2) {
223222900Snp		/* <reg> <value> */
224222900Snp
225222900Snp		w = 1;
226222900Snp
227222900Snp		p = str_to_number(argv[0], &addr, NULL);
228222900Snp		if (*p) {
229222900Snp			warnx("invalid register \"%s\"", argv[0]);
230222900Snp			return (EINVAL);
231222900Snp		}
232222900Snp
233222900Snp		p = str_to_number(argv[1], NULL, &val);
234222900Snp		if (*p) {
235222900Snp			warnx("invalid value \"%s\"", argv[1]);
236222900Snp			return (EINVAL);
237222900Snp		}
238222900Snp	} else {
239222900Snp		warnx("reg: invalid number of arguments (%d)", argc);
240222900Snp		return (EINVAL);
241222900Snp	}
242222900Snp
243222900Snp	if (w)
244222900Snp		rc = write_reg(addr, size, val);
245222900Snp	else {
246222900Snp		rc = read_reg(addr, size, &val);
247222900Snp		if (rc == 0)
248222900Snp			printf("0x%llx [%llu]\n", val, val);
249222900Snp	}
250222900Snp
251222900Snp	return (rc);
252222900Snp}
253222900Snp
254222900Snpstatic inline uint32_t
255222900Snpxtract(uint32_t val, int shift, int len)
256222900Snp{
257222900Snp	return (val >> shift) & ((1 << len) - 1);
258222900Snp}
259222900Snp
260222900Snpstatic int
261222900Snpdump_block_regs(const struct reg_info *reg_array, const uint32_t *regs)
262222900Snp{
263222900Snp	uint32_t reg_val = 0;
264222900Snp
265222900Snp	for ( ; reg_array->name; ++reg_array)
266222900Snp		if (!reg_array->len) {
267222900Snp			reg_val = regs[reg_array->addr / 4];
268222900Snp			printf("[%#7x] %-47s %#-10x %u\n", reg_array->addr,
269222900Snp			       reg_array->name, reg_val, reg_val);
270222900Snp		} else {
271222900Snp			uint32_t v = xtract(reg_val, reg_array->addr,
272222900Snp					    reg_array->len);
273222900Snp
274222900Snp			printf("    %*u:%u %-47s %#-10x %u\n",
275222900Snp			       reg_array->addr < 10 ? 3 : 2,
276222900Snp			       reg_array->addr + reg_array->len - 1,
277222900Snp			       reg_array->addr, reg_array->name, v, v);
278222900Snp		}
279222900Snp
280222900Snp	return (1);
281222900Snp}
282222900Snp
283222900Snpstatic int
284222900Snpdump_regs_table(int argc, const char *argv[], const uint32_t *regs,
285222900Snp    const struct mod_regs *modtab, int nmodules)
286222900Snp{
287222900Snp	int i, j, match;
288222900Snp
289222900Snp	for (i = 0; i < argc; i++) {
290222900Snp		for (j = 0; j < nmodules; j++) {
291222900Snp			if (!strcmp(argv[i], modtab[j].name))
292222900Snp				break;
293222900Snp		}
294222900Snp
295222900Snp		if (j == nmodules) {
296222900Snp			warnx("invalid register block \"%s\"", argv[i]);
297222900Snp			fprintf(stderr, "\nAvailable blocks:");
298222900Snp			for ( ; nmodules; nmodules--, modtab++)
299222900Snp				fprintf(stderr, " %s", modtab->name);
300222900Snp			fprintf(stderr, "\n");
301222900Snp			return (EINVAL);
302222900Snp		}
303222900Snp	}
304222900Snp
305222900Snp	for ( ; nmodules; nmodules--, modtab++) {
306222900Snp
307222900Snp		match = argc == 0 ? 1 : 0;
308222900Snp		for (i = 0; !match && i < argc; i++) {
309222900Snp			if (!strcmp(argv[i], modtab->name))
310222900Snp				match = 1;
311222900Snp		}
312222900Snp
313222900Snp		if (match)
314222900Snp			dump_block_regs(modtab->ri, regs);
315222900Snp	}
316222900Snp
317222900Snp	return (0);
318222900Snp}
319222900Snp
320222900Snp#define T4_MODREGS(name) { #name, t4_##name##_regs }
321222900Snpstatic int
322222900Snpdump_regs_t4(int argc, const char *argv[], const uint32_t *regs)
323222900Snp{
324222900Snp	static struct mod_regs t4_mod[] = {
325222900Snp		T4_MODREGS(sge),
326222900Snp		{ "pci", t4_pcie_regs },
327222900Snp		T4_MODREGS(dbg),
328222900Snp		T4_MODREGS(mc),
329222900Snp		T4_MODREGS(ma),
330222900Snp		{ "edc0", t4_edc_0_regs },
331222900Snp		{ "edc1", t4_edc_1_regs },
332259048Snp		T4_MODREGS(cim),
333222900Snp		T4_MODREGS(tp),
334222900Snp		T4_MODREGS(ulp_rx),
335222900Snp		T4_MODREGS(ulp_tx),
336222900Snp		{ "pmrx", t4_pm_rx_regs },
337222900Snp		{ "pmtx", t4_pm_tx_regs },
338222900Snp		T4_MODREGS(mps),
339222900Snp		{ "cplsw", t4_cpl_switch_regs },
340222900Snp		T4_MODREGS(smb),
341222900Snp		{ "i2c", t4_i2cm_regs },
342222900Snp		T4_MODREGS(mi),
343222900Snp		T4_MODREGS(uart),
344259048Snp		T4_MODREGS(pmu),
345222900Snp		T4_MODREGS(sf),
346222900Snp		T4_MODREGS(pl),
347222900Snp		T4_MODREGS(le),
348222900Snp		T4_MODREGS(ncsi),
349222900Snp		T4_MODREGS(xgmac)
350222900Snp	};
351222900Snp
352287297Srodrigc	return dump_regs_table(argc, argv, regs, t4_mod, nitems(t4_mod));
353222900Snp}
354222900Snp#undef T4_MODREGS
355222900Snp
356248925Snp#define T5_MODREGS(name) { #name, t5_##name##_regs }
357222900Snpstatic int
358248925Snpdump_regs_t5(int argc, const char *argv[], const uint32_t *regs)
359248925Snp{
360248925Snp	static struct mod_regs t5_mod[] = {
361248925Snp		T5_MODREGS(sge),
362248925Snp		{ "pci", t5_pcie_regs },
363248925Snp		T5_MODREGS(dbg),
364248925Snp		{ "mc0", t5_mc_0_regs },
365248925Snp		{ "mc1", t5_mc_1_regs },
366248925Snp		T5_MODREGS(ma),
367248925Snp		{ "edc0", t5_edc_t50_regs },
368248925Snp		{ "edc1", t5_edc_t51_regs },
369248925Snp		T5_MODREGS(cim),
370248925Snp		T5_MODREGS(tp),
371248925Snp		{ "ulprx", t5_ulp_rx_regs },
372248925Snp		{ "ulptx", t5_ulp_tx_regs },
373248925Snp		{ "pmrx", t5_pm_rx_regs },
374248925Snp		{ "pmtx", t5_pm_tx_regs },
375248925Snp		T5_MODREGS(mps),
376248925Snp		{ "cplsw", t5_cpl_switch_regs },
377248925Snp		T5_MODREGS(smb),
378248925Snp		{ "i2c", t5_i2cm_regs },
379248925Snp		T5_MODREGS(mi),
380248925Snp		T5_MODREGS(uart),
381248925Snp		T5_MODREGS(pmu),
382248925Snp		T5_MODREGS(sf),
383248925Snp		T5_MODREGS(pl),
384248925Snp		T5_MODREGS(le),
385248925Snp		T5_MODREGS(ncsi),
386248925Snp		T5_MODREGS(mac),
387248925Snp		{ "hma", t5_hma_t5_regs }
388248925Snp	};
389248925Snp
390287297Srodrigc	return dump_regs_table(argc, argv, regs, t5_mod, nitems(t5_mod));
391248925Snp}
392248925Snp#undef T5_MODREGS
393248925Snp
394296471Snp#define T6_MODREGS(name) { #name, t6_##name##_regs }
395248925Snpstatic int
396296471Snpdump_regs_t6(int argc, const char *argv[], const uint32_t *regs)
397296471Snp{
398296471Snp	static struct mod_regs t6_mod[] = {
399296471Snp		T6_MODREGS(sge),
400296471Snp		{ "pci", t6_pcie_regs },
401296471Snp		T6_MODREGS(dbg),
402296471Snp		{ "mc0", t6_mc_0_regs },
403296471Snp		T6_MODREGS(ma),
404296471Snp		{ "edc0", t6_edc_t60_regs },
405296471Snp		{ "edc1", t6_edc_t61_regs },
406296471Snp		T6_MODREGS(cim),
407296471Snp		T6_MODREGS(tp),
408296471Snp		{ "ulprx", t6_ulp_rx_regs },
409296471Snp		{ "ulptx", t6_ulp_tx_regs },
410296471Snp		{ "pmrx", t6_pm_rx_regs },
411296471Snp		{ "pmtx", t6_pm_tx_regs },
412296471Snp		T6_MODREGS(mps),
413296471Snp		{ "cplsw", t6_cpl_switch_regs },
414296471Snp		T6_MODREGS(smb),
415296471Snp		{ "i2c", t6_i2cm_regs },
416296471Snp		T6_MODREGS(mi),
417296471Snp		T6_MODREGS(uart),
418296471Snp		T6_MODREGS(pmu),
419296471Snp		T6_MODREGS(sf),
420296471Snp		T6_MODREGS(pl),
421296471Snp		T6_MODREGS(le),
422296471Snp		T6_MODREGS(ncsi),
423296471Snp		T6_MODREGS(mac),
424296471Snp		{ "hma", t6_hma_t6_regs }
425296471Snp	};
426296471Snp
427296471Snp	return dump_regs_table(argc, argv, regs, t6_mod, nitems(t6_mod));
428296471Snp}
429296471Snp#undef T6_MODREGS
430296471Snp
431296471Snpstatic int
432296471Snpdump_regs_t4vf(int argc, const char *argv[], const uint32_t *regs)
433296471Snp{
434296471Snp	static struct mod_regs t4vf_mod[] = {
435296471Snp		{ "sge", t4vf_sge_regs },
436296471Snp		{ "mps", t4vf_mps_regs },
437296471Snp		{ "pl", t4vf_pl_regs },
438296471Snp		{ "mbdata", t4vf_mbdata_regs },
439296471Snp		{ "cim", t4vf_cim_regs },
440296471Snp	};
441296471Snp
442296471Snp	return dump_regs_table(argc, argv, regs, t4vf_mod, nitems(t4vf_mod));
443296471Snp}
444296471Snp
445296471Snpstatic int
446296471Snpdump_regs_t5vf(int argc, const char *argv[], const uint32_t *regs)
447296471Snp{
448296471Snp	static struct mod_regs t5vf_mod[] = {
449296471Snp		{ "sge", t5vf_sge_regs },
450296471Snp		{ "mps", t4vf_mps_regs },
451296471Snp		{ "pl", t5vf_pl_regs },
452296471Snp		{ "mbdata", t4vf_mbdata_regs },
453296471Snp		{ "cim", t4vf_cim_regs },
454296471Snp	};
455296471Snp
456296471Snp	return dump_regs_table(argc, argv, regs, t5vf_mod, nitems(t5vf_mod));
457296471Snp}
458296471Snp
459296471Snpstatic int
460296471Snpdump_regs_t6vf(int argc, const char *argv[], const uint32_t *regs)
461296471Snp{
462296471Snp	static struct mod_regs t6vf_mod[] = {
463296471Snp		{ "sge", t5vf_sge_regs },
464296471Snp		{ "mps", t4vf_mps_regs },
465296471Snp		{ "pl", t6vf_pl_regs },
466296471Snp		{ "mbdata", t4vf_mbdata_regs },
467296471Snp		{ "cim", t4vf_cim_regs },
468296471Snp	};
469296471Snp
470296471Snp	return dump_regs_table(argc, argv, regs, t6vf_mod, nitems(t6vf_mod));
471296471Snp}
472296471Snp
473296471Snpstatic int
474222900Snpdump_regs(int argc, const char *argv[])
475222900Snp{
476248925Snp	int vers, revision, rc;
477222900Snp	struct t4_regdump regs;
478248925Snp	uint32_t len;
479222900Snp
480248925Snp	len = max(T4_REGDUMP_SIZE, T5_REGDUMP_SIZE);
481248925Snp	regs.data = calloc(1, len);
482222900Snp	if (regs.data == NULL) {
483222900Snp		warnc(ENOMEM, "regdump");
484222900Snp		return (ENOMEM);
485222900Snp	}
486222900Snp
487248925Snp	regs.len = len;
488222900Snp	rc = doit(CHELSIO_T4_REGDUMP, &regs);
489222900Snp	if (rc != 0)
490222900Snp		return (rc);
491222900Snp
492222900Snp	vers = get_card_vers(regs.version);
493222900Snp	revision = (regs.version >> 10) & 0x3f;
494222900Snp
495222900Snp	if (vers == 4) {
496222900Snp		if (revision == 0x3f)
497222900Snp			rc = dump_regs_t4vf(argc, argv, regs.data);
498222900Snp		else
499222900Snp			rc = dump_regs_t4(argc, argv, regs.data);
500296471Snp	} else if (vers == 5) {
501296471Snp		if (revision == 0x3f)
502296471Snp			rc = dump_regs_t5vf(argc, argv, regs.data);
503296471Snp		else
504296471Snp			rc = dump_regs_t5(argc, argv, regs.data);
505296471Snp	} else if (vers == 6) {
506296471Snp		if (revision == 0x3f)
507296471Snp			rc = dump_regs_t6vf(argc, argv, regs.data);
508296471Snp		else
509296471Snp			rc = dump_regs_t6(argc, argv, regs.data);
510296471Snp	} else {
511248925Snp		warnx("%s (type %d, rev %d) is not a known card.",
512222900Snp		    nexus, vers, revision);
513222900Snp		return (ENOTSUP);
514222900Snp	}
515222900Snp
516222900Snp	free(regs.data);
517222900Snp	return (rc);
518222900Snp}
519222900Snp
520222900Snpstatic void
521222900Snpdo_show_info_header(uint32_t mode)
522222900Snp{
523222900Snp	uint32_t i;
524222900Snp
525296236Snp	printf("%4s %8s", "Idx", "Hits");
526222900Snp	for (i = T4_FILTER_FCoE; i <= T4_FILTER_IP_FRAGMENT; i <<= 1) {
527222900Snp		switch (mode & i) {
528222900Snp		case T4_FILTER_FCoE:
529296236Snp			printf(" FCoE");
530222900Snp			break;
531222900Snp
532222900Snp		case T4_FILTER_PORT:
533296236Snp			printf(" Port");
534222900Snp			break;
535222900Snp
536228561Snp		case T4_FILTER_VNIC:
537296481Snp			if (mode & T4_FILTER_IC_VNIC)
538296481Snp				printf("   VFvld:PF:VF");
539296481Snp			else
540296481Snp				printf("     vld:oVLAN");
541222900Snp			break;
542222900Snp
543228561Snp		case T4_FILTER_VLAN:
544296236Snp			printf("      vld:VLAN");
545222900Snp			break;
546222900Snp
547222900Snp		case T4_FILTER_IP_TOS:
548296236Snp			printf("   TOS");
549222900Snp			break;
550222900Snp
551222900Snp		case T4_FILTER_IP_PROTO:
552296236Snp			printf("  Prot");
553222900Snp			break;
554222900Snp
555222900Snp		case T4_FILTER_ETH_TYPE:
556296236Snp			printf("   EthType");
557222900Snp			break;
558222900Snp
559222900Snp		case T4_FILTER_MAC_IDX:
560296236Snp			printf("  MACIdx");
561222900Snp			break;
562222900Snp
563222900Snp		case T4_FILTER_MPS_HIT_TYPE:
564296236Snp			printf(" MPS");
565222900Snp			break;
566222900Snp
567222900Snp		case T4_FILTER_IP_FRAGMENT:
568296236Snp			printf(" Frag");
569222900Snp			break;
570222900Snp
571222900Snp		default:
572222900Snp			/* compressed filter field not enabled */
573222900Snp			break;
574222900Snp		}
575222900Snp	}
576222900Snp	printf(" %20s %20s %9s %9s %s\n",
577222900Snp	    "DIP", "SIP", "DPORT", "SPORT", "Action");
578222900Snp}
579222900Snp
580222900Snp/*
581222900Snp * Parse an argument sub-vector as a { <parameter name> <value>[:<mask>] }
582222900Snp * ordered tuple.  If the parameter name in the argument sub-vector does not
583222900Snp * match the passed in parameter name, then a zero is returned for the
584222900Snp * function and no parsing is performed.  If there is a match, then the value
585222900Snp * and optional mask are parsed and returned in the provided return value
586222900Snp * pointers.  If no optional mask is specified, then a default mask of all 1s
587222900Snp * will be returned.
588222900Snp *
589222900Snp * An error in parsing the value[:mask] will result in an error message and
590222900Snp * program termination.
591222900Snp */
592222900Snpstatic int
593222900Snpparse_val_mask(const char *param, const char *args[], uint32_t *val,
594222900Snp    uint32_t *mask)
595222900Snp{
596222900Snp	char *p;
597222900Snp
598222900Snp	if (strcmp(param, args[0]) != 0)
599222900Snp		return (EINVAL);
600222900Snp
601222900Snp	*val = strtoul(args[1], &p, 0);
602222900Snp	if (p > args[1]) {
603222900Snp		if (p[0] == 0) {
604222900Snp			*mask = ~0;
605222900Snp			return (0);
606222900Snp		}
607222900Snp
608222900Snp		if (p[0] == ':' && p[1] != 0) {
609222900Snp			*mask = strtoul(p+1, &p, 0);
610222900Snp			if (p[0] == 0)
611222900Snp				return (0);
612222900Snp		}
613222900Snp	}
614222900Snp
615222900Snp	warnx("parameter \"%s\" has bad \"value[:mask]\" %s",
616222900Snp	    args[0], args[1]);
617222900Snp
618222900Snp	return (EINVAL);
619222900Snp}
620222900Snp
621222900Snp/*
622222900Snp * Parse an argument sub-vector as a { <parameter name> <addr>[/<mask>] }
623222900Snp * ordered tuple.  If the parameter name in the argument sub-vector does not
624222900Snp * match the passed in parameter name, then a zero is returned for the
625222900Snp * function and no parsing is performed.  If there is a match, then the value
626222900Snp * and optional mask are parsed and returned in the provided return value
627222900Snp * pointers.  If no optional mask is specified, then a default mask of all 1s
628222900Snp * will be returned.
629222900Snp *
630222900Snp * The value return parameter "afp" is used to specify the expected address
631222900Snp * family -- IPv4 or IPv6 -- of the address[/mask] and return its actual
632222900Snp * format.  A passed in value of AF_UNSPEC indicates that either IPv4 or IPv6
633222900Snp * is acceptable; AF_INET means that only IPv4 addresses are acceptable; and
634222900Snp * AF_INET6 means that only IPv6 are acceptable.  AF_INET is returned for IPv4
635222900Snp * and AF_INET6 for IPv6 addresses, respectively.  IPv4 address/mask pairs are
636222900Snp * returned in the first four bytes of the address and mask return values with
637222900Snp * the address A.B.C.D returned with { A, B, C, D } returned in addresses { 0,
638222900Snp * 1, 2, 3}, respectively.
639222900Snp *
640222900Snp * An error in parsing the value[:mask] will result in an error message and
641222900Snp * program termination.
642222900Snp */
643222900Snpstatic int
644222900Snpparse_ipaddr(const char *param, const char *args[], int *afp, uint8_t addr[],
645222900Snp    uint8_t mask[])
646222900Snp{
647222900Snp	const char *colon, *afn;
648222900Snp	char *slash;
649222900Snp	uint8_t *m;
650222900Snp	int af, ret;
651222900Snp	unsigned int masksize;
652222900Snp
653222900Snp	/*
654222900Snp	 * Is this our parameter?
655222900Snp	 */
656222900Snp	if (strcmp(param, args[0]) != 0)
657222900Snp		return (EINVAL);
658222900Snp
659222900Snp	/*
660222900Snp	 * Fundamental IPv4 versus IPv6 selection.
661222900Snp	 */
662222900Snp	colon = strchr(args[1], ':');
663222900Snp	if (!colon) {
664222900Snp		afn = "IPv4";
665222900Snp		af = AF_INET;
666222900Snp		masksize = 32;
667222900Snp	} else {
668222900Snp		afn = "IPv6";
669222900Snp		af = AF_INET6;
670222900Snp		masksize = 128;
671222900Snp	}
672222900Snp	if (*afp == AF_UNSPEC)
673222900Snp		*afp = af;
674222900Snp	else if (*afp != af) {
675222900Snp		warnx("address %s is not of expected family %s",
676222900Snp		    args[1], *afp == AF_INET ? "IP" : "IPv6");
677222900Snp		return (EINVAL);
678222900Snp	}
679222900Snp
680222900Snp	/*
681222900Snp	 * Parse address (temporarily stripping off any "/mask"
682222900Snp	 * specification).
683222900Snp	 */
684222900Snp	slash = strchr(args[1], '/');
685222900Snp	if (slash)
686222900Snp		*slash = 0;
687222900Snp	ret = inet_pton(af, args[1], addr);
688222900Snp	if (slash)
689222900Snp		*slash = '/';
690222900Snp	if (ret <= 0) {
691222900Snp		warnx("Cannot parse %s %s address %s", param, afn, args[1]);
692222900Snp		return (EINVAL);
693222900Snp	}
694222900Snp
695222900Snp	/*
696222900Snp	 * Parse optional mask specification.
697222900Snp	 */
698222900Snp	if (slash) {
699222900Snp		char *p;
700222900Snp		unsigned int prefix = strtoul(slash + 1, &p, 10);
701222900Snp
702222900Snp		if (p == slash + 1) {
703222900Snp			warnx("missing address prefix for %s", param);
704222900Snp			return (EINVAL);
705222900Snp		}
706222900Snp		if (*p) {
707222900Snp			warnx("%s is not a valid address prefix", slash + 1);
708222900Snp			return (EINVAL);
709222900Snp		}
710222900Snp		if (prefix > masksize) {
711222900Snp			warnx("prefix %u is too long for an %s address",
712222900Snp			     prefix, afn);
713222900Snp			return (EINVAL);
714222900Snp		}
715222900Snp		memset(mask, 0, masksize / 8);
716222900Snp		masksize = prefix;
717222900Snp	}
718222900Snp
719222900Snp	/*
720222900Snp	 * Fill in mask.
721222900Snp	 */
722222900Snp	for (m = mask; masksize >= 8; m++, masksize -= 8)
723222900Snp		*m = ~0;
724222900Snp	if (masksize)
725222900Snp		*m = ~0 << (8 - masksize);
726222900Snp
727222900Snp	return (0);
728222900Snp}
729222900Snp
730222900Snp/*
731222900Snp * Parse an argument sub-vector as a { <parameter name> <value> } ordered
732222900Snp * tuple.  If the parameter name in the argument sub-vector does not match the
733222900Snp * passed in parameter name, then a zero is returned for the function and no
734222900Snp * parsing is performed.  If there is a match, then the value is parsed and
735222900Snp * returned in the provided return value pointer.
736222900Snp */
737222900Snpstatic int
738222900Snpparse_val(const char *param, const char *args[], uint32_t *val)
739222900Snp{
740222900Snp	char *p;
741222900Snp
742222900Snp	if (strcmp(param, args[0]) != 0)
743222900Snp		return (EINVAL);
744222900Snp
745222900Snp	*val = strtoul(args[1], &p, 0);
746222900Snp	if (p > args[1] && p[0] == 0)
747222900Snp		return (0);
748222900Snp
749222900Snp	warnx("parameter \"%s\" has bad \"value\" %s", args[0], args[1]);
750222900Snp	return (EINVAL);
751222900Snp}
752222900Snp
753222900Snpstatic void
754222900Snpfilters_show_ipaddr(int type, uint8_t *addr, uint8_t *addrm)
755222900Snp{
756222900Snp	int noctets, octet;
757222900Snp
758222900Snp	printf(" ");
759222900Snp	if (type == 0) {
760222900Snp		noctets = 4;
761222900Snp		printf("%3s", " ");
762222900Snp	} else
763222900Snp	noctets = 16;
764222900Snp
765222900Snp	for (octet = 0; octet < noctets; octet++)
766222900Snp		printf("%02x", addr[octet]);
767222900Snp	printf("/");
768222900Snp	for (octet = 0; octet < noctets; octet++)
769222900Snp		printf("%02x", addrm[octet]);
770222900Snp}
771222900Snp
772222900Snpstatic void
773222900Snpdo_show_one_filter_info(struct t4_filter *t, uint32_t mode)
774222900Snp{
775222900Snp	uint32_t i;
776222900Snp
777222900Snp	printf("%4d", t->idx);
778222900Snp	if (t->hits == UINT64_MAX)
779222900Snp		printf(" %8s", "-");
780222900Snp	else
781222900Snp		printf(" %8ju", t->hits);
782222900Snp
783222900Snp	/*
784222900Snp	 * Compressed header portion of filter.
785222900Snp	 */
786222900Snp	for (i = T4_FILTER_FCoE; i <= T4_FILTER_IP_FRAGMENT; i <<= 1) {
787222900Snp		switch (mode & i) {
788222900Snp		case T4_FILTER_FCoE:
789222900Snp			printf("  %1d/%1d", t->fs.val.fcoe, t->fs.mask.fcoe);
790222900Snp			break;
791222900Snp
792222900Snp		case T4_FILTER_PORT:
793222900Snp			printf("  %1d/%1d", t->fs.val.iport, t->fs.mask.iport);
794222900Snp			break;
795222900Snp
796228561Snp		case T4_FILTER_VNIC:
797296481Snp			if (mode & T4_FILTER_IC_VNIC) {
798296481Snp				printf(" %1d:%1x:%02x/%1d:%1x:%02x",
799296481Snp				    t->fs.val.pfvf_vld,
800296481Snp				    (t->fs.val.vnic >> 13) & 0x7,
801296481Snp				    t->fs.val.vnic & 0x1fff,
802296481Snp				    t->fs.mask.pfvf_vld,
803296481Snp				    (t->fs.mask.vnic >> 13) & 0x7,
804296481Snp				    t->fs.mask.vnic & 0x1fff);
805296481Snp			} else {
806296481Snp				printf(" %1d:%04x/%1d:%04x",
807296481Snp				    t->fs.val.ovlan_vld, t->fs.val.vnic,
808296481Snp				    t->fs.mask.ovlan_vld, t->fs.mask.vnic);
809296481Snp			}
810222900Snp			break;
811222900Snp
812228561Snp		case T4_FILTER_VLAN:
813222900Snp			printf(" %1d:%04x/%1d:%04x",
814228561Snp			    t->fs.val.vlan_vld, t->fs.val.vlan,
815228561Snp			    t->fs.mask.vlan_vld, t->fs.mask.vlan);
816222900Snp			break;
817222900Snp
818222900Snp		case T4_FILTER_IP_TOS:
819222900Snp			printf(" %02x/%02x", t->fs.val.tos, t->fs.mask.tos);
820222900Snp			break;
821222900Snp
822222900Snp		case T4_FILTER_IP_PROTO:
823222900Snp			printf(" %02x/%02x", t->fs.val.proto, t->fs.mask.proto);
824222900Snp			break;
825222900Snp
826222900Snp		case T4_FILTER_ETH_TYPE:
827222900Snp			printf(" %04x/%04x", t->fs.val.ethtype,
828222900Snp			    t->fs.mask.ethtype);
829222900Snp			break;
830222900Snp
831222900Snp		case T4_FILTER_MAC_IDX:
832222900Snp			printf(" %03x/%03x", t->fs.val.macidx,
833222900Snp			    t->fs.mask.macidx);
834222900Snp			break;
835222900Snp
836222900Snp		case T4_FILTER_MPS_HIT_TYPE:
837222900Snp			printf(" %1x/%1x", t->fs.val.matchtype,
838222900Snp			    t->fs.mask.matchtype);
839222900Snp			break;
840222900Snp
841222900Snp		case T4_FILTER_IP_FRAGMENT:
842222900Snp			printf("  %1d/%1d", t->fs.val.frag, t->fs.mask.frag);
843222900Snp			break;
844222900Snp
845222900Snp		default:
846222900Snp			/* compressed filter field not enabled */
847222900Snp			break;
848222900Snp		}
849222900Snp	}
850222900Snp
851222900Snp	/*
852222900Snp	 * Fixed portion of filter.
853222900Snp	 */
854222900Snp	filters_show_ipaddr(t->fs.type, t->fs.val.dip, t->fs.mask.dip);
855222900Snp	filters_show_ipaddr(t->fs.type, t->fs.val.sip, t->fs.mask.sip);
856222900Snp	printf(" %04x/%04x %04x/%04x",
857222900Snp		 t->fs.val.dport, t->fs.mask.dport,
858222900Snp		 t->fs.val.sport, t->fs.mask.sport);
859222900Snp
860222900Snp	/*
861222900Snp	 * Variable length filter action.
862222900Snp	 */
863222900Snp	if (t->fs.action == FILTER_DROP)
864222900Snp		printf(" Drop");
865222900Snp	else if (t->fs.action == FILTER_SWITCH) {
866222900Snp		printf(" Switch: port=%d", t->fs.eport);
867222900Snp	if (t->fs.newdmac)
868222900Snp		printf(
869222900Snp			", dmac=%02x:%02x:%02x:%02x:%02x:%02x "
870222900Snp			", l2tidx=%d",
871222900Snp			t->fs.dmac[0], t->fs.dmac[1],
872222900Snp			t->fs.dmac[2], t->fs.dmac[3],
873222900Snp			t->fs.dmac[4], t->fs.dmac[5],
874222900Snp			t->l2tidx);
875222900Snp	if (t->fs.newsmac)
876222900Snp		printf(
877222900Snp			", smac=%02x:%02x:%02x:%02x:%02x:%02x "
878222900Snp			", smtidx=%d",
879222900Snp			t->fs.smac[0], t->fs.smac[1],
880222900Snp			t->fs.smac[2], t->fs.smac[3],
881222900Snp			t->fs.smac[4], t->fs.smac[5],
882222900Snp			t->smtidx);
883222900Snp	if (t->fs.newvlan == VLAN_REMOVE)
884222900Snp		printf(", vlan=none");
885222900Snp	else if (t->fs.newvlan == VLAN_INSERT)
886222900Snp		printf(", vlan=insert(%x)", t->fs.vlan);
887222900Snp	else if (t->fs.newvlan == VLAN_REWRITE)
888222900Snp		printf(", vlan=rewrite(%x)", t->fs.vlan);
889222900Snp	} else {
890222900Snp		printf(" Pass: Q=");
891222900Snp		if (t->fs.dirsteer == 0) {
892222900Snp			printf("RSS");
893222900Snp			if (t->fs.maskhash)
894222900Snp				printf("(TCB=hash)");
895222900Snp		} else {
896222900Snp			printf("%d", t->fs.iq);
897222900Snp			if (t->fs.dirsteerhash == 0)
898222900Snp				printf("(QID)");
899222900Snp			else
900222900Snp				printf("(hash)");
901222900Snp		}
902222900Snp	}
903222900Snp	if (t->fs.prio)
904222900Snp		printf(" Prio");
905222900Snp	if (t->fs.rpttid)
906222900Snp		printf(" RptTID");
907222900Snp	printf("\n");
908222900Snp}
909222900Snp
910222900Snpstatic int
911222900Snpshow_filters(void)
912222900Snp{
913222900Snp	uint32_t mode = 0, header = 0;
914222900Snp	struct t4_filter t;
915222900Snp	int rc;
916222900Snp
917222900Snp	/* Get the global filter mode first */
918222900Snp	rc = doit(CHELSIO_T4_GET_FILTER_MODE, &mode);
919222900Snp	if (rc != 0)
920222900Snp		return (rc);
921222900Snp
922222900Snp	t.idx = 0;
923222900Snp	for (t.idx = 0; ; t.idx++) {
924222900Snp		rc = doit(CHELSIO_T4_GET_FILTER, &t);
925222900Snp		if (rc != 0 || t.idx == 0xffffffff)
926222900Snp			break;
927222900Snp
928222900Snp		if (!header) {
929222900Snp			do_show_info_header(mode);
930222900Snp			header = 1;
931222900Snp		}
932222900Snp		do_show_one_filter_info(&t, mode);
933222900Snp	};
934222900Snp
935222900Snp	return (rc);
936222900Snp}
937222900Snp
938222900Snpstatic int
939222900Snpget_filter_mode(void)
940222900Snp{
941222900Snp	uint32_t mode = 0;
942222900Snp	int rc;
943222900Snp
944222900Snp	rc = doit(CHELSIO_T4_GET_FILTER_MODE, &mode);
945222900Snp	if (rc != 0)
946222900Snp		return (rc);
947222900Snp
948222900Snp	if (mode & T4_FILTER_IPv4)
949222900Snp		printf("ipv4 ");
950222900Snp
951222900Snp	if (mode & T4_FILTER_IPv6)
952222900Snp		printf("ipv6 ");
953222900Snp
954222900Snp	if (mode & T4_FILTER_IP_SADDR)
955222900Snp		printf("sip ");
956296236Snp
957222900Snp	if (mode & T4_FILTER_IP_DADDR)
958222900Snp		printf("dip ");
959222900Snp
960222900Snp	if (mode & T4_FILTER_IP_SPORT)
961222900Snp		printf("sport ");
962222900Snp
963222900Snp	if (mode & T4_FILTER_IP_DPORT)
964222900Snp		printf("dport ");
965222900Snp
966249368Snp	if (mode & T4_FILTER_IP_FRAGMENT)
967249368Snp		printf("frag ");
968249368Snp
969222900Snp	if (mode & T4_FILTER_MPS_HIT_TYPE)
970222900Snp		printf("matchtype ");
971222900Snp
972222900Snp	if (mode & T4_FILTER_MAC_IDX)
973222900Snp		printf("macidx ");
974222900Snp
975222900Snp	if (mode & T4_FILTER_ETH_TYPE)
976222900Snp		printf("ethtype ");
977222900Snp
978222900Snp	if (mode & T4_FILTER_IP_PROTO)
979222900Snp		printf("proto ");
980222900Snp
981222900Snp	if (mode & T4_FILTER_IP_TOS)
982222900Snp		printf("tos ");
983222900Snp
984228561Snp	if (mode & T4_FILTER_VLAN)
985228561Snp		printf("vlan ");
986222900Snp
987296481Snp	if (mode & T4_FILTER_VNIC) {
988296481Snp		if (mode & T4_FILTER_IC_VNIC)
989296481Snp			printf("vnic_id ");
990296481Snp		else
991296481Snp			printf("ovlan ");
992296481Snp	}
993222900Snp
994222900Snp	if (mode & T4_FILTER_PORT)
995222900Snp		printf("iport ");
996222900Snp
997222900Snp	if (mode & T4_FILTER_FCoE)
998222900Snp		printf("fcoe ");
999222900Snp
1000222900Snp	printf("\n");
1001222900Snp
1002222900Snp	return (0);
1003222900Snp}
1004222900Snp
1005222900Snpstatic int
1006222900Snpset_filter_mode(int argc, const char *argv[])
1007222900Snp{
1008222900Snp	uint32_t mode = 0;
1009296481Snp	int vnic = 0, ovlan = 0;
1010222900Snp
1011222900Snp	for (; argc; argc--, argv++) {
1012249368Snp		if (!strcmp(argv[0], "frag"))
1013249368Snp			mode |= T4_FILTER_IP_FRAGMENT;
1014249368Snp
1015222900Snp		if (!strcmp(argv[0], "matchtype"))
1016222900Snp			mode |= T4_FILTER_MPS_HIT_TYPE;
1017222900Snp
1018222900Snp		if (!strcmp(argv[0], "macidx"))
1019222900Snp			mode |= T4_FILTER_MAC_IDX;
1020222900Snp
1021222900Snp		if (!strcmp(argv[0], "ethtype"))
1022222900Snp			mode |= T4_FILTER_ETH_TYPE;
1023222900Snp
1024222900Snp		if (!strcmp(argv[0], "proto"))
1025222900Snp			mode |= T4_FILTER_IP_PROTO;
1026222900Snp
1027222900Snp		if (!strcmp(argv[0], "tos"))
1028222900Snp			mode |= T4_FILTER_IP_TOS;
1029222900Snp
1030228561Snp		if (!strcmp(argv[0], "vlan"))
1031228561Snp			mode |= T4_FILTER_VLAN;
1032222900Snp
1033296481Snp		if (!strcmp(argv[0], "ovlan")) {
1034228561Snp			mode |= T4_FILTER_VNIC;
1035296481Snp			ovlan++;
1036296481Snp		}
1037222900Snp
1038296481Snp		if (!strcmp(argv[0], "vnic_id")) {
1039296481Snp			mode |= T4_FILTER_VNIC;
1040296481Snp			mode |= T4_FILTER_IC_VNIC;
1041296481Snp			vnic++;
1042296481Snp		}
1043296481Snp
1044222900Snp		if (!strcmp(argv[0], "iport"))
1045222900Snp			mode |= T4_FILTER_PORT;
1046222900Snp
1047222900Snp		if (!strcmp(argv[0], "fcoe"))
1048222900Snp			mode |= T4_FILTER_FCoE;
1049222900Snp	}
1050222900Snp
1051296481Snp	if (vnic > 0 && ovlan > 0) {
1052296481Snp		warnx("\"vnic_id\" and \"ovlan\" are mutually exclusive.");
1053296481Snp		return (EINVAL);
1054296481Snp	}
1055296481Snp
1056222900Snp	return doit(CHELSIO_T4_SET_FILTER_MODE, &mode);
1057222900Snp}
1058222900Snp
1059222900Snpstatic int
1060222900Snpdel_filter(uint32_t idx)
1061222900Snp{
1062222900Snp	struct t4_filter t;
1063222900Snp
1064222900Snp	t.idx = idx;
1065222900Snp
1066222900Snp	return doit(CHELSIO_T4_DEL_FILTER, &t);
1067222900Snp}
1068222900Snp
1069222900Snpstatic int
1070222900Snpset_filter(uint32_t idx, int argc, const char *argv[])
1071222900Snp{
1072222900Snp	int af = AF_UNSPEC, start_arg = 0;
1073222900Snp	struct t4_filter t;
1074222900Snp
1075222900Snp	if (argc < 2) {
1076222900Snp		warnc(EINVAL, "%s", __func__);
1077222900Snp		return (EINVAL);
1078222900Snp	};
1079222900Snp	bzero(&t, sizeof (t));
1080222900Snp	t.idx = idx;
1081252470Snp	t.fs.hitcnts = 1;
1082222900Snp
1083222900Snp	for (start_arg = 0; start_arg + 2 <= argc; start_arg += 2) {
1084222900Snp		const char **args = &argv[start_arg];
1085222900Snp		uint32_t val, mask;
1086222900Snp
1087222900Snp		if (!strcmp(argv[start_arg], "type")) {
1088222900Snp			int newaf;
1089222900Snp			if (!strcasecmp(argv[start_arg + 1], "ipv4"))
1090222900Snp				newaf = AF_INET;
1091222900Snp			else if (!strcasecmp(argv[start_arg + 1], "ipv6"))
1092222900Snp				newaf = AF_INET6;
1093222900Snp			else {
1094222900Snp				warnx("invalid type \"%s\"; "
1095222900Snp				    "must be one of \"ipv4\" or \"ipv6\"",
1096222900Snp				    argv[start_arg + 1]);
1097222900Snp				return (EINVAL);
1098222900Snp			}
1099222900Snp
1100222900Snp			if (af != AF_UNSPEC && af != newaf) {
1101222900Snp				warnx("conflicting IPv4/IPv6 specifications.");
1102222900Snp				return (EINVAL);
1103222900Snp			}
1104222900Snp			af = newaf;
1105222900Snp		} else if (!parse_val_mask("fcoe", args, &val, &mask)) {
1106222900Snp			t.fs.val.fcoe = val;
1107222900Snp			t.fs.mask.fcoe = mask;
1108222900Snp		} else if (!parse_val_mask("iport", args, &val, &mask)) {
1109222900Snp			t.fs.val.iport = val;
1110222900Snp			t.fs.mask.iport = mask;
1111222900Snp		} else if (!parse_val_mask("ovlan", args, &val, &mask)) {
1112228561Snp			t.fs.val.vnic = val;
1113228561Snp			t.fs.mask.vnic = mask;
1114296481Snp			t.fs.val.ovlan_vld = 1;
1115296481Snp			t.fs.mask.ovlan_vld = 1;
1116245520Snp		} else if (!parse_val_mask("ivlan", args, &val, &mask)) {
1117228561Snp			t.fs.val.vlan = val;
1118228561Snp			t.fs.mask.vlan = mask;
1119228561Snp			t.fs.val.vlan_vld = 1;
1120228561Snp			t.fs.mask.vlan_vld = 1;
1121296481Snp		} else if (!parse_val_mask("pf", args, &val, &mask)) {
1122296481Snp			t.fs.val.vnic &= 0x1fff;
1123296481Snp			t.fs.val.vnic |= (val & 0x7) << 13;
1124296481Snp			t.fs.mask.vnic &= 0x1fff;
1125296481Snp			t.fs.mask.vnic |= (mask & 0x7) << 13;
1126296481Snp			t.fs.val.pfvf_vld = 1;
1127296481Snp			t.fs.mask.pfvf_vld = 1;
1128296481Snp		} else if (!parse_val_mask("vf", args, &val, &mask)) {
1129296481Snp			t.fs.val.vnic &= 0xe000;
1130296481Snp			t.fs.val.vnic |= val & 0x1fff;
1131296481Snp			t.fs.mask.vnic &= 0xe000;
1132296481Snp			t.fs.mask.vnic |= mask & 0x1fff;
1133296481Snp			t.fs.val.pfvf_vld = 1;
1134296481Snp			t.fs.mask.pfvf_vld = 1;
1135222900Snp		} else if (!parse_val_mask("tos", args, &val, &mask)) {
1136222900Snp			t.fs.val.tos = val;
1137222900Snp			t.fs.mask.tos = mask;
1138222900Snp		} else if (!parse_val_mask("proto", args, &val, &mask)) {
1139222900Snp			t.fs.val.proto = val;
1140222900Snp			t.fs.mask.proto = mask;
1141222900Snp		} else if (!parse_val_mask("ethtype", args, &val, &mask)) {
1142222900Snp			t.fs.val.ethtype = val;
1143222900Snp			t.fs.mask.ethtype = mask;
1144222900Snp		} else if (!parse_val_mask("macidx", args, &val, &mask)) {
1145222900Snp			t.fs.val.macidx = val;
1146222900Snp			t.fs.mask.macidx = mask;
1147222900Snp		} else if (!parse_val_mask("matchtype", args, &val, &mask)) {
1148222900Snp			t.fs.val.matchtype = val;
1149222900Snp			t.fs.mask.matchtype = mask;
1150222900Snp		} else if (!parse_val_mask("frag", args, &val, &mask)) {
1151222900Snp			t.fs.val.frag = val;
1152222900Snp			t.fs.mask.frag = mask;
1153222900Snp		} else if (!parse_val_mask("dport", args, &val, &mask)) {
1154222900Snp			t.fs.val.dport = val;
1155222900Snp			t.fs.mask.dport = mask;
1156222900Snp		} else if (!parse_val_mask("sport", args, &val, &mask)) {
1157222900Snp			t.fs.val.sport = val;
1158222900Snp			t.fs.mask.sport = mask;
1159222900Snp		} else if (!parse_ipaddr("dip", args, &af, t.fs.val.dip,
1160222900Snp		    t.fs.mask.dip)) {
1161222900Snp			/* nada */;
1162222900Snp		} else if (!parse_ipaddr("sip", args, &af, t.fs.val.sip,
1163222900Snp		    t.fs.mask.sip)) {
1164222900Snp			/* nada */;
1165222900Snp		} else if (!strcmp(argv[start_arg], "action")) {
1166222900Snp			if (!strcmp(argv[start_arg + 1], "pass"))
1167222900Snp				t.fs.action = FILTER_PASS;
1168222900Snp			else if (!strcmp(argv[start_arg + 1], "drop"))
1169222900Snp				t.fs.action = FILTER_DROP;
1170222900Snp			else if (!strcmp(argv[start_arg + 1], "switch"))
1171222900Snp				t.fs.action = FILTER_SWITCH;
1172222900Snp			else {
1173222900Snp				warnx("invalid action \"%s\"; must be one of"
1174222900Snp				     " \"pass\", \"drop\" or \"switch\"",
1175222900Snp				     argv[start_arg + 1]);
1176222900Snp				return (EINVAL);
1177222900Snp			}
1178222900Snp		} else if (!parse_val("hitcnts", args, &val)) {
1179222900Snp			t.fs.hitcnts = val;
1180222900Snp		} else if (!parse_val("prio", args, &val)) {
1181222900Snp			t.fs.prio = val;
1182222900Snp		} else if (!parse_val("rpttid", args, &val)) {
1183222900Snp			t.fs.rpttid = 1;
1184222900Snp		} else if (!parse_val("queue", args, &val)) {
1185222900Snp			t.fs.dirsteer = 1;
1186222900Snp			t.fs.iq = val;
1187222900Snp		} else if (!parse_val("tcbhash", args, &val)) {
1188222900Snp			t.fs.maskhash = 1;
1189222900Snp			t.fs.dirsteerhash = 1;
1190222900Snp		} else if (!parse_val("eport", args, &val)) {
1191222900Snp			t.fs.eport = val;
1192222900Snp		} else if (!strcmp(argv[start_arg], "dmac")) {
1193222900Snp			struct ether_addr *daddr;
1194222900Snp
1195222900Snp			daddr = ether_aton(argv[start_arg + 1]);
1196222900Snp			if (daddr == NULL) {
1197222900Snp				warnx("invalid dmac address \"%s\"",
1198222900Snp				    argv[start_arg + 1]);
1199222900Snp				return (EINVAL);
1200222900Snp			}
1201222900Snp			memcpy(t.fs.dmac, daddr, ETHER_ADDR_LEN);
1202222900Snp			t.fs.newdmac = 1;
1203222900Snp		} else if (!strcmp(argv[start_arg], "smac")) {
1204222900Snp			struct ether_addr *saddr;
1205222900Snp
1206222900Snp			saddr = ether_aton(argv[start_arg + 1]);
1207222900Snp			if (saddr == NULL) {
1208222900Snp				warnx("invalid smac address \"%s\"",
1209222900Snp				    argv[start_arg + 1]);
1210222900Snp				return (EINVAL);
1211222900Snp			}
1212222900Snp			memcpy(t.fs.smac, saddr, ETHER_ADDR_LEN);
1213222900Snp			t.fs.newsmac = 1;
1214222900Snp		} else if (!strcmp(argv[start_arg], "vlan")) {
1215222900Snp			char *p;
1216222900Snp			if (!strcmp(argv[start_arg + 1], "none")) {
1217222900Snp				t.fs.newvlan = VLAN_REMOVE;
1218222900Snp			} else if (argv[start_arg + 1][0] == '=') {
1219222900Snp				t.fs.newvlan = VLAN_REWRITE;
1220222900Snp			} else if (argv[start_arg + 1][0] == '+') {
1221222900Snp				t.fs.newvlan = VLAN_INSERT;
1222245520Snp			} else if (isdigit(argv[start_arg + 1][0]) &&
1223245520Snp			    !parse_val_mask("vlan", args, &val, &mask)) {
1224245520Snp				t.fs.val.vlan = val;
1225245520Snp				t.fs.mask.vlan = mask;
1226245520Snp				t.fs.val.vlan_vld = 1;
1227245520Snp				t.fs.mask.vlan_vld = 1;
1228222900Snp			} else {
1229222900Snp				warnx("unknown vlan parameter \"%s\"; must"
1230245520Snp				     " be one of \"none\", \"=<vlan>\", "
1231245520Snp				     " \"+<vlan>\", or \"<vlan>\"",
1232245520Snp				     argv[start_arg + 1]);
1233222900Snp				return (EINVAL);
1234222900Snp			}
1235222900Snp			if (t.fs.newvlan == VLAN_REWRITE ||
1236222900Snp			    t.fs.newvlan == VLAN_INSERT) {
1237222900Snp				t.fs.vlan = strtoul(argv[start_arg + 1] + 1,
1238222900Snp				    &p, 0);
1239222900Snp				if (p == argv[start_arg + 1] + 1 || p[0] != 0) {
1240222900Snp					warnx("invalid vlan \"%s\"",
1241222900Snp					     argv[start_arg + 1]);
1242222900Snp					return (EINVAL);
1243222900Snp				}
1244222900Snp			}
1245222900Snp		} else {
1246222900Snp			warnx("invalid parameter \"%s\"", argv[start_arg]);
1247222900Snp			return (EINVAL);
1248222900Snp		}
1249222900Snp	}
1250222900Snp	if (start_arg != argc) {
1251222900Snp		warnx("no value for \"%s\"", argv[start_arg]);
1252222900Snp		return (EINVAL);
1253222900Snp	}
1254222900Snp
1255222900Snp	/*
1256222900Snp	 * Check basic sanity of option combinations.
1257222900Snp	 */
1258222900Snp	if (t.fs.action != FILTER_SWITCH &&
1259222900Snp	    (t.fs.eport || t.fs.newdmac || t.fs.newsmac || t.fs.newvlan)) {
1260222900Snp		warnx("prio, port dmac, smac and vlan only make sense with"
1261222900Snp		     " \"action switch\"");
1262222900Snp		return (EINVAL);
1263222900Snp	}
1264222900Snp	if (t.fs.action != FILTER_PASS &&
1265222900Snp	    (t.fs.rpttid || t.fs.dirsteer || t.fs.maskhash)) {
1266222900Snp		warnx("rpttid, queue and tcbhash don't make sense with"
1267222900Snp		     " action \"drop\" or \"switch\"");
1268222900Snp		return (EINVAL);
1269222900Snp	}
1270296481Snp	if (t.fs.val.ovlan_vld && t.fs.val.pfvf_vld) {
1271296481Snp		warnx("ovlan and vnic_id (pf/vf) are mutually exclusive");
1272296481Snp		return (EINVAL);
1273296481Snp	}
1274222900Snp
1275222900Snp	t.fs.type = (af == AF_INET6 ? 1 : 0); /* default IPv4 */
1276222900Snp	return doit(CHELSIO_T4_SET_FILTER, &t);
1277222900Snp}
1278222900Snp
1279222900Snpstatic int
1280222900Snpfilter_cmd(int argc, const char *argv[])
1281222900Snp{
1282222900Snp	long long val;
1283222900Snp	uint32_t idx;
1284222900Snp	char *s;
1285222900Snp
1286222900Snp	if (argc == 0) {
1287222900Snp		warnx("filter: no arguments.");
1288222900Snp		return (EINVAL);
1289222900Snp	};
1290222900Snp
1291222900Snp	/* list */
1292222900Snp	if (strcmp(argv[0], "list") == 0) {
1293222900Snp		if (argc != 1)
1294222900Snp			warnx("trailing arguments after \"list\" ignored.");
1295222900Snp
1296222900Snp		return show_filters();
1297222900Snp	}
1298222900Snp
1299222900Snp	/* mode */
1300222900Snp	if (argc == 1 && strcmp(argv[0], "mode") == 0)
1301222900Snp		return get_filter_mode();
1302222900Snp
1303222900Snp	/* mode <mode> */
1304222900Snp	if (strcmp(argv[0], "mode") == 0)
1305222900Snp		return set_filter_mode(argc - 1, argv + 1);
1306222900Snp
1307222900Snp	/* <idx> ... */
1308222900Snp	s = str_to_number(argv[0], NULL, &val);
1309222900Snp	if (*s || val > 0xffffffffU) {
1310222900Snp		warnx("\"%s\" is neither an index nor a filter subcommand.",
1311222900Snp		    argv[0]);
1312222900Snp		return (EINVAL);
1313222900Snp	}
1314222900Snp	idx = (uint32_t) val;
1315222900Snp
1316222900Snp	/* <idx> delete|clear */
1317222900Snp	if (argc == 2 &&
1318222900Snp	    (strcmp(argv[1], "delete") == 0 || strcmp(argv[1], "clear") == 0)) {
1319222900Snp		return del_filter(idx);
1320222900Snp	}
1321222900Snp
1322222900Snp	/* <idx> [<param> <val>] ... */
1323222900Snp	return set_filter(idx, argc - 1, argv + 1);
1324222900Snp}
1325222900Snp
1326222974Snp/*
1327222974Snp * Shows the fields of a multi-word structure.  The structure is considered to
1328222974Snp * consist of @nwords 32-bit words (i.e, it's an (@nwords * 32)-bit structure)
1329222974Snp * whose fields are described by @fd.  The 32-bit words are given in @words
1330222974Snp * starting with the least significant 32-bit word.
1331222974Snp */
1332222974Snpstatic void
1333222974Snpshow_struct(const uint32_t *words, int nwords, const struct field_desc *fd)
1334222974Snp{
1335222974Snp	unsigned int w = 0;
1336222974Snp	const struct field_desc *p;
1337222974Snp
1338222974Snp	for (p = fd; p->name; p++)
1339222974Snp		w = max(w, strlen(p->name));
1340222974Snp
1341222974Snp	while (fd->name) {
1342222974Snp		unsigned long long data;
1343222974Snp		int first_word = fd->start / 32;
1344222974Snp		int shift = fd->start % 32;
1345222974Snp		int width = fd->end - fd->start + 1;
1346222974Snp		unsigned long long mask = (1ULL << width) - 1;
1347222974Snp
1348222974Snp		data = (words[first_word] >> shift) |
1349222974Snp		       ((uint64_t)words[first_word + 1] << (32 - shift));
1350222974Snp		if (shift)
1351222974Snp		       data |= ((uint64_t)words[first_word + 2] << (64 - shift));
1352222974Snp		data &= mask;
1353222974Snp		if (fd->islog2)
1354222974Snp			data = 1 << data;
1355222974Snp		printf("%-*s ", w, fd->name);
1356222974Snp		printf(fd->hex ? "%#llx\n" : "%llu\n", data << fd->shift);
1357222974Snp		fd++;
1358222974Snp	}
1359222974Snp}
1360222974Snp
1361222974Snp#define FIELD(name, start, end) { name, start, end, 0, 0, 0 }
1362222974Snp#define FIELD1(name, start) FIELD(name, start, start)
1363222974Snp
1364222974Snpstatic void
1365306137Snpshow_t5t6_ctxt(const struct t4_sge_context *p, int vers)
1366222974Snp{
1367284984Snp	static struct field_desc egress_t5[] = {
1368284984Snp		FIELD("DCA_ST:", 181, 191),
1369222974Snp		FIELD1("StatusPgNS:", 180),
1370222974Snp		FIELD1("StatusPgRO:", 179),
1371222974Snp		FIELD1("FetchNS:", 178),
1372222974Snp		FIELD1("FetchRO:", 177),
1373222974Snp		FIELD1("Valid:", 176),
1374222974Snp		FIELD("PCIeDataChannel:", 174, 175),
1375284984Snp		FIELD1("StatusPgTPHintEn:", 173),
1376284984Snp		FIELD("StatusPgTPHint:", 171, 172),
1377284984Snp		FIELD1("FetchTPHintEn:", 170),
1378284984Snp		FIELD("FetchTPHint:", 168, 169),
1379284984Snp		FIELD1("FCThreshOverride:", 167),
1380284984Snp		{ "WRLength:", 162, 166, 9, 0, 1 },
1381284984Snp		FIELD1("WRLengthKnown:", 161),
1382284984Snp		FIELD1("ReschedulePending:", 160),
1383284984Snp		FIELD1("OnChipQueue:", 159),
1384284984Snp		FIELD1("FetchSizeMode:", 158),
1385284984Snp		{ "FetchBurstMin:", 156, 157, 4, 0, 1 },
1386284984Snp		FIELD1("FLMPacking:", 155),
1387284984Snp		FIELD("FetchBurstMax:", 153, 154),
1388284984Snp		FIELD("uPToken:", 133, 152),
1389284984Snp		FIELD1("uPTokenEn:", 132),
1390284984Snp		FIELD1("UserModeIO:", 131),
1391284984Snp		FIELD("uPFLCredits:", 123, 130),
1392284984Snp		FIELD1("uPFLCreditEn:", 122),
1393284984Snp		FIELD("FID:", 111, 121),
1394284984Snp		FIELD("HostFCMode:", 109, 110),
1395284984Snp		FIELD1("HostFCOwner:", 108),
1396284984Snp		{ "CIDXFlushThresh:", 105, 107, 0, 0, 1 },
1397284984Snp		FIELD("CIDX:", 89, 104),
1398284984Snp		FIELD("PIDX:", 73, 88),
1399284984Snp		{ "BaseAddress:", 18, 72, 9, 1 },
1400284984Snp		FIELD("QueueSize:", 2, 17),
1401284984Snp		FIELD1("QueueType:", 1),
1402284984Snp		FIELD1("CachePriority:", 0),
1403284984Snp		{ NULL }
1404284984Snp	};
1405306137Snp	static struct field_desc egress_t6[] = {
1406306137Snp		FIELD("DCA_ST:", 181, 191),
1407306137Snp		FIELD1("StatusPgNS:", 180),
1408306137Snp		FIELD1("StatusPgRO:", 179),
1409306137Snp		FIELD1("FetchNS:", 178),
1410306137Snp		FIELD1("FetchRO:", 177),
1411306137Snp		FIELD1("Valid:", 176),
1412306137Snp		FIELD1("ReschedulePending_1:", 175),
1413306137Snp		FIELD1("PCIeDataChannel:", 174),
1414306137Snp		FIELD1("StatusPgTPHintEn:", 173),
1415306137Snp		FIELD("StatusPgTPHint:", 171, 172),
1416306137Snp		FIELD1("FetchTPHintEn:", 170),
1417306137Snp		FIELD("FetchTPHint:", 168, 169),
1418306137Snp		FIELD1("FCThreshOverride:", 167),
1419306137Snp		{ "WRLength:", 162, 166, 9, 0, 1 },
1420306137Snp		FIELD1("WRLengthKnown:", 161),
1421306137Snp		FIELD1("ReschedulePending:", 160),
1422306137Snp		FIELD("TimerIx:", 157, 159),
1423306137Snp		FIELD1("FetchBurstMin:", 156),
1424306137Snp		FIELD1("FLMPacking:", 155),
1425306137Snp		FIELD("FetchBurstMax:", 153, 154),
1426306137Snp		FIELD("uPToken:", 133, 152),
1427306137Snp		FIELD1("uPTokenEn:", 132),
1428306137Snp		FIELD1("UserModeIO:", 131),
1429306137Snp		FIELD("uPFLCredits:", 123, 130),
1430306137Snp		FIELD1("uPFLCreditEn:", 122),
1431306137Snp		FIELD("FID:", 111, 121),
1432306137Snp		FIELD("HostFCMode:", 109, 110),
1433306137Snp		FIELD1("HostFCOwner:", 108),
1434306137Snp		{ "CIDXFlushThresh:", 105, 107, 0, 0, 1 },
1435306137Snp		FIELD("CIDX:", 89, 104),
1436306137Snp		FIELD("PIDX:", 73, 88),
1437306137Snp		{ "BaseAddress:", 18, 72, 9, 1 },
1438306137Snp		FIELD("QueueSize:", 2, 17),
1439306137Snp		FIELD1("QueueType:", 1),
1440306137Snp		FIELD1("FetchSizeMode:", 0),
1441306137Snp		{ NULL }
1442306137Snp	};
1443284984Snp	static struct field_desc fl_t5[] = {
1444284984Snp		FIELD("DCA_ST:", 181, 191),
1445284984Snp		FIELD1("StatusPgNS:", 180),
1446284984Snp		FIELD1("StatusPgRO:", 179),
1447284984Snp		FIELD1("FetchNS:", 178),
1448284984Snp		FIELD1("FetchRO:", 177),
1449284984Snp		FIELD1("Valid:", 176),
1450284984Snp		FIELD("PCIeDataChannel:", 174, 175),
1451284984Snp		FIELD1("StatusPgTPHintEn:", 173),
1452284984Snp		FIELD("StatusPgTPHint:", 171, 172),
1453284984Snp		FIELD1("FetchTPHintEn:", 170),
1454284984Snp		FIELD("FetchTPHint:", 168, 169),
1455284984Snp		FIELD1("FCThreshOverride:", 167),
1456284984Snp		FIELD1("ReschedulePending:", 160),
1457284984Snp		FIELD1("OnChipQueue:", 159),
1458284984Snp		FIELD1("FetchSizeMode:", 158),
1459284984Snp		{ "FetchBurstMin:", 156, 157, 4, 0, 1 },
1460284984Snp		FIELD1("FLMPacking:", 155),
1461284984Snp		FIELD("FetchBurstMax:", 153, 154),
1462284984Snp		FIELD1("FLMcongMode:", 152),
1463284984Snp		FIELD("MaxuPFLCredits:", 144, 151),
1464284984Snp		FIELD("FLMcontextID:", 133, 143),
1465284984Snp		FIELD1("uPTokenEn:", 132),
1466284984Snp		FIELD1("UserModeIO:", 131),
1467284984Snp		FIELD("uPFLCredits:", 123, 130),
1468284984Snp		FIELD1("uPFLCreditEn:", 122),
1469284984Snp		FIELD("FID:", 111, 121),
1470284984Snp		FIELD("HostFCMode:", 109, 110),
1471284984Snp		FIELD1("HostFCOwner:", 108),
1472284984Snp		{ "CIDXFlushThresh:", 105, 107, 0, 0, 1 },
1473284984Snp		FIELD("CIDX:", 89, 104),
1474284984Snp		FIELD("PIDX:", 73, 88),
1475284984Snp		{ "BaseAddress:", 18, 72, 9, 1 },
1476284984Snp		FIELD("QueueSize:", 2, 17),
1477284984Snp		FIELD1("QueueType:", 1),
1478284984Snp		FIELD1("CachePriority:", 0),
1479284984Snp		{ NULL }
1480284984Snp	};
1481284984Snp	static struct field_desc ingress_t5[] = {
1482284984Snp		FIELD("DCA_ST:", 143, 153),
1483284984Snp		FIELD1("ISCSICoalescing:", 142),
1484284984Snp		FIELD1("Queue_Valid:", 141),
1485284984Snp		FIELD1("TimerPending:", 140),
1486284984Snp		FIELD1("DropRSS:", 139),
1487284984Snp		FIELD("PCIeChannel:", 137, 138),
1488284984Snp		FIELD1("SEInterruptArmed:", 136),
1489284984Snp		FIELD1("CongestionMgtEnable:", 135),
1490284984Snp		FIELD1("NoSnoop:", 134),
1491284984Snp		FIELD1("RelaxedOrdering:", 133),
1492284984Snp		FIELD1("GTSmode:", 132),
1493284984Snp		FIELD1("TPHintEn:", 131),
1494284984Snp		FIELD("TPHint:", 129, 130),
1495284984Snp		FIELD1("UpdateScheduling:", 128),
1496284984Snp		FIELD("UpdateDelivery:", 126, 127),
1497284984Snp		FIELD1("InterruptSent:", 125),
1498284984Snp		FIELD("InterruptIDX:", 114, 124),
1499284984Snp		FIELD1("InterruptDestination:", 113),
1500284984Snp		FIELD1("InterruptArmed:", 112),
1501284984Snp		FIELD("RxIntCounter:", 106, 111),
1502284984Snp		FIELD("RxIntCounterThreshold:", 104, 105),
1503284984Snp		FIELD1("Generation:", 103),
1504284984Snp		{ "BaseAddress:", 48, 102, 9, 1 },
1505284984Snp		FIELD("PIDX:", 32, 47),
1506284984Snp		FIELD("CIDX:", 16, 31),
1507284984Snp		{ "QueueSize:", 4, 15, 4, 0 },
1508284984Snp		{ "QueueEntrySize:", 2, 3, 4, 0, 1 },
1509284984Snp		FIELD1("QueueEntryOverride:", 1),
1510284984Snp		FIELD1("CachePriority:", 0),
1511284984Snp		{ NULL }
1512284984Snp	};
1513306137Snp	static struct field_desc ingress_t6[] = {
1514306137Snp		FIELD1("SP_NS:", 158),
1515306137Snp		FIELD1("SP_RO:", 157),
1516306137Snp		FIELD1("SP_TPHintEn:", 156),
1517306137Snp		FIELD("SP_TPHint:", 154, 155),
1518306137Snp		FIELD("DCA_ST:", 143, 153),
1519306137Snp		FIELD1("ISCSICoalescing:", 142),
1520306137Snp		FIELD1("Queue_Valid:", 141),
1521306137Snp		FIELD1("TimerPending:", 140),
1522306137Snp		FIELD1("DropRSS:", 139),
1523306137Snp		FIELD("PCIeChannel:", 137, 138),
1524306137Snp		FIELD1("SEInterruptArmed:", 136),
1525306137Snp		FIELD1("CongestionMgtEnable:", 135),
1526306137Snp		FIELD1("NoSnoop:", 134),
1527306137Snp		FIELD1("RelaxedOrdering:", 133),
1528306137Snp		FIELD1("GTSmode:", 132),
1529306137Snp		FIELD1("TPHintEn:", 131),
1530306137Snp		FIELD("TPHint:", 129, 130),
1531306137Snp		FIELD1("UpdateScheduling:", 128),
1532306137Snp		FIELD("UpdateDelivery:", 126, 127),
1533306137Snp		FIELD1("InterruptSent:", 125),
1534306137Snp		FIELD("InterruptIDX:", 114, 124),
1535306137Snp		FIELD1("InterruptDestination:", 113),
1536306137Snp		FIELD1("InterruptArmed:", 112),
1537306137Snp		FIELD("RxIntCounter:", 106, 111),
1538306137Snp		FIELD("RxIntCounterThreshold:", 104, 105),
1539306137Snp		FIELD1("Generation:", 103),
1540306137Snp		{ "BaseAddress:", 48, 102, 9, 1 },
1541306137Snp		FIELD("PIDX:", 32, 47),
1542306137Snp		FIELD("CIDX:", 16, 31),
1543306137Snp		{ "QueueSize:", 4, 15, 4, 0 },
1544306137Snp		{ "QueueEntrySize:", 2, 3, 4, 0, 1 },
1545306137Snp		FIELD1("QueueEntryOverride:", 1),
1546306137Snp		FIELD1("CachePriority:", 0),
1547306137Snp		{ NULL }
1548306137Snp	};
1549284984Snp	static struct field_desc flm_t5[] = {
1550284984Snp		FIELD1("Valid:", 89),
1551284984Snp		FIELD("SplitLenMode:", 87, 88),
1552284984Snp		FIELD1("TPHintEn:", 86),
1553284984Snp		FIELD("TPHint:", 84, 85),
1554284984Snp		FIELD1("NoSnoop:", 83),
1555284984Snp		FIELD1("RelaxedOrdering:", 82),
1556284984Snp		FIELD("DCA_ST:", 71, 81),
1557284984Snp		FIELD("EQid:", 54, 70),
1558284984Snp		FIELD("SplitEn:", 52, 53),
1559284984Snp		FIELD1("PadEn:", 51),
1560284984Snp		FIELD1("PackEn:", 50),
1561284984Snp		FIELD1("Cache_Lock :", 49),
1562284984Snp		FIELD1("CongDrop:", 48),
1563284984Snp		FIELD("PackOffset:", 16, 47),
1564284984Snp		FIELD("CIDX:", 8, 15),
1565284984Snp		FIELD("PIDX:", 0, 7),
1566284984Snp		{ NULL }
1567284984Snp	};
1568306137Snp	static struct field_desc flm_t6[] = {
1569306137Snp		FIELD1("Valid:", 89),
1570306137Snp		FIELD("SplitLenMode:", 87, 88),
1571306137Snp		FIELD1("TPHintEn:", 86),
1572306137Snp		FIELD("TPHint:", 84, 85),
1573306137Snp		FIELD1("NoSnoop:", 83),
1574306137Snp		FIELD1("RelaxedOrdering:", 82),
1575306137Snp		FIELD("DCA_ST:", 71, 81),
1576306137Snp		FIELD("EQid:", 54, 70),
1577306137Snp		FIELD("SplitEn:", 52, 53),
1578306137Snp		FIELD1("PadEn:", 51),
1579306137Snp		FIELD1("PackEn:", 50),
1580306137Snp		FIELD1("Cache_Lock :", 49),
1581306137Snp		FIELD1("CongDrop:", 48),
1582306138Snp		FIELD1("Inflight:", 47),
1583306137Snp		FIELD1("CongEn:", 46),
1584306137Snp		FIELD1("CongMode:", 45),
1585306137Snp		FIELD("PackOffset:", 20, 39),
1586306137Snp		FIELD("CIDX:", 8, 15),
1587306137Snp		FIELD("PIDX:", 0, 7),
1588306137Snp		{ NULL }
1589306137Snp	};
1590284984Snp	static struct field_desc conm_t5[] = {
1591284984Snp		FIELD1("CngMPSEnable:", 21),
1592284984Snp		FIELD("CngTPMode:", 19, 20),
1593284984Snp		FIELD1("CngDBPHdr:", 18),
1594284984Snp		FIELD1("CngDBPData:", 17),
1595284984Snp		FIELD1("CngIMSG:", 16),
1596284984Snp		{ "CngChMap:", 0, 15, 0, 1, 0 },
1597284984Snp		{ NULL }
1598284984Snp	};
1599284984Snp
1600306137Snp	if (p->mem_id == SGE_CONTEXT_EGRESS) {
1601306137Snp		if (p->data[0] & 2)
1602306137Snp			show_struct(p->data, 6, fl_t5);
1603306137Snp		else if (vers == 5)
1604306137Snp			show_struct(p->data, 6, egress_t5);
1605306137Snp		else
1606306137Snp			show_struct(p->data, 6, egress_t6);
1607306137Snp	} else if (p->mem_id == SGE_CONTEXT_FLM)
1608306137Snp		show_struct(p->data, 3, vers == 5 ? flm_t5 : flm_t6);
1609284984Snp	else if (p->mem_id == SGE_CONTEXT_INGRESS)
1610306137Snp		show_struct(p->data, 5, vers == 5 ? ingress_t5 : ingress_t6);
1611284984Snp	else if (p->mem_id == SGE_CONTEXT_CNM)
1612284984Snp		show_struct(p->data, 1, conm_t5);
1613284984Snp}
1614284984Snp
1615284984Snpstatic void
1616284984Snpshow_t4_ctxt(const struct t4_sge_context *p)
1617284984Snp{
1618284984Snp	static struct field_desc egress_t4[] = {
1619284984Snp		FIELD1("StatusPgNS:", 180),
1620284984Snp		FIELD1("StatusPgRO:", 179),
1621284984Snp		FIELD1("FetchNS:", 178),
1622284984Snp		FIELD1("FetchRO:", 177),
1623284984Snp		FIELD1("Valid:", 176),
1624284984Snp		FIELD("PCIeDataChannel:", 174, 175),
1625222974Snp		FIELD1("DCAEgrQEn:", 173),
1626222974Snp		FIELD("DCACPUID:", 168, 172),
1627222974Snp		FIELD1("FCThreshOverride:", 167),
1628222974Snp		FIELD("WRLength:", 162, 166),
1629222974Snp		FIELD1("WRLengthKnown:", 161),
1630222974Snp		FIELD1("ReschedulePending:", 160),
1631222974Snp		FIELD1("OnChipQueue:", 159),
1632222974Snp		FIELD1("FetchSizeMode", 158),
1633222974Snp		{ "FetchBurstMin:", 156, 157, 4, 0, 1 },
1634222974Snp		{ "FetchBurstMax:", 153, 154, 6, 0, 1 },
1635222974Snp		FIELD("uPToken:", 133, 152),
1636222974Snp		FIELD1("uPTokenEn:", 132),
1637222974Snp		FIELD1("UserModeIO:", 131),
1638222974Snp		FIELD("uPFLCredits:", 123, 130),
1639222974Snp		FIELD1("uPFLCreditEn:", 122),
1640222974Snp		FIELD("FID:", 111, 121),
1641222974Snp		FIELD("HostFCMode:", 109, 110),
1642222974Snp		FIELD1("HostFCOwner:", 108),
1643222974Snp		{ "CIDXFlushThresh:", 105, 107, 0, 0, 1 },
1644222974Snp		FIELD("CIDX:", 89, 104),
1645222974Snp		FIELD("PIDX:", 73, 88),
1646222974Snp		{ "BaseAddress:", 18, 72, 9, 1 },
1647222974Snp		FIELD("QueueSize:", 2, 17),
1648222974Snp		FIELD1("QueueType:", 1),
1649222974Snp		FIELD1("CachePriority:", 0),
1650222974Snp		{ NULL }
1651222974Snp	};
1652284984Snp	static struct field_desc fl_t4[] = {
1653222974Snp		FIELD1("StatusPgNS:", 180),
1654222974Snp		FIELD1("StatusPgRO:", 179),
1655222974Snp		FIELD1("FetchNS:", 178),
1656222974Snp		FIELD1("FetchRO:", 177),
1657222974Snp		FIELD1("Valid:", 176),
1658222974Snp		FIELD("PCIeDataChannel:", 174, 175),
1659222974Snp		FIELD1("DCAEgrQEn:", 173),
1660222974Snp		FIELD("DCACPUID:", 168, 172),
1661222974Snp		FIELD1("FCThreshOverride:", 167),
1662222974Snp		FIELD1("ReschedulePending:", 160),
1663222974Snp		FIELD1("OnChipQueue:", 159),
1664222974Snp		FIELD1("FetchSizeMode", 158),
1665222974Snp		{ "FetchBurstMin:", 156, 157, 4, 0, 1 },
1666222974Snp		{ "FetchBurstMax:", 153, 154, 6, 0, 1 },
1667222974Snp		FIELD1("FLMcongMode:", 152),
1668222974Snp		FIELD("MaxuPFLCredits:", 144, 151),
1669222974Snp		FIELD("FLMcontextID:", 133, 143),
1670222974Snp		FIELD1("uPTokenEn:", 132),
1671222974Snp		FIELD1("UserModeIO:", 131),
1672222974Snp		FIELD("uPFLCredits:", 123, 130),
1673222974Snp		FIELD1("uPFLCreditEn:", 122),
1674222974Snp		FIELD("FID:", 111, 121),
1675222974Snp		FIELD("HostFCMode:", 109, 110),
1676222974Snp		FIELD1("HostFCOwner:", 108),
1677222974Snp		{ "CIDXFlushThresh:", 105, 107, 0, 0, 1 },
1678222974Snp		FIELD("CIDX:", 89, 104),
1679222974Snp		FIELD("PIDX:", 73, 88),
1680222974Snp		{ "BaseAddress:", 18, 72, 9, 1 },
1681222974Snp		FIELD("QueueSize:", 2, 17),
1682222974Snp		FIELD1("QueueType:", 1),
1683222974Snp		FIELD1("CachePriority:", 0),
1684222974Snp		{ NULL }
1685222974Snp	};
1686284984Snp	static struct field_desc ingress_t4[] = {
1687222974Snp		FIELD1("NoSnoop:", 145),
1688222974Snp		FIELD1("RelaxedOrdering:", 144),
1689222974Snp		FIELD1("GTSmode:", 143),
1690222974Snp		FIELD1("ISCSICoalescing:", 142),
1691222974Snp		FIELD1("Valid:", 141),
1692222974Snp		FIELD1("TimerPending:", 140),
1693222974Snp		FIELD1("DropRSS:", 139),
1694222974Snp		FIELD("PCIeChannel:", 137, 138),
1695222974Snp		FIELD1("SEInterruptArmed:", 136),
1696222974Snp		FIELD1("CongestionMgtEnable:", 135),
1697222974Snp		FIELD1("DCAIngQEnable:", 134),
1698222974Snp		FIELD("DCACPUID:", 129, 133),
1699222974Snp		FIELD1("UpdateScheduling:", 128),
1700222974Snp		FIELD("UpdateDelivery:", 126, 127),
1701222974Snp		FIELD1("InterruptSent:", 125),
1702222974Snp		FIELD("InterruptIDX:", 114, 124),
1703222974Snp		FIELD1("InterruptDestination:", 113),
1704222974Snp		FIELD1("InterruptArmed:", 112),
1705222974Snp		FIELD("RxIntCounter:", 106, 111),
1706222974Snp		FIELD("RxIntCounterThreshold:", 104, 105),
1707222974Snp		FIELD1("Generation:", 103),
1708222974Snp		{ "BaseAddress:", 48, 102, 9, 1 },
1709222974Snp		FIELD("PIDX:", 32, 47),
1710222974Snp		FIELD("CIDX:", 16, 31),
1711222974Snp		{ "QueueSize:", 4, 15, 4, 0 },
1712222974Snp		{ "QueueEntrySize:", 2, 3, 4, 0, 1 },
1713222974Snp		FIELD1("QueueEntryOverride:", 1),
1714222974Snp		FIELD1("CachePriority:", 0),
1715222974Snp		{ NULL }
1716222974Snp	};
1717284984Snp	static struct field_desc flm_t4[] = {
1718222974Snp		FIELD1("NoSnoop:", 79),
1719222974Snp		FIELD1("RelaxedOrdering:", 78),
1720222974Snp		FIELD1("Valid:", 77),
1721222974Snp		FIELD("DCACPUID:", 72, 76),
1722222974Snp		FIELD1("DCAFLEn:", 71),
1723222974Snp		FIELD("EQid:", 54, 70),
1724222974Snp		FIELD("SplitEn:", 52, 53),
1725222974Snp		FIELD1("PadEn:", 51),
1726222974Snp		FIELD1("PackEn:", 50),
1727222974Snp		FIELD1("DBpriority:", 48),
1728222974Snp		FIELD("PackOffset:", 16, 47),
1729222974Snp		FIELD("CIDX:", 8, 15),
1730222974Snp		FIELD("PIDX:", 0, 7),
1731222974Snp		{ NULL }
1732222974Snp	};
1733284984Snp	static struct field_desc conm_t4[] = {
1734222974Snp		FIELD1("CngDBPHdr:", 6),
1735222974Snp		FIELD1("CngDBPData:", 5),
1736222974Snp		FIELD1("CngIMSG:", 4),
1737261534Snp		{ "CngChMap:", 0, 3, 0, 1, 0},
1738222974Snp		{ NULL }
1739222974Snp	};
1740222974Snp
1741222974Snp	if (p->mem_id == SGE_CONTEXT_EGRESS)
1742284984Snp		show_struct(p->data, 6, (p->data[0] & 2) ? fl_t4 : egress_t4);
1743222974Snp	else if (p->mem_id == SGE_CONTEXT_FLM)
1744284984Snp		show_struct(p->data, 3, flm_t4);
1745222974Snp	else if (p->mem_id == SGE_CONTEXT_INGRESS)
1746284984Snp		show_struct(p->data, 5, ingress_t4);
1747222974Snp	else if (p->mem_id == SGE_CONTEXT_CNM)
1748284984Snp		show_struct(p->data, 1, conm_t4);
1749222974Snp}
1750222974Snp
1751222974Snp#undef FIELD
1752222974Snp#undef FIELD1
1753222974Snp
1754222900Snpstatic int
1755222974Snpget_sge_context(int argc, const char *argv[])
1756222974Snp{
1757222974Snp	int rc;
1758222974Snp	char *p;
1759222974Snp	long cid;
1760222974Snp	struct t4_sge_context cntxt = {0};
1761222974Snp
1762222974Snp	if (argc != 2) {
1763222974Snp		warnx("sge_context: incorrect number of arguments.");
1764222974Snp		return (EINVAL);
1765222974Snp	}
1766222974Snp
1767222974Snp	if (!strcmp(argv[0], "egress"))
1768222974Snp		cntxt.mem_id = SGE_CONTEXT_EGRESS;
1769222974Snp	else if (!strcmp(argv[0], "ingress"))
1770222974Snp		cntxt.mem_id = SGE_CONTEXT_INGRESS;
1771222974Snp	else if (!strcmp(argv[0], "fl"))
1772222974Snp		cntxt.mem_id = SGE_CONTEXT_FLM;
1773222974Snp	else if (!strcmp(argv[0], "cong"))
1774222974Snp		cntxt.mem_id = SGE_CONTEXT_CNM;
1775222974Snp	else {
1776222974Snp		warnx("unknown context type \"%s\"; known types are egress, "
1777222974Snp		    "ingress, fl, and cong.", argv[0]);
1778222974Snp		return (EINVAL);
1779222974Snp	}
1780222974Snp
1781222974Snp	p = str_to_number(argv[1], &cid, NULL);
1782222974Snp	if (*p) {
1783222974Snp		warnx("invalid context id \"%s\"", argv[1]);
1784222974Snp		return (EINVAL);
1785222974Snp	}
1786222974Snp	cntxt.cid = cid;
1787222974Snp
1788222974Snp	rc = doit(CHELSIO_T4_GET_SGE_CONTEXT, &cntxt);
1789222974Snp	if (rc != 0)
1790222974Snp		return (rc);
1791222974Snp
1792284984Snp	if (chip_id == 4)
1793284984Snp		show_t4_ctxt(&cntxt);
1794284984Snp	else
1795306137Snp		show_t5t6_ctxt(&cntxt, chip_id);
1796284984Snp
1797222974Snp	return (0);
1798222974Snp}
1799222974Snp
1800222974Snpstatic int
1801228594Snploadfw(int argc, const char *argv[])
1802228594Snp{
1803228594Snp	int rc, fd;
1804228594Snp	struct t4_data data = {0};
1805228594Snp	const char *fname = argv[0];
1806228594Snp	struct stat st = {0};
1807228594Snp
1808228594Snp	if (argc != 1) {
1809228594Snp		warnx("loadfw: incorrect number of arguments.");
1810228594Snp		return (EINVAL);
1811228594Snp	}
1812228594Snp
1813228594Snp	fd = open(fname, O_RDONLY);
1814228594Snp	if (fd < 0) {
1815228594Snp		warn("open(%s)", fname);
1816228594Snp		return (errno);
1817228594Snp	}
1818228594Snp
1819228594Snp	if (fstat(fd, &st) < 0) {
1820228594Snp		warn("fstat");
1821228594Snp		close(fd);
1822228594Snp		return (errno);
1823228594Snp	}
1824228594Snp
1825228594Snp	data.len = st.st_size;
1826273360Snp	data.data = mmap(0, data.len, PROT_READ, MAP_PRIVATE, fd, 0);
1827228594Snp	if (data.data == MAP_FAILED) {
1828228594Snp		warn("mmap");
1829228594Snp		close(fd);
1830228594Snp		return (errno);
1831228594Snp	}
1832228594Snp
1833228594Snp	rc = doit(CHELSIO_T4_LOAD_FW, &data);
1834228594Snp	munmap(data.data, data.len);
1835228594Snp	close(fd);
1836228594Snp	return (rc);
1837228594Snp}
1838228594Snp
1839228594Snpstatic int
1840306823Snploadcfg(int argc, const char *argv[])
1841306823Snp{
1842306823Snp	int rc, fd;
1843306823Snp	struct t4_data data = {0};
1844306823Snp	const char *fname = argv[0];
1845306823Snp	struct stat st = {0};
1846306823Snp
1847306823Snp	if (argc != 1) {
1848306823Snp		warnx("loadcfg: incorrect number of arguments.");
1849306823Snp		return (EINVAL);
1850306823Snp	}
1851306823Snp
1852306823Snp	if (strcmp(fname, "clear") == 0)
1853306823Snp		return (doit(CHELSIO_T4_LOAD_CFG, &data));
1854306823Snp
1855306823Snp	fd = open(fname, O_RDONLY);
1856306823Snp	if (fd < 0) {
1857306823Snp		warn("open(%s)", fname);
1858306823Snp		return (errno);
1859306823Snp	}
1860306823Snp
1861306823Snp	if (fstat(fd, &st) < 0) {
1862306823Snp		warn("fstat");
1863306823Snp		close(fd);
1864306823Snp		return (errno);
1865306823Snp	}
1866306823Snp
1867306823Snp	data.len = st.st_size;
1868306823Snp	data.len &= ~3;		/* Clip off to make it a multiple of 4 */
1869306823Snp	data.data = mmap(0, data.len, PROT_READ, MAP_PRIVATE, fd, 0);
1870306823Snp	if (data.data == MAP_FAILED) {
1871306823Snp		warn("mmap");
1872306823Snp		close(fd);
1873306823Snp		return (errno);
1874306823Snp	}
1875306823Snp
1876306823Snp	rc = doit(CHELSIO_T4_LOAD_CFG, &data);
1877306823Snp	munmap(data.data, data.len);
1878306823Snp	close(fd);
1879306823Snp	return (rc);
1880306823Snp}
1881306823Snp
1882306823Snpstatic int
1883228594Snpread_mem(uint32_t addr, uint32_t len, void (*output)(uint32_t *, uint32_t))
1884228594Snp{
1885228594Snp	int rc;
1886228594Snp	struct t4_mem_range mr;
1887228594Snp
1888228594Snp	mr.addr = addr;
1889228594Snp	mr.len = len;
1890228594Snp	mr.data = malloc(mr.len);
1891228594Snp
1892228594Snp	if (mr.data == 0) {
1893228594Snp		warn("read_mem: malloc");
1894228594Snp		return (errno);
1895228594Snp	}
1896228594Snp
1897228594Snp	rc = doit(CHELSIO_T4_GET_MEM, &mr);
1898228594Snp	if (rc != 0)
1899228594Snp		goto done;
1900228594Snp
1901228594Snp	if (output)
1902228594Snp		(*output)(mr.data, mr.len);
1903228594Snpdone:
1904228594Snp	free(mr.data);
1905228594Snp	return (rc);
1906228594Snp}
1907228594Snp
1908228594Snp/*
1909228594Snp * Display memory as list of 'n' 4-byte values per line.
1910228594Snp */
1911228594Snpstatic void
1912228594Snpshow_mem(uint32_t *buf, uint32_t len)
1913228594Snp{
1914228594Snp	const char *s;
1915228594Snp	int i, n = 8;
1916228594Snp
1917228594Snp	while (len) {
1918228594Snp		for (i = 0; len && i < n; i++, buf++, len -= 4) {
1919228594Snp			s = i ? " " : "";
1920228594Snp			printf("%s%08x", s, htonl(*buf));
1921228594Snp		}
1922228594Snp		printf("\n");
1923228594Snp	}
1924228594Snp}
1925228594Snp
1926228594Snpstatic int
1927228594Snpmemdump(int argc, const char *argv[])
1928228594Snp{
1929228594Snp	char *p;
1930228594Snp	long l;
1931228594Snp	uint32_t addr, len;
1932228594Snp
1933228594Snp	if (argc != 2) {
1934228594Snp		warnx("incorrect number of arguments.");
1935228594Snp		return (EINVAL);
1936228594Snp	}
1937228594Snp
1938228594Snp	p = str_to_number(argv[0], &l, NULL);
1939228594Snp	if (*p) {
1940228594Snp		warnx("invalid address \"%s\"", argv[0]);
1941228594Snp		return (EINVAL);
1942228594Snp	}
1943228594Snp	addr = l;
1944228594Snp
1945228594Snp	p = str_to_number(argv[1], &l, NULL);
1946228594Snp	if (*p) {
1947228594Snp		warnx("memdump: invalid length \"%s\"", argv[1]);
1948228594Snp		return (EINVAL);
1949228594Snp	}
1950228594Snp	len = l;
1951228594Snp
1952228594Snp	return (read_mem(addr, len, show_mem));
1953228594Snp}
1954228594Snp
1955228594Snp/*
1956228594Snp * Display TCB as list of 'n' 4-byte values per line.
1957228594Snp */
1958228594Snpstatic void
1959228594Snpshow_tcb(uint32_t *buf, uint32_t len)
1960228594Snp{
1961228594Snp	const char *s;
1962228594Snp	int i, n = 8;
1963228594Snp
1964228594Snp	while (len) {
1965228594Snp		for (i = 0; len && i < n; i++, buf++, len -= 4) {
1966228594Snp			s = i ? " " : "";
1967228594Snp			printf("%s%08x", s, htonl(*buf));
1968228594Snp		}
1969228594Snp		printf("\n");
1970228594Snp	}
1971228594Snp}
1972228594Snp
1973228594Snp#define A_TP_CMM_TCB_BASE 0x7d10
1974228594Snp#define TCB_SIZE 128
1975228594Snpstatic int
1976228594Snpread_tcb(int argc, const char *argv[])
1977228594Snp{
1978228594Snp	char *p;
1979228594Snp	long l;
1980228594Snp	long long val;
1981228594Snp	unsigned int tid;
1982228594Snp	uint32_t addr;
1983228594Snp	int rc;
1984228594Snp
1985228594Snp	if (argc != 1) {
1986228594Snp		warnx("incorrect number of arguments.");
1987228594Snp		return (EINVAL);
1988228594Snp	}
1989228594Snp
1990228594Snp	p = str_to_number(argv[0], &l, NULL);
1991228594Snp	if (*p) {
1992228594Snp		warnx("invalid tid \"%s\"", argv[0]);
1993228594Snp		return (EINVAL);
1994228594Snp	}
1995228594Snp	tid = l;
1996228594Snp
1997228594Snp	rc = read_reg(A_TP_CMM_TCB_BASE, 4, &val);
1998228594Snp	if (rc != 0)
1999228594Snp		return (rc);
2000228594Snp
2001228594Snp	addr = val + tid * TCB_SIZE;
2002228594Snp
2003228594Snp	return (read_mem(addr, TCB_SIZE, show_tcb));
2004228594Snp}
2005228594Snp
2006228594Snpstatic int
2007241401Snpread_i2c(int argc, const char *argv[])
2008241401Snp{
2009241401Snp	char *p;
2010241401Snp	long l;
2011241401Snp	struct t4_i2c_data i2cd;
2012241401Snp	int rc, i;
2013241401Snp
2014241401Snp	if (argc < 3 || argc > 4) {
2015241401Snp		warnx("incorrect number of arguments.");
2016241401Snp		return (EINVAL);
2017241401Snp	}
2018241401Snp
2019241401Snp	p = str_to_number(argv[0], &l, NULL);
2020241401Snp	if (*p || l > UCHAR_MAX) {
2021241401Snp		warnx("invalid port id \"%s\"", argv[0]);
2022241401Snp		return (EINVAL);
2023241401Snp	}
2024241401Snp	i2cd.port_id = l;
2025241401Snp
2026241401Snp	p = str_to_number(argv[1], &l, NULL);
2027241401Snp	if (*p || l > UCHAR_MAX) {
2028241401Snp		warnx("invalid i2c device address \"%s\"", argv[1]);
2029241401Snp		return (EINVAL);
2030241401Snp	}
2031241401Snp	i2cd.dev_addr = l;
2032241401Snp
2033241401Snp	p = str_to_number(argv[2], &l, NULL);
2034241401Snp	if (*p || l > UCHAR_MAX) {
2035241401Snp		warnx("invalid byte offset \"%s\"", argv[2]);
2036241401Snp		return (EINVAL);
2037241401Snp	}
2038241401Snp	i2cd.offset = l;
2039241401Snp
2040241401Snp	if (argc == 4) {
2041241401Snp		p = str_to_number(argv[3], &l, NULL);
2042241401Snp		if (*p || l > sizeof(i2cd.data)) {
2043241401Snp			warnx("invalid number of bytes \"%s\"", argv[3]);
2044241401Snp			return (EINVAL);
2045241401Snp		}
2046241401Snp		i2cd.len = l;
2047241401Snp	} else
2048241401Snp		i2cd.len = 1;
2049241401Snp
2050241401Snp	rc = doit(CHELSIO_T4_GET_I2C, &i2cd);
2051241401Snp	if (rc != 0)
2052241401Snp		return (rc);
2053241401Snp
2054241401Snp	for (i = 0; i < i2cd.len; i++)
2055241401Snp		printf("0x%x [%u]\n", i2cd.data[i], i2cd.data[i]);
2056241401Snp
2057241401Snp	return (0);
2058241401Snp}
2059241401Snp
2060241401Snpstatic int
2061241416Snpclearstats(int argc, const char *argv[])
2062241416Snp{
2063241416Snp	char *p;
2064241416Snp	long l;
2065241416Snp	uint32_t port;
2066241416Snp
2067241416Snp	if (argc != 1) {
2068241416Snp		warnx("incorrect number of arguments.");
2069241416Snp		return (EINVAL);
2070241416Snp	}
2071241416Snp
2072241416Snp	p = str_to_number(argv[0], &l, NULL);
2073241416Snp	if (*p) {
2074241416Snp		warnx("invalid port id \"%s\"", argv[0]);
2075241416Snp		return (EINVAL);
2076241416Snp	}
2077241416Snp	port = l;
2078241416Snp
2079241416Snp	return doit(CHELSIO_T4_CLEAR_STATS, &port);
2080241416Snp}
2081241416Snp
2082241416Snpstatic int
2083253691Snpshow_tracers(void)
2084253691Snp{
2085253691Snp	struct t4_tracer t;
2086253691Snp	char *s;
2087253691Snp	int rc, port_idx, i;
2088253691Snp	long long val;
2089253691Snp
2090253691Snp	/* Magic values: MPS_TRC_CFG = 0x9800. MPS_TRC_CFG[1:1] = TrcEn */
2091253691Snp	rc = read_reg(0x9800, 4, &val);
2092253691Snp	if (rc != 0)
2093253691Snp		return (rc);
2094253691Snp	printf("tracing is %s\n", val & 2 ? "ENABLED" : "DISABLED");
2095253691Snp
2096253691Snp	t.idx = 0;
2097253691Snp	for (t.idx = 0; ; t.idx++) {
2098253691Snp		rc = doit(CHELSIO_T4_GET_TRACER, &t);
2099253691Snp		if (rc != 0 || t.idx == 0xff)
2100253691Snp			break;
2101253691Snp
2102253691Snp		if (t.tp.port < 4) {
2103253691Snp			s = "Rx";
2104253691Snp			port_idx = t.tp.port;
2105253691Snp		} else if (t.tp.port < 8) {
2106253691Snp			s = "Tx";
2107253691Snp			port_idx = t.tp.port - 4;
2108253691Snp		} else if (t.tp.port < 12) {
2109253691Snp			s = "loopback";
2110253691Snp			port_idx = t.tp.port - 8;
2111253691Snp		} else if (t.tp.port < 16) {
2112253691Snp			s = "MPS Rx";
2113253691Snp			port_idx = t.tp.port - 12;
2114253691Snp		} else if (t.tp.port < 20) {
2115253691Snp			s = "MPS Tx";
2116253691Snp			port_idx = t.tp.port - 16;
2117253691Snp		} else {
2118253691Snp			s = "unknown";
2119253691Snp			port_idx = t.tp.port;
2120253691Snp		}
2121253691Snp
2122253691Snp		printf("\ntracer %u (currently %s) captures ", t.idx,
2123253691Snp		    t.enabled ? "ENABLED" : "DISABLED");
2124253691Snp		if (t.tp.port < 8)
2125253691Snp			printf("port %u %s, ", port_idx, s);
2126253691Snp		else
2127253691Snp			printf("%s %u, ", s, port_idx);
2128253691Snp		printf("snap length: %u, min length: %u\n", t.tp.snap_len,
2129253691Snp		    t.tp.min_len);
2130253691Snp		printf("packets captured %smatch filter\n",
2131253691Snp		    t.tp.invert ? "do not " : "");
2132253691Snp		if (t.tp.skip_ofst) {
2133253691Snp			printf("filter pattern: ");
2134253691Snp			for (i = 0; i < t.tp.skip_ofst * 2; i += 2)
2135253691Snp				printf("%08x%08x", t.tp.data[i],
2136253691Snp				    t.tp.data[i + 1]);
2137253691Snp			printf("/");
2138253691Snp			for (i = 0; i < t.tp.skip_ofst * 2; i += 2)
2139253691Snp				printf("%08x%08x", t.tp.mask[i],
2140253691Snp				    t.tp.mask[i + 1]);
2141253691Snp			printf("@0\n");
2142253691Snp		}
2143253691Snp		printf("filter pattern: ");
2144253691Snp		for (i = t.tp.skip_ofst * 2; i < T4_TRACE_LEN / 4; i += 2)
2145253691Snp			printf("%08x%08x", t.tp.data[i], t.tp.data[i + 1]);
2146253691Snp		printf("/");
2147253691Snp		for (i = t.tp.skip_ofst * 2; i < T4_TRACE_LEN / 4; i += 2)
2148253691Snp			printf("%08x%08x", t.tp.mask[i], t.tp.mask[i + 1]);
2149253691Snp		printf("@%u\n", (t.tp.skip_ofst + t.tp.skip_len) * 8);
2150253691Snp	}
2151253691Snp
2152253691Snp	return (rc);
2153253691Snp}
2154253691Snp
2155253691Snpstatic int
2156253691Snptracer_onoff(uint8_t idx, int enabled)
2157253691Snp{
2158253691Snp	struct t4_tracer t;
2159253691Snp
2160253691Snp	t.idx = idx;
2161253691Snp	t.enabled = enabled;
2162253691Snp	t.valid = 0;
2163253691Snp
2164253691Snp	return doit(CHELSIO_T4_SET_TRACER, &t);
2165253691Snp}
2166253691Snp
2167253691Snpstatic void
2168253691Snpcreate_tracing_ifnet()
2169253691Snp{
2170253691Snp	char *cmd[] = {
2171253691Snp		"/sbin/ifconfig", __DECONST(char *, nexus), "create", NULL
2172253691Snp	};
2173253691Snp	char *env[] = {NULL};
2174253691Snp
2175253691Snp	if (vfork() == 0) {
2176253691Snp		close(STDERR_FILENO);
2177253691Snp		execve(cmd[0], cmd, env);
2178253691Snp		_exit(0);
2179253691Snp	}
2180253691Snp}
2181253691Snp
2182253691Snp/*
2183253691Snp * XXX: Allow user to specify snaplen, minlen, and pattern (including inverted
2184253691Snp * matching).  Right now this is a quick-n-dirty implementation that traces the
2185253691Snp * first 128B of all tx or rx on a port
2186253691Snp */
2187253691Snpstatic int
2188253691Snpset_tracer(uint8_t idx, int argc, const char *argv[])
2189253691Snp{
2190253691Snp	struct t4_tracer t;
2191253691Snp	int len, port;
2192253691Snp
2193253691Snp	bzero(&t, sizeof (t));
2194253691Snp	t.idx = idx;
2195253691Snp	t.enabled = 1;
2196253691Snp	t.valid = 1;
2197253691Snp
2198253691Snp	if (argc != 1) {
2199253691Snp		warnx("must specify tx<n> or rx<n>.");
2200253691Snp		return (EINVAL);
2201253691Snp	}
2202253691Snp
2203253691Snp	len = strlen(argv[0]);
2204253691Snp	if (len != 3) {
2205253691Snp		warnx("argument must be 3 characters (tx<n> or rx<n>)");
2206253691Snp		return (EINVAL);
2207253691Snp	}
2208253691Snp
2209253691Snp	if (strncmp(argv[0], "tx", 2) == 0) {
2210253691Snp		port = argv[0][2] - '0';
2211253691Snp		if (port < 0 || port > 3) {
2212253691Snp			warnx("'%c' in %s is invalid", argv[0][2], argv[0]);
2213253691Snp			return (EINVAL);
2214253691Snp		}
2215253691Snp		port += 4;
2216253691Snp	} else if (strncmp(argv[0], "rx", 2) == 0) {
2217253691Snp		port = argv[0][2] - '0';
2218253691Snp		if (port < 0 || port > 3) {
2219253691Snp			warnx("'%c' in %s is invalid", argv[0][2], argv[0]);
2220253691Snp			return (EINVAL);
2221253691Snp		}
2222253691Snp	} else {
2223253691Snp		warnx("argument '%s' isn't tx<n> or rx<n>", argv[0]);
2224253691Snp		return (EINVAL);
2225253691Snp	}
2226253691Snp
2227253691Snp	t.tp.snap_len = 128;
2228253691Snp	t.tp.min_len = 0;
2229253691Snp	t.tp.skip_ofst = 0;
2230253691Snp	t.tp.skip_len = 0;
2231253691Snp	t.tp.invert = 0;
2232253691Snp	t.tp.port = port;
2233253691Snp
2234253691Snp	create_tracing_ifnet();
2235253691Snp	return doit(CHELSIO_T4_SET_TRACER, &t);
2236253691Snp}
2237253691Snp
2238253691Snpstatic int
2239259048Snptracer_cmd(int argc, const char *argv[])
2240259048Snp{
2241259048Snp	long long val;
2242259048Snp	uint8_t idx;
2243259048Snp	char *s;
2244259048Snp
2245259048Snp	if (argc == 0) {
2246259048Snp		warnx("tracer: no arguments.");
2247259048Snp		return (EINVAL);
2248259048Snp	};
2249259048Snp
2250259048Snp	/* list */
2251259048Snp	if (strcmp(argv[0], "list") == 0) {
2252259048Snp		if (argc != 1)
2253259048Snp			warnx("trailing arguments after \"list\" ignored.");
2254259048Snp
2255259048Snp		return show_tracers();
2256259048Snp	}
2257259048Snp
2258259048Snp	/* <idx> ... */
2259259048Snp	s = str_to_number(argv[0], NULL, &val);
2260259048Snp	if (*s || val > 0xff) {
2261259048Snp		warnx("\"%s\" is neither an index nor a tracer subcommand.",
2262259048Snp		    argv[0]);
2263259048Snp		return (EINVAL);
2264259048Snp	}
2265259048Snp	idx = (int8_t)val;
2266259048Snp
2267259048Snp	/* <idx> disable */
2268259048Snp	if (argc == 2 && strcmp(argv[1], "disable") == 0)
2269259048Snp		return tracer_onoff(idx, 0);
2270259048Snp
2271259048Snp	/* <idx> enable */
2272259048Snp	if (argc == 2 && strcmp(argv[1], "enable") == 0)
2273259048Snp		return tracer_onoff(idx, 1);
2274259048Snp
2275259048Snp	/* <idx> ... */
2276259048Snp	return set_tracer(idx, argc - 1, argv + 1);
2277259048Snp}
2278259048Snp
2279259048Snpstatic int
2280269106Snpmodinfo_raw(int port_id)
2281269106Snp{
2282269106Snp	uint8_t offset;
2283269106Snp	struct t4_i2c_data i2cd;
2284269106Snp	int rc;
2285269106Snp
2286269106Snp	for (offset = 0; offset < 96; offset += sizeof(i2cd.data)) {
2287269106Snp		bzero(&i2cd, sizeof(i2cd));
2288269106Snp		i2cd.port_id = port_id;
2289269106Snp		i2cd.dev_addr = 0xa0;
2290269106Snp		i2cd.offset = offset;
2291269106Snp		i2cd.len = sizeof(i2cd.data);
2292269106Snp		rc = doit(CHELSIO_T4_GET_I2C, &i2cd);
2293269106Snp		if (rc != 0)
2294269106Snp			return (rc);
2295269106Snp		printf("%02x:  %02x %02x %02x %02x  %02x %02x %02x %02x",
2296269106Snp		    offset, i2cd.data[0], i2cd.data[1], i2cd.data[2],
2297269106Snp		    i2cd.data[3], i2cd.data[4], i2cd.data[5], i2cd.data[6],
2298269106Snp		    i2cd.data[7]);
2299269106Snp
2300269106Snp		printf("  %c%c%c%c %c%c%c%c\n",
2301269106Snp		    isprint(i2cd.data[0]) ? i2cd.data[0] : '.',
2302269106Snp		    isprint(i2cd.data[1]) ? i2cd.data[1] : '.',
2303269106Snp		    isprint(i2cd.data[2]) ? i2cd.data[2] : '.',
2304269106Snp		    isprint(i2cd.data[3]) ? i2cd.data[3] : '.',
2305269106Snp		    isprint(i2cd.data[4]) ? i2cd.data[4] : '.',
2306269106Snp		    isprint(i2cd.data[5]) ? i2cd.data[5] : '.',
2307269106Snp		    isprint(i2cd.data[6]) ? i2cd.data[6] : '.',
2308269106Snp		    isprint(i2cd.data[7]) ? i2cd.data[7] : '.');
2309269106Snp	}
2310269106Snp
2311269106Snp	return (0);
2312269106Snp}
2313269106Snp
2314269106Snpstatic int
2315258698Snpmodinfo(int argc, const char *argv[])
2316258698Snp{
2317258698Snp	long port;
2318258698Snp	char string[16], *p;
2319258698Snp	struct t4_i2c_data i2cd;
2320258698Snp	int rc, i;
2321258698Snp	uint16_t temp, vcc, tx_bias, tx_power, rx_power;
2322258698Snp
2323269106Snp	if (argc < 1) {
2324258698Snp		warnx("must supply a port");
2325258698Snp		return (EINVAL);
2326258698Snp	}
2327258698Snp
2328269106Snp	if (argc > 2) {
2329269106Snp		warnx("too many arguments");
2330269106Snp		return (EINVAL);
2331269106Snp	}
2332269106Snp
2333258698Snp	p = str_to_number(argv[0], &port, NULL);
2334258698Snp	if (*p || port > UCHAR_MAX) {
2335258698Snp		warnx("invalid port id \"%s\"", argv[0]);
2336258698Snp		return (EINVAL);
2337258698Snp	}
2338258698Snp
2339269106Snp	if (argc == 2) {
2340269106Snp		if (!strcmp(argv[1], "raw"))
2341269106Snp			return (modinfo_raw(port));
2342269106Snp		else {
2343269106Snp			warnx("second argument can only be \"raw\"");
2344269106Snp			return (EINVAL);
2345269106Snp		}
2346269106Snp	}
2347269106Snp
2348258698Snp	bzero(&i2cd, sizeof(i2cd));
2349258698Snp	i2cd.len = 1;
2350258698Snp	i2cd.port_id = port;
2351258698Snp	i2cd.dev_addr = SFF_8472_BASE;
2352258698Snp
2353258698Snp	i2cd.offset = SFF_8472_ID;
2354258698Snp	if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
2355258698Snp		goto fail;
2356258698Snp
2357258698Snp	if (i2cd.data[0] > SFF_8472_ID_LAST)
2358258698Snp		printf("Unknown ID\n");
2359258698Snp	else
2360258698Snp		printf("ID: %s\n", sff_8472_id[i2cd.data[0]]);
2361258698Snp
2362258698Snp	bzero(&string, sizeof(string));
2363258698Snp	for (i = SFF_8472_VENDOR_START; i < SFF_8472_VENDOR_END; i++) {
2364258698Snp		i2cd.offset = i;
2365258698Snp		if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
2366258698Snp			goto fail;
2367258698Snp		string[i - SFF_8472_VENDOR_START] = i2cd.data[0];
2368258698Snp	}
2369258698Snp	printf("Vendor %s\n", string);
2370258698Snp
2371258698Snp	bzero(&string, sizeof(string));
2372258698Snp	for (i = SFF_8472_SN_START; i < SFF_8472_SN_END; i++) {
2373258698Snp		i2cd.offset = i;
2374258698Snp		if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
2375258698Snp			goto fail;
2376258698Snp		string[i - SFF_8472_SN_START] = i2cd.data[0];
2377258698Snp	}
2378258698Snp	printf("SN %s\n", string);
2379258698Snp
2380258698Snp	bzero(&string, sizeof(string));
2381258698Snp	for (i = SFF_8472_PN_START; i < SFF_8472_PN_END; i++) {
2382258698Snp		i2cd.offset = i;
2383258698Snp		if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
2384258698Snp			goto fail;
2385258698Snp		string[i - SFF_8472_PN_START] = i2cd.data[0];
2386258698Snp	}
2387258698Snp	printf("PN %s\n", string);
2388258698Snp
2389258698Snp	bzero(&string, sizeof(string));
2390258698Snp	for (i = SFF_8472_REV_START; i < SFF_8472_REV_END; i++) {
2391258698Snp		i2cd.offset = i;
2392258698Snp		if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
2393258698Snp			goto fail;
2394258698Snp		string[i - SFF_8472_REV_START] = i2cd.data[0];
2395258698Snp	}
2396258698Snp	printf("Rev %s\n", string);
2397258698Snp
2398258698Snp	i2cd.offset = SFF_8472_DIAG_TYPE;
2399258698Snp	if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
2400258698Snp		goto fail;
2401258698Snp
2402258698Snp	if ((char )i2cd.data[0] & (SFF_8472_DIAG_IMPL |
2403258698Snp				   SFF_8472_DIAG_INTERNAL)) {
2404258698Snp
2405258698Snp		/* Switch to reading from the Diagnostic address. */
2406258698Snp		i2cd.dev_addr = SFF_8472_DIAG;
2407258698Snp		i2cd.len = 1;
2408258698Snp
2409258698Snp		i2cd.offset = SFF_8472_TEMP;
2410258698Snp		if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
2411258698Snp			goto fail;
2412258698Snp		temp = i2cd.data[0] << 8;
2413258698Snp		printf("Temp: ");
2414258698Snp		if ((temp & SFF_8472_TEMP_SIGN) == SFF_8472_TEMP_SIGN)
2415258698Snp			printf("-");
2416258698Snp		else
2417258698Snp			printf("+");
2418258698Snp		printf("%dC\n", (temp & SFF_8472_TEMP_MSK) >>
2419258698Snp		    SFF_8472_TEMP_SHIFT);
2420258698Snp
2421258698Snp		i2cd.offset = SFF_8472_VCC;
2422258698Snp		if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
2423258698Snp			goto fail;
2424258698Snp		vcc = i2cd.data[0] << 8;
2425258698Snp		printf("Vcc %fV\n", vcc / SFF_8472_VCC_FACTOR);
2426258698Snp
2427258698Snp		i2cd.offset = SFF_8472_TX_BIAS;
2428258698Snp		if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
2429258698Snp			goto fail;
2430258698Snp		tx_bias = i2cd.data[0] << 8;
2431258698Snp		printf("TX Bias %fuA\n", tx_bias / SFF_8472_BIAS_FACTOR);
2432258698Snp
2433258698Snp		i2cd.offset = SFF_8472_TX_POWER;
2434258698Snp		if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
2435258698Snp			goto fail;
2436258698Snp		tx_power = i2cd.data[0] << 8;
2437258698Snp		printf("TX Power %fmW\n", tx_power / SFF_8472_POWER_FACTOR);
2438258698Snp
2439258698Snp		i2cd.offset = SFF_8472_RX_POWER;
2440258698Snp		if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
2441258698Snp			goto fail;
2442258698Snp		rx_power = i2cd.data[0] << 8;
2443258698Snp		printf("RX Power %fmW\n", rx_power / SFF_8472_POWER_FACTOR);
2444258698Snp
2445258698Snp	} else
2446258698Snp		printf("Diagnostics not supported.\n");
2447258698Snp
2448258698Snp	return(0);
2449258698Snp
2450258698Snpfail:
2451258698Snp	if (rc == EPERM)
2452258698Snp		warnx("No module/cable in port %ld", port);
2453258698Snp	return (rc);
2454258698Snp
2455258698Snp}
2456258698Snp
2457259048Snp/* XXX: pass in a low/high and do range checks as well */
2458258698Snpstatic int
2459259048Snpget_sched_param(const char *param, const char *args[], long *val)
2460253691Snp{
2461259048Snp	char *p;
2462253691Snp
2463259048Snp	if (strcmp(param, args[0]) != 0)
2464259048Snp		return (EINVAL);
2465259048Snp
2466259048Snp	p = str_to_number(args[1], val, NULL);
2467259048Snp	if (*p) {
2468259048Snp		warnx("parameter \"%s\" has bad value \"%s\"", args[0],
2469259048Snp		    args[1]);
2470259048Snp		return (EINVAL);
2471259048Snp	}
2472259048Snp
2473259048Snp	return (0);
2474259048Snp}
2475259048Snp
2476259048Snpstatic int
2477259048Snpsched_class(int argc, const char *argv[])
2478259048Snp{
2479259048Snp	struct t4_sched_params op;
2480259048Snp	int errs, i;
2481259048Snp
2482259048Snp	memset(&op, 0xff, sizeof(op));
2483259048Snp	op.subcmd = -1;
2484259048Snp	op.type = -1;
2485253691Snp	if (argc == 0) {
2486259048Snp		warnx("missing scheduling sub-command");
2487253691Snp		return (EINVAL);
2488259048Snp	}
2489259048Snp	if (!strcmp(argv[0], "config")) {
2490259048Snp		op.subcmd = SCHED_CLASS_SUBCMD_CONFIG;
2491259048Snp		op.u.config.minmax = -1;
2492259048Snp	} else if (!strcmp(argv[0], "params")) {
2493259048Snp		op.subcmd = SCHED_CLASS_SUBCMD_PARAMS;
2494259048Snp		op.u.params.level = op.u.params.mode = op.u.params.rateunit =
2495259048Snp		    op.u.params.ratemode = op.u.params.channel =
2496259048Snp		    op.u.params.cl = op.u.params.minrate = op.u.params.maxrate =
2497259048Snp		    op.u.params.weight = op.u.params.pktsize = -1;
2498259048Snp	} else {
2499259048Snp		warnx("invalid scheduling sub-command \"%s\"", argv[0]);
2500259048Snp		return (EINVAL);
2501259048Snp	}
2502253691Snp
2503259048Snp	/* Decode remaining arguments ... */
2504259048Snp	errs = 0;
2505259048Snp	for (i = 1; i < argc; i += 2) {
2506259048Snp		const char **args = &argv[i];
2507259048Snp		long l;
2508253691Snp
2509259048Snp		if (i + 1 == argc) {
2510259048Snp			warnx("missing argument for \"%s\"", args[0]);
2511259048Snp			errs++;
2512259048Snp			break;
2513259048Snp		}
2514259048Snp
2515259048Snp		if (!strcmp(args[0], "type")) {
2516259048Snp			if (!strcmp(args[1], "packet"))
2517259048Snp				op.type = SCHED_CLASS_TYPE_PACKET;
2518259048Snp			else {
2519259048Snp				warnx("invalid type parameter \"%s\"", args[1]);
2520259048Snp				errs++;
2521259048Snp			}
2522259048Snp
2523259048Snp			continue;
2524259048Snp		}
2525259048Snp
2526259048Snp		if (op.subcmd == SCHED_CLASS_SUBCMD_CONFIG) {
2527259048Snp			if(!get_sched_param("minmax", args, &l))
2528259048Snp				op.u.config.minmax = (int8_t)l;
2529259048Snp			else {
2530259048Snp				warnx("unknown scheduler config parameter "
2531259048Snp				    "\"%s\"", args[0]);
2532259048Snp				errs++;
2533259048Snp			}
2534259048Snp
2535259048Snp			continue;
2536259048Snp		}
2537259048Snp
2538259048Snp		/* Rest applies only to SUBCMD_PARAMS */
2539259048Snp		if (op.subcmd != SCHED_CLASS_SUBCMD_PARAMS)
2540259048Snp			continue;
2541259048Snp
2542259048Snp		if (!strcmp(args[0], "level")) {
2543259048Snp			if (!strcmp(args[1], "cl-rl"))
2544259048Snp				op.u.params.level = SCHED_CLASS_LEVEL_CL_RL;
2545259048Snp			else if (!strcmp(args[1], "cl-wrr"))
2546259048Snp				op.u.params.level = SCHED_CLASS_LEVEL_CL_WRR;
2547259048Snp			else if (!strcmp(args[1], "ch-rl"))
2548259048Snp				op.u.params.level = SCHED_CLASS_LEVEL_CH_RL;
2549259048Snp			else {
2550259048Snp				warnx("invalid level parameter \"%s\"",
2551259048Snp				    args[1]);
2552259048Snp				errs++;
2553259048Snp			}
2554259048Snp		} else if (!strcmp(args[0], "mode")) {
2555259048Snp			if (!strcmp(args[1], "class"))
2556259048Snp				op.u.params.mode = SCHED_CLASS_MODE_CLASS;
2557259048Snp			else if (!strcmp(args[1], "flow"))
2558259048Snp				op.u.params.mode = SCHED_CLASS_MODE_FLOW;
2559259048Snp			else {
2560259048Snp				warnx("invalid mode parameter \"%s\"", args[1]);
2561259048Snp				errs++;
2562259048Snp			}
2563259048Snp		} else if (!strcmp(args[0], "rate-unit")) {
2564259048Snp			if (!strcmp(args[1], "bits"))
2565259048Snp				op.u.params.rateunit = SCHED_CLASS_RATEUNIT_BITS;
2566259048Snp			else if (!strcmp(args[1], "pkts"))
2567259048Snp				op.u.params.rateunit = SCHED_CLASS_RATEUNIT_PKTS;
2568259048Snp			else {
2569259048Snp				warnx("invalid rate-unit parameter \"%s\"",
2570259048Snp				    args[1]);
2571259048Snp				errs++;
2572259048Snp			}
2573259048Snp		} else if (!strcmp(args[0], "rate-mode")) {
2574259048Snp			if (!strcmp(args[1], "relative"))
2575259048Snp				op.u.params.ratemode = SCHED_CLASS_RATEMODE_REL;
2576259048Snp			else if (!strcmp(args[1], "absolute"))
2577259048Snp				op.u.params.ratemode = SCHED_CLASS_RATEMODE_ABS;
2578259048Snp			else {
2579259048Snp				warnx("invalid rate-mode parameter \"%s\"",
2580259048Snp				    args[1]);
2581259048Snp				errs++;
2582259048Snp			}
2583259048Snp		} else if (!get_sched_param("channel", args, &l))
2584259048Snp			op.u.params.channel = (int8_t)l;
2585259048Snp		else if (!get_sched_param("class", args, &l))
2586259048Snp			op.u.params.cl = (int8_t)l;
2587259048Snp		else if (!get_sched_param("min-rate", args, &l))
2588259048Snp			op.u.params.minrate = (int32_t)l;
2589259048Snp		else if (!get_sched_param("max-rate", args, &l))
2590259048Snp			op.u.params.maxrate = (int32_t)l;
2591259048Snp		else if (!get_sched_param("weight", args, &l))
2592259048Snp			op.u.params.weight = (int16_t)l;
2593259048Snp		else if (!get_sched_param("pkt-size", args, &l))
2594259048Snp			op.u.params.pktsize = (int16_t)l;
2595259048Snp		else {
2596259048Snp			warnx("unknown scheduler parameter \"%s\"", args[0]);
2597259048Snp			errs++;
2598259048Snp		}
2599253691Snp	}
2600253691Snp
2601259048Snp	/*
2602259048Snp	 * Catch some logical fallacies in terms of argument combinations here
2603259048Snp	 * so we can offer more than just the EINVAL return from the driver.
2604259048Snp	 * The driver will be able to catch a lot more issues since it knows
2605259048Snp	 * the specifics of the device hardware capabilities like how many
2606259048Snp	 * channels, classes, etc. the device supports.
2607259048Snp	 */
2608259048Snp	if (op.type < 0) {
2609259048Snp		warnx("sched \"type\" parameter missing");
2610259048Snp		errs++;
2611259048Snp	}
2612259048Snp	if (op.subcmd == SCHED_CLASS_SUBCMD_CONFIG) {
2613259048Snp		if (op.u.config.minmax < 0) {
2614259048Snp			warnx("sched config \"minmax\" parameter missing");
2615259048Snp			errs++;
2616259048Snp		}
2617259048Snp	}
2618259048Snp	if (op.subcmd == SCHED_CLASS_SUBCMD_PARAMS) {
2619259048Snp		if (op.u.params.level < 0) {
2620259048Snp			warnx("sched params \"level\" parameter missing");
2621259048Snp			errs++;
2622259048Snp		}
2623259048Snp		if (op.u.params.mode < 0) {
2624259048Snp			warnx("sched params \"mode\" parameter missing");
2625259048Snp			errs++;
2626259048Snp		}
2627259048Snp		if (op.u.params.rateunit < 0) {
2628259048Snp			warnx("sched params \"rate-unit\" parameter missing");
2629259048Snp			errs++;
2630259048Snp		}
2631259048Snp		if (op.u.params.ratemode < 0) {
2632259048Snp			warnx("sched params \"rate-mode\" parameter missing");
2633259048Snp			errs++;
2634259048Snp		}
2635259048Snp		if (op.u.params.channel < 0) {
2636259048Snp			warnx("sched params \"channel\" missing");
2637259048Snp			errs++;
2638259048Snp		}
2639259048Snp		if (op.u.params.cl < 0) {
2640259048Snp			warnx("sched params \"class\" missing");
2641259048Snp			errs++;
2642259048Snp		}
2643259048Snp		if (op.u.params.maxrate < 0 &&
2644259048Snp		    (op.u.params.level == SCHED_CLASS_LEVEL_CL_RL ||
2645259048Snp		    op.u.params.level == SCHED_CLASS_LEVEL_CH_RL)) {
2646259048Snp			warnx("sched params \"max-rate\" missing for "
2647259048Snp			    "rate-limit level");
2648259048Snp			errs++;
2649259048Snp		}
2650259048Snp		if (op.u.params.weight < 0 &&
2651259048Snp		    op.u.params.level == SCHED_CLASS_LEVEL_CL_WRR) {
2652259048Snp			warnx("sched params \"weight\" missing for "
2653259048Snp			    "weighted-round-robin level");
2654259048Snp			errs++;
2655259048Snp		}
2656259048Snp		if (op.u.params.pktsize < 0 &&
2657259048Snp		    (op.u.params.level == SCHED_CLASS_LEVEL_CL_RL ||
2658259048Snp		    op.u.params.level == SCHED_CLASS_LEVEL_CH_RL)) {
2659259048Snp			warnx("sched params \"pkt-size\" missing for "
2660259048Snp			    "rate-limit level");
2661259048Snp			errs++;
2662259048Snp		}
2663259048Snp		if (op.u.params.mode == SCHED_CLASS_MODE_FLOW &&
2664259048Snp		    op.u.params.ratemode != SCHED_CLASS_RATEMODE_ABS) {
2665259048Snp			warnx("sched params mode flow needs rate-mode absolute");
2666259048Snp			errs++;
2667259048Snp		}
2668259048Snp		if (op.u.params.ratemode == SCHED_CLASS_RATEMODE_REL &&
2669259048Snp		    !in_range(op.u.params.maxrate, 1, 100)) {
2670259048Snp                        warnx("sched params \"max-rate\" takes "
2671259048Snp			    "percentage value(1-100) for rate-mode relative");
2672259048Snp                        errs++;
2673259048Snp                }
2674259048Snp                if (op.u.params.ratemode == SCHED_CLASS_RATEMODE_ABS &&
2675301516Snp		    !in_range(op.u.params.maxrate, 1, 100000000)) {
2676259048Snp                        warnx("sched params \"max-rate\" takes "
2677301516Snp			    "value(1-100000000) for rate-mode absolute");
2678259048Snp                        errs++;
2679259048Snp                }
2680259048Snp                if (op.u.params.maxrate > 0 &&
2681259048Snp		    op.u.params.maxrate < op.u.params.minrate) {
2682259048Snp                        warnx("sched params \"max-rate\" is less than "
2683259048Snp			    "\"min-rate\"");
2684259048Snp                        errs++;
2685259048Snp                }
2686259048Snp	}
2687259048Snp
2688259048Snp	if (errs > 0) {
2689259048Snp		warnx("%d error%s in sched-class command", errs,
2690259048Snp		    errs == 1 ? "" : "s");
2691253691Snp		return (EINVAL);
2692253691Snp	}
2693253691Snp
2694259048Snp	return doit(CHELSIO_T4_SCHED_CLASS, &op);
2695259048Snp}
2696253691Snp
2697259048Snpstatic int
2698259048Snpsched_queue(int argc, const char *argv[])
2699259048Snp{
2700259048Snp	struct t4_sched_queue op = {0};
2701259048Snp	char *p;
2702259048Snp	long val;
2703253691Snp
2704259048Snp	if (argc != 3) {
2705259048Snp		/* need "<port> <queue> <class> */
2706259048Snp		warnx("incorrect number of arguments.");
2707259048Snp		return (EINVAL);
2708259048Snp	}
2709259048Snp
2710259048Snp	p = str_to_number(argv[0], &val, NULL);
2711259048Snp	if (*p || val > UCHAR_MAX) {
2712259048Snp		warnx("invalid port id \"%s\"", argv[0]);
2713259048Snp		return (EINVAL);
2714259048Snp	}
2715259048Snp	op.port = (uint8_t)val;
2716259048Snp
2717259048Snp	if (!strcmp(argv[1], "all") || !strcmp(argv[1], "*"))
2718259048Snp		op.queue = -1;
2719259048Snp	else {
2720259048Snp		p = str_to_number(argv[1], &val, NULL);
2721259048Snp		if (*p || val < -1) {
2722259048Snp			warnx("invalid queue \"%s\"", argv[1]);
2723259048Snp			return (EINVAL);
2724259048Snp		}
2725259048Snp		op.queue = (int8_t)val;
2726259048Snp	}
2727259048Snp
2728259048Snp	if (!strcmp(argv[2], "unbind") || !strcmp(argv[2], "clear"))
2729259048Snp		op.cl = -1;
2730259048Snp	else {
2731259048Snp		p = str_to_number(argv[2], &val, NULL);
2732259048Snp		if (*p || val < -1) {
2733259048Snp			warnx("invalid class \"%s\"", argv[2]);
2734259048Snp			return (EINVAL);
2735259048Snp		}
2736259048Snp		op.cl = (int8_t)val;
2737259048Snp	}
2738259048Snp
2739259048Snp	return doit(CHELSIO_T4_SCHED_QUEUE, &op);
2740253691Snp}
2741253691Snp
2742253691Snpstatic int
2743222900Snprun_cmd(int argc, const char *argv[])
2744222900Snp{
2745222900Snp	int rc = -1;
2746222900Snp	const char *cmd = argv[0];
2747222900Snp
2748222900Snp	/* command */
2749222900Snp	argc--;
2750222900Snp	argv++;
2751222900Snp
2752222900Snp	if (!strcmp(cmd, "reg") || !strcmp(cmd, "reg32"))
2753222900Snp		rc = register_io(argc, argv, 4);
2754222900Snp	else if (!strcmp(cmd, "reg64"))
2755222900Snp		rc = register_io(argc, argv, 8);
2756222900Snp	else if (!strcmp(cmd, "regdump"))
2757222900Snp		rc = dump_regs(argc, argv);
2758222900Snp	else if (!strcmp(cmd, "filter"))
2759222900Snp		rc = filter_cmd(argc, argv);
2760222974Snp	else if (!strcmp(cmd, "context"))
2761222974Snp		rc = get_sge_context(argc, argv);
2762228594Snp	else if (!strcmp(cmd, "loadfw"))
2763228594Snp		rc = loadfw(argc, argv);
2764228594Snp	else if (!strcmp(cmd, "memdump"))
2765228594Snp		rc = memdump(argc, argv);
2766228594Snp	else if (!strcmp(cmd, "tcb"))
2767228594Snp		rc = read_tcb(argc, argv);
2768241401Snp	else if (!strcmp(cmd, "i2c"))
2769241401Snp		rc = read_i2c(argc, argv);
2770241416Snp	else if (!strcmp(cmd, "clearstats"))
2771241416Snp		rc = clearstats(argc, argv);
2772253691Snp	else if (!strcmp(cmd, "tracer"))
2773253691Snp		rc = tracer_cmd(argc, argv);
2774258698Snp	else if (!strcmp(cmd, "modinfo"))
2775258698Snp		rc = modinfo(argc, argv);
2776259048Snp	else if (!strcmp(cmd, "sched-class"))
2777259048Snp		rc = sched_class(argc, argv);
2778259048Snp	else if (!strcmp(cmd, "sched-queue"))
2779259048Snp		rc = sched_queue(argc, argv);
2780306823Snp	else if (!strcmp(cmd, "loadcfg"))
2781306823Snp		rc = loadcfg(argc, argv);
2782222900Snp	else {
2783222900Snp		rc = EINVAL;
2784222900Snp		warnx("invalid command \"%s\"", cmd);
2785222900Snp	}
2786222900Snp
2787222900Snp	return (rc);
2788222900Snp}
2789222900Snp
2790222900Snp#define MAX_ARGS 15
2791222900Snpstatic int
2792222900Snprun_cmd_loop(void)
2793222900Snp{
2794222900Snp	int i, rc = 0;
2795222900Snp	char buffer[128], *buf;
2796222900Snp	const char *args[MAX_ARGS + 1];
2797222900Snp
2798222900Snp	/*
2799222900Snp	 * Simple loop: displays a "> " prompt and processes any input as a
2800222900Snp	 * cxgbetool command.  You're supposed to enter only the part after
2801222900Snp	 * "cxgbetool t4nexX".  Use "quit" or "exit" to exit.
2802222900Snp	 */
2803222900Snp	for (;;) {
2804222900Snp		fprintf(stdout, "> ");
2805222900Snp		fflush(stdout);
2806222900Snp		buf = fgets(buffer, sizeof(buffer), stdin);
2807222900Snp		if (buf == NULL) {
2808222900Snp			if (ferror(stdin)) {
2809222900Snp				warn("stdin error");
2810222900Snp				rc = errno;	/* errno from fgets */
2811222900Snp			}
2812222900Snp			break;
2813222900Snp		}
2814222900Snp
2815222900Snp		i = 0;
2816222900Snp		while ((args[i] = strsep(&buf, " \t\n")) != NULL) {
2817222900Snp			if (args[i][0] != 0 && ++i == MAX_ARGS)
2818222900Snp				break;
2819222900Snp		}
2820222900Snp		args[i] = 0;
2821222900Snp
2822222900Snp		if (i == 0)
2823222900Snp			continue;	/* skip empty line */
2824222900Snp
2825222900Snp		if (!strcmp(args[0], "quit") || !strcmp(args[0], "exit"))
2826222900Snp			break;
2827222900Snp
2828222900Snp		rc = run_cmd(i, args);
2829222900Snp	}
2830222900Snp
2831222900Snp	/* rc normally comes from the last command (not including quit/exit) */
2832222900Snp	return (rc);
2833222900Snp}
2834222900Snp
2835222900Snpint
2836222900Snpmain(int argc, const char *argv[])
2837222900Snp{
2838222900Snp	int rc = -1;
2839222900Snp
2840222900Snp	progname = argv[0];
2841222900Snp
2842222900Snp	if (argc == 2) {
2843222900Snp		if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
2844222900Snp			usage(stdout);
2845222900Snp			exit(0);
2846222900Snp		}
2847222900Snp	}
2848222900Snp
2849222900Snp	if (argc < 3) {
2850222900Snp		usage(stderr);
2851222900Snp		exit(EINVAL);
2852222900Snp	}
2853222900Snp
2854222900Snp	nexus = argv[1];
2855222900Snp
2856222900Snp	/* progname and nexus */
2857222900Snp	argc -= 2;
2858222900Snp	argv += 2;
2859222900Snp
2860222900Snp	if (argc == 1 && !strcmp(argv[0], "stdio"))
2861222900Snp		rc = run_cmd_loop();
2862222900Snp	else
2863222900Snp		rc = run_cmd(argc, argv);
2864222900Snp
2865222900Snp	return (rc);
2866222900Snp}
2867