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>
29 #include <sys/types.h>
32 typedef u_int64_t u64;
33 typedef u_int32_t u32;
34 typedef u_int16_t u16;
38 #include <linux/sockios.h>
39 #include <linux/ethtool.h>
40 #include <linux/mii.h>
43 #define ROBO_PHY_ADDR 0x1E /* robo switch phy address */
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 */
50 #define REG_MII_PAGE_ENABLE 1
51 #define REG_MII_ADDR_WRITE 1
52 #define REG_MII_ADDR_READ 2
54 /* Private et.o ioctls */
55 #define SIOCGETCPHYRD (SIOCDEVPRIVATE + 9)
56 #define SIOCSETCPHYWR (SIOCDEVPRIVATE + 10)
61 int et; /* use private ioctls */
64 static u16 mdio_read(robo_t *robo, u16 phy_id, u8 reg)
67 int args[2] = { reg };
69 if (phy_id != ROBO_PHY_ADDR) {
71 "Access to real 'phy' registers unavaliable.\n"
72 "Upgrade kernel driver.\n");
77 robo->ifr.ifr_data = (caddr_t) args;
78 if (ioctl(robo->fd, SIOCGETCPHYRD, (caddr_t)&robo->ifr) < 0) {
79 perror("SIOCGETCPHYRD");
85 struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&robo->ifr.ifr_data;
88 if (ioctl(robo->fd, SIOCGMIIREG, &robo->ifr) < 0) {
89 perror("SIOCGMIIREG");
96 static void mdio_write(robo_t *robo, u16 phy_id, u8 reg, u16 val)
99 int args[2] = { reg, val };
101 if (phy_id != ROBO_PHY_ADDR) {
103 "Access to real 'phy' registers unavaliable.\n"
104 "Upgrade kernel driver.\n");
108 robo->ifr.ifr_data = (caddr_t) args;
109 if (ioctl(robo->fd, SIOCSETCPHYWR, (caddr_t)&robo->ifr) < 0) {
110 perror("SIOCGETCPHYWR");
114 struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&robo->ifr.ifr_data;
115 mii->phy_id = phy_id;
118 if (ioctl(robo->fd, SIOCSMIIREG, &robo->ifr) < 0) {
119 perror("SIOCSMIIREG");
125 static int robo_reg(robo_t *robo, u8 page, u8 reg, u8 op)
129 /* set page number */
130 mdio_write(robo, ROBO_PHY_ADDR, REG_MII_PAGE,
131 (page << 8) | REG_MII_PAGE_ENABLE);
133 /* set register address */
134 mdio_write(robo, ROBO_PHY_ADDR, REG_MII_ADDR,
137 /* check if operation completed */
139 if ((mdio_read(robo, ROBO_PHY_ADDR, REG_MII_ADDR) & 3) == 0)
143 fprintf(stderr, "robo_reg: timeout\n");
149 static void robo_read(robo_t *robo, u8 page, u8 reg, u16 *val, int count)
153 robo_reg(robo, page, reg, REG_MII_ADDR_READ);
155 for (i = 0; i < count; i++)
156 val[i] = mdio_read(robo, ROBO_PHY_ADDR, REG_MII_DATA0 + i);
159 static u16 robo_read16(robo_t *robo, u8 page, u8 reg)
161 robo_reg(robo, page, reg, REG_MII_ADDR_READ);
163 return mdio_read(robo, ROBO_PHY_ADDR, REG_MII_DATA0);
166 static u32 robo_read32(robo_t *robo, u8 page, u8 reg)
168 robo_reg(robo, page, reg, REG_MII_ADDR_READ);
170 return mdio_read(robo, ROBO_PHY_ADDR, REG_MII_DATA0) +
171 (mdio_read(robo, ROBO_PHY_ADDR, REG_MII_DATA0 + 1) << 16);
174 static void robo_write16(robo_t *robo, u8 page, u8 reg, u16 val16)
177 mdio_write(robo, ROBO_PHY_ADDR, REG_MII_DATA0, val16);
179 robo_reg(robo, page, reg, REG_MII_ADDR_WRITE);
182 static void robo_write32(robo_t *robo, u8 page, u8 reg, u32 val32)
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);
188 robo_reg(robo, page, reg, REG_MII_ADDR_WRITE);
191 /* checks that attached switch is 5325E/5350 */
192 static int robo_vlan5350(robo_t *robo)
194 /* set vlan access id to 15 and read it back */
196 robo_write16(robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16);
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);
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" };
210 } media[5] = { { "auto", BMCR_ANENABLE | BMCR_ANRESTART },
211 { "10HD", 0 }, { "10FD", BMCR_FULLDPLX },
212 { "100HD", BMCR_SPEED100 }, { "100FD", BMCR_SPEED100 | BMCR_FULLDPLX } };
217 } mdix[3] = { { "auto", 0x0000 }, { "on", 0x1800 }, { "off", 0x0800 } };
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");
228 fprintf(stderr, "Usage: robocfg <op> ... <op>\n"
229 "Operations are as below:\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"
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);
253 int bcm53xx_probe(const char *dev)
255 struct ethtool_drvinfo info;
259 fprintf(stderr, "probing %s\n", dev);
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);
267 perror("SIOCETHTOOL");
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);
278 /* try access using MII ioctls - get phy address */
280 if (ioctl(robo.fd, SIOCGMIIPHY, &robo.ifr) < 0)
284 unsigned int args[2] = { 2 };
286 robo.ifr.ifr_data = (caddr_t) args;
287 ret = ioctl(robo.fd, SIOCGETCPHYRD, (caddr_t)&robo.ifr);
289 perror("SIOCGETCPHYRD");
292 phyid = args[1] & 0xffff;
295 robo.ifr.ifr_data = (caddr_t) args;
296 ret = ioctl(robo.fd, SIOCGETCPHYRD, (caddr_t)&robo.ifr);
298 perror("SIOCGETCPHYRD");
301 phyid |= args[1] << 16;
303 struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&robo.ifr.ifr_data;
304 mii->phy_id = ROBO_PHY_ADDR;
306 ret = ioctl(robo.fd, SIOCGMIIREG, &robo.ifr);
308 perror("SIOCGMIIREG");
311 phyid = mii->val_out & 0xffff;
313 mii->phy_id = ROBO_PHY_ADDR;
315 ret = ioctl(robo.fd, SIOCGMIIREG, &robo.ifr);
317 perror("SIOCGMIIREG");
320 phyid |= mii->val_out << 16;
323 if (phyid == 0xffffffff || phyid == 0x55210022) {
332 main(int argc, char *argv[])
340 if ((robo.fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
345 if (bcm53xx_probe("eth1")) {
346 if (bcm53xx_probe("eth0")) {
347 perror("bcm53xx_probe");
352 robo5350 = robo_vlan5350(&robo);
354 for (i = 1; i < argc;) {
355 if (strcasecmp(argv[i], "port") == 0 && (i + 1) < argc)
357 int index = atoi(argv[++i]);
358 /* read port specs */
360 if (strcasecmp(argv[i], "state") == 0 && ++i < argc) {
361 for (j = 0; j < 4 && strcasecmp(argv[i], rxtx[j]); j++);
364 robo_write16(&robo,ROBO_CTRL_PAGE, port[index],
365 (robo_read16(&robo, ROBO_CTRL_PAGE, port[index]) & ~(3 << 0)) | (j << 0));
367 fprintf(stderr, "Invalid state '%s'.\n", argv[i]);
371 if (strcasecmp(argv[i], "stp") == 0 && ++i < argc) {
372 for (j = 0; j < 8 && strcasecmp(argv[i], stp[j]); j++);
375 robo_write16(&robo,ROBO_CTRL_PAGE, port[index],
376 (robo_read16(&robo, ROBO_CTRL_PAGE, port[index]) & ~(7 << 5)) | (j << 5));
378 fprintf(stderr, "Invalid stp '%s'.\n", argv[i]);
382 if (strcasecmp(argv[i], "media") == 0 && ++i < argc) {
383 for (j = 0; j < 5 && strcasecmp(argv[i], media[j].name); j++);
385 mdio_write(&robo, port[index], MII_BMCR, media[j].bmcr);
387 fprintf(stderr, "Invalid media '%s'.\n", argv[i]);
391 if (strcasecmp(argv[i], "mdi-x") == 0 && ++i < argc) {
392 for (j = 0; j < 3 && strcasecmp(argv[i], mdix[j].name); j++);
394 mdio_write(&robo, port[index], 0x1c, mdix[j].value |
395 (mdio_read(&robo, port[index], 0x1c) & ~0x1800));
397 fprintf(stderr, "Invalid mdi-x '%s'.\n", argv[i]);
401 if (strcasecmp(argv[i], "tag") == 0 && ++i < argc) {
403 /* change vlan tag */
404 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_PORT0_DEF_TAG + (index << 1), j);
408 if (strcasecmp(argv[i], "vlan") == 0 && (i + 1) < argc)
410 int index = atoi(argv[++i]);
412 if (strcasecmp(argv[i], "ports") == 0 && ++i < argc) {
413 char *ports = argv[i];
417 while (*ports >= '0' && *ports <= '9') {
421 /* untag if needed, CPU port requires special handling */
422 if (*ports == 'u' || (j != 5 && (*ports == ' ' || *ports == 0)))
426 /* change default vlan tag */
427 robo_write16(&robo, ROBO_VLAN_PAGE,
428 ROBO_VLAN_PORT0_DEF_TAG + (j << 1), index);
430 if (*ports == '*' || *ports == 't' || *ports == ' ') ports++;
433 while (*ports == ' ') ports++;
437 fprintf(stderr, "Invalid ports '%s'.\n", argv[i]);
440 /* write config now */
441 val16 = (index) /* vlan */ | (1 << 12) /* write */ | (1 << 13) /* enable */;
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);
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);
455 if (strcasecmp(argv[i], "switch") == 0 && (i + 1) < argc)
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));
463 if (strcasecmp(argv[i], "vlans") == 0 && (i + 1) < 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++)
470 /* write config now */
471 val16 = (j) /* vlan */ | (1 << 12) /* write */ | (1 << 13) /* enable */;
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);
476 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_WRITE, 0);
477 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS, val16);
481 if (strcasecmp(argv[i], "enable") == 0 || strcasecmp(argv[i], "disable") == 0)
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 */);
488 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_CTRL1, disable ? 0 :
489 (1 << 1) | (1 << 2) | (1 << 3) /* RSV multicast */);
491 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_CTRL4, disable ? 0 :
492 (1 << 6) /* drop invalid VID frames */);
494 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_CTRL5, disable ? 0 :
495 (1 << 3) /* drop miss V table frames */);
500 if (strcasecmp(argv[i], "show") == 0)
504 fprintf(stderr, "Invalid option %s\n", argv[i]);
511 if (argc == 1) usage();
517 printf("Switch: %sabled\n", robo_read16(&robo, ROBO_CTRL_PAGE, ROBO_SWITCH_MODE) & 2 ? "en" : "dis");
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");
525 val16 = robo_read16(&robo, ROBO_CTRL_PAGE, port[i]);
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)));
530 robo_read(&robo, ROBO_STAT_PAGE, ROBO_LSA_PORT0 + port[i] * 6, mac, 3);
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);
536 val16 = robo_read16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_CTRL0);
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" : "");
545 for (i = 0; i <= (robo5350 ? VLAN_ID_MAX5350 : VLAN_ID_MAX); i++) {
547 val16 = (i) /* vlan */ | (0 << 12) /* read */ | (1 << 13) /* enable */;
551 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16);
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");
565 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS, val16);
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");