Standardize package Config.in, change prompt to package name + short description...
[openwrt.git] / 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 }