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