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,
  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                          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
 174static 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(&params, 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, &params);
 200
 201        return 0;
 202}
 203
 204static 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
 229static 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
 239static 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
 272static 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
 384exit:
 385        info.aborted = !!(err);
 386        cfg80211_scan_done(request, &info);
 387        priv->scan_request = NULL;
 388        return err;
 389}
 390
 391static 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
 429exit:
 430        return err;
 431}
 432
 433static 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                                err = -EINVAL;
 474                                goto exit;
 475                        }
 476
 477                        result = prism2_domibset_uint32(wlandev,
 478                                DIDMIB_DOT11SMT_PRIVACYTABLE_WEPDEFAULTKEYID,
 479                                sme->key_idx);
 480                        if (result)
 481                                goto exit;
 482
 483                        /* send key to driver */
 484                        did = didmib_dot11smt_wepdefaultkeystable_key(
 485                                        sme->key_idx + 1);
 486                        result = prism2_domibset_pstr32(wlandev,
 487                                                        did, sme->key_len,
 488                                                        (u8 *)sme->key);
 489                        if (result)
 490                                goto exit;
 491                }
 492
 493                /* Assume we should set privacy invoked and exclude unencrypted
 494                 * We could possible use sme->privacy here, but the assumption
 495                 * seems reasonable anyways
 496                 */
 497                result = prism2_domibset_uint32(wlandev,
 498                                                DIDMIB_DOT11SMT_PRIVACYTABLE_PRIVACYINVOKED,
 499                                                P80211ENUM_truth_true);
 500                if (result)
 501                        goto exit;
 502
 503                result = prism2_domibset_uint32(wlandev,
 504                                                DIDMIB_DOT11SMT_PRIVACYTABLE_EXCLUDEUNENCRYPTED,
 505                                                P80211ENUM_truth_true);
 506                if (result)
 507                        goto exit;
 508
 509        } else {
 510                /* Assume we should unset privacy invoked
 511                 * and exclude unencrypted
 512                 */
 513                result = prism2_domibset_uint32(wlandev,
 514                                                DIDMIB_DOT11SMT_PRIVACYTABLE_PRIVACYINVOKED,
 515                                                P80211ENUM_truth_false);
 516                if (result)
 517                        goto exit;
 518
 519                result = prism2_domibset_uint32(wlandev,
 520                                                DIDMIB_DOT11SMT_PRIVACYTABLE_EXCLUDEUNENCRYPTED,
 521                                                P80211ENUM_truth_false);
 522                if (result)
 523                        goto exit;
 524        }
 525
 526        /* Now do the actual join. Note there is no way that I can
 527         * see to request a specific bssid
 528         */
 529        msg_join.msgcode = DIDMSG_LNXREQ_AUTOJOIN;
 530
 531        memcpy(msg_join.ssid.data.data, sme->ssid, length);
 532        msg_join.ssid.data.len = length;
 533
 534        result = p80211req_dorequest(wlandev, (u8 *)&msg_join);
 535
 536exit:
 537        if (result)
 538                err = -EFAULT;
 539
 540        return err;
 541}
 542
 543static int prism2_disconnect(struct wiphy *wiphy, struct net_device *dev,
 544                             u16 reason_code)
 545{
 546        struct wlandevice *wlandev = dev->ml_priv;
 547        struct p80211msg_lnxreq_autojoin msg_join;
 548        int result;
 549        int err = 0;
 550
 551        /* Do a join, with a bogus ssid. Thats the only way I can think of */
 552        msg_join.msgcode = DIDMSG_LNXREQ_AUTOJOIN;
 553
 554        memcpy(msg_join.ssid.data.data, "---", 3);
 555        msg_join.ssid.data.len = 3;
 556
 557        result = p80211req_dorequest(wlandev, (u8 *)&msg_join);
 558
 559        if (result)
 560                err = -EFAULT;
 561
 562        return err;
 563}
 564
 565static int prism2_join_ibss(struct wiphy *wiphy, struct net_device *dev,
 566                            struct cfg80211_ibss_params *params)
 567{
 568        return -EOPNOTSUPP;
 569}
 570
 571static int prism2_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
 572{
 573        return -EOPNOTSUPP;
 574}
 575
 576static int prism2_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
 577                               enum nl80211_tx_power_setting type, int mbm)
 578{
 579        struct prism2_wiphy_private *priv = wiphy_priv(wiphy);
 580        struct wlandevice *wlandev = priv->wlandev;
 581        u32 data;
 582        int result;
 583        int err = 0;
 584
 585        if (type == NL80211_TX_POWER_AUTOMATIC)
 586                data = 30;
 587        else
 588                data = MBM_TO_DBM(mbm);
 589
 590        result = prism2_domibset_uint32(wlandev,
 591                DIDMIB_DOT11PHY_TXPOWERTABLE_CURRENTTXPOWERLEVEL,
 592                data);
 593
 594        if (result) {
 595                err = -EFAULT;
 596                goto exit;
 597        }
 598
 599exit:
 600        return err;
 601}
 602
 603static int prism2_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
 604                               int *dbm)
 605{
 606        struct prism2_wiphy_private *priv = wiphy_priv(wiphy);
 607        struct wlandevice *wlandev = priv->wlandev;
 608        struct p80211msg_dot11req_mibget msg;
 609        struct p80211item_uint32 *mibitem;
 610        int result;
 611        int err = 0;
 612
 613        mibitem = (struct p80211item_uint32 *)&msg.mibattribute.data;
 614        msg.msgcode = DIDMSG_DOT11REQ_MIBGET;
 615        mibitem->did = DIDMIB_DOT11PHY_TXPOWERTABLE_CURRENTTXPOWERLEVEL;
 616
 617        result = p80211req_dorequest(wlandev, (u8 *)&msg);
 618
 619        if (result) {
 620                err = -EFAULT;
 621                goto exit;
 622        }
 623
 624        *dbm = mibitem->data;
 625
 626exit:
 627        return err;
 628}
 629
 630/* Interface callback functions, passing data back up to the cfg80211 layer */
 631void prism2_connect_result(struct wlandevice *wlandev, u8 failed)
 632{
 633        u16 status = failed ?
 634                     WLAN_STATUS_UNSPECIFIED_FAILURE : WLAN_STATUS_SUCCESS;
 635
 636        cfg80211_connect_result(wlandev->netdev, wlandev->bssid,
 637                                NULL, 0, NULL, 0, status, GFP_KERNEL);
 638}
 639
 640void prism2_disconnected(struct wlandevice *wlandev)
 641{
 642        cfg80211_disconnected(wlandev->netdev, 0, NULL,
 643                              0, false, GFP_KERNEL);
 644}
 645
 646void prism2_roamed(struct wlandevice *wlandev)
 647{
 648        struct cfg80211_roam_info roam_info = {
 649                .bssid = wlandev->bssid,
 650        };
 651
 652        cfg80211_roamed(wlandev->netdev, &roam_info, GFP_KERNEL);
 653}
 654
 655/* Structures for declaring wiphy interface */
 656static const struct cfg80211_ops prism2_usb_cfg_ops = {
 657        .change_virtual_intf = prism2_change_virtual_intf,
 658        .add_key = prism2_add_key,
 659        .get_key = prism2_get_key,
 660        .del_key = prism2_del_key,
 661        .set_default_key = prism2_set_default_key,
 662        .get_station = prism2_get_station,
 663        .scan = prism2_scan,
 664        .set_wiphy_params = prism2_set_wiphy_params,
 665        .connect = prism2_connect,
 666        .disconnect = prism2_disconnect,
 667        .join_ibss = prism2_join_ibss,
 668        .leave_ibss = prism2_leave_ibss,
 669        .set_tx_power = prism2_set_tx_power,
 670        .get_tx_power = prism2_get_tx_power,
 671};
 672
 673/* Functions to create/free wiphy interface */
 674static struct wiphy *wlan_create_wiphy(struct device *dev,
 675                                       struct wlandevice *wlandev)
 676{
 677        struct wiphy *wiphy;
 678        struct prism2_wiphy_private *priv;
 679
 680        wiphy = wiphy_new(&prism2_usb_cfg_ops, sizeof(*priv));
 681        if (!wiphy)
 682                return NULL;
 683
 684        priv = wiphy_priv(wiphy);
 685        priv->wlandev = wlandev;
 686        memcpy(priv->channels, prism2_channels, sizeof(prism2_channels));
 687        memcpy(priv->rates, prism2_rates, sizeof(prism2_rates));
 688        priv->band.channels = priv->channels;
 689        priv->band.n_channels = ARRAY_SIZE(prism2_channels);
 690        priv->band.bitrates = priv->rates;
 691        priv->band.n_bitrates = ARRAY_SIZE(prism2_rates);
 692        priv->band.band = NL80211_BAND_2GHZ;
 693        priv->band.ht_cap.ht_supported = false;
 694        wiphy->bands[NL80211_BAND_2GHZ] = &priv->band;
 695
 696        set_wiphy_dev(wiphy, dev);
 697        wiphy->privid = prism2_wiphy_privid;
 698        wiphy->max_scan_ssids = 1;
 699        wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION)
 700                                 | BIT(NL80211_IFTYPE_ADHOC);
 701        wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
 702        wiphy->n_cipher_suites = PRISM2_NUM_CIPHER_SUITES;
 703        wiphy->cipher_suites = prism2_cipher_suites;
 704
 705        if (wiphy_register(wiphy) < 0) {
 706                wiphy_free(wiphy);
 707                return NULL;
 708        }
 709
 710        return wiphy;
 711}
 712
 713static void wlan_free_wiphy(struct wiphy *wiphy)
 714{
 715        wiphy_unregister(wiphy);
 716        wiphy_free(wiphy);
 717}
 718