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