linux/drivers/staging/wfx/hif_tx.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Implementation of host-to-chip commands (aka request/confirmation) of WFxxx
   4 * Split Mac (WSM) API.
   5 *
   6 * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
   7 * Copyright (c) 2010, ST-Ericsson
   8 */
   9#include <linux/etherdevice.h>
  10
  11#include "hif_tx.h"
  12#include "wfx.h"
  13#include "bh.h"
  14#include "hwio.h"
  15#include "debug.h"
  16#include "sta.h"
  17
  18void wfx_init_hif_cmd(struct wfx_hif_cmd *hif_cmd)
  19{
  20        init_completion(&hif_cmd->ready);
  21        init_completion(&hif_cmd->done);
  22        mutex_init(&hif_cmd->lock);
  23}
  24
  25static void wfx_fill_header(struct hif_msg *hif, int if_id,
  26                            unsigned int cmd, size_t size)
  27{
  28        if (if_id == -1)
  29                if_id = 2;
  30
  31        WARN(cmd > 0x3f, "invalid WSM command %#.2x", cmd);
  32        WARN(size > 0xFFF, "requested buffer is too large: %zu bytes", size);
  33        WARN(if_id > 0x3, "invalid interface ID %d", if_id);
  34
  35        hif->len = cpu_to_le16(size + 4);
  36        hif->id = cmd;
  37        hif->interface = if_id;
  38}
  39
  40static void *wfx_alloc_hif(size_t body_len, struct hif_msg **hif)
  41{
  42        *hif = kzalloc(sizeof(struct hif_msg) + body_len, GFP_KERNEL);
  43        if (*hif)
  44                return (*hif)->body;
  45        else
  46                return NULL;
  47}
  48
  49int wfx_cmd_send(struct wfx_dev *wdev, struct hif_msg *request,
  50                 void *reply, size_t reply_len, bool no_reply)
  51{
  52        const char *mib_name = "";
  53        const char *mib_sep = "";
  54        int cmd = request->id;
  55        int vif = request->interface;
  56        int ret;
  57
  58        // Do not wait for any reply if chip is frozen
  59        if (wdev->chip_frozen)
  60                return -ETIMEDOUT;
  61
  62        mutex_lock(&wdev->hif_cmd.lock);
  63        WARN(wdev->hif_cmd.buf_send, "data locking error");
  64
  65        // Note: call to complete() below has an implicit memory barrier that
  66        // hopefully protect buf_send
  67        wdev->hif_cmd.buf_send = request;
  68        wdev->hif_cmd.buf_recv = reply;
  69        wdev->hif_cmd.len_recv = reply_len;
  70        complete(&wdev->hif_cmd.ready);
  71
  72        wfx_bh_request_tx(wdev);
  73
  74        if (no_reply) {
  75                // Chip won't reply. Give enough time to the wq to send the
  76                // buffer.
  77                msleep(100);
  78                wdev->hif_cmd.buf_send = NULL;
  79                mutex_unlock(&wdev->hif_cmd.lock);
  80                return 0;
  81        }
  82
  83        if (wdev->poll_irq)
  84                wfx_bh_poll_irq(wdev);
  85
  86        ret = wait_for_completion_timeout(&wdev->hif_cmd.done, 1 * HZ);
  87        if (!ret) {
  88                dev_err(wdev->dev, "chip is abnormally long to answer\n");
  89                reinit_completion(&wdev->hif_cmd.ready);
  90                ret = wait_for_completion_timeout(&wdev->hif_cmd.done, 3 * HZ);
  91        }
  92        if (!ret) {
  93                dev_err(wdev->dev, "chip did not answer\n");
  94                wfx_pending_dump_old_frames(wdev, 3000);
  95                wdev->chip_frozen = true;
  96                reinit_completion(&wdev->hif_cmd.done);
  97                ret = -ETIMEDOUT;
  98        } else {
  99                ret = wdev->hif_cmd.ret;
 100        }
 101
 102        wdev->hif_cmd.buf_send = NULL;
 103        mutex_unlock(&wdev->hif_cmd.lock);
 104
 105        if (ret &&
 106            (cmd == HIF_REQ_ID_READ_MIB || cmd == HIF_REQ_ID_WRITE_MIB)) {
 107                mib_name = get_mib_name(((u16 *)request)[2]);
 108                mib_sep = "/";
 109        }
 110        if (ret < 0)
 111                dev_err(wdev->dev,
 112                        "WSM request %s%s%s (%#.2x) on vif %d returned error %d\n",
 113                        get_hif_name(cmd), mib_sep, mib_name, cmd, vif, ret);
 114        if (ret > 0)
 115                dev_warn(wdev->dev,
 116                         "WSM request %s%s%s (%#.2x) on vif %d returned status %d\n",
 117                         get_hif_name(cmd), mib_sep, mib_name, cmd, vif, ret);
 118
 119        return ret;
 120}
 121
 122// This function is special. After HIF_REQ_ID_SHUT_DOWN, chip won't reply to any
 123// request anymore. Obviously, only call this function during device unregister.
 124int hif_shutdown(struct wfx_dev *wdev)
 125{
 126        int ret;
 127        struct hif_msg *hif;
 128
 129        wfx_alloc_hif(0, &hif);
 130        if (!hif)
 131                return -ENOMEM;
 132        wfx_fill_header(hif, -1, HIF_REQ_ID_SHUT_DOWN, 0);
 133        ret = wfx_cmd_send(wdev, hif, NULL, 0, true);
 134        if (wdev->pdata.gpio_wakeup)
 135                gpiod_set_value(wdev->pdata.gpio_wakeup, 0);
 136        else
 137                control_reg_write(wdev, 0);
 138        kfree(hif);
 139        return ret;
 140}
 141
 142int hif_configuration(struct wfx_dev *wdev, const u8 *conf, size_t len)
 143{
 144        int ret;
 145        size_t buf_len = sizeof(struct hif_req_configuration) + len;
 146        struct hif_msg *hif;
 147        struct hif_req_configuration *body = wfx_alloc_hif(buf_len, &hif);
 148
 149        if (!hif)
 150                return -ENOMEM;
 151        body->length = cpu_to_le16(len);
 152        memcpy(body->pds_data, conf, len);
 153        wfx_fill_header(hif, -1, HIF_REQ_ID_CONFIGURATION, buf_len);
 154        ret = wfx_cmd_send(wdev, hif, NULL, 0, false);
 155        kfree(hif);
 156        return ret;
 157}
 158
 159int hif_reset(struct wfx_vif *wvif, bool reset_stat)
 160{
 161        int ret;
 162        struct hif_msg *hif;
 163        struct hif_req_reset *body = wfx_alloc_hif(sizeof(*body), &hif);
 164
 165        if (!hif)
 166                return -ENOMEM;
 167        body->reset_stat = reset_stat;
 168        wfx_fill_header(hif, wvif->id, HIF_REQ_ID_RESET, sizeof(*body));
 169        ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false);
 170        kfree(hif);
 171        return ret;
 172}
 173
 174int hif_read_mib(struct wfx_dev *wdev, int vif_id, u16 mib_id,
 175                 void *val, size_t val_len)
 176{
 177        int ret;
 178        struct hif_msg *hif;
 179        int buf_len = sizeof(struct hif_cnf_read_mib) + val_len;
 180        struct hif_req_read_mib *body = wfx_alloc_hif(sizeof(*body), &hif);
 181        struct hif_cnf_read_mib *reply = kmalloc(buf_len, GFP_KERNEL);
 182
 183        if (!body || !reply) {
 184                ret = -ENOMEM;
 185                goto out;
 186        }
 187        body->mib_id = cpu_to_le16(mib_id);
 188        wfx_fill_header(hif, vif_id, HIF_REQ_ID_READ_MIB, sizeof(*body));
 189        ret = wfx_cmd_send(wdev, hif, reply, buf_len, false);
 190
 191        if (!ret && mib_id != le16_to_cpu(reply->mib_id)) {
 192                dev_warn(wdev->dev, "%s: confirmation mismatch request\n",
 193                         __func__);
 194                ret = -EIO;
 195        }
 196        if (ret == -ENOMEM)
 197                dev_err(wdev->dev, "buffer is too small to receive %s (%zu < %d)\n",
 198                        get_mib_name(mib_id), val_len,
 199                        le16_to_cpu(reply->length));
 200        if (!ret)
 201                memcpy(val, &reply->mib_data, le16_to_cpu(reply->length));
 202        else
 203                memset(val, 0xFF, val_len);
 204out:
 205        kfree(hif);
 206        kfree(reply);
 207        return ret;
 208}
 209
 210int hif_write_mib(struct wfx_dev *wdev, int vif_id, u16 mib_id,
 211                  void *val, size_t val_len)
 212{
 213        int ret;
 214        struct hif_msg *hif;
 215        int buf_len = sizeof(struct hif_req_write_mib) + val_len;
 216        struct hif_req_write_mib *body = wfx_alloc_hif(buf_len, &hif);
 217
 218        if (!hif)
 219                return -ENOMEM;
 220        body->mib_id = cpu_to_le16(mib_id);
 221        body->length = cpu_to_le16(val_len);
 222        memcpy(&body->mib_data, val, val_len);
 223        wfx_fill_header(hif, vif_id, HIF_REQ_ID_WRITE_MIB, buf_len);
 224        ret = wfx_cmd_send(wdev, hif, NULL, 0, false);
 225        kfree(hif);
 226        return ret;
 227}
 228
 229int hif_scan(struct wfx_vif *wvif, struct cfg80211_scan_request *req,
 230             int chan_start_idx, int chan_num, int *timeout)
 231{
 232        int ret, i;
 233        struct hif_msg *hif;
 234        size_t buf_len =
 235                sizeof(struct hif_req_start_scan_alt) + chan_num * sizeof(u8);
 236        struct hif_req_start_scan_alt *body = wfx_alloc_hif(buf_len, &hif);
 237        int tmo_chan_fg, tmo_chan_bg, tmo;
 238
 239        WARN(chan_num > HIF_API_MAX_NB_CHANNELS, "invalid params");
 240        WARN(req->n_ssids > HIF_API_MAX_NB_SSIDS, "invalid params");
 241
 242        if (!hif)
 243                return -ENOMEM;
 244        for (i = 0; i < req->n_ssids; i++) {
 245                memcpy(body->ssid_def[i].ssid, req->ssids[i].ssid,
 246                       IEEE80211_MAX_SSID_LEN);
 247                body->ssid_def[i].ssid_length =
 248                        cpu_to_le32(req->ssids[i].ssid_len);
 249        }
 250        body->num_of_ssids = HIF_API_MAX_NB_SSIDS;
 251        body->maintain_current_bss = 1;
 252        body->disallow_ps = 1;
 253        body->tx_power_level =
 254                cpu_to_le32(req->channels[chan_start_idx]->max_power);
 255        body->num_of_channels = chan_num;
 256        for (i = 0; i < chan_num; i++)
 257                body->channel_list[i] =
 258                        req->channels[i + chan_start_idx]->hw_value;
 259        if (req->no_cck)
 260                body->max_transmit_rate = API_RATE_INDEX_G_6MBPS;
 261        else
 262                body->max_transmit_rate = API_RATE_INDEX_B_1MBPS;
 263        if (req->channels[chan_start_idx]->flags & IEEE80211_CHAN_NO_IR) {
 264                body->min_channel_time = cpu_to_le32(50);
 265                body->max_channel_time = cpu_to_le32(150);
 266        } else {
 267                body->min_channel_time = cpu_to_le32(10);
 268                body->max_channel_time = cpu_to_le32(50);
 269                body->num_of_probe_requests = 2;
 270                body->probe_delay = 100;
 271        }
 272        tmo_chan_bg = le32_to_cpu(body->max_channel_time) * USEC_PER_TU;
 273        tmo_chan_fg = 512 * USEC_PER_TU + body->probe_delay;
 274        tmo_chan_fg *= body->num_of_probe_requests;
 275        tmo = chan_num * max(tmo_chan_bg, tmo_chan_fg) + 512 * USEC_PER_TU;
 276        if (timeout)
 277                *timeout = usecs_to_jiffies(tmo);
 278
 279        wfx_fill_header(hif, wvif->id, HIF_REQ_ID_START_SCAN, buf_len);
 280        ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false);
 281        kfree(hif);
 282        return ret;
 283}
 284
 285int hif_stop_scan(struct wfx_vif *wvif)
 286{
 287        int ret;
 288        struct hif_msg *hif;
 289        // body associated to HIF_REQ_ID_STOP_SCAN is empty
 290        wfx_alloc_hif(0, &hif);
 291
 292        if (!hif)
 293                return -ENOMEM;
 294        wfx_fill_header(hif, wvif->id, HIF_REQ_ID_STOP_SCAN, 0);
 295        ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false);
 296        kfree(hif);
 297        return ret;
 298}
 299
 300int hif_join(struct wfx_vif *wvif, const struct ieee80211_bss_conf *conf,
 301             struct ieee80211_channel *channel, const u8 *ssid, int ssidlen)
 302{
 303        int ret;
 304        struct hif_msg *hif;
 305        struct hif_req_join *body = wfx_alloc_hif(sizeof(*body), &hif);
 306
 307        WARN_ON(!conf->beacon_int);
 308        WARN_ON(!conf->basic_rates);
 309        WARN_ON(sizeof(body->ssid) < ssidlen);
 310        WARN(!conf->ibss_joined && !ssidlen, "joining an unknown BSS");
 311        if (WARN_ON(!channel))
 312                return -EINVAL;
 313        if (!hif)
 314                return -ENOMEM;
 315        body->infrastructure_bss_mode = !conf->ibss_joined;
 316        body->short_preamble = conf->use_short_preamble;
 317        if (channel->flags & IEEE80211_CHAN_NO_IR)
 318                body->probe_for_join = 0;
 319        else
 320                body->probe_for_join = 1;
 321        body->channel_number = channel->hw_value;
 322        body->beacon_interval = cpu_to_le32(conf->beacon_int);
 323        body->basic_rate_set =
 324                cpu_to_le32(wfx_rate_mask_to_hw(wvif->wdev, conf->basic_rates));
 325        memcpy(body->bssid, conf->bssid, sizeof(body->bssid));
 326        if (ssid) {
 327                body->ssid_length = cpu_to_le32(ssidlen);
 328                memcpy(body->ssid, ssid, ssidlen);
 329        }
 330        wfx_fill_header(hif, wvif->id, HIF_REQ_ID_JOIN, sizeof(*body));
 331        ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false);
 332        kfree(hif);
 333        return ret;
 334}
 335
 336int hif_set_bss_params(struct wfx_vif *wvif, int aid, int beacon_lost_count)
 337{
 338        int ret;
 339        struct hif_msg *hif;
 340        struct hif_req_set_bss_params *body =
 341                wfx_alloc_hif(sizeof(*body), &hif);
 342
 343        if (!hif)
 344                return -ENOMEM;
 345        body->aid = cpu_to_le16(aid);
 346        body->beacon_lost_count = beacon_lost_count;
 347        wfx_fill_header(hif, wvif->id, HIF_REQ_ID_SET_BSS_PARAMS,
 348                        sizeof(*body));
 349        ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false);
 350        kfree(hif);
 351        return ret;
 352}
 353
 354int hif_add_key(struct wfx_dev *wdev, const struct hif_req_add_key *arg)
 355{
 356        int ret;
 357        struct hif_msg *hif;
 358        // FIXME: only send necessary bits
 359        struct hif_req_add_key *body = wfx_alloc_hif(sizeof(*body), &hif);
 360
 361        if (!hif)
 362                return -ENOMEM;
 363        // FIXME: swap bytes as necessary in body
 364        memcpy(body, arg, sizeof(*body));
 365        if (wfx_api_older_than(wdev, 1, 5))
 366                // Legacy firmwares expect that add_key to be sent on right
 367                // interface.
 368                wfx_fill_header(hif, arg->int_id, HIF_REQ_ID_ADD_KEY,
 369                                sizeof(*body));
 370        else
 371                wfx_fill_header(hif, -1, HIF_REQ_ID_ADD_KEY, sizeof(*body));
 372        ret = wfx_cmd_send(wdev, hif, NULL, 0, false);
 373        kfree(hif);
 374        return ret;
 375}
 376
 377int hif_remove_key(struct wfx_dev *wdev, int idx)
 378{
 379        int ret;
 380        struct hif_msg *hif;
 381        struct hif_req_remove_key *body = wfx_alloc_hif(sizeof(*body), &hif);
 382
 383        if (!hif)
 384                return -ENOMEM;
 385        body->entry_index = idx;
 386        wfx_fill_header(hif, -1, HIF_REQ_ID_REMOVE_KEY, sizeof(*body));
 387        ret = wfx_cmd_send(wdev, hif, NULL, 0, false);
 388        kfree(hif);
 389        return ret;
 390}
 391
 392int hif_set_edca_queue_params(struct wfx_vif *wvif, u16 queue,
 393                              const struct ieee80211_tx_queue_params *arg)
 394{
 395        int ret;
 396        struct hif_msg *hif;
 397        struct hif_req_edca_queue_params *body = wfx_alloc_hif(sizeof(*body),
 398                                                               &hif);
 399
 400        if (!body)
 401                return -ENOMEM;
 402
 403        WARN_ON(arg->aifs > 255);
 404        if (!hif)
 405                return -ENOMEM;
 406        body->aifsn = arg->aifs;
 407        body->cw_min = cpu_to_le16(arg->cw_min);
 408        body->cw_max = cpu_to_le16(arg->cw_max);
 409        body->tx_op_limit = cpu_to_le16(arg->txop * USEC_PER_TXOP);
 410        body->queue_id = 3 - queue;
 411        // API 2.0 has changed queue IDs values
 412        if (wfx_api_older_than(wvif->wdev, 2, 0) && queue == IEEE80211_AC_BE)
 413                body->queue_id = HIF_QUEUE_ID_BACKGROUND;
 414        if (wfx_api_older_than(wvif->wdev, 2, 0) && queue == IEEE80211_AC_BK)
 415                body->queue_id = HIF_QUEUE_ID_BESTEFFORT;
 416        wfx_fill_header(hif, wvif->id, HIF_REQ_ID_EDCA_QUEUE_PARAMS,
 417                        sizeof(*body));
 418        ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false);
 419        kfree(hif);
 420        return ret;
 421}
 422
 423int hif_set_pm(struct wfx_vif *wvif, bool ps, int dynamic_ps_timeout)
 424{
 425        int ret;
 426        struct hif_msg *hif;
 427        struct hif_req_set_pm_mode *body = wfx_alloc_hif(sizeof(*body), &hif);
 428
 429        if (!body)
 430                return -ENOMEM;
 431
 432        if (!hif)
 433                return -ENOMEM;
 434        if (ps) {
 435                body->enter_psm = 1;
 436                // Firmware does not support more than 128ms
 437                body->fast_psm_idle_period = min(dynamic_ps_timeout * 2, 255);
 438                if (body->fast_psm_idle_period)
 439                        body->fast_psm = 1;
 440        }
 441        wfx_fill_header(hif, wvif->id, HIF_REQ_ID_SET_PM_MODE, sizeof(*body));
 442        ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false);
 443        kfree(hif);
 444        return ret;
 445}
 446
 447int hif_start(struct wfx_vif *wvif, const struct ieee80211_bss_conf *conf,
 448              const struct ieee80211_channel *channel)
 449{
 450        int ret;
 451        struct hif_msg *hif;
 452        struct hif_req_start *body = wfx_alloc_hif(sizeof(*body), &hif);
 453
 454        WARN_ON(!conf->beacon_int);
 455        if (!hif)
 456                return -ENOMEM;
 457        body->dtim_period = conf->dtim_period;
 458        body->short_preamble = conf->use_short_preamble;
 459        body->channel_number = channel->hw_value;
 460        body->beacon_interval = cpu_to_le32(conf->beacon_int);
 461        body->basic_rate_set =
 462                cpu_to_le32(wfx_rate_mask_to_hw(wvif->wdev, conf->basic_rates));
 463        body->ssid_length = conf->ssid_len;
 464        memcpy(body->ssid, conf->ssid, conf->ssid_len);
 465        wfx_fill_header(hif, wvif->id, HIF_REQ_ID_START, sizeof(*body));
 466        ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false);
 467        kfree(hif);
 468        return ret;
 469}
 470
 471int hif_beacon_transmit(struct wfx_vif *wvif, bool enable)
 472{
 473        int ret;
 474        struct hif_msg *hif;
 475        struct hif_req_beacon_transmit *body = wfx_alloc_hif(sizeof(*body),
 476                                                             &hif);
 477
 478        if (!hif)
 479                return -ENOMEM;
 480        body->enable_beaconing = enable ? 1 : 0;
 481        wfx_fill_header(hif, wvif->id, HIF_REQ_ID_BEACON_TRANSMIT,
 482                        sizeof(*body));
 483        ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false);
 484        kfree(hif);
 485        return ret;
 486}
 487
 488int hif_map_link(struct wfx_vif *wvif, bool unmap, u8 *mac_addr, int sta_id, bool mfp)
 489{
 490        int ret;
 491        struct hif_msg *hif;
 492        struct hif_req_map_link *body = wfx_alloc_hif(sizeof(*body), &hif);
 493
 494        if (!hif)
 495                return -ENOMEM;
 496        if (mac_addr)
 497                ether_addr_copy(body->mac_addr, mac_addr);
 498        body->mfpc = mfp ? 1 : 0;
 499        body->unmap = unmap ? 1 : 0;
 500        body->peer_sta_id = sta_id;
 501        wfx_fill_header(hif, wvif->id, HIF_REQ_ID_MAP_LINK, sizeof(*body));
 502        ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false);
 503        kfree(hif);
 504        return ret;
 505}
 506
 507int hif_update_ie_beacon(struct wfx_vif *wvif, const u8 *ies, size_t ies_len)
 508{
 509        int ret;
 510        struct hif_msg *hif;
 511        int buf_len = sizeof(struct hif_req_update_ie) + ies_len;
 512        struct hif_req_update_ie *body = wfx_alloc_hif(buf_len, &hif);
 513
 514        if (!hif)
 515                return -ENOMEM;
 516        body->beacon = 1;
 517        body->num_ies = cpu_to_le16(1);
 518        memcpy(body->ie, ies, ies_len);
 519        wfx_fill_header(hif, wvif->id, HIF_REQ_ID_UPDATE_IE, buf_len);
 520        ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false);
 521        kfree(hif);
 522        return ret;
 523}
 524