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 */ 7static 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 24static 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 32static 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 */ 38struct 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 48static const void * const prism2_wiphy_privid = &prism2_wiphy_privid; 49 50/* Helper Functions */ 51static 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 73static 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 87static 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 */ 103static 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 141exit: 142 return err; 143} 144 145static int prism2_add_key(struct wiphy *wiphy, struct net_device *dev, 146 int link_id, u8 key_index, bool pairwise, 147 const u8 *mac_addr, 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 174static int prism2_get_key(struct wiphy *wiphy, struct net_device *dev, 175 int link_id, 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 204static int prism2_del_key(struct wiphy *wiphy, struct net_device *dev, 205 int link_id, u8 key_index, bool pairwise, 206 const u8 *mac_addr) 207{ 208 struct wlandevice *wlandev = dev->ml_priv; 209 u32 did; 210 int err = 0; 211 int result = 0; 212 213 /* There is no direct way in the hardware (AFAIK) of removing 214 * a key, so we will cheat by setting the key to a bogus value 215 */ 216 217 if (key_index >= NUM_WEPKEYS) 218 return -EINVAL; 219 220 /* send key to driver */ 221 did = didmib_dot11smt_wepdefaultkeystable_key(key_index + 1); 222 result = prism2_domibset_pstr32(wlandev, did, 13, "0000000000000"); 223 224 if (result) 225 err = -EFAULT; 226 227 return err; 228} 229 230static int prism2_set_default_key(struct wiphy *wiphy, struct net_device *dev, 231 int link_id, u8 key_index, bool unicast, 232 bool multicast) 233{ 234 struct wlandevice *wlandev = dev->ml_priv; 235 236 return prism2_domibset_uint32(wlandev, 237 DIDMIB_DOT11SMT_PRIVACYTABLE_WEPDEFAULTKEYID, 238 key_index); 239} 240 241static int prism2_get_station(struct wiphy *wiphy, struct net_device *dev, 242 const u8 *mac, struct station_info *sinfo) 243{ 244 struct wlandevice *wlandev = dev->ml_priv; 245 struct p80211msg_lnxreq_commsquality quality; 246 int result; 247 248 memset(sinfo, 0, sizeof(*sinfo)); 249 250 if (!wlandev || (wlandev->msdstate != WLAN_MSD_RUNNING)) 251 return -EOPNOTSUPP; 252 253 /* build request message */ 254 quality.msgcode = DIDMSG_LNXREQ_COMMSQUALITY; 255 quality.dbm.data = P80211ENUM_truth_true; 256 quality.dbm.status = P80211ENUM_msgitem_status_data_ok; 257 258 /* send message to nsd */ 259 if (!wlandev->mlmerequest) 260 return -EOPNOTSUPP; 261 262 result = wlandev->mlmerequest(wlandev, (struct p80211msg *)&quality); 263 264 if (result == 0) { 265 sinfo->txrate.legacy = quality.txrate.data; 266 sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); 267 sinfo->signal = quality.level.data; 268 sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); 269 } 270 271 return result; 272} 273 274static int prism2_scan(struct wiphy *wiphy, 275 struct cfg80211_scan_request *request) 276{ 277 struct net_device *dev; 278 struct prism2_wiphy_private *priv = wiphy_priv(wiphy); 279 struct wlandevice *wlandev; 280 struct p80211msg_dot11req_scan msg1; 281 struct p80211msg_dot11req_scan_results *msg2; 282 struct cfg80211_bss *bss; 283 struct cfg80211_scan_info info = {}; 284 285 int result; 286 int err = 0; 287 int numbss = 0; 288 int i = 0; 289 u8 ie_buf[46]; 290 int ie_len; 291 292 if (!request) 293 return -EINVAL; 294 295 dev = request->wdev->netdev; 296 wlandev = dev->ml_priv; 297 298 if (priv->scan_request && priv->scan_request != request) 299 return -EBUSY; 300 301 if (wlandev->macmode == WLAN_MACMODE_ESS_AP) { 302 netdev_err(dev, "Can't scan in AP mode\n"); 303 return -EOPNOTSUPP; 304 } 305 306 msg2 = kzalloc(sizeof(*msg2), GFP_KERNEL); 307 if (!msg2) 308 return -ENOMEM; 309 310 priv->scan_request = request; 311 312 memset(&msg1, 0x00, sizeof(msg1)); 313 msg1.msgcode = DIDMSG_DOT11REQ_SCAN; 314 msg1.bsstype.data = P80211ENUM_bsstype_any; 315 316 memset(&msg1.bssid.data.data, 0xFF, sizeof(msg1.bssid.data.data)); 317 msg1.bssid.data.len = 6; 318 319 if (request->n_ssids > 0) { 320 msg1.scantype.data = P80211ENUM_scantype_active; 321 msg1.ssid.data.len = request->ssids->ssid_len; 322 memcpy(msg1.ssid.data.data, 323 request->ssids->ssid, request->ssids->ssid_len); 324 } else { 325 msg1.scantype.data = 0; 326 } 327 msg1.probedelay.data = 0; 328 329 for (i = 0; 330 (i < request->n_channels) && i < ARRAY_SIZE(prism2_channels); 331 i++) 332 msg1.channellist.data.data[i] = 333 ieee80211_frequency_to_channel(request->channels[i]->center_freq); 334 msg1.channellist.data.len = request->n_channels; 335 336 msg1.maxchanneltime.data = 250; 337 msg1.minchanneltime.data = 200; 338 339 result = p80211req_dorequest(wlandev, (u8 *)&msg1); 340 if (result) { 341 err = prism2_result2err(msg1.resultcode.data); 342 goto exit; 343 } 344 /* Now retrieve scan results */ 345 numbss = msg1.numbss.data; 346 347 for (i = 0; i < numbss; i++) { 348 int freq; 349 350 msg2->msgcode = DIDMSG_DOT11REQ_SCAN_RESULTS; 351 msg2->bssindex.data = i; 352 353 result = p80211req_dorequest(wlandev, (u8 *)&msg2); 354 if ((result != 0) || 355 (msg2->resultcode.data != P80211ENUM_resultcode_success)) { 356 break; 357 } 358 359 ie_buf[0] = WLAN_EID_SSID; 360 ie_buf[1] = msg2->ssid.data.len; 361 ie_len = ie_buf[1] + 2; 362 memcpy(&ie_buf[2], &msg2->ssid.data.data, msg2->ssid.data.len); 363 freq = ieee80211_channel_to_frequency(msg2->dschannel.data, 364 NL80211_BAND_2GHZ); 365 bss = cfg80211_inform_bss(wiphy, 366 ieee80211_get_channel(wiphy, freq), 367 CFG80211_BSS_FTYPE_UNKNOWN, 368 (const u8 *)&msg2->bssid.data.data, 369 msg2->timestamp.data, msg2->capinfo.data, 370 msg2->beaconperiod.data, 371 ie_buf, 372 ie_len, 373 (msg2->signal.data - 65536) * 100, /* Conversion to signed type */ 374 GFP_KERNEL); 375 376 if (!bss) { 377 err = -ENOMEM; 378 goto exit; 379 } 380 381 cfg80211_put_bss(wiphy, bss); 382 } 383 384 if (result) 385 err = prism2_result2err(msg2->resultcode.data); 386 387exit: 388 info.aborted = !!(err); 389 cfg80211_scan_done(request, &info); 390 priv->scan_request = NULL; 391 kfree(msg2); 392 return err; 393} 394 395static int prism2_set_wiphy_params(struct wiphy *wiphy, u32 changed) 396{ 397 struct prism2_wiphy_private *priv = wiphy_priv(wiphy); 398 struct wlandevice *wlandev = priv->wlandev; 399 u32 data; 400 int result; 401 int err = 0; 402 403 if (changed & WIPHY_PARAM_RTS_THRESHOLD) { 404 if (wiphy->rts_threshold == -1) 405 data = 2347; 406 else 407 data = wiphy->rts_threshold; 408 409 result = prism2_domibset_uint32(wlandev, 410 DIDMIB_DOT11MAC_OPERATIONTABLE_RTSTHRESHOLD, 411 data); 412 if (result) { 413 err = -EFAULT; 414 goto exit; 415 } 416 } 417 418 if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { 419 if (wiphy->frag_threshold == -1) 420 data = 2346; 421 else 422 data = wiphy->frag_threshold; 423 424 result = prism2_domibset_uint32(wlandev, 425 DIDMIB_DOT11MAC_OPERATIONTABLE_FRAGMENTATIONTHRESHOLD, 426 data); 427 if (result) { 428 err = -EFAULT; 429 goto exit; 430 } 431 } 432 433exit: 434 return err; 435} 436 437static int prism2_connect(struct wiphy *wiphy, struct net_device *dev, 438 struct cfg80211_connect_params *sme) 439{ 440 struct wlandevice *wlandev = dev->ml_priv; 441 struct ieee80211_channel *channel = sme->channel; 442 struct p80211msg_lnxreq_autojoin msg_join; 443 u32 did; 444 int length = sme->ssid_len; 445 int chan = -1; 446 int is_wep = (sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP40) || 447 (sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP104); 448 int result; 449 int err = 0; 450 451 /* Set the channel */ 452 if (channel) { 453 chan = ieee80211_frequency_to_channel(channel->center_freq); 454 result = prism2_domibset_uint32(wlandev, 455 DIDMIB_DOT11PHY_DSSSTABLE_CURRENTCHANNEL, 456 chan); 457 if (result) 458 goto exit; 459 } 460 461 /* Set the authorization */ 462 if ((sme->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) || 463 ((sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) && !is_wep)) 464 msg_join.authtype.data = P80211ENUM_authalg_opensystem; 465 else if ((sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY) || 466 ((sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) && is_wep)) 467 msg_join.authtype.data = P80211ENUM_authalg_sharedkey; 468 else 469 netdev_warn(dev, 470 "Unhandled authorisation type for connect (%d)\n", 471 sme->auth_type); 472 473 /* Set the encryption - we only support wep */ 474 if (is_wep) { 475 if (sme->key) { 476 if (sme->key_idx >= NUM_WEPKEYS) 477 return -EINVAL; 478 479 result = prism2_domibset_uint32(wlandev, 480 DIDMIB_DOT11SMT_PRIVACYTABLE_WEPDEFAULTKEYID, 481 sme->key_idx); 482 if (result) 483 goto exit; 484 485 /* send key to driver */ 486 did = didmib_dot11smt_wepdefaultkeystable_key(sme->key_idx + 1); 487 result = prism2_domibset_pstr32(wlandev, 488 did, sme->key_len, 489 (u8 *)sme->key); 490 if (result) 491 goto exit; 492 } 493 494 /* Assume we should set privacy invoked and exclude unencrypted 495 * We could possible use sme->privacy here, but the assumption 496 * seems reasonable anyways 497 */ 498 result = prism2_domibset_uint32(wlandev, 499 DIDMIB_DOT11SMT_PRIVACYTABLE_PRIVACYINVOKED, 500 P80211ENUM_truth_true); 501 if (result) 502 goto exit; 503 504 result = prism2_domibset_uint32(wlandev, 505 DIDMIB_DOT11SMT_PRIVACYTABLE_EXCLUDEUNENCRYPTED, 506 P80211ENUM_truth_true); 507 if (result) 508 goto exit; 509 510 } else { 511 /* Assume we should unset privacy invoked 512 * and exclude unencrypted 513 */ 514 result = prism2_domibset_uint32(wlandev, 515 DIDMIB_DOT11SMT_PRIVACYTABLE_PRIVACYINVOKED, 516 P80211ENUM_truth_false); 517 if (result) 518 goto exit; 519 520 result = prism2_domibset_uint32(wlandev, 521 DIDMIB_DOT11SMT_PRIVACYTABLE_EXCLUDEUNENCRYPTED, 522 P80211ENUM_truth_false); 523 if (result) 524 goto exit; 525 } 526 527 /* Now do the actual join. Note there is no way that I can 528 * see to request a specific bssid 529 */ 530 msg_join.msgcode = DIDMSG_LNXREQ_AUTOJOIN; 531 532 memcpy(msg_join.ssid.data.data, sme->ssid, length); 533 msg_join.ssid.data.len = length; 534 535 result = p80211req_dorequest(wlandev, (u8 *)&msg_join); 536 537exit: 538 if (result) 539 err = -EFAULT; 540 541 return err; 542} 543 544static int prism2_disconnect(struct wiphy *wiphy, struct net_device *dev, 545 u16 reason_code) 546{ 547 struct wlandevice *wlandev = dev->ml_priv; 548 struct p80211msg_lnxreq_autojoin msg_join; 549 int result; 550 int err = 0; 551 552 /* Do a join, with a bogus ssid. Thats the only way I can think of */ 553 msg_join.msgcode = DIDMSG_LNXREQ_AUTOJOIN; 554 555 memcpy(msg_join.ssid.data.data, "---", 3); 556 msg_join.ssid.data.len = 3; 557 558 result = p80211req_dorequest(wlandev, (u8 *)&msg_join); 559 560 if (result) 561 err = -EFAULT; 562 563 return err; 564} 565 566static int prism2_join_ibss(struct wiphy *wiphy, struct net_device *dev, 567 struct cfg80211_ibss_params *params) 568{ 569 return -EOPNOTSUPP; 570} 571 572static int prism2_leave_ibss(struct wiphy *wiphy, struct net_device *dev) 573{ 574 return -EOPNOTSUPP; 575} 576 577static int prism2_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, 578 enum nl80211_tx_power_setting type, int mbm) 579{ 580 struct prism2_wiphy_private *priv = wiphy_priv(wiphy); 581 struct wlandevice *wlandev = priv->wlandev; 582 u32 data; 583 int result; 584 int err = 0; 585 586 if (type == NL80211_TX_POWER_AUTOMATIC) 587 data = 30; 588 else 589 data = MBM_TO_DBM(mbm); 590 591 result = prism2_domibset_uint32(wlandev, 592 DIDMIB_DOT11PHY_TXPOWERTABLE_CURRENTTXPOWERLEVEL, 593 data); 594 595 if (result) { 596 err = -EFAULT; 597 goto exit; 598 } 599 600exit: 601 return err; 602} 603 604static int prism2_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, 605 int *dbm) 606{ 607 struct prism2_wiphy_private *priv = wiphy_priv(wiphy); 608 struct wlandevice *wlandev = priv->wlandev; 609 struct p80211msg_dot11req_mibget msg; 610 struct p80211item_uint32 *mibitem; 611 int result; 612 int err = 0; 613 614 mibitem = (struct p80211item_uint32 *)&msg.mibattribute.data; 615 msg.msgcode = DIDMSG_DOT11REQ_MIBGET; 616 mibitem->did = DIDMIB_DOT11PHY_TXPOWERTABLE_CURRENTTXPOWERLEVEL; 617 618 result = p80211req_dorequest(wlandev, (u8 *)&msg); 619 620 if (result) { 621 err = -EFAULT; 622 goto exit; 623 } 624 625 *dbm = mibitem->data; 626 627exit: 628 return err; 629} 630 631/* Interface callback functions, passing data back up to the cfg80211 layer */ 632void prism2_connect_result(struct wlandevice *wlandev, u8 failed) 633{ 634 u16 status = failed ? 635 WLAN_STATUS_UNSPECIFIED_FAILURE : WLAN_STATUS_SUCCESS; 636 637 cfg80211_connect_result(wlandev->netdev, wlandev->bssid, 638 NULL, 0, NULL, 0, status, GFP_KERNEL); 639} 640 641void prism2_disconnected(struct wlandevice *wlandev) 642{ 643 cfg80211_disconnected(wlandev->netdev, 0, NULL, 644 0, false, GFP_KERNEL); 645} 646 647void prism2_roamed(struct wlandevice *wlandev) 648{ 649 struct cfg80211_roam_info roam_info = { 650 .links[0].bssid = wlandev->bssid, 651 }; 652 653 cfg80211_roamed(wlandev->netdev, &roam_info, GFP_KERNEL); 654} 655 656/* Structures for declaring wiphy interface */ 657static const struct cfg80211_ops prism2_usb_cfg_ops = { 658 .change_virtual_intf = prism2_change_virtual_intf, 659 .add_key = prism2_add_key, 660 .get_key = prism2_get_key, 661 .del_key = prism2_del_key, 662 .set_default_key = prism2_set_default_key, 663 .get_station = prism2_get_station, 664 .scan = prism2_scan, 665 .set_wiphy_params = prism2_set_wiphy_params, 666 .connect = prism2_connect, 667 .disconnect = prism2_disconnect, 668 .join_ibss = prism2_join_ibss, 669 .leave_ibss = prism2_leave_ibss, 670 .set_tx_power = prism2_set_tx_power, 671 .get_tx_power = prism2_get_tx_power, 672}; 673 674/* Functions to create/free wiphy interface */ 675static struct wiphy *wlan_create_wiphy(struct device *dev, 676 struct wlandevice *wlandev) 677{ 678 struct wiphy *wiphy; 679 struct prism2_wiphy_private *priv; 680 681 wiphy = wiphy_new(&prism2_usb_cfg_ops, sizeof(*priv)); 682 if (!wiphy) 683 return NULL; 684 685 priv = wiphy_priv(wiphy); 686 priv->wlandev = wlandev; 687 memcpy(priv->channels, prism2_channels, sizeof(prism2_channels)); 688 memcpy(priv->rates, prism2_rates, sizeof(prism2_rates)); 689 priv->band.channels = priv->channels; 690 priv->band.n_channels = ARRAY_SIZE(prism2_channels); 691 priv->band.bitrates = priv->rates; 692 priv->band.n_bitrates = ARRAY_SIZE(prism2_rates); 693 priv->band.band = NL80211_BAND_2GHZ; 694 priv->band.ht_cap.ht_supported = false; 695 wiphy->bands[NL80211_BAND_2GHZ] = &priv->band; 696 697 set_wiphy_dev(wiphy, dev); 698 wiphy->privid = prism2_wiphy_privid; 699 wiphy->max_scan_ssids = 1; 700 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) 701 | BIT(NL80211_IFTYPE_ADHOC); 702 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; 703 wiphy->n_cipher_suites = PRISM2_NUM_CIPHER_SUITES; 704 wiphy->cipher_suites = prism2_cipher_suites; 705 706 if (wiphy_register(wiphy) < 0) { 707 wiphy_free(wiphy); 708 return NULL; 709 } 710 711 return wiphy; 712} 713 714static void wlan_free_wiphy(struct wiphy *wiphy) 715{ 716 wiphy_unregister(wiphy); 717 wiphy_free(wiphy); 718} 719