cxgbetool.c revision 222900
1222900Snp/*-
2222900Snp * Copyright (c) 2011 Chelsio Communications, Inc.
3222900Snp * All rights reserved.
4222900Snp * Written by: Navdeep Parhar <np@FreeBSD.org>
5222900Snp *
6222900Snp * Redistribution and use in source and binary forms, with or without
7222900Snp * modification, are permitted provided that the following conditions
8222900Snp * are met:
9222900Snp * 1. Redistributions of source code must retain the above copyright
10222900Snp *    notice, this list of conditions and the following disclaimer.
11222900Snp * 2. Redistributions in binary form must reproduce the above copyright
12222900Snp *    notice, this list of conditions and the following disclaimer in the
13222900Snp *    documentation and/or other materials provided with the distribution.
14222900Snp *
15222900Snp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16222900Snp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17222900Snp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18222900Snp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19222900Snp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20222900Snp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21222900Snp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22222900Snp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23222900Snp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24222900Snp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25222900Snp * SUCH DAMAGE.
26222900Snp */
27222900Snp
28222900Snp#include <sys/cdefs.h>
29222900Snp__FBSDID("$FreeBSD: head/tools/tools/cxgbetool/cxgbetool.c 222900 2011-06-09 20:21:45Z np $");
30222900Snp
31222900Snp#include <stdint.h>
32222900Snp#include <stdlib.h>
33222900Snp#include <errno.h>
34222900Snp#include <err.h>
35222900Snp#include <fcntl.h>
36222900Snp#include <string.h>
37222900Snp#include <stdio.h>
38222900Snp#include <sys/ioctl.h>
39222900Snp#include <sys/types.h>
40222900Snp#include <sys/socket.h>
41222900Snp#include <net/ethernet.h>
42222900Snp#include <netinet/in.h>
43222900Snp#include <arpa/inet.h>
44222900Snp
45222900Snp#include "t4_ioctl.h"
46222900Snp
47222900Snp#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
48222900Snp
49222900Snpstatic const char *progname, *nexus;
50222900Snp
51222900Snpstruct reg_info {
52222900Snp	const char *name;
53222900Snp	uint32_t addr;
54222900Snp	uint32_t len;
55222900Snp};
56222900Snp
57222900Snpstruct mod_regs {
58222900Snp	const char *name;
59222900Snp	const struct reg_info *ri;
60222900Snp};
61222900Snp
62222900Snp#include "reg_defs_t4.c"
63222900Snp#include "reg_defs_t4vf.c"
64222900Snp
65222900Snpstatic void
66222900Snpusage(FILE *fp)
67222900Snp{
68222900Snp	fprintf(fp, "Usage: %s <nexus> [operation]\n", progname);
69222900Snp	fprintf(fp,
70222900Snp	    "\tfilter <idx> [<param> <val>] ...    set a filter\n"
71222900Snp	    "\tfilter <idx> delete|clear           delete a filter\n"
72222900Snp	    "\tfilter list                         list all filters\n"
73222900Snp	    "\tfilter mode [<match>] ...           get/set global filter mode\n"
74222900Snp	    "\treg <address>[=<val>]               read/write register\n"
75222900Snp	    "\treg64 <address>[=<val>]             read/write 64 bit register\n"
76222900Snp	    "\tregdump [<module>] ...              dump registers\n"
77222900Snp	    "\tstdio                               interactive mode\n"
78222900Snp	    );
79222900Snp}
80222900Snp
81222900Snpstatic inline unsigned int
82222900Snpget_card_vers(unsigned int version)
83222900Snp{
84222900Snp	return (version & 0x3ff);
85222900Snp}
86222900Snp
87222900Snpstatic int
88222900Snpreal_doit(unsigned long cmd, void *data, const char *cmdstr)
89222900Snp{
90222900Snp	static int fd = -1;
91222900Snp	int rc = 0;
92222900Snp
93222900Snp	if (fd == -1) {
94222900Snp		char buf[64];
95222900Snp
96222900Snp		snprintf(buf, sizeof(buf), "/dev/%s", nexus);
97222900Snp		if ((fd = open(buf, O_RDWR)) < 0) {
98222900Snp			warn("open(%s)", nexus);
99222900Snp			rc = errno;
100222900Snp			return (rc);
101222900Snp		}
102222900Snp	}
103222900Snp
104222900Snp	rc = ioctl(fd, cmd, data);
105222900Snp	if (rc < 0) {
106222900Snp		warn("%s", cmdstr);
107222900Snp		rc = errno;
108222900Snp	}
109222900Snp
110222900Snp	return (rc);
111222900Snp}
112222900Snp#define doit(x, y) real_doit(x, y, #x)
113222900Snp
114222900Snpstatic char *
115222900Snpstr_to_number(const char *s, long *val, long long *vall)
116222900Snp{
117222900Snp	char *p;
118222900Snp
119222900Snp	if (vall)
120222900Snp		*vall = strtoll(s, &p, 0);
121222900Snp	else if (val)
122222900Snp		*val = strtol(s, &p, 0);
123222900Snp	else
124222900Snp		p = NULL;
125222900Snp
126222900Snp	return (p);
127222900Snp}
128222900Snp
129222900Snpstatic int
130222900Snpread_reg(long addr, int size, long long *val)
131222900Snp{
132222900Snp	struct t4_reg reg;
133222900Snp	int rc;
134222900Snp
135222900Snp	reg.addr = (uint32_t) addr;
136222900Snp	reg.size = (uint32_t) size;
137222900Snp	reg.val = 0;
138222900Snp
139222900Snp	rc = doit(CHELSIO_T4_GETREG, &reg);
140222900Snp
141222900Snp	*val = reg.val;
142222900Snp
143222900Snp	return (rc);
144222900Snp}
145222900Snp
146222900Snpstatic int
147222900Snpwrite_reg(long addr, int size, long long val)
148222900Snp{
149222900Snp	struct t4_reg reg;
150222900Snp
151222900Snp	reg.addr = (uint32_t) addr;
152222900Snp	reg.size = (uint32_t) size;
153222900Snp	reg.val = (uint64_t) val;
154222900Snp
155222900Snp	return doit(CHELSIO_T4_SETREG, &reg);
156222900Snp}
157222900Snp
158222900Snpstatic int
159222900Snpregister_io(int argc, const char *argv[], int size)
160222900Snp{
161222900Snp	char *p, *v;
162222900Snp	long addr;
163222900Snp	long long val;
164222900Snp	int w = 0, rc;
165222900Snp
166222900Snp	if (argc == 1) {
167222900Snp		/* <reg> OR <reg>=<value> */
168222900Snp
169222900Snp		p = str_to_number(argv[0], &addr, NULL);
170222900Snp		if (*p) {
171222900Snp			if (*p != '=') {
172222900Snp				warnx("invalid register \"%s\"", argv[0]);
173222900Snp				return (EINVAL);
174222900Snp			}
175222900Snp
176222900Snp			w = 1;
177222900Snp			v = p + 1;
178222900Snp			p = str_to_number(v, NULL, &val);
179222900Snp
180222900Snp			if (*p) {
181222900Snp				warnx("invalid value \"%s\"", v);
182222900Snp				return (EINVAL);
183222900Snp			}
184222900Snp		}
185222900Snp
186222900Snp	} else if (argc == 2) {
187222900Snp		/* <reg> <value> */
188222900Snp
189222900Snp		w = 1;
190222900Snp
191222900Snp		p = str_to_number(argv[0], &addr, NULL);
192222900Snp		if (*p) {
193222900Snp			warnx("invalid register \"%s\"", argv[0]);
194222900Snp			return (EINVAL);
195222900Snp		}
196222900Snp
197222900Snp		p = str_to_number(argv[1], NULL, &val);
198222900Snp		if (*p) {
199222900Snp			warnx("invalid value \"%s\"", argv[1]);
200222900Snp			return (EINVAL);
201222900Snp		}
202222900Snp	} else {
203222900Snp		warnx("reg: invalid number of arguments (%d)", argc);
204222900Snp		return (EINVAL);
205222900Snp	}
206222900Snp
207222900Snp	if (w)
208222900Snp		rc = write_reg(addr, size, val);
209222900Snp	else {
210222900Snp		rc = read_reg(addr, size, &val);
211222900Snp		if (rc == 0)
212222900Snp			printf("0x%llx [%llu]\n", val, val);
213222900Snp	}
214222900Snp
215222900Snp	return (rc);
216222900Snp}
217222900Snp
218222900Snpstatic inline uint32_t
219222900Snpxtract(uint32_t val, int shift, int len)
220222900Snp{
221222900Snp	return (val >> shift) & ((1 << len) - 1);
222222900Snp}
223222900Snp
224222900Snpstatic int
225222900Snpdump_block_regs(const struct reg_info *reg_array, const uint32_t *regs)
226222900Snp{
227222900Snp	uint32_t reg_val = 0;
228222900Snp
229222900Snp	for ( ; reg_array->name; ++reg_array)
230222900Snp		if (!reg_array->len) {
231222900Snp			reg_val = regs[reg_array->addr / 4];
232222900Snp			printf("[%#7x] %-47s %#-10x %u\n", reg_array->addr,
233222900Snp			       reg_array->name, reg_val, reg_val);
234222900Snp		} else {
235222900Snp			uint32_t v = xtract(reg_val, reg_array->addr,
236222900Snp					    reg_array->len);
237222900Snp
238222900Snp			printf("    %*u:%u %-47s %#-10x %u\n",
239222900Snp			       reg_array->addr < 10 ? 3 : 2,
240222900Snp			       reg_array->addr + reg_array->len - 1,
241222900Snp			       reg_array->addr, reg_array->name, v, v);
242222900Snp		}
243222900Snp
244222900Snp	return (1);
245222900Snp}
246222900Snp
247222900Snpstatic int
248222900Snpdump_regs_table(int argc, const char *argv[], const uint32_t *regs,
249222900Snp    const struct mod_regs *modtab, int nmodules)
250222900Snp{
251222900Snp	int i, j, match;
252222900Snp
253222900Snp	for (i = 0; i < argc; i++) {
254222900Snp		for (j = 0; j < nmodules; j++) {
255222900Snp			if (!strcmp(argv[i], modtab[j].name))
256222900Snp				break;
257222900Snp		}
258222900Snp
259222900Snp		if (j == nmodules) {
260222900Snp			warnx("invalid register block \"%s\"", argv[i]);
261222900Snp			fprintf(stderr, "\nAvailable blocks:");
262222900Snp			for ( ; nmodules; nmodules--, modtab++)
263222900Snp				fprintf(stderr, " %s", modtab->name);
264222900Snp			fprintf(stderr, "\n");
265222900Snp			return (EINVAL);
266222900Snp		}
267222900Snp	}
268222900Snp
269222900Snp	for ( ; nmodules; nmodules--, modtab++) {
270222900Snp
271222900Snp		match = argc == 0 ? 1 : 0;
272222900Snp		for (i = 0; !match && i < argc; i++) {
273222900Snp			if (!strcmp(argv[i], modtab->name))
274222900Snp				match = 1;
275222900Snp		}
276222900Snp
277222900Snp		if (match)
278222900Snp			dump_block_regs(modtab->ri, regs);
279222900Snp	}
280222900Snp
281222900Snp	return (0);
282222900Snp}
283222900Snp
284222900Snp#define T4_MODREGS(name) { #name, t4_##name##_regs }
285222900Snpstatic int
286222900Snpdump_regs_t4(int argc, const char *argv[], const uint32_t *regs)
287222900Snp{
288222900Snp	static struct mod_regs t4_mod[] = {
289222900Snp		T4_MODREGS(sge),
290222900Snp		{ "pci", t4_pcie_regs },
291222900Snp		T4_MODREGS(dbg),
292222900Snp		T4_MODREGS(mc),
293222900Snp		T4_MODREGS(ma),
294222900Snp		{ "edc0", t4_edc_0_regs },
295222900Snp		{ "edc1", t4_edc_1_regs },
296222900Snp		T4_MODREGS(cim),
297222900Snp		T4_MODREGS(tp),
298222900Snp		T4_MODREGS(ulp_rx),
299222900Snp		T4_MODREGS(ulp_tx),
300222900Snp		{ "pmrx", t4_pm_rx_regs },
301222900Snp		{ "pmtx", t4_pm_tx_regs },
302222900Snp		T4_MODREGS(mps),
303222900Snp		{ "cplsw", t4_cpl_switch_regs },
304222900Snp		T4_MODREGS(smb),
305222900Snp		{ "i2c", t4_i2cm_regs },
306222900Snp		T4_MODREGS(mi),
307222900Snp		T4_MODREGS(uart),
308222900Snp		T4_MODREGS(pmu),
309222900Snp		T4_MODREGS(sf),
310222900Snp		T4_MODREGS(pl),
311222900Snp		T4_MODREGS(le),
312222900Snp		T4_MODREGS(ncsi),
313222900Snp		T4_MODREGS(xgmac)
314222900Snp	};
315222900Snp
316222900Snp	return dump_regs_table(argc, argv, regs, t4_mod, ARRAY_SIZE(t4_mod));
317222900Snp}
318222900Snp#undef T4_MODREGS
319222900Snp
320222900Snpstatic int
321222900Snpdump_regs_t4vf(int argc, const char *argv[], const uint32_t *regs)
322222900Snp{
323222900Snp	static struct mod_regs t4vf_mod[] = {
324222900Snp		{ "sge", t4vf_sge_regs },
325222900Snp		{ "mps", t4vf_mps_regs },
326222900Snp		{ "pl", t4vf_pl_regs },
327222900Snp		{ "mbdata", t4vf_mbdata_regs },
328222900Snp		{ "cim", t4vf_cim_regs },
329222900Snp	};
330222900Snp
331222900Snp	return dump_regs_table(argc, argv, regs, t4vf_mod,
332222900Snp	    ARRAY_SIZE(t4vf_mod));
333222900Snp}
334222900Snp
335222900Snpstatic int
336222900Snpdump_regs(int argc, const char *argv[])
337222900Snp{
338222900Snp	int vers, revision, is_pcie, rc;
339222900Snp	struct t4_regdump regs;
340222900Snp
341222900Snp	regs.data = calloc(1, T4_REGDUMP_SIZE);
342222900Snp	if (regs.data == NULL) {
343222900Snp		warnc(ENOMEM, "regdump");
344222900Snp		return (ENOMEM);
345222900Snp	}
346222900Snp
347222900Snp	regs.len = T4_REGDUMP_SIZE;
348222900Snp	rc = doit(CHELSIO_T4_REGDUMP, &regs);
349222900Snp	if (rc != 0)
350222900Snp		return (rc);
351222900Snp
352222900Snp	vers = get_card_vers(regs.version);
353222900Snp	revision = (regs.version >> 10) & 0x3f;
354222900Snp	is_pcie = (regs.version & 0x80000000) != 0;
355222900Snp
356222900Snp	if (vers == 4) {
357222900Snp		if (revision == 0x3f)
358222900Snp			rc = dump_regs_t4vf(argc, argv, regs.data);
359222900Snp		else
360222900Snp			rc = dump_regs_t4(argc, argv, regs.data);
361222900Snp	} else {
362222900Snp		warnx("%s (type %d, rev %d) is not a T4 card.",
363222900Snp		    nexus, vers, revision);
364222900Snp		return (ENOTSUP);
365222900Snp	}
366222900Snp
367222900Snp	free(regs.data);
368222900Snp	return (rc);
369222900Snp}
370222900Snp
371222900Snpstatic void
372222900Snpdo_show_info_header(uint32_t mode)
373222900Snp{
374222900Snp	uint32_t i;
375222900Snp
376222900Snp	printf ("%4s %8s", "Idx", "Hits");
377222900Snp	for (i = T4_FILTER_FCoE; i <= T4_FILTER_IP_FRAGMENT; i <<= 1) {
378222900Snp		switch (mode & i) {
379222900Snp		case T4_FILTER_FCoE:
380222900Snp			printf (" FCoE");
381222900Snp			break;
382222900Snp
383222900Snp		case T4_FILTER_PORT:
384222900Snp			printf (" Port");
385222900Snp			break;
386222900Snp
387222900Snp		case T4_FILTER_OVLAN:
388222900Snp			printf ("     vld:oVLAN");
389222900Snp			break;
390222900Snp
391222900Snp		case T4_FILTER_IVLAN:
392222900Snp			printf ("     vld:iVLAN");
393222900Snp			break;
394222900Snp
395222900Snp		case T4_FILTER_IP_TOS:
396222900Snp			printf ("   TOS");
397222900Snp			break;
398222900Snp
399222900Snp		case T4_FILTER_IP_PROTO:
400222900Snp			printf ("  Prot");
401222900Snp			break;
402222900Snp
403222900Snp		case T4_FILTER_ETH_TYPE:
404222900Snp			printf ("   EthType");
405222900Snp			break;
406222900Snp
407222900Snp		case T4_FILTER_MAC_IDX:
408222900Snp			printf ("  MACIdx");
409222900Snp			break;
410222900Snp
411222900Snp		case T4_FILTER_MPS_HIT_TYPE:
412222900Snp			printf (" MPS");
413222900Snp			break;
414222900Snp
415222900Snp		case T4_FILTER_IP_FRAGMENT:
416222900Snp			printf (" Frag");
417222900Snp			break;
418222900Snp
419222900Snp		default:
420222900Snp			/* compressed filter field not enabled */
421222900Snp			break;
422222900Snp		}
423222900Snp	}
424222900Snp	printf(" %20s %20s %9s %9s %s\n",
425222900Snp	    "DIP", "SIP", "DPORT", "SPORT", "Action");
426222900Snp}
427222900Snp
428222900Snp/*
429222900Snp * Parse an argument sub-vector as a { <parameter name> <value>[:<mask>] }
430222900Snp * ordered tuple.  If the parameter name in the argument sub-vector does not
431222900Snp * match the passed in parameter name, then a zero is returned for the
432222900Snp * function and no parsing is performed.  If there is a match, then the value
433222900Snp * and optional mask are parsed and returned in the provided return value
434222900Snp * pointers.  If no optional mask is specified, then a default mask of all 1s
435222900Snp * will be returned.
436222900Snp *
437222900Snp * An error in parsing the value[:mask] will result in an error message and
438222900Snp * program termination.
439222900Snp */
440222900Snpstatic int
441222900Snpparse_val_mask(const char *param, const char *args[], uint32_t *val,
442222900Snp    uint32_t *mask)
443222900Snp{
444222900Snp	char *p;
445222900Snp
446222900Snp	if (strcmp(param, args[0]) != 0)
447222900Snp		return (EINVAL);
448222900Snp
449222900Snp	*val = strtoul(args[1], &p, 0);
450222900Snp	if (p > args[1]) {
451222900Snp		if (p[0] == 0) {
452222900Snp			*mask = ~0;
453222900Snp			return (0);
454222900Snp		}
455222900Snp
456222900Snp		if (p[0] == ':' && p[1] != 0) {
457222900Snp			*mask = strtoul(p+1, &p, 0);
458222900Snp			if (p[0] == 0)
459222900Snp				return (0);
460222900Snp		}
461222900Snp	}
462222900Snp
463222900Snp	warnx("parameter \"%s\" has bad \"value[:mask]\" %s",
464222900Snp	    args[0], args[1]);
465222900Snp
466222900Snp	return (EINVAL);
467222900Snp}
468222900Snp
469222900Snp/*
470222900Snp * Parse an argument sub-vector as a { <parameter name> <addr>[/<mask>] }
471222900Snp * ordered tuple.  If the parameter name in the argument sub-vector does not
472222900Snp * match the passed in parameter name, then a zero is returned for the
473222900Snp * function and no parsing is performed.  If there is a match, then the value
474222900Snp * and optional mask are parsed and returned in the provided return value
475222900Snp * pointers.  If no optional mask is specified, then a default mask of all 1s
476222900Snp * will be returned.
477222900Snp *
478222900Snp * The value return parameter "afp" is used to specify the expected address
479222900Snp * family -- IPv4 or IPv6 -- of the address[/mask] and return its actual
480222900Snp * format.  A passed in value of AF_UNSPEC indicates that either IPv4 or IPv6
481222900Snp * is acceptable; AF_INET means that only IPv4 addresses are acceptable; and
482222900Snp * AF_INET6 means that only IPv6 are acceptable.  AF_INET is returned for IPv4
483222900Snp * and AF_INET6 for IPv6 addresses, respectively.  IPv4 address/mask pairs are
484222900Snp * returned in the first four bytes of the address and mask return values with
485222900Snp * the address A.B.C.D returned with { A, B, C, D } returned in addresses { 0,
486222900Snp * 1, 2, 3}, respectively.
487222900Snp *
488222900Snp * An error in parsing the value[:mask] will result in an error message and
489222900Snp * program termination.
490222900Snp */
491222900Snpstatic int
492222900Snpparse_ipaddr(const char *param, const char *args[], int *afp, uint8_t addr[],
493222900Snp    uint8_t mask[])
494222900Snp{
495222900Snp	const char *colon, *afn;
496222900Snp	char *slash;
497222900Snp	uint8_t *m;
498222900Snp	int af, ret;
499222900Snp	unsigned int masksize;
500222900Snp
501222900Snp	/*
502222900Snp	 * Is this our parameter?
503222900Snp	 */
504222900Snp	if (strcmp(param, args[0]) != 0)
505222900Snp		return (EINVAL);
506222900Snp
507222900Snp	/*
508222900Snp	 * Fundamental IPv4 versus IPv6 selection.
509222900Snp	 */
510222900Snp	colon = strchr(args[1], ':');
511222900Snp	if (!colon) {
512222900Snp		afn = "IPv4";
513222900Snp		af = AF_INET;
514222900Snp		masksize = 32;
515222900Snp	} else {
516222900Snp		afn = "IPv6";
517222900Snp		af = AF_INET6;
518222900Snp		masksize = 128;
519222900Snp	}
520222900Snp	if (*afp == AF_UNSPEC)
521222900Snp		*afp = af;
522222900Snp	else if (*afp != af) {
523222900Snp		warnx("address %s is not of expected family %s",
524222900Snp		    args[1], *afp == AF_INET ? "IP" : "IPv6");
525222900Snp		return (EINVAL);
526222900Snp	}
527222900Snp
528222900Snp	/*
529222900Snp	 * Parse address (temporarily stripping off any "/mask"
530222900Snp	 * specification).
531222900Snp	 */
532222900Snp	slash = strchr(args[1], '/');
533222900Snp	if (slash)
534222900Snp		*slash = 0;
535222900Snp	ret = inet_pton(af, args[1], addr);
536222900Snp	if (slash)
537222900Snp		*slash = '/';
538222900Snp	if (ret <= 0) {
539222900Snp		warnx("Cannot parse %s %s address %s", param, afn, args[1]);
540222900Snp		return (EINVAL);
541222900Snp	}
542222900Snp
543222900Snp	/*
544222900Snp	 * Parse optional mask specification.
545222900Snp	 */
546222900Snp	if (slash) {
547222900Snp		char *p;
548222900Snp		unsigned int prefix = strtoul(slash + 1, &p, 10);
549222900Snp
550222900Snp		if (p == slash + 1) {
551222900Snp			warnx("missing address prefix for %s", param);
552222900Snp			return (EINVAL);
553222900Snp		}
554222900Snp		if (*p) {
555222900Snp			warnx("%s is not a valid address prefix", slash + 1);
556222900Snp			return (EINVAL);
557222900Snp		}
558222900Snp		if (prefix > masksize) {
559222900Snp			warnx("prefix %u is too long for an %s address",
560222900Snp			     prefix, afn);
561222900Snp			return (EINVAL);
562222900Snp		}
563222900Snp		memset(mask, 0, masksize / 8);
564222900Snp		masksize = prefix;
565222900Snp	}
566222900Snp
567222900Snp	/*
568222900Snp	 * Fill in mask.
569222900Snp	 */
570222900Snp	for (m = mask; masksize >= 8; m++, masksize -= 8)
571222900Snp		*m = ~0;
572222900Snp	if (masksize)
573222900Snp		*m = ~0 << (8 - masksize);
574222900Snp
575222900Snp	return (0);
576222900Snp}
577222900Snp
578222900Snp/*
579222900Snp * Parse an argument sub-vector as a { <parameter name> <value> } ordered
580222900Snp * tuple.  If the parameter name in the argument sub-vector does not match the
581222900Snp * passed in parameter name, then a zero is returned for the function and no
582222900Snp * parsing is performed.  If there is a match, then the value is parsed and
583222900Snp * returned in the provided return value pointer.
584222900Snp */
585222900Snpstatic int
586222900Snpparse_val(const char *param, const char *args[], uint32_t *val)
587222900Snp{
588222900Snp	char *p;
589222900Snp
590222900Snp	if (strcmp(param, args[0]) != 0)
591222900Snp		return (EINVAL);
592222900Snp
593222900Snp	*val = strtoul(args[1], &p, 0);
594222900Snp	if (p > args[1] && p[0] == 0)
595222900Snp		return (0);
596222900Snp
597222900Snp	warnx("parameter \"%s\" has bad \"value\" %s", args[0], args[1]);
598222900Snp	return (EINVAL);
599222900Snp}
600222900Snp
601222900Snpstatic void
602222900Snpfilters_show_ipaddr(int type, uint8_t *addr, uint8_t *addrm)
603222900Snp{
604222900Snp	int noctets, octet;
605222900Snp
606222900Snp	printf(" ");
607222900Snp	if (type == 0) {
608222900Snp		noctets = 4;
609222900Snp		printf("%3s", " ");
610222900Snp	} else
611222900Snp	noctets = 16;
612222900Snp
613222900Snp	for (octet = 0; octet < noctets; octet++)
614222900Snp		printf("%02x", addr[octet]);
615222900Snp	printf("/");
616222900Snp	for (octet = 0; octet < noctets; octet++)
617222900Snp		printf("%02x", addrm[octet]);
618222900Snp}
619222900Snp
620222900Snpstatic void
621222900Snpdo_show_one_filter_info(struct t4_filter *t, uint32_t mode)
622222900Snp{
623222900Snp	uint32_t i;
624222900Snp
625222900Snp	printf("%4d", t->idx);
626222900Snp	if (t->hits == UINT64_MAX)
627222900Snp		printf(" %8s", "-");
628222900Snp	else
629222900Snp		printf(" %8ju", t->hits);
630222900Snp
631222900Snp	/*
632222900Snp	 * Compressed header portion of filter.
633222900Snp	 */
634222900Snp	for (i = T4_FILTER_FCoE; i <= T4_FILTER_IP_FRAGMENT; i <<= 1) {
635222900Snp		switch (mode & i) {
636222900Snp		case T4_FILTER_FCoE:
637222900Snp			printf("  %1d/%1d", t->fs.val.fcoe, t->fs.mask.fcoe);
638222900Snp			break;
639222900Snp
640222900Snp		case T4_FILTER_PORT:
641222900Snp			printf("  %1d/%1d", t->fs.val.iport, t->fs.mask.iport);
642222900Snp			break;
643222900Snp
644222900Snp		case T4_FILTER_OVLAN:
645222900Snp			printf(" %1d:%1x:%02x/%1d:%1x:%02x",
646222900Snp			    t->fs.val.ovlan_vld, (t->fs.val.ovlan >> 7) & 0x7,
647222900Snp			    t->fs.val.ovlan & 0x7f, t->fs.mask.ovlan_vld,
648222900Snp			    (t->fs.mask.ovlan >> 7) & 0x7,
649222900Snp			    t->fs.mask.ovlan & 0x7f);
650222900Snp			break;
651222900Snp
652222900Snp		case T4_FILTER_IVLAN:
653222900Snp			printf(" %1d:%04x/%1d:%04x",
654222900Snp			    t->fs.val.ivlan_vld, t->fs.val.ivlan,
655222900Snp			    t->fs.mask.ivlan_vld, t->fs.mask.ivlan);
656222900Snp			break;
657222900Snp
658222900Snp		case T4_FILTER_IP_TOS:
659222900Snp			printf(" %02x/%02x", t->fs.val.tos, t->fs.mask.tos);
660222900Snp			break;
661222900Snp
662222900Snp		case T4_FILTER_IP_PROTO:
663222900Snp			printf(" %02x/%02x", t->fs.val.proto, t->fs.mask.proto);
664222900Snp			break;
665222900Snp
666222900Snp		case T4_FILTER_ETH_TYPE:
667222900Snp			printf(" %04x/%04x", t->fs.val.ethtype,
668222900Snp			    t->fs.mask.ethtype);
669222900Snp			break;
670222900Snp
671222900Snp		case T4_FILTER_MAC_IDX:
672222900Snp			printf(" %03x/%03x", t->fs.val.macidx,
673222900Snp			    t->fs.mask.macidx);
674222900Snp			break;
675222900Snp
676222900Snp		case T4_FILTER_MPS_HIT_TYPE:
677222900Snp			printf(" %1x/%1x", t->fs.val.matchtype,
678222900Snp			    t->fs.mask.matchtype);
679222900Snp			break;
680222900Snp
681222900Snp		case T4_FILTER_IP_FRAGMENT:
682222900Snp			printf("  %1d/%1d", t->fs.val.frag, t->fs.mask.frag);
683222900Snp			break;
684222900Snp
685222900Snp		default:
686222900Snp			/* compressed filter field not enabled */
687222900Snp			break;
688222900Snp		}
689222900Snp	}
690222900Snp
691222900Snp	/*
692222900Snp	 * Fixed portion of filter.
693222900Snp	 */
694222900Snp	filters_show_ipaddr(t->fs.type, t->fs.val.dip, t->fs.mask.dip);
695222900Snp	filters_show_ipaddr(t->fs.type, t->fs.val.sip, t->fs.mask.sip);
696222900Snp	printf(" %04x/%04x %04x/%04x",
697222900Snp		 t->fs.val.dport, t->fs.mask.dport,
698222900Snp		 t->fs.val.sport, t->fs.mask.sport);
699222900Snp
700222900Snp	/*
701222900Snp	 * Variable length filter action.
702222900Snp	 */
703222900Snp	if (t->fs.action == FILTER_DROP)
704222900Snp		printf(" Drop");
705222900Snp	else if (t->fs.action == FILTER_SWITCH) {
706222900Snp		printf(" Switch: port=%d", t->fs.eport);
707222900Snp	if (t->fs.newdmac)
708222900Snp		printf(
709222900Snp			", dmac=%02x:%02x:%02x:%02x:%02x:%02x "
710222900Snp			", l2tidx=%d",
711222900Snp			t->fs.dmac[0], t->fs.dmac[1],
712222900Snp			t->fs.dmac[2], t->fs.dmac[3],
713222900Snp			t->fs.dmac[4], t->fs.dmac[5],
714222900Snp			t->l2tidx);
715222900Snp	if (t->fs.newsmac)
716222900Snp		printf(
717222900Snp			", smac=%02x:%02x:%02x:%02x:%02x:%02x "
718222900Snp			", smtidx=%d",
719222900Snp			t->fs.smac[0], t->fs.smac[1],
720222900Snp			t->fs.smac[2], t->fs.smac[3],
721222900Snp			t->fs.smac[4], t->fs.smac[5],
722222900Snp			t->smtidx);
723222900Snp	if (t->fs.newvlan == VLAN_REMOVE)
724222900Snp		printf(", vlan=none");
725222900Snp	else if (t->fs.newvlan == VLAN_INSERT)
726222900Snp		printf(", vlan=insert(%x)", t->fs.vlan);
727222900Snp	else if (t->fs.newvlan == VLAN_REWRITE)
728222900Snp		printf(", vlan=rewrite(%x)", t->fs.vlan);
729222900Snp	} else {
730222900Snp		printf(" Pass: Q=");
731222900Snp		if (t->fs.dirsteer == 0) {
732222900Snp			printf("RSS");
733222900Snp			if (t->fs.maskhash)
734222900Snp				printf("(TCB=hash)");
735222900Snp		} else {
736222900Snp			printf("%d", t->fs.iq);
737222900Snp			if (t->fs.dirsteerhash == 0)
738222900Snp				printf("(QID)");
739222900Snp			else
740222900Snp				printf("(hash)");
741222900Snp		}
742222900Snp	}
743222900Snp	if (t->fs.prio)
744222900Snp		printf(" Prio");
745222900Snp	if (t->fs.rpttid)
746222900Snp		printf(" RptTID");
747222900Snp	printf("\n");
748222900Snp}
749222900Snp
750222900Snpstatic int
751222900Snpshow_filters(void)
752222900Snp{
753222900Snp	uint32_t mode = 0, header = 0;
754222900Snp	struct t4_filter t;
755222900Snp	int rc;
756222900Snp
757222900Snp	/* Get the global filter mode first */
758222900Snp	rc = doit(CHELSIO_T4_GET_FILTER_MODE, &mode);
759222900Snp	if (rc != 0)
760222900Snp		return (rc);
761222900Snp
762222900Snp	t.idx = 0;
763222900Snp	for (t.idx = 0; ; t.idx++) {
764222900Snp		rc = doit(CHELSIO_T4_GET_FILTER, &t);
765222900Snp		if (rc != 0 || t.idx == 0xffffffff)
766222900Snp			break;
767222900Snp
768222900Snp		if (!header) {
769222900Snp			do_show_info_header(mode);
770222900Snp			header = 1;
771222900Snp		}
772222900Snp		do_show_one_filter_info(&t, mode);
773222900Snp	};
774222900Snp
775222900Snp	return (rc);
776222900Snp}
777222900Snp
778222900Snpstatic int
779222900Snpget_filter_mode(void)
780222900Snp{
781222900Snp	uint32_t mode = 0;
782222900Snp	int rc;
783222900Snp
784222900Snp	rc = doit(CHELSIO_T4_GET_FILTER_MODE, &mode);
785222900Snp	if (rc != 0)
786222900Snp		return (rc);
787222900Snp
788222900Snp	if (mode & T4_FILTER_IPv4)
789222900Snp		printf("ipv4 ");
790222900Snp
791222900Snp	if (mode & T4_FILTER_IPv6)
792222900Snp		printf("ipv6 ");
793222900Snp
794222900Snp	if (mode & T4_FILTER_IP_SADDR)
795222900Snp		printf("sip ");
796222900Snp
797222900Snp	if (mode & T4_FILTER_IP_DADDR)
798222900Snp		printf("dip ");
799222900Snp
800222900Snp	if (mode & T4_FILTER_IP_SPORT)
801222900Snp		printf("sport ");
802222900Snp
803222900Snp	if (mode & T4_FILTER_IP_DPORT)
804222900Snp		printf("dport ");
805222900Snp
806222900Snp	if (mode & T4_FILTER_MPS_HIT_TYPE)
807222900Snp		printf("matchtype ");
808222900Snp
809222900Snp	if (mode & T4_FILTER_MAC_IDX)
810222900Snp		printf("macidx ");
811222900Snp
812222900Snp	if (mode & T4_FILTER_ETH_TYPE)
813222900Snp		printf("ethtype ");
814222900Snp
815222900Snp	if (mode & T4_FILTER_IP_PROTO)
816222900Snp		printf("proto ");
817222900Snp
818222900Snp	if (mode & T4_FILTER_IP_TOS)
819222900Snp		printf("tos ");
820222900Snp
821222900Snp	if (mode & T4_FILTER_IVLAN)
822222900Snp		printf("ivlan ");
823222900Snp
824222900Snp	if (mode & T4_FILTER_OVLAN)
825222900Snp		printf("ovlan ");
826222900Snp
827222900Snp	if (mode & T4_FILTER_PORT)
828222900Snp		printf("iport ");
829222900Snp
830222900Snp	if (mode & T4_FILTER_FCoE)
831222900Snp		printf("fcoe ");
832222900Snp
833222900Snp	printf("\n");
834222900Snp
835222900Snp	return (0);
836222900Snp}
837222900Snp
838222900Snpstatic int
839222900Snpset_filter_mode(int argc, const char *argv[])
840222900Snp{
841222900Snp	uint32_t mode = 0;
842222900Snp
843222900Snp	for (; argc; argc--, argv++) {
844222900Snp		if (!strcmp(argv[0], "matchtype"))
845222900Snp			mode |= T4_FILTER_MPS_HIT_TYPE;
846222900Snp
847222900Snp		if (!strcmp(argv[0], "macidx"))
848222900Snp			mode |= T4_FILTER_MAC_IDX;
849222900Snp
850222900Snp		if (!strcmp(argv[0], "ethtype"))
851222900Snp			mode |= T4_FILTER_ETH_TYPE;
852222900Snp
853222900Snp		if (!strcmp(argv[0], "proto"))
854222900Snp			mode |= T4_FILTER_IP_PROTO;
855222900Snp
856222900Snp		if (!strcmp(argv[0], "tos"))
857222900Snp			mode |= T4_FILTER_IP_TOS;
858222900Snp
859222900Snp		if (!strcmp(argv[0], "ivlan"))
860222900Snp			mode |= T4_FILTER_IVLAN;
861222900Snp
862222900Snp		if (!strcmp(argv[0], "ovlan"))
863222900Snp			mode |= T4_FILTER_OVLAN;
864222900Snp
865222900Snp		if (!strcmp(argv[0], "iport"))
866222900Snp			mode |= T4_FILTER_PORT;
867222900Snp
868222900Snp		if (!strcmp(argv[0], "fcoe"))
869222900Snp			mode |= T4_FILTER_FCoE;
870222900Snp	}
871222900Snp
872222900Snp	return doit(CHELSIO_T4_SET_FILTER_MODE, &mode);
873222900Snp}
874222900Snp
875222900Snpstatic int
876222900Snpdel_filter(uint32_t idx)
877222900Snp{
878222900Snp	struct t4_filter t;
879222900Snp
880222900Snp	t.idx = idx;
881222900Snp
882222900Snp	return doit(CHELSIO_T4_DEL_FILTER, &t);
883222900Snp}
884222900Snp
885222900Snpstatic int
886222900Snpset_filter(uint32_t idx, int argc, const char *argv[])
887222900Snp{
888222900Snp	int af = AF_UNSPEC, start_arg = 0;
889222900Snp	struct t4_filter t;
890222900Snp
891222900Snp	if (argc < 2) {
892222900Snp		warnc(EINVAL, "%s", __func__);
893222900Snp		return (EINVAL);
894222900Snp	};
895222900Snp	bzero(&t, sizeof (t));
896222900Snp	t.idx = idx;
897222900Snp
898222900Snp	for (start_arg = 0; start_arg + 2 <= argc; start_arg += 2) {
899222900Snp		const char **args = &argv[start_arg];
900222900Snp		uint32_t val, mask;
901222900Snp
902222900Snp		if (!strcmp(argv[start_arg], "type")) {
903222900Snp			int newaf;
904222900Snp			if (!strcasecmp(argv[start_arg + 1], "ipv4"))
905222900Snp				newaf = AF_INET;
906222900Snp			else if (!strcasecmp(argv[start_arg + 1], "ipv6"))
907222900Snp				newaf = AF_INET6;
908222900Snp			else {
909222900Snp				warnx("invalid type \"%s\"; "
910222900Snp				    "must be one of \"ipv4\" or \"ipv6\"",
911222900Snp				    argv[start_arg + 1]);
912222900Snp				return (EINVAL);
913222900Snp			}
914222900Snp
915222900Snp			if (af != AF_UNSPEC && af != newaf) {
916222900Snp				warnx("conflicting IPv4/IPv6 specifications.");
917222900Snp				return (EINVAL);
918222900Snp			}
919222900Snp			af = newaf;
920222900Snp		} else if (!parse_val_mask("fcoe", args, &val, &mask)) {
921222900Snp			t.fs.val.fcoe = val;
922222900Snp			t.fs.mask.fcoe = mask;
923222900Snp		} else if (!parse_val_mask("iport", args, &val, &mask)) {
924222900Snp			t.fs.val.iport = val;
925222900Snp			t.fs.mask.iport = mask;
926222900Snp		} else if (!parse_val_mask("ovlan", args, &val, &mask)) {
927222900Snp			t.fs.val.ovlan = val;
928222900Snp			t.fs.mask.ovlan = mask;
929222900Snp			t.fs.val.ovlan_vld = 1;
930222900Snp			t.fs.mask.ovlan_vld = 1;
931222900Snp		} else if (!parse_val_mask("ivlan", args, &val, &mask)) {
932222900Snp			t.fs.val.ivlan = val;
933222900Snp			t.fs.mask.ivlan = mask;
934222900Snp			t.fs.val.ivlan_vld = 1;
935222900Snp			t.fs.mask.ivlan_vld = 1;
936222900Snp		} else if (!parse_val_mask("tos", args, &val, &mask)) {
937222900Snp			t.fs.val.tos = val;
938222900Snp			t.fs.mask.tos = mask;
939222900Snp		} else if (!parse_val_mask("proto", args, &val, &mask)) {
940222900Snp			t.fs.val.proto = val;
941222900Snp			t.fs.mask.proto = mask;
942222900Snp		} else if (!parse_val_mask("ethtype", args, &val, &mask)) {
943222900Snp			t.fs.val.ethtype = val;
944222900Snp			t.fs.mask.ethtype = mask;
945222900Snp		} else if (!parse_val_mask("macidx", args, &val, &mask)) {
946222900Snp			t.fs.val.macidx = val;
947222900Snp			t.fs.mask.macidx = mask;
948222900Snp		} else if (!parse_val_mask("matchtype", args, &val, &mask)) {
949222900Snp			t.fs.val.matchtype = val;
950222900Snp			t.fs.mask.matchtype = mask;
951222900Snp		} else if (!parse_val_mask("frag", args, &val, &mask)) {
952222900Snp			t.fs.val.frag = val;
953222900Snp			t.fs.mask.frag = mask;
954222900Snp		} else if (!parse_val_mask("dport", args, &val, &mask)) {
955222900Snp			t.fs.val.dport = val;
956222900Snp			t.fs.mask.dport = mask;
957222900Snp		} else if (!parse_val_mask("sport", args, &val, &mask)) {
958222900Snp			t.fs.val.sport = val;
959222900Snp			t.fs.mask.sport = mask;
960222900Snp		} else if (!parse_ipaddr("dip", args, &af, t.fs.val.dip,
961222900Snp		    t.fs.mask.dip)) {
962222900Snp			/* nada */;
963222900Snp		} else if (!parse_ipaddr("sip", args, &af, t.fs.val.sip,
964222900Snp		    t.fs.mask.sip)) {
965222900Snp			/* nada */;
966222900Snp		} else if (!strcmp(argv[start_arg], "action")) {
967222900Snp			if (!strcmp(argv[start_arg + 1], "pass"))
968222900Snp				t.fs.action = FILTER_PASS;
969222900Snp			else if (!strcmp(argv[start_arg + 1], "drop"))
970222900Snp				t.fs.action = FILTER_DROP;
971222900Snp			else if (!strcmp(argv[start_arg + 1], "switch"))
972222900Snp				t.fs.action = FILTER_SWITCH;
973222900Snp			else {
974222900Snp				warnx("invalid action \"%s\"; must be one of"
975222900Snp				     " \"pass\", \"drop\" or \"switch\"",
976222900Snp				     argv[start_arg + 1]);
977222900Snp				return (EINVAL);
978222900Snp			}
979222900Snp		} else if (!parse_val("hitcnts", args, &val)) {
980222900Snp			t.fs.hitcnts = val;
981222900Snp		} else if (!parse_val("prio", args, &val)) {
982222900Snp			t.fs.prio = val;
983222900Snp		} else if (!parse_val("rpttid", args, &val)) {
984222900Snp			t.fs.rpttid = 1;
985222900Snp		} else if (!parse_val("queue", args, &val)) {
986222900Snp			t.fs.dirsteer = 1;
987222900Snp			t.fs.iq = val;
988222900Snp		} else if (!parse_val("tcbhash", args, &val)) {
989222900Snp			t.fs.maskhash = 1;
990222900Snp			t.fs.dirsteerhash = 1;
991222900Snp		} else if (!parse_val("eport", args, &val)) {
992222900Snp			t.fs.eport = val;
993222900Snp		} else if (!strcmp(argv[start_arg], "dmac")) {
994222900Snp			struct ether_addr *daddr;
995222900Snp
996222900Snp			daddr = ether_aton(argv[start_arg + 1]);
997222900Snp			if (daddr == NULL) {
998222900Snp				warnx("invalid dmac address \"%s\"",
999222900Snp				    argv[start_arg + 1]);
1000222900Snp				return (EINVAL);
1001222900Snp			}
1002222900Snp			memcpy(t.fs.dmac, daddr, ETHER_ADDR_LEN);
1003222900Snp			t.fs.newdmac = 1;
1004222900Snp		} else if (!strcmp(argv[start_arg], "smac")) {
1005222900Snp			struct ether_addr *saddr;
1006222900Snp
1007222900Snp			saddr = ether_aton(argv[start_arg + 1]);
1008222900Snp			if (saddr == NULL) {
1009222900Snp				warnx("invalid smac address \"%s\"",
1010222900Snp				    argv[start_arg + 1]);
1011222900Snp				return (EINVAL);
1012222900Snp			}
1013222900Snp			memcpy(t.fs.smac, saddr, ETHER_ADDR_LEN);
1014222900Snp			t.fs.newsmac = 1;
1015222900Snp		} else if (!strcmp(argv[start_arg], "vlan")) {
1016222900Snp			char *p;
1017222900Snp			if (!strcmp(argv[start_arg + 1], "none")) {
1018222900Snp				t.fs.newvlan = VLAN_REMOVE;
1019222900Snp			} else if (argv[start_arg + 1][0] == '=') {
1020222900Snp				t.fs.newvlan = VLAN_REWRITE;
1021222900Snp			} else if (argv[start_arg + 1][0] == '+') {
1022222900Snp				t.fs.newvlan = VLAN_INSERT;
1023222900Snp			} else {
1024222900Snp				warnx("unknown vlan parameter \"%s\"; must"
1025222900Snp				     " be one of \"none\", \"=<vlan>\" or"
1026222900Snp				     " \"+<vlan>\"", argv[start_arg + 1]);
1027222900Snp				return (EINVAL);
1028222900Snp			}
1029222900Snp			if (t.fs.newvlan == VLAN_REWRITE ||
1030222900Snp			    t.fs.newvlan == VLAN_INSERT) {
1031222900Snp				t.fs.vlan = strtoul(argv[start_arg + 1] + 1,
1032222900Snp				    &p, 0);
1033222900Snp				if (p == argv[start_arg + 1] + 1 || p[0] != 0) {
1034222900Snp					warnx("invalid vlan \"%s\"",
1035222900Snp					     argv[start_arg + 1]);
1036222900Snp					return (EINVAL);
1037222900Snp				}
1038222900Snp			}
1039222900Snp		} else {
1040222900Snp			warnx("invalid parameter \"%s\"", argv[start_arg]);
1041222900Snp			return (EINVAL);
1042222900Snp		}
1043222900Snp	}
1044222900Snp	if (start_arg != argc) {
1045222900Snp		warnx("no value for \"%s\"", argv[start_arg]);
1046222900Snp		return (EINVAL);
1047222900Snp	}
1048222900Snp
1049222900Snp	/*
1050222900Snp	 * Check basic sanity of option combinations.
1051222900Snp	 */
1052222900Snp	if (t.fs.action != FILTER_SWITCH &&
1053222900Snp	    (t.fs.eport || t.fs.newdmac || t.fs.newsmac || t.fs.newvlan)) {
1054222900Snp		warnx("prio, port dmac, smac and vlan only make sense with"
1055222900Snp		     " \"action switch\"");
1056222900Snp		return (EINVAL);
1057222900Snp	}
1058222900Snp	if (t.fs.action != FILTER_PASS &&
1059222900Snp	    (t.fs.rpttid || t.fs.dirsteer || t.fs.maskhash)) {
1060222900Snp		warnx("rpttid, queue and tcbhash don't make sense with"
1061222900Snp		     " action \"drop\" or \"switch\"");
1062222900Snp		return (EINVAL);
1063222900Snp	}
1064222900Snp
1065222900Snp	t.fs.type = (af == AF_INET6 ? 1 : 0); /* default IPv4 */
1066222900Snp	return doit(CHELSIO_T4_SET_FILTER, &t);
1067222900Snp}
1068222900Snp
1069222900Snpstatic int
1070222900Snpfilter_cmd(int argc, const char *argv[])
1071222900Snp{
1072222900Snp	long long val;
1073222900Snp	uint32_t idx;
1074222900Snp	char *s;
1075222900Snp
1076222900Snp	if (argc == 0) {
1077222900Snp		warnx("filter: no arguments.");
1078222900Snp		return (EINVAL);
1079222900Snp	};
1080222900Snp
1081222900Snp	/* list */
1082222900Snp	if (strcmp(argv[0], "list") == 0) {
1083222900Snp		if (argc != 1)
1084222900Snp			warnx("trailing arguments after \"list\" ignored.");
1085222900Snp
1086222900Snp		return show_filters();
1087222900Snp	}
1088222900Snp
1089222900Snp	/* mode */
1090222900Snp	if (argc == 1 && strcmp(argv[0], "mode") == 0)
1091222900Snp		return get_filter_mode();
1092222900Snp
1093222900Snp	/* mode <mode> */
1094222900Snp	if (strcmp(argv[0], "mode") == 0)
1095222900Snp		return set_filter_mode(argc - 1, argv + 1);
1096222900Snp
1097222900Snp	/* <idx> ... */
1098222900Snp	s = str_to_number(argv[0], NULL, &val);
1099222900Snp	if (*s || val > 0xffffffffU) {
1100222900Snp		warnx("\"%s\" is neither an index nor a filter subcommand.",
1101222900Snp		    argv[0]);
1102222900Snp		return (EINVAL);
1103222900Snp	}
1104222900Snp	idx = (uint32_t) val;
1105222900Snp
1106222900Snp	/* <idx> delete|clear */
1107222900Snp	if (argc == 2 &&
1108222900Snp	    (strcmp(argv[1], "delete") == 0 || strcmp(argv[1], "clear") == 0)) {
1109222900Snp		return del_filter(idx);
1110222900Snp	}
1111222900Snp
1112222900Snp	/* <idx> [<param> <val>] ... */
1113222900Snp	return set_filter(idx, argc - 1, argv + 1);
1114222900Snp}
1115222900Snp
1116222900Snpstatic int
1117222900Snprun_cmd(int argc, const char *argv[])
1118222900Snp{
1119222900Snp	int rc = -1;
1120222900Snp	const char *cmd = argv[0];
1121222900Snp
1122222900Snp	/* command */
1123222900Snp	argc--;
1124222900Snp	argv++;
1125222900Snp
1126222900Snp	if (!strcmp(cmd, "reg") || !strcmp(cmd, "reg32"))
1127222900Snp		rc = register_io(argc, argv, 4);
1128222900Snp	else if (!strcmp(cmd, "reg64"))
1129222900Snp		rc = register_io(argc, argv, 8);
1130222900Snp	else if (!strcmp(cmd, "regdump"))
1131222900Snp		rc = dump_regs(argc, argv);
1132222900Snp	else if (!strcmp(cmd, "filter"))
1133222900Snp		rc = filter_cmd(argc, argv);
1134222900Snp	else {
1135222900Snp		rc = EINVAL;
1136222900Snp		warnx("invalid command \"%s\"", cmd);
1137222900Snp	}
1138222900Snp
1139222900Snp	return (rc);
1140222900Snp}
1141222900Snp
1142222900Snp#define MAX_ARGS 15
1143222900Snpstatic int
1144222900Snprun_cmd_loop(void)
1145222900Snp{
1146222900Snp	int i, rc = 0;
1147222900Snp	char buffer[128], *buf;
1148222900Snp	const char *args[MAX_ARGS + 1];
1149222900Snp
1150222900Snp	/*
1151222900Snp	 * Simple loop: displays a "> " prompt and processes any input as a
1152222900Snp	 * cxgbetool command.  You're supposed to enter only the part after
1153222900Snp	 * "cxgbetool t4nexX".  Use "quit" or "exit" to exit.
1154222900Snp	 */
1155222900Snp	for (;;) {
1156222900Snp		fprintf(stdout, "> ");
1157222900Snp		fflush(stdout);
1158222900Snp		buf = fgets(buffer, sizeof(buffer), stdin);
1159222900Snp		if (buf == NULL) {
1160222900Snp			if (ferror(stdin)) {
1161222900Snp				warn("stdin error");
1162222900Snp				rc = errno;	/* errno from fgets */
1163222900Snp			}
1164222900Snp			break;
1165222900Snp		}
1166222900Snp
1167222900Snp		i = 0;
1168222900Snp		while ((args[i] = strsep(&buf, " \t\n")) != NULL) {
1169222900Snp			if (args[i][0] != 0 && ++i == MAX_ARGS)
1170222900Snp				break;
1171222900Snp		}
1172222900Snp		args[i] = 0;
1173222900Snp
1174222900Snp		if (i == 0)
1175222900Snp			continue;	/* skip empty line */
1176222900Snp
1177222900Snp		if (!strcmp(args[0], "quit") || !strcmp(args[0], "exit"))
1178222900Snp			break;
1179222900Snp
1180222900Snp		rc = run_cmd(i, args);
1181222900Snp	}
1182222900Snp
1183222900Snp	/* rc normally comes from the last command (not including quit/exit) */
1184222900Snp	return (rc);
1185222900Snp}
1186222900Snp
1187222900Snpint
1188222900Snpmain(int argc, const char *argv[])
1189222900Snp{
1190222900Snp	int rc = -1;
1191222900Snp
1192222900Snp	progname = argv[0];
1193222900Snp
1194222900Snp	if (argc == 2) {
1195222900Snp		if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
1196222900Snp			usage(stdout);
1197222900Snp			exit(0);
1198222900Snp		}
1199222900Snp	}
1200222900Snp
1201222900Snp	if (argc < 3) {
1202222900Snp		usage(stderr);
1203222900Snp		exit(EINVAL);
1204222900Snp	}
1205222900Snp
1206222900Snp	nexus = argv[1];
1207222900Snp
1208222900Snp	/* progname and nexus */
1209222900Snp	argc -= 2;
1210222900Snp	argv += 2;
1211222900Snp
1212222900Snp	if (argc == 1 && !strcmp(argv[0], "stdio"))
1213222900Snp		rc = run_cmd_loop();
1214222900Snp	else
1215222900Snp		rc = run_cmd(argc, argv);
1216222900Snp
1217222900Snp	return (rc);
1218222900Snp}
1219