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