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