2 * WPA Supplicant - roboswitch driver interface
3 * Copyright (c) 2008-2012 Jouke Witteveen
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
10 #include <sys/ioctl.h>
11 #include <linux/sockios.h>
12 #include <linux/if_ether.h>
13 #include <linux/mii.h>
18 #include "l2_packet/l2_packet.h"
20 #define ROBO_PHY_ADDR 0x1e /* RoboSwitch PHY address */
22 /* MII access registers */
23 #define ROBO_MII_PAGE 0x10 /* MII page register */
24 #define ROBO_MII_ADDR 0x11 /* MII address register */
25 #define ROBO_MII_DATA_OFFSET 0x18 /* Start of MII data registers */
27 #define ROBO_MII_PAGE_ENABLE 0x01 /* MII page op code */
28 #define ROBO_MII_ADDR_WRITE 0x01 /* MII address write op code */
29 #define ROBO_MII_ADDR_READ 0x02 /* MII address read op code */
30 #define ROBO_MII_DATA_MAX 4 /* Consecutive MII data registers */
31 #define ROBO_MII_RETRY_MAX 10 /* Read attempts before giving up */
34 #define ROBO_ARLCTRL_PAGE 0x04 /* ARL control page */
35 #define ROBO_VLAN_PAGE 0x34 /* VLAN page */
37 /* ARL control page registers */
38 #define ROBO_ARLCTRL_CONF 0x00 /* ARL configuration register */
39 #define ROBO_ARLCTRL_ADDR_1 0x10 /* Multiport address 1 */
40 #define ROBO_ARLCTRL_VEC_1 0x16 /* Multiport vector 1 */
41 #define ROBO_ARLCTRL_ADDR_2 0x20 /* Multiport address 2 */
42 #define ROBO_ARLCTRL_VEC_2 0x26 /* Multiport vector 2 */
44 /* VLAN page registers */
45 #define ROBO_VLAN_ACCESS 0x08 /* VLAN table access register */
46 #define ROBO_VLAN_ACCESS_5350 0x06 /* VLAN table access register (5350) */
47 #define ROBO_VLAN_READ 0x0c /* VLAN read register */
48 #define ROBO_VLAN_MAX 0xff /* Maximum number of VLANs */
51 static const u8 pae_group_addr
[ETH_ALEN
] =
52 { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
55 struct wpa_driver_roboswitch_data
{
57 struct l2_packet_data
*l2
;
58 char ifname
[IFNAMSIZ
+ 1];
59 u8 own_addr
[ETH_ALEN
];
66 /* Copied from the kernel-only part of mii.h. */
67 static inline struct mii_ioctl_data
*if_mii(struct ifreq
*rq
)
69 return (struct mii_ioctl_data
*) &rq
->ifr_ifru
;
74 * RoboSwitch uses 16-bit Big Endian addresses.
75 * The ordering of the words is reversed in the MII registers.
77 static void wpa_driver_roboswitch_addr_be16(const u8 addr
[ETH_ALEN
], u16
*be
)
80 for (i
= 0; i
< ETH_ALEN
; i
+= 2)
81 be
[(ETH_ALEN
- i
) / 2 - 1] = WPA_GET_BE16(addr
+ i
);
85 static u16
wpa_driver_roboswitch_mdio_read(
86 struct wpa_driver_roboswitch_data
*drv
, u8 reg
)
88 struct mii_ioctl_data
*mii
= if_mii(&drv
->ifr
);
90 mii
->phy_id
= ROBO_PHY_ADDR
;
93 if (ioctl(drv
->fd
, SIOCGMIIREG
, &drv
->ifr
) < 0) {
94 wpa_printf(MSG_ERROR
, "ioctl[SIOCGMIIREG]: %s",
102 static void wpa_driver_roboswitch_mdio_write(
103 struct wpa_driver_roboswitch_data
*drv
, u8 reg
, u16 val
)
105 struct mii_ioctl_data
*mii
= if_mii(&drv
->ifr
);
107 mii
->phy_id
= ROBO_PHY_ADDR
;
111 if (ioctl(drv
->fd
, SIOCSMIIREG
, &drv
->ifr
) < 0) {
112 wpa_printf(MSG_ERROR
, "ioctl[SIOCSMIIREG]: %s",
118 static int wpa_driver_roboswitch_reg(struct wpa_driver_roboswitch_data
*drv
,
119 u8 page
, u8 reg
, u8 op
)
123 /* set page number */
124 wpa_driver_roboswitch_mdio_write(drv
, ROBO_MII_PAGE
,
125 (page
<< 8) | ROBO_MII_PAGE_ENABLE
);
126 /* set register address */
127 wpa_driver_roboswitch_mdio_write(drv
, ROBO_MII_ADDR
, (reg
<< 8) | op
);
129 /* check if operation completed */
130 for (i
= 0; i
< ROBO_MII_RETRY_MAX
; ++i
) {
131 if ((wpa_driver_roboswitch_mdio_read(drv
, ROBO_MII_ADDR
) & 3)
140 static int wpa_driver_roboswitch_read(struct wpa_driver_roboswitch_data
*drv
,
141 u8 page
, u8 reg
, u16
*val
, int len
)
145 if (len
> ROBO_MII_DATA_MAX
||
146 wpa_driver_roboswitch_reg(drv
, page
, reg
, ROBO_MII_ADDR_READ
) < 0)
149 for (i
= 0; i
< len
; ++i
) {
150 val
[i
] = wpa_driver_roboswitch_mdio_read(
151 drv
, ROBO_MII_DATA_OFFSET
+ i
);
158 static int wpa_driver_roboswitch_write(struct wpa_driver_roboswitch_data
*drv
,
159 u8 page
, u8 reg
, u16
*val
, int len
)
163 if (len
> ROBO_MII_DATA_MAX
) return -1;
164 for (i
= 0; i
< len
; ++i
) {
165 wpa_driver_roboswitch_mdio_write(drv
, ROBO_MII_DATA_OFFSET
+ i
,
168 return wpa_driver_roboswitch_reg(drv
, page
, reg
, ROBO_MII_ADDR_WRITE
);
172 static void wpa_driver_roboswitch_receive(void *priv
, const u8
*src_addr
,
173 const u8
*buf
, size_t len
)
175 struct wpa_driver_roboswitch_data
*drv
= priv
;
177 if (len
> 14 && WPA_GET_BE16(buf
+ 12) == ETH_P_EAPOL
&&
178 os_memcmp(buf
, drv
->own_addr
, ETH_ALEN
) == 0)
179 drv_event_eapol_rx(drv
->ctx
, src_addr
, buf
+ 14, len
- 14);
183 static int wpa_driver_roboswitch_get_ssid(void *priv
, u8
*ssid
)
190 static int wpa_driver_roboswitch_get_bssid(void *priv
, u8
*bssid
)
192 /* Report PAE group address as the "BSSID" for wired connection. */
193 os_memcpy(bssid
, pae_group_addr
, ETH_ALEN
);
198 static int wpa_driver_roboswitch_get_capa(void *priv
,
199 struct wpa_driver_capa
*capa
)
201 os_memset(capa
, 0, sizeof(*capa
));
202 capa
->flags
= WPA_DRIVER_FLAGS_WIRED
;
207 static int wpa_driver_roboswitch_set_param(void *priv
, const char *param
)
209 struct wpa_driver_roboswitch_data
*drv
= priv
;
212 if (param
== NULL
|| os_strstr(param
, "multicast_only=1") == NULL
) {
213 sep
= drv
->ifname
+ os_strlen(drv
->ifname
);
215 drv
->l2
= l2_packet_init(drv
->ifname
, NULL
, ETH_P_ALL
,
216 wpa_driver_roboswitch_receive
, drv
,
218 if (drv
->l2
== NULL
) {
219 wpa_printf(MSG_INFO
, "%s: Unable to listen on %s",
220 __func__
, drv
->ifname
);
224 l2_packet_get_own_addr(drv
->l2
, drv
->own_addr
);
226 wpa_printf(MSG_DEBUG
, "%s: Ignoring unicast frames", __func__
);
233 static const char * wpa_driver_roboswitch_get_ifname(void *priv
)
235 struct wpa_driver_roboswitch_data
*drv
= priv
;
240 static int wpa_driver_roboswitch_join(struct wpa_driver_roboswitch_data
*drv
,
241 u16 ports
, const u8
*addr
)
243 u16 read1
[3], read2
[3], addr_be16
[3];
245 wpa_driver_roboswitch_addr_be16(addr
, addr_be16
);
247 if (wpa_driver_roboswitch_read(drv
, ROBO_ARLCTRL_PAGE
,
248 ROBO_ARLCTRL_CONF
, read1
, 1) < 0)
250 if (!(read1
[0] & (1 << 4))) {
251 /* multiport addresses are not yet enabled */
253 wpa_driver_roboswitch_write(drv
, ROBO_ARLCTRL_PAGE
,
254 ROBO_ARLCTRL_ADDR_1
, addr_be16
, 3);
255 wpa_driver_roboswitch_write(drv
, ROBO_ARLCTRL_PAGE
,
256 ROBO_ARLCTRL_VEC_1
, &ports
, 1);
257 wpa_driver_roboswitch_write(drv
, ROBO_ARLCTRL_PAGE
,
258 ROBO_ARLCTRL_ADDR_2
, addr_be16
, 3);
259 wpa_driver_roboswitch_write(drv
, ROBO_ARLCTRL_PAGE
,
260 ROBO_ARLCTRL_VEC_2
, &ports
, 1);
261 wpa_driver_roboswitch_write(drv
, ROBO_ARLCTRL_PAGE
,
262 ROBO_ARLCTRL_CONF
, read1
, 1);
264 /* if both multiport addresses are the same we can add */
265 if (wpa_driver_roboswitch_read(drv
, ROBO_ARLCTRL_PAGE
,
266 ROBO_ARLCTRL_ADDR_1
, read1
, 3) ||
267 wpa_driver_roboswitch_read(drv
, ROBO_ARLCTRL_PAGE
,
268 ROBO_ARLCTRL_ADDR_2
, read2
, 3) ||
269 os_memcmp(read1
, read2
, 6) != 0)
271 if (wpa_driver_roboswitch_read(drv
, ROBO_ARLCTRL_PAGE
,
272 ROBO_ARLCTRL_VEC_1
, read1
, 1) ||
273 wpa_driver_roboswitch_read(drv
, ROBO_ARLCTRL_PAGE
,
274 ROBO_ARLCTRL_VEC_2
, read2
, 1) ||
275 read1
[0] != read2
[0])
277 wpa_driver_roboswitch_write(drv
, ROBO_ARLCTRL_PAGE
,
278 ROBO_ARLCTRL_ADDR_1
, addr_be16
, 3);
279 wpa_driver_roboswitch_write(drv
, ROBO_ARLCTRL_PAGE
,
280 ROBO_ARLCTRL_VEC_1
, &ports
, 1);
286 static int wpa_driver_roboswitch_leave(struct wpa_driver_roboswitch_data
*drv
,
287 u16 ports
, const u8
*addr
)
289 u16 _read
, addr_be16
[3], addr_read
[3], ports_read
;
291 wpa_driver_roboswitch_addr_be16(addr
, addr_be16
);
293 if (wpa_driver_roboswitch_read(drv
, ROBO_ARLCTRL_PAGE
,
294 ROBO_ARLCTRL_CONF
, &_read
, 1) < 0)
296 /* If ARL control is disabled, there is nothing to leave. */
297 if (!(_read
& (1 << 4))) return -1;
299 if (wpa_driver_roboswitch_read(drv
, ROBO_ARLCTRL_PAGE
,
300 ROBO_ARLCTRL_ADDR_1
, addr_read
, 3) < 0 ||
301 wpa_driver_roboswitch_read(drv
, ROBO_ARLCTRL_PAGE
,
302 ROBO_ARLCTRL_VEC_1
, &ports_read
, 1) < 0)
304 /* check if we occupy multiport address 1 */
305 if (os_memcmp(addr_read
, addr_be16
, 6) == 0 && ports_read
== ports
) {
306 if (wpa_driver_roboswitch_read(drv
, ROBO_ARLCTRL_PAGE
,
307 ROBO_ARLCTRL_ADDR_2
, addr_read
,
309 wpa_driver_roboswitch_read(drv
, ROBO_ARLCTRL_PAGE
,
310 ROBO_ARLCTRL_VEC_2
, &ports_read
,
313 /* and multiport address 2 */
314 if (os_memcmp(addr_read
, addr_be16
, 6) == 0 &&
315 ports_read
== ports
) {
317 wpa_driver_roboswitch_write(drv
, ROBO_ARLCTRL_PAGE
,
318 ROBO_ARLCTRL_CONF
, &_read
,
321 wpa_driver_roboswitch_read(drv
, ROBO_ARLCTRL_PAGE
,
324 wpa_driver_roboswitch_read(drv
, ROBO_ARLCTRL_PAGE
,
327 wpa_driver_roboswitch_write(drv
, ROBO_ARLCTRL_PAGE
,
330 wpa_driver_roboswitch_write(drv
, ROBO_ARLCTRL_PAGE
,
335 if (wpa_driver_roboswitch_read(drv
, ROBO_ARLCTRL_PAGE
,
336 ROBO_ARLCTRL_ADDR_2
, addr_read
,
338 wpa_driver_roboswitch_read(drv
, ROBO_ARLCTRL_PAGE
,
339 ROBO_ARLCTRL_VEC_2
, &ports_read
,
342 /* or multiport address 2 */
343 if (os_memcmp(addr_read
, addr_be16
, 6) == 0 &&
344 ports_read
== ports
) {
345 wpa_driver_roboswitch_write(drv
, ROBO_ARLCTRL_PAGE
,
348 wpa_driver_roboswitch_write(drv
, ROBO_ARLCTRL_PAGE
,
357 static void * wpa_driver_roboswitch_init(void *ctx
, const char *ifname
)
359 struct wpa_driver_roboswitch_data
*drv
;
361 u16 vlan
= 0, _read
[2];
363 drv
= os_zalloc(sizeof(*drv
));
364 if (drv
== NULL
) return NULL
;
366 drv
->own_addr
[0] = '\0';
368 /* copy ifname and take a pointer to the second to last character */
370 os_strlcpy(drv
->ifname
, ifname
, sizeof(drv
->ifname
)) - 2;
371 /* find the '.' separating <interface> and <vlan> */
372 while (sep
> drv
->ifname
&& *sep
!= '.') sep
--;
373 if (sep
<= drv
->ifname
) {
374 wpa_printf(MSG_INFO
, "%s: No <interface>.<vlan> pair in "
375 "interface name %s", __func__
, drv
->ifname
);
381 if (*sep
< '0' || *sep
> '9') {
382 wpa_printf(MSG_INFO
, "%s: Invalid vlan specification "
383 "in interface name %s", __func__
, ifname
);
389 if (vlan
> ROBO_VLAN_MAX
) {
390 wpa_printf(MSG_INFO
, "%s: VLAN out of range in "
391 "interface name %s", __func__
, ifname
);
397 drv
->fd
= socket(PF_INET
, SOCK_DGRAM
, 0);
399 wpa_printf(MSG_INFO
, "%s: Unable to create socket", __func__
);
404 os_memset(&drv
->ifr
, 0, sizeof(drv
->ifr
));
405 os_strlcpy(drv
->ifr
.ifr_name
, drv
->ifname
, IFNAMSIZ
);
406 if (ioctl(drv
->fd
, SIOCGMIIPHY
, &drv
->ifr
) < 0) {
407 wpa_printf(MSG_ERROR
, "ioctl[SIOCGMIIPHY]: %s",
412 /* BCM63xx devices provide 0 here */
413 if (if_mii(&drv
->ifr
)->phy_id
!= ROBO_PHY_ADDR
&&
414 if_mii(&drv
->ifr
)->phy_id
!= 0) {
415 wpa_printf(MSG_INFO
, "%s: Invalid phy address (not a "
416 "RoboSwitch?)", __func__
);
421 /* set and read back to see if the register can be used */
422 _read
[0] = ROBO_VLAN_MAX
;
423 wpa_driver_roboswitch_write(drv
, ROBO_VLAN_PAGE
, ROBO_VLAN_ACCESS_5350
,
425 wpa_driver_roboswitch_read(drv
, ROBO_VLAN_PAGE
, ROBO_VLAN_ACCESS_5350
,
427 drv
->is_5350
= _read
[0] == _read
[1];
429 /* set the read bit */
431 wpa_driver_roboswitch_write(drv
, ROBO_VLAN_PAGE
,
432 drv
->is_5350
? ROBO_VLAN_ACCESS_5350
435 wpa_driver_roboswitch_read(drv
, ROBO_VLAN_PAGE
, ROBO_VLAN_READ
, _read
,
436 drv
->is_5350
? 2 : 1);
437 if (!(drv
->is_5350
? _read
[1] & (1 << 4) : _read
[0] & (1 << 14))) {
438 wpa_printf(MSG_INFO
, "%s: Could not get port information for "
439 "VLAN %d", __func__
, vlan
& ~(1 << 13));
443 drv
->ports
= _read
[0] & 0x001F;
444 /* add the MII port */
445 drv
->ports
|= 1 << 8;
446 if (wpa_driver_roboswitch_join(drv
, drv
->ports
, pae_group_addr
) < 0) {
447 wpa_printf(MSG_INFO
, "%s: Unable to join PAE group", __func__
);
451 wpa_printf(MSG_DEBUG
, "%s: Added PAE group address to "
452 "RoboSwitch ARL", __func__
);
459 static void wpa_driver_roboswitch_deinit(void *priv
)
461 struct wpa_driver_roboswitch_data
*drv
= priv
;
464 l2_packet_deinit(drv
->l2
);
467 if (wpa_driver_roboswitch_leave(drv
, drv
->ports
, pae_group_addr
) < 0) {
468 wpa_printf(MSG_DEBUG
, "%s: Unable to leave PAE group",
477 const struct wpa_driver_ops wpa_driver_roboswitch_ops
= {
478 .name
= "roboswitch",
479 .desc
= "wpa_supplicant roboswitch driver",
480 .get_ssid
= wpa_driver_roboswitch_get_ssid
,
481 .get_bssid
= wpa_driver_roboswitch_get_bssid
,
482 .get_capa
= wpa_driver_roboswitch_get_capa
,
483 .init
= wpa_driver_roboswitch_init
,
484 .deinit
= wpa_driver_roboswitch_deinit
,
485 .set_param
= wpa_driver_roboswitch_set_param
,
486 .get_ifname
= wpa_driver_roboswitch_get_ifname
,