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