usbmode: update data files
[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 #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 }