4b7e82583bb031251a1de8caec43d7cce69d4844
[openwrt/staging/luka.git] / package / utils / robocfg / src / robocfg.c
1 /*
2 * Broadcom BCM5325E/536x switch configuration utility
3 *
4 * Copyright (C) 2005 Oleg I. Vdovikin
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301, USA.
20 */
21
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/param.h>
27 #include <sys/ioctl.h>
28 #include <sys/socket.h>
29 #include <sys/types.h>
30
31 /* linux stuff */
32 typedef u_int64_t u64;
33 typedef u_int32_t u32;
34 typedef u_int16_t u16;
35 typedef u_int8_t u8;
36
37 #include <linux/if.h>
38 #include <linux/sockios.h>
39 #include <linux/ethtool.h>
40 #include <linux/mii.h>
41
42 #include "etc53xx.h"
43 #define ROBO_PHY_ADDR 0x1E /* robo switch phy address */
44
45 /* MII registers */
46 #define REG_MII_PAGE 0x10 /* MII Page register */
47 #define REG_MII_ADDR 0x11 /* MII Address register */
48 #define REG_MII_DATA0 0x18 /* MII Data register 0 */
49
50 #define REG_MII_PAGE_ENABLE 1
51 #define REG_MII_ADDR_WRITE 1
52 #define REG_MII_ADDR_READ 2
53
54 /* Private et.o ioctls */
55 #define SIOCGETCPHYRD (SIOCDEVPRIVATE + 9)
56 #define SIOCSETCPHYWR (SIOCDEVPRIVATE + 10)
57
58 typedef struct {
59 struct ifreq ifr;
60 int fd;
61 int et; /* use private ioctls */
62 } robo_t;
63
64 static u16 mdio_read(robo_t *robo, u16 phy_id, u8 reg)
65 {
66 if (robo->et) {
67 int args[2] = { reg };
68
69 if (phy_id != ROBO_PHY_ADDR) {
70 fprintf(stderr,
71 "Access to real 'phy' registers unavaliable.\n"
72 "Upgrade kernel driver.\n");
73
74 return 0xffff;
75 }
76
77 robo->ifr.ifr_data = (caddr_t) args;
78 if (ioctl(robo->fd, SIOCGETCPHYRD, (caddr_t)&robo->ifr) < 0) {
79 perror("SIOCGETCPHYRD");
80 exit(1);
81 }
82
83 return args[1];
84 } else {
85 struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&robo->ifr.ifr_data;
86 mii->phy_id = phy_id;
87 mii->reg_num = reg;
88 if (ioctl(robo->fd, SIOCGMIIREG, &robo->ifr) < 0) {
89 perror("SIOCGMIIREG");
90 exit(1);
91 }
92 return mii->val_out;
93 }
94 }
95
96 static void mdio_write(robo_t *robo, u16 phy_id, u8 reg, u16 val)
97 {
98 if (robo->et) {
99 int args[2] = { reg, val };
100
101 if (phy_id != ROBO_PHY_ADDR) {
102 fprintf(stderr,
103 "Access to real 'phy' registers unavaliable.\n"
104 "Upgrade kernel driver.\n");
105 return;
106 }
107
108 robo->ifr.ifr_data = (caddr_t) args;
109 if (ioctl(robo->fd, SIOCSETCPHYWR, (caddr_t)&robo->ifr) < 0) {
110 perror("SIOCGETCPHYWR");
111 exit(1);
112 }
113 } else {
114 struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&robo->ifr.ifr_data;
115 mii->phy_id = phy_id;
116 mii->reg_num = reg;
117 mii->val_in = val;
118 if (ioctl(robo->fd, SIOCSMIIREG, &robo->ifr) < 0) {
119 perror("SIOCSMIIREG");
120 exit(1);
121 }
122 }
123 }
124
125 static int robo_reg(robo_t *robo, u8 page, u8 reg, u8 op)
126 {
127 int i = 3;
128
129 /* set page number */
130 mdio_write(robo, ROBO_PHY_ADDR, REG_MII_PAGE,
131 (page << 8) | REG_MII_PAGE_ENABLE);
132
133 /* set register address */
134 mdio_write(robo, ROBO_PHY_ADDR, REG_MII_ADDR,
135 (reg << 8) | op);
136
137 /* check if operation completed */
138 while (i--) {
139 if ((mdio_read(robo, ROBO_PHY_ADDR, REG_MII_ADDR) & 3) == 0)
140 return 0;
141 }
142
143 fprintf(stderr, "robo_reg: timeout\n");
144 exit(1);
145
146 return 0;
147 }
148
149 static void robo_read(robo_t *robo, u8 page, u8 reg, u16 *val, int count)
150 {
151 int i;
152
153 robo_reg(robo, page, reg, REG_MII_ADDR_READ);
154
155 for (i = 0; i < count; i++)
156 val[i] = mdio_read(robo, ROBO_PHY_ADDR, REG_MII_DATA0 + i);
157 }
158
159 static u16 robo_read16(robo_t *robo, u8 page, u8 reg)
160 {
161 robo_reg(robo, page, reg, REG_MII_ADDR_READ);
162
163 return mdio_read(robo, ROBO_PHY_ADDR, REG_MII_DATA0);
164 }
165
166 static u32 robo_read32(robo_t *robo, u8 page, u8 reg)
167 {
168 robo_reg(robo, page, reg, REG_MII_ADDR_READ);
169
170 return mdio_read(robo, ROBO_PHY_ADDR, REG_MII_DATA0) +
171 (mdio_read(robo, ROBO_PHY_ADDR, REG_MII_DATA0 + 1) << 16);
172 }
173
174 static void robo_write16(robo_t *robo, u8 page, u8 reg, u16 val16)
175 {
176 /* write data */
177 mdio_write(robo, ROBO_PHY_ADDR, REG_MII_DATA0, val16);
178
179 robo_reg(robo, page, reg, REG_MII_ADDR_WRITE);
180 }
181
182 static void robo_write32(robo_t *robo, u8 page, u8 reg, u32 val32)
183 {
184 /* write data */
185 mdio_write(robo, ROBO_PHY_ADDR, REG_MII_DATA0, val32 & 65535);
186 mdio_write(robo, ROBO_PHY_ADDR, REG_MII_DATA0 + 1, val32 >> 16);
187
188 robo_reg(robo, page, reg, REG_MII_ADDR_WRITE);
189 }
190
191 /* checks that attached switch is 5325E/5350 */
192 static int robo_vlan5350(robo_t *robo)
193 {
194 /* set vlan access id to 15 and read it back */
195 u16 val16 = 15;
196 robo_write16(robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16);
197
198 /* 5365 will refuse this as it does not have this reg */
199 return (robo_read16(robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350) == val16);
200 }
201
202 u8 port[6] = { 0, 1, 2, 3, 4, 8 };
203 char ports[6] = { 'W', '4', '3', '2', '1', 'C' };
204 char *rxtx[4] = { "enabled", "rx_disabled", "tx_disabled", "disabled" };
205 char *stp[8] = { "none", "disable", "block", "listen", "learn", "forward", "6", "7" };
206
207 struct {
208 char *name;
209 u16 bmcr;
210 } media[5] = { { "auto", BMCR_ANENABLE | BMCR_ANRESTART },
211 { "10HD", 0 }, { "10FD", BMCR_FULLDPLX },
212 { "100HD", BMCR_SPEED100 }, { "100FD", BMCR_SPEED100 | BMCR_FULLDPLX } };
213
214 struct {
215 char *name;
216 u16 value;
217 } mdix[3] = { { "auto", 0x0000 }, { "on", 0x1800 }, { "off", 0x0800 } };
218
219 void usage()
220 {
221 fprintf(stderr, "Broadcom BCM5325E/536x switch configuration utility\n"
222 "Copyright (C) 2005 Oleg I. Vdovikin\n\n"
223 "This program is distributed in the hope that it will be useful,\n"
224 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
225 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
226 "GNU General Public License for more details.\n\n");
227
228 fprintf(stderr, "Usage: robocfg <op> ... <op>\n"
229 "Operations are as below:\n"
230 "\tshow\n"
231 "\tswitch <enable|disable>\n"
232 "\tport <port_number> [state <%s|%s|%s|%s>]\n\t\t[stp %s|%s|%s|%s|%s|%s] [tag <vlan_tag>]\n"
233 "\t\t[media %s|%s|%s|%s|%s] [mdi-x %s|%s|%s]\n"
234 "\tvlan <vlan_number> [ports <ports_list>]\n"
235 "\tvlans <enable|disable|reset>\n\n"
236 "\tports_list should be one argument, space separated, quoted if needed,\n"
237 "\tport number could be followed by 't' to leave packet vlan tagged (CPU \n"
238 "\tport default) or by 'u' to untag packet (other ports default) before \n"
239 "\tbringing it to the port, '*' is ignored\n"
240 "\nSamples:\n"
241 "1) ASUS WL-500g Deluxe stock config (eth0 is WAN, eth0.1 is LAN):\n"
242 "robocfg switch disable vlans enable reset vlan 0 ports \"0 5u\" vlan 1 ports \"1 2 3 4 5t\""
243 " port 0 state enabled stp none switch enable\n"
244 "2) WRT54g, WL-500g Deluxe OpenWRT config (vlan0 is LAN, vlan1 is WAN):\n"
245 "robocfg switch disable vlans enable reset vlan 0 ports \"1 2 3 4 5t\" vlan 1 ports \"0 5t\""
246 " port 0 state enabled stp none switch enable\n",
247 rxtx[0], rxtx[1], rxtx[2], rxtx[3], stp[0], stp[1], stp[2], stp[3], stp[4], stp[5],
248 media[0].name, media[1].name, media[2].name, media[3].name, media[4].name,
249 mdix[0].name, mdix[1].name, mdix[2].name);
250 }
251
252 static robo_t robo;
253 int bcm53xx_probe(const char *dev)
254 {
255 struct ethtool_drvinfo info;
256 unsigned int phyid;
257 int ret;
258
259 fprintf(stderr, "probing %s\n", dev);
260
261 strcpy(robo.ifr.ifr_name, dev);
262 memset(&info, 0, sizeof(info));
263 info.cmd = ETHTOOL_GDRVINFO;
264 robo.ifr.ifr_data = (caddr_t)&info;
265 ret = ioctl(robo.fd, SIOCETHTOOL, (caddr_t)&robo.ifr);
266 if (ret < 0) {
267 perror("SIOCETHTOOL");
268 return ret;
269 }
270
271 if ( strcmp(info.driver, "et0") &&
272 strcmp(info.driver, "b44") &&
273 strcmp(info.driver, "bcm63xx_enet") ) {
274 fprintf(stderr, "driver not supported %s\n", info.driver);
275 return -ENOSYS;
276 }
277
278 /* try access using MII ioctls - get phy address */
279 robo.et = 0;
280 if (ioctl(robo.fd, SIOCGMIIPHY, &robo.ifr) < 0)
281 robo.et = 1;
282
283 if (robo.et) {
284 unsigned int args[2] = { 2 };
285
286 robo.ifr.ifr_data = (caddr_t) args;
287 ret = ioctl(robo.fd, SIOCGETCPHYRD, (caddr_t)&robo.ifr);
288 if (ret < 0) {
289 perror("SIOCGETCPHYRD");
290 return ret;
291 }
292 phyid = args[1] & 0xffff;
293
294 args[0] = 3;
295 robo.ifr.ifr_data = (caddr_t) args;
296 ret = ioctl(robo.fd, SIOCGETCPHYRD, (caddr_t)&robo.ifr);
297 if (ret < 0) {
298 perror("SIOCGETCPHYRD");
299 return ret;
300 }
301 phyid |= args[1] << 16;
302 } else {
303 struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&robo.ifr.ifr_data;
304 mii->phy_id = ROBO_PHY_ADDR;
305 mii->reg_num = 2;
306 ret = ioctl(robo.fd, SIOCGMIIREG, &robo.ifr);
307 if (ret < 0) {
308 perror("SIOCGMIIREG");
309 return ret;
310 }
311 phyid = mii->val_out & 0xffff;
312
313 mii->phy_id = ROBO_PHY_ADDR;
314 mii->reg_num = 3;
315 ret = ioctl(robo.fd, SIOCGMIIREG, &robo.ifr);
316 if (ret < 0) {
317 perror("SIOCGMIIREG");
318 return ret;
319 }
320 phyid |= mii->val_out << 16;
321 }
322
323 if (phyid == 0xffffffff || phyid == 0x55210022) {
324 perror("phyid");
325 return -EIO;
326 }
327
328 return 0;
329 }
330
331 int
332 main(int argc, char *argv[])
333 {
334 u16 val16;
335 u16 mac[3];
336 int i = 0, j;
337 int robo5350 = 0;
338 u32 phyid;
339
340 if ((robo.fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
341 perror("socket");
342 exit(1);
343 }
344
345 if (bcm53xx_probe("eth1")) {
346 if (bcm53xx_probe("eth0")) {
347 perror("bcm53xx_probe");
348 exit(1);
349 }
350 }
351
352 robo5350 = robo_vlan5350(&robo);
353
354 for (i = 1; i < argc;) {
355 if (strcasecmp(argv[i], "port") == 0 && (i + 1) < argc)
356 {
357 int index = atoi(argv[++i]);
358 /* read port specs */
359 while (++i < argc) {
360 if (strcasecmp(argv[i], "state") == 0 && ++i < argc) {
361 for (j = 0; j < 4 && strcasecmp(argv[i], rxtx[j]); j++);
362 if (j < 4) {
363 /* change state */
364 robo_write16(&robo,ROBO_CTRL_PAGE, port[index],
365 (robo_read16(&robo, ROBO_CTRL_PAGE, port[index]) & ~(3 << 0)) | (j << 0));
366 } else {
367 fprintf(stderr, "Invalid state '%s'.\n", argv[i]);
368 exit(1);
369 }
370 } else
371 if (strcasecmp(argv[i], "stp") == 0 && ++i < argc) {
372 for (j = 0; j < 8 && strcasecmp(argv[i], stp[j]); j++);
373 if (j < 8) {
374 /* change stp */
375 robo_write16(&robo,ROBO_CTRL_PAGE, port[index],
376 (robo_read16(&robo, ROBO_CTRL_PAGE, port[index]) & ~(7 << 5)) | (j << 5));
377 } else {
378 fprintf(stderr, "Invalid stp '%s'.\n", argv[i]);
379 exit(1);
380 }
381 } else
382 if (strcasecmp(argv[i], "media") == 0 && ++i < argc) {
383 for (j = 0; j < 5 && strcasecmp(argv[i], media[j].name); j++);
384 if (j < 5) {
385 mdio_write(&robo, port[index], MII_BMCR, media[j].bmcr);
386 } else {
387 fprintf(stderr, "Invalid media '%s'.\n", argv[i]);
388 exit(1);
389 }
390 } else
391 if (strcasecmp(argv[i], "mdi-x") == 0 && ++i < argc) {
392 for (j = 0; j < 3 && strcasecmp(argv[i], mdix[j].name); j++);
393 if (j < 3) {
394 mdio_write(&robo, port[index], 0x1c, mdix[j].value |
395 (mdio_read(&robo, port[index], 0x1c) & ~0x1800));
396 } else {
397 fprintf(stderr, "Invalid mdi-x '%s'.\n", argv[i]);
398 exit(1);
399 }
400 } else
401 if (strcasecmp(argv[i], "tag") == 0 && ++i < argc) {
402 j = atoi(argv[i]);
403 /* change vlan tag */
404 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_PORT0_DEF_TAG + (index << 1), j);
405 } else break;
406 }
407 } else
408 if (strcasecmp(argv[i], "vlan") == 0 && (i + 1) < argc)
409 {
410 int index = atoi(argv[++i]);
411 while (++i < argc) {
412 if (strcasecmp(argv[i], "ports") == 0 && ++i < argc) {
413 char *ports = argv[i];
414 int untag = 0;
415 int member = 0;
416
417 while (*ports >= '0' && *ports <= '9') {
418 j = *ports++ - '0';
419 member |= 1 << j;
420
421 /* untag if needed, CPU port requires special handling */
422 if (*ports == 'u' || (j != 5 && (*ports == ' ' || *ports == 0)))
423 {
424 untag |= 1 << j;
425 if (*ports) ports++;
426 /* change default vlan tag */
427 robo_write16(&robo, ROBO_VLAN_PAGE,
428 ROBO_VLAN_PORT0_DEF_TAG + (j << 1), index);
429 } else
430 if (*ports == '*' || *ports == 't' || *ports == ' ') ports++;
431 else break;
432
433 while (*ports == ' ') ports++;
434 }
435
436 if (*ports) {
437 fprintf(stderr, "Invalid ports '%s'.\n", argv[i]);
438 exit(1);
439 } else {
440 /* write config now */
441 val16 = (index) /* vlan */ | (1 << 12) /* write */ | (1 << 13) /* enable */;
442 if (robo5350) {
443 robo_write32(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_WRITE_5350,
444 (1 << 20) /* valid */ | (untag << 6) | member);
445 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16);
446 } else {
447 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_WRITE,
448 (1 << 14) /* valid */ | (untag << 7) | member);
449 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS, val16);
450 }
451 }
452 } else break;
453 }
454 } else
455 if (strcasecmp(argv[i], "switch") == 0 && (i + 1) < argc)
456 {
457 /* enable/disable switching */
458 robo_write16(&robo, ROBO_CTRL_PAGE, ROBO_SWITCH_MODE,
459 (robo_read16(&robo, ROBO_CTRL_PAGE, ROBO_SWITCH_MODE) & ~2) |
460 (*argv[++i] == 'e' ? 2 : 0));
461 i++;
462 } else
463 if (strcasecmp(argv[i], "vlans") == 0 && (i + 1) < argc)
464 {
465 while (++i < argc) {
466 if (strcasecmp(argv[i], "reset") == 0) {
467 /* reset vlan validity bit */
468 for (j = 0; j <= (robo5350 ? VLAN_ID_MAX5350 : VLAN_ID_MAX); j++)
469 {
470 /* write config now */
471 val16 = (j) /* vlan */ | (1 << 12) /* write */ | (1 << 13) /* enable */;
472 if (robo5350) {
473 robo_write32(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_WRITE_5350, 0);
474 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16);
475 } else {
476 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_WRITE, 0);
477 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS, val16);
478 }
479 }
480 } else
481 if (strcasecmp(argv[i], "enable") == 0 || strcasecmp(argv[i], "disable") == 0)
482 {
483 int disable = (*argv[i] == 'd') || (*argv[i] == 'D');
484 /* enable/disable vlans */
485 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_CTRL0, disable ? 0 :
486 (1 << 7) /* 802.1Q VLAN */ | (3 << 5) /* mac check and hash */);
487
488 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_CTRL1, disable ? 0 :
489 (1 << 1) | (1 << 2) | (1 << 3) /* RSV multicast */);
490
491 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_CTRL4, disable ? 0 :
492 (1 << 6) /* drop invalid VID frames */);
493
494 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_CTRL5, disable ? 0 :
495 (1 << 3) /* drop miss V table frames */);
496
497 } else break;
498 }
499 } else
500 if (strcasecmp(argv[i], "show") == 0)
501 {
502 break;
503 } else {
504 fprintf(stderr, "Invalid option %s\n", argv[i]);
505 usage();
506 exit(1);
507 }
508 }
509
510 if (i == argc) {
511 if (argc == 1) usage();
512 return 0;
513 }
514
515 /* show config */
516
517 printf("Switch: %sabled\n", robo_read16(&robo, ROBO_CTRL_PAGE, ROBO_SWITCH_MODE) & 2 ? "en" : "dis");
518
519 for (i = 0; i < 6; i++) {
520 printf(robo_read16(&robo, ROBO_STAT_PAGE, ROBO_LINK_STAT_SUMMARY) & (1 << port[i]) ?
521 "Port %d(%c): %s%s " : "Port %d(%c): DOWN ", i, ports[i],
522 robo_read16(&robo, ROBO_STAT_PAGE, ROBO_SPEED_STAT_SUMMARY) & (1 << port[i]) ? "100" : " 10",
523 robo_read16(&robo, ROBO_STAT_PAGE, ROBO_DUPLEX_STAT_SUMMARY) & (1 << port[i]) ? "FD" : "HD");
524
525 val16 = robo_read16(&robo, ROBO_CTRL_PAGE, port[i]);
526
527 printf("%s stp: %s vlan: %d ", rxtx[val16 & 3], stp[(val16 >> 5) & 7],
528 robo_read16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_PORT0_DEF_TAG + (i << 1)));
529
530 robo_read(&robo, ROBO_STAT_PAGE, ROBO_LSA_PORT0 + port[i] * 6, mac, 3);
531
532 printf("mac: %02x:%02x:%02x:%02x:%02x:%02x\n",
533 mac[2] >> 8, mac[2] & 255, mac[1] >> 8, mac[1] & 255, mac[0] >> 8, mac[0] & 255);
534 }
535
536 val16 = robo_read16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_CTRL0);
537
538 printf("VLANs: %s %sabled%s%s\n",
539 robo5350 ? "BCM5325/535x" : "BCM536x",
540 (val16 & (1 << 7)) ? "en" : "dis",
541 (val16 & (1 << 6)) ? " mac_check" : "",
542 (val16 & (1 << 5)) ? " mac_hash" : "");
543
544 /* scan VLANs */
545 for (i = 0; i <= (robo5350 ? VLAN_ID_MAX5350 : VLAN_ID_MAX); i++) {
546 /* issue read */
547 val16 = (i) /* vlan */ | (0 << 12) /* read */ | (1 << 13) /* enable */;
548
549 if (robo5350) {
550 u32 val32;
551 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16);
552 /* actual read */
553 val32 = robo_read32(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_READ);
554 if ((val32 & (1 << 20)) /* valid */) {
555 printf("vlan%d:", i);
556 for (j = 0; j < 6; j++) {
557 if (val32 & (1 << j)) {
558 printf(" %d%s", j, (val32 & (1 << (j + 6))) ?
559 (j == 5 ? "u" : "") : "t");
560 }
561 }
562 printf("\n");
563 }
564 } else {
565 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS, val16);
566 /* actual read */
567 val16 = robo_read16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_READ);
568 if ((val16 & (1 << 14)) /* valid */) {
569 printf("vlan%d:", i);
570 for (j = 0; j < 6; j++) {
571 if (val16 & (1 << j)) {
572 printf(" %d%s", j, (val16 & (1 << (j + 7))) ?
573 (j == 5 ? "u" : "") : "t");
574 }
575 }
576 printf("\n");
577 }
578 }
579 }
580
581 return (0);
582 }