linux/drivers/net/wireless/quantenna/qtnfmac/event.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
   3
   4#include <linux/kernel.h>
   5#include <linux/module.h>
   6#include <linux/slab.h>
   7
   8#include "cfg80211.h"
   9#include "core.h"
  10#include "qlink.h"
  11#include "bus.h"
  12#include "trans.h"
  13#include "util.h"
  14#include "event.h"
  15#include "qlink_util.h"
  16
  17static int
  18qtnf_event_handle_sta_assoc(struct qtnf_wmac *mac, struct qtnf_vif *vif,
  19                            const struct qlink_event_sta_assoc *sta_assoc,
  20                            u16 len)
  21{
  22        const u8 *sta_addr;
  23        u16 frame_control;
  24        struct station_info *sinfo;
  25        size_t payload_len;
  26        u16 tlv_type;
  27        u16 tlv_value_len;
  28        size_t tlv_full_len;
  29        const struct qlink_tlv_hdr *tlv;
  30        int ret = 0;
  31
  32        if (unlikely(len < sizeof(*sta_assoc))) {
  33                pr_err("VIF%u.%u: payload is too short (%u < %zu)\n",
  34                       mac->macid, vif->vifid, len, sizeof(*sta_assoc));
  35                return -EINVAL;
  36        }
  37
  38        if (vif->wdev.iftype != NL80211_IFTYPE_AP) {
  39                pr_err("VIF%u.%u: STA_ASSOC event when not in AP mode\n",
  40                       mac->macid, vif->vifid);
  41                return -EPROTO;
  42        }
  43
  44        sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
  45        if (!sinfo)
  46                return -ENOMEM;
  47
  48        sta_addr = sta_assoc->sta_addr;
  49        frame_control = le16_to_cpu(sta_assoc->frame_control);
  50
  51        pr_debug("VIF%u.%u: MAC:%pM FC:%x\n", mac->macid, vif->vifid, sta_addr,
  52                 frame_control);
  53
  54        qtnf_sta_list_add(vif, sta_addr);
  55
  56        sinfo->assoc_req_ies = NULL;
  57        sinfo->assoc_req_ies_len = 0;
  58        sinfo->generation = vif->generation;
  59
  60        payload_len = len - sizeof(*sta_assoc);
  61        tlv = (const struct qlink_tlv_hdr *)sta_assoc->ies;
  62
  63        while (payload_len >= sizeof(*tlv)) {
  64                tlv_type = le16_to_cpu(tlv->type);
  65                tlv_value_len = le16_to_cpu(tlv->len);
  66                tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr);
  67
  68                if (tlv_full_len > payload_len) {
  69                        ret = -EINVAL;
  70                        goto out;
  71                }
  72
  73                if (tlv_type == QTN_TLV_ID_IE_SET) {
  74                        const struct qlink_tlv_ie_set *ie_set;
  75                        unsigned int ie_len;
  76
  77                        if (payload_len < sizeof(*ie_set)) {
  78                                ret = -EINVAL;
  79                                goto out;
  80                        }
  81
  82                        ie_set = (const struct qlink_tlv_ie_set *)tlv;
  83                        ie_len = tlv_value_len -
  84                                (sizeof(*ie_set) - sizeof(ie_set->hdr));
  85
  86                        if (ie_set->type == QLINK_IE_SET_ASSOC_REQ && ie_len) {
  87                                sinfo->assoc_req_ies = ie_set->ie_data;
  88                                sinfo->assoc_req_ies_len = ie_len;
  89                        }
  90                }
  91
  92                payload_len -= tlv_full_len;
  93                tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len);
  94        }
  95
  96        if (payload_len) {
  97                ret = -EINVAL;
  98                goto out;
  99        }
 100
 101        cfg80211_new_sta(vif->netdev, sta_assoc->sta_addr, sinfo,
 102                         GFP_KERNEL);
 103
 104out:
 105        kfree(sinfo);
 106        return ret;
 107}
 108
 109static int
 110qtnf_event_handle_sta_deauth(struct qtnf_wmac *mac, struct qtnf_vif *vif,
 111                             const struct qlink_event_sta_deauth *sta_deauth,
 112                             u16 len)
 113{
 114        const u8 *sta_addr;
 115        u16 reason;
 116
 117        if (unlikely(len < sizeof(*sta_deauth))) {
 118                pr_err("VIF%u.%u: payload is too short (%u < %zu)\n",
 119                       mac->macid, vif->vifid, len,
 120                       sizeof(struct qlink_event_sta_deauth));
 121                return -EINVAL;
 122        }
 123
 124        if (vif->wdev.iftype != NL80211_IFTYPE_AP) {
 125                pr_err("VIF%u.%u: STA_DEAUTH event when not in AP mode\n",
 126                       mac->macid, vif->vifid);
 127                return -EPROTO;
 128        }
 129
 130        sta_addr = sta_deauth->sta_addr;
 131        reason = le16_to_cpu(sta_deauth->reason);
 132
 133        pr_debug("VIF%u.%u: MAC:%pM reason:%x\n", mac->macid, vif->vifid,
 134                 sta_addr, reason);
 135
 136        if (qtnf_sta_list_del(vif, sta_addr))
 137                cfg80211_del_sta(vif->netdev, sta_deauth->sta_addr,
 138                                 GFP_KERNEL);
 139
 140        return 0;
 141}
 142
 143static int
 144qtnf_event_handle_bss_join(struct qtnf_vif *vif,
 145                           const struct qlink_event_bss_join *join_info,
 146                           u16 len)
 147{
 148        struct wiphy *wiphy = priv_to_wiphy(vif->mac);
 149        enum ieee80211_statuscode status = le16_to_cpu(join_info->status);
 150        struct cfg80211_chan_def chandef;
 151        struct cfg80211_bss *bss = NULL;
 152        u8 *ie = NULL;
 153        size_t payload_len;
 154        u16 tlv_type;
 155        u16 tlv_value_len;
 156        size_t tlv_full_len;
 157        const struct qlink_tlv_hdr *tlv;
 158        const u8 *rsp_ies = NULL;
 159        size_t rsp_ies_len = 0;
 160
 161        if (unlikely(len < sizeof(*join_info))) {
 162                pr_err("VIF%u.%u: payload is too short (%u < %zu)\n",
 163                       vif->mac->macid, vif->vifid, len,
 164                       sizeof(struct qlink_event_bss_join));
 165                return -EINVAL;
 166        }
 167
 168        if (vif->wdev.iftype != NL80211_IFTYPE_STATION) {
 169                pr_err("VIF%u.%u: BSS_JOIN event when not in STA mode\n",
 170                       vif->mac->macid, vif->vifid);
 171                return -EPROTO;
 172        }
 173
 174        pr_debug("VIF%u.%u: BSSID:%pM status:%u\n",
 175                 vif->mac->macid, vif->vifid, join_info->bssid, status);
 176
 177        if (status != WLAN_STATUS_SUCCESS)
 178                goto done;
 179
 180        qlink_chandef_q2cfg(wiphy, &join_info->chan, &chandef);
 181        if (!cfg80211_chandef_valid(&chandef)) {
 182                pr_warn("MAC%u.%u: bad channel freq=%u cf1=%u cf2=%u bw=%u\n",
 183                        vif->mac->macid, vif->vifid,
 184                        chandef.chan->center_freq,
 185                        chandef.center_freq1,
 186                        chandef.center_freq2,
 187                        chandef.width);
 188                status = WLAN_STATUS_UNSPECIFIED_FAILURE;
 189                goto done;
 190        }
 191
 192        bss = cfg80211_get_bss(wiphy, chandef.chan, join_info->bssid,
 193                               NULL, 0, IEEE80211_BSS_TYPE_ESS,
 194                               IEEE80211_PRIVACY_ANY);
 195        if (!bss) {
 196                pr_warn("VIF%u.%u: add missing BSS:%pM chan:%u\n",
 197                        vif->mac->macid, vif->vifid,
 198                        join_info->bssid, chandef.chan->hw_value);
 199
 200                if (!vif->wdev.ssid_len) {
 201                        pr_warn("VIF%u.%u: SSID unknown for BSS:%pM\n",
 202                                vif->mac->macid, vif->vifid,
 203                                join_info->bssid);
 204                        status = WLAN_STATUS_UNSPECIFIED_FAILURE;
 205                        goto done;
 206                }
 207
 208                ie = kzalloc(2 + vif->wdev.ssid_len, GFP_KERNEL);
 209                if (!ie) {
 210                        pr_warn("VIF%u.%u: IE alloc failed for BSS:%pM\n",
 211                                vif->mac->macid, vif->vifid,
 212                                join_info->bssid);
 213                        status = WLAN_STATUS_UNSPECIFIED_FAILURE;
 214                        goto done;
 215                }
 216
 217                ie[0] = WLAN_EID_SSID;
 218                ie[1] = vif->wdev.ssid_len;
 219                memcpy(ie + 2, vif->wdev.ssid, vif->wdev.ssid_len);
 220
 221                bss = cfg80211_inform_bss(wiphy, chandef.chan,
 222                                          CFG80211_BSS_FTYPE_UNKNOWN,
 223                                          join_info->bssid, 0,
 224                                          WLAN_CAPABILITY_ESS, 100,
 225                                          ie, 2 + vif->wdev.ssid_len,
 226                                          0, GFP_KERNEL);
 227                if (!bss) {
 228                        pr_warn("VIF%u.%u: can't connect to unknown BSS: %pM\n",
 229                                vif->mac->macid, vif->vifid,
 230                                join_info->bssid);
 231                        status = WLAN_STATUS_UNSPECIFIED_FAILURE;
 232                        goto done;
 233                }
 234        }
 235
 236        payload_len = len - sizeof(*join_info);
 237        tlv = (struct qlink_tlv_hdr *)join_info->ies;
 238
 239        while (payload_len >= sizeof(struct qlink_tlv_hdr)) {
 240                tlv_type = le16_to_cpu(tlv->type);
 241                tlv_value_len = le16_to_cpu(tlv->len);
 242                tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr);
 243
 244                if (payload_len < tlv_full_len) {
 245                        pr_warn("invalid %u TLV\n", tlv_type);
 246                        status = WLAN_STATUS_UNSPECIFIED_FAILURE;
 247                        goto done;
 248                }
 249
 250                if (tlv_type == QTN_TLV_ID_IE_SET) {
 251                        const struct qlink_tlv_ie_set *ie_set;
 252                        unsigned int ie_len;
 253
 254                        if (payload_len < sizeof(*ie_set)) {
 255                                pr_warn("invalid IE_SET TLV\n");
 256                                status = WLAN_STATUS_UNSPECIFIED_FAILURE;
 257                                goto done;
 258                        }
 259
 260                        ie_set = (const struct qlink_tlv_ie_set *)tlv;
 261                        ie_len = tlv_value_len -
 262                                (sizeof(*ie_set) - sizeof(ie_set->hdr));
 263
 264                        switch (ie_set->type) {
 265                        case QLINK_IE_SET_ASSOC_RESP:
 266                                if (ie_len) {
 267                                        rsp_ies = ie_set->ie_data;
 268                                        rsp_ies_len = ie_len;
 269                                }
 270                                break;
 271                        default:
 272                                pr_warn("unexpected IE type: %u\n",
 273                                        ie_set->type);
 274                                break;
 275                        }
 276                }
 277
 278                payload_len -= tlv_full_len;
 279                tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len);
 280        }
 281
 282        if (payload_len)
 283                pr_warn("VIF%u.%u: unexpected remaining payload: %zu\n",
 284                        vif->mac->macid, vif->vifid, payload_len);
 285
 286done:
 287        cfg80211_connect_result(vif->netdev, join_info->bssid, NULL, 0, rsp_ies,
 288                                rsp_ies_len, status, GFP_KERNEL);
 289        if (bss) {
 290                if (!ether_addr_equal(vif->bssid, join_info->bssid))
 291                        ether_addr_copy(vif->bssid, join_info->bssid);
 292                cfg80211_put_bss(wiphy, bss);
 293        }
 294
 295        if (status == WLAN_STATUS_SUCCESS)
 296                netif_carrier_on(vif->netdev);
 297
 298        kfree(ie);
 299        return 0;
 300}
 301
 302static int
 303qtnf_event_handle_bss_leave(struct qtnf_vif *vif,
 304                            const struct qlink_event_bss_leave *leave_info,
 305                            u16 len)
 306{
 307        if (unlikely(len < sizeof(*leave_info))) {
 308                pr_err("VIF%u.%u: payload is too short (%u < %zu)\n",
 309                       vif->mac->macid, vif->vifid, len,
 310                       sizeof(struct qlink_event_bss_leave));
 311                return -EINVAL;
 312        }
 313
 314        if (vif->wdev.iftype != NL80211_IFTYPE_STATION) {
 315                pr_err("VIF%u.%u: BSS_LEAVE event when not in STA mode\n",
 316                       vif->mac->macid, vif->vifid);
 317                return -EPROTO;
 318        }
 319
 320        pr_debug("VIF%u.%u: disconnected\n", vif->mac->macid, vif->vifid);
 321
 322        cfg80211_disconnected(vif->netdev, le16_to_cpu(leave_info->reason),
 323                              NULL, 0, 0, GFP_KERNEL);
 324        netif_carrier_off(vif->netdev);
 325
 326        return 0;
 327}
 328
 329static int
 330qtnf_event_handle_mgmt_received(struct qtnf_vif *vif,
 331                                const struct qlink_event_rxmgmt *rxmgmt,
 332                                u16 len)
 333{
 334        const size_t min_len = sizeof(*rxmgmt) +
 335                               sizeof(struct ieee80211_hdr_3addr);
 336        const struct ieee80211_hdr_3addr *frame = (void *)rxmgmt->frame_data;
 337        const u16 frame_len = len - sizeof(*rxmgmt);
 338        enum nl80211_rxmgmt_flags flags = 0;
 339
 340        if (unlikely(len < min_len)) {
 341                pr_err("VIF%u.%u: payload is too short (%u < %zu)\n",
 342                       vif->mac->macid, vif->vifid, len, min_len);
 343                return -EINVAL;
 344        }
 345
 346        if (le32_to_cpu(rxmgmt->flags) & QLINK_RXMGMT_FLAG_ANSWERED)
 347                flags |= NL80211_RXMGMT_FLAG_ANSWERED;
 348
 349        pr_debug("%s LEN:%u FC:%.4X SA:%pM\n", vif->netdev->name, frame_len,
 350                 le16_to_cpu(frame->frame_control), frame->addr2);
 351
 352        cfg80211_rx_mgmt(&vif->wdev, le32_to_cpu(rxmgmt->freq), rxmgmt->sig_dbm,
 353                         rxmgmt->frame_data, frame_len, flags);
 354
 355        return 0;
 356}
 357
 358static int
 359qtnf_event_handle_scan_results(struct qtnf_vif *vif,
 360                               const struct qlink_event_scan_result *sr,
 361                               u16 len)
 362{
 363        struct cfg80211_bss *bss;
 364        struct ieee80211_channel *channel;
 365        struct wiphy *wiphy = priv_to_wiphy(vif->mac);
 366        enum cfg80211_bss_frame_type frame_type = CFG80211_BSS_FTYPE_UNKNOWN;
 367        size_t payload_len;
 368        u16 tlv_type;
 369        u16 tlv_value_len;
 370        size_t tlv_full_len;
 371        const struct qlink_tlv_hdr *tlv;
 372        const u8 *ies = NULL;
 373        size_t ies_len = 0;
 374
 375        if (len < sizeof(*sr)) {
 376                pr_err("VIF%u.%u: payload is too short\n", vif->mac->macid,
 377                       vif->vifid);
 378                return -EINVAL;
 379        }
 380
 381        channel = ieee80211_get_channel(wiphy, le16_to_cpu(sr->freq));
 382        if (!channel) {
 383                pr_err("VIF%u.%u: channel at %u MHz not found\n",
 384                       vif->mac->macid, vif->vifid, le16_to_cpu(sr->freq));
 385                return -EINVAL;
 386        }
 387
 388        payload_len = len - sizeof(*sr);
 389        tlv = (struct qlink_tlv_hdr *)sr->payload;
 390
 391        while (payload_len >= sizeof(struct qlink_tlv_hdr)) {
 392                tlv_type = le16_to_cpu(tlv->type);
 393                tlv_value_len = le16_to_cpu(tlv->len);
 394                tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr);
 395
 396                if (tlv_full_len > payload_len)
 397                        return -EINVAL;
 398
 399                if (tlv_type == QTN_TLV_ID_IE_SET) {
 400                        const struct qlink_tlv_ie_set *ie_set;
 401                        unsigned int ie_len;
 402
 403                        if (payload_len < sizeof(*ie_set))
 404                                return -EINVAL;
 405
 406                        ie_set = (const struct qlink_tlv_ie_set *)tlv;
 407                        ie_len = tlv_value_len -
 408                                (sizeof(*ie_set) - sizeof(ie_set->hdr));
 409
 410                        switch (ie_set->type) {
 411                        case QLINK_IE_SET_BEACON_IES:
 412                                frame_type = CFG80211_BSS_FTYPE_BEACON;
 413                                break;
 414                        case QLINK_IE_SET_PROBE_RESP_IES:
 415                                frame_type = CFG80211_BSS_FTYPE_PRESP;
 416                                break;
 417                        default:
 418                                frame_type = CFG80211_BSS_FTYPE_UNKNOWN;
 419                        }
 420
 421                        if (ie_len) {
 422                                ies = ie_set->ie_data;
 423                                ies_len = ie_len;
 424                        }
 425                }
 426
 427                payload_len -= tlv_full_len;
 428                tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len);
 429        }
 430
 431        if (payload_len)
 432                return -EINVAL;
 433
 434        bss = cfg80211_inform_bss(wiphy, channel, frame_type,
 435                                  sr->bssid, get_unaligned_le64(&sr->tsf),
 436                                  le16_to_cpu(sr->capab),
 437                                  le16_to_cpu(sr->bintval), ies, ies_len,
 438                                  DBM_TO_MBM(sr->sig_dbm), GFP_KERNEL);
 439        if (!bss)
 440                return -ENOMEM;
 441
 442        cfg80211_put_bss(wiphy, bss);
 443
 444        return 0;
 445}
 446
 447static int
 448qtnf_event_handle_scan_complete(struct qtnf_wmac *mac,
 449                                const struct qlink_event_scan_complete *status,
 450                                u16 len)
 451{
 452        if (len < sizeof(*status)) {
 453                pr_err("MAC%u: payload is too short\n", mac->macid);
 454                return -EINVAL;
 455        }
 456
 457        qtnf_scan_done(mac, le32_to_cpu(status->flags) & QLINK_SCAN_ABORTED);
 458
 459        return 0;
 460}
 461
 462static int
 463qtnf_event_handle_freq_change(struct qtnf_wmac *mac,
 464                              const struct qlink_event_freq_change *data,
 465                              u16 len)
 466{
 467        struct wiphy *wiphy = priv_to_wiphy(mac);
 468        struct cfg80211_chan_def chandef;
 469        struct qtnf_vif *vif;
 470        int i;
 471
 472        if (len < sizeof(*data)) {
 473                pr_err("MAC%u: payload is too short\n", mac->macid);
 474                return -EINVAL;
 475        }
 476
 477        if (!wiphy->registered)
 478                return 0;
 479
 480        qlink_chandef_q2cfg(wiphy, &data->chan, &chandef);
 481
 482        if (!cfg80211_chandef_valid(&chandef)) {
 483                pr_err("MAC%u: bad channel freq=%u cf1=%u cf2=%u bw=%u\n",
 484                       mac->macid, chandef.chan->center_freq,
 485                       chandef.center_freq1, chandef.center_freq2,
 486                       chandef.width);
 487                return -EINVAL;
 488        }
 489
 490        pr_debug("MAC%d: new channel ieee=%u freq1=%u freq2=%u bw=%u\n",
 491                 mac->macid, chandef.chan->hw_value, chandef.center_freq1,
 492                 chandef.center_freq2, chandef.width);
 493
 494        for (i = 0; i < QTNF_MAX_INTF; i++) {
 495                vif = &mac->iflist[i];
 496
 497                if (vif->wdev.iftype == NL80211_IFTYPE_UNSPECIFIED)
 498                        continue;
 499
 500                if (vif->wdev.iftype == NL80211_IFTYPE_STATION &&
 501                    !vif->wdev.current_bss)
 502                        continue;
 503
 504                if (!vif->netdev)
 505                        continue;
 506
 507                mutex_lock(&vif->wdev.mtx);
 508                cfg80211_ch_switch_notify(vif->netdev, &chandef);
 509                mutex_unlock(&vif->wdev.mtx);
 510        }
 511
 512        return 0;
 513}
 514
 515static int qtnf_event_handle_radar(struct qtnf_vif *vif,
 516                                   const struct qlink_event_radar *ev,
 517                                   u16 len)
 518{
 519        struct wiphy *wiphy = priv_to_wiphy(vif->mac);
 520        struct cfg80211_chan_def chandef;
 521
 522        if (len < sizeof(*ev)) {
 523                pr_err("MAC%u: payload is too short\n", vif->mac->macid);
 524                return -EINVAL;
 525        }
 526
 527        if (!wiphy->registered || !vif->netdev)
 528                return 0;
 529
 530        qlink_chandef_q2cfg(wiphy, &ev->chan, &chandef);
 531
 532        if (!cfg80211_chandef_valid(&chandef)) {
 533                pr_err("MAC%u: bad channel f1=%u f2=%u bw=%u\n",
 534                       vif->mac->macid,
 535                       chandef.center_freq1, chandef.center_freq2,
 536                       chandef.width);
 537                return -EINVAL;
 538        }
 539
 540        pr_info("%s: radar event=%u f1=%u f2=%u bw=%u\n",
 541                vif->netdev->name, ev->event,
 542                chandef.center_freq1, chandef.center_freq2,
 543                chandef.width);
 544
 545        switch (ev->event) {
 546        case QLINK_RADAR_DETECTED:
 547                cfg80211_radar_event(wiphy, &chandef, GFP_KERNEL);
 548                break;
 549        case QLINK_RADAR_CAC_FINISHED:
 550                if (!vif->wdev.cac_started)
 551                        break;
 552
 553                cfg80211_cac_event(vif->netdev, &chandef,
 554                                   NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
 555                break;
 556        case QLINK_RADAR_CAC_ABORTED:
 557                if (!vif->wdev.cac_started)
 558                        break;
 559
 560                cfg80211_cac_event(vif->netdev, &chandef,
 561                                   NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
 562                break;
 563        case QLINK_RADAR_CAC_STARTED:
 564                if (vif->wdev.cac_started)
 565                        break;
 566
 567                if (!wiphy_ext_feature_isset(wiphy,
 568                                             NL80211_EXT_FEATURE_DFS_OFFLOAD))
 569                        break;
 570
 571                cfg80211_cac_event(vif->netdev, &chandef,
 572                                   NL80211_RADAR_CAC_STARTED, GFP_KERNEL);
 573                break;
 574        default:
 575                pr_warn("%s: unhandled radar event %u\n",
 576                        vif->netdev->name, ev->event);
 577                break;
 578        }
 579
 580        return 0;
 581}
 582
 583static int
 584qtnf_event_handle_external_auth(struct qtnf_vif *vif,
 585                                const struct qlink_event_external_auth *ev,
 586                                u16 len)
 587{
 588        struct cfg80211_external_auth_params auth = {0};
 589        struct wiphy *wiphy = priv_to_wiphy(vif->mac);
 590        int ret;
 591
 592        if (len < sizeof(*ev)) {
 593                pr_err("MAC%u: payload is too short\n", vif->mac->macid);
 594                return -EINVAL;
 595        }
 596
 597        if (!wiphy->registered || !vif->netdev)
 598                return 0;
 599
 600        if (ev->ssid_len) {
 601                memcpy(auth.ssid.ssid, ev->ssid, ev->ssid_len);
 602                auth.ssid.ssid_len = ev->ssid_len;
 603        }
 604
 605        auth.key_mgmt_suite = le32_to_cpu(ev->akm_suite);
 606        ether_addr_copy(auth.bssid, ev->bssid);
 607        auth.action = ev->action;
 608
 609        pr_info("%s: external auth bss=%pM action=%u akm=%u\n",
 610                vif->netdev->name, auth.bssid, auth.action,
 611                auth.key_mgmt_suite);
 612
 613        ret = cfg80211_external_auth_request(vif->netdev, &auth, GFP_KERNEL);
 614        if (ret)
 615                pr_warn("failed to offload external auth request\n");
 616
 617        return ret;
 618}
 619
 620static int qtnf_event_parse(struct qtnf_wmac *mac,
 621                            const struct sk_buff *event_skb)
 622{
 623        const struct qlink_event *event;
 624        struct qtnf_vif *vif = NULL;
 625        int ret = -1;
 626        u16 event_id;
 627        u16 event_len;
 628
 629        event = (const struct qlink_event *)event_skb->data;
 630        event_id = le16_to_cpu(event->event_id);
 631        event_len = le16_to_cpu(event->mhdr.len);
 632
 633        if (likely(event->vifid < QTNF_MAX_INTF)) {
 634                vif = &mac->iflist[event->vifid];
 635        } else {
 636                pr_err("invalid vif(%u)\n", event->vifid);
 637                return -EINVAL;
 638        }
 639
 640        switch (event_id) {
 641        case QLINK_EVENT_STA_ASSOCIATED:
 642                ret = qtnf_event_handle_sta_assoc(mac, vif, (const void *)event,
 643                                                  event_len);
 644                break;
 645        case QLINK_EVENT_STA_DEAUTH:
 646                ret = qtnf_event_handle_sta_deauth(mac, vif,
 647                                                   (const void *)event,
 648                                                   event_len);
 649                break;
 650        case QLINK_EVENT_MGMT_RECEIVED:
 651                ret = qtnf_event_handle_mgmt_received(vif, (const void *)event,
 652                                                      event_len);
 653                break;
 654        case QLINK_EVENT_SCAN_RESULTS:
 655                ret = qtnf_event_handle_scan_results(vif, (const void *)event,
 656                                                     event_len);
 657                break;
 658        case QLINK_EVENT_SCAN_COMPLETE:
 659                ret = qtnf_event_handle_scan_complete(mac, (const void *)event,
 660                                                      event_len);
 661                break;
 662        case QLINK_EVENT_BSS_JOIN:
 663                ret = qtnf_event_handle_bss_join(vif, (const void *)event,
 664                                                 event_len);
 665                break;
 666        case QLINK_EVENT_BSS_LEAVE:
 667                ret = qtnf_event_handle_bss_leave(vif, (const void *)event,
 668                                                  event_len);
 669                break;
 670        case QLINK_EVENT_FREQ_CHANGE:
 671                ret = qtnf_event_handle_freq_change(mac, (const void *)event,
 672                                                    event_len);
 673                break;
 674        case QLINK_EVENT_RADAR:
 675                ret = qtnf_event_handle_radar(vif, (const void *)event,
 676                                              event_len);
 677                break;
 678        case QLINK_EVENT_EXTERNAL_AUTH:
 679                ret = qtnf_event_handle_external_auth(vif, (const void *)event,
 680                                                      event_len);
 681                break;
 682        default:
 683                pr_warn("unknown event type: %x\n", event_id);
 684                break;
 685        }
 686
 687        return ret;
 688}
 689
 690static int qtnf_event_process_skb(struct qtnf_bus *bus,
 691                                  const struct sk_buff *skb)
 692{
 693        const struct qlink_event *event;
 694        struct qtnf_wmac *mac;
 695        int res;
 696
 697        if (unlikely(!skb || skb->len < sizeof(*event))) {
 698                pr_err("invalid event buffer\n");
 699                return -EINVAL;
 700        }
 701
 702        event = (struct qlink_event *)skb->data;
 703
 704        mac = qtnf_core_get_mac(bus, event->macid);
 705
 706        pr_debug("new event id:%x len:%u mac:%u vif:%u\n",
 707                 le16_to_cpu(event->event_id), le16_to_cpu(event->mhdr.len),
 708                 event->macid, event->vifid);
 709
 710        if (unlikely(!mac))
 711                return -ENXIO;
 712
 713        rtnl_lock();
 714        res = qtnf_event_parse(mac, skb);
 715        rtnl_unlock();
 716
 717        return res;
 718}
 719
 720void qtnf_event_work_handler(struct work_struct *work)
 721{
 722        struct qtnf_bus *bus = container_of(work, struct qtnf_bus, event_work);
 723        struct sk_buff_head *event_queue = &bus->trans.event_queue;
 724        struct sk_buff *current_event_skb = skb_dequeue(event_queue);
 725
 726        while (current_event_skb) {
 727                qtnf_event_process_skb(bus, current_event_skb);
 728                dev_kfree_skb_any(current_event_skb);
 729                current_event_skb = skb_dequeue(event_queue);
 730        }
 731}
 732