1c1d255d3SCy Schubert /* 2c1d255d3SCy Schubert * WPA Supplicant - roboswitch driver interface 3c1d255d3SCy Schubert * Copyright (c) 2008-2012 Jouke Witteveen 4c1d255d3SCy Schubert * 5c1d255d3SCy Schubert * This software may be distributed under the terms of the BSD license. 6c1d255d3SCy Schubert * See README for more details. 7c1d255d3SCy Schubert */ 8c1d255d3SCy Schubert 9c1d255d3SCy Schubert #include "includes.h" 10c1d255d3SCy Schubert #include <sys/ioctl.h> 11c1d255d3SCy Schubert #include <linux/sockios.h> 12c1d255d3SCy Schubert #include <linux/if_ether.h> 13c1d255d3SCy Schubert #include <linux/mii.h> 14c1d255d3SCy Schubert #include <net/if.h> 15c1d255d3SCy Schubert 16c1d255d3SCy Schubert #include "common.h" 17c1d255d3SCy Schubert #include "driver.h" 18c1d255d3SCy Schubert #include "l2_packet/l2_packet.h" 19c1d255d3SCy Schubert 20c1d255d3SCy Schubert #define ROBO_PHY_ADDR 0x1e /* RoboSwitch PHY address */ 21c1d255d3SCy Schubert 22c1d255d3SCy Schubert /* MII access registers */ 23c1d255d3SCy Schubert #define ROBO_MII_PAGE 0x10 /* MII page register */ 24c1d255d3SCy Schubert #define ROBO_MII_ADDR 0x11 /* MII address register */ 25c1d255d3SCy Schubert #define ROBO_MII_DATA_OFFSET 0x18 /* Start of MII data registers */ 26c1d255d3SCy Schubert 27c1d255d3SCy Schubert #define ROBO_MII_PAGE_ENABLE 0x01 /* MII page op code */ 28c1d255d3SCy Schubert #define ROBO_MII_ADDR_WRITE 0x01 /* MII address write op code */ 29c1d255d3SCy Schubert #define ROBO_MII_ADDR_READ 0x02 /* MII address read op code */ 30c1d255d3SCy Schubert #define ROBO_MII_DATA_MAX 4 /* Consecutive MII data registers */ 31c1d255d3SCy Schubert #define ROBO_MII_RETRY_MAX 10 /* Read attempts before giving up */ 32c1d255d3SCy Schubert 33c1d255d3SCy Schubert /* Page numbers */ 34c1d255d3SCy Schubert #define ROBO_ARLCTRL_PAGE 0x04 /* ARL control page */ 35c1d255d3SCy Schubert #define ROBO_VLAN_PAGE 0x34 /* VLAN page */ 36c1d255d3SCy Schubert 37c1d255d3SCy Schubert /* ARL control page registers */ 38c1d255d3SCy Schubert #define ROBO_ARLCTRL_CONF 0x00 /* ARL configuration register */ 39c1d255d3SCy Schubert #define ROBO_ARLCTRL_ADDR_1 0x10 /* Multiport address 1 */ 40c1d255d3SCy Schubert #define ROBO_ARLCTRL_VEC_1 0x16 /* Multiport vector 1 */ 41c1d255d3SCy Schubert #define ROBO_ARLCTRL_ADDR_2 0x20 /* Multiport address 2 */ 42c1d255d3SCy Schubert #define ROBO_ARLCTRL_VEC_2 0x26 /* Multiport vector 2 */ 43c1d255d3SCy Schubert 44c1d255d3SCy Schubert /* VLAN page registers */ 45c1d255d3SCy Schubert #define ROBO_VLAN_ACCESS 0x08 /* VLAN table access register */ 46c1d255d3SCy Schubert #define ROBO_VLAN_ACCESS_5350 0x06 /* VLAN table access register (5350) */ 47c1d255d3SCy Schubert #define ROBO_VLAN_READ 0x0c /* VLAN read register */ 48c1d255d3SCy Schubert #define ROBO_VLAN_MAX 0xff /* Maximum number of VLANs */ 49c1d255d3SCy Schubert 50c1d255d3SCy Schubert 51c1d255d3SCy Schubert static const u8 pae_group_addr[ETH_ALEN] = 52c1d255d3SCy Schubert { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; 53c1d255d3SCy Schubert 54c1d255d3SCy Schubert 55c1d255d3SCy Schubert struct wpa_driver_roboswitch_data { 56c1d255d3SCy Schubert void *ctx; 57c1d255d3SCy Schubert struct l2_packet_data *l2; 58c1d255d3SCy Schubert char ifname[IFNAMSIZ + 1]; 59c1d255d3SCy Schubert u8 own_addr[ETH_ALEN]; 60c1d255d3SCy Schubert struct ifreq ifr; 61c1d255d3SCy Schubert int fd, is_5350; 62c1d255d3SCy Schubert u16 ports; 63c1d255d3SCy Schubert }; 64c1d255d3SCy Schubert 65c1d255d3SCy Schubert 66c1d255d3SCy Schubert /* Copied from the kernel-only part of mii.h. */ 67c1d255d3SCy Schubert static inline struct mii_ioctl_data *if_mii(struct ifreq *rq) 68c1d255d3SCy Schubert { 69c1d255d3SCy Schubert return (struct mii_ioctl_data *) &rq->ifr_ifru; 70c1d255d3SCy Schubert } 71c1d255d3SCy Schubert 72c1d255d3SCy Schubert 73c1d255d3SCy Schubert /* 74c1d255d3SCy Schubert * RoboSwitch uses 16-bit Big Endian addresses. 75c1d255d3SCy Schubert * The ordering of the words is reversed in the MII registers. 76c1d255d3SCy Schubert */ 77c1d255d3SCy Schubert static void wpa_driver_roboswitch_addr_be16(const u8 addr[ETH_ALEN], u16 *be) 78c1d255d3SCy Schubert { 79c1d255d3SCy Schubert int i; 80c1d255d3SCy Schubert for (i = 0; i < ETH_ALEN; i += 2) 81c1d255d3SCy Schubert be[(ETH_ALEN - i) / 2 - 1] = WPA_GET_BE16(addr + i); 82c1d255d3SCy Schubert } 83c1d255d3SCy Schubert 84c1d255d3SCy Schubert 85c1d255d3SCy Schubert static u16 wpa_driver_roboswitch_mdio_read( 86c1d255d3SCy Schubert struct wpa_driver_roboswitch_data *drv, u8 reg) 87c1d255d3SCy Schubert { 88c1d255d3SCy Schubert struct mii_ioctl_data *mii = if_mii(&drv->ifr); 89c1d255d3SCy Schubert 90c1d255d3SCy Schubert mii->phy_id = ROBO_PHY_ADDR; 91c1d255d3SCy Schubert mii->reg_num = reg; 92c1d255d3SCy Schubert 93c1d255d3SCy Schubert if (ioctl(drv->fd, SIOCGMIIREG, &drv->ifr) < 0) { 94c1d255d3SCy Schubert wpa_printf(MSG_ERROR, "ioctl[SIOCGMIIREG]: %s", 95c1d255d3SCy Schubert strerror(errno)); 96c1d255d3SCy Schubert return 0x00; 97c1d255d3SCy Schubert } 98c1d255d3SCy Schubert return mii->val_out; 99c1d255d3SCy Schubert } 100c1d255d3SCy Schubert 101c1d255d3SCy Schubert 102c1d255d3SCy Schubert static void wpa_driver_roboswitch_mdio_write( 103c1d255d3SCy Schubert struct wpa_driver_roboswitch_data *drv, u8 reg, u16 val) 104c1d255d3SCy Schubert { 105c1d255d3SCy Schubert struct mii_ioctl_data *mii = if_mii(&drv->ifr); 106c1d255d3SCy Schubert 107c1d255d3SCy Schubert mii->phy_id = ROBO_PHY_ADDR; 108c1d255d3SCy Schubert mii->reg_num = reg; 109c1d255d3SCy Schubert mii->val_in = val; 110c1d255d3SCy Schubert 111c1d255d3SCy Schubert if (ioctl(drv->fd, SIOCSMIIREG, &drv->ifr) < 0) { 112c1d255d3SCy Schubert wpa_printf(MSG_ERROR, "ioctl[SIOCSMIIREG]: %s", 113c1d255d3SCy Schubert strerror(errno)); 114c1d255d3SCy Schubert } 115c1d255d3SCy Schubert } 116c1d255d3SCy Schubert 117c1d255d3SCy Schubert 118c1d255d3SCy Schubert static int wpa_driver_roboswitch_reg(struct wpa_driver_roboswitch_data *drv, 119c1d255d3SCy Schubert u8 page, u8 reg, u8 op) 120c1d255d3SCy Schubert { 121c1d255d3SCy Schubert int i; 122c1d255d3SCy Schubert 123c1d255d3SCy Schubert /* set page number */ 124c1d255d3SCy Schubert wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_PAGE, 125c1d255d3SCy Schubert (page << 8) | ROBO_MII_PAGE_ENABLE); 126c1d255d3SCy Schubert /* set register address */ 127c1d255d3SCy Schubert wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_ADDR, (reg << 8) | op); 128c1d255d3SCy Schubert 129c1d255d3SCy Schubert /* check if operation completed */ 130c1d255d3SCy Schubert for (i = 0; i < ROBO_MII_RETRY_MAX; ++i) { 131c1d255d3SCy Schubert if ((wpa_driver_roboswitch_mdio_read(drv, ROBO_MII_ADDR) & 3) 132c1d255d3SCy Schubert == 0) 133c1d255d3SCy Schubert return 0; 134c1d255d3SCy Schubert } 135c1d255d3SCy Schubert /* timeout */ 136c1d255d3SCy Schubert return -1; 137c1d255d3SCy Schubert } 138c1d255d3SCy Schubert 139c1d255d3SCy Schubert 140c1d255d3SCy Schubert static int wpa_driver_roboswitch_read(struct wpa_driver_roboswitch_data *drv, 141c1d255d3SCy Schubert u8 page, u8 reg, u16 *val, int len) 142c1d255d3SCy Schubert { 143c1d255d3SCy Schubert int i; 144c1d255d3SCy Schubert 145c1d255d3SCy Schubert if (len > ROBO_MII_DATA_MAX || 146c1d255d3SCy Schubert wpa_driver_roboswitch_reg(drv, page, reg, ROBO_MII_ADDR_READ) < 0) 147c1d255d3SCy Schubert return -1; 148c1d255d3SCy Schubert 149c1d255d3SCy Schubert for (i = 0; i < len; ++i) { 150c1d255d3SCy Schubert val[i] = wpa_driver_roboswitch_mdio_read( 151c1d255d3SCy Schubert drv, ROBO_MII_DATA_OFFSET + i); 152c1d255d3SCy Schubert } 153c1d255d3SCy Schubert 154c1d255d3SCy Schubert return 0; 155c1d255d3SCy Schubert } 156c1d255d3SCy Schubert 157c1d255d3SCy Schubert 158c1d255d3SCy Schubert static int wpa_driver_roboswitch_write(struct wpa_driver_roboswitch_data *drv, 159c1d255d3SCy Schubert u8 page, u8 reg, u16 *val, int len) 160c1d255d3SCy Schubert { 161c1d255d3SCy Schubert int i; 162c1d255d3SCy Schubert 163c1d255d3SCy Schubert if (len > ROBO_MII_DATA_MAX) return -1; 164c1d255d3SCy Schubert for (i = 0; i < len; ++i) { 165c1d255d3SCy Schubert wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_DATA_OFFSET + i, 166c1d255d3SCy Schubert val[i]); 167c1d255d3SCy Schubert } 168c1d255d3SCy Schubert return wpa_driver_roboswitch_reg(drv, page, reg, ROBO_MII_ADDR_WRITE); 169c1d255d3SCy Schubert } 170c1d255d3SCy Schubert 171c1d255d3SCy Schubert 172c1d255d3SCy Schubert static void wpa_driver_roboswitch_receive(void *priv, const u8 *src_addr, 173c1d255d3SCy Schubert const u8 *buf, size_t len) 174c1d255d3SCy Schubert { 175c1d255d3SCy Schubert struct wpa_driver_roboswitch_data *drv = priv; 176c1d255d3SCy Schubert 177c1d255d3SCy Schubert if (len > 14 && WPA_GET_BE16(buf + 12) == ETH_P_EAPOL && 178*a90b9d01SCy Schubert ether_addr_equal(buf, drv->own_addr)) 179c1d255d3SCy Schubert drv_event_eapol_rx(drv->ctx, src_addr, buf + 14, len - 14); 180c1d255d3SCy Schubert } 181c1d255d3SCy Schubert 182c1d255d3SCy Schubert 183c1d255d3SCy Schubert static int wpa_driver_roboswitch_get_ssid(void *priv, u8 *ssid) 184c1d255d3SCy Schubert { 185c1d255d3SCy Schubert ssid[0] = 0; 186c1d255d3SCy Schubert return 0; 187c1d255d3SCy Schubert } 188c1d255d3SCy Schubert 189c1d255d3SCy Schubert 190c1d255d3SCy Schubert static int wpa_driver_roboswitch_get_bssid(void *priv, u8 *bssid) 191c1d255d3SCy Schubert { 192c1d255d3SCy Schubert /* Report PAE group address as the "BSSID" for wired connection. */ 193c1d255d3SCy Schubert os_memcpy(bssid, pae_group_addr, ETH_ALEN); 194c1d255d3SCy Schubert return 0; 195c1d255d3SCy Schubert } 196c1d255d3SCy Schubert 197c1d255d3SCy Schubert 198c1d255d3SCy Schubert static int wpa_driver_roboswitch_get_capa(void *priv, 199c1d255d3SCy Schubert struct wpa_driver_capa *capa) 200c1d255d3SCy Schubert { 201c1d255d3SCy Schubert os_memset(capa, 0, sizeof(*capa)); 202c1d255d3SCy Schubert capa->flags = WPA_DRIVER_FLAGS_WIRED; 203c1d255d3SCy Schubert return 0; 204c1d255d3SCy Schubert } 205c1d255d3SCy Schubert 206c1d255d3SCy Schubert 207c1d255d3SCy Schubert static int wpa_driver_roboswitch_set_param(void *priv, const char *param) 208c1d255d3SCy Schubert { 209c1d255d3SCy Schubert struct wpa_driver_roboswitch_data *drv = priv; 210c1d255d3SCy Schubert char *sep; 211c1d255d3SCy Schubert 212c1d255d3SCy Schubert if (param == NULL || os_strstr(param, "multicast_only=1") == NULL) { 213c1d255d3SCy Schubert sep = drv->ifname + os_strlen(drv->ifname); 214c1d255d3SCy Schubert *sep = '.'; 215c1d255d3SCy Schubert drv->l2 = l2_packet_init(drv->ifname, NULL, ETH_P_ALL, 216c1d255d3SCy Schubert wpa_driver_roboswitch_receive, drv, 217c1d255d3SCy Schubert 1); 218c1d255d3SCy Schubert if (drv->l2 == NULL) { 219c1d255d3SCy Schubert wpa_printf(MSG_INFO, "%s: Unable to listen on %s", 220c1d255d3SCy Schubert __func__, drv->ifname); 221c1d255d3SCy Schubert return -1; 222c1d255d3SCy Schubert } 223c1d255d3SCy Schubert *sep = '\0'; 224c1d255d3SCy Schubert l2_packet_get_own_addr(drv->l2, drv->own_addr); 225c1d255d3SCy Schubert } else { 226c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "%s: Ignoring unicast frames", __func__); 227c1d255d3SCy Schubert drv->l2 = NULL; 228c1d255d3SCy Schubert } 229c1d255d3SCy Schubert return 0; 230c1d255d3SCy Schubert } 231c1d255d3SCy Schubert 232c1d255d3SCy Schubert 233c1d255d3SCy Schubert static const char * wpa_driver_roboswitch_get_ifname(void *priv) 234c1d255d3SCy Schubert { 235c1d255d3SCy Schubert struct wpa_driver_roboswitch_data *drv = priv; 236c1d255d3SCy Schubert return drv->ifname; 237c1d255d3SCy Schubert } 238c1d255d3SCy Schubert 239c1d255d3SCy Schubert 240c1d255d3SCy Schubert static int wpa_driver_roboswitch_join(struct wpa_driver_roboswitch_data *drv, 241c1d255d3SCy Schubert u16 ports, const u8 *addr) 242c1d255d3SCy Schubert { 243c1d255d3SCy Schubert u16 read1[3], read2[3], addr_be16[3]; 244c1d255d3SCy Schubert 245c1d255d3SCy Schubert wpa_driver_roboswitch_addr_be16(addr, addr_be16); 246c1d255d3SCy Schubert 247c1d255d3SCy Schubert if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 248c1d255d3SCy Schubert ROBO_ARLCTRL_CONF, read1, 1) < 0) 249c1d255d3SCy Schubert return -1; 250c1d255d3SCy Schubert if (!(read1[0] & (1 << 4))) { 251c1d255d3SCy Schubert /* multiport addresses are not yet enabled */ 252c1d255d3SCy Schubert read1[0] |= 1 << 4; 253c1d255d3SCy Schubert wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, 254c1d255d3SCy Schubert ROBO_ARLCTRL_ADDR_1, addr_be16, 3); 255c1d255d3SCy Schubert wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, 256c1d255d3SCy Schubert ROBO_ARLCTRL_VEC_1, &ports, 1); 257c1d255d3SCy Schubert wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, 258c1d255d3SCy Schubert ROBO_ARLCTRL_ADDR_2, addr_be16, 3); 259c1d255d3SCy Schubert wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, 260c1d255d3SCy Schubert ROBO_ARLCTRL_VEC_2, &ports, 1); 261c1d255d3SCy Schubert wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, 262c1d255d3SCy Schubert ROBO_ARLCTRL_CONF, read1, 1); 263c1d255d3SCy Schubert } else { 264c1d255d3SCy Schubert /* if both multiport addresses are the same we can add */ 265c1d255d3SCy Schubert if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 266c1d255d3SCy Schubert ROBO_ARLCTRL_ADDR_1, read1, 3) || 267c1d255d3SCy Schubert wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 268c1d255d3SCy Schubert ROBO_ARLCTRL_ADDR_2, read2, 3) || 269c1d255d3SCy Schubert os_memcmp(read1, read2, 6) != 0) 270c1d255d3SCy Schubert return -1; 271c1d255d3SCy Schubert if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 272c1d255d3SCy Schubert ROBO_ARLCTRL_VEC_1, read1, 1) || 273c1d255d3SCy Schubert wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 274c1d255d3SCy Schubert ROBO_ARLCTRL_VEC_2, read2, 1) || 275c1d255d3SCy Schubert read1[0] != read2[0]) 276c1d255d3SCy Schubert return -1; 277c1d255d3SCy Schubert wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, 278c1d255d3SCy Schubert ROBO_ARLCTRL_ADDR_1, addr_be16, 3); 279c1d255d3SCy Schubert wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, 280c1d255d3SCy Schubert ROBO_ARLCTRL_VEC_1, &ports, 1); 281c1d255d3SCy Schubert } 282c1d255d3SCy Schubert return 0; 283c1d255d3SCy Schubert } 284c1d255d3SCy Schubert 285c1d255d3SCy Schubert 286c1d255d3SCy Schubert static int wpa_driver_roboswitch_leave(struct wpa_driver_roboswitch_data *drv, 287c1d255d3SCy Schubert u16 ports, const u8 *addr) 288c1d255d3SCy Schubert { 289c1d255d3SCy Schubert u16 _read, addr_be16[3], addr_read[3], ports_read; 290c1d255d3SCy Schubert 291c1d255d3SCy Schubert wpa_driver_roboswitch_addr_be16(addr, addr_be16); 292c1d255d3SCy Schubert 293c1d255d3SCy Schubert if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 294c1d255d3SCy Schubert ROBO_ARLCTRL_CONF, &_read, 1) < 0) 295c1d255d3SCy Schubert return -1; 296c1d255d3SCy Schubert /* If ARL control is disabled, there is nothing to leave. */ 297c1d255d3SCy Schubert if (!(_read & (1 << 4))) return -1; 298c1d255d3SCy Schubert 299c1d255d3SCy Schubert if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 300c1d255d3SCy Schubert ROBO_ARLCTRL_ADDR_1, addr_read, 3) < 0 || 301c1d255d3SCy Schubert wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 302c1d255d3SCy Schubert ROBO_ARLCTRL_VEC_1, &ports_read, 1) < 0) 303c1d255d3SCy Schubert return -1; 304c1d255d3SCy Schubert /* check if we occupy multiport address 1 */ 305c1d255d3SCy Schubert if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) { 306c1d255d3SCy Schubert if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 307c1d255d3SCy Schubert ROBO_ARLCTRL_ADDR_2, addr_read, 308c1d255d3SCy Schubert 3) < 0 || 309c1d255d3SCy Schubert wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 310c1d255d3SCy Schubert ROBO_ARLCTRL_VEC_2, &ports_read, 311c1d255d3SCy Schubert 1) < 0) 312c1d255d3SCy Schubert return -1; 313c1d255d3SCy Schubert /* and multiport address 2 */ 314c1d255d3SCy Schubert if (os_memcmp(addr_read, addr_be16, 6) == 0 && 315c1d255d3SCy Schubert ports_read == ports) { 316c1d255d3SCy Schubert _read &= ~(1 << 4); 317c1d255d3SCy Schubert wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, 318c1d255d3SCy Schubert ROBO_ARLCTRL_CONF, &_read, 319c1d255d3SCy Schubert 1); 320c1d255d3SCy Schubert } else { 321c1d255d3SCy Schubert wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 322c1d255d3SCy Schubert ROBO_ARLCTRL_ADDR_1, 323c1d255d3SCy Schubert addr_read, 3); 324c1d255d3SCy Schubert wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 325c1d255d3SCy Schubert ROBO_ARLCTRL_VEC_1, 326c1d255d3SCy Schubert &ports_read, 1); 327c1d255d3SCy Schubert wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, 328c1d255d3SCy Schubert ROBO_ARLCTRL_ADDR_2, 329c1d255d3SCy Schubert addr_read, 3); 330c1d255d3SCy Schubert wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, 331c1d255d3SCy Schubert ROBO_ARLCTRL_VEC_2, 332c1d255d3SCy Schubert &ports_read, 1); 333c1d255d3SCy Schubert } 334c1d255d3SCy Schubert } else { 335c1d255d3SCy Schubert if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 336c1d255d3SCy Schubert ROBO_ARLCTRL_ADDR_2, addr_read, 337c1d255d3SCy Schubert 3) < 0 || 338c1d255d3SCy Schubert wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 339c1d255d3SCy Schubert ROBO_ARLCTRL_VEC_2, &ports_read, 340c1d255d3SCy Schubert 1) < 0) 341c1d255d3SCy Schubert return -1; 342c1d255d3SCy Schubert /* or multiport address 2 */ 343c1d255d3SCy Schubert if (os_memcmp(addr_read, addr_be16, 6) == 0 && 344c1d255d3SCy Schubert ports_read == ports) { 345c1d255d3SCy Schubert wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, 346c1d255d3SCy Schubert ROBO_ARLCTRL_ADDR_1, 347c1d255d3SCy Schubert addr_read, 3); 348c1d255d3SCy Schubert wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, 349c1d255d3SCy Schubert ROBO_ARLCTRL_VEC_1, 350c1d255d3SCy Schubert &ports_read, 1); 351c1d255d3SCy Schubert } else return -1; 352c1d255d3SCy Schubert } 353c1d255d3SCy Schubert return 0; 354c1d255d3SCy Schubert } 355c1d255d3SCy Schubert 356c1d255d3SCy Schubert 357c1d255d3SCy Schubert static void * wpa_driver_roboswitch_init(void *ctx, const char *ifname) 358c1d255d3SCy Schubert { 359c1d255d3SCy Schubert struct wpa_driver_roboswitch_data *drv; 360c1d255d3SCy Schubert char *sep; 361c1d255d3SCy Schubert u16 vlan = 0, _read[2]; 362c1d255d3SCy Schubert 363c1d255d3SCy Schubert drv = os_zalloc(sizeof(*drv)); 364c1d255d3SCy Schubert if (drv == NULL) return NULL; 365c1d255d3SCy Schubert drv->ctx = ctx; 366c1d255d3SCy Schubert drv->own_addr[0] = '\0'; 367c1d255d3SCy Schubert 368c1d255d3SCy Schubert /* copy ifname and take a pointer to the second to last character */ 369c1d255d3SCy Schubert sep = drv->ifname + 370c1d255d3SCy Schubert os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)) - 2; 371c1d255d3SCy Schubert /* find the '.' separating <interface> and <vlan> */ 372c1d255d3SCy Schubert while (sep > drv->ifname && *sep != '.') sep--; 373c1d255d3SCy Schubert if (sep <= drv->ifname) { 374c1d255d3SCy Schubert wpa_printf(MSG_INFO, "%s: No <interface>.<vlan> pair in " 375c1d255d3SCy Schubert "interface name %s", __func__, drv->ifname); 376c1d255d3SCy Schubert os_free(drv); 377c1d255d3SCy Schubert return NULL; 378c1d255d3SCy Schubert } 379c1d255d3SCy Schubert *sep = '\0'; 380c1d255d3SCy Schubert while (*++sep) { 381c1d255d3SCy Schubert if (*sep < '0' || *sep > '9') { 382c1d255d3SCy Schubert wpa_printf(MSG_INFO, "%s: Invalid vlan specification " 383c1d255d3SCy Schubert "in interface name %s", __func__, ifname); 384c1d255d3SCy Schubert os_free(drv); 385c1d255d3SCy Schubert return NULL; 386c1d255d3SCy Schubert } 387c1d255d3SCy Schubert vlan *= 10; 388c1d255d3SCy Schubert vlan += *sep - '0'; 389c1d255d3SCy Schubert if (vlan > ROBO_VLAN_MAX) { 390c1d255d3SCy Schubert wpa_printf(MSG_INFO, "%s: VLAN out of range in " 391c1d255d3SCy Schubert "interface name %s", __func__, ifname); 392c1d255d3SCy Schubert os_free(drv); 393c1d255d3SCy Schubert return NULL; 394c1d255d3SCy Schubert } 395c1d255d3SCy Schubert } 396c1d255d3SCy Schubert 397c1d255d3SCy Schubert drv->fd = socket(PF_INET, SOCK_DGRAM, 0); 398c1d255d3SCy Schubert if (drv->fd < 0) { 399c1d255d3SCy Schubert wpa_printf(MSG_INFO, "%s: Unable to create socket", __func__); 400c1d255d3SCy Schubert os_free(drv); 401c1d255d3SCy Schubert return NULL; 402c1d255d3SCy Schubert } 403c1d255d3SCy Schubert 404c1d255d3SCy Schubert os_memset(&drv->ifr, 0, sizeof(drv->ifr)); 405c1d255d3SCy Schubert os_strlcpy(drv->ifr.ifr_name, drv->ifname, IFNAMSIZ); 406c1d255d3SCy Schubert if (ioctl(drv->fd, SIOCGMIIPHY, &drv->ifr) < 0) { 407c1d255d3SCy Schubert wpa_printf(MSG_ERROR, "ioctl[SIOCGMIIPHY]: %s", 408c1d255d3SCy Schubert strerror(errno)); 409c1d255d3SCy Schubert os_free(drv); 410c1d255d3SCy Schubert return NULL; 411c1d255d3SCy Schubert } 412c1d255d3SCy Schubert /* BCM63xx devices provide 0 here */ 413c1d255d3SCy Schubert if (if_mii(&drv->ifr)->phy_id != ROBO_PHY_ADDR && 414c1d255d3SCy Schubert if_mii(&drv->ifr)->phy_id != 0) { 415c1d255d3SCy Schubert wpa_printf(MSG_INFO, "%s: Invalid phy address (not a " 416c1d255d3SCy Schubert "RoboSwitch?)", __func__); 417c1d255d3SCy Schubert os_free(drv); 418c1d255d3SCy Schubert return NULL; 419c1d255d3SCy Schubert } 420c1d255d3SCy Schubert 421c1d255d3SCy Schubert /* set and read back to see if the register can be used */ 422c1d255d3SCy Schubert _read[0] = ROBO_VLAN_MAX; 423c1d255d3SCy Schubert wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350, 424c1d255d3SCy Schubert _read, 1); 425c1d255d3SCy Schubert wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350, 426c1d255d3SCy Schubert _read + 1, 1); 427c1d255d3SCy Schubert drv->is_5350 = _read[0] == _read[1]; 428c1d255d3SCy Schubert 429c1d255d3SCy Schubert /* set the read bit */ 430c1d255d3SCy Schubert vlan |= 1 << 13; 431c1d255d3SCy Schubert wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, 432c1d255d3SCy Schubert drv->is_5350 ? ROBO_VLAN_ACCESS_5350 433c1d255d3SCy Schubert : ROBO_VLAN_ACCESS, 434c1d255d3SCy Schubert &vlan, 1); 435c1d255d3SCy Schubert wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_READ, _read, 436c1d255d3SCy Schubert drv->is_5350 ? 2 : 1); 437c1d255d3SCy Schubert if (!(drv->is_5350 ? _read[1] & (1 << 4) : _read[0] & (1 << 14))) { 438c1d255d3SCy Schubert wpa_printf(MSG_INFO, "%s: Could not get port information for " 439c1d255d3SCy Schubert "VLAN %d", __func__, vlan & ~(1 << 13)); 440c1d255d3SCy Schubert os_free(drv); 441c1d255d3SCy Schubert return NULL; 442c1d255d3SCy Schubert } 443c1d255d3SCy Schubert drv->ports = _read[0] & 0x001F; 444c1d255d3SCy Schubert /* add the MII port */ 445c1d255d3SCy Schubert drv->ports |= 1 << 8; 446c1d255d3SCy Schubert if (wpa_driver_roboswitch_join(drv, drv->ports, pae_group_addr) < 0) { 447c1d255d3SCy Schubert wpa_printf(MSG_INFO, "%s: Unable to join PAE group", __func__); 448c1d255d3SCy Schubert os_free(drv); 449c1d255d3SCy Schubert return NULL; 450c1d255d3SCy Schubert } else { 451c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "%s: Added PAE group address to " 452c1d255d3SCy Schubert "RoboSwitch ARL", __func__); 453c1d255d3SCy Schubert } 454c1d255d3SCy Schubert 455c1d255d3SCy Schubert return drv; 456c1d255d3SCy Schubert } 457c1d255d3SCy Schubert 458c1d255d3SCy Schubert 459c1d255d3SCy Schubert static void wpa_driver_roboswitch_deinit(void *priv) 460c1d255d3SCy Schubert { 461c1d255d3SCy Schubert struct wpa_driver_roboswitch_data *drv = priv; 462c1d255d3SCy Schubert 463c1d255d3SCy Schubert if (drv->l2) { 464c1d255d3SCy Schubert l2_packet_deinit(drv->l2); 465c1d255d3SCy Schubert drv->l2 = NULL; 466c1d255d3SCy Schubert } 467c1d255d3SCy Schubert if (wpa_driver_roboswitch_leave(drv, drv->ports, pae_group_addr) < 0) { 468c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "%s: Unable to leave PAE group", 469c1d255d3SCy Schubert __func__); 470c1d255d3SCy Schubert } 471c1d255d3SCy Schubert 472c1d255d3SCy Schubert close(drv->fd); 473c1d255d3SCy Schubert os_free(drv); 474c1d255d3SCy Schubert } 475c1d255d3SCy Schubert 476c1d255d3SCy Schubert 477c1d255d3SCy Schubert const struct wpa_driver_ops wpa_driver_roboswitch_ops = { 478c1d255d3SCy Schubert .name = "roboswitch", 479c1d255d3SCy Schubert .desc = "wpa_supplicant roboswitch driver", 480c1d255d3SCy Schubert .get_ssid = wpa_driver_roboswitch_get_ssid, 481c1d255d3SCy Schubert .get_bssid = wpa_driver_roboswitch_get_bssid, 482c1d255d3SCy Schubert .get_capa = wpa_driver_roboswitch_get_capa, 483c1d255d3SCy Schubert .init = wpa_driver_roboswitch_init, 484c1d255d3SCy Schubert .deinit = wpa_driver_roboswitch_deinit, 485c1d255d3SCy Schubert .set_param = wpa_driver_roboswitch_set_param, 486c1d255d3SCy Schubert .get_ifname = wpa_driver_roboswitch_get_ifname, 487c1d255d3SCy Schubert }; 488