wmi.c 52.5 KB
Newer Older
1
/*
2
 * Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
3 4 5 6 7 8 9 10 11 12 13 14 15 16
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

17
#include <linux/moduleparam.h>
18
#include <linux/etherdevice.h>
19
#include <linux/if_arp.h>
20 21

#include "wil6210.h"
22
#include "txrx.h"
23
#include "wmi.h"
V
Vladimir Kondratiev 已提交
24
#include "trace.h"
25

26
static uint max_assoc_sta = WIL6210_MAX_CID;
27
module_param(max_assoc_sta, uint, 0644);
28 29
MODULE_PARM_DESC(max_assoc_sta, " Max number of stations associated to the AP");

30
int agg_wsize; /* = 0; */
31
module_param(agg_wsize, int, 0644);
32 33 34
MODULE_PARM_DESC(agg_wsize, " Window size for Tx Block Ack after connect;"
		 " 0 - use default; < 0 - don't auto-establish");

35
u8 led_id = WIL_LED_INVALID_ID;
36
module_param(led_id, byte, 0444);
37 38 39
MODULE_PARM_DESC(led_id,
		 " 60G device led enablement. Set the led ID (0-2) to enable");

40 41
#define WIL_WAIT_FOR_SUSPEND_RESUME_COMP 200

42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
/**
 * WMI event receiving - theory of operations
 *
 * When firmware about to report WMI event, it fills memory area
 * in the mailbox and raises misc. IRQ. Thread interrupt handler invoked for
 * the misc IRQ, function @wmi_recv_cmd called by thread IRQ handler.
 *
 * @wmi_recv_cmd reads event, allocates memory chunk  and attaches it to the
 * event list @wil->pending_wmi_ev. Then, work queue @wil->wmi_wq wakes up
 * and handles events within the @wmi_event_worker. Every event get detached
 * from list, processed and deleted.
 *
 * Purpose for this mechanism is to release IRQ thread; otherwise,
 * if WMI event handling involves another WMI command flow, this 2-nd flow
 * won't be completed because of blocked IRQ thread.
 */

/**
 * Addressing - theory of operations
 *
 * There are several buses present on the WIL6210 card.
 * Same memory areas are visible at different address on
 * the different busses. There are 3 main bus masters:
 *  - MAC CPU (ucode)
 *  - User CPU (firmware)
 *  - AHB (host)
 *
 * On the PCI bus, there is one BAR (BAR0) of 2Mb size, exposing
 * AHB addresses starting from 0x880000
 *
 * Internally, firmware uses addresses that allows faster access but
 * are invisible from the host. To read from these addresses, alternative
 * AHB address must be used.
 *
 * Memory mapping
 * Linker address         PCI/Host address
 *                        0x880000 .. 0xa80000  2Mb BAR0
 * 0x800000 .. 0x807000   0x900000 .. 0x907000  28k DCCM
 * 0x840000 .. 0x857000   0x908000 .. 0x91f000  92k PERIPH
 */

/**
 * @fw_mapping provides memory remapping table
85 86
 *
 * array size should be in sync with the declaration in the wil6210.h
87
 */
88
const struct fw_map fw_mapping[] = {
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
	/* FW code RAM 256k */
	{0x000000, 0x040000, 0x8c0000, "fw_code", true},
	/* FW data RAM 32k */
	{0x800000, 0x808000, 0x900000, "fw_data", true},
	/* periph data 128k */
	{0x840000, 0x860000, 0x908000, "fw_peri", true},
	/* various RGF 40k */
	{0x880000, 0x88a000, 0x880000, "rgf", true},
	/* AGC table   4k */
	{0x88a000, 0x88b000, 0x88a000, "AGC_tbl", true},
	/* Pcie_ext_rgf 4k */
	{0x88b000, 0x88c000, 0x88b000, "rgf_ext", true},
	/* mac_ext_rgf 512b */
	{0x88c000, 0x88c200, 0x88c000, "mac_rgf_ext", true},
	/* upper area 548k */
	{0x8c0000, 0x949000, 0x8c0000, "upper", true},
	/* UCODE areas - accessible by debugfs blobs but not by
	 * wmi_addr_remap. UCODE areas MUST be added AFTER FW areas!
107
	 */
108 109 110 111
	/* ucode code RAM 128k */
	{0x000000, 0x020000, 0x920000, "uc_code", false},
	/* ucode data RAM 16k */
	{0x800000, 0x804000, 0x940000, "uc_data", false},
112 113
};

114 115 116 117 118 119 120 121
struct blink_on_off_time led_blink_time[] = {
	{WIL_LED_BLINK_ON_SLOW_MS, WIL_LED_BLINK_OFF_SLOW_MS},
	{WIL_LED_BLINK_ON_MED_MS, WIL_LED_BLINK_OFF_MED_MS},
	{WIL_LED_BLINK_ON_FAST_MS, WIL_LED_BLINK_OFF_FAST_MS},
};

u8 led_polarity = LED_POLARITY_LOW_ACTIVE;

122
/**
123
 * return AHB address for given firmware internal (linker) address
124 125 126 127 128 129 130 131
 * @x - internal address
 * If address have no valid AHB mapping, return 0
 */
static u32 wmi_addr_remap(u32 x)
{
	uint i;

	for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) {
132 133
		if (fw_mapping[i].fw &&
		    ((x >= fw_mapping[i].from) && (x < fw_mapping[i].to)))
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
			return x + fw_mapping[i].host - fw_mapping[i].from;
	}

	return 0;
}

/**
 * Check address validity for WMI buffer; remap if needed
 * @ptr - internal (linker) fw/ucode address
 *
 * Valid buffer should be DWORD aligned
 *
 * return address for accessing buffer from the host;
 * if buffer is not valid, return NULL.
 */
void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_)
{
	u32 off;
	u32 ptr = le32_to_cpu(ptr_);

	if (ptr % 4)
		return NULL;

	ptr = wmi_addr_remap(ptr);
	if (ptr < WIL6210_FW_HOST_OFF)
		return NULL;

	off = HOSTADDR(ptr);
162
	if (off > wil->bar_size - 4)
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
		return NULL;

	return wil->csr + off;
}

/**
 * Check address validity
 */
void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr)
{
	u32 off;

	if (ptr % 4)
		return NULL;

	if (ptr < WIL6210_FW_HOST_OFF)
		return NULL;

	off = HOSTADDR(ptr);
182
	if (off > wil->bar_size - 4)
183 184 185 186 187 188 189 190 191
		return NULL;

	return wil->csr + off;
}

int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr,
		 struct wil6210_mbox_hdr *hdr)
{
	void __iomem *src = wmi_buffer(wil, ptr);
192

193 194 195 196 197 198 199 200 201 202 203 204
	if (!src)
		return -EINVAL;

	wil_memcpy_fromio_32(hdr, src, sizeof(*hdr));

	return 0;
}

static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
{
	struct {
		struct wil6210_mbox_hdr hdr;
L
Lior David 已提交
205
		struct wmi_cmd_hdr wmi;
206 207 208 209 210 211 212
	} __packed cmd = {
		.hdr = {
			.type = WIL_MBOX_HDR_TYPE_WMI,
			.flags = 0,
			.len = cpu_to_le16(sizeof(cmd.wmi) + len),
		},
		.wmi = {
213
			.mid = 0,
L
Lior David 已提交
214
			.command_id = cpu_to_le16(cmdid),
215 216 217 218 219 220 221 222
		},
	};
	struct wil6210_mbox_ring *r = &wil->mbox_ctl.tx;
	struct wil6210_mbox_ring_desc d_head;
	u32 next_head;
	void __iomem *dst;
	void __iomem *head = wmi_addr(wil, r->head);
	uint retry;
223
	int rc = 0;
224 225 226 227 228 229 230 231 232

	if (sizeof(cmd) + len > r->entry_size) {
		wil_err(wil, "WMI size too large: %d bytes, max is %d\n",
			(int)(sizeof(cmd) + len), r->entry_size);
		return -ERANGE;
	}

	might_sleep();

233
	if (!test_bit(wil_status_fwready, wil->status)) {
234
		wil_err(wil, "WMI: cannot send command while FW not ready\n");
235 236 237
		return -EAGAIN;
	}

238 239 240 241 242 243 244 245 246 247
	/* Allow sending only suspend / resume commands during susepnd flow */
	if ((test_bit(wil_status_suspending, wil->status) ||
	     test_bit(wil_status_suspended, wil->status) ||
	     test_bit(wil_status_resuming, wil->status)) &&
	     ((cmdid != WMI_TRAFFIC_SUSPEND_CMDID) &&
	      (cmdid != WMI_TRAFFIC_RESUME_CMDID))) {
		wil_err(wil, "WMI: reject send_command during suspend\n");
		return -EINVAL;
	}

248 249 250 251
	if (!head) {
		wil_err(wil, "WMI head is garbage: 0x%08x\n", r->head);
		return -EINVAL;
	}
252 253 254

	wil_halp_vote(wil);

255 256 257 258 259 260 261 262 263
	/* read Tx head till it is not busy */
	for (retry = 5; retry > 0; retry--) {
		wil_memcpy_fromio_32(&d_head, head, sizeof(d_head));
		if (d_head.sync == 0)
			break;
		msleep(20);
	}
	if (d_head.sync != 0) {
		wil_err(wil, "WMI head busy\n");
264 265
		rc = -EBUSY;
		goto out;
266 267 268
	}
	/* next head */
	next_head = r->base + ((r->head - r->base + sizeof(d_head)) % r->size);
269
	wil_dbg_wmi(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head);
270 271
	/* wait till FW finish with previous command */
	for (retry = 5; retry > 0; retry--) {
272 273
		if (!test_bit(wil_status_fwready, wil->status)) {
			wil_err(wil, "WMI: cannot send command while FW not ready\n");
274 275
			rc = -EAGAIN;
			goto out;
276
		}
277 278
		r->tail = wil_r(wil, RGF_MBOX +
				offsetof(struct wil6210_mbox_ctl, tx.tail));
279 280 281 282 283 284
		if (next_head != r->tail)
			break;
		msleep(20);
	}
	if (next_head == r->tail) {
		wil_err(wil, "WMI ring full\n");
285 286
		rc = -EBUSY;
		goto out;
287 288 289 290 291
	}
	dst = wmi_buffer(wil, d_head.addr);
	if (!dst) {
		wil_err(wil, "invalid WMI buffer: 0x%08x\n",
			le32_to_cpu(d_head.addr));
292 293
		rc = -EAGAIN;
		goto out;
294 295 296
	}
	cmd.hdr.seq = cpu_to_le16(++wil->wmi_seq);
	/* set command */
297 298
	wil_dbg_wmi(wil, "WMI command 0x%04x [%d]\n", cmdid, len);
	wil_hex_dump_wmi("Cmd ", DUMP_PREFIX_OFFSET, 16, 1, &cmd,
299
			 sizeof(cmd), true);
300
	wil_hex_dump_wmi("cmd ", DUMP_PREFIX_OFFSET, 16, 1, buf,
301 302 303 304
			 len, true);
	wil_memcpy_toio_32(dst, &cmd, sizeof(cmd));
	wil_memcpy_toio_32(dst + sizeof(cmd), buf, len);
	/* mark entry as full */
305
	wil_w(wil, r->head + offsetof(struct wil6210_mbox_ring_desc, sync), 1);
306
	/* advance next ptr */
307 308
	wil_w(wil, RGF_MBOX + offsetof(struct wil6210_mbox_ctl, tx.head),
	      r->head = next_head);
309

310
	trace_wil6210_wmi_cmd(&cmd.wmi, buf, len);
V
Vladimir Kondratiev 已提交
311

312
	/* interrupt to FW */
313 314
	wil_w(wil, RGF_USER_USER_ICR + offsetof(struct RGF_ICR, ICS),
	      SW_INT_MBOX);
315

316 317 318
out:
	wil_halp_unvote(wil);
	return rc;
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
}

int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
{
	int rc;

	mutex_lock(&wil->wmi_mutex);
	rc = __wmi_send(wil, cmdid, buf, len);
	mutex_unlock(&wil->wmi_mutex);

	return rc;
}

/*=== Event handlers ===*/
static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len)
{
	struct wireless_dev *wdev = wil->wdev;
	struct wmi_ready_event *evt = d;
337

338
	wil->n_mids = evt->numof_additional_mids;
339

340 341
	wil_info(wil, "FW ver. %s(SW %d); MAC %pM; %d MID's\n",
		 wil->fw_version, le32_to_cpu(evt->sw_version),
342
		 evt->mac, wil->n_mids);
V
Vladimir Kondratiev 已提交
343
	/* ignore MAC address, we already have it from the boot loader */
344 345
	strlcpy(wdev->wiphy->fw_version, wil->fw_version,
		sizeof(wdev->wiphy->fw_version));
346

347
	wil_set_recovery_state(wil, fw_recovery_idle);
348
	set_bit(wil_status_fwready, wil->status);
349
	/* let the reset sequence continue */
350 351 352 353 354 355 356 357 358
	complete(&wil->wmi_ready);
}

static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
{
	struct wmi_rx_mgmt_packet_event *data = d;
	struct wiphy *wiphy = wil_to_wiphy(wil);
	struct ieee80211_mgmt *rx_mgmt_frame =
			(struct ieee80211_mgmt *)data->payload;
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
	int flen = len - offsetof(struct wmi_rx_mgmt_packet_event, payload);
	int ch_no;
	u32 freq;
	struct ieee80211_channel *channel;
	s32 signal;
	__le16 fc;
	u32 d_len;
	u16 d_status;

	if (flen < 0) {
		wil_err(wil, "MGMT Rx: short event, len %d\n", len);
		return;
	}

	d_len = le32_to_cpu(data->info.len);
	if (d_len != flen) {
		wil_err(wil,
			"MGMT Rx: length mismatch, d_len %d should be %d\n",
			d_len, flen);
		return;
	}

	ch_no = data->info.channel + 1;
382
	freq = ieee80211_channel_to_frequency(ch_no, NL80211_BAND_60GHZ);
383
	channel = ieee80211_get_channel(wiphy, freq);
D
Dedy Lansky 已提交
384 385 386 387
	if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING, wil->fw_capabilities))
		signal = 100 * data->info.rssi;
	else
		signal = data->info.sqi;
388 389 390
	d_status = le16_to_cpu(data->info.status);
	fc = rx_mgmt_frame->frame_control;

D
Dedy Lansky 已提交
391 392
	wil_dbg_wmi(wil, "MGMT Rx: channel %d MCS %d RSSI %d SQI %d%%\n",
		    data->info.channel, data->info.mcs, data->info.rssi,
393
		    data->info.sqi);
394 395
	wil_dbg_wmi(wil, "status 0x%04x len %d fc 0x%04x\n", d_status, d_len,
		    le16_to_cpu(fc));
396
	wil_dbg_wmi(wil, "qid %d mid %d cid %d\n",
397
		    data->info.qid, data->info.mid, data->info.cid);
398 399
	wil_hex_dump_wmi("MGMT Rx ", DUMP_PREFIX_OFFSET, 16, 1, rx_mgmt_frame,
			 d_len, true);
400 401 402 403 404 405 406 407

	if (!channel) {
		wil_err(wil, "Frame on unsupported channel\n");
		return;
	}

	if (ieee80211_is_beacon(fc) || ieee80211_is_probe_resp(fc)) {
		struct cfg80211_bss *bss;
408 409 410 411 412 413 414 415 416 417 418
		u64 tsf = le64_to_cpu(rx_mgmt_frame->u.beacon.timestamp);
		u16 cap = le16_to_cpu(rx_mgmt_frame->u.beacon.capab_info);
		u16 bi = le16_to_cpu(rx_mgmt_frame->u.beacon.beacon_int);
		const u8 *ie_buf = rx_mgmt_frame->u.beacon.variable;
		size_t ie_len = d_len - offsetof(struct ieee80211_mgmt,
						 u.beacon.variable);
		wil_dbg_wmi(wil, "Capability info : 0x%04x\n", cap);
		wil_dbg_wmi(wil, "TSF : 0x%016llx\n", tsf);
		wil_dbg_wmi(wil, "Beacon interval : %d\n", bi);
		wil_hex_dump_wmi("IE ", DUMP_PREFIX_OFFSET, 16, 1, ie_buf,
				 ie_len, true);
419

D
Dedy Lansky 已提交
420 421
		wil_dbg_wmi(wil, "Capability info : 0x%04x\n", cap);

422 423
		bss = cfg80211_inform_bss_frame(wiphy, channel, rx_mgmt_frame,
						d_len, signal, GFP_KERNEL);
424
		if (bss) {
425
			wil_dbg_wmi(wil, "Added BSS %pM\n",
426
				    rx_mgmt_frame->bssid);
427
			cfg80211_put_bss(wiphy, bss);
428
		} else {
429
			wil_err(wil, "cfg80211_inform_bss_frame() failed\n");
430
		}
431
	} else {
432 433
		mutex_lock(&wil->p2p_wdev_mutex);
		cfg80211_rx_mgmt(wil->radio_wdev, freq, signal,
434
				 (void *)rx_mgmt_frame, d_len, 0);
435
		mutex_unlock(&wil->p2p_wdev_mutex);
436 437 438
	}
}

439 440 441 442 443 444 445 446 447 448 449
static void wmi_evt_tx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
{
	struct wmi_tx_mgmt_packet_event *data = d;
	struct ieee80211_mgmt *mgmt_frame =
			(struct ieee80211_mgmt *)data->payload;
	int flen = len - offsetof(struct wmi_tx_mgmt_packet_event, payload);

	wil_hex_dump_wmi("MGMT Tx ", DUMP_PREFIX_OFFSET, 16, 1, mgmt_frame,
			 flen, true);
}

450 451 452
static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id,
				  void *d, int len)
{
453
	mutex_lock(&wil->p2p_wdev_mutex);
454 455
	if (wil->scan_request) {
		struct wmi_scan_complete_event *data = d;
M
Maya Erez 已提交
456
		int status = le32_to_cpu(data->status);
457
		struct cfg80211_scan_info info = {
M
Maya Erez 已提交
458 459
			.aborted = ((status != WMI_SCAN_SUCCESS) &&
				(status != WMI_SCAN_ABORT_REJECTED)),
460
		};
461

M
Maya Erez 已提交
462
		wil_dbg_wmi(wil, "SCAN_COMPLETE(0x%08x)\n", status);
463
		wil_dbg_misc(wil, "Complete scan_request 0x%p aborted %d\n",
464
			     wil->scan_request, info.aborted);
465
		del_timer_sync(&wil->scan_timer);
466
		cfg80211_scan_done(wil->scan_request, &info);
467
		wil->radio_wdev = wil->wdev;
468
		wil->scan_request = NULL;
M
Maya Erez 已提交
469
		wake_up_interruptible(&wil->wq);
470 471 472 473
		if (wil->p2p.pending_listen_wdev) {
			wil_dbg_misc(wil, "Scheduling delayed listen\n");
			schedule_work(&wil->p2p.delayed_listen_work);
		}
474 475 476
	} else {
		wil_err(wil, "SCAN_COMPLETE while not scanning\n");
	}
477
	mutex_unlock(&wil->p2p_wdev_mutex);
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
}

static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
{
	struct net_device *ndev = wil_to_ndev(wil);
	struct wireless_dev *wdev = wil->wdev;
	struct wmi_connect_event *evt = d;
	int ch; /* channel number */
	struct station_info sinfo;
	u8 *assoc_req_ie, *assoc_resp_ie;
	size_t assoc_req_ielen, assoc_resp_ielen;
	/* capinfo(u16) + listen_interval(u16) + IEs */
	const size_t assoc_req_ie_offset = sizeof(u16) * 2;
	/* capinfo(u16) + status_code(u16) + associd(u16) + IEs */
	const size_t assoc_resp_ie_offset = sizeof(u16) * 3;
493
	int rc;
494 495 496 497 498 499 500 501 502 503 504 505 506

	if (len < sizeof(*evt)) {
		wil_err(wil, "Connect event too short : %d bytes\n", len);
		return;
	}
	if (len != sizeof(*evt) + evt->beacon_ie_len + evt->assoc_req_len +
		   evt->assoc_resp_len) {
		wil_err(wil,
			"Connect event corrupted : %d != %d + %d + %d + %d\n",
			len, (int)sizeof(*evt), evt->beacon_ie_len,
			evt->assoc_req_len, evt->assoc_resp_len);
		return;
	}
507 508 509 510 511
	if (evt->cid >= WIL6210_MAX_CID) {
		wil_err(wil, "Connect CID invalid : %d\n", evt->cid);
		return;
	}

512
	ch = evt->channel + 1;
513 514
	wil_info(wil, "Connect %pM channel [%d] cid %d aid %d\n",
		 evt->bssid, ch, evt->cid, evt->aid);
515
	wil_hex_dump_wmi("connect AI : ", DUMP_PREFIX_OFFSET, 16, 1,
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
			 evt->assoc_info, len - sizeof(*evt), true);

	/* figure out IE's */
	assoc_req_ie = &evt->assoc_info[evt->beacon_ie_len +
					assoc_req_ie_offset];
	assoc_req_ielen = evt->assoc_req_len - assoc_req_ie_offset;
	if (evt->assoc_req_len <= assoc_req_ie_offset) {
		assoc_req_ie = NULL;
		assoc_req_ielen = 0;
	}

	assoc_resp_ie = &evt->assoc_info[evt->beacon_ie_len +
					 evt->assoc_req_len +
					 assoc_resp_ie_offset];
	assoc_resp_ielen = evt->assoc_resp_len - assoc_resp_ie_offset;
	if (evt->assoc_resp_len <= assoc_resp_ie_offset) {
		assoc_resp_ie = NULL;
		assoc_resp_ielen = 0;
	}

536 537 538 539 540 541 542 543
	if (test_bit(wil_status_resetting, wil->status) ||
	    !test_bit(wil_status_fwready, wil->status)) {
		wil_err(wil, "status_resetting, cancel connect event, CID %d\n",
			evt->cid);
		/* no need for cleanup, wil_reset will do that */
		return;
	}

544 545
	mutex_lock(&wil->mutex);

546 547
	if ((wdev->iftype == NL80211_IFTYPE_STATION) ||
	    (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
548
		if (!test_bit(wil_status_fwconnecting, wil->status)) {
549
			wil_err(wil, "Not in connecting state\n");
550
			mutex_unlock(&wil->mutex);
551 552 553
			return;
		}
		del_timer_sync(&wil->connect_timer);
554 555 556
	} else if ((wdev->iftype == NL80211_IFTYPE_AP) ||
		   (wdev->iftype == NL80211_IFTYPE_P2P_GO)) {
		if (wil->sta[evt->cid].status != wil_sta_unused) {
557 558
			wil_err(wil, "AP: Invalid status %d for CID %d\n",
				wil->sta[evt->cid].status, evt->cid);
559 560 561
			mutex_unlock(&wil->mutex);
			return;
		}
562 563 564 565 566 567
	}

	/* FIXME FW can transmit only ucast frames to peer */
	/* FIXME real ring_id instead of hard coded 0 */
	ether_addr_copy(wil->sta[evt->cid].addr, evt->bssid);
	wil->sta[evt->cid].status = wil_sta_conn_pending;
568

569 570
	rc = wil_tx_init(wil, evt->cid);
	if (rc) {
571 572
		wil_err(wil, "config tx vring failed for CID %d, rc (%d)\n",
			evt->cid, rc);
573
		wmi_disconnect_sta(wil, wil->sta[evt->cid].addr,
574
				   WLAN_REASON_UNSPECIFIED, false, false);
575
	} else {
576
		wil_info(wil, "successful connection to CID %d\n", evt->cid);
577 578 579 580 581 582
	}

	if ((wdev->iftype == NL80211_IFTYPE_STATION) ||
	    (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
		if (rc) {
			netif_carrier_off(ndev);
583
			wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
584
			wil_err(wil, "cfg80211_connect_result with failure\n");
585 586 587 588 589 590
			cfg80211_connect_result(ndev, evt->bssid, NULL, 0,
						NULL, 0,
						WLAN_STATUS_UNSPECIFIED_FAILURE,
						GFP_KERNEL);
			goto out;
		} else {
591 592 593 594 595 596 597 598
			struct wiphy *wiphy = wil_to_wiphy(wil);

			cfg80211_ref_bss(wiphy, wil->bss);
			cfg80211_connect_bss(ndev, evt->bssid, wil->bss,
					     assoc_req_ie, assoc_req_ielen,
					     assoc_resp_ie, assoc_resp_ielen,
					     WLAN_STATUS_SUCCESS, GFP_KERNEL,
					     NL80211_TIMEOUT_UNSPECIFIED);
599
		}
600
		wil->bss = NULL;
601 602
	} else if ((wdev->iftype == NL80211_IFTYPE_AP) ||
		   (wdev->iftype == NL80211_IFTYPE_P2P_GO)) {
603 604 605 606
		if (rc) {
			if (disable_ap_sme)
				/* notify new_sta has failed */
				cfg80211_del_sta(ndev, evt->bssid, GFP_KERNEL);
607
			goto out;
608
		}
609

610 611 612 613 614 615 616 617 618 619
		memset(&sinfo, 0, sizeof(sinfo));

		sinfo.generation = wil->sinfo_gen++;

		if (assoc_req_ie) {
			sinfo.assoc_req_ies = assoc_req_ie;
			sinfo.assoc_req_ies_len = assoc_req_ielen;
		}

		cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL);
620
	} else {
621 622
		wil_err(wil, "unhandled iftype %d for CID %d\n", wdev->iftype,
			evt->cid);
623
		goto out;
624 625
	}

626
	wil->sta[evt->cid].status = wil_sta_connected;
627
	wil->sta[evt->cid].aid = evt->aid;
628
	set_bit(wil_status_fwconnected, wil->status);
D
Dedy Lansky 已提交
629
	wil_update_net_queues_bh(wil, NULL, false);
630

631 632 633 634 635
out:
	if (rc)
		wil->sta[evt->cid].status = wil_sta_unused;
	clear_bit(wil_status_fwconnecting, wil->status);
	mutex_unlock(&wil->mutex);
636 637 638 639 640 641
}

static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
			       void *d, int len)
{
	struct wmi_disconnect_event *evt = d;
642
	u16 reason_code = le16_to_cpu(evt->protocol_reason_status);
643

644 645
	wil_info(wil, "Disconnect %pM reason [proto %d wmi %d]\n",
		 evt->bssid, reason_code, evt->disconnect_reason);
646 647 648

	wil->sinfo_gen++;

649 650 651 652 653 654 655
	if (test_bit(wil_status_resetting, wil->status) ||
	    !test_bit(wil_status_fwready, wil->status)) {
		wil_err(wil, "status_resetting, cancel disconnect event\n");
		/* no need for cleanup, wil_reset will do that */
		return;
	}

656
	mutex_lock(&wil->mutex);
657
	wil6210_disconnect(wil, evt->bssid, reason_code, true);
658
	mutex_unlock(&wil->mutex);
659 660 661 662 663 664 665 666 667 668 669 670 671 672 673
}

/*
 * Firmware reports EAPOL frame using WME event.
 * Reconstruct Ethernet frame and deliver it via normal Rx
 */
static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id,
			     void *d, int len)
{
	struct net_device *ndev = wil_to_ndev(wil);
	struct wmi_eapol_rx_event *evt = d;
	u16 eapol_len = le16_to_cpu(evt->eapol_len);
	int sz = eapol_len + ETH_HLEN;
	struct sk_buff *skb;
	struct ethhdr *eth;
674 675
	int cid;
	struct wil_net_stats *stats = NULL;
676

677
	wil_dbg_wmi(wil, "EAPOL len %d from %pM\n", eapol_len,
678 679
		    evt->src_mac);

680 681 682 683
	cid = wil_find_cid(wil, evt->src_mac);
	if (cid >= 0)
		stats = &wil->sta[cid].stats;

684 685 686 687 688 689 690 691 692 693
	if (eapol_len > 196) { /* TODO: revisit size limit */
		wil_err(wil, "EAPOL too large\n");
		return;
	}

	skb = alloc_skb(sz, GFP_KERNEL);
	if (!skb) {
		wil_err(wil, "Failed to allocate skb\n");
		return;
	}
694

695
	eth = skb_put(skb, ETH_HLEN);
696 697
	ether_addr_copy(eth->h_dest, ndev->dev_addr);
	ether_addr_copy(eth->h_source, evt->src_mac);
698
	eth->h_proto = cpu_to_be16(ETH_P_PAE);
699
	skb_put_data(skb, evt->eapol, eapol_len);
700 701 702
	skb->protocol = eth_type_trans(skb, ndev);
	if (likely(netif_rx_ni(skb) == NET_RX_SUCCESS)) {
		ndev->stats.rx_packets++;
703 704 705 706 707
		ndev->stats.rx_bytes += sz;
		if (stats) {
			stats->rx_packets++;
			stats->rx_bytes += sz;
		}
708 709
	} else {
		ndev->stats.rx_dropped++;
710 711
		if (stats)
			stats->rx_dropped++;
712 713 714
	}
}

715
static void wmi_evt_vring_en(struct wil6210_priv *wil, int id, void *d, int len)
716
{
717 718
	struct wmi_vring_en_event *evt = d;
	u8 vri = evt->vring_index;
719
	struct wireless_dev *wdev = wil_to_wdev(wil);
720

721
	wil_dbg_wmi(wil, "Enable vring %d\n", vri);
722

723 724
	if (vri >= ARRAY_SIZE(wil->vring_tx)) {
		wil_err(wil, "Enable for invalid vring %d\n", vri);
725 726
		return;
	}
727 728 729 730 731 732

	if (wdev->iftype != NL80211_IFTYPE_AP || !disable_ap_sme)
		/* in AP mode with disable_ap_sme, this is done by
		 * wil_cfg80211_change_station()
		 */
		wil->vring_tx_data[vri].dot1x_open = true;
733
	if (vri == wil->bcast_vring) /* no BA for bcast */
734
		return;
735 736
	if (agg_wsize >= 0)
		wil_addba_tx_request(wil, vri, agg_wsize);
737 738
}

739 740 741
static void wmi_evt_ba_status(struct wil6210_priv *wil, int id, void *d,
			      int len)
{
L
Lior David 已提交
742
	struct wmi_ba_status_event *evt = d;
V
Vladimir Kondratiev 已提交
743
	struct vring_tx_data *txdata;
744

745
	wil_dbg_wmi(wil, "BACK[%d] %s {%d} timeout %d AMSDU%s\n",
746 747
		    evt->ringid,
		    evt->status == WMI_BA_AGREED ? "OK" : "N/A",
748 749
		    evt->agg_wsize, __le16_to_cpu(evt->ba_timeout),
		    evt->amsdu ? "+" : "-");
750 751 752 753

	if (evt->ringid >= WIL6210_MAX_TX_RINGS) {
		wil_err(wil, "invalid ring id %d\n", evt->ringid);
		return;
V
Vladimir Kondratiev 已提交
754 755
	}

V
Vladimir Kondratiev 已提交
756 757 758
	if (evt->status != WMI_BA_AGREED) {
		evt->ba_timeout = 0;
		evt->agg_wsize = 0;
759
		evt->amsdu = 0;
760 761
	}

V
Vladimir Kondratiev 已提交
762
	txdata = &wil->vring_tx_data[evt->ringid];
763

V
Vladimir Kondratiev 已提交
764 765
	txdata->agg_timeout = le16_to_cpu(evt->ba_timeout);
	txdata->agg_wsize = evt->agg_wsize;
766
	txdata->agg_amsdu = evt->amsdu;
767
	txdata->addba_in_progress = false;
V
Vladimir Kondratiev 已提交
768
}
769

V
Vladimir Kondratiev 已提交
770 771 772 773
static void wmi_evt_addba_rx_req(struct wil6210_priv *wil, int id, void *d,
				 int len)
{
	struct wmi_rcp_addba_req_event *evt = d;
774

V
Vladimir Kondratiev 已提交
775 776 777 778
	wil_addba_rx_request(wil, evt->cidxtid, evt->dialog_token,
			     evt->ba_param_set, evt->ba_timeout,
			     evt->ba_seq_ctrl);
}
779

V
Vladimir Kondratiev 已提交
780
static void wmi_evt_delba(struct wil6210_priv *wil, int id, void *d, int len)
781
__acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
V
Vladimir Kondratiev 已提交
782 783 784 785 786 787 788
{
	struct wmi_delba_event *evt = d;
	u8 cid, tid;
	u16 reason = __le16_to_cpu(evt->reason);
	struct wil_sta_info *sta;
	struct wil_tid_ampdu_rx *r;

789
	might_sleep();
V
Vladimir Kondratiev 已提交
790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806
	parse_cidxtid(evt->cidxtid, &cid, &tid);
	wil_dbg_wmi(wil, "DELBA CID %d TID %d from %s reason %d\n",
		    cid, tid,
		    evt->from_initiator ? "originator" : "recipient",
		    reason);
	if (!evt->from_initiator) {
		int i;
		/* find Tx vring it belongs to */
		for (i = 0; i < ARRAY_SIZE(wil->vring2cid_tid); i++) {
			if ((wil->vring2cid_tid[i][0] == cid) &&
			    (wil->vring2cid_tid[i][1] == tid)) {
				struct vring_tx_data *txdata =
					&wil->vring_tx_data[i];

				wil_dbg_wmi(wil, "DELBA Tx vring %d\n", i);
				txdata->agg_timeout = 0;
				txdata->agg_wsize = 0;
807
				txdata->addba_in_progress = false;
V
Vladimir Kondratiev 已提交
808 809 810 811 812 813 814

				break; /* max. 1 matching ring */
			}
		}
		if (i >= ARRAY_SIZE(wil->vring2cid_tid))
			wil_err(wil, "DELBA: unable to find Tx vring\n");
		return;
815
	}
816

V
Vladimir Kondratiev 已提交
817 818
	sta = &wil->sta[cid];

819
	spin_lock_bh(&sta->tid_rx_lock);
V
Vladimir Kondratiev 已提交
820 821 822 823 824

	r = sta->tid_rx[tid];
	sta->tid_rx[tid] = NULL;
	wil_tid_ampdu_rx_free(wil, r);

825
	spin_unlock_bh(&sta->tid_rx_lock);
826 827
}

828 829 830 831 832 833 834 835 836
/**
 * Some events are ignored for purpose; and need not be interpreted as
 * "unhandled events"
 */
static void wmi_evt_ignore(struct wil6210_priv *wil, int id, void *d, int len)
{
	wil_dbg_wmi(wil, "Ignore event 0x%04x len %d\n", id, len);
}

837 838 839 840 841 842
static const struct {
	int eventid;
	void (*handler)(struct wil6210_priv *wil, int eventid,
			void *data, int data_len);
} wmi_evt_handlers[] = {
	{WMI_READY_EVENTID,		wmi_evt_ready},
843
	{WMI_FW_READY_EVENTID,			wmi_evt_ignore},
844
	{WMI_RX_MGMT_PACKET_EVENTID,	wmi_evt_rx_mgmt},
845
	{WMI_TX_MGMT_PACKET_EVENTID,		wmi_evt_tx_mgmt},
846 847 848 849
	{WMI_SCAN_COMPLETE_EVENTID,	wmi_evt_scan_complete},
	{WMI_CONNECT_EVENTID,		wmi_evt_connect},
	{WMI_DISCONNECT_EVENTID,	wmi_evt_disconnect},
	{WMI_EAPOL_RX_EVENTID,		wmi_evt_eapol_rx},
850
	{WMI_BA_STATUS_EVENTID,		wmi_evt_ba_status},
V
Vladimir Kondratiev 已提交
851 852
	{WMI_RCP_ADDBA_REQ_EVENTID,	wmi_evt_addba_rx_req},
	{WMI_DELBA_EVENTID,		wmi_evt_delba},
853
	{WMI_VRING_EN_EVENTID,		wmi_evt_vring_en},
854
	{WMI_DATA_PORT_OPEN_EVENTID,		wmi_evt_ignore},
855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871
};

/*
 * Run in IRQ context
 * Extract WMI command from mailbox. Queue it to the @wil->pending_wmi_ev
 * that will be eventually handled by the @wmi_event_worker in the thread
 * context of thread "wil6210_wmi"
 */
void wmi_recv_cmd(struct wil6210_priv *wil)
{
	struct wil6210_mbox_ring_desc d_tail;
	struct wil6210_mbox_hdr hdr;
	struct wil6210_mbox_ring *r = &wil->mbox_ctl.rx;
	struct pending_wmi_event *evt;
	u8 *cmd;
	void __iomem *src;
	ulong flags;
872
	unsigned n;
873
	unsigned int num_immed_reply = 0;
874

875
	if (!test_bit(wil_status_mbox_ready, wil->status)) {
D
Dedy Lansky 已提交
876
		wil_err(wil, "Reset in progress. Cannot handle WMI event\n");
V
Vladimir Kondratiev 已提交
877 878 879
		return;
	}

880 881 882 883 884
	if (test_bit(wil_status_suspended, wil->status)) {
		wil_err(wil, "suspended. cannot handle WMI event\n");
		return;
	}

885
	for (n = 0;; n++) {
886
		u16 len;
887
		bool q;
888
		bool immed_reply = false;
889

890 891
		r->head = wil_r(wil, RGF_MBOX +
				offsetof(struct wil6210_mbox_ctl, rx.head));
892 893
		if (r->tail == r->head)
			break;
894

895 896 897
		wil_dbg_wmi(wil, "Mbox head %08x tail %08x\n",
			    r->head, r->tail);
		/* read cmd descriptor from tail */
898 899 900 901
		wil_memcpy_fromio_32(&d_tail, wil->csr + HOSTADDR(r->tail),
				     sizeof(struct wil6210_mbox_ring_desc));
		if (d_tail.sync == 0) {
			wil_err(wil, "Mbox evt not owned by FW?\n");
902
			break;
903 904
		}

905
		/* read cmd header from descriptor */
906 907 908
		if (0 != wmi_read_hdr(wil, d_tail.addr, &hdr)) {
			wil_err(wil, "Mbox evt at 0x%08x?\n",
				le32_to_cpu(d_tail.addr));
909
			break;
910 911
		}
		len = le16_to_cpu(hdr.len);
912 913 914 915 916
		wil_dbg_wmi(wil, "Mbox evt %04x %04x %04x %02x\n",
			    le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type),
			    hdr.flags);

		/* read cmd buffer from descriptor */
917 918 919 920 921
		src = wmi_buffer(wil, d_tail.addr) +
		      sizeof(struct wil6210_mbox_hdr);
		evt = kmalloc(ALIGN(offsetof(struct pending_wmi_event,
					     event.wmi) + len, 4),
			      GFP_KERNEL);
922
		if (!evt)
923
			break;
924

925 926 927 928
		evt->event.hdr = hdr;
		cmd = (void *)&evt->event.wmi;
		wil_memcpy_fromio_32(cmd, src, len);
		/* mark entry as empty */
929 930
		wil_w(wil, r->tail +
		      offsetof(struct wil6210_mbox_ring_desc, sync), 0);
931 932
		/* indicate */
		if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) &&
L
Lior David 已提交
933 934 935 936
		    (len >= sizeof(struct wmi_cmd_hdr))) {
			struct wmi_cmd_hdr *wmi = &evt->event.wmi;
			u16 id = le16_to_cpu(wmi->command_id);
			u32 tstamp = le32_to_cpu(wmi->fw_timestamp);
937 938 939 940 941 942 943 944 945
			if (test_bit(wil_status_resuming, wil->status)) {
				if (id == WMI_TRAFFIC_RESUME_EVENTID)
					clear_bit(wil_status_resuming,
						  wil->status);
				else
					wil_err(wil,
						"WMI evt %d while resuming\n",
						id);
			}
946
			spin_lock_irqsave(&wil->wmi_ev_lock, flags);
947 948 949 950 951 952
			if (wil->reply_id && wil->reply_id == id) {
				if (wil->reply_buf) {
					memcpy(wil->reply_buf, wmi,
					       min(len, wil->reply_size));
					immed_reply = true;
				}
953 954 955 956 957
				if (id == WMI_TRAFFIC_SUSPEND_EVENTID) {
					wil_dbg_wmi(wil,
						    "set suspend_resp_rcvd\n");
					wil->suspend_resp_rcvd = true;
				}
958
			}
959
			spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
960

961 962 963 964
			wil_dbg_wmi(wil, "WMI event 0x%04x MID %d @%d msec\n",
				    id, wmi->mid, tstamp);
			trace_wil6210_wmi_event(wmi, &wmi[1],
						len - sizeof(*wmi));
965
		}
966
		wil_hex_dump_wmi("evt ", DUMP_PREFIX_OFFSET, 16, 1,
967 968 969 970 971
				 &evt->event.hdr, sizeof(hdr) + len, true);

		/* advance tail */
		r->tail = r->base + ((r->tail - r->base +
			  sizeof(struct wil6210_mbox_ring_desc)) % r->size);
972 973
		wil_w(wil, RGF_MBOX +
		      offsetof(struct wil6210_mbox_ctl, rx.tail), r->tail);
974

975
		if (immed_reply) {
976 977
			wil_dbg_wmi(wil, "recv_cmd: Complete WMI 0x%04x\n",
				    wil->reply_id);
978 979 980 981 982 983 984 985 986 987 988
			kfree(evt);
			num_immed_reply++;
			complete(&wil->wmi_call);
		} else {
			/* add to the pending list */
			spin_lock_irqsave(&wil->wmi_ev_lock, flags);
			list_add_tail(&evt->list, &wil->pending_wmi_ev);
			spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
			q = queue_work(wil->wmi_wq, &wil->wmi_event_worker);
			wil_dbg_wmi(wil, "queue_work -> %d\n", q);
		}
989
	}
990
	/* normally, 1 event per IRQ should be processed */
991
	wil_dbg_wmi(wil, "recv_cmd: -> %d events queued, %d completed\n",
992
		    n - num_immed_reply, num_immed_reply);
993 994 995 996 997 998
}

int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len,
	     u16 reply_id, void *reply, u8 reply_size, int to_msec)
{
	int rc;
999
	unsigned long remain;
1000 1001 1002

	mutex_lock(&wil->wmi_mutex);

1003 1004 1005 1006
	spin_lock(&wil->wmi_ev_lock);
	wil->reply_id = reply_id;
	wil->reply_buf = reply;
	wil->reply_size = reply_size;
1007
	reinit_completion(&wil->wmi_call);
1008 1009
	spin_unlock(&wil->wmi_ev_lock);

1010 1011 1012 1013
	rc = __wmi_send(wil, cmdid, buf, len);
	if (rc)
		goto out;

1014 1015
	remain = wait_for_completion_timeout(&wil->wmi_call,
					     msecs_to_jiffies(to_msec));
1016 1017 1018 1019 1020
	if (0 == remain) {
		wil_err(wil, "wmi_call(0x%04x->0x%04x) timeout %d msec\n",
			cmdid, reply_id, to_msec);
		rc = -ETIME;
	} else {
1021
		wil_dbg_wmi(wil,
1022 1023 1024 1025
			    "wmi_call(0x%04x->0x%04x) completed in %d msec\n",
			    cmdid, reply_id,
			    to_msec - jiffies_to_msecs(remain));
	}
1026 1027 1028

out:
	spin_lock(&wil->wmi_ev_lock);
1029 1030 1031
	wil->reply_id = 0;
	wil->reply_buf = NULL;
	wil->reply_size = 0;
1032 1033
	spin_unlock(&wil->wmi_ev_lock);

1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045
	mutex_unlock(&wil->wmi_mutex);

	return rc;
}

int wmi_echo(struct wil6210_priv *wil)
{
	struct wmi_echo_cmd cmd = {
		.value = cpu_to_le32(0x12345678),
	};

	return wmi_call(wil, WMI_ECHO_CMDID, &cmd, sizeof(cmd),
1046
			WMI_ECHO_RSP_EVENTID, NULL, 0, 50);
1047 1048 1049 1050 1051 1052
}

int wmi_set_mac_address(struct wil6210_priv *wil, void *addr)
{
	struct wmi_set_mac_address_cmd cmd;

1053
	ether_addr_copy(cmd.mac, addr);
1054

1055
	wil_dbg_wmi(wil, "Set MAC %pM\n", addr);
1056 1057 1058 1059

	return wmi_send(wil, WMI_SET_MAC_ADDRESS_CMDID, &cmd, sizeof(cmd));
}

1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113
int wmi_led_cfg(struct wil6210_priv *wil, bool enable)
{
	int rc = 0;
	struct wmi_led_cfg_cmd cmd = {
		.led_mode = enable,
		.id = led_id,
		.slow_blink_cfg.blink_on =
			cpu_to_le32(led_blink_time[WIL_LED_TIME_SLOW].on_ms),
		.slow_blink_cfg.blink_off =
			cpu_to_le32(led_blink_time[WIL_LED_TIME_SLOW].off_ms),
		.medium_blink_cfg.blink_on =
			cpu_to_le32(led_blink_time[WIL_LED_TIME_MED].on_ms),
		.medium_blink_cfg.blink_off =
			cpu_to_le32(led_blink_time[WIL_LED_TIME_MED].off_ms),
		.fast_blink_cfg.blink_on =
			cpu_to_le32(led_blink_time[WIL_LED_TIME_FAST].on_ms),
		.fast_blink_cfg.blink_off =
			cpu_to_le32(led_blink_time[WIL_LED_TIME_FAST].off_ms),
		.led_polarity = led_polarity,
	};
	struct {
		struct wmi_cmd_hdr wmi;
		struct wmi_led_cfg_done_event evt;
	} __packed reply;

	if (led_id == WIL_LED_INVALID_ID)
		goto out;

	if (led_id > WIL_LED_MAX_ID) {
		wil_err(wil, "Invalid led id %d\n", led_id);
		rc = -EINVAL;
		goto out;
	}

	wil_dbg_wmi(wil,
		    "%s led %d\n",
		    enable ? "enabling" : "disabling", led_id);

	rc = wmi_call(wil, WMI_LED_CFG_CMDID, &cmd, sizeof(cmd),
		      WMI_LED_CFG_DONE_EVENTID, &reply, sizeof(reply),
		      100);
	if (rc)
		goto out;

	if (reply.evt.status) {
		wil_err(wil, "led %d cfg failed with status %d\n",
			led_id, le32_to_cpu(reply.evt.status));
		rc = -EINVAL;
	}

out:
	return rc;
}

H
Hamad Kadmany 已提交
1114
int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype,
1115
		  u8 chan, u8 hidden_ssid, u8 is_go)
1116
{
1117 1118 1119
	int rc;

	struct wmi_pcp_start_cmd cmd = {
1120 1121 1122
		.bcon_interval = cpu_to_le16(bi),
		.network_type = wmi_nettype,
		.disable_sec_offload = 1,
V
Vladimir Kondratiev 已提交
1123
		.channel = chan - 1,
1124
		.pcp_max_assoc_sta = max_assoc_sta,
H
Hamad Kadmany 已提交
1125
		.hidden_ssid = hidden_ssid,
1126
		.is_go = is_go,
1127
		.disable_ap_sme = disable_ap_sme,
1128
		.abft_len = wil->abft_len,
1129
	};
1130
	struct {
L
Lior David 已提交
1131
		struct wmi_cmd_hdr wmi;
1132 1133
		struct wmi_pcp_started_event evt;
	} __packed reply;
1134

1135
	if (!wil->privacy)
1136 1137
		cmd.disable_sec = 1;

1138 1139 1140 1141 1142 1143 1144 1145
	if ((cmd.pcp_max_assoc_sta > WIL6210_MAX_CID) ||
	    (cmd.pcp_max_assoc_sta <= 0)) {
		wil_info(wil,
			 "Requested connection limit %u, valid values are 1 - %d. Setting to %d\n",
			 max_assoc_sta, WIL6210_MAX_CID, WIL6210_MAX_CID);
		cmd.pcp_max_assoc_sta = WIL6210_MAX_CID;
	}

1146 1147 1148 1149 1150 1151 1152
	if (disable_ap_sme &&
	    !test_bit(WMI_FW_CAPABILITY_DISABLE_AP_SME,
		      wil->fw_capabilities)) {
		wil_err(wil, "disable_ap_sme not supported by FW\n");
		return -EOPNOTSUPP;
	}

1153 1154 1155 1156
	/*
	 * Processing time may be huge, in case of secure AP it takes about
	 * 3500ms for FW to start AP
	 */
1157
	rc = wmi_call(wil, WMI_PCP_START_CMDID, &cmd, sizeof(cmd),
1158
		      WMI_PCP_STARTED_EVENTID, &reply, sizeof(reply), 5000);
1159 1160 1161 1162 1163 1164
	if (rc)
		return rc;

	if (reply.evt.status != WMI_FW_STATUS_SUCCESS)
		rc = -EINVAL;

1165 1166 1167 1168
	if (wmi_nettype != WMI_NETTYPE_P2P)
		/* Don't fail due to error in the led configuration */
		wmi_led_cfg(wil, true);

1169 1170 1171 1172 1173
	return rc;
}

int wmi_pcp_stop(struct wil6210_priv *wil)
{
1174 1175 1176 1177 1178 1179
	int rc;

	rc = wmi_led_cfg(wil, false);
	if (rc)
		return rc;

1180 1181
	return wmi_call(wil, WMI_PCP_STOP_CMDID, NULL, 0,
			WMI_PCP_STOPPED_EVENTID, NULL, 0, 20);
1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201
}

int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid)
{
	struct wmi_set_ssid_cmd cmd = {
		.ssid_len = cpu_to_le32(ssid_len),
	};

	if (ssid_len > sizeof(cmd.ssid))
		return -EINVAL;

	memcpy(cmd.ssid, ssid, ssid_len);

	return wmi_send(wil, WMI_SET_SSID_CMDID, &cmd, sizeof(cmd));
}

int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid)
{
	int rc;
	struct {
L
Lior David 已提交
1202
		struct wmi_cmd_hdr wmi;
1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234
		struct wmi_set_ssid_cmd cmd;
	} __packed reply;
	int len; /* reply.cmd.ssid_len in CPU order */

	rc = wmi_call(wil, WMI_GET_SSID_CMDID, NULL, 0, WMI_GET_SSID_EVENTID,
		      &reply, sizeof(reply), 20);
	if (rc)
		return rc;

	len = le32_to_cpu(reply.cmd.ssid_len);
	if (len > sizeof(reply.cmd.ssid))
		return -EINVAL;

	*ssid_len = len;
	memcpy(ssid, reply.cmd.ssid, len);

	return 0;
}

int wmi_set_channel(struct wil6210_priv *wil, int channel)
{
	struct wmi_set_pcp_channel_cmd cmd = {
		.channel = channel - 1,
	};

	return wmi_send(wil, WMI_SET_PCP_CHANNEL_CMDID, &cmd, sizeof(cmd));
}

int wmi_get_channel(struct wil6210_priv *wil, int *channel)
{
	int rc;
	struct {
L
Lior David 已提交
1235
		struct wmi_cmd_hdr wmi;
1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251
		struct wmi_set_pcp_channel_cmd cmd;
	} __packed reply;

	rc = wmi_call(wil, WMI_GET_PCP_CHANNEL_CMDID, NULL, 0,
		      WMI_GET_PCP_CHANNEL_EVENTID, &reply, sizeof(reply), 20);
	if (rc)
		return rc;

	if (reply.cmd.channel > 3)
		return -EINVAL;

	*channel = reply.cmd.channel + 1;

	return 0;
}

D
Dedy Lansky 已提交
1252
int wmi_p2p_cfg(struct wil6210_priv *wil, int channel, int bi)
1253
{
D
Dedy Lansky 已提交
1254
	int rc;
1255
	struct wmi_p2p_cfg_cmd cmd = {
D
Dedy Lansky 已提交
1256 1257
		.discovery_mode = WMI_DISCOVERY_MODE_PEER2PEER,
		.bcon_interval = cpu_to_le16(bi),
1258 1259
		.channel = channel - 1,
	};
D
Dedy Lansky 已提交
1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293
	struct {
		struct wmi_cmd_hdr wmi;
		struct wmi_p2p_cfg_done_event evt;
	} __packed reply;

	wil_dbg_wmi(wil, "sending WMI_P2P_CFG_CMDID\n");

	rc = wmi_call(wil, WMI_P2P_CFG_CMDID, &cmd, sizeof(cmd),
		      WMI_P2P_CFG_DONE_EVENTID, &reply, sizeof(reply), 300);
	if (!rc && reply.evt.status != WMI_FW_STATUS_SUCCESS) {
		wil_err(wil, "P2P_CFG failed. status %d\n", reply.evt.status);
		rc = -EINVAL;
	}

	return rc;
}

int wmi_start_listen(struct wil6210_priv *wil)
{
	int rc;
	struct {
		struct wmi_cmd_hdr wmi;
		struct wmi_listen_started_event evt;
	} __packed reply;

	wil_dbg_wmi(wil, "sending WMI_START_LISTEN_CMDID\n");

	rc = wmi_call(wil, WMI_START_LISTEN_CMDID, NULL, 0,
		      WMI_LISTEN_STARTED_EVENTID, &reply, sizeof(reply), 300);
	if (!rc && reply.evt.status != WMI_FW_STATUS_SUCCESS) {
		wil_err(wil, "device failed to start listen. status %d\n",
			reply.evt.status);
		rc = -EINVAL;
	}
1294

D
Dedy Lansky 已提交
1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331
	return rc;
}

int wmi_start_search(struct wil6210_priv *wil)
{
	int rc;
	struct {
		struct wmi_cmd_hdr wmi;
		struct wmi_search_started_event evt;
	} __packed reply;

	wil_dbg_wmi(wil, "sending WMI_START_SEARCH_CMDID\n");

	rc = wmi_call(wil, WMI_START_SEARCH_CMDID, NULL, 0,
		      WMI_SEARCH_STARTED_EVENTID, &reply, sizeof(reply), 300);
	if (!rc && reply.evt.status != WMI_FW_STATUS_SUCCESS) {
		wil_err(wil, "device failed to start search. status %d\n",
			reply.evt.status);
		rc = -EINVAL;
	}

	return rc;
}

int wmi_stop_discovery(struct wil6210_priv *wil)
{
	int rc;

	wil_dbg_wmi(wil, "sending WMI_DISCOVERY_STOP_CMDID\n");

	rc = wmi_call(wil, WMI_DISCOVERY_STOP_CMDID, NULL, 0,
		      WMI_DISCOVERY_STOPPED_EVENTID, NULL, 0, 100);

	if (rc)
		wil_err(wil, "Failed to stop discovery\n");

	return rc;
1332 1333
}

1334
int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index,
1335
		       const void *mac_addr, int key_usage)
1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347
{
	struct wmi_delete_cipher_key_cmd cmd = {
		.key_index = key_index,
	};

	if (mac_addr)
		memcpy(cmd.mac, mac_addr, WMI_MAC_LEN);

	return wmi_send(wil, WMI_DELETE_CIPHER_KEY_CMDID, &cmd, sizeof(cmd));
}

int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index,
1348 1349
		       const void *mac_addr, int key_len, const void *key,
		       int key_usage)
1350 1351 1352
{
	struct wmi_add_cipher_key_cmd cmd = {
		.key_index = key_index,
1353
		.key_usage = key_usage,
1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368
		.key_len = key_len,
	};

	if (!key || (key_len > sizeof(cmd.key)))
		return -EINVAL;

	memcpy(cmd.key, key, key_len);
	if (mac_addr)
		memcpy(cmd.mac, mac_addr, WMI_MAC_LEN);

	return wmi_send(wil, WMI_ADD_CIPHER_KEY_CMDID, &cmd, sizeof(cmd));
}

int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie)
{
1369 1370 1371 1372 1373 1374 1375
	static const char *const names[] = {
		[WMI_FRAME_BEACON]	= "BEACON",
		[WMI_FRAME_PROBE_REQ]	= "PROBE_REQ",
		[WMI_FRAME_PROBE_RESP]	= "WMI_FRAME_PROBE_RESP",
		[WMI_FRAME_ASSOC_REQ]	= "WMI_FRAME_ASSOC_REQ",
		[WMI_FRAME_ASSOC_RESP]	= "WMI_FRAME_ASSOC_RESP",
	};
1376 1377 1378
	int rc;
	u16 len = sizeof(struct wmi_set_appie_cmd) + ie_len;
	struct wmi_set_appie_cmd *cmd = kzalloc(len, GFP_KERNEL);
1379

1380 1381 1382 1383
	if (!cmd) {
		rc = -ENOMEM;
		goto out;
	}
1384 1385
	if (!ie)
		ie_len = 0;
1386 1387 1388 1389 1390

	cmd->mgmt_frm_type = type;
	/* BUG: FW API define ieLen as u8. Will fix FW */
	cmd->ie_len = cpu_to_le16(ie_len);
	memcpy(cmd->ie_info, ie, ie_len);
1391
	rc = wmi_send(wil, WMI_SET_APPIE_CMDID, cmd, len);
1392
	kfree(cmd);
1393 1394 1395 1396 1397 1398
out:
	if (rc) {
		const char *name = type < ARRAY_SIZE(names) ?
				   names[type] : "??";
		wil_err(wil, "set_ie(%d %s) failed : %d\n", type, name, rc);
	}
1399 1400 1401 1402

	return rc;
}

1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414
/**
 * wmi_rxon - turn radio on/off
 * @on:		turn on if true, off otherwise
 *
 * Only switch radio. Channel should be set separately.
 * No timeout for rxon - radio turned on forever unless some other call
 * turns it off
 */
int wmi_rxon(struct wil6210_priv *wil, bool on)
{
	int rc;
	struct {
L
Lior David 已提交
1415
		struct wmi_cmd_hdr wmi;
1416 1417 1418
		struct wmi_listen_started_event evt;
	} __packed reply;

1419
	wil_info(wil, "(%s)\n", on ? "on" : "off");
1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434

	if (on) {
		rc = wmi_call(wil, WMI_START_LISTEN_CMDID, NULL, 0,
			      WMI_LISTEN_STARTED_EVENTID,
			      &reply, sizeof(reply), 100);
		if ((rc == 0) && (reply.evt.status != WMI_FW_STATUS_SUCCESS))
			rc = -EINVAL;
	} else {
		rc = wmi_call(wil, WMI_DISCOVERY_STOP_CMDID, NULL, 0,
			      WMI_DISCOVERY_STOPPED_EVENTID, NULL, 0, 20);
	}

	return rc;
}

1435 1436 1437 1438 1439 1440 1441
int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring)
{
	struct wireless_dev *wdev = wil->wdev;
	struct net_device *ndev = wil_to_ndev(wil);
	struct wmi_cfg_rx_chain_cmd cmd = {
		.action = WMI_RX_CHAIN_ADD,
		.rx_sw_ring = {
L
Lior David 已提交
1442 1443
			.max_mpdu_size = cpu_to_le16(
				wil_mtu2macbuf(wil->rx_buf_len)),
1444 1445 1446 1447 1448
			.ring_mem_base = cpu_to_le64(vring->pa),
			.ring_size = cpu_to_le16(vring->size),
		},
		.mid = 0, /* TODO - what is it? */
		.decap_trans_type = WMI_DECAP_TYPE_802_3,
V
Vladimir Kondratiev 已提交
1449
		.reorder_type = WMI_RX_SW_REORDER,
1450
		.host_thrsh = cpu_to_le16(rx_ring_overflow_thrsh),
1451 1452
	};
	struct {
L
Lior David 已提交
1453
		struct wmi_cmd_hdr wmi;
1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467
		struct wmi_cfg_rx_chain_done_event evt;
	} __packed evt;
	int rc;

	if (wdev->iftype == NL80211_IFTYPE_MONITOR) {
		struct ieee80211_channel *ch = wdev->preset_chandef.chan;

		cmd.sniffer_cfg.mode = cpu_to_le32(WMI_SNIFFER_ON);
		if (ch)
			cmd.sniffer_cfg.channel = ch->hw_value - 1;
		cmd.sniffer_cfg.phy_info_mode =
			cpu_to_le32(ndev->type == ARPHRD_IEEE80211_RADIOTAP);
		cmd.sniffer_cfg.phy_support =
			cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL)
1468
				    ? WMI_SNIFFER_CP : WMI_SNIFFER_BOTH_PHYS);
1469 1470 1471 1472 1473 1474
	} else {
		/* Initialize offload (in non-sniffer mode).
		 * Linux IP stack always calculates IP checksum
		 * HW always calculate TCP/UDP checksum
		 */
		cmd.l3_l4_ctrl |= (1 << L3_L4_CTRL_TCPIP_CHECKSUM_EN_POS);
1475
	}
1476 1477 1478 1479 1480

	if (rx_align_2)
		cmd.l2_802_3_offload_ctrl |=
				L2_802_3_OFFLOAD_CTRL_SNAP_KEEP_MSK;

1481 1482 1483 1484 1485 1486 1487 1488
	/* typical time for secure PCP is 840ms */
	rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd),
		      WMI_CFG_RX_CHAIN_DONE_EVENTID, &evt, sizeof(evt), 2000);
	if (rc)
		return rc;

	vring->hwtail = le32_to_cpu(evt.evt.rx_ring_tail_ptr);

1489
	wil_dbg_misc(wil, "Rx init: status %d tail 0x%08x\n",
1490 1491 1492 1493 1494 1495 1496 1497
		     le32_to_cpu(evt.evt.status), vring->hwtail);

	if (le32_to_cpu(evt.evt.status) != WMI_CFG_RX_CHAIN_SUCCESS)
		rc = -EINVAL;

	return rc;
}

1498
int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_bb, u32 *t_rf)
1499 1500 1501
{
	int rc;
	struct wmi_temp_sense_cmd cmd = {
1502 1503 1504
		.measure_baseband_en = cpu_to_le32(!!t_bb),
		.measure_rf_en = cpu_to_le32(!!t_rf),
		.measure_mode = cpu_to_le32(TEMPERATURE_MEASURE_NOW),
1505 1506
	};
	struct {
L
Lior David 已提交
1507
		struct wmi_cmd_hdr wmi;
1508 1509 1510 1511 1512 1513 1514 1515
		struct wmi_temp_sense_done_event evt;
	} __packed reply;

	rc = wmi_call(wil, WMI_TEMP_SENSE_CMDID, &cmd, sizeof(cmd),
		      WMI_TEMP_SENSE_DONE_EVENTID, &reply, sizeof(reply), 100);
	if (rc)
		return rc;

1516 1517 1518 1519
	if (t_bb)
		*t_bb = le32_to_cpu(reply.evt.baseband_t1000);
	if (t_rf)
		*t_rf = le32_to_cpu(reply.evt.rf_t1000);
1520 1521 1522 1523

	return 0;
}

1524 1525
int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac,
		       u16 reason, bool full_disconnect, bool del_sta)
1526
{
1527 1528
	int rc;
	u16 reason_code;
1529 1530 1531 1532
	struct wmi_disconnect_sta_cmd disc_sta_cmd = {
		.disconnect_reason = cpu_to_le16(reason),
	};
	struct wmi_del_sta_cmd del_sta_cmd = {
1533 1534
		.disconnect_reason = cpu_to_le16(reason),
	};
1535
	struct {
L
Lior David 已提交
1536
		struct wmi_cmd_hdr wmi;
1537 1538
		struct wmi_disconnect_event evt;
	} __packed reply;
1539

1540
	wil_dbg_wmi(wil, "disconnect_sta: (%pM, reason %d)\n", mac, reason);
1541

1542
	wil->locally_generated_disc = true;
1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553
	if (del_sta) {
		ether_addr_copy(del_sta_cmd.dst_mac, mac);
		rc = wmi_call(wil, WMI_DEL_STA_CMDID, &del_sta_cmd,
			      sizeof(del_sta_cmd), WMI_DISCONNECT_EVENTID,
			      &reply, sizeof(reply), 1000);
	} else {
		ether_addr_copy(disc_sta_cmd.dst_mac, mac);
		rc = wmi_call(wil, WMI_DISCONNECT_STA_CMDID, &disc_sta_cmd,
			      sizeof(disc_sta_cmd), WMI_DISCONNECT_EVENTID,
			      &reply, sizeof(reply), 1000);
	}
1554 1555 1556 1557 1558 1559
	/* failure to disconnect in reasonable time treated as FW error */
	if (rc) {
		wil_fw_error_recovery(wil);
		return rc;
	}

1560 1561 1562 1563 1564 1565
	if (full_disconnect) {
		/* call event handler manually after processing wmi_call,
		 * to avoid deadlock - disconnect event handler acquires
		 * wil->mutex while it is already held here
		 */
		reason_code = le16_to_cpu(reply.evt.protocol_reason_status);
1566

1567 1568 1569
		wil_dbg_wmi(wil, "Disconnect %pM reason [proto %d wmi %d]\n",
			    reply.evt.bssid, reason_code,
			    reply.evt.disconnect_reason);
1570

1571 1572 1573
		wil->sinfo_gen++;
		wil6210_disconnect(wil, reply.evt.bssid, reason_code, true);
	}
1574
	return 0;
1575 1576
}

V
Vladimir Kondratiev 已提交
1577 1578 1579 1580 1581 1582
int wmi_addba(struct wil6210_priv *wil, u8 ringid, u8 size, u16 timeout)
{
	struct wmi_vring_ba_en_cmd cmd = {
		.ringid = ringid,
		.agg_max_wsize = size,
		.ba_timeout = cpu_to_le16(timeout),
1583
		.amsdu = 0,
V
Vladimir Kondratiev 已提交
1584 1585
	};

1586 1587
	wil_dbg_wmi(wil, "addba: (ring %d size %d timeout %d)\n", ringid, size,
		    timeout);
V
Vladimir Kondratiev 已提交
1588 1589 1590 1591

	return wmi_send(wil, WMI_VRING_BA_EN_CMDID, &cmd, sizeof(cmd));
}

1592
int wmi_delba_tx(struct wil6210_priv *wil, u8 ringid, u16 reason)
V
Vladimir Kondratiev 已提交
1593 1594 1595 1596 1597 1598
{
	struct wmi_vring_ba_dis_cmd cmd = {
		.ringid = ringid,
		.reason = cpu_to_le16(reason),
	};

1599
	wil_dbg_wmi(wil, "delba_tx: (ring %d reason %d)\n", ringid, reason);
V
Vladimir Kondratiev 已提交
1600 1601 1602 1603

	return wmi_send(wil, WMI_VRING_BA_DIS_CMDID, &cmd, sizeof(cmd));
}

1604 1605 1606 1607 1608 1609 1610
int wmi_delba_rx(struct wil6210_priv *wil, u8 cidxtid, u16 reason)
{
	struct wmi_rcp_delba_cmd cmd = {
		.cidxtid = cidxtid,
		.reason = cpu_to_le16(reason),
	};

1611 1612
	wil_dbg_wmi(wil, "delba_rx: (CID %d TID %d reason %d)\n", cidxtid & 0xf,
		    (cidxtid >> 4) & 0xf, reason);
1613 1614 1615 1616

	return wmi_send(wil, WMI_RCP_DELBA_CMDID, &cmd, sizeof(cmd));
}

V
Vladimir Kondratiev 已提交
1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634
int wmi_addba_rx_resp(struct wil6210_priv *wil, u8 cid, u8 tid, u8 token,
		      u16 status, bool amsdu, u16 agg_wsize, u16 timeout)
{
	int rc;
	struct wmi_rcp_addba_resp_cmd cmd = {
		.cidxtid = mk_cidxtid(cid, tid),
		.dialog_token = token,
		.status_code = cpu_to_le16(status),
		/* bit 0: A-MSDU supported
		 * bit 1: policy (should be 0 for us)
		 * bits 2..5: TID
		 * bits 6..15: buffer size
		 */
		.ba_param_set = cpu_to_le16((amsdu ? 1 : 0) | (tid << 2) |
					    (agg_wsize << 6)),
		.ba_timeout = cpu_to_le16(timeout),
	};
	struct {
L
Lior David 已提交
1635
		struct wmi_cmd_hdr wmi;
V
Vladimir Kondratiev 已提交
1636 1637 1638 1639 1640 1641 1642 1643
		struct wmi_rcp_addba_resp_sent_event evt;
	} __packed reply;

	wil_dbg_wmi(wil,
		    "ADDBA response for CID %d TID %d size %d timeout %d status %d AMSDU%s\n",
		    cid, tid, agg_wsize, timeout, status, amsdu ? "+" : "-");

	rc = wmi_call(wil, WMI_RCP_ADDBA_RESP_CMDID, &cmd, sizeof(cmd),
1644 1645
		      WMI_RCP_ADDBA_RESP_SENT_EVENTID, &reply, sizeof(reply),
		      100);
V
Vladimir Kondratiev 已提交
1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657
	if (rc)
		return rc;

	if (reply.evt.status) {
		wil_err(wil, "ADDBA response failed with status %d\n",
			le16_to_cpu(reply.evt.status));
		rc = -EINVAL;
	}

	return rc;
}

1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691
int wmi_ps_dev_profile_cfg(struct wil6210_priv *wil,
			   enum wmi_ps_profile_type ps_profile)
{
	int rc;
	struct wmi_ps_dev_profile_cfg_cmd cmd = {
		.ps_profile = ps_profile,
	};
	struct {
		struct wmi_cmd_hdr wmi;
		struct wmi_ps_dev_profile_cfg_event evt;
	} __packed reply;
	u32 status;

	wil_dbg_wmi(wil, "Setting ps dev profile %d\n", ps_profile);

	reply.evt.status = cpu_to_le32(WMI_PS_CFG_CMD_STATUS_ERROR);

	rc = wmi_call(wil, WMI_PS_DEV_PROFILE_CFG_CMDID, &cmd, sizeof(cmd),
		      WMI_PS_DEV_PROFILE_CFG_EVENTID, &reply, sizeof(reply),
		      100);
	if (rc)
		return rc;

	status = le32_to_cpu(reply.evt.status);

	if (status != WMI_PS_CFG_CMD_STATUS_SUCCESS) {
		wil_err(wil, "ps dev profile cfg failed with status %d\n",
			status);
		rc = -EINVAL;
	}

	return rc;
}

1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750
int wmi_set_mgmt_retry(struct wil6210_priv *wil, u8 retry_short)
{
	int rc;
	struct wmi_set_mgmt_retry_limit_cmd cmd = {
		.mgmt_retry_limit = retry_short,
	};
	struct {
		struct wmi_cmd_hdr wmi;
		struct wmi_set_mgmt_retry_limit_event evt;
	} __packed reply;

	wil_dbg_wmi(wil, "Setting mgmt retry short %d\n", retry_short);

	if (!test_bit(WMI_FW_CAPABILITY_MGMT_RETRY_LIMIT, wil->fw_capabilities))
		return -ENOTSUPP;

	reply.evt.status = WMI_FW_STATUS_FAILURE;

	rc = wmi_call(wil, WMI_SET_MGMT_RETRY_LIMIT_CMDID, &cmd, sizeof(cmd),
		      WMI_SET_MGMT_RETRY_LIMIT_EVENTID, &reply, sizeof(reply),
		      100);
	if (rc)
		return rc;

	if (reply.evt.status != WMI_FW_STATUS_SUCCESS) {
		wil_err(wil, "set mgmt retry limit failed with status %d\n",
			reply.evt.status);
		rc = -EINVAL;
	}

	return rc;
}

int wmi_get_mgmt_retry(struct wil6210_priv *wil, u8 *retry_short)
{
	int rc;
	struct {
		struct wmi_cmd_hdr wmi;
		struct wmi_get_mgmt_retry_limit_event evt;
	} __packed reply;

	wil_dbg_wmi(wil, "getting mgmt retry short\n");

	if (!test_bit(WMI_FW_CAPABILITY_MGMT_RETRY_LIMIT, wil->fw_capabilities))
		return -ENOTSUPP;

	reply.evt.mgmt_retry_limit = 0;
	rc = wmi_call(wil, WMI_GET_MGMT_RETRY_LIMIT_CMDID, NULL, 0,
		      WMI_GET_MGMT_RETRY_LIMIT_EVENTID, &reply, sizeof(reply),
		      100);
	if (rc)
		return rc;

	if (retry_short)
		*retry_short = reply.evt.mgmt_retry_limit;

	return 0;
}

M
Maya Erez 已提交
1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763
int wmi_abort_scan(struct wil6210_priv *wil)
{
	int rc;

	wil_dbg_wmi(wil, "sending WMI_ABORT_SCAN_CMDID\n");

	rc = wmi_send(wil, WMI_ABORT_SCAN_CMDID, NULL, 0);
	if (rc)
		wil_err(wil, "Failed to abort scan (%d)\n", rc);

	return rc;
}

1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781
int wmi_new_sta(struct wil6210_priv *wil, const u8 *mac, u8 aid)
{
	int rc;
	struct wmi_new_sta_cmd cmd = {
		.aid = aid,
	};

	wil_dbg_wmi(wil, "new sta %pM, aid %d\n", mac, aid);

	ether_addr_copy(cmd.dst_mac, mac);

	rc = wmi_send(wil, WMI_NEW_STA_CMDID, &cmd, sizeof(cmd));
	if (rc)
		wil_err(wil, "Failed to send new sta (%d)\n", rc);

	return rc;
}

1782 1783
void wmi_event_flush(struct wil6210_priv *wil)
{
1784
	ulong flags;
1785 1786
	struct pending_wmi_event *evt, *t;

1787
	wil_dbg_wmi(wil, "event_flush\n");
1788

1789 1790
	spin_lock_irqsave(&wil->wmi_ev_lock, flags);

1791 1792 1793 1794
	list_for_each_entry_safe(evt, t, &wil->pending_wmi_ev, list) {
		list_del(&evt->list);
		kfree(evt);
	}
1795 1796

	spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
1797 1798
}

1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877
int wmi_suspend(struct wil6210_priv *wil)
{
	int rc;
	struct wmi_traffic_suspend_cmd cmd = {
		.wakeup_trigger = wil->wakeup_trigger,
	};
	struct {
		struct wmi_cmd_hdr wmi;
		struct wmi_traffic_suspend_event evt;
	} __packed reply;
	u32 suspend_to = WIL_WAIT_FOR_SUSPEND_RESUME_COMP;

	wil->suspend_resp_rcvd = false;
	wil->suspend_resp_comp = false;

	reply.evt.status = WMI_TRAFFIC_SUSPEND_REJECTED;

	rc = wmi_call(wil, WMI_TRAFFIC_SUSPEND_CMDID, &cmd, sizeof(cmd),
		      WMI_TRAFFIC_SUSPEND_EVENTID, &reply, sizeof(reply),
		      suspend_to);
	if (rc) {
		wil_err(wil, "wmi_call for suspend req failed, rc=%d\n", rc);
		if (rc == -ETIME)
			/* wmi_call TO */
			wil->suspend_stats.rejected_by_device++;
		else
			wil->suspend_stats.rejected_by_host++;
		goto out;
	}

	wil_dbg_wmi(wil, "waiting for suspend_response_completed\n");

	rc = wait_event_interruptible_timeout(wil->wq,
					      wil->suspend_resp_comp,
					      msecs_to_jiffies(suspend_to));
	if (rc == 0) {
		wil_err(wil, "TO waiting for suspend_response_completed\n");
		if (wil->suspend_resp_rcvd)
			/* Device responded but we TO due to another reason */
			wil->suspend_stats.rejected_by_host++;
		else
			wil->suspend_stats.rejected_by_device++;
		rc = -EBUSY;
		goto out;
	}

	wil_dbg_wmi(wil, "suspend_response_completed rcvd\n");
	if (reply.evt.status == WMI_TRAFFIC_SUSPEND_REJECTED) {
		wil_dbg_pm(wil, "device rejected the suspend\n");
		wil->suspend_stats.rejected_by_device++;
	}
	rc = reply.evt.status;

out:
	wil->suspend_resp_rcvd = false;
	wil->suspend_resp_comp = false;

	return rc;
}

int wmi_resume(struct wil6210_priv *wil)
{
	int rc;
	struct {
		struct wmi_cmd_hdr wmi;
		struct wmi_traffic_resume_event evt;
	} __packed reply;

	reply.evt.status = WMI_TRAFFIC_RESUME_FAILED;

	rc = wmi_call(wil, WMI_TRAFFIC_RESUME_CMDID, NULL, 0,
		      WMI_TRAFFIC_RESUME_EVENTID, &reply, sizeof(reply),
		      WIL_WAIT_FOR_SUSPEND_RESUME_COMP);
	if (rc)
		return rc;

	return reply.evt.status;
}

1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898
static bool wmi_evt_call_handler(struct wil6210_priv *wil, int id,
				 void *d, int len)
{
	uint i;

	for (i = 0; i < ARRAY_SIZE(wmi_evt_handlers); i++) {
		if (wmi_evt_handlers[i].eventid == id) {
			wmi_evt_handlers[i].handler(wil, id, d, len);
			return true;
		}
	}

	return false;
}

static void wmi_event_handle(struct wil6210_priv *wil,
			     struct wil6210_mbox_hdr *hdr)
{
	u16 len = le16_to_cpu(hdr->len);

	if ((hdr->type == WIL_MBOX_HDR_TYPE_WMI) &&
L
Lior David 已提交
1899 1900
	    (len >= sizeof(struct wmi_cmd_hdr))) {
		struct wmi_cmd_hdr *wmi = (void *)(&hdr[1]);
1901
		void *evt_data = (void *)(&wmi[1]);
L
Lior David 已提交
1902
		u16 id = le16_to_cpu(wmi->command_id);
1903 1904 1905

		wil_dbg_wmi(wil, "Handle WMI 0x%04x (reply_id 0x%04x)\n",
			    id, wil->reply_id);
1906 1907
		/* check if someone waits for this event */
		if (wil->reply_id && wil->reply_id == id) {
1908 1909 1910
			WARN_ON(wil->reply_buf);
			wmi_evt_call_handler(wil, id, evt_data,
					     len - sizeof(*wmi));
1911 1912
			wil_dbg_wmi(wil, "event_handle: Complete WMI 0x%04x\n",
				    id);
1913
			complete(&wil->wmi_call);
1914 1915 1916 1917 1918 1919
			return;
		}
		/* unsolicited event */
		/* search for handler */
		if (!wmi_evt_call_handler(wil, id, evt_data,
					  len - sizeof(*wmi))) {
1920
			wil_info(wil, "Unhandled event 0x%04x\n", id);
1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958
		}
	} else {
		wil_err(wil, "Unknown event type\n");
		print_hex_dump(KERN_ERR, "evt?? ", DUMP_PREFIX_OFFSET, 16, 1,
			       hdr, sizeof(*hdr) + len, true);
	}
}

/*
 * Retrieve next WMI event from the pending list
 */
static struct list_head *next_wmi_ev(struct wil6210_priv *wil)
{
	ulong flags;
	struct list_head *ret = NULL;

	spin_lock_irqsave(&wil->wmi_ev_lock, flags);

	if (!list_empty(&wil->pending_wmi_ev)) {
		ret = wil->pending_wmi_ev.next;
		list_del(ret);
	}

	spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);

	return ret;
}

/*
 * Handler for the WMI events
 */
void wmi_event_worker(struct work_struct *work)
{
	struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
						 wmi_event_worker);
	struct pending_wmi_event *evt;
	struct list_head *lh;

1959
	wil_dbg_wmi(wil, "event_worker: Start\n");
1960 1961 1962 1963 1964
	while ((lh = next_wmi_ev(wil)) != NULL) {
		evt = list_entry(lh, struct pending_wmi_event, list);
		wmi_event_handle(wil, &evt->event.hdr);
		kfree(evt);
	}
1965
	wil_dbg_wmi(wil, "event_worker: Finished\n");
1966
}
1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999

bool wil_is_wmi_idle(struct wil6210_priv *wil)
{
	ulong flags;
	struct wil6210_mbox_ring *r = &wil->mbox_ctl.rx;
	bool rc = false;

	spin_lock_irqsave(&wil->wmi_ev_lock, flags);

	/* Check if there are pending WMI events in the events queue */
	if (!list_empty(&wil->pending_wmi_ev)) {
		wil_dbg_pm(wil, "Pending WMI events in queue\n");
		goto out;
	}

	/* Check if there is a pending WMI call */
	if (wil->reply_id) {
		wil_dbg_pm(wil, "Pending WMI call\n");
		goto out;
	}

	/* Check if there are pending RX events in mbox */
	r->head = wil_r(wil, RGF_MBOX +
			offsetof(struct wil6210_mbox_ctl, rx.head));
	if (r->tail != r->head)
		wil_dbg_pm(wil, "Pending WMI mbox events\n");
	else
		rc = true;

out:
	spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
	return rc;
}