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