| 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 | } |
| 582 | |