mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2024-12-25 09:56:47 +02:00
e3778a7367
git-svn-id: svn://svn.openwrt.org/openwrt/trunk/openwrt@889 3c298f89-4303-0410-b956-a3cf2f4a3e73
480 lines
14 KiB
C
480 lines
14 KiB
C
/*
|
|
* Broadcom BCM5325E/536x switch configuration utility
|
|
*
|
|
* Copyright (C) 2005 Oleg I. Vdovikin
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301, USA.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/param.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/socket.h>
|
|
|
|
/* linux stuff */
|
|
typedef u_int64_t u64;
|
|
typedef u_int32_t u32;
|
|
typedef u_int16_t u16;
|
|
typedef u_int8_t u8;
|
|
|
|
#include <linux/if.h>
|
|
#include <linux/sockios.h>
|
|
#include <linux/ethtool.h>
|
|
#include <linux/mii.h>
|
|
|
|
#include "etc53xx.h"
|
|
|
|
/* MII registers */
|
|
#define REG_MII_PAGE 0x10 /* MII Page register */
|
|
#define REG_MII_ADDR 0x11 /* MII Address register */
|
|
#define REG_MII_DATA0 0x18 /* MII Data register 0 */
|
|
|
|
#define REG_MII_PAGE_ENABLE 1
|
|
#define REG_MII_ADDR_WRITE 1
|
|
#define REG_MII_ADDR_READ 2
|
|
|
|
/* Private et.o ioctls */
|
|
#define SIOCGETCPHYRD (SIOCDEVPRIVATE + 9)
|
|
#define SIOCSETCPHYWR (SIOCDEVPRIVATE + 10)
|
|
|
|
typedef struct {
|
|
struct ifreq ifr;
|
|
int fd;
|
|
int et; /* use private ioctls */
|
|
} robo_t;
|
|
|
|
static u16 mdio_read(robo_t *robo, u8 reg)
|
|
{
|
|
if (robo->et) {
|
|
int args[2] = { reg };
|
|
|
|
robo->ifr.ifr_data = (caddr_t) args;
|
|
if (ioctl(robo->fd, SIOCGETCPHYRD, (caddr_t)&robo->ifr) < 0) {
|
|
perror("SIOCGETCPHYRD");
|
|
exit(1);
|
|
}
|
|
|
|
return args[1];
|
|
} else {
|
|
struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&robo->ifr.ifr_data;
|
|
mii->reg_num = reg;
|
|
if (ioctl(robo->fd, SIOCGMIIREG, &robo->ifr) < 0) {
|
|
perror("SIOCGMIIREG");
|
|
exit(1);
|
|
}
|
|
return mii->val_out;
|
|
}
|
|
}
|
|
|
|
static void mdio_write(robo_t *robo, u8 reg, u16 val)
|
|
{
|
|
if (robo->et) {
|
|
int args[2] = { reg, val };
|
|
|
|
robo->ifr.ifr_data = (caddr_t) args;
|
|
if (ioctl(robo->fd, SIOCSETCPHYWR, (caddr_t)&robo->ifr) < 0) {
|
|
perror("SIOCGETCPHYWR");
|
|
exit(1);
|
|
}
|
|
} else {
|
|
struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&robo->ifr.ifr_data;
|
|
mii->reg_num = reg;
|
|
mii->val_in = val;
|
|
if (ioctl(robo->fd, SIOCSMIIREG, &robo->ifr) < 0) {
|
|
perror("SIOCSMIIREG");
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int robo_reg(robo_t *robo, u8 page, u8 reg, u8 op)
|
|
{
|
|
int i = 3;
|
|
|
|
/* set page number */
|
|
mdio_write(robo, REG_MII_PAGE,
|
|
(page << 8) | REG_MII_PAGE_ENABLE);
|
|
|
|
/* set register address */
|
|
mdio_write(robo, REG_MII_ADDR, (reg << 8) | op);
|
|
|
|
/* check if operation completed */
|
|
while (i--) {
|
|
if ((mdio_read(robo, REG_MII_ADDR) & 3) == 0)
|
|
return 0;
|
|
}
|
|
|
|
fprintf(stderr, "robo_reg: timeout\n");
|
|
exit(1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void robo_read(robo_t *robo, u8 page, u8 reg, u16 *val, int count)
|
|
{
|
|
int i;
|
|
|
|
robo_reg(robo, page, reg, REG_MII_ADDR_READ);
|
|
|
|
for (i = 0; i < count; i++)
|
|
val[i] = mdio_read(robo, REG_MII_DATA0 + i);
|
|
}
|
|
|
|
static u16 robo_read16(robo_t *robo, u8 page, u8 reg)
|
|
{
|
|
robo_reg(robo, page, reg, REG_MII_ADDR_READ);
|
|
|
|
return mdio_read(robo, REG_MII_DATA0);
|
|
}
|
|
|
|
static u32 robo_read32(robo_t *robo, u8 page, u8 reg)
|
|
{
|
|
robo_reg(robo, page, reg, REG_MII_ADDR_READ);
|
|
|
|
return mdio_read(robo, REG_MII_DATA0) +
|
|
(mdio_read(robo, REG_MII_DATA0 + 1) << 16);
|
|
}
|
|
|
|
static void robo_write16(robo_t *robo, u8 page, u8 reg, u16 val16)
|
|
{
|
|
/* write data */
|
|
mdio_write(robo, REG_MII_DATA0, val16);
|
|
|
|
robo_reg(robo, page, reg, REG_MII_ADDR_WRITE);
|
|
}
|
|
|
|
static void robo_write32(robo_t *robo, u8 page, u8 reg, u32 val32)
|
|
{
|
|
/* write data */
|
|
mdio_write(robo, REG_MII_DATA0, val32 & 65535);
|
|
mdio_write(robo, REG_MII_DATA0 + 1, val32 >> 16);
|
|
|
|
robo_reg(robo, page, reg, REG_MII_ADDR_WRITE);
|
|
}
|
|
|
|
/* checks that attached switch is 5325E/5350 */
|
|
static int robo_vlan5350(robo_t *robo)
|
|
{
|
|
/* set vlan access id to 15 and read it back */
|
|
u16 val16 = 15;
|
|
robo_write16(robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16);
|
|
|
|
/* 5365 will refuse this as it does not have this reg */
|
|
return (robo_read16(robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350) == val16);
|
|
}
|
|
|
|
u8 port[6] = { 0, 1, 2, 3, 4, 8 };
|
|
char ports[6] = { 'W', '4', '3', '2', '1', 'C' };
|
|
char *rxtx[4] = { "enabled", "rx_disabled", "tx_disabled", "disabled" };
|
|
char *stp[8] = { "none", "disable", "block", "listen", "learn", "forward", "6", "7" };
|
|
|
|
void usage()
|
|
{
|
|
fprintf(stderr, "Broadcom BCM5325E/536x switch configuration utility\n"
|
|
"Copyright (C) 2005 Oleg I. Vdovikin\n\n"
|
|
"This program is distributed in the hope that it will be useful,\n"
|
|
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
|
|
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
|
|
"GNU General Public License for more details.\n\n");
|
|
|
|
fprintf(stderr, "Usage: robocfg <op> ... <op>\n"
|
|
"Operations are as below:\n"
|
|
"\tshow\n"
|
|
"\tswitch <enable|disable>\n"
|
|
"\tport <port_number> [state <%s|%s|%s|%s>]\n\t\t[stp %s|%s|%s|%s|%s|%s] [tag <vlan_tag>]\n"
|
|
"\tvlan <vlan_number> [ports <ports_list>]\n"
|
|
"\tvlans <enable|disable|reset>\n\n"
|
|
"\tports_list should be one argument, space separated, quoted if needed,\n"
|
|
"\tport number could be followed by 't' to leave packet vlan tagged (CPU \n"
|
|
"\tport default) or by 'u' to untag packet (other ports default) before \n"
|
|
"\tbringing it to the port, '*' is ignored\n"
|
|
"\nSamples:\n"
|
|
"1) ASUS WL-500g Deluxe stock config (eth0 is WAN, eth0.1 is LAN):\n"
|
|
"robocfg switch disable vlans enable reset vlan 0 ports \"0 5u\" vlan 1 ports \"1 2 3 4 5t\""
|
|
" port 0 state enabled stp none switch enable\n"
|
|
"2) WRT54g, WL-500g Deluxe OpenWRT config (vlan0 is LAN, vlan1 is WAN):\n"
|
|
"robocfg switch disable vlans enable reset vlan 0 ports \"1 2 3 4 5t\" vlan 1 ports \"0 5t\""
|
|
" port 0 state enabled stp none switch enable\n",
|
|
rxtx[0], rxtx[1], rxtx[2], rxtx[3], stp[0], stp[1], stp[2], stp[3], stp[4], stp[5]);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
u16 val16;
|
|
u16 mac[3];
|
|
int i = 0, j;
|
|
int robo5350 = 0;
|
|
u32 phyid;
|
|
|
|
static robo_t robo;
|
|
struct ethtool_drvinfo info;
|
|
|
|
if ((robo.fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
|
perror("socket");
|
|
exit(1);
|
|
}
|
|
|
|
/* the only interface for now */
|
|
strcpy(robo.ifr.ifr_name, "eth0");
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
info.cmd = ETHTOOL_GDRVINFO;
|
|
robo.ifr.ifr_data = (caddr_t)&info;
|
|
if (ioctl(robo.fd, SIOCETHTOOL, (caddr_t)&robo.ifr) < 0) {
|
|
perror("SIOCETHTOOL: your ethernet module is either unsupported or outdated");
|
|
// exit(1);
|
|
} else
|
|
if (strcmp(info.driver, "et0") && strcmp(info.driver, "b44")) {
|
|
fprintf(stderr, "No suitable module found for %s (managed by %s)\n",
|
|
robo.ifr.ifr_name, info.driver);
|
|
exit(1);
|
|
}
|
|
|
|
/* try access using MII ioctls - get phy address */
|
|
if (ioctl(robo.fd, SIOCGMIIPHY, &robo.ifr) < 0) {
|
|
robo.et = 1;
|
|
} else {
|
|
/* got phy address check for robo address */
|
|
struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&robo.ifr.ifr_data;
|
|
if (mii->phy_id != 30) {
|
|
fprintf(stderr, "Invalid phy address (%d)\n", mii->phy_id);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
phyid = mdio_read(&robo, 0x2) | (mdio_read(&robo, 0x3) << 16);
|
|
|
|
if (phyid == 0xffffffff || phyid == 0x55210022) {
|
|
fprintf(stderr, "No Robo switch in managed mode found\n");
|
|
exit(1);
|
|
}
|
|
|
|
robo5350 = robo_vlan5350(&robo);
|
|
|
|
for (i = 1; i < argc;) {
|
|
if (strcmp(argv[i], "port") == 0 && (i + 1) < argc)
|
|
{
|
|
int index = atoi(argv[++i]);
|
|
/* read port specs */
|
|
while (++i < argc) {
|
|
if (strcmp(argv[i], "state") == 0 && ++i < argc) {
|
|
for (j = 0; j < 4 && strcmp(argv[i], rxtx[j]); j++);
|
|
if (j < 4) {
|
|
/* change state */
|
|
robo_write16(&robo,ROBO_CTRL_PAGE, port[index],
|
|
(robo_read16(&robo, ROBO_CTRL_PAGE, port[index]) & ~(3 << 0)) | (j << 0));
|
|
} else {
|
|
fprintf(stderr, "Invalid state '%s'.\n", argv[i]);
|
|
exit(1);
|
|
}
|
|
} else
|
|
if (strcmp(argv[i], "stp") == 0 && ++i < argc) {
|
|
for (j = 0; j < 8 && strcmp(argv[i], stp[j]); j++);
|
|
if (j < 8) {
|
|
/* change stp */
|
|
robo_write16(&robo,ROBO_CTRL_PAGE, port[index],
|
|
(robo_read16(&robo, ROBO_CTRL_PAGE, port[index]) & ~(7 << 5)) | (j << 5));
|
|
} else {
|
|
fprintf(stderr, "Invalid stp '%s'.\n", argv[i]);
|
|
exit(1);
|
|
}
|
|
} else
|
|
if (strcmp(argv[i], "tag") == 0 && ++i < argc) {
|
|
j = atoi(argv[i]);
|
|
/* change vlan tag */
|
|
robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_PORT0_DEF_TAG + (index << 1), j);
|
|
} else break;
|
|
}
|
|
} else
|
|
if (strcmp(argv[i], "vlan") == 0 && (i + 1) < argc)
|
|
{
|
|
int index = atoi(argv[++i]);
|
|
while (++i < argc) {
|
|
if (strcmp(argv[i], "ports") == 0 && ++i < argc) {
|
|
char *ports = argv[i];
|
|
int untag = 0;
|
|
int member = 0;
|
|
|
|
while (*ports >= '0' && *ports <= '9') {
|
|
j = *ports++ - '0';
|
|
member |= 1 << j;
|
|
|
|
/* untag if needed, CPU port requires special handling */
|
|
if (*ports == 'u' || (j != 5 && (*ports == ' ' || *ports == 0)))
|
|
{
|
|
untag |= 1 << j;
|
|
if (*ports) ports++;
|
|
/* change default vlan tag */
|
|
robo_write16(&robo, ROBO_VLAN_PAGE,
|
|
ROBO_VLAN_PORT0_DEF_TAG + (j << 1), index);
|
|
} else
|
|
if (*ports == '*' || *ports == 't' || *ports == ' ') ports++;
|
|
else break;
|
|
|
|
while (*ports == ' ') ports++;
|
|
}
|
|
|
|
if (*ports) {
|
|
fprintf(stderr, "Invalid ports '%s'.\n", argv[i]);
|
|
exit(1);
|
|
} else {
|
|
/* write config now */
|
|
val16 = (index) /* vlan */ | (1 << 12) /* write */ | (1 << 13) /* enable */;
|
|
if (robo5350) {
|
|
robo_write32(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_WRITE_5350,
|
|
(1 << 20) /* valid */ | (untag << 6) | member);
|
|
robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16);
|
|
} else {
|
|
robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_WRITE,
|
|
(1 << 14) /* valid */ | (untag << 7) | member);
|
|
robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS, val16);
|
|
}
|
|
}
|
|
} else break;
|
|
}
|
|
} else
|
|
if (strcmp(argv[i], "switch") == 0 && (i + 1) < argc)
|
|
{
|
|
/* enable/disable switching */
|
|
robo_write16(&robo, ROBO_CTRL_PAGE, ROBO_SWITCH_MODE,
|
|
(robo_read16(&robo, ROBO_CTRL_PAGE, ROBO_SWITCH_MODE) & ~2) |
|
|
(*argv[++i] == 'e' ? 2 : 0));
|
|
i++;
|
|
} else
|
|
if (strcmp(argv[i], "vlans") == 0 && (i + 1) < argc)
|
|
{
|
|
while (++i < argc) {
|
|
if (strcmp(argv[i], "reset") == 0) {
|
|
/* reset vlan validity bit */
|
|
for (j = 0; j <= (robo5350 ? VLAN_ID_MAX5350 : VLAN_ID_MAX); j++)
|
|
{
|
|
/* write config now */
|
|
val16 = (j) /* vlan */ | (1 << 12) /* write */ | (1 << 13) /* enable */;
|
|
if (robo5350) {
|
|
robo_write32(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_WRITE_5350, 0);
|
|
robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16);
|
|
} else {
|
|
robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_WRITE, 0);
|
|
robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS, val16);
|
|
}
|
|
}
|
|
} else
|
|
if (strcmp(argv[i], "enable") == 0 || strcmp(argv[i], "disable") == 0)
|
|
{
|
|
int disable = (*argv[i] == 'd');
|
|
/* enable/disable vlans */
|
|
robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_CTRL0, disable ? 0 :
|
|
(1 << 7) /* 802.1Q VLAN */ | (3 << 5) /* mac check and hash */);
|
|
|
|
robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_CTRL1, disable ? 0 :
|
|
(1 << 1) | (1 << 2) | (1 << 3) /* RSV multicast */);
|
|
|
|
robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_CTRL4, disable ? 0 :
|
|
(1 << 6) /* drop invalid VID frames */);
|
|
|
|
robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_CTRL5, disable ? 0 :
|
|
(1 << 3) /* drop miss V table frames */);
|
|
|
|
} else break;
|
|
}
|
|
} else
|
|
if (strcmp(argv[i], "show") == 0)
|
|
{
|
|
break;
|
|
} else {
|
|
fprintf(stderr, "Invalid option %s\n", argv[i]);
|
|
usage();
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
if (i == argc) {
|
|
if (argc == 1) usage();
|
|
return 0;
|
|
}
|
|
|
|
/* show config */
|
|
|
|
printf("Switch: %sabled\n", robo_read16(&robo, ROBO_CTRL_PAGE, ROBO_SWITCH_MODE) & 2 ? "en" : "dis");
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
printf(robo_read16(&robo, ROBO_STAT_PAGE, ROBO_LINK_STAT_SUMMARY) & (1 << port[i]) ?
|
|
"Port %d(%c): %s%s " : "Port %d(%c): DOWN ", i, ports[i],
|
|
robo_read16(&robo, ROBO_STAT_PAGE, ROBO_SPEED_STAT_SUMMARY) & (1 << port[i]) ? "100" : " 10",
|
|
robo_read16(&robo, ROBO_STAT_PAGE, ROBO_DUPLEX_STAT_SUMMARY) & (1 << port[i]) ? "FD" : "HD");
|
|
|
|
val16 = robo_read16(&robo, ROBO_CTRL_PAGE, port[i]);
|
|
|
|
printf("%s stp: %s vlan: %d ", rxtx[val16 & 3], stp[(val16 >> 5) & 7],
|
|
robo_read16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_PORT0_DEF_TAG + (i << 1)));
|
|
|
|
robo_read(&robo, ROBO_STAT_PAGE, ROBO_LSA_PORT0 + port[i] * 6, mac, 3);
|
|
|
|
printf("mac: %02x:%02x:%02x:%02x:%02x:%02x\n",
|
|
mac[2] >> 8, mac[2] & 255, mac[1] >> 8, mac[1] & 255, mac[0] >> 8, mac[0] & 255);
|
|
}
|
|
|
|
val16 = robo_read16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_CTRL0);
|
|
|
|
printf("VLANs: %s %sabled%s%s\n",
|
|
robo5350 ? "BCM5325/535x" : "BCM536x",
|
|
(val16 & (1 << 7)) ? "en" : "dis",
|
|
(val16 & (1 << 6)) ? " mac_check" : "",
|
|
(val16 & (1 << 5)) ? " mac_hash" : "");
|
|
|
|
/* scan VLANs */
|
|
for (i = 0; i <= (robo5350 ? VLAN_ID_MAX5350 : VLAN_ID_MAX); i++) {
|
|
/* issue read */
|
|
val16 = (i) /* vlan */ | (0 << 12) /* read */ | (1 << 13) /* enable */;
|
|
|
|
if (robo5350) {
|
|
u32 val32;
|
|
robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16);
|
|
/* actual read */
|
|
val32 = robo_read32(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_READ);
|
|
if ((val32 & (1 << 20)) /* valid */) {
|
|
printf("vlan%d:", i);
|
|
for (j = 0; j < 6; j++) {
|
|
if (val32 & (1 << j)) {
|
|
printf(" %d%s", j, (val32 & (1 << (j + 6))) ?
|
|
(j == 5 ? "u" : "") : "t");
|
|
}
|
|
}
|
|
printf("\n");
|
|
}
|
|
} else {
|
|
robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS, val16);
|
|
/* actual read */
|
|
val16 = robo_read16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_READ);
|
|
if ((val16 & (1 << 14)) /* valid */) {
|
|
printf("vlan%d:", i);
|
|
for (j = 0; j < 6; j++) {
|
|
if (val16 & (1 << j)) {
|
|
printf(" %d%s", j, (val16 & (1 << (j + 7))) ?
|
|
(j == 5 ? "u" : "") : "t");
|
|
}
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
return (0);
|
|
}
|