1/* 2 * WPA Supplicant - roboswitch driver interface 3 * Copyright (c) 2008-2009 Jouke Witteveen 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 as 7 * published by the Free Software Foundation. 8 * 9 * Alternatively, this software may be distributed under the terms of BSD 10 * license. 11 * 12 * See README and COPYING for more details. 13 */ 14 15#include "includes.h" 16#include <sys/ioctl.h> 17#include <linux/if.h> 18#include <linux/sockios.h> 19#include <linux/if_ether.h> 20#include <linux/if_vlan.h> 21#include <linux/mii.h> 22 23#include "common.h" 24#include "driver.h" 25#include "l2_packet/l2_packet.h" 26 27#ifndef ETH_P_EAPOL 28#define ETH_P_EAPOL 0x888e 29#endif 30 31#define ROBO_PHY_ADDR 0x1e /* RoboSwitch PHY address */ 32 33#define SIOCGETCPHYRD (SIOCDEVPRIVATE + 9) 34#define SIOCSETCPHYWR (SIOCDEVPRIVATE + 10) 35#define SIOCGETCPHYRD2 (SIOCDEVPRIVATE + 12) 36#define SIOCSETCPHYWR2 (SIOCDEVPRIVATE + 13) 37#define SIOCGETCROBORD (SIOCDEVPRIVATE + 14) 38#define SIOCSETCROBOWR (SIOCDEVPRIVATE + 15) 39 40/* MII access registers */ 41#define ROBO_MII_PAGE 0x10 /* MII page register */ 42#define ROBO_MII_ADDR 0x11 /* MII address register */ 43#define ROBO_MII_DATA_OFFSET 0x18 /* Start of MII data registers */ 44 45#define ROBO_MII_PAGE_ENABLE 0x01 /* MII page op code */ 46#define ROBO_MII_ADDR_WRITE 0x01 /* MII address write op code */ 47#define ROBO_MII_ADDR_READ 0x02 /* MII address read op code */ 48#define ROBO_MII_DATA_MAX 4 /* Consecutive MII data registers */ 49#define ROBO_MII_RETRY_MAX 10 /* Read attempts before giving up */ 50 51/* Page numbers */ 52#define ROBO_ARLCTRL_PAGE 0x04 /* ARL control page */ 53#define ROBO_VLAN_PAGE 0x34 /* VLAN page */ 54 55/* ARL control page registers */ 56#define ROBO_ARLCTRL_CONF 0x00 /* ARL configuration register */ 57#define ROBO_ARLCTRL_ADDR_1 0x10 /* Multiport address 1 */ 58#define ROBO_ARLCTRL_VEC_1 0x16 /* Multiport vector 1 */ 59#define ROBO_ARLCTRL_ADDR_2 0x20 /* Multiport address 2 */ 60#define ROBO_ARLCTRL_VEC_2 0x26 /* Multiport vector 2 */ 61 62/* VLAN page registers */ 63#define ROBO_VLAN_ACCESS 0x08 /* VLAN table access register */ 64#define ROBO_VLAN_ACCESS_5350 0x06 /* VLAN table access register (5350) */ 65#define ROBO_VLAN_READ 0x0c /* VLAN read register */ 66#define ROBO_VLAN_MAX 0xff /* Maximum number of VLANs */ 67#define ROBO_VLAN_MAX_5350 0x0f /* Maximum number of VLANs (5350) */ 68 69 70/* RoboSwitch Models */ 71enum { 72 BCM536x = 0, /* 5365 */ 73 BCM535x = 1 << 0, /* 5325, 5352, 5354 */ 74 BCM5356 = 1 << 1 /* 5356, 5357 */ 75}; 76 77 78static const u8 pae_group_addr[ETH_ALEN] = 79{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; 80 81 82struct wpa_driver_roboswitch_data { 83 void *ctx; 84 struct l2_packet_data *l2, *l2_vlan; 85 char ifname[IFNAMSIZ + 1]; 86 u8 own_addr[ETH_ALEN]; 87 struct ifreq ifr; 88 int fd, model, et; 89 u16 ports; 90}; 91 92 93/* Copied from the kernel-only part of mii.h. */ 94static inline struct mii_ioctl_data *if_mii(struct ifreq *rq) 95{ 96 return (struct mii_ioctl_data *) &rq->ifr_ifru; 97} 98 99 100/* 101 * RoboSwitch uses 16-bit Big Endian addresses. 102 * The ordering of the words is reversed in the MII registers. 103 */ 104static void wpa_driver_roboswitch_addr_be16(const u8 addr[ETH_ALEN], u16 *be) 105{ 106 int i; 107 for (i = 0; i < ETH_ALEN; i += 2) 108 be[(ETH_ALEN - i) / 2 - 1] = WPA_GET_BE16(addr + i); 109} 110 111 112static u16 wpa_driver_roboswitch_mdio_access( 113 struct wpa_driver_roboswitch_data *drv, u8 phy, u8 reg, u16 val, int op) 114{ 115 if (drv->et) { 116 static int et_ioctl[2][2] = {{ SIOCGETCPHYRD, SIOCSETCPHYWR }, 117 { SIOCGETCPHYRD2, SIOCSETCPHYWR2 }}; 118 int args[2] = { reg, val }; 119 120 drv->ifr.ifr_data = (caddr_t) args; 121 if (phy != ROBO_PHY_ADDR) { 122 args[0] |= phy << 16; 123 if (ioctl(drv->fd, et_ioctl[1][op], &drv->ifr) < 0) 124 return 0xffff; 125 } else 126 if (ioctl(drv->fd, et_ioctl[0][op], &drv->ifr) < 0) 127 return 0xffff; 128 129 return op ? 0 : args[1]; 130 } else { 131 static int mii_ioctl[2] = { SIOCGMIIREG, SIOCSMIIREG }; 132 struct mii_ioctl_data *mii = if_mii(&drv->ifr); 133 134 mii->phy_id = phy; 135 mii->reg_num = reg; 136 mii->val_in = val; 137 if (ioctl(drv->fd, mii_ioctl[op], &drv->ifr) < 0) 138 return 0xffff; 139 140 return op ? 0 : mii->val_out; 141 } 142} 143 144 145static inline u16 wpa_driver_roboswitch_mdio_read( 146 struct wpa_driver_roboswitch_data *drv, u8 reg) 147{ 148 return wpa_driver_roboswitch_mdio_access(drv, ROBO_PHY_ADDR, reg, 0, 0); 149} 150 151 152static inline void wpa_driver_roboswitch_mdio_write( 153 struct wpa_driver_roboswitch_data *drv, u8 reg, u16 val) 154{ 155 wpa_driver_roboswitch_mdio_access(drv, ROBO_PHY_ADDR, reg, val, 1); 156} 157 158 159static int wpa_driver_roboswitch_reg(struct wpa_driver_roboswitch_data *drv, 160 u8 page, u8 reg, u8 op) 161{ 162 int i; 163 164 /* set page number */ 165 wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_PAGE, 166 (page << 8) | ROBO_MII_PAGE_ENABLE); 167 /* set register address */ 168 wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_ADDR, (reg << 8) | op); 169 170 /* check if operation completed */ 171 for (i = 0; i < ROBO_MII_RETRY_MAX; ++i) { 172 if ((wpa_driver_roboswitch_mdio_read(drv, 173 ROBO_MII_ADDR) & 3) == 0) 174 return 0; 175 } 176 177 /* timeout */ 178 return -1; 179} 180 181 182static int wpa_driver_roboswitch_read(struct wpa_driver_roboswitch_data *drv, 183 u8 page, u8 reg, u16 *val, int len) 184{ 185 int i; 186 187 if (len > ROBO_MII_DATA_MAX || 188 wpa_driver_roboswitch_reg(drv, page, reg, ROBO_MII_ADDR_READ) < 0) 189 return -1; 190 191 for (i = 0; i < len; ++i) { 192 val[i] = wpa_driver_roboswitch_mdio_read(drv, 193 ROBO_MII_DATA_OFFSET + i); 194 } 195 196 return 0; 197} 198 199 200static int wpa_driver_roboswitch_write(struct wpa_driver_roboswitch_data *drv, 201 u8 page, u8 reg, u16 *val, int len) 202{ 203 int i; 204 205 if (len > ROBO_MII_DATA_MAX) return -1; 206 for (i = 0; i < len; ++i) { 207 wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_DATA_OFFSET + i, 208 val[i]); 209 } 210 return wpa_driver_roboswitch_reg(drv, page, reg, ROBO_MII_ADDR_WRITE); 211} 212 213 214static void wpa_driver_roboswitch_receive(void *priv, const u8 *src_addr, 215 const u8 *buf, size_t len) 216{ 217 struct wpa_driver_roboswitch_data *drv = priv; 218 219 if (len > 14 && WPA_GET_BE16(buf + 12) == ETH_P_EAPOL && 220 (os_memcmp(buf, drv->own_addr, ETH_ALEN) == 0 || 221 os_memcmp(buf, pae_group_addr, ETH_ALEN) == 0)) { 222 wpa_supplicant_rx_eapol(drv->ctx, src_addr, buf + 14, 223 len - 14); 224 } 225} 226 227 228static int wpa_driver_roboswitch_send(void *priv, const u8 *dest, u16 proto, 229 const u8 *data, size_t data_len) 230{ 231 struct wpa_driver_roboswitch_data *drv = priv; 232 struct { 233 struct l2_ethhdr eth; 234 u8 data[0]; 235 } STRUCT_PACKED *msg; 236 size_t msg_len; 237 int res; 238 239 if (drv->l2 == NULL) 240 return -1; 241 242 msg_len = sizeof(msg->eth) + data_len; 243 msg = os_malloc(msg_len); 244 if (msg == NULL) 245 return -1; 246 247 os_memset(&msg->eth, 0, sizeof(msg->eth)); 248 os_memcpy(msg->eth.h_dest, dest, ETH_ALEN); 249 os_memcpy(msg->eth.h_source, drv->own_addr, ETH_ALEN); 250 msg->eth.h_proto = host_to_be16(proto); 251 os_memcpy(msg->data, data, data_len); 252 253 res = l2_packet_send(drv->l2, dest, proto, (u8 *) msg, msg_len); 254 os_free(msg); 255 256 return res; 257} 258 259 260static int wpa_driver_roboswitch_get_ssid(void *priv, u8 *ssid) 261{ 262 ssid[0] = 0; 263 return 0; 264} 265 266 267static int wpa_driver_roboswitch_get_bssid(void *priv, u8 *bssid) 268{ 269 /* Report PAE group address as the "BSSID" for wired connection. */ 270 os_memcpy(bssid, pae_group_addr, ETH_ALEN); 271 return 0; 272} 273 274 275static const u8 * wpa_driver_roboswitch_get_mac_addr(void *priv) 276{ 277 struct wpa_driver_roboswitch_data *drv = priv; 278 return drv->own_addr; 279} 280 281 282static int wpa_driver_roboswitch_join(struct wpa_driver_roboswitch_data *drv, 283 u16 ports, const u8 *addr) 284{ 285 u16 read1[3], read2[3], addr_be16[3]; 286 287 wpa_driver_roboswitch_addr_be16(addr, addr_be16); 288 289 if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 290 ROBO_ARLCTRL_CONF, read1, 1) < 0) 291 return -1; 292 if (!(read1[0] & (1 << 4))) { 293 /* multiport addresses are not yet enabled */ 294 read1[0] |= 1 << 4; 295 wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, 296 ROBO_ARLCTRL_ADDR_1, addr_be16, 3); 297 wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, 298 ROBO_ARLCTRL_VEC_1, &ports, 1); 299 wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, 300 ROBO_ARLCTRL_ADDR_2, addr_be16, 3); 301 wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, 302 ROBO_ARLCTRL_VEC_2, &ports, 1); 303 wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, 304 ROBO_ARLCTRL_CONF, read1, 1); 305 } else { 306 /* if both multiport addresses are the same we can add */ 307 wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 308 ROBO_ARLCTRL_ADDR_1, read1, 3); 309 wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 310 ROBO_ARLCTRL_ADDR_2, read2, 3); 311 if (os_memcmp(read1, read2, 6) != 0) 312 return -1; 313 wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 314 ROBO_ARLCTRL_VEC_1, read1, 1); 315 wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 316 ROBO_ARLCTRL_VEC_2, read2, 1); 317 if (read1[0] != read2[0]) 318 return -1; 319 wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, 320 ROBO_ARLCTRL_ADDR_1, addr_be16, 3); 321 wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, 322 ROBO_ARLCTRL_VEC_1, &ports, 1); 323 } 324 return 0; 325} 326 327 328static int wpa_driver_roboswitch_leave(struct wpa_driver_roboswitch_data *drv, 329 u16 ports, const u8 *addr) 330{ 331 u16 _read, addr_be16[3], addr_read[3], ports_read; 332 333 wpa_driver_roboswitch_addr_be16(addr, addr_be16); 334 335 wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_CONF, 336 &_read, 1); 337 /* If ARL control is disabled, there is nothing to leave. */ 338 if (!(_read & (1 << 4))) return -1; 339 340 wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 341 ROBO_ARLCTRL_ADDR_1, addr_read, 3); 342 wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1, 343 &ports_read, 1); 344 /* check if we occupy multiport address 1 */ 345 if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) { 346 wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 347 ROBO_ARLCTRL_ADDR_2, addr_read, 3); 348 wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 349 ROBO_ARLCTRL_VEC_2, &ports_read, 1); 350 /* and multiport address 2 */ 351 if (os_memcmp(addr_read, addr_be16, 6) == 0 && 352 ports_read == ports) { 353 _read &= ~(1 << 4); 354 wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, 355 ROBO_ARLCTRL_CONF, &_read, 356 1); 357 } else { 358 wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 359 ROBO_ARLCTRL_ADDR_1, 360 addr_read, 3); 361 wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 362 ROBO_ARLCTRL_VEC_1, 363 &ports_read, 1); 364 wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, 365 ROBO_ARLCTRL_ADDR_2, 366 addr_read, 3); 367 wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, 368 ROBO_ARLCTRL_VEC_2, 369 &ports_read, 1); 370 } 371 } else { 372 wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 373 ROBO_ARLCTRL_ADDR_2, addr_read, 3); 374 wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, 375 ROBO_ARLCTRL_VEC_2, &ports_read, 1); 376 /* or multiport address 2 */ 377 if (os_memcmp(addr_read, addr_be16, 6) == 0 && 378 ports_read == ports) { 379 wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, 380 ROBO_ARLCTRL_ADDR_1, 381 addr_read, 3); 382 wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, 383 ROBO_ARLCTRL_VEC_1, 384 &ports_read, 1); 385 } else return -1; 386 } 387 return 0; 388} 389 390 391static void * wpa_driver_roboswitch_init(void *ctx, const char *ifname) 392{ 393 struct wpa_driver_roboswitch_data *drv; 394 struct vlan_ioctl_args ifv; 395 u32 phyid; 396 u16 vlan, i; 397 union { 398 u32 val32; 399 u16 val16[2]; 400 } u; 401 402 drv = os_zalloc(sizeof(*drv)); 403 if (drv == NULL) return NULL; 404 drv->ctx = ctx; 405 406 drv->fd = socket(PF_INET, SOCK_DGRAM, 0); 407 if (drv->fd < 0) { 408 wpa_printf(MSG_INFO, "%s: Unable to create socket", __func__); 409 os_free(drv); 410 return NULL; 411 } 412 413 os_memset(&ifv, 0, sizeof(ifv)); 414 os_strlcpy(ifv.device1, ifname, sizeof(ifv.device1)); 415 ifv.cmd = GET_VLAN_REALDEV_NAME_CMD; 416 if (ioctl(drv->fd, SIOCGIFVLAN, &ifv) >= 0) { 417 os_strlcpy(drv->ifname, ifv.u.device2, sizeof(drv->ifname)); 418 ifv.cmd = GET_VLAN_VID_CMD; 419 if (ioctl(drv->fd, SIOCGIFVLAN, &ifv) < 0) { 420 perror("ioctl[SIOCGIFVLAN]"); 421 goto error; 422 } 423 vlan = ifv.u.VID; 424 } else 425 if (sscanf(ifname, "vlan%hu", &vlan) == 1) { 426 os_strlcpy(drv->ifname, "eth0", sizeof(drv->ifname)); 427 } else 428 if (sscanf(ifname, "%16[^.].%hu", drv->ifname, &vlan) != 2) { 429 os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); 430 vlan = (u16) -1; 431 } 432 433 os_memset(&drv->ifr, 0, sizeof(drv->ifr)); 434 os_strlcpy(drv->ifr.ifr_name, drv->ifname, IFNAMSIZ); 435 if (ioctl(drv->fd, SIOCGMIIPHY, &drv->ifr) < 0) { 436 drv->et = 1; 437 } else 438 if (if_mii(&drv->ifr)->phy_id != ROBO_PHY_ADDR) { 439 wpa_printf(MSG_INFO, "%s: Invalid phy address (not a " 440 "RoboSwitch?)", __func__); 441 goto error; 442 } 443 444 phyid = wpa_driver_roboswitch_mdio_read(drv, 0x03) << 16 | 445 wpa_driver_roboswitch_mdio_read(drv, 0x02); 446 if (phyid == 0) 447 phyid = wpa_driver_roboswitch_mdio_access(drv, 0, 0x03, 0, 0) << 16 | 448 wpa_driver_roboswitch_mdio_access(drv, 0, 0x02, 0, 0); 449 if (phyid == 0xffffffff || phyid == 0x55210022) { 450 wpa_printf(MSG_INFO, "%s: No RoboSwitch in managed " 451 "mode found", __func__); 452 goto error; 453 } 454 455 /* set and read back to see if the register can be used */ 456 u.val16[0] = ROBO_VLAN_MAX; 457 wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350, 458 &u.val16[0], 1); 459 wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350, 460 &u.val16[1], 1); 461 if (u.val16[0] == u.val16[1]) { 462 drv->model = BCM535x; 463 /* dirty trick for 5356/5357 */ 464 if ((phyid & 0xfff0ffff) == 0x5da00362 || 465 (phyid & 0xfff0ffff) == 0x5e000362) 466 drv->model |= BCM5356; 467 } else 468 drv->model = BCM536x; 469 470 wpa_printf(MSG_INFO, "%s: RoboSwitch id 0x%x model 0x%x", 471 __func__, phyid, drv->model); 472 473 if (vlan != (u16) -1 && 474 vlan > ((drv->model == BCM536x) ? ROBO_VLAN_MAX 475 : ROBO_VLAN_MAX_5350)) { 476 wpa_printf(MSG_INFO, "%s: VLAN %d out of range on interface " 477 "%s", __func__, vlan, drv->ifname); 478 goto error; 479 } 480 481 i = (vlan == (u16) -1) ? 0 : vlan; 482 for ( ; i <= ((drv->model == BCM536x) ? ROBO_VLAN_MAX 483 : ROBO_VLAN_MAX_5350); i++) { 484 u.val16[0] = i; 485 /* set the read bit */ 486 u.val16[0] |= (1 << 13); 487 wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, 488 (drv->model == BCM536x) ? ROBO_VLAN_ACCESS 489 : ROBO_VLAN_ACCESS_5350, 490 &u.val16[0], 1); 491 wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_READ, 492 &u.val16[0], (drv->model == BCM536x) ? 1 : 2); 493 /* is vlan enabled */ 494 if (drv->model == BCM536x && 495 u.val16[0] & (1 << 14)) { 496 if (vlan != (u16) -1) 497 break; 498 if (u.val16[0] & (1 << 5) && u.val16[0] & (1 << 12)) { 499 vlan = i; 500 break; 501 } 502 } else 503 if (drv->model & BCM535x && 504 u.val32 & (1 << ((drv->model & BCM5356) ? 24 : 20))) { 505 if (vlan != (u16) -1) 506 break; 507 if (u.val32 & (1 << 5) && u.val32 & (1 << 11)) { 508 vlan = i; 509 vlan |= (drv->model & BCM5356) ? 510 (u.val32 & 0xff000) >> 12 : 511 (u.val32 & 0xff000) >> 8; 512 break; 513 } 514 } else 515 /* is vlan specified */ 516 if (vlan != (u16) -1) { 517 wpa_printf(MSG_INFO, "%s: Could not get port information for " 518 "VLAN %d", __func__, vlan); 519 goto error; 520 } 521 } 522 523 if (vlan == (u16) -1) { 524 wpa_printf(MSG_INFO, "%s: Unable to find VLAN for " 525 "interface %s", __func__, drv->ifname); 526 goto error; 527 } 528 wpa_printf(MSG_DEBUG, "%s: Used VLAN %d ports on RoboSwitch interface " 529 "%s", __func__, vlan, drv->ifname); 530 531 /* even if empty */ 532 drv->ports = u.val16[0] & 0x001f; 533 /* add the MII port */ 534 drv->ports |= 1 << 8; 535 536 drv->l2 = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL, 537 wpa_driver_roboswitch_receive, drv, 538 1); 539 if (drv->l2 == NULL) { 540 wpa_printf(MSG_INFO, "%s: Unable to listen on %s", 541 __func__, drv->ifname); 542 goto error; 543 } 544 l2_packet_get_own_addr(drv->l2, drv->own_addr); 545 546 if (os_strcmp(drv->ifname, ifname) != 0) { 547 /* may fail for not existent vlanX or ethX.X */ 548 drv->l2_vlan = l2_packet_init(ifname, NULL, ETH_P_EAPOL, 549 wpa_driver_roboswitch_receive, 550 drv, 1); 551 if (drv->l2_vlan) 552 l2_packet_get_own_addr(drv->l2_vlan, drv->own_addr); 553 } 554 555 if (wpa_driver_roboswitch_join(drv, drv->ports, pae_group_addr) < 0) { 556 wpa_printf(MSG_INFO, "%s: Unable to join PAE group", __func__); 557 goto error; 558 } else { 559 wpa_printf(MSG_DEBUG, "%s: Added PAE group address to " 560 "RoboSwitch ARL", __func__); 561 } 562 563 return drv; 564 565error: 566 close(drv->fd); 567 os_free(drv); 568 return NULL; 569} 570 571 572static void wpa_driver_roboswitch_deinit(void *priv) 573{ 574 struct wpa_driver_roboswitch_data *drv = priv; 575 576 if (drv->l2) { 577 l2_packet_deinit(drv->l2); 578 drv->l2 = NULL; 579 } 580 if (drv->l2_vlan) { 581 l2_packet_deinit(drv->l2_vlan); 582 drv->l2_vlan = NULL; 583 } 584 if (wpa_driver_roboswitch_leave(drv, drv->ports, pae_group_addr) < 0) { 585 wpa_printf(MSG_DEBUG, "%s: Unable to leave PAE group", 586 __func__); 587 } 588 589 close(drv->fd); 590 os_free(drv); 591} 592 593 594const struct wpa_driver_ops wpa_driver_roboswitch_ops = { 595 .name = "roboswitch", 596 .desc = "wpa_supplicant roboswitch driver", 597 .get_ssid = wpa_driver_roboswitch_get_ssid, 598 .get_bssid = wpa_driver_roboswitch_get_bssid, 599 .init = wpa_driver_roboswitch_init, 600 .deinit = wpa_driver_roboswitch_deinit, 601 .get_mac_addr = wpa_driver_roboswitch_get_mac_addr, 602 .send_eapol = wpa_driver_roboswitch_send, 603}; 604