]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/etherswitchcfg/etherswitchcfg.c
MFV r328323,328324:
[FreeBSD/FreeBSD.git] / sbin / etherswitchcfg / etherswitchcfg.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2011-2012 Stefan Bethke.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  */
30
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33
34 #include <ctype.h>
35 #include <err.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sysexits.h>
42 #include <unistd.h>
43 #include <sys/types.h>
44 #include <sys/ioctl.h>
45 #include <net/if.h>
46 #include <net/if_media.h>
47 #include <dev/etherswitch/etherswitch.h>
48
49 int     get_media_subtype(int, const char *);
50 int     get_media_mode(int, const char *);
51 int     get_media_options(int, const char *);
52 int     lookup_media_word(struct ifmedia_description *, const char *);
53 void    print_media_word(int, int);
54 void    print_media_word_ifconfig(int);
55
56 /* some constants */
57 #define IEEE802DOT1Q_VID_MAX    4094
58 #define IFMEDIAREQ_NULISTENTRIES        256
59
60 enum cmdmode {
61         MODE_NONE = 0,
62         MODE_PORT,
63         MODE_CONFIG,
64         MODE_VLANGROUP,
65         MODE_REGISTER,
66         MODE_PHYREG
67 };
68
69 struct cfg {
70         int                                     fd;
71         int                                     verbose;
72         int                                     mediatypes;
73         const char                      *controlfile;
74         etherswitch_conf_t      conf;
75         etherswitch_info_t      info;
76         enum cmdmode            mode;
77         int                                     unit;
78 };
79
80 struct cmds {
81         enum cmdmode    mode;
82         const char              *name;
83         int                             args;
84         void                    (*f)(struct cfg *, char *argv[]);
85 };
86 static struct cmds cmds[];
87
88 /* Must match the ETHERSWITCH_PORT_LED_* enum order */
89 static const char *ledstyles[] = { "default", "on", "off", "blink", NULL };
90
91 /*
92  * Print a value a la the %b format of the kernel's printf.
93  * Stolen from ifconfig.c.
94  */
95 static void
96 printb(const char *s, unsigned v, const char *bits)
97 {
98         int i, any = 0;
99         char c;
100
101         if (bits && *bits == 8)
102                 printf("%s=%o", s, v);
103         else
104                 printf("%s=%x", s, v);
105         bits++;
106         if (bits) {
107                 putchar('<');
108                 while ((i = *bits++) != '\0') {
109                         if (v & (1 << (i-1))) {
110                                 if (any)
111                                         putchar(',');
112                                 any = 1;
113                                 for (; (c = *bits) > 32; bits++)
114                                         putchar(c);
115                         } else
116                                 for (; *bits > 32; bits++)
117                                         ;
118                 }
119                 putchar('>');
120         }
121 }
122
123 static int
124 read_register(struct cfg *cfg, int r)
125 {
126         struct etherswitch_reg er;
127         
128         er.reg = r;
129         if (ioctl(cfg->fd, IOETHERSWITCHGETREG, &er) != 0)
130                 err(EX_OSERR, "ioctl(IOETHERSWITCHGETREG)");
131         return (er.val);
132 }
133
134 static void
135 write_register(struct cfg *cfg, int r, int v)
136 {
137         struct etherswitch_reg er;
138         
139         er.reg = r;
140         er.val = v;
141         if (ioctl(cfg->fd, IOETHERSWITCHSETREG, &er) != 0)
142                 err(EX_OSERR, "ioctl(IOETHERSWITCHSETREG)");
143 }
144
145 static int
146 read_phyregister(struct cfg *cfg, int phy, int reg)
147 {
148         struct etherswitch_phyreg er;
149         
150         er.phy = phy;
151         er.reg = reg;
152         if (ioctl(cfg->fd, IOETHERSWITCHGETPHYREG, &er) != 0)
153                 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPHYREG)");
154         return (er.val);
155 }
156
157 static void
158 write_phyregister(struct cfg *cfg, int phy, int reg, int val)
159 {
160         struct etherswitch_phyreg er;
161         
162         er.phy = phy;
163         er.reg = reg;
164         er.val = val;
165         if (ioctl(cfg->fd, IOETHERSWITCHSETPHYREG, &er) != 0)
166                 err(EX_OSERR, "ioctl(IOETHERSWITCHSETPHYREG)");
167 }
168
169 static void
170 set_port_vid(struct cfg *cfg, char *argv[])
171 {
172         int v;
173         etherswitch_port_t p;
174         
175         v = strtol(argv[1], NULL, 0);
176         if (v < 0 || v > IEEE802DOT1Q_VID_MAX)
177                 errx(EX_USAGE, "pvid must be between 0 and %d",
178                     IEEE802DOT1Q_VID_MAX);
179         bzero(&p, sizeof(p));
180         p.es_port = cfg->unit;
181         if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0)
182                 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)");
183         p.es_pvid = v;
184         if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0)
185                 err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)");
186 }
187
188 static void
189 set_port_flag(struct cfg *cfg, char *argv[])
190 {
191         char *flag;
192         int n;
193         uint32_t f;
194         etherswitch_port_t p;
195
196         n = 0;
197         f = 0;
198         flag = argv[0];
199         if (strcmp(flag, "none") != 0) {
200                 if (*flag == '-') {
201                         n++;
202                         flag++;
203                 }
204                 if (strcasecmp(flag, "striptag") == 0)
205                         f = ETHERSWITCH_PORT_STRIPTAG;
206                 else if (strcasecmp(flag, "addtag") == 0)
207                         f = ETHERSWITCH_PORT_ADDTAG;
208                 else if (strcasecmp(flag, "firstlock") == 0)
209                         f = ETHERSWITCH_PORT_FIRSTLOCK;
210                 else if (strcasecmp(flag, "dropuntagged") == 0)
211                         f = ETHERSWITCH_PORT_DROPUNTAGGED;
212                 else if (strcasecmp(flag, "doubletag") == 0)
213                         f = ETHERSWITCH_PORT_DOUBLE_TAG;
214                 else if (strcasecmp(flag, "ingress") == 0)
215                         f = ETHERSWITCH_PORT_INGRESS;
216         }
217         bzero(&p, sizeof(p));
218         p.es_port = cfg->unit;
219         if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0)
220                 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)");
221         if (n)
222                 p.es_flags &= ~f;
223         else
224                 p.es_flags |= f;
225         if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0)
226                 err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)");
227 }
228
229 static void
230 set_port_media(struct cfg *cfg, char *argv[])
231 {
232         etherswitch_port_t p;
233         int ifm_ulist[IFMEDIAREQ_NULISTENTRIES];
234         int subtype;
235         
236         bzero(&p, sizeof(p));
237         p.es_port = cfg->unit;
238         p.es_ifmr.ifm_ulist = ifm_ulist;
239         p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES;
240         if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0)
241                 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)");
242         if (p.es_ifmr.ifm_count == 0)
243                 return;
244         subtype = get_media_subtype(IFM_TYPE(ifm_ulist[0]), argv[1]);
245         p.es_ifr.ifr_media = (p.es_ifmr.ifm_current & IFM_IMASK) |
246                 IFM_TYPE(ifm_ulist[0]) | subtype;
247         if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0)
248                 err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)");
249 }
250
251 static void
252 set_port_mediaopt(struct cfg *cfg, char *argv[])
253 {
254         etherswitch_port_t p;
255         int ifm_ulist[IFMEDIAREQ_NULISTENTRIES];
256         int options;
257         
258         bzero(&p, sizeof(p));
259         p.es_port = cfg->unit;
260         p.es_ifmr.ifm_ulist = ifm_ulist;
261         p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES;
262         if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0)
263                 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)");
264         options = get_media_options(IFM_TYPE(ifm_ulist[0]), argv[1]);
265         if (options == -1)
266                 errx(EX_USAGE, "invalid media options \"%s\"", argv[1]);
267         if (options & IFM_HDX) {
268                 p.es_ifr.ifr_media &= ~IFM_FDX;
269                 options &= ~IFM_HDX;
270         }
271         p.es_ifr.ifr_media |= options;
272         if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0)
273                 err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)");
274 }
275
276 static void
277 set_port_led(struct cfg *cfg, char *argv[])
278 {
279         etherswitch_port_t p;
280         int led;
281         int i;
282         
283         bzero(&p, sizeof(p));
284         p.es_port = cfg->unit;
285         if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0)
286                 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)");
287
288         led = strtol(argv[1], NULL, 0);
289         if (led < 1 || led > p.es_nleds)
290                 errx(EX_USAGE, "invalid led number %s; must be between 1 and %d",
291                         argv[1], p.es_nleds);
292
293         led--;
294
295         for (i=0; ledstyles[i] != NULL; i++) {
296                 if (strcmp(argv[2], ledstyles[i]) == 0) {
297                         p.es_led[led] = i;
298                         break;
299                 }
300         } 
301         if (ledstyles[i] == NULL)
302                 errx(EX_USAGE, "invalid led style \"%s\"", argv[2]);
303
304         if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0)
305                 err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)");
306 }
307
308 static void
309 set_vlangroup_vid(struct cfg *cfg, char *argv[])
310 {
311         int v;
312         etherswitch_vlangroup_t vg;
313
314         memset(&vg, 0, sizeof(vg));
315         v = strtol(argv[1], NULL, 0);
316         if (v < 0 || v > IEEE802DOT1Q_VID_MAX)
317                 errx(EX_USAGE, "vlan must be between 0 and %d", IEEE802DOT1Q_VID_MAX);
318         vg.es_vlangroup = cfg->unit;
319         if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0)
320                 err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)");
321         vg.es_vid = v;
322         if (ioctl(cfg->fd, IOETHERSWITCHSETVLANGROUP, &vg) != 0)
323                 err(EX_OSERR, "ioctl(IOETHERSWITCHSETVLANGROUP)");
324 }
325
326 static void
327 set_vlangroup_members(struct cfg *cfg, char *argv[])
328 {
329         etherswitch_vlangroup_t vg;
330         int member, untagged;
331         char *c, *d;
332         int v;
333
334         member = untagged = 0;
335         memset(&vg, 0, sizeof(vg));
336         if (strcmp(argv[1], "none") != 0) {
337                 for (c=argv[1]; *c; c=d) {
338                         v = strtol(c, &d, 0);
339                         if (d == c)
340                                 break;
341                         if (v < 0 || v >= cfg->info.es_nports)
342                                 errx(EX_USAGE, "Member port must be between 0 and %d", cfg->info.es_nports-1);
343                         if (d[0] == ',' || d[0] == '\0' ||
344                                 ((d[0] == 't' || d[0] == 'T') && (d[1] == ',' || d[1] == '\0'))) {
345                                 if (d[0] == 't' || d[0] == 'T') {
346                                         untagged &= ~ETHERSWITCH_PORTMASK(v);
347                                         d++;
348                                 } else
349                                         untagged |= ETHERSWITCH_PORTMASK(v);
350                                 member |= ETHERSWITCH_PORTMASK(v);
351                                 d++;
352                         } else
353                                 errx(EX_USAGE, "Invalid members specification \"%s\"", d);
354                 }
355         }
356         vg.es_vlangroup = cfg->unit;
357         if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0)
358                 err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)");
359         vg.es_member_ports = member;
360         vg.es_untagged_ports = untagged;
361         if (ioctl(cfg->fd, IOETHERSWITCHSETVLANGROUP, &vg) != 0)
362                 err(EX_OSERR, "ioctl(IOETHERSWITCHSETVLANGROUP)");
363 }
364
365 static int
366 set_register(struct cfg *cfg, char *arg)
367 {
368         int a, v;
369         char *c;
370         
371         a = strtol(arg, &c, 0);
372         if (c==arg)
373                 return (1);
374         if (*c == '=') {
375                 v = strtoul(c+1, NULL, 0);
376                 write_register(cfg, a, v);
377         }
378         printf("\treg 0x%04x=0x%08x\n", a, read_register(cfg, a));
379         return (0);
380 }
381
382 static int
383 set_phyregister(struct cfg *cfg, char *arg)
384 {
385         int phy, reg, val;
386         char *c, *d;
387         
388         phy = strtol(arg, &c, 0);
389         if (c==arg)
390                 return (1);
391         if (*c != '.')
392                 return (1);
393         d = c+1;
394         reg = strtol(d, &c, 0);
395         if (d == c)
396                 return (1);
397         if (*c == '=') {
398                 val = strtoul(c+1, NULL, 0);
399                 write_phyregister(cfg, phy, reg, val);
400         }
401         printf("\treg %d.0x%02x=0x%04x\n", phy, reg, read_phyregister(cfg, phy, reg));
402         return (0);
403 }
404
405 static void
406 set_vlan_mode(struct cfg *cfg, char *argv[])
407 {
408         etherswitch_conf_t conf;
409
410         bzero(&conf, sizeof(conf));
411         conf.cmd = ETHERSWITCH_CONF_VLAN_MODE;
412         if (strcasecmp(argv[1], "isl") == 0)
413                 conf.vlan_mode = ETHERSWITCH_VLAN_ISL;
414         else if (strcasecmp(argv[1], "port") == 0)
415                 conf.vlan_mode = ETHERSWITCH_VLAN_PORT;
416         else if (strcasecmp(argv[1], "dot1q") == 0)
417                 conf.vlan_mode = ETHERSWITCH_VLAN_DOT1Q;
418         else if (strcasecmp(argv[1], "dot1q4k") == 0)
419                 conf.vlan_mode = ETHERSWITCH_VLAN_DOT1Q_4K;
420         else if (strcasecmp(argv[1], "qinq") == 0)
421                 conf.vlan_mode = ETHERSWITCH_VLAN_DOUBLE_TAG;
422         else
423                 conf.vlan_mode = 0;
424         if (ioctl(cfg->fd, IOETHERSWITCHSETCONF, &conf) != 0)
425                 err(EX_OSERR, "ioctl(IOETHERSWITCHSETCONF)");
426 }
427
428 static void
429 print_config(struct cfg *cfg)
430 {
431         const char *c;
432
433         /* Get the device name. */
434         c = strrchr(cfg->controlfile, '/');
435         if (c != NULL)
436                 c = c + 1;
437         else
438                 c = cfg->controlfile;
439
440         /* Print VLAN mode. */
441         if (cfg->conf.cmd & ETHERSWITCH_CONF_VLAN_MODE) {
442                 printf("%s: VLAN mode: ", c);
443                 switch (cfg->conf.vlan_mode) {
444                 case ETHERSWITCH_VLAN_ISL:
445                         printf("ISL\n");
446                         break;
447                 case ETHERSWITCH_VLAN_PORT:
448                         printf("PORT\n");
449                         break;
450                 case ETHERSWITCH_VLAN_DOT1Q:
451                         printf("DOT1Q\n");
452                         break;
453                 case ETHERSWITCH_VLAN_DOT1Q_4K:
454                         printf("DOT1Q4K\n");
455                         break;
456                 case ETHERSWITCH_VLAN_DOUBLE_TAG:
457                         printf("QinQ\n");
458                         break;
459                 default:
460                         printf("none\n");
461                 }
462         }
463 }
464
465 static void
466 print_port(struct cfg *cfg, int port)
467 {
468         etherswitch_port_t p;
469         int ifm_ulist[IFMEDIAREQ_NULISTENTRIES];
470         int i;
471
472         bzero(&p, sizeof(p));
473         p.es_port = port;
474         p.es_ifmr.ifm_ulist = ifm_ulist;
475         p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES;
476         if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0)
477                 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)");
478         printf("port%d:\n", port);
479         if (cfg->conf.vlan_mode == ETHERSWITCH_VLAN_DOT1Q)
480                 printf("\tpvid: %d\n", p.es_pvid);
481         printb("\tflags", p.es_flags, ETHERSWITCH_PORT_FLAGS_BITS);
482         printf("\n");
483         if (p.es_nleds) {
484                 printf("\tled: ");
485                 for (i = 0; i < p.es_nleds; i++) {
486                         printf("%d:%s%s", i+1, ledstyles[p.es_led[i]], (i==p.es_nleds-1)?"":" ");
487                 }
488                 printf("\n");
489         }
490         printf("\tmedia: ");
491         print_media_word(p.es_ifmr.ifm_current, 1);
492         if (p.es_ifmr.ifm_active != p.es_ifmr.ifm_current) {
493                 putchar(' ');
494                 putchar('(');
495                 print_media_word(p.es_ifmr.ifm_active, 0);
496                 putchar(')');
497         }
498         putchar('\n');
499         printf("\tstatus: %s\n", (p.es_ifmr.ifm_status & IFM_ACTIVE) != 0 ? "active" : "no carrier");
500         if (cfg->mediatypes) {
501                 printf("\tsupported media:\n");
502                 if (p.es_ifmr.ifm_count > IFMEDIAREQ_NULISTENTRIES)
503                         p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES;
504                 for (i=0; i<p.es_ifmr.ifm_count; i++) {
505                         printf("\t\tmedia ");
506                         print_media_word(ifm_ulist[i], 0);
507                         putchar('\n');
508                 }
509         }
510 }
511
512 static void
513 print_vlangroup(struct cfg *cfg, int vlangroup)
514 {
515         etherswitch_vlangroup_t vg;
516         int i, comma;
517         
518         vg.es_vlangroup = vlangroup;
519         if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0)
520                 err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)");
521         if ((vg.es_vid & ETHERSWITCH_VID_VALID) == 0)
522                 return;
523         vg.es_vid &= ETHERSWITCH_VID_MASK;
524         printf("vlangroup%d:\n", vlangroup);
525         if (cfg->conf.vlan_mode == ETHERSWITCH_VLAN_PORT)
526                 printf("\tport: %d\n", vg.es_vid);
527         else
528                 printf("\tvlan: %d\n", vg.es_vid);
529         printf("\tmembers ");
530         comma = 0;
531         if (vg.es_member_ports != 0)
532                 for (i=0; i<cfg->info.es_nports; i++) {
533                         if ((vg.es_member_ports & ETHERSWITCH_PORTMASK(i)) != 0) {
534                                 if (comma)
535                                         printf(",");
536                                 printf("%d", i);
537                                 if ((vg.es_untagged_ports & ETHERSWITCH_PORTMASK(i)) == 0)
538                                         printf("t");
539                                 comma = 1;
540                         }
541                 }
542         else
543                 printf("none");
544         printf("\n");
545 }
546
547 static void
548 print_info(struct cfg *cfg)
549 {
550         const char *c;
551         int i;
552         
553         c = strrchr(cfg->controlfile, '/');
554         if (c != NULL)
555                 c = c + 1;
556         else
557                 c = cfg->controlfile;
558         if (cfg->verbose) {
559                 printf("%s: %s with %d ports and %d VLAN groups\n", c,
560                     cfg->info.es_name, cfg->info.es_nports,
561                     cfg->info.es_nvlangroups);
562                 printf("%s: ", c);
563                 printb("VLAN capabilities",  cfg->info.es_vlan_caps,
564                     ETHERSWITCH_VLAN_CAPS_BITS);
565                 printf("\n");
566         }
567         print_config(cfg);
568         for (i=0; i<cfg->info.es_nports; i++) {
569                 print_port(cfg, i);
570         }
571         for (i=0; i<cfg->info.es_nvlangroups; i++) {
572                 print_vlangroup(cfg, i);
573         }
574 }
575
576 static void
577 usage(struct cfg *cfg __unused, char *argv[] __unused)
578 {
579         fprintf(stderr, "usage: etherswitchctl\n");
580         fprintf(stderr, "\tetherswitchcfg [-f control file] info\n");
581         fprintf(stderr, "\tetherswitchcfg [-f control file] config "
582             "command parameter\n");
583         fprintf(stderr, "\t\tconfig commands: vlan_mode\n");
584         fprintf(stderr, "\tetherswitchcfg [-f control file] phy "
585             "phy.register[=value]\n");
586         fprintf(stderr, "\tetherswitchcfg [-f control file] portX "
587             "[flags] command parameter\n");
588         fprintf(stderr, "\t\tport commands: pvid, media, mediaopt, led\n");
589         fprintf(stderr, "\tetherswitchcfg [-f control file] reg "
590             "register[=value]\n");
591         fprintf(stderr, "\tetherswitchcfg [-f control file] vlangroupX "
592             "command parameter\n");
593         fprintf(stderr, "\t\tvlangroup commands: vlan, members\n");
594         exit(EX_USAGE);
595 }
596
597 static void
598 newmode(struct cfg *cfg, enum cmdmode mode)
599 {
600         if (mode == cfg->mode)
601                 return;
602         switch (cfg->mode) {
603         case MODE_NONE:
604                 break;
605         case MODE_CONFIG:
606                 /*
607                  * Read the updated the configuration (it can be different
608                  * from the last time we read it).
609                  */
610                 if (ioctl(cfg->fd, IOETHERSWITCHGETCONF, &cfg->conf) != 0)
611                         err(EX_OSERR, "ioctl(IOETHERSWITCHGETCONF)");
612                 print_config(cfg);
613                 break;
614         case MODE_PORT:
615                 print_port(cfg, cfg->unit);
616                 break;
617         case MODE_VLANGROUP:
618                 print_vlangroup(cfg, cfg->unit);
619                 break;
620         case MODE_REGISTER:
621         case MODE_PHYREG:
622                 break;
623         }
624         cfg->mode = mode;
625 }
626
627 int
628 main(int argc, char *argv[])
629 {
630         int ch;
631         struct cfg cfg;
632         int i;
633         
634         bzero(&cfg, sizeof(cfg));
635         cfg.controlfile = "/dev/etherswitch0";
636         while ((ch = getopt(argc, argv, "f:mv?")) != -1)
637                 switch(ch) {
638                 case 'f':
639                         cfg.controlfile = optarg;
640                         break;
641                 case 'm':
642                         cfg.mediatypes++;
643                         break;
644                 case 'v':
645                         cfg.verbose++;
646                         break;
647                 case '?':
648                         /* FALLTHROUGH */
649                 default:
650                         usage(&cfg, argv);
651                 }
652         argc -= optind;
653         argv += optind;
654         cfg.fd = open(cfg.controlfile, O_RDONLY);
655         if (cfg.fd < 0)
656                 err(EX_UNAVAILABLE, "Can't open control file: %s", cfg.controlfile);
657         if (ioctl(cfg.fd, IOETHERSWITCHGETINFO, &cfg.info) != 0)
658                 err(EX_OSERR, "ioctl(IOETHERSWITCHGETINFO)");
659         if (ioctl(cfg.fd, IOETHERSWITCHGETCONF, &cfg.conf) != 0)
660                 err(EX_OSERR, "ioctl(IOETHERSWITCHGETCONF)");
661         if (argc == 0) {
662                 print_info(&cfg);
663                 return (0);
664         }
665         cfg.mode = MODE_NONE;
666         while (argc > 0) {
667                 switch(cfg.mode) {
668                 case MODE_NONE:
669                         if (strcmp(argv[0], "info") == 0) {
670                                 print_info(&cfg);
671                         } else if (sscanf(argv[0], "port%d", &cfg.unit) == 1) {
672                                 if (cfg.unit < 0 || cfg.unit >= cfg.info.es_nports)
673                                         errx(EX_USAGE, "port unit must be between 0 and %d", cfg.info.es_nports - 1);
674                                 newmode(&cfg, MODE_PORT);
675                         } else if (sscanf(argv[0], "vlangroup%d", &cfg.unit) == 1) {
676                                 if (cfg.unit < 0 || cfg.unit >= cfg.info.es_nvlangroups)
677                                         errx(EX_USAGE,
678                                             "vlangroup unit must be between 0 and %d",
679                                             cfg.info.es_nvlangroups - 1);
680                                 newmode(&cfg, MODE_VLANGROUP);
681                         } else if (strcmp(argv[0], "config") == 0) {
682                                 newmode(&cfg, MODE_CONFIG);
683                         } else if (strcmp(argv[0], "phy") == 0) {
684                                 newmode(&cfg, MODE_PHYREG);
685                         } else if (strcmp(argv[0], "reg") == 0) {
686                                 newmode(&cfg, MODE_REGISTER);
687                         } else if (strcmp(argv[0], "help") == 0) {
688                                 usage(&cfg, argv);
689                         } else {
690                                 errx(EX_USAGE, "Unknown command \"%s\"", argv[0]);
691                         }
692                         break;
693                 case MODE_PORT:
694                 case MODE_CONFIG:
695                 case MODE_VLANGROUP:
696                         for(i=0; cmds[i].name != NULL; i++) {
697                                 if (cfg.mode == cmds[i].mode && strcmp(argv[0], cmds[i].name) == 0) {
698                                         if (argc < (cmds[i].args + 1)) {
699                                                 printf("%s needs %d argument%s\n", cmds[i].name, cmds[i].args, (cmds[i].args==1)?"":",");
700                                                 break;
701                                         }
702                                         (cmds[i].f)(&cfg, argv);
703                                         argc -= cmds[i].args;
704                                         argv += cmds[i].args;
705                                         break;
706                                 }
707                         }
708                         if (cmds[i].name == NULL) {
709                                 newmode(&cfg, MODE_NONE);
710                                 continue;
711                         }
712                         break;
713                 case MODE_REGISTER:
714                         if (set_register(&cfg, argv[0]) != 0) {
715                                 newmode(&cfg, MODE_NONE);
716                                 continue;
717                         }
718                         break;
719                 case MODE_PHYREG:
720                         if (set_phyregister(&cfg, argv[0]) != 0) {
721                                 newmode(&cfg, MODE_NONE);
722                                 continue;
723                         }
724                         break;
725                 }
726                 argc--;
727                 argv++;
728         }
729         /* switch back to command mode to print configuration for last command */
730         newmode(&cfg, MODE_NONE);
731         close(cfg.fd);
732         return (0);
733 }
734
735 static struct cmds cmds[] = {
736         { MODE_PORT, "pvid", 1, set_port_vid },
737         { MODE_PORT, "media", 1, set_port_media },
738         { MODE_PORT, "mediaopt", 1, set_port_mediaopt },
739         { MODE_PORT, "led", 2, set_port_led },
740         { MODE_PORT, "addtag", 0, set_port_flag },
741         { MODE_PORT, "-addtag", 0, set_port_flag },
742         { MODE_PORT, "ingress", 0, set_port_flag },
743         { MODE_PORT, "-ingress", 0, set_port_flag },
744         { MODE_PORT, "striptag", 0, set_port_flag },
745         { MODE_PORT, "-striptag", 0, set_port_flag },
746         { MODE_PORT, "doubletag", 0, set_port_flag },
747         { MODE_PORT, "-doubletag", 0, set_port_flag },
748         { MODE_PORT, "firstlock", 0, set_port_flag },
749         { MODE_PORT, "-firstlock", 0, set_port_flag },
750         { MODE_PORT, "dropuntagged", 0, set_port_flag },
751         { MODE_PORT, "-dropuntagged", 0, set_port_flag },
752         { MODE_CONFIG, "vlan_mode", 1, set_vlan_mode },
753         { MODE_VLANGROUP, "vlan", 1, set_vlangroup_vid },
754         { MODE_VLANGROUP, "members", 1, set_vlangroup_members },
755         { 0, NULL, 0, NULL }
756 };