1 // SPDX-License-Identifier: GPL-2.0 2 /* cfg80211 Interface for prism2_usb module */ 3 #include "hfa384x.h" 4 #include "prism2mgmt.h" 5 6 /* Prism2 channel/frequency/bitrate declarations */ 7 static const struct ieee80211_channel prism2_channels[] = { 8 { .center_freq = 2412 }, 9 { .center_freq = 2417 }, 10 { .center_freq = 2422 }, 11 { .center_freq = 2427 }, 12 { .center_freq = 2432 }, 13 { .center_freq = 2437 }, 14 { .center_freq = 2442 }, 15 { .center_freq = 2447 }, 16 { .center_freq = 2452 }, 17 { .center_freq = 2457 }, 18 { .center_freq = 2462 }, 19 { .center_freq = 2467 }, 20 { .center_freq = 2472 }, 21 { .center_freq = 2484 }, 22 }; 23 24 static const struct ieee80211_rate prism2_rates[] = { 25 { .bitrate = 10 }, 26 { .bitrate = 20 }, 27 { .bitrate = 55 }, 28 { .bitrate = 110 } 29 }; 30 31 #define PRISM2_NUM_CIPHER_SUITES 2 32 static const u32 prism2_cipher_suites[PRISM2_NUM_CIPHER_SUITES] = { 33 WLAN_CIPHER_SUITE_WEP40, 34 WLAN_CIPHER_SUITE_WEP104 35 }; 36 37 /* prism2 device private data */ 38 struct prism2_wiphy_private { 39 struct wlandevice *wlandev; 40 41 struct ieee80211_supported_band band; 42 struct ieee80211_channel channels[ARRAY_SIZE(prism2_channels)]; 43 struct ieee80211_rate rates[ARRAY_SIZE(prism2_rates)]; 44 45 struct cfg80211_scan_request *scan_request; 46 }; 47 48 static const void * const prism2_wiphy_privid = &prism2_wiphy_privid; 49 50 /* Helper Functions */ 51 static int prism2_result2err(int prism2_result) 52 { 53 int err = 0; 54 55 switch (prism2_result) { 56 case P80211ENUM_resultcode_invalid_parameters: 57 err = -EINVAL; 58 break; 59 case P80211ENUM_resultcode_implementation_failure: 60 err = -EIO; 61 break; 62 case P80211ENUM_resultcode_not_supported: 63 err = -EOPNOTSUPP; 64 break; 65 default: 66 err = 0; 67 break; 68 } 69 70 return err; 71 } 72 73 static int prism2_domibset_uint32(struct wlandevice *wlandev, 74 u32 did, u32 data) 75 { 76 struct p80211msg_dot11req_mibset msg; 77 struct p80211item_uint32 *mibitem = 78 (struct p80211item_uint32 *)&msg.mibattribute.data; 79 80 msg.msgcode = DIDMSG_DOT11REQ_MIBSET; 81 mibitem->did = did; 82 mibitem->data = data; 83 84 return p80211req_dorequest(wlandev, (u8 *)&msg); 85 } 86 87 static int prism2_domibset_pstr32(struct wlandevice *wlandev, 88 u32 did, u8 len, const u8 *data) 89 { 90 struct p80211msg_dot11req_mibset msg; 91 struct p80211item_pstr32 *mibitem = 92 (struct p80211item_pstr32 *)&msg.mibattribute.data; 93 94 msg.msgcode = DIDMSG_DOT11REQ_MIBSET; 95 mibitem->did = did; 96 mibitem->data.len = len; 97 memcpy(mibitem->data.data, data, len); 98 99 return p80211req_dorequest(wlandev, (u8 *)&msg); 100 } 101 102 /* The interface functions, called by the cfg80211 layer */ 103 static int prism2_change_virtual_intf(struct wiphy *wiphy, 104 struct net_device *dev, 105 enum nl80211_iftype type, 106 struct vif_params *params) 107 { 108 struct wlandevice *wlandev = dev->ml_priv; 109 u32 data; 110 int result; 111 int err = 0; 112 113 switch (type) { 114 case NL80211_IFTYPE_ADHOC: 115 if (wlandev->macmode == WLAN_MACMODE_IBSS_STA) 116 goto exit; 117 wlandev->macmode = WLAN_MACMODE_IBSS_STA; 118 data = 0; 119 break; 120 case NL80211_IFTYPE_STATION: 121 if (wlandev->macmode == WLAN_MACMODE_ESS_STA) 122 goto exit; 123 wlandev->macmode = WLAN_MACMODE_ESS_STA; 124 data = 1; 125 break; 126 default: 127 netdev_warn(dev, "Operation mode: %d not support\n", type); 128 return -EOPNOTSUPP; 129 } 130 131 /* Set Operation mode to the PORT TYPE RID */ 132 result = prism2_domibset_uint32(wlandev, 133 DIDMIB_P2_STATIC_CNFPORTTYPE, 134 data); 135 136 if (result) 137 err = -EFAULT; 138 139 dev->ieee80211_ptr->iftype = type; 140 141 exit: 142 return err; 143 } 144 145 static int prism2_add_key(struct wiphy *wiphy, struct net_device *dev, 146 u8 key_index, bool pairwise, const u8 *mac_addr, 147 struct key_params *params) 148 { 149 struct wlandevice *wlandev = dev->ml_priv; 150 u32 did; 151 152 if (key_index >= NUM_WEPKEYS) 153 return -EINVAL; 154 155 if (params->cipher != WLAN_CIPHER_SUITE_WEP40 && 156 params->cipher != WLAN_CIPHER_SUITE_WEP104) { 157 pr_debug("Unsupported cipher suite\n"); 158 return -EFAULT; 159 } 160 161 if (prism2_domibset_uint32(wlandev, 162 DIDMIB_DOT11SMT_PRIVACYTABLE_WEPDEFAULTKEYID, 163 key_index)) 164 return -EFAULT; 165 166 /* send key to driver */ 167 did = didmib_dot11smt_wepdefaultkeystable_key(key_index + 1); 168 169 if (prism2_domibset_pstr32(wlandev, did, params->key_len, params->key)) 170 return -EFAULT; 171 return 0; 172 } 173 174 static int prism2_get_key(struct wiphy *wiphy, struct net_device *dev, 175 u8 key_index, bool pairwise, 176 const u8 *mac_addr, void *cookie, 177 void (*callback)(void *cookie, struct key_params*)) 178 { 179 struct wlandevice *wlandev = dev->ml_priv; 180 struct key_params params; 181 int len; 182 183 if (key_index >= NUM_WEPKEYS) 184 return -EINVAL; 185 186 len = wlandev->wep_keylens[key_index]; 187 memset(¶ms, 0, sizeof(params)); 188 189 if (len == 13) 190 params.cipher = WLAN_CIPHER_SUITE_WEP104; 191 else if (len == 5) 192 params.cipher = WLAN_CIPHER_SUITE_WEP104; 193 else 194 return -ENOENT; 195 params.key_len = len; 196 params.key = wlandev->wep_keys[key_index]; 197 params.seq_len = 0; 198 199 callback(cookie, ¶ms); 200 201 return 0; 202 } 203 204 static int prism2_del_key(struct wiphy *wiphy, struct net_device *dev, 205 u8 key_index, bool pairwise, const u8 *mac_addr) 206 { 207 struct wlandevice *wlandev = dev->ml_priv; 208 u32 did; 209 int err = 0; 210 int result = 0; 211 212 /* There is no direct way in the hardware (AFAIK) of removing 213 * a key, so we will cheat by setting the key to a bogus value 214 */ 215 216 if (key_index >= NUM_WEPKEYS) 217 return -EINVAL; 218 219 /* send key to driver */ 220 did = didmib_dot11smt_wepdefaultkeystable_key(key_index + 1); 221 result = prism2_domibset_pstr32(wlandev, did, 13, "0000000000000"); 222 223 if (result) 224 err = -EFAULT; 225 226 return err; 227 } 228 229 static int prism2_set_default_key(struct wiphy *wiphy, struct net_device *dev, 230 u8 key_index, bool unicast, bool multicast) 231 { 232 struct wlandevice *wlandev = dev->ml_priv; 233 234 return prism2_domibset_uint32(wlandev, 235 DIDMIB_DOT11SMT_PRIVACYTABLE_WEPDEFAULTKEYID, 236 key_index); 237 } 238 239 static int prism2_get_station(struct wiphy *wiphy, struct net_device *dev, 240 const u8 *mac, struct station_info *sinfo) 241 { 242 struct wlandevice *wlandev = dev->ml_priv; 243 struct p80211msg_lnxreq_commsquality quality; 244 int result; 245 246 memset(sinfo, 0, sizeof(*sinfo)); 247 248 if (!wlandev || (wlandev->msdstate != WLAN_MSD_RUNNING)) 249 return -EOPNOTSUPP; 250 251 /* build request message */ 252 quality.msgcode = DIDMSG_LNXREQ_COMMSQUALITY; 253 quality.dbm.data = P80211ENUM_truth_true; 254 quality.dbm.status = P80211ENUM_msgitem_status_data_ok; 255 256 /* send message to nsd */ 257 if (!wlandev->mlmerequest) 258 return -EOPNOTSUPP; 259 260 result = wlandev->mlmerequest(wlandev, (struct p80211msg *)&quality); 261 262 if (result == 0) { 263 sinfo->txrate.legacy = quality.txrate.data; 264 sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); 265 sinfo->signal = quality.level.data; 266 sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); 267 } 268 269 return result; 270 } 271 272 static int prism2_scan(struct wiphy *wiphy, 273 struct cfg80211_scan_request *request) 274 { 275 struct net_device *dev; 276 struct prism2_wiphy_private *priv = wiphy_priv(wiphy); 277 struct wlandevice *wlandev; 278 struct p80211msg_dot11req_scan msg1; 279 struct p80211msg_dot11req_scan_results msg2; 280 struct cfg80211_bss *bss; 281 struct cfg80211_scan_info info = {}; 282 283 int result; 284 int err = 0; 285 int numbss = 0; 286 int i = 0; 287 u8 ie_buf[46]; 288 int ie_len; 289 290 if (!request) 291 return -EINVAL; 292 293 dev = request->wdev->netdev; 294 wlandev = dev->ml_priv; 295 296 if (priv->scan_request && priv->scan_request != request) 297 return -EBUSY; 298 299 if (wlandev->macmode == WLAN_MACMODE_ESS_AP) { 300 netdev_err(dev, "Can't scan in AP mode\n"); 301 return -EOPNOTSUPP; 302 } 303 304 priv->scan_request = request; 305 306 memset(&msg1, 0x00, sizeof(msg1)); 307 msg1.msgcode = DIDMSG_DOT11REQ_SCAN; 308 msg1.bsstype.data = P80211ENUM_bsstype_any; 309 310 memset(&msg1.bssid.data.data, 0xFF, sizeof(msg1.bssid.data.data)); 311 msg1.bssid.data.len = 6; 312 313 if (request->n_ssids > 0) { 314 msg1.scantype.data = P80211ENUM_scantype_active; 315 msg1.ssid.data.len = request->ssids->ssid_len; 316 memcpy(msg1.ssid.data.data, 317 request->ssids->ssid, request->ssids->ssid_len); 318 } else { 319 msg1.scantype.data = 0; 320 } 321 msg1.probedelay.data = 0; 322 323 for (i = 0; 324 (i < request->n_channels) && i < ARRAY_SIZE(prism2_channels); 325 i++) 326 msg1.channellist.data.data[i] = 327 ieee80211_frequency_to_channel( 328 request->channels[i]->center_freq); 329 msg1.channellist.data.len = request->n_channels; 330 331 msg1.maxchanneltime.data = 250; 332 msg1.minchanneltime.data = 200; 333 334 result = p80211req_dorequest(wlandev, (u8 *)&msg1); 335 if (result) { 336 err = prism2_result2err(msg1.resultcode.data); 337 goto exit; 338 } 339 /* Now retrieve scan results */ 340 numbss = msg1.numbss.data; 341 342 for (i = 0; i < numbss; i++) { 343 int freq; 344 345 memset(&msg2, 0, sizeof(msg2)); 346 msg2.msgcode = DIDMSG_DOT11REQ_SCAN_RESULTS; 347 msg2.bssindex.data = i; 348 349 result = p80211req_dorequest(wlandev, (u8 *)&msg2); 350 if ((result != 0) || 351 (msg2.resultcode.data != P80211ENUM_resultcode_success)) { 352 break; 353 } 354 355 ie_buf[0] = WLAN_EID_SSID; 356 ie_buf[1] = msg2.ssid.data.len; 357 ie_len = ie_buf[1] + 2; 358 memcpy(&ie_buf[2], &msg2.ssid.data.data, msg2.ssid.data.len); 359 freq = ieee80211_channel_to_frequency(msg2.dschannel.data, 360 NL80211_BAND_2GHZ); 361 bss = cfg80211_inform_bss(wiphy, 362 ieee80211_get_channel(wiphy, freq), 363 CFG80211_BSS_FTYPE_UNKNOWN, 364 (const u8 *)&msg2.bssid.data.data, 365 msg2.timestamp.data, msg2.capinfo.data, 366 msg2.beaconperiod.data, 367 ie_buf, 368 ie_len, 369 (msg2.signal.data - 65536) * 100, /* Conversion to signed type */ 370 GFP_KERNEL 371 ); 372 373 if (!bss) { 374 err = -ENOMEM; 375 goto exit; 376 } 377 378 cfg80211_put_bss(wiphy, bss); 379 } 380 381 if (result) 382 err = prism2_result2err(msg2.resultcode.data); 383 384 exit: 385 info.aborted = !!(err); 386 cfg80211_scan_done(request, &info); 387 priv->scan_request = NULL; 388 return err; 389 } 390 391 static int prism2_set_wiphy_params(struct wiphy *wiphy, u32 changed) 392 { 393 struct prism2_wiphy_private *priv = wiphy_priv(wiphy); 394 struct wlandevice *wlandev = priv->wlandev; 395 u32 data; 396 int result; 397 int err = 0; 398 399 if (changed & WIPHY_PARAM_RTS_THRESHOLD) { 400 if (wiphy->rts_threshold == -1) 401 data = 2347; 402 else 403 data = wiphy->rts_threshold; 404 405 result = prism2_domibset_uint32(wlandev, 406 DIDMIB_DOT11MAC_OPERATIONTABLE_RTSTHRESHOLD, 407 data); 408 if (result) { 409 err = -EFAULT; 410 goto exit; 411 } 412 } 413 414 if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { 415 if (wiphy->frag_threshold == -1) 416 data = 2346; 417 else 418 data = wiphy->frag_threshold; 419 420 result = prism2_domibset_uint32(wlandev, 421 DIDMIB_DOT11MAC_OPERATIONTABLE_FRAGMENTATIONTHRESHOLD, 422 data); 423 if (result) { 424 err = -EFAULT; 425 goto exit; 426 } 427 } 428 429 exit: 430 return err; 431 } 432 433 static int prism2_connect(struct wiphy *wiphy, struct net_device *dev, 434 struct cfg80211_connect_params *sme) 435 { 436 struct wlandevice *wlandev = dev->ml_priv; 437 struct ieee80211_channel *channel = sme->channel; 438 struct p80211msg_lnxreq_autojoin msg_join; 439 u32 did; 440 int length = sme->ssid_len; 441 int chan = -1; 442 int is_wep = (sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP40) || 443 (sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP104); 444 int result; 445 int err = 0; 446 447 /* Set the channel */ 448 if (channel) { 449 chan = ieee80211_frequency_to_channel(channel->center_freq); 450 result = prism2_domibset_uint32(wlandev, 451 DIDMIB_DOT11PHY_DSSSTABLE_CURRENTCHANNEL, 452 chan); 453 if (result) 454 goto exit; 455 } 456 457 /* Set the authorization */ 458 if ((sme->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) || 459 ((sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) && !is_wep)) 460 msg_join.authtype.data = P80211ENUM_authalg_opensystem; 461 else if ((sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY) || 462 ((sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) && is_wep)) 463 msg_join.authtype.data = P80211ENUM_authalg_sharedkey; 464 else 465 netdev_warn(dev, 466 "Unhandled authorisation type for connect (%d)\n", 467 sme->auth_type); 468 469 /* Set the encryption - we only support wep */ 470 if (is_wep) { 471 if (sme->key) { 472 if (sme->key_idx >= NUM_WEPKEYS) 473 return -EINVAL; 474 475 result = prism2_domibset_uint32(wlandev, 476 DIDMIB_DOT11SMT_PRIVACYTABLE_WEPDEFAULTKEYID, 477 sme->key_idx); 478 if (result) 479 goto exit; 480 481 /* send key to driver */ 482 did = didmib_dot11smt_wepdefaultkeystable_key( 483 sme->key_idx + 1); 484 result = prism2_domibset_pstr32(wlandev, 485 did, sme->key_len, 486 (u8 *)sme->key); 487 if (result) 488 goto exit; 489 } 490 491 /* Assume we should set privacy invoked and exclude unencrypted 492 * We could possible use sme->privacy here, but the assumption 493 * seems reasonable anyways 494 */ 495 result = prism2_domibset_uint32(wlandev, 496 DIDMIB_DOT11SMT_PRIVACYTABLE_PRIVACYINVOKED, 497 P80211ENUM_truth_true); 498 if (result) 499 goto exit; 500 501 result = prism2_domibset_uint32(wlandev, 502 DIDMIB_DOT11SMT_PRIVACYTABLE_EXCLUDEUNENCRYPTED, 503 P80211ENUM_truth_true); 504 if (result) 505 goto exit; 506 507 } else { 508 /* Assume we should unset privacy invoked 509 * and exclude unencrypted 510 */ 511 result = prism2_domibset_uint32(wlandev, 512 DIDMIB_DOT11SMT_PRIVACYTABLE_PRIVACYINVOKED, 513 P80211ENUM_truth_false); 514 if (result) 515 goto exit; 516 517 result = prism2_domibset_uint32(wlandev, 518 DIDMIB_DOT11SMT_PRIVACYTABLE_EXCLUDEUNENCRYPTED, 519 P80211ENUM_truth_false); 520 if (result) 521 goto exit; 522 } 523 524 /* Now do the actual join. Note there is no way that I can 525 * see to request a specific bssid 526 */ 527 msg_join.msgcode = DIDMSG_LNXREQ_AUTOJOIN; 528 529 memcpy(msg_join.ssid.data.data, sme->ssid, length); 530 msg_join.ssid.data.len = length; 531 532 result = p80211req_dorequest(wlandev, (u8 *)&msg_join); 533 534 exit: 535 if (result) 536 err = -EFAULT; 537 538 return err; 539 } 540 541 static int prism2_disconnect(struct wiphy *wiphy, struct net_device *dev, 542 u16 reason_code) 543 { 544 struct wlandevice *wlandev = dev->ml_priv; 545 struct p80211msg_lnxreq_autojoin msg_join; 546 int result; 547 int err = 0; 548 549 /* Do a join, with a bogus ssid. Thats the only way I can think of */ 550 msg_join.msgcode = DIDMSG_LNXREQ_AUTOJOIN; 551 552 memcpy(msg_join.ssid.data.data, "---", 3); 553 msg_join.ssid.data.len = 3; 554 555 result = p80211req_dorequest(wlandev, (u8 *)&msg_join); 556 557 if (result) 558 err = -EFAULT; 559 560 return err; 561 } 562 563 static int prism2_join_ibss(struct wiphy *wiphy, struct net_device *dev, 564 struct cfg80211_ibss_params *params) 565 { 566 return -EOPNOTSUPP; 567 } 568 569 static int prism2_leave_ibss(struct wiphy *wiphy, struct net_device *dev) 570 { 571 return -EOPNOTSUPP; 572 } 573 574 static int prism2_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, 575 enum nl80211_tx_power_setting type, int mbm) 576 { 577 struct prism2_wiphy_private *priv = wiphy_priv(wiphy); 578 struct wlandevice *wlandev = priv->wlandev; 579 u32 data; 580 int result; 581 int err = 0; 582 583 if (type == NL80211_TX_POWER_AUTOMATIC) 584 data = 30; 585 else 586 data = MBM_TO_DBM(mbm); 587 588 result = prism2_domibset_uint32(wlandev, 589 DIDMIB_DOT11PHY_TXPOWERTABLE_CURRENTTXPOWERLEVEL, 590 data); 591 592 if (result) { 593 err = -EFAULT; 594 goto exit; 595 } 596 597 exit: 598 return err; 599 } 600 601 static int prism2_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, 602 int *dbm) 603 { 604 struct prism2_wiphy_private *priv = wiphy_priv(wiphy); 605 struct wlandevice *wlandev = priv->wlandev; 606 struct p80211msg_dot11req_mibget msg; 607 struct p80211item_uint32 *mibitem; 608 int result; 609 int err = 0; 610 611 mibitem = (struct p80211item_uint32 *)&msg.mibattribute.data; 612 msg.msgcode = DIDMSG_DOT11REQ_MIBGET; 613 mibitem->did = DIDMIB_DOT11PHY_TXPOWERTABLE_CURRENTTXPOWERLEVEL; 614 615 result = p80211req_dorequest(wlandev, (u8 *)&msg); 616 617 if (result) { 618 err = -EFAULT; 619 goto exit; 620 } 621 622 *dbm = mibitem->data; 623 624 exit: 625 return err; 626 } 627 628 /* Interface callback functions, passing data back up to the cfg80211 layer */ 629 void prism2_connect_result(struct wlandevice *wlandev, u8 failed) 630 { 631 u16 status = failed ? 632 WLAN_STATUS_UNSPECIFIED_FAILURE : WLAN_STATUS_SUCCESS; 633 634 cfg80211_connect_result(wlandev->netdev, wlandev->bssid, 635 NULL, 0, NULL, 0, status, GFP_KERNEL); 636 } 637 638 void prism2_disconnected(struct wlandevice *wlandev) 639 { 640 cfg80211_disconnected(wlandev->netdev, 0, NULL, 641 0, false, GFP_KERNEL); 642 } 643 644 void prism2_roamed(struct wlandevice *wlandev) 645 { 646 struct cfg80211_roam_info roam_info = { 647 .bssid = wlandev->bssid, 648 }; 649 650 cfg80211_roamed(wlandev->netdev, &roam_info, GFP_KERNEL); 651 } 652 653 /* Structures for declaring wiphy interface */ 654 static const struct cfg80211_ops prism2_usb_cfg_ops = { 655 .change_virtual_intf = prism2_change_virtual_intf, 656 .add_key = prism2_add_key, 657 .get_key = prism2_get_key, 658 .del_key = prism2_del_key, 659 .set_default_key = prism2_set_default_key, 660 .get_station = prism2_get_station, 661 .scan = prism2_scan, 662 .set_wiphy_params = prism2_set_wiphy_params, 663 .connect = prism2_connect, 664 .disconnect = prism2_disconnect, 665 .join_ibss = prism2_join_ibss, 666 .leave_ibss = prism2_leave_ibss, 667 .set_tx_power = prism2_set_tx_power, 668 .get_tx_power = prism2_get_tx_power, 669 }; 670 671 /* Functions to create/free wiphy interface */ 672 static struct wiphy *wlan_create_wiphy(struct device *dev, 673 struct wlandevice *wlandev) 674 { 675 struct wiphy *wiphy; 676 struct prism2_wiphy_private *priv; 677 678 wiphy = wiphy_new(&prism2_usb_cfg_ops, sizeof(*priv)); 679 if (!wiphy) 680 return NULL; 681 682 priv = wiphy_priv(wiphy); 683 priv->wlandev = wlandev; 684 memcpy(priv->channels, prism2_channels, sizeof(prism2_channels)); 685 memcpy(priv->rates, prism2_rates, sizeof(prism2_rates)); 686 priv->band.channels = priv->channels; 687 priv->band.n_channels = ARRAY_SIZE(prism2_channels); 688 priv->band.bitrates = priv->rates; 689 priv->band.n_bitrates = ARRAY_SIZE(prism2_rates); 690 priv->band.band = NL80211_BAND_2GHZ; 691 priv->band.ht_cap.ht_supported = false; 692 wiphy->bands[NL80211_BAND_2GHZ] = &priv->band; 693 694 set_wiphy_dev(wiphy, dev); 695 wiphy->privid = prism2_wiphy_privid; 696 wiphy->max_scan_ssids = 1; 697 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) 698 | BIT(NL80211_IFTYPE_ADHOC); 699 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; 700 wiphy->n_cipher_suites = PRISM2_NUM_CIPHER_SUITES; 701 wiphy->cipher_suites = prism2_cipher_suites; 702 703 if (wiphy_register(wiphy) < 0) { 704 wiphy_free(wiphy); 705 return NULL; 706 } 707 708 return wiphy; 709 } 710 711 static void wlan_free_wiphy(struct wiphy *wiphy) 712 { 713 wiphy_unregister(wiphy); 714 wiphy_free(wiphy); 715 } 716