2 * Broadcom BCM5325E/536x switch configuration utility
4 * Copyright (C) 2005 Oleg I. Vdovikin
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.
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.
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
26 #include <sys/param.h>
27 #include <sys/ioctl.h>
28 #include <sys/socket.h>
31 typedef u_int64_t u64
;
32 typedef u_int32_t u32
;
33 typedef u_int16_t u16
;
37 #include <linux/sockios.h>
38 #include <linux/ethtool.h>
39 #include <linux/mii.h>
42 #define ROBO_PHY_ADDR 0x1E /* robo switch phy address */
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 */
49 #define REG_MII_PAGE_ENABLE 1
50 #define REG_MII_ADDR_WRITE 1
51 #define REG_MII_ADDR_READ 2
53 /* Private et.o ioctls */
54 #define SIOCGETCPHYRD (SIOCDEVPRIVATE + 9)
55 #define SIOCSETCPHYWR (SIOCDEVPRIVATE + 10)
60 int et
; /* use private ioctls */
63 static u16
mdio_read(robo_t
*robo
, u16 phy_id
, u8 reg
)
66 int args
[2] = { reg
};
68 if (phy_id
!= ROBO_PHY_ADDR
) {
70 "Access to real 'phy' registers unavaliable.\n"
71 "Upgrade kernel driver.\n");
76 robo
->ifr
.ifr_data
= (caddr_t
) args
;
77 if (ioctl(robo
->fd
, SIOCGETCPHYRD
, (caddr_t
)&robo
->ifr
) < 0) {
78 perror("SIOCGETCPHYRD");
84 struct mii_ioctl_data
*mii
= (struct mii_ioctl_data
*)&robo
->ifr
.ifr_data
;
87 if (ioctl(robo
->fd
, SIOCGMIIREG
, &robo
->ifr
) < 0) {
88 perror("SIOCGMIIREG");
95 static void mdio_write(robo_t
*robo
, u16 phy_id
, u8 reg
, u16 val
)
98 int args
[2] = { reg
, val
};
100 if (phy_id
!= ROBO_PHY_ADDR
) {
102 "Access to real 'phy' registers unavaliable.\n"
103 "Upgrade kernel driver.\n");
107 robo
->ifr
.ifr_data
= (caddr_t
) args
;
108 if (ioctl(robo
->fd
, SIOCSETCPHYWR
, (caddr_t
)&robo
->ifr
) < 0) {
109 perror("SIOCGETCPHYWR");
113 struct mii_ioctl_data
*mii
= (struct mii_ioctl_data
*)&robo
->ifr
.ifr_data
;
114 mii
->phy_id
= phy_id
;
117 if (ioctl(robo
->fd
, SIOCSMIIREG
, &robo
->ifr
) < 0) {
118 perror("SIOCSMIIREG");
124 static int robo_reg(robo_t
*robo
, u8 page
, u8 reg
, u8 op
)
128 /* set page number */
129 mdio_write(robo
, ROBO_PHY_ADDR
, REG_MII_PAGE
,
130 (page
<< 8) | REG_MII_PAGE_ENABLE
);
132 /* set register address */
133 mdio_write(robo
, ROBO_PHY_ADDR
, REG_MII_ADDR
,
136 /* check if operation completed */
138 if ((mdio_read(robo
, ROBO_PHY_ADDR
, REG_MII_ADDR
) & 3) == 0)
142 fprintf(stderr
, "robo_reg: timeout\n");
148 static void robo_read(robo_t
*robo
, u8 page
, u8 reg
, u16
*val
, int count
)
152 robo_reg(robo
, page
, reg
, REG_MII_ADDR_READ
);
154 for (i
= 0; i
< count
; i
++)
155 val
[i
] = mdio_read(robo
, ROBO_PHY_ADDR
, REG_MII_DATA0
+ i
);
158 static u16
robo_read16(robo_t
*robo
, u8 page
, u8 reg
)
160 robo_reg(robo
, page
, reg
, REG_MII_ADDR_READ
);
162 return mdio_read(robo
, ROBO_PHY_ADDR
, REG_MII_DATA0
);
165 static u32
robo_read32(robo_t
*robo
, u8 page
, u8 reg
)
167 robo_reg(robo
, page
, reg
, REG_MII_ADDR_READ
);
169 return mdio_read(robo
, ROBO_PHY_ADDR
, REG_MII_DATA0
) +
170 (mdio_read(robo
, ROBO_PHY_ADDR
, REG_MII_DATA0
+ 1) << 16);
173 static void robo_write16(robo_t
*robo
, u8 page
, u8 reg
, u16 val16
)
176 mdio_write(robo
, ROBO_PHY_ADDR
, REG_MII_DATA0
, val16
);
178 robo_reg(robo
, page
, reg
, REG_MII_ADDR_WRITE
);
181 static void robo_write32(robo_t
*robo
, u8 page
, u8 reg
, u32 val32
)
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);
187 robo_reg(robo
, page
, reg
, REG_MII_ADDR_WRITE
);
190 /* checks that attached switch is 5325E/5350 */
191 static int robo_vlan5350(robo_t
*robo
)
193 /* set vlan access id to 15 and read it back */
195 robo_write16(robo
, ROBO_VLAN_PAGE
, ROBO_VLAN_TABLE_ACCESS_5350
, val16
);
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
);
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" };
209 } media
[5] = { { "auto", BMCR_ANENABLE
| BMCR_ANRESTART
},
210 { "10HD", 0 }, { "10FD", BMCR_FULLDPLX
},
211 { "100HD", BMCR_SPEED100
}, { "100FD", BMCR_SPEED100
| BMCR_FULLDPLX
} };
216 } mdix
[3] = { { "auto", 0x0000 }, { "on", 0x1800 }, { "off", 0x0800 } };
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");
227 fprintf(stderr
, "Usage: robocfg <op> ... <op>\n"
228 "Operations are as below:\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"
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
);
252 int bcm53xx_probe(const char *dev
)
254 struct ethtool_drvinfo info
;
258 fprintf(stderr
, "probing %s\n", dev
);
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
);
266 perror("SIOCETHTOOL");
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
);
277 /* try access using MII ioctls - get phy address */
279 if (ioctl(robo
.fd
, SIOCGMIIPHY
, &robo
.ifr
) < 0)
283 unsigned int args
[2] = { 2 };
285 robo
.ifr
.ifr_data
= (caddr_t
) args
;
286 ret
= ioctl(robo
.fd
, SIOCGETCPHYRD
, (caddr_t
)&robo
.ifr
);
288 perror("SIOCGETCPHYRD");
291 phyid
= args
[1] & 0xffff;
294 robo
.ifr
.ifr_data
= (caddr_t
) args
;
295 ret
= ioctl(robo
.fd
, SIOCGETCPHYRD
, (caddr_t
)&robo
.ifr
);
297 perror("SIOCGETCPHYRD");
300 phyid
|= args
[1] << 16;
302 struct mii_ioctl_data
*mii
= (struct mii_ioctl_data
*)&robo
.ifr
.ifr_data
;
303 mii
->phy_id
= ROBO_PHY_ADDR
;
305 ret
= ioctl(robo
.fd
, SIOCGMIIREG
, &robo
.ifr
);
307 perror("SIOCGMIIREG");
310 phyid
= mii
->val_out
& 0xffff;
312 mii
->phy_id
= ROBO_PHY_ADDR
;
314 ret
= ioctl(robo
.fd
, SIOCGMIIREG
, &robo
.ifr
);
316 perror("SIOCGMIIREG");
319 phyid
|= mii
->val_out
<< 16;
322 if (phyid
== 0xffffffff || phyid
== 0x55210022) {
331 main(int argc
, char *argv
[])
339 if ((robo
.fd
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0) {
344 if (bcm53xx_probe("eth1")) {
345 if (bcm53xx_probe("eth0")) {
346 perror("bcm53xx_probe");
351 robo5350
= robo_vlan5350(&robo
);
353 for (i
= 1; i
< argc
;) {
354 if (strcasecmp(argv
[i
], "port") == 0 && (i
+ 1) < argc
)
356 int index
= atoi(argv
[++i
]);
357 /* read port specs */
359 if (strcasecmp(argv
[i
], "state") == 0 && ++i
< argc
) {
360 for (j
= 0; j
< 4 && strcasecmp(argv
[i
], rxtx
[j
]); j
++);
363 robo_write16(&robo
,ROBO_CTRL_PAGE
, port
[index
],
364 (robo_read16(&robo
, ROBO_CTRL_PAGE
, port
[index
]) & ~(3 << 0)) | (j
<< 0));
366 fprintf(stderr
, "Invalid state '%s'.\n", argv
[i
]);
370 if (strcasecmp(argv
[i
], "stp") == 0 && ++i
< argc
) {
371 for (j
= 0; j
< 8 && strcasecmp(argv
[i
], stp
[j
]); j
++);
374 robo_write16(&robo
,ROBO_CTRL_PAGE
, port
[index
],
375 (robo_read16(&robo
, ROBO_CTRL_PAGE
, port
[index
]) & ~(7 << 5)) | (j
<< 5));
377 fprintf(stderr
, "Invalid stp '%s'.\n", argv
[i
]);
381 if (strcasecmp(argv
[i
], "media") == 0 && ++i
< argc
) {
382 for (j
= 0; j
< 5 && strcasecmp(argv
[i
], media
[j
].name
); j
++);
384 mdio_write(&robo
, port
[index
], MII_BMCR
, media
[j
].bmcr
);
386 fprintf(stderr
, "Invalid media '%s'.\n", argv
[i
]);
390 if (strcasecmp(argv
[i
], "mdi-x") == 0 && ++i
< argc
) {
391 for (j
= 0; j
< 3 && strcasecmp(argv
[i
], mdix
[j
].name
); j
++);
393 mdio_write(&robo
, port
[index
], 0x1c, mdix
[j
].value
|
394 (mdio_read(&robo
, port
[index
], 0x1c) & ~0x1800));
396 fprintf(stderr
, "Invalid mdi-x '%s'.\n", argv
[i
]);
400 if (strcasecmp(argv
[i
], "tag") == 0 && ++i
< argc
) {
402 /* change vlan tag */
403 robo_write16(&robo
, ROBO_VLAN_PAGE
, ROBO_VLAN_PORT0_DEF_TAG
+ (index
<< 1), j
);
407 if (strcasecmp(argv
[i
], "vlan") == 0 && (i
+ 1) < argc
)
409 int index
= atoi(argv
[++i
]);
411 if (strcasecmp(argv
[i
], "ports") == 0 && ++i
< argc
) {
412 char *ports
= argv
[i
];
416 while (*ports
>= '0' && *ports
<= '9') {
420 /* untag if needed, CPU port requires special handling */
421 if (*ports
== 'u' || (j
!= 5 && (*ports
== ' ' || *ports
== 0)))
425 /* change default vlan tag */
426 robo_write16(&robo
, ROBO_VLAN_PAGE
,
427 ROBO_VLAN_PORT0_DEF_TAG
+ (j
<< 1), index
);
429 if (*ports
== '*' || *ports
== 't' || *ports
== ' ') ports
++;
432 while (*ports
== ' ') ports
++;
436 fprintf(stderr
, "Invalid ports '%s'.\n", argv
[i
]);
439 /* write config now */
440 val16
= (index
) /* vlan */ | (1 << 12) /* write */ | (1 << 13) /* enable */;
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
);
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
);
454 if (strcasecmp(argv
[i
], "switch") == 0 && (i
+ 1) < argc
)
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));
462 if (strcasecmp(argv
[i
], "vlans") == 0 && (i
+ 1) < 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
++)
469 /* write config now */
470 val16
= (j
) /* vlan */ | (1 << 12) /* write */ | (1 << 13) /* enable */;
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
);
475 robo_write16(&robo
, ROBO_VLAN_PAGE
, ROBO_VLAN_WRITE
, 0);
476 robo_write16(&robo
, ROBO_VLAN_PAGE
, ROBO_VLAN_TABLE_ACCESS
, val16
);
480 if (strcasecmp(argv
[i
], "enable") == 0 || strcasecmp(argv
[i
], "disable") == 0)
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 */);
487 robo_write16(&robo
, ROBO_VLAN_PAGE
, ROBO_VLAN_CTRL1
, disable
? 0 :
488 (1 << 1) | (1 << 2) | (1 << 3) /* RSV multicast */);
490 robo_write16(&robo
, ROBO_VLAN_PAGE
, ROBO_VLAN_CTRL4
, disable
? 0 :
491 (1 << 6) /* drop invalid VID frames */);
493 robo_write16(&robo
, ROBO_VLAN_PAGE
, ROBO_VLAN_CTRL5
, disable
? 0 :
494 (1 << 3) /* drop miss V table frames */);
499 if (strcasecmp(argv
[i
], "show") == 0)
503 fprintf(stderr
, "Invalid option %s\n", argv
[i
]);
510 if (argc
== 1) usage();
516 printf("Switch: %sabled\n", robo_read16(&robo
, ROBO_CTRL_PAGE
, ROBO_SWITCH_MODE
) & 2 ? "en" : "dis");
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");
524 val16
= robo_read16(&robo
, ROBO_CTRL_PAGE
, port
[i
]);
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)));
529 robo_read(&robo
, ROBO_STAT_PAGE
, ROBO_LSA_PORT0
+ port
[i
] * 6, mac
, 3);
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);
535 val16
= robo_read16(&robo
, ROBO_VLAN_PAGE
, ROBO_VLAN_CTRL0
);
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" : "");
544 for (i
= 0; i
<= (robo5350
? VLAN_ID_MAX5350
: VLAN_ID_MAX
); i
++) {
546 val16
= (i
) /* vlan */ | (0 << 12) /* read */ | (1 << 13) /* enable */;
550 robo_write16(&robo
, ROBO_VLAN_PAGE
, ROBO_VLAN_TABLE_ACCESS_5350
, val16
);
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");
564 robo_write16(&robo
, ROBO_VLAN_PAGE
, ROBO_VLAN_TABLE_ACCESS
, val16
);
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");