14a5fb1bbSJérôme Pouiller // SPDX-License-Identifier: GPL-2.0-only 24a5fb1bbSJérôme Pouiller /* 34a5fb1bbSJérôme Pouiller * Implementation of the host-to-chip commands (aka request/confirmation) of the 44a5fb1bbSJérôme Pouiller * hardware API. 54a5fb1bbSJérôme Pouiller * 64a5fb1bbSJérôme Pouiller * Copyright (c) 2017-2020, Silicon Laboratories, Inc. 74a5fb1bbSJérôme Pouiller * Copyright (c) 2010, ST-Ericsson 84a5fb1bbSJérôme Pouiller */ 94a5fb1bbSJérôme Pouiller #include <linux/etherdevice.h> 104a5fb1bbSJérôme Pouiller 114a5fb1bbSJérôme Pouiller #include "hif_tx.h" 124a5fb1bbSJérôme Pouiller #include "wfx.h" 134a5fb1bbSJérôme Pouiller #include "bh.h" 144a5fb1bbSJérôme Pouiller #include "hwio.h" 154a5fb1bbSJérôme Pouiller #include "debug.h" 164a5fb1bbSJérôme Pouiller #include "sta.h" 174a5fb1bbSJérôme Pouiller 184a5fb1bbSJérôme Pouiller void wfx_init_hif_cmd(struct wfx_hif_cmd *hif_cmd) 194a5fb1bbSJérôme Pouiller { 204a5fb1bbSJérôme Pouiller init_completion(&hif_cmd->ready); 214a5fb1bbSJérôme Pouiller init_completion(&hif_cmd->done); 224a5fb1bbSJérôme Pouiller mutex_init(&hif_cmd->lock); 234a5fb1bbSJérôme Pouiller } 244a5fb1bbSJérôme Pouiller 254a5fb1bbSJérôme Pouiller static void wfx_fill_header(struct wfx_hif_msg *hif, int if_id, unsigned int cmd, size_t size) 264a5fb1bbSJérôme Pouiller { 274a5fb1bbSJérôme Pouiller if (if_id == -1) 284a5fb1bbSJérôme Pouiller if_id = 2; 294a5fb1bbSJérôme Pouiller 304a5fb1bbSJérôme Pouiller WARN(cmd > 0x3f, "invalid hardware command %#.2x", cmd); 314a5fb1bbSJérôme Pouiller WARN(size > 0xFFF, "requested buffer is too large: %zu bytes", size); 324a5fb1bbSJérôme Pouiller WARN(if_id > 0x3, "invalid interface ID %d", if_id); 334a5fb1bbSJérôme Pouiller 344a5fb1bbSJérôme Pouiller hif->len = cpu_to_le16(size + 4); 354a5fb1bbSJérôme Pouiller hif->id = cmd; 364a5fb1bbSJérôme Pouiller hif->interface = if_id; 374a5fb1bbSJérôme Pouiller } 384a5fb1bbSJérôme Pouiller 394a5fb1bbSJérôme Pouiller static void *wfx_alloc_hif(size_t body_len, struct wfx_hif_msg **hif) 404a5fb1bbSJérôme Pouiller { 414a5fb1bbSJérôme Pouiller *hif = kzalloc(sizeof(struct wfx_hif_msg) + body_len, GFP_KERNEL); 424a5fb1bbSJérôme Pouiller if (*hif) 434a5fb1bbSJérôme Pouiller return (*hif)->body; 444a5fb1bbSJérôme Pouiller else 454a5fb1bbSJérôme Pouiller return NULL; 464a5fb1bbSJérôme Pouiller } 474a5fb1bbSJérôme Pouiller 4894c104d5SJérôme Pouiller static u32 wfx_rate_mask_to_hw(struct wfx_dev *wdev, u32 rates) 4994c104d5SJérôme Pouiller { 5094c104d5SJérôme Pouiller int i; 5194c104d5SJérôme Pouiller u32 ret = 0; 5294c104d5SJérôme Pouiller /* The device only supports 2GHz */ 5394c104d5SJérôme Pouiller struct ieee80211_supported_band *sband = wdev->hw->wiphy->bands[NL80211_BAND_2GHZ]; 5494c104d5SJérôme Pouiller 5594c104d5SJérôme Pouiller for (i = 0; i < sband->n_bitrates; i++) { 5694c104d5SJérôme Pouiller if (rates & BIT(i)) { 5794c104d5SJérôme Pouiller if (i >= sband->n_bitrates) 5894c104d5SJérôme Pouiller dev_warn(wdev->dev, "unsupported basic rate\n"); 5994c104d5SJérôme Pouiller else 6094c104d5SJérôme Pouiller ret |= BIT(sband->bitrates[i].hw_value); 6194c104d5SJérôme Pouiller } 6294c104d5SJérôme Pouiller } 6394c104d5SJérôme Pouiller return ret; 6494c104d5SJérôme Pouiller } 6594c104d5SJérôme Pouiller 664a5fb1bbSJérôme Pouiller int wfx_cmd_send(struct wfx_dev *wdev, struct wfx_hif_msg *request, 674a5fb1bbSJérôme Pouiller void *reply, size_t reply_len, bool no_reply) 684a5fb1bbSJérôme Pouiller { 694a5fb1bbSJérôme Pouiller const char *mib_name = ""; 704a5fb1bbSJérôme Pouiller const char *mib_sep = ""; 714a5fb1bbSJérôme Pouiller int cmd = request->id; 724a5fb1bbSJérôme Pouiller int vif = request->interface; 734a5fb1bbSJérôme Pouiller int ret; 744a5fb1bbSJérôme Pouiller 754a5fb1bbSJérôme Pouiller /* Do not wait for any reply if chip is frozen */ 764a5fb1bbSJérôme Pouiller if (wdev->chip_frozen) 774a5fb1bbSJérôme Pouiller return -ETIMEDOUT; 784a5fb1bbSJérôme Pouiller 794a5fb1bbSJérôme Pouiller mutex_lock(&wdev->hif_cmd.lock); 804a5fb1bbSJérôme Pouiller WARN(wdev->hif_cmd.buf_send, "data locking error"); 814a5fb1bbSJérôme Pouiller 824a5fb1bbSJérôme Pouiller /* Note: call to complete() below has an implicit memory barrier that hopefully protect 834a5fb1bbSJérôme Pouiller * buf_send 844a5fb1bbSJérôme Pouiller */ 854a5fb1bbSJérôme Pouiller wdev->hif_cmd.buf_send = request; 864a5fb1bbSJérôme Pouiller wdev->hif_cmd.buf_recv = reply; 874a5fb1bbSJérôme Pouiller wdev->hif_cmd.len_recv = reply_len; 884a5fb1bbSJérôme Pouiller complete(&wdev->hif_cmd.ready); 894a5fb1bbSJérôme Pouiller 904a5fb1bbSJérôme Pouiller wfx_bh_request_tx(wdev); 914a5fb1bbSJérôme Pouiller 924a5fb1bbSJérôme Pouiller if (no_reply) { 934a5fb1bbSJérôme Pouiller /* Chip won't reply. Ensure the wq has send the buffer before to continue. */ 94eeff214dSTetsuo Handa flush_workqueue(wdev->bh_wq); 954a5fb1bbSJérôme Pouiller ret = 0; 964a5fb1bbSJérôme Pouiller goto end; 974a5fb1bbSJérôme Pouiller } 984a5fb1bbSJérôme Pouiller 994a5fb1bbSJérôme Pouiller if (wdev->poll_irq) 1004a5fb1bbSJérôme Pouiller wfx_bh_poll_irq(wdev); 1014a5fb1bbSJérôme Pouiller 1024a5fb1bbSJérôme Pouiller ret = wait_for_completion_timeout(&wdev->hif_cmd.done, 1 * HZ); 1034a5fb1bbSJérôme Pouiller if (!ret) { 1044a5fb1bbSJérôme Pouiller dev_err(wdev->dev, "chip is abnormally long to answer\n"); 1054a5fb1bbSJérôme Pouiller reinit_completion(&wdev->hif_cmd.ready); 1064a5fb1bbSJérôme Pouiller ret = wait_for_completion_timeout(&wdev->hif_cmd.done, 3 * HZ); 1074a5fb1bbSJérôme Pouiller } 1084a5fb1bbSJérôme Pouiller if (!ret) { 1094a5fb1bbSJérôme Pouiller dev_err(wdev->dev, "chip did not answer\n"); 1104a5fb1bbSJérôme Pouiller wfx_pending_dump_old_frames(wdev, 3000); 1114a5fb1bbSJérôme Pouiller wdev->chip_frozen = true; 1124a5fb1bbSJérôme Pouiller reinit_completion(&wdev->hif_cmd.done); 1134a5fb1bbSJérôme Pouiller ret = -ETIMEDOUT; 1144a5fb1bbSJérôme Pouiller } else { 1154a5fb1bbSJérôme Pouiller ret = wdev->hif_cmd.ret; 1164a5fb1bbSJérôme Pouiller } 1174a5fb1bbSJérôme Pouiller 1184a5fb1bbSJérôme Pouiller end: 1194a5fb1bbSJérôme Pouiller wdev->hif_cmd.buf_send = NULL; 1204a5fb1bbSJérôme Pouiller mutex_unlock(&wdev->hif_cmd.lock); 1214a5fb1bbSJérôme Pouiller 1224a5fb1bbSJérôme Pouiller if (ret && 1234a5fb1bbSJérôme Pouiller (cmd == HIF_REQ_ID_READ_MIB || cmd == HIF_REQ_ID_WRITE_MIB)) { 1244a5fb1bbSJérôme Pouiller mib_name = wfx_get_mib_name(((u16 *)request)[2]); 1254a5fb1bbSJérôme Pouiller mib_sep = "/"; 1264a5fb1bbSJérôme Pouiller } 1274a5fb1bbSJérôme Pouiller if (ret < 0) 1284a5fb1bbSJérôme Pouiller dev_err(wdev->dev, "hardware request %s%s%s (%#.2x) on vif %d returned error %d\n", 1294a5fb1bbSJérôme Pouiller wfx_get_hif_name(cmd), mib_sep, mib_name, cmd, vif, ret); 1304a5fb1bbSJérôme Pouiller if (ret > 0) 1314a5fb1bbSJérôme Pouiller dev_warn(wdev->dev, "hardware request %s%s%s (%#.2x) on vif %d returned status %d\n", 1324a5fb1bbSJérôme Pouiller wfx_get_hif_name(cmd), mib_sep, mib_name, cmd, vif, ret); 1334a5fb1bbSJérôme Pouiller 1344a5fb1bbSJérôme Pouiller return ret; 1354a5fb1bbSJérôme Pouiller } 1364a5fb1bbSJérôme Pouiller 1374a5fb1bbSJérôme Pouiller /* This function is special. After HIF_REQ_ID_SHUT_DOWN, chip won't reply to any request anymore. 1384a5fb1bbSJérôme Pouiller * Obviously, only call this function during device unregister. 1394a5fb1bbSJérôme Pouiller */ 1404a5fb1bbSJérôme Pouiller int wfx_hif_shutdown(struct wfx_dev *wdev) 1414a5fb1bbSJérôme Pouiller { 1424a5fb1bbSJérôme Pouiller int ret; 1434a5fb1bbSJérôme Pouiller struct wfx_hif_msg *hif; 1444a5fb1bbSJérôme Pouiller 1454a5fb1bbSJérôme Pouiller wfx_alloc_hif(0, &hif); 1464a5fb1bbSJérôme Pouiller if (!hif) 1474a5fb1bbSJérôme Pouiller return -ENOMEM; 1484a5fb1bbSJérôme Pouiller wfx_fill_header(hif, -1, HIF_REQ_ID_SHUT_DOWN, 0); 1494a5fb1bbSJérôme Pouiller ret = wfx_cmd_send(wdev, hif, NULL, 0, true); 1504a5fb1bbSJérôme Pouiller if (wdev->pdata.gpio_wakeup) 1514a5fb1bbSJérôme Pouiller gpiod_set_value(wdev->pdata.gpio_wakeup, 0); 1524a5fb1bbSJérôme Pouiller else 1534a5fb1bbSJérôme Pouiller wfx_control_reg_write(wdev, 0); 1544a5fb1bbSJérôme Pouiller kfree(hif); 1554a5fb1bbSJérôme Pouiller return ret; 1564a5fb1bbSJérôme Pouiller } 1574a5fb1bbSJérôme Pouiller 1584a5fb1bbSJérôme Pouiller int wfx_hif_configuration(struct wfx_dev *wdev, const u8 *conf, size_t len) 1594a5fb1bbSJérôme Pouiller { 1604a5fb1bbSJérôme Pouiller int ret; 1614a5fb1bbSJérôme Pouiller size_t buf_len = sizeof(struct wfx_hif_req_configuration) + len; 1624a5fb1bbSJérôme Pouiller struct wfx_hif_msg *hif; 1634a5fb1bbSJérôme Pouiller struct wfx_hif_req_configuration *body = wfx_alloc_hif(buf_len, &hif); 1644a5fb1bbSJérôme Pouiller 1654a5fb1bbSJérôme Pouiller if (!hif) 1664a5fb1bbSJérôme Pouiller return -ENOMEM; 1674a5fb1bbSJérôme Pouiller body->length = cpu_to_le16(len); 1684a5fb1bbSJérôme Pouiller memcpy(body->pds_data, conf, len); 1694a5fb1bbSJérôme Pouiller wfx_fill_header(hif, -1, HIF_REQ_ID_CONFIGURATION, buf_len); 1704a5fb1bbSJérôme Pouiller ret = wfx_cmd_send(wdev, hif, NULL, 0, false); 1714a5fb1bbSJérôme Pouiller kfree(hif); 1724a5fb1bbSJérôme Pouiller return ret; 1734a5fb1bbSJérôme Pouiller } 1744a5fb1bbSJérôme Pouiller 1754a5fb1bbSJérôme Pouiller int wfx_hif_reset(struct wfx_vif *wvif, bool reset_stat) 1764a5fb1bbSJérôme Pouiller { 1774a5fb1bbSJérôme Pouiller int ret; 1784a5fb1bbSJérôme Pouiller struct wfx_hif_msg *hif; 1794a5fb1bbSJérôme Pouiller struct wfx_hif_req_reset *body = wfx_alloc_hif(sizeof(*body), &hif); 1804a5fb1bbSJérôme Pouiller 1814a5fb1bbSJérôme Pouiller if (!hif) 1824a5fb1bbSJérôme Pouiller return -ENOMEM; 1834a5fb1bbSJérôme Pouiller body->reset_stat = reset_stat; 1844a5fb1bbSJérôme Pouiller wfx_fill_header(hif, wvif->id, HIF_REQ_ID_RESET, sizeof(*body)); 1854a5fb1bbSJérôme Pouiller ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); 1864a5fb1bbSJérôme Pouiller kfree(hif); 1874a5fb1bbSJérôme Pouiller return ret; 1884a5fb1bbSJérôme Pouiller } 1894a5fb1bbSJérôme Pouiller 1904a5fb1bbSJérôme Pouiller int wfx_hif_read_mib(struct wfx_dev *wdev, int vif_id, u16 mib_id, void *val, size_t val_len) 1914a5fb1bbSJérôme Pouiller { 1924a5fb1bbSJérôme Pouiller int ret; 1934a5fb1bbSJérôme Pouiller struct wfx_hif_msg *hif; 1944a5fb1bbSJérôme Pouiller int buf_len = sizeof(struct wfx_hif_cnf_read_mib) + val_len; 1954a5fb1bbSJérôme Pouiller struct wfx_hif_req_read_mib *body = wfx_alloc_hif(sizeof(*body), &hif); 1964a5fb1bbSJérôme Pouiller struct wfx_hif_cnf_read_mib *reply = kmalloc(buf_len, GFP_KERNEL); 1974a5fb1bbSJérôme Pouiller 1984a5fb1bbSJérôme Pouiller if (!body || !reply) { 1994a5fb1bbSJérôme Pouiller ret = -ENOMEM; 2004a5fb1bbSJérôme Pouiller goto out; 2014a5fb1bbSJérôme Pouiller } 2024a5fb1bbSJérôme Pouiller body->mib_id = cpu_to_le16(mib_id); 2034a5fb1bbSJérôme Pouiller wfx_fill_header(hif, vif_id, HIF_REQ_ID_READ_MIB, sizeof(*body)); 2044a5fb1bbSJérôme Pouiller ret = wfx_cmd_send(wdev, hif, reply, buf_len, false); 2054a5fb1bbSJérôme Pouiller 2064a5fb1bbSJérôme Pouiller if (!ret && mib_id != le16_to_cpu(reply->mib_id)) { 2074a5fb1bbSJérôme Pouiller dev_warn(wdev->dev, "%s: confirmation mismatch request\n", __func__); 2084a5fb1bbSJérôme Pouiller ret = -EIO; 2094a5fb1bbSJérôme Pouiller } 2104a5fb1bbSJérôme Pouiller if (ret == -ENOMEM) 2114a5fb1bbSJérôme Pouiller dev_err(wdev->dev, "buffer is too small to receive %s (%zu < %d)\n", 2124a5fb1bbSJérôme Pouiller wfx_get_mib_name(mib_id), val_len, le16_to_cpu(reply->length)); 2134a5fb1bbSJérôme Pouiller if (!ret) 2144a5fb1bbSJérôme Pouiller memcpy(val, &reply->mib_data, le16_to_cpu(reply->length)); 2154a5fb1bbSJérôme Pouiller else 2164a5fb1bbSJérôme Pouiller memset(val, 0xFF, val_len); 2174a5fb1bbSJérôme Pouiller out: 2184a5fb1bbSJérôme Pouiller kfree(hif); 2194a5fb1bbSJérôme Pouiller kfree(reply); 2204a5fb1bbSJérôme Pouiller return ret; 2214a5fb1bbSJérôme Pouiller } 2224a5fb1bbSJérôme Pouiller 2234a5fb1bbSJérôme Pouiller int wfx_hif_write_mib(struct wfx_dev *wdev, int vif_id, u16 mib_id, void *val, size_t val_len) 2244a5fb1bbSJérôme Pouiller { 2254a5fb1bbSJérôme Pouiller int ret; 2264a5fb1bbSJérôme Pouiller struct wfx_hif_msg *hif; 2274a5fb1bbSJérôme Pouiller int buf_len = sizeof(struct wfx_hif_req_write_mib) + val_len; 2284a5fb1bbSJérôme Pouiller struct wfx_hif_req_write_mib *body = wfx_alloc_hif(buf_len, &hif); 2294a5fb1bbSJérôme Pouiller 2304a5fb1bbSJérôme Pouiller if (!hif) 2314a5fb1bbSJérôme Pouiller return -ENOMEM; 2324a5fb1bbSJérôme Pouiller body->mib_id = cpu_to_le16(mib_id); 2334a5fb1bbSJérôme Pouiller body->length = cpu_to_le16(val_len); 2344a5fb1bbSJérôme Pouiller memcpy(&body->mib_data, val, val_len); 2354a5fb1bbSJérôme Pouiller wfx_fill_header(hif, vif_id, HIF_REQ_ID_WRITE_MIB, buf_len); 2364a5fb1bbSJérôme Pouiller ret = wfx_cmd_send(wdev, hif, NULL, 0, false); 2374a5fb1bbSJérôme Pouiller kfree(hif); 2384a5fb1bbSJérôme Pouiller return ret; 2394a5fb1bbSJérôme Pouiller } 2404a5fb1bbSJérôme Pouiller 241*fc5cb24fSJérôme Pouiller /* Hijack scan request to implement Remain-On-Channel */ 242*fc5cb24fSJérôme Pouiller int wfx_hif_scan_uniq(struct wfx_vif *wvif, struct ieee80211_channel *chan, int duration) 243*fc5cb24fSJérôme Pouiller { 244*fc5cb24fSJérôme Pouiller int ret; 245*fc5cb24fSJérôme Pouiller struct wfx_hif_msg *hif; 246*fc5cb24fSJérôme Pouiller size_t buf_len = sizeof(struct wfx_hif_req_start_scan_alt) + sizeof(u8); 247*fc5cb24fSJérôme Pouiller struct wfx_hif_req_start_scan_alt *body = wfx_alloc_hif(buf_len, &hif); 248*fc5cb24fSJérôme Pouiller 249*fc5cb24fSJérôme Pouiller if (!hif) 250*fc5cb24fSJérôme Pouiller return -ENOMEM; 251*fc5cb24fSJérôme Pouiller body->num_of_ssids = HIF_API_MAX_NB_SSIDS; 252*fc5cb24fSJérôme Pouiller body->maintain_current_bss = 1; 253*fc5cb24fSJérôme Pouiller body->disallow_ps = 1; 254*fc5cb24fSJérôme Pouiller body->tx_power_level = cpu_to_le32(chan->max_power); 255*fc5cb24fSJérôme Pouiller body->num_of_channels = 1; 256*fc5cb24fSJérôme Pouiller body->channel_list[0] = chan->hw_value; 257*fc5cb24fSJérôme Pouiller body->max_transmit_rate = API_RATE_INDEX_B_1MBPS; 258*fc5cb24fSJérôme Pouiller body->min_channel_time = cpu_to_le32(duration); 259*fc5cb24fSJérôme Pouiller body->max_channel_time = cpu_to_le32(duration * 110 / 100); 260*fc5cb24fSJérôme Pouiller wfx_fill_header(hif, wvif->id, HIF_REQ_ID_START_SCAN, buf_len); 261*fc5cb24fSJérôme Pouiller ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); 262*fc5cb24fSJérôme Pouiller kfree(hif); 263*fc5cb24fSJérôme Pouiller return ret; 264*fc5cb24fSJérôme Pouiller } 265*fc5cb24fSJérôme Pouiller 2664a5fb1bbSJérôme Pouiller int wfx_hif_scan(struct wfx_vif *wvif, struct cfg80211_scan_request *req, 2674a5fb1bbSJérôme Pouiller int chan_start_idx, int chan_num) 2684a5fb1bbSJérôme Pouiller { 2694a5fb1bbSJérôme Pouiller int ret, i; 2704a5fb1bbSJérôme Pouiller struct wfx_hif_msg *hif; 2714a5fb1bbSJérôme Pouiller size_t buf_len = sizeof(struct wfx_hif_req_start_scan_alt) + chan_num * sizeof(u8); 2724a5fb1bbSJérôme Pouiller struct wfx_hif_req_start_scan_alt *body = wfx_alloc_hif(buf_len, &hif); 2734a5fb1bbSJérôme Pouiller 2744a5fb1bbSJérôme Pouiller WARN(chan_num > HIF_API_MAX_NB_CHANNELS, "invalid params"); 2754a5fb1bbSJérôme Pouiller WARN(req->n_ssids > HIF_API_MAX_NB_SSIDS, "invalid params"); 2764a5fb1bbSJérôme Pouiller 2774a5fb1bbSJérôme Pouiller if (!hif) 2784a5fb1bbSJérôme Pouiller return -ENOMEM; 2794a5fb1bbSJérôme Pouiller for (i = 0; i < req->n_ssids; i++) { 2804a5fb1bbSJérôme Pouiller memcpy(body->ssid_def[i].ssid, req->ssids[i].ssid, IEEE80211_MAX_SSID_LEN); 2814a5fb1bbSJérôme Pouiller body->ssid_def[i].ssid_length = cpu_to_le32(req->ssids[i].ssid_len); 2824a5fb1bbSJérôme Pouiller } 2834a5fb1bbSJérôme Pouiller body->num_of_ssids = HIF_API_MAX_NB_SSIDS; 2844a5fb1bbSJérôme Pouiller body->maintain_current_bss = 1; 2854a5fb1bbSJérôme Pouiller body->disallow_ps = 1; 2864a5fb1bbSJérôme Pouiller body->tx_power_level = cpu_to_le32(req->channels[chan_start_idx]->max_power); 2874a5fb1bbSJérôme Pouiller body->num_of_channels = chan_num; 2884a5fb1bbSJérôme Pouiller for (i = 0; i < chan_num; i++) 2894a5fb1bbSJérôme Pouiller body->channel_list[i] = req->channels[i + chan_start_idx]->hw_value; 2904a5fb1bbSJérôme Pouiller if (req->no_cck) 2914a5fb1bbSJérôme Pouiller body->max_transmit_rate = API_RATE_INDEX_G_6MBPS; 2924a5fb1bbSJérôme Pouiller else 2934a5fb1bbSJérôme Pouiller body->max_transmit_rate = API_RATE_INDEX_B_1MBPS; 2944a5fb1bbSJérôme Pouiller if (req->channels[chan_start_idx]->flags & IEEE80211_CHAN_NO_IR) { 2954a5fb1bbSJérôme Pouiller body->min_channel_time = cpu_to_le32(50); 2964a5fb1bbSJérôme Pouiller body->max_channel_time = cpu_to_le32(150); 2974a5fb1bbSJérôme Pouiller } else { 2984a5fb1bbSJérôme Pouiller body->min_channel_time = cpu_to_le32(10); 2994a5fb1bbSJérôme Pouiller body->max_channel_time = cpu_to_le32(50); 3004a5fb1bbSJérôme Pouiller body->num_of_probe_requests = 2; 3014a5fb1bbSJérôme Pouiller body->probe_delay = 100; 3024a5fb1bbSJérôme Pouiller } 3034a5fb1bbSJérôme Pouiller 3044a5fb1bbSJérôme Pouiller wfx_fill_header(hif, wvif->id, HIF_REQ_ID_START_SCAN, buf_len); 3054a5fb1bbSJérôme Pouiller ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); 3064a5fb1bbSJérôme Pouiller kfree(hif); 3074a5fb1bbSJérôme Pouiller return ret; 3084a5fb1bbSJérôme Pouiller } 3094a5fb1bbSJérôme Pouiller 3104a5fb1bbSJérôme Pouiller int wfx_hif_stop_scan(struct wfx_vif *wvif) 3114a5fb1bbSJérôme Pouiller { 3124a5fb1bbSJérôme Pouiller int ret; 3134a5fb1bbSJérôme Pouiller struct wfx_hif_msg *hif; 3144a5fb1bbSJérôme Pouiller /* body associated to HIF_REQ_ID_STOP_SCAN is empty */ 3154a5fb1bbSJérôme Pouiller wfx_alloc_hif(0, &hif); 3164a5fb1bbSJérôme Pouiller 3174a5fb1bbSJérôme Pouiller if (!hif) 3184a5fb1bbSJérôme Pouiller return -ENOMEM; 3194a5fb1bbSJérôme Pouiller wfx_fill_header(hif, wvif->id, HIF_REQ_ID_STOP_SCAN, 0); 3204a5fb1bbSJérôme Pouiller ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); 3214a5fb1bbSJérôme Pouiller kfree(hif); 3224a5fb1bbSJérôme Pouiller return ret; 3234a5fb1bbSJérôme Pouiller } 3244a5fb1bbSJérôme Pouiller 3254a5fb1bbSJérôme Pouiller int wfx_hif_join(struct wfx_vif *wvif, const struct ieee80211_bss_conf *conf, 326f8525b97SGreg Kroah-Hartman struct ieee80211_channel *channel, const u8 *ssid, int ssid_len) 3274a5fb1bbSJérôme Pouiller { 328f276e20bSJohannes Berg struct ieee80211_vif *vif = container_of(conf, struct ieee80211_vif, 329f276e20bSJohannes Berg bss_conf); 3304a5fb1bbSJérôme Pouiller int ret; 3314a5fb1bbSJérôme Pouiller struct wfx_hif_msg *hif; 3324a5fb1bbSJérôme Pouiller struct wfx_hif_req_join *body = wfx_alloc_hif(sizeof(*body), &hif); 3334a5fb1bbSJérôme Pouiller 3344a5fb1bbSJérôme Pouiller WARN_ON(!conf->beacon_int); 3354a5fb1bbSJérôme Pouiller WARN_ON(!conf->basic_rates); 336f8525b97SGreg Kroah-Hartman WARN_ON(sizeof(body->ssid) < ssid_len); 337f276e20bSJohannes Berg WARN(!vif->cfg.ibss_joined && !ssid_len, "joining an unknown BSS"); 3384a5fb1bbSJérôme Pouiller if (!hif) 3394a5fb1bbSJérôme Pouiller return -ENOMEM; 340f276e20bSJohannes Berg body->infrastructure_bss_mode = !vif->cfg.ibss_joined; 3414a5fb1bbSJérôme Pouiller body->short_preamble = conf->use_short_preamble; 3424a5fb1bbSJérôme Pouiller body->probe_for_join = !(channel->flags & IEEE80211_CHAN_NO_IR); 3434a5fb1bbSJérôme Pouiller body->channel_number = channel->hw_value; 3444a5fb1bbSJérôme Pouiller body->beacon_interval = cpu_to_le32(conf->beacon_int); 3454a5fb1bbSJérôme Pouiller body->basic_rate_set = cpu_to_le32(wfx_rate_mask_to_hw(wvif->wdev, conf->basic_rates)); 3464a5fb1bbSJérôme Pouiller memcpy(body->bssid, conf->bssid, sizeof(body->bssid)); 3474a5fb1bbSJérôme Pouiller if (ssid) { 348f8525b97SGreg Kroah-Hartman body->ssid_length = cpu_to_le32(ssid_len); 349f8525b97SGreg Kroah-Hartman memcpy(body->ssid, ssid, ssid_len); 3504a5fb1bbSJérôme Pouiller } 3514a5fb1bbSJérôme Pouiller wfx_fill_header(hif, wvif->id, HIF_REQ_ID_JOIN, sizeof(*body)); 3524a5fb1bbSJérôme Pouiller ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); 3534a5fb1bbSJérôme Pouiller kfree(hif); 3544a5fb1bbSJérôme Pouiller return ret; 3554a5fb1bbSJérôme Pouiller } 3564a5fb1bbSJérôme Pouiller 3574a5fb1bbSJérôme Pouiller int wfx_hif_set_bss_params(struct wfx_vif *wvif, int aid, int beacon_lost_count) 3584a5fb1bbSJérôme Pouiller { 3594a5fb1bbSJérôme Pouiller int ret; 3604a5fb1bbSJérôme Pouiller struct wfx_hif_msg *hif; 3614a5fb1bbSJérôme Pouiller struct wfx_hif_req_set_bss_params *body = wfx_alloc_hif(sizeof(*body), &hif); 3624a5fb1bbSJérôme Pouiller 3634a5fb1bbSJérôme Pouiller if (!hif) 3644a5fb1bbSJérôme Pouiller return -ENOMEM; 3654a5fb1bbSJérôme Pouiller body->aid = cpu_to_le16(aid); 3664a5fb1bbSJérôme Pouiller body->beacon_lost_count = beacon_lost_count; 3674a5fb1bbSJérôme Pouiller wfx_fill_header(hif, wvif->id, HIF_REQ_ID_SET_BSS_PARAMS, sizeof(*body)); 3684a5fb1bbSJérôme Pouiller ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); 3694a5fb1bbSJérôme Pouiller kfree(hif); 3704a5fb1bbSJérôme Pouiller return ret; 3714a5fb1bbSJérôme Pouiller } 3724a5fb1bbSJérôme Pouiller 3734a5fb1bbSJérôme Pouiller int wfx_hif_add_key(struct wfx_dev *wdev, const struct wfx_hif_req_add_key *arg) 3744a5fb1bbSJérôme Pouiller { 3754a5fb1bbSJérôme Pouiller int ret; 3764a5fb1bbSJérôme Pouiller struct wfx_hif_msg *hif; 3774a5fb1bbSJérôme Pouiller /* FIXME: only send necessary bits */ 3784a5fb1bbSJérôme Pouiller struct wfx_hif_req_add_key *body = wfx_alloc_hif(sizeof(*body), &hif); 3794a5fb1bbSJérôme Pouiller 3804a5fb1bbSJérôme Pouiller if (!hif) 3814a5fb1bbSJérôme Pouiller return -ENOMEM; 3824a5fb1bbSJérôme Pouiller /* FIXME: swap bytes as necessary in body */ 3834a5fb1bbSJérôme Pouiller memcpy(body, arg, sizeof(*body)); 3844a5fb1bbSJérôme Pouiller if (wfx_api_older_than(wdev, 1, 5)) 3854a5fb1bbSJérôme Pouiller /* Legacy firmwares expect that add_key to be sent on right interface. */ 3864a5fb1bbSJérôme Pouiller wfx_fill_header(hif, arg->int_id, HIF_REQ_ID_ADD_KEY, sizeof(*body)); 3874a5fb1bbSJérôme Pouiller else 3884a5fb1bbSJérôme Pouiller wfx_fill_header(hif, -1, HIF_REQ_ID_ADD_KEY, sizeof(*body)); 3894a5fb1bbSJérôme Pouiller ret = wfx_cmd_send(wdev, hif, NULL, 0, false); 3904a5fb1bbSJérôme Pouiller kfree(hif); 3914a5fb1bbSJérôme Pouiller return ret; 3924a5fb1bbSJérôme Pouiller } 3934a5fb1bbSJérôme Pouiller 3944a5fb1bbSJérôme Pouiller int wfx_hif_remove_key(struct wfx_dev *wdev, int idx) 3954a5fb1bbSJérôme Pouiller { 3964a5fb1bbSJérôme Pouiller int ret; 3974a5fb1bbSJérôme Pouiller struct wfx_hif_msg *hif; 3984a5fb1bbSJérôme Pouiller struct wfx_hif_req_remove_key *body = wfx_alloc_hif(sizeof(*body), &hif); 3994a5fb1bbSJérôme Pouiller 4004a5fb1bbSJérôme Pouiller if (!hif) 4014a5fb1bbSJérôme Pouiller return -ENOMEM; 4024a5fb1bbSJérôme Pouiller body->entry_index = idx; 4034a5fb1bbSJérôme Pouiller wfx_fill_header(hif, -1, HIF_REQ_ID_REMOVE_KEY, sizeof(*body)); 4044a5fb1bbSJérôme Pouiller ret = wfx_cmd_send(wdev, hif, NULL, 0, false); 4054a5fb1bbSJérôme Pouiller kfree(hif); 4064a5fb1bbSJérôme Pouiller return ret; 4074a5fb1bbSJérôme Pouiller } 4084a5fb1bbSJérôme Pouiller 4094a5fb1bbSJérôme Pouiller int wfx_hif_set_edca_queue_params(struct wfx_vif *wvif, u16 queue, 4104a5fb1bbSJérôme Pouiller const struct ieee80211_tx_queue_params *arg) 4114a5fb1bbSJérôme Pouiller { 4124a5fb1bbSJérôme Pouiller int ret; 4134a5fb1bbSJérôme Pouiller struct wfx_hif_msg *hif; 4144a5fb1bbSJérôme Pouiller struct wfx_hif_req_edca_queue_params *body = wfx_alloc_hif(sizeof(*body), &hif); 4154a5fb1bbSJérôme Pouiller 4164a5fb1bbSJérôme Pouiller if (!body) 4174a5fb1bbSJérôme Pouiller return -ENOMEM; 4184a5fb1bbSJérôme Pouiller 4194a5fb1bbSJérôme Pouiller WARN_ON(arg->aifs > 255); 4204a5fb1bbSJérôme Pouiller if (!hif) 4214a5fb1bbSJérôme Pouiller return -ENOMEM; 4224a5fb1bbSJérôme Pouiller body->aifsn = arg->aifs; 4234a5fb1bbSJérôme Pouiller body->cw_min = cpu_to_le16(arg->cw_min); 4244a5fb1bbSJérôme Pouiller body->cw_max = cpu_to_le16(arg->cw_max); 4254a5fb1bbSJérôme Pouiller body->tx_op_limit = cpu_to_le16(arg->txop * USEC_PER_TXOP); 4264a5fb1bbSJérôme Pouiller body->queue_id = 3 - queue; 4274a5fb1bbSJérôme Pouiller /* API 2.0 has changed queue IDs values */ 4284a5fb1bbSJérôme Pouiller if (wfx_api_older_than(wvif->wdev, 2, 0) && queue == IEEE80211_AC_BE) 4294a5fb1bbSJérôme Pouiller body->queue_id = HIF_QUEUE_ID_BACKGROUND; 4304a5fb1bbSJérôme Pouiller if (wfx_api_older_than(wvif->wdev, 2, 0) && queue == IEEE80211_AC_BK) 4314a5fb1bbSJérôme Pouiller body->queue_id = HIF_QUEUE_ID_BESTEFFORT; 4324a5fb1bbSJérôme Pouiller wfx_fill_header(hif, wvif->id, HIF_REQ_ID_EDCA_QUEUE_PARAMS, sizeof(*body)); 4334a5fb1bbSJérôme Pouiller ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); 4344a5fb1bbSJérôme Pouiller kfree(hif); 4354a5fb1bbSJérôme Pouiller return ret; 4364a5fb1bbSJérôme Pouiller } 4374a5fb1bbSJérôme Pouiller 4384a5fb1bbSJérôme Pouiller int wfx_hif_set_pm(struct wfx_vif *wvif, bool ps, int dynamic_ps_timeout) 4394a5fb1bbSJérôme Pouiller { 4404a5fb1bbSJérôme Pouiller int ret; 4414a5fb1bbSJérôme Pouiller struct wfx_hif_msg *hif; 4424a5fb1bbSJérôme Pouiller struct wfx_hif_req_set_pm_mode *body = wfx_alloc_hif(sizeof(*body), &hif); 4434a5fb1bbSJérôme Pouiller 4444a5fb1bbSJérôme Pouiller if (!body) 4454a5fb1bbSJérôme Pouiller return -ENOMEM; 4464a5fb1bbSJérôme Pouiller 4474a5fb1bbSJérôme Pouiller if (!hif) 4484a5fb1bbSJérôme Pouiller return -ENOMEM; 4494a5fb1bbSJérôme Pouiller if (ps) { 4504a5fb1bbSJérôme Pouiller body->enter_psm = 1; 4514a5fb1bbSJérôme Pouiller /* Firmware does not support more than 128ms */ 4524a5fb1bbSJérôme Pouiller body->fast_psm_idle_period = min(dynamic_ps_timeout * 2, 255); 4534a5fb1bbSJérôme Pouiller if (body->fast_psm_idle_period) 4544a5fb1bbSJérôme Pouiller body->fast_psm = 1; 4554a5fb1bbSJérôme Pouiller } 4564a5fb1bbSJérôme Pouiller wfx_fill_header(hif, wvif->id, HIF_REQ_ID_SET_PM_MODE, sizeof(*body)); 4574a5fb1bbSJérôme Pouiller ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); 4584a5fb1bbSJérôme Pouiller kfree(hif); 4594a5fb1bbSJérôme Pouiller return ret; 4604a5fb1bbSJérôme Pouiller } 4614a5fb1bbSJérôme Pouiller 4624a5fb1bbSJérôme Pouiller int wfx_hif_start(struct wfx_vif *wvif, const struct ieee80211_bss_conf *conf, 4634a5fb1bbSJérôme Pouiller const struct ieee80211_channel *channel) 4644a5fb1bbSJérôme Pouiller { 465f276e20bSJohannes Berg struct ieee80211_vif *vif = container_of(conf, struct ieee80211_vif, 466f276e20bSJohannes Berg bss_conf); 4674a5fb1bbSJérôme Pouiller int ret; 4684a5fb1bbSJérôme Pouiller struct wfx_hif_msg *hif; 4694a5fb1bbSJérôme Pouiller struct wfx_hif_req_start *body = wfx_alloc_hif(sizeof(*body), &hif); 4704a5fb1bbSJérôme Pouiller 4714a5fb1bbSJérôme Pouiller WARN_ON(!conf->beacon_int); 4724a5fb1bbSJérôme Pouiller if (!hif) 4734a5fb1bbSJérôme Pouiller return -ENOMEM; 4744a5fb1bbSJérôme Pouiller body->dtim_period = conf->dtim_period; 4754a5fb1bbSJérôme Pouiller body->short_preamble = conf->use_short_preamble; 4764a5fb1bbSJérôme Pouiller body->channel_number = channel->hw_value; 4774a5fb1bbSJérôme Pouiller body->beacon_interval = cpu_to_le32(conf->beacon_int); 4784a5fb1bbSJérôme Pouiller body->basic_rate_set = cpu_to_le32(wfx_rate_mask_to_hw(wvif->wdev, conf->basic_rates)); 479f276e20bSJohannes Berg body->ssid_length = vif->cfg.ssid_len; 480f276e20bSJohannes Berg memcpy(body->ssid, vif->cfg.ssid, vif->cfg.ssid_len); 4814a5fb1bbSJérôme Pouiller wfx_fill_header(hif, wvif->id, HIF_REQ_ID_START, sizeof(*body)); 4824a5fb1bbSJérôme Pouiller ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); 4834a5fb1bbSJérôme Pouiller kfree(hif); 4844a5fb1bbSJérôme Pouiller return ret; 4854a5fb1bbSJérôme Pouiller } 4864a5fb1bbSJérôme Pouiller 4874a5fb1bbSJérôme Pouiller int wfx_hif_beacon_transmit(struct wfx_vif *wvif, bool enable) 4884a5fb1bbSJérôme Pouiller { 4894a5fb1bbSJérôme Pouiller int ret; 4904a5fb1bbSJérôme Pouiller struct wfx_hif_msg *hif; 4914a5fb1bbSJérôme Pouiller struct wfx_hif_req_beacon_transmit *body = wfx_alloc_hif(sizeof(*body), &hif); 4924a5fb1bbSJérôme Pouiller 4934a5fb1bbSJérôme Pouiller if (!hif) 4944a5fb1bbSJérôme Pouiller return -ENOMEM; 4954a5fb1bbSJérôme Pouiller body->enable_beaconing = enable ? 1 : 0; 4964a5fb1bbSJérôme Pouiller wfx_fill_header(hif, wvif->id, HIF_REQ_ID_BEACON_TRANSMIT, sizeof(*body)); 4974a5fb1bbSJérôme Pouiller ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); 4984a5fb1bbSJérôme Pouiller kfree(hif); 4994a5fb1bbSJérôme Pouiller return ret; 5004a5fb1bbSJérôme Pouiller } 5014a5fb1bbSJérôme Pouiller 5024a5fb1bbSJérôme Pouiller int wfx_hif_map_link(struct wfx_vif *wvif, bool unmap, u8 *mac_addr, int sta_id, bool mfp) 5034a5fb1bbSJérôme Pouiller { 5044a5fb1bbSJérôme Pouiller int ret; 5054a5fb1bbSJérôme Pouiller struct wfx_hif_msg *hif; 5064a5fb1bbSJérôme Pouiller struct wfx_hif_req_map_link *body = wfx_alloc_hif(sizeof(*body), &hif); 5074a5fb1bbSJérôme Pouiller 5084a5fb1bbSJérôme Pouiller if (!hif) 5094a5fb1bbSJérôme Pouiller return -ENOMEM; 5104a5fb1bbSJérôme Pouiller if (mac_addr) 5114a5fb1bbSJérôme Pouiller ether_addr_copy(body->mac_addr, mac_addr); 5124a5fb1bbSJérôme Pouiller body->mfpc = mfp ? 1 : 0; 5134a5fb1bbSJérôme Pouiller body->unmap = unmap ? 1 : 0; 5144a5fb1bbSJérôme Pouiller body->peer_sta_id = sta_id; 5154a5fb1bbSJérôme Pouiller wfx_fill_header(hif, wvif->id, HIF_REQ_ID_MAP_LINK, sizeof(*body)); 5164a5fb1bbSJérôme Pouiller ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); 5174a5fb1bbSJérôme Pouiller kfree(hif); 5184a5fb1bbSJérôme Pouiller return ret; 5194a5fb1bbSJérôme Pouiller } 5204a5fb1bbSJérôme Pouiller 5214a5fb1bbSJérôme Pouiller int wfx_hif_update_ie_beacon(struct wfx_vif *wvif, const u8 *ies, size_t ies_len) 5224a5fb1bbSJérôme Pouiller { 5234a5fb1bbSJérôme Pouiller int ret; 5244a5fb1bbSJérôme Pouiller struct wfx_hif_msg *hif; 5254a5fb1bbSJérôme Pouiller int buf_len = sizeof(struct wfx_hif_req_update_ie) + ies_len; 5264a5fb1bbSJérôme Pouiller struct wfx_hif_req_update_ie *body = wfx_alloc_hif(buf_len, &hif); 5274a5fb1bbSJérôme Pouiller 5284a5fb1bbSJérôme Pouiller if (!hif) 5294a5fb1bbSJérôme Pouiller return -ENOMEM; 5304a5fb1bbSJérôme Pouiller body->beacon = 1; 5314a5fb1bbSJérôme Pouiller body->num_ies = cpu_to_le16(1); 5324a5fb1bbSJérôme Pouiller memcpy(body->ie, ies, ies_len); 5334a5fb1bbSJérôme Pouiller wfx_fill_header(hif, wvif->id, HIF_REQ_ID_UPDATE_IE, buf_len); 5344a5fb1bbSJérôme Pouiller ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); 5354a5fb1bbSJérôme Pouiller kfree(hif); 5364a5fb1bbSJérôme Pouiller return ret; 5374a5fb1bbSJérôme Pouiller } 538