finally move buildroot-ng to trunk
[openwrt/svn-archive/archive.git] / package / 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 <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/param.h>
26 #include <sys/ioctl.h>
27 #include <sys/socket.h>
28
29 /* linux stuff */
30 typedef u_int64_t u64;
31 typedef u_int32_t u32;
32 typedef u_int16_t u16;
33 typedef u_int8_t u8;
34
35 #include <linux/if.h>
36 #include <linux/sockios.h>
37 #include <linux/ethtool.h>
38 #include <linux/mii.h>
39
40 #include "etc53xx.h"
41 #define ROBO_PHY_ADDR 0x1E /* robo switch phy address */
42
43 /* MII registers */
44 #define REG_MII_PAGE 0x10 /* MII Page register */
45 #define REG_MII_ADDR 0x11 /* MII Address register */
46 #define REG_MII_DATA0 0x18 /* MII Data register 0 */
47
48 #define REG_MII_PAGE_ENABLE 1
49 #define REG_MII_ADDR_WRITE 1
50 #define REG_MII_ADDR_READ 2
51
52 /* Private et.o ioctls */
53 #define SIOCGETCPHYRD (SIOCDEVPRIVATE + 9)
54 #define SIOCSETCPHYWR (SIOCDEVPRIVATE + 10)
55
56 typedef struct {
57 struct ifreq ifr;
58 int fd;
59 int et; /* use private ioctls */
60 } robo_t;
61
62 static u16 mdio_read(robo_t *robo, u16 phy_id, u8 reg)
63 {
64 if (robo->et) {
65 int args[2] = { reg };
66
67 if (phy_id != ROBO_PHY_ADDR) {
68 fprintf(stderr,
69 "Access to real 'phy' registers unavaliable.\n"
70 "Upgrade kernel driver.\n");
71
72 return 0xffff;
73 }
74
75 robo->ifr.ifr_data = (caddr_t) args;
76 if (ioctl(robo->fd, SIOCGETCPHYRD, (caddr_t)&robo->ifr) < 0) {
77 perror("SIOCGETCPHYRD");
78 exit(1);
79 }
80
81 return args[1];
82 } else {
83 struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&robo->ifr.ifr_data;
84 mii->phy_id = phy_id;
85 mii->reg_num = reg;
86 if (ioctl(robo->fd, SIOCGMIIREG, &robo->ifr) < 0) {
87 perror("SIOCGMIIREG");
88 exit(1);
89 }
90 return mii->val_out;
91 }
92 }
93
94 static void mdio_write(robo_t *robo, u16 phy_id, u8 reg, u16 val)
95 {
96 if (robo->et) {
97 int args[2] = { reg, val };
98
99 if (phy_id != ROBO_PHY_ADDR) {
100 fprintf(stderr,
101 "Access to real 'phy' registers unavaliable.\n"
102 "Upgrade kernel driver.\n");
103 return;
104 }
105
106 robo->ifr.ifr_data = (caddr_t) args;
107 if (ioctl(robo->fd, SIOCSETCPHYWR, (caddr_t)&robo->ifr) < 0) {
108 perror("SIOCGETCPHYWR");
109 exit(1);
110 }
111 } else {
112 struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&robo->ifr.ifr_data;
113 mii->phy_id = phy_id;
114 mii->reg_num = reg;
115 mii->val_in = val;
116 if (ioctl(robo->fd, SIOCSMIIREG, &robo->ifr) < 0) {
117 perror("SIOCSMIIREG");
118 exit(1);
119 }
120 }
121 }
122
123 static int robo_reg(robo_t *robo, u8 page, u8 reg, u8 op)
124 {
125 int i = 3;
126
127 /* set page number */
128 mdio_write(robo, ROBO_PHY_ADDR, REG_MII_PAGE,
129 (page << 8) | REG_MII_PAGE_ENABLE);
130
131 /* set register address */
132 mdio_write(robo, ROBO_PHY_ADDR, REG_MII_ADDR,
133 (reg << 8) | op);
134
135 /* check if operation completed */
136 while (i--) {
137 if ((mdio_read(robo, ROBO_PHY_ADDR, REG_MII_ADDR) & 3) == 0)
138 return 0;
139 }
140
141 fprintf(stderr, "robo_reg: timeout\n");
142 exit(1);
143
144 return 0;
145 }
146
147 static void robo_read(robo_t *robo, u8 page, u8 reg, u16 *val, int count)
148 {
149 int i;
150
151 robo_reg(robo, page, reg, REG_MII_ADDR_READ);
152
153 for (i = 0; i < count; i++)
154 val[i] = mdio_read(robo, ROBO_PHY_ADDR, REG_MII_DATA0 + i);
155 }
156
157 static u16 robo_read16(robo_t *robo, u8 page, u8 reg)
158 {
159 robo_reg(robo, page, reg, REG_MII_ADDR_READ);
160
161 return mdio_read(robo, ROBO_PHY_ADDR, REG_MII_DATA0);
162 }
163
164 static u32 robo_read32(robo_t *robo, u8 page, u8 reg)
165 {
166 robo_reg(robo, page, reg, REG_MII_ADDR_READ);
167
168 return mdio_read(robo, ROBO_PHY_ADDR, REG_MII_DATA0) +
169 (mdio_read(robo, ROBO_PHY_ADDR, REG_MII_DATA0 + 1) << 16);
170 }
171
172 static void robo_write16(robo_t *robo, u8 page, u8 reg, u16 val16)
173 {
174 /* write data */
175 mdio_write(robo, ROBO_PHY_ADDR, REG_MII_DATA0, val16);
176
177 robo_reg(robo, page, reg, REG_MII_ADDR_WRITE);
178 }
179
180 static void robo_write32(robo_t *robo, u8 page, u8 reg, u32 val32)
181 {
182 /* write data */
183 mdio_write(robo, ROBO_PHY_ADDR, REG_MII_DATA0, val32 & 65535);
184 mdio_write(robo, ROBO_PHY_ADDR, REG_MII_DATA0 + 1, val32 >> 16);
185
186 robo_reg(robo, page, reg, REG_MII_ADDR_WRITE);
187 }
188
189 /* checks that attached switch is 5325E/5350 */
190 static int robo_vlan5350(robo_t *robo)
191 {
192 /* set vlan access id to 15 and read it back */
193 u16 val16 = 15;
194 robo_write16(robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16);
195
196 /* 5365 will refuse this as it does not have this reg */
197 return (robo_read16(robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350) == val16);
198 }
199
200 u8 port[6] = { 0, 1, 2, 3, 4, 8 };
201 char ports[6] = { 'W', '4', '3', '2', '1', 'C' };
202 char *rxtx[4] = { "enabled", "rx_disabled", "tx_disabled", "disabled" };
203 char *stp[8] = { "none", "disable", "block", "listen", "learn", "forward", "6", "7" };
204
205 struct {
206 char *name;
207 u16 bmcr;
208 } media[5] = { { "auto", BMCR_ANENABLE | BMCR_ANRESTART },
209 { "10HD", 0 }, { "10FD", BMCR_FULLDPLX },
210 { "100HD", BMCR_SPEED100 }, { "100FD", BMCR_SPEED100 | BMCR_FULLDPLX } };
211
212 struct {
213 char *name;
214 u16 value;
215 } mdix[3] = { { "auto", 0x0000 }, { "on", 0x1800 }, { "off", 0x0800 } };
216
217 void usage()
218 {
219 fprintf(stderr, "Broadcom BCM5325E/536x switch configuration utility\n"
220 "Copyright (C) 2005 Oleg I. Vdovikin\n\n"
221 "This program is distributed in the hope that it will be useful,\n"
222 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
223 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
224 "GNU General Public License for more details.\n\n");
225
226 fprintf(stderr, "Usage: robocfg <op> ... <op>\n"
227 "Operations are as below:\n"
228 "\tshow\n"
229 "\tswitch <enable|disable>\n"
230 "\tport <port_number> [state <%s|%s|%s|%s>]\n\t\t[stp %s|%s|%s|%s|%s|%s] [tag <vlan_tag>]\n"
231 "\t\t[media %s|%s|%s|%s|%s] [mdi-x %s|%s|%s]\n"
232 "\tvlan <vlan_number> [ports <ports_list>]\n"
233 "\tvlans <enable|disable|reset>\n\n"
234 "\tports_list should be one argument, space separated, quoted if needed,\n"
235 "\tport number could be followed by 't' to leave packet vlan tagged (CPU \n"
236 "\tport default) or by 'u' to untag packet (other ports default) before \n"
237 "\tbringing it to the port, '*' is ignored\n"
238 "\nSamples:\n"
239 "1) ASUS WL-500g Deluxe stock config (eth0 is WAN, eth0.1 is LAN):\n"
240 "robocfg switch disable vlans enable reset vlan 0 ports \"0 5u\" vlan 1 ports \"1 2 3 4 5t\""
241 " port 0 state enabled stp none switch enable\n"
242 "2) WRT54g, WL-500g Deluxe OpenWRT config (vlan0 is LAN, vlan1 is WAN):\n"
243 "robocfg switch disable vlans enable reset vlan 0 ports \"1 2 3 4 5t\" vlan 1 ports \"0 5t\""
244 " port 0 state enabled stp none switch enable\n",
245 rxtx[0], rxtx[1], rxtx[2], rxtx[3], stp[0], stp[1], stp[2], stp[3], stp[4], stp[5],
246 media[0].name, media[1].name, media[2].name, media[3].name, media[4].name,
247 mdix[0].name, mdix[1].name, mdix[2].name);
248 }
249
250 int
251 main(int argc, char *argv[])
252 {
253 u16 val16;
254 u16 mac[3];
255 int i = 0, j;
256 int robo5350 = 0;
257 u32 phyid;
258
259 static robo_t robo;
260 struct ethtool_drvinfo info;
261
262 if ((robo.fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
263 perror("socket");
264 exit(1);
265 }
266
267 /* the only interface for now */
268 strcpy(robo.ifr.ifr_name, "eth0");
269
270 memset(&info, 0, sizeof(info));
271 info.cmd = ETHTOOL_GDRVINFO;
272 robo.ifr.ifr_data = (caddr_t)&info;
273 if (ioctl(robo.fd, SIOCETHTOOL, (caddr_t)&robo.ifr) < 0) {
274 perror("SIOCETHTOOL: your ethernet module is either unsupported or outdated");
275 // exit(1);
276 } else
277 if (strcmp(info.driver, "et0") && strcmp(info.driver, "b44")) {
278 fprintf(stderr, "No suitable module found for %s (managed by %s)\n",
279 robo.ifr.ifr_name, info.driver);
280 exit(1);
281 }
282
283 /* try access using MII ioctls - get phy address */
284 if (ioctl(robo.fd, SIOCGMIIPHY, &robo.ifr) < 0) {
285 robo.et = 1;
286 } else {
287 /* got phy address check for robo address */
288 struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&robo.ifr.ifr_data;
289 if (mii->phy_id != ROBO_PHY_ADDR) {
290 fprintf(stderr, "Invalid phy address (%d)\n", mii->phy_id);
291 exit(1);
292 }
293 }
294
295 phyid = mdio_read(&robo, ROBO_PHY_ADDR, 0x2) |
296 (mdio_read(&robo, ROBO_PHY_ADDR, 0x3) << 16);
297
298 if (phyid == 0xffffffff || phyid == 0x55210022) {
299 fprintf(stderr, "No Robo switch in managed mode found\n");
300 exit(1);
301 }
302
303 robo5350 = robo_vlan5350(&robo);
304
305 for (i = 1; i < argc;) {
306 if (strcasecmp(argv[i], "port") == 0 && (i + 1) < argc)
307 {
308 int index = atoi(argv[++i]);
309 /* read port specs */
310 while (++i < argc) {
311 if (strcasecmp(argv[i], "state") == 0 && ++i < argc) {
312 for (j = 0; j < 4 && strcasecmp(argv[i], rxtx[j]); j++);
313 if (j < 4) {
314 /* change state */
315 robo_write16(&robo,ROBO_CTRL_PAGE, port[index],
316 (robo_read16(&robo, ROBO_CTRL_PAGE, port[index]) & ~(3 << 0)) | (j << 0));
317 } else {
318 fprintf(stderr, "Invalid state '%s'.\n", argv[i]);
319 exit(1);
320 }
321 } else
322 if (strcasecmp(argv[i], "stp") == 0 && ++i < argc) {
323 for (j = 0; j < 8 && strcasecmp(argv[i], stp[j]); j++);
324 if (j < 8) {
325 /* change stp */
326 robo_write16(&robo,ROBO_CTRL_PAGE, port[index],
327 (robo_read16(&robo, ROBO_CTRL_PAGE, port[index]) & ~(7 << 5)) | (j << 5));
328 } else {
329 fprintf(stderr, "Invalid stp '%s'.\n", argv[i]);
330 exit(1);
331 }
332 } else
333 if (strcasecmp(argv[i], "media") == 0 && ++i < argc) {
334 for (j = 0; j < 5 && strcasecmp(argv[i], media[j].name); j++);
335 if (j < 5) {
336 mdio_write(&robo, port[index], MII_BMCR, media[j].bmcr);
337 } else {
338 fprintf(stderr, "Invalid media '%s'.\n", argv[i]);
339 exit(1);
340 }
341 } else
342 if (strcasecmp(argv[i], "mdi-x") == 0 && ++i < argc) {
343 for (j = 0; j < 3 && strcasecmp(argv[i], mdix[j].name); j++);
344 if (j < 3) {
345 mdio_write(&robo, port[index], 0x1c, mdix[j].value |
346 (mdio_read(&robo, port[index], 0x1c) & ~0x1800));
347 } else {
348 fprintf(stderr, "Invalid mdi-x '%s'.\n", argv[i]);
349 exit(1);
350 }
351 } else
352 if (strcasecmp(argv[i], "tag") == 0 && ++i < argc) {
353 j = atoi(argv[i]);
354 /* change vlan tag */
355 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_PORT0_DEF_TAG + (index << 1), j);
356 } else break;
357 }
358 } else
359 if (strcasecmp(argv[i], "vlan") == 0 && (i + 1) < argc)
360 {
361 int index = atoi(argv[++i]);
362 while (++i < argc) {
363 if (strcasecmp(argv[i], "ports") == 0 && ++i < argc) {
364 char *ports = argv[i];
365 int untag = 0;
366 int member = 0;
367
368 while (*ports >= '0' && *ports <= '9') {
369 j = *ports++ - '0';
370 member |= 1 << j;
371
372 /* untag if needed, CPU port requires special handling */
373 if (*ports == 'u' || (j != 5 && (*ports == ' ' || *ports == 0)))
374 {
375 untag |= 1 << j;
376 if (*ports) ports++;
377 /* change default vlan tag */
378 robo_write16(&robo, ROBO_VLAN_PAGE,
379 ROBO_VLAN_PORT0_DEF_TAG + (j << 1), index);
380 } else
381 if (*ports == '*' || *ports == 't' || *ports == ' ') ports++;
382 else break;
383
384 while (*ports == ' ') ports++;
385 }
386
387 if (*ports) {
388 fprintf(stderr, "Invalid ports '%s'.\n", argv[i]);
389 exit(1);
390 } else {
391 /* write config now */
392 val16 = (index) /* vlan */ | (1 << 12) /* write */ | (1 << 13) /* enable */;
393 if (robo5350) {
394 robo_write32(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_WRITE_5350,
395 (1 << 20) /* valid */ | (untag << 6) | member);
396 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16);
397 } else {
398 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_WRITE,
399 (1 << 14) /* valid */ | (untag << 7) | member);
400 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS, val16);
401 }
402 }
403 } else break;
404 }
405 } else
406 if (strcasecmp(argv[i], "switch") == 0 && (i + 1) < argc)
407 {
408 /* enable/disable switching */
409 robo_write16(&robo, ROBO_CTRL_PAGE, ROBO_SWITCH_MODE,
410 (robo_read16(&robo, ROBO_CTRL_PAGE, ROBO_SWITCH_MODE) & ~2) |
411 (*argv[++i] == 'e' ? 2 : 0));
412 i++;
413 } else
414 if (strcasecmp(argv[i], "vlans") == 0 && (i + 1) < argc)
415 {
416 while (++i < argc) {
417 if (strcasecmp(argv[i], "reset") == 0) {
418 /* reset vlan validity bit */
419 for (j = 0; j <= (robo5350 ? VLAN_ID_MAX5350 : VLAN_ID_MAX); j++)
420 {
421 /* write config now */
422 val16 = (j) /* vlan */ | (1 << 12) /* write */ | (1 << 13) /* enable */;
423 if (robo5350) {
424 robo_write32(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_WRITE_5350, 0);
425 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16);
426 } else {
427 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_WRITE, 0);
428 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS, val16);
429 }
430 }
431 } else
432 if (strcasecmp(argv[i], "enable") == 0 || strcasecmp(argv[i], "disable") == 0)
433 {
434 int disable = (*argv[i] == 'd') || (*argv[i] == 'D');
435 /* enable/disable vlans */
436 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_CTRL0, disable ? 0 :
437 (1 << 7) /* 802.1Q VLAN */ | (3 << 5) /* mac check and hash */);
438
439 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_CTRL1, disable ? 0 :
440 (1 << 1) | (1 << 2) | (1 << 3) /* RSV multicast */);
441
442 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_CTRL4, disable ? 0 :
443 (1 << 6) /* drop invalid VID frames */);
444
445 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_CTRL5, disable ? 0 :
446 (1 << 3) /* drop miss V table frames */);
447
448 } else break;
449 }
450 } else
451 if (strcasecmp(argv[i], "show") == 0)
452 {
453 break;
454 } else {
455 fprintf(stderr, "Invalid option %s\n", argv[i]);
456 usage();
457 exit(1);
458 }
459 }
460
461 if (i == argc) {
462 if (argc == 1) usage();
463 return 0;
464 }
465
466 /* show config */
467
468 printf("Switch: %sabled\n", robo_read16(&robo, ROBO_CTRL_PAGE, ROBO_SWITCH_MODE) & 2 ? "en" : "dis");
469
470 for (i = 0; i < 6; i++) {
471 printf(robo_read16(&robo, ROBO_STAT_PAGE, ROBO_LINK_STAT_SUMMARY) & (1 << port[i]) ?
472 "Port %d(%c): %s%s " : "Port %d(%c): DOWN ", i, ports[i],
473 robo_read16(&robo, ROBO_STAT_PAGE, ROBO_SPEED_STAT_SUMMARY) & (1 << port[i]) ? "100" : " 10",
474 robo_read16(&robo, ROBO_STAT_PAGE, ROBO_DUPLEX_STAT_SUMMARY) & (1 << port[i]) ? "FD" : "HD");
475
476 val16 = robo_read16(&robo, ROBO_CTRL_PAGE, port[i]);
477
478 printf("%s stp: %s vlan: %d ", rxtx[val16 & 3], stp[(val16 >> 5) & 7],
479 robo_read16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_PORT0_DEF_TAG + (i << 1)));
480
481 robo_read(&robo, ROBO_STAT_PAGE, ROBO_LSA_PORT0 + port[i] * 6, mac, 3);
482
483 printf("mac: %02x:%02x:%02x:%02x:%02x:%02x\n",
484 mac[2] >> 8, mac[2] & 255, mac[1] >> 8, mac[1] & 255, mac[0] >> 8, mac[0] & 255);
485 }
486
487 val16 = robo_read16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_CTRL0);
488
489 printf("VLANs: %s %sabled%s%s\n",
490 robo5350 ? "BCM5325/535x" : "BCM536x",
491 (val16 & (1 << 7)) ? "en" : "dis",
492 (val16 & (1 << 6)) ? " mac_check" : "",
493 (val16 & (1 << 5)) ? " mac_hash" : "");
494
495 /* scan VLANs */
496 for (i = 0; i <= (robo5350 ? VLAN_ID_MAX5350 : VLAN_ID_MAX); i++) {
497 /* issue read */
498 val16 = (i) /* vlan */ | (0 << 12) /* read */ | (1 << 13) /* enable */;
499
500 if (robo5350) {
501 u32 val32;
502 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16);
503 /* actual read */
504 val32 = robo_read32(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_READ);
505 if ((val32 & (1 << 20)) /* valid */) {
506 printf("vlan%d:", i);
507 for (j = 0; j < 6; j++) {
508 if (val32 & (1 << j)) {
509 printf(" %d%s", j, (val32 & (1 << (j + 6))) ?
510 (j == 5 ? "u" : "") : "t");
511 }
512 }
513 printf("\n");
514 }
515 } else {
516 robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS, val16);
517 /* actual read */
518 val16 = robo_read16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_READ);
519 if ((val16 & (1 << 14)) /* valid */) {
520 printf("vlan%d:", i);
521 for (j = 0; j < 6; j++) {
522 if (val16 & (1 << j)) {
523 printf(" %d%s", j, (val16 & (1 << (j + 7))) ?
524 (j == 5 ? "u" : "") : "t");
525 }
526 }
527 printf("\n");
528 }
529 }
530 }
531
532 return (0);
533 }