1/******************************************************************************
2 *
3 * Copyright(c) 2009-2012  Realtek Corporation.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12 * more details.
13 *
14 * The full GNU General Public License is included in this distribution in the
15 * file called LICENSE.
16 *
17 * Contact Information:
18 * wlanfae <wlanfae@realtek.com>
19 * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
20 * Hsinchu 300, Taiwan.
21 *
22 * Larry Finger <Larry.Finger@lwfinger.net>
23 *
24 *****************************************************************************/
25
26#include "../wifi.h"
27#include "../pci.h"
28#include "../base.h"
29#include "../core.h"
30#include "../rtl8192ce/reg.h"
31#include "../rtl8192ce/def.h"
32#include "fw_common.h"
33#include <linux/export.h>
34#include <linux/kmemleak.h>
35
36static void _rtl92c_enable_fw_download(struct ieee80211_hw *hw, bool enable)
37{
38	struct rtl_priv *rtlpriv = rtl_priv(hw);
39	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
40
41	if (rtlhal->hw_type == HARDWARE_TYPE_RTL8192CU) {
42		u32 value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL);
43		if (enable)
44			value32 |= MCUFWDL_EN;
45		else
46			value32 &= ~MCUFWDL_EN;
47		rtl_write_dword(rtlpriv, REG_MCUFWDL, value32);
48	} else if (rtlhal->hw_type == HARDWARE_TYPE_RTL8192CE) {
49		u8 tmp;
50		if (enable) {
51
52			tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
53			rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1,
54				       tmp | 0x04);
55
56			tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL);
57			rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp | 0x01);
58
59			tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL + 2);
60			rtl_write_byte(rtlpriv, REG_MCUFWDL + 2, tmp & 0xf7);
61		} else {
62
63			tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL);
64			rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp & 0xfe);
65
66			rtl_write_byte(rtlpriv, REG_MCUFWDL + 1, 0x00);
67		}
68	}
69}
70
71static void _rtl92c_fw_block_write(struct ieee80211_hw *hw,
72				   const u8 *buffer, u32 size)
73{
74	struct rtl_priv *rtlpriv = rtl_priv(hw);
75	u32 blocksize = sizeof(u32);
76	u8 *bufferptr = (u8 *)buffer;
77	u32 *pu4byteptr = (u32 *)buffer;
78	u32 i, offset, blockcount, remainsize;
79
80	blockcount = size / blocksize;
81	remainsize = size % blocksize;
82
83	for (i = 0; i < blockcount; i++) {
84		offset = i * blocksize;
85		rtl_write_dword(rtlpriv, (FW_8192C_START_ADDRESS + offset),
86				*(pu4byteptr + i));
87	}
88
89	if (remainsize) {
90		offset = blockcount * blocksize;
91		bufferptr += offset;
92		for (i = 0; i < remainsize; i++) {
93			rtl_write_byte(rtlpriv, (FW_8192C_START_ADDRESS +
94						 offset + i), *(bufferptr + i));
95		}
96	}
97}
98
99static void _rtl92c_fw_page_write(struct ieee80211_hw *hw,
100				  u32 page, const u8 *buffer, u32 size)
101{
102	struct rtl_priv *rtlpriv = rtl_priv(hw);
103	u8 value8;
104	u8 u8page = (u8) (page & 0x07);
105
106	value8 = (rtl_read_byte(rtlpriv, REG_MCUFWDL + 2) & 0xF8) | u8page;
107
108	rtl_write_byte(rtlpriv, (REG_MCUFWDL + 2), value8);
109	_rtl92c_fw_block_write(hw, buffer, size);
110}
111
112static void _rtl92c_fill_dummy(u8 *pfwbuf, u32 *pfwlen)
113{
114	u32 fwlen = *pfwlen;
115	u8 remain = (u8) (fwlen % 4);
116
117	remain = (remain == 0) ? 0 : (4 - remain);
118
119	while (remain > 0) {
120		pfwbuf[fwlen] = 0;
121		fwlen++;
122		remain--;
123	}
124
125	*pfwlen = fwlen;
126}
127
128static void _rtl92c_write_fw(struct ieee80211_hw *hw,
129			     enum version_8192c version, u8 *buffer, u32 size)
130{
131	struct rtl_priv *rtlpriv = rtl_priv(hw);
132	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
133	bool is_version_b;
134	u8 *bufferptr = (u8 *)buffer;
135
136	RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "FW size is %d bytes,\n", size);
137	is_version_b = IS_NORMAL_CHIP(version);
138	if (is_version_b) {
139		u32 pageNums, remainsize;
140		u32 page, offset;
141
142		if (rtlhal->hw_type == HARDWARE_TYPE_RTL8192CE)
143			_rtl92c_fill_dummy(bufferptr, &size);
144
145		pageNums = size / FW_8192C_PAGE_SIZE;
146		remainsize = size % FW_8192C_PAGE_SIZE;
147
148		if (pageNums > 4) {
149			RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
150				 "Page numbers should not greater then 4\n");
151		}
152
153		for (page = 0; page < pageNums; page++) {
154			offset = page * FW_8192C_PAGE_SIZE;
155			_rtl92c_fw_page_write(hw, page, (bufferptr + offset),
156					      FW_8192C_PAGE_SIZE);
157		}
158
159		if (remainsize) {
160			offset = pageNums * FW_8192C_PAGE_SIZE;
161			page = pageNums;
162			_rtl92c_fw_page_write(hw, page, (bufferptr + offset),
163					      remainsize);
164		}
165	} else {
166		_rtl92c_fw_block_write(hw, buffer, size);
167	}
168}
169
170static int _rtl92c_fw_free_to_go(struct ieee80211_hw *hw)
171{
172	struct rtl_priv *rtlpriv = rtl_priv(hw);
173	int err = -EIO;
174	u32 counter = 0;
175	u32 value32;
176
177	do {
178		value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL);
179	} while ((counter++ < FW_8192C_POLLING_TIMEOUT_COUNT) &&
180		 (!(value32 & FWDL_ChkSum_rpt)));
181
182	if (counter >= FW_8192C_POLLING_TIMEOUT_COUNT) {
183		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
184			 "chksum report faill ! REG_MCUFWDL:0x%08x .\n",
185			  value32);
186		goto exit;
187	}
188
189	RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE,
190		 "Checksum report OK ! REG_MCUFWDL:0x%08x .\n", value32);
191
192	value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL);
193	value32 |= MCUFWDL_RDY;
194	value32 &= ~WINTINI_RDY;
195	rtl_write_dword(rtlpriv, REG_MCUFWDL, value32);
196
197	counter = 0;
198
199	do {
200		value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL);
201		if (value32 & WINTINI_RDY) {
202			RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE,
203				 "Polling FW ready success!! REG_MCUFWDL:0x%08x .\n",
204					value32);
205			err = 0;
206			goto exit;
207		}
208
209		mdelay(FW_8192C_POLLING_DELAY);
210
211	} while (counter++ < FW_8192C_POLLING_TIMEOUT_COUNT);
212
213	RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
214		 "Polling FW ready fail!! REG_MCUFWDL:0x%08x .\n", value32);
215
216exit:
217	return err;
218}
219
220int rtl92c_download_fw(struct ieee80211_hw *hw)
221{
222	struct rtl_priv *rtlpriv = rtl_priv(hw);
223	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
224	struct rtlwifi_firmware_header *pfwheader;
225	u8 *pfwdata;
226	u32 fwsize;
227	int err;
228	enum version_8192c version = rtlhal->version;
229
230	if (!rtlhal->pfirmware)
231		return 1;
232
233	pfwheader = (struct rtlwifi_firmware_header *)rtlhal->pfirmware;
234	pfwdata = (u8 *)rtlhal->pfirmware;
235	fwsize = rtlhal->fwsize;
236	if (IS_FW_HEADER_EXIST(pfwheader)) {
237		RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG,
238			 "Firmware Version(%d), Signature(%#x),Size(%d)\n",
239			  pfwheader->version, pfwheader->signature,
240			  (int)sizeof(struct rtlwifi_firmware_header));
241
242		rtlhal->fw_version = le16_to_cpu(pfwheader->version);
243		rtlhal->fw_subversion = pfwheader->subversion;
244		pfwdata = pfwdata + sizeof(struct rtlwifi_firmware_header);
245		fwsize = fwsize - sizeof(struct rtlwifi_firmware_header);
246	}
247
248	_rtl92c_enable_fw_download(hw, true);
249	_rtl92c_write_fw(hw, version, pfwdata, fwsize);
250	_rtl92c_enable_fw_download(hw, false);
251
252	err = _rtl92c_fw_free_to_go(hw);
253	if (err) {
254		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
255			 "Firmware is not ready to run!\n");
256	} else {
257		RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE,
258			 "Firmware is ready to run!\n");
259	}
260
261	return 0;
262}
263EXPORT_SYMBOL(rtl92c_download_fw);
264
265static bool _rtl92c_check_fw_read_last_h2c(struct ieee80211_hw *hw, u8 boxnum)
266{
267	struct rtl_priv *rtlpriv = rtl_priv(hw);
268	u8 val_hmetfr, val_mcutst_1;
269	bool result = false;
270
271	val_hmetfr = rtl_read_byte(rtlpriv, REG_HMETFR);
272	val_mcutst_1 = rtl_read_byte(rtlpriv, (REG_MCUTST_1 + boxnum));
273
274	if (((val_hmetfr >> boxnum) & BIT(0)) == 0 && val_mcutst_1 == 0)
275		result = true;
276	return result;
277}
278
279static void _rtl92c_fill_h2c_command(struct ieee80211_hw *hw,
280			      u8 element_id, u32 cmd_len, u8 *cmdbuffer)
281{
282	struct rtl_priv *rtlpriv = rtl_priv(hw);
283	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
284	u8 boxnum;
285	u16 box_reg = 0, box_extreg = 0;
286	u8 u1b_tmp;
287	bool isfw_read = false;
288	u8 buf_index = 0;
289	bool bwrite_sucess = false;
290	u8 wait_h2c_limmit = 100;
291	u8 wait_writeh2c_limmit = 100;
292	u8 boxcontent[4], boxextcontent[2];
293	u32 h2c_waitcounter = 0;
294	unsigned long flag;
295	u8 idx;
296
297	RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "come in\n");
298
299	while (true) {
300		spin_lock_irqsave(&rtlpriv->locks.h2c_lock, flag);
301		if (rtlhal->h2c_setinprogress) {
302			RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
303				 "H2C set in progress! Wait to set..element_id(%d).\n",
304				 element_id);
305			while (rtlhal->h2c_setinprogress) {
306				spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock,
307						       flag);
308				h2c_waitcounter++;
309				RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
310					 "Wait 100 us (%d times)...\n",
311					  h2c_waitcounter);
312				udelay(100);
313
314				if (h2c_waitcounter > 1000)
315					return;
316				spin_lock_irqsave(&rtlpriv->locks.h2c_lock,
317						  flag);
318			}
319			spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag);
320		} else {
321			rtlhal->h2c_setinprogress = true;
322			spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag);
323			break;
324		}
325	}
326
327	while (!bwrite_sucess) {
328		wait_writeh2c_limmit--;
329		if (wait_writeh2c_limmit == 0) {
330			RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
331				 "Write H2C fail because no trigger for FW INT!\n");
332			break;
333		}
334
335		boxnum = rtlhal->last_hmeboxnum;
336		switch (boxnum) {
337		case 0:
338			box_reg = REG_HMEBOX_0;
339			box_extreg = REG_HMEBOX_EXT_0;
340			break;
341		case 1:
342			box_reg = REG_HMEBOX_1;
343			box_extreg = REG_HMEBOX_EXT_1;
344			break;
345		case 2:
346			box_reg = REG_HMEBOX_2;
347			box_extreg = REG_HMEBOX_EXT_2;
348			break;
349		case 3:
350			box_reg = REG_HMEBOX_3;
351			box_extreg = REG_HMEBOX_EXT_3;
352			break;
353		default:
354			RT_TRACE(rtlpriv, COMP_ERR, DBG_LOUD,
355				 "switch case not process\n");
356			break;
357		}
358
359		isfw_read = _rtl92c_check_fw_read_last_h2c(hw, boxnum);
360		while (!isfw_read) {
361			wait_h2c_limmit--;
362			if (wait_h2c_limmit == 0) {
363				RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
364					 "Waiting too long for FW read clear HMEBox(%d)!\n",
365					 boxnum);
366				break;
367			}
368
369			udelay(10);
370
371			isfw_read = _rtl92c_check_fw_read_last_h2c(hw, boxnum);
372			u1b_tmp = rtl_read_byte(rtlpriv, 0x1BF);
373			RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
374				 "Waiting for FW read clear HMEBox(%d)!!! 0x1BF = %2x\n",
375				 boxnum, u1b_tmp);
376		}
377
378		if (!isfw_read) {
379			RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
380				 "Write H2C register BOX[%d] fail!!!!! Fw do not read.\n",
381				 boxnum);
382			break;
383		}
384
385		memset(boxcontent, 0, sizeof(boxcontent));
386		memset(boxextcontent, 0, sizeof(boxextcontent));
387		boxcontent[0] = element_id;
388		RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
389			 "Write element_id box_reg(%4x) = %2x\n",
390			  box_reg, element_id);
391
392		switch (cmd_len) {
393		case 1:
394			boxcontent[0] &= ~(BIT(7));
395			memcpy((u8 *)(boxcontent) + 1,
396			       cmdbuffer + buf_index, 1);
397
398			for (idx = 0; idx < 4; idx++) {
399				rtl_write_byte(rtlpriv, box_reg + idx,
400					       boxcontent[idx]);
401			}
402			break;
403		case 2:
404			boxcontent[0] &= ~(BIT(7));
405			memcpy((u8 *)(boxcontent) + 1,
406			       cmdbuffer + buf_index, 2);
407
408			for (idx = 0; idx < 4; idx++) {
409				rtl_write_byte(rtlpriv, box_reg + idx,
410					       boxcontent[idx]);
411			}
412			break;
413		case 3:
414			boxcontent[0] &= ~(BIT(7));
415			memcpy((u8 *)(boxcontent) + 1,
416			       cmdbuffer + buf_index, 3);
417
418			for (idx = 0; idx < 4; idx++) {
419				rtl_write_byte(rtlpriv, box_reg + idx,
420					       boxcontent[idx]);
421			}
422			break;
423		case 4:
424			boxcontent[0] |= (BIT(7));
425			memcpy((u8 *)(boxextcontent),
426			       cmdbuffer + buf_index, 2);
427			memcpy((u8 *)(boxcontent) + 1,
428			       cmdbuffer + buf_index + 2, 2);
429
430			for (idx = 0; idx < 2; idx++) {
431				rtl_write_byte(rtlpriv, box_extreg + idx,
432					       boxextcontent[idx]);
433			}
434
435			for (idx = 0; idx < 4; idx++) {
436				rtl_write_byte(rtlpriv, box_reg + idx,
437					       boxcontent[idx]);
438			}
439			break;
440		case 5:
441			boxcontent[0] |= (BIT(7));
442			memcpy((u8 *)(boxextcontent),
443			       cmdbuffer + buf_index, 2);
444			memcpy((u8 *)(boxcontent) + 1,
445			       cmdbuffer + buf_index + 2, 3);
446
447			for (idx = 0; idx < 2; idx++) {
448				rtl_write_byte(rtlpriv, box_extreg + idx,
449					       boxextcontent[idx]);
450			}
451
452			for (idx = 0; idx < 4; idx++) {
453				rtl_write_byte(rtlpriv, box_reg + idx,
454					       boxcontent[idx]);
455			}
456			break;
457		default:
458			RT_TRACE(rtlpriv, COMP_ERR, DBG_LOUD,
459				 "switch case not process\n");
460			break;
461		}
462
463		bwrite_sucess = true;
464
465		rtlhal->last_hmeboxnum = boxnum + 1;
466		if (rtlhal->last_hmeboxnum == 4)
467			rtlhal->last_hmeboxnum = 0;
468
469		RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
470			 "pHalData->last_hmeboxnum  = %d\n",
471			  rtlhal->last_hmeboxnum);
472	}
473
474	spin_lock_irqsave(&rtlpriv->locks.h2c_lock, flag);
475	rtlhal->h2c_setinprogress = false;
476	spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag);
477
478	RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "go out\n");
479}
480
481void rtl92c_fill_h2c_cmd(struct ieee80211_hw *hw,
482			 u8 element_id, u32 cmd_len, u8 *cmdbuffer)
483{
484	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
485	u32 tmp_cmdbuf[2];
486
487	if (!rtlhal->fw_ready) {
488		RT_ASSERT(false,
489			  "return H2C cmd because of Fw download fail!!!\n");
490		return;
491	}
492
493	memset(tmp_cmdbuf, 0, 8);
494	memcpy(tmp_cmdbuf, cmdbuffer, cmd_len);
495	_rtl92c_fill_h2c_command(hw, element_id, cmd_len, (u8 *)&tmp_cmdbuf);
496
497	return;
498}
499EXPORT_SYMBOL(rtl92c_fill_h2c_cmd);
500
501void rtl92c_firmware_selfreset(struct ieee80211_hw *hw)
502{
503	u8 u1b_tmp;
504	u8 delay = 100;
505	struct rtl_priv *rtlpriv = rtl_priv(hw);
506
507	rtl_write_byte(rtlpriv, REG_HMETFR + 3, 0x20);
508	u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
509
510	while (u1b_tmp & BIT(2)) {
511		delay--;
512		if (delay == 0) {
513			RT_ASSERT(false, "8051 reset fail.\n");
514			break;
515		}
516		udelay(50);
517		u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
518	}
519}
520EXPORT_SYMBOL(rtl92c_firmware_selfreset);
521
522void rtl92c_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode)
523{
524	struct rtl_priv *rtlpriv = rtl_priv(hw);
525	u8 u1_h2c_set_pwrmode[3] = { 0 };
526	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
527
528	RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "FW LPS mode = %d\n", mode);
529
530	SET_H2CCMD_PWRMODE_PARM_MODE(u1_h2c_set_pwrmode, mode);
531	SET_H2CCMD_PWRMODE_PARM_SMART_PS(u1_h2c_set_pwrmode,
532		(rtlpriv->mac80211.p2p) ? ppsc->smart_ps : 1);
533	SET_H2CCMD_PWRMODE_PARM_BCN_PASS_TIME(u1_h2c_set_pwrmode,
534					      ppsc->reg_max_lps_awakeintvl);
535
536	RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG,
537		      "rtl92c_set_fw_rsvdpagepkt(): u1_h2c_set_pwrmode\n",
538		      u1_h2c_set_pwrmode, 3);
539	rtl92c_fill_h2c_cmd(hw, H2C_SETPWRMODE, 3, u1_h2c_set_pwrmode);
540}
541EXPORT_SYMBOL(rtl92c_set_fw_pwrmode_cmd);
542
543#define BEACON_PG		0 /*->1*/
544#define PSPOLL_PG		2
545#define NULL_PG			3
546#define PROBERSP_PG		4 /*->5*/
547
548#define TOTAL_RESERVED_PKT_LEN	768
549
550static u8 reserved_page_packet[TOTAL_RESERVED_PKT_LEN] = {
551	/* page 0 beacon */
552	0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
553	0xFF, 0xFF, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42,
554	0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x50, 0x08,
555	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
556	0x64, 0x00, 0x00, 0x04, 0x00, 0x0C, 0x6C, 0x69,
557	0x6E, 0x6B, 0x73, 0x79, 0x73, 0x5F, 0x77, 0x6C,
558	0x61, 0x6E, 0x01, 0x04, 0x82, 0x84, 0x8B, 0x96,
559	0x03, 0x01, 0x01, 0x06, 0x02, 0x00, 0x00, 0x2A,
560	0x01, 0x00, 0x32, 0x08, 0x24, 0x30, 0x48, 0x6C,
561	0x0C, 0x12, 0x18, 0x60, 0x2D, 0x1A, 0x6C, 0x18,
562	0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
563	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
564	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
565	0x3D, 0x00, 0xDD, 0x06, 0x00, 0xE0, 0x4C, 0x02,
566	0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
567	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
568
569	/* page 1 beacon */
570	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
571	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
572	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
573	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
574	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
575	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
576	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
577	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
578	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
579	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
580	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
581	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
582	0x10, 0x00, 0x20, 0x8C, 0x00, 0x12, 0x10, 0x00,
583	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
584	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
585	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
586
587	/* page 2  ps-poll */
588	0xA4, 0x10, 0x01, 0xC0, 0x00, 0x40, 0x10, 0x10,
589	0x00, 0x03, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42,
590	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
591	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
592	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
593	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
594	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
595	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
596	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
597	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
598	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
599	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
600	0x18, 0x00, 0x20, 0x8C, 0x00, 0x12, 0x00, 0x00,
601	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
602	0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
603	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
604
605	/* page 3  null */
606	0x48, 0x01, 0x00, 0x00, 0x00, 0x40, 0x10, 0x10,
607	0x00, 0x03, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42,
608	0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x00, 0x00,
609	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
610	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
611	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
612	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
613	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
614	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
615	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
616	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
617	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
618	0x72, 0x00, 0x20, 0x8C, 0x00, 0x12, 0x00, 0x00,
619	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
620	0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
621	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
622
623	/* page 4  probe_resp */
624	0x50, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x10,
625	0x00, 0x03, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42,
626	0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x00, 0x00,
627	0x9E, 0x46, 0x15, 0x32, 0x27, 0xF2, 0x2D, 0x00,
628	0x64, 0x00, 0x00, 0x04, 0x00, 0x0C, 0x6C, 0x69,
629	0x6E, 0x6B, 0x73, 0x79, 0x73, 0x5F, 0x77, 0x6C,
630	0x61, 0x6E, 0x01, 0x04, 0x82, 0x84, 0x8B, 0x96,
631	0x03, 0x01, 0x01, 0x06, 0x02, 0x00, 0x00, 0x2A,
632	0x01, 0x00, 0x32, 0x08, 0x24, 0x30, 0x48, 0x6C,
633	0x0C, 0x12, 0x18, 0x60, 0x2D, 0x1A, 0x6C, 0x18,
634	0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
635	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
636	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
637	0x3D, 0x00, 0xDD, 0x06, 0x00, 0xE0, 0x4C, 0x02,
638	0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
639	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
640
641	/* page 5  probe_resp */
642	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
643	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
644	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
645	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
646	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
647	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
648	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
649	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
650	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
651	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
652	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
653	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
654	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
655	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
656	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
657	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
658};
659
660void rtl92c_set_fw_rsvdpagepkt(struct ieee80211_hw *hw,
661	 bool (*cmd_send_packet)(struct ieee80211_hw *, struct sk_buff *))
662{
663	struct rtl_priv *rtlpriv = rtl_priv(hw);
664	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
665	struct sk_buff *skb = NULL;
666
667	u32 totalpacketlen;
668	bool rtstatus;
669	u8 u1rsvdpageloc[3] = { 0 };
670	bool b_dlok = false;
671
672	u8 *beacon;
673	u8 *p_pspoll;
674	u8 *nullfunc;
675	u8 *p_probersp;
676	/*---------------------------------------------------------
677				(1) beacon
678	---------------------------------------------------------*/
679	beacon = &reserved_page_packet[BEACON_PG * 128];
680	SET_80211_HDR_ADDRESS2(beacon, mac->mac_addr);
681	SET_80211_HDR_ADDRESS3(beacon, mac->bssid);
682
683	/*-------------------------------------------------------
684				(2) ps-poll
685	--------------------------------------------------------*/
686	p_pspoll = &reserved_page_packet[PSPOLL_PG * 128];
687	SET_80211_PS_POLL_AID(p_pspoll, (mac->assoc_id | 0xc000));
688	SET_80211_PS_POLL_BSSID(p_pspoll, mac->bssid);
689	SET_80211_PS_POLL_TA(p_pspoll, mac->mac_addr);
690
691	SET_H2CCMD_RSVDPAGE_LOC_PSPOLL(u1rsvdpageloc, PSPOLL_PG);
692
693	/*--------------------------------------------------------
694				(3) null data
695	---------------------------------------------------------*/
696	nullfunc = &reserved_page_packet[NULL_PG * 128];
697	SET_80211_HDR_ADDRESS1(nullfunc, mac->bssid);
698	SET_80211_HDR_ADDRESS2(nullfunc, mac->mac_addr);
699	SET_80211_HDR_ADDRESS3(nullfunc, mac->bssid);
700
701	SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(u1rsvdpageloc, NULL_PG);
702
703	/*---------------------------------------------------------
704				(4) probe response
705	----------------------------------------------------------*/
706	p_probersp = &reserved_page_packet[PROBERSP_PG * 128];
707	SET_80211_HDR_ADDRESS1(p_probersp, mac->bssid);
708	SET_80211_HDR_ADDRESS2(p_probersp, mac->mac_addr);
709	SET_80211_HDR_ADDRESS3(p_probersp, mac->bssid);
710
711	SET_H2CCMD_RSVDPAGE_LOC_PROBE_RSP(u1rsvdpageloc, PROBERSP_PG);
712
713	totalpacketlen = TOTAL_RESERVED_PKT_LEN;
714
715	RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_LOUD,
716		      "rtl92c_set_fw_rsvdpagepkt(): HW_VAR_SET_TX_CMD: ALL\n",
717		      &reserved_page_packet[0], totalpacketlen);
718	RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG,
719		      "rtl92c_set_fw_rsvdpagepkt(): HW_VAR_SET_TX_CMD: ALL\n",
720		      u1rsvdpageloc, 3);
721
722
723	skb = dev_alloc_skb(totalpacketlen);
724	memcpy((u8 *)skb_put(skb, totalpacketlen),
725	       &reserved_page_packet, totalpacketlen);
726
727	if (cmd_send_packet)
728		rtstatus = cmd_send_packet(hw, skb);
729	else
730		rtstatus = rtl_cmd_send_packet(hw, skb);
731
732	if (rtstatus)
733		b_dlok = true;
734
735	if (b_dlok) {
736		RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
737			 "Set RSVD page location to Fw.\n");
738		RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG,
739				"H2C_RSVDPAGE:\n",
740				u1rsvdpageloc, 3);
741		rtl92c_fill_h2c_cmd(hw, H2C_RSVDPAGE,
742				    sizeof(u1rsvdpageloc), u1rsvdpageloc);
743	} else
744		RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
745			 "Set RSVD page location to Fw FAIL!!!!!!.\n");
746}
747EXPORT_SYMBOL(rtl92c_set_fw_rsvdpagepkt);
748
749void rtl92c_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus)
750{
751	u8 u1_joinbssrpt_parm[1] = { 0 };
752
753	SET_H2CCMD_JOINBSSRPT_PARM_OPMODE(u1_joinbssrpt_parm, mstatus);
754
755	rtl92c_fill_h2c_cmd(hw, H2C_JOINBSSRPT, 1, u1_joinbssrpt_parm);
756}
757EXPORT_SYMBOL(rtl92c_set_fw_joinbss_report_cmd);
758
759static void rtl92c_set_p2p_ctw_period_cmd(struct ieee80211_hw *hw, u8 ctwindow)
760{
761	u8 u1_ctwindow_period[1] = { ctwindow};
762
763	rtl92c_fill_h2c_cmd(hw, H2C_P2P_PS_CTW_CMD, 1, u1_ctwindow_period);
764}
765
766/* refactored routine */
767static void set_noa_data(struct rtl_priv *rtlpriv,
768			 struct rtl_p2p_ps_info *p2pinfo,
769			 struct p2p_ps_offload_t *p2p_ps_offload)
770{
771	int i;
772	u32	start_time, tsf_low;
773
774	/* hw only support 2 set of NoA */
775	for (i = 0 ; i < p2pinfo->noa_num ; i++) {
776		/* To control the reg setting for which NOA*/
777		rtl_write_byte(rtlpriv, 0x5cf, (i << 4));
778		if (i == 0)
779			p2p_ps_offload->noa0_en = 1;
780		else
781			p2p_ps_offload->noa1_en = 1;
782
783		/* config P2P NoA Descriptor Register */
784		rtl_write_dword(rtlpriv, 0x5E0,
785				p2pinfo->noa_duration[i]);
786		rtl_write_dword(rtlpriv, 0x5E4,
787				p2pinfo->noa_interval[i]);
788
789		/*Get Current TSF value */
790		tsf_low = rtl_read_dword(rtlpriv, REG_TSFTR);
791
792		start_time = p2pinfo->noa_start_time[i];
793		if (p2pinfo->noa_count_type[i] != 1) {
794			while (start_time <= (tsf_low+(50*1024))) {
795				start_time += p2pinfo->noa_interval[i];
796				if (p2pinfo->noa_count_type[i] != 255)
797					p2pinfo->noa_count_type[i]--;
798			}
799		}
800		rtl_write_dword(rtlpriv, 0x5E8, start_time);
801		rtl_write_dword(rtlpriv, 0x5EC,
802				p2pinfo->noa_count_type[i]);
803	}
804}
805
806void rtl92c_set_p2p_ps_offload_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state)
807{
808	struct rtl_priv *rtlpriv = rtl_priv(hw);
809	struct rtl_ps_ctl *rtlps = rtl_psc(rtl_priv(hw));
810	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
811	struct rtl_p2p_ps_info *p2pinfo = &(rtlps->p2p_ps_info);
812	struct p2p_ps_offload_t *p2p_ps_offload = &rtlhal->p2p_ps_offload;
813	u16	ctwindow;
814
815	switch (p2p_ps_state) {
816	case P2P_PS_DISABLE:
817			RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
818				 "P2P_PS_DISABLE\n");
819			memset(p2p_ps_offload, 0, sizeof(*p2p_ps_offload));
820			break;
821	case P2P_PS_ENABLE:
822			RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
823				 "P2P_PS_ENABLE\n");
824			/* update CTWindow value. */
825			if (p2pinfo->ctwindow > 0) {
826				p2p_ps_offload->ctwindow_en = 1;
827				ctwindow = p2pinfo->ctwindow;
828				rtl92c_set_p2p_ctw_period_cmd(hw, ctwindow);
829			}
830			/* call refactored routine */
831			set_noa_data(rtlpriv, p2pinfo, p2p_ps_offload);
832
833			if ((p2pinfo->opp_ps == 1) || (p2pinfo->noa_num > 0)) {
834				/* rst p2p circuit */
835				rtl_write_byte(rtlpriv, REG_DUAL_TSF_RST,
836					       BIT(4));
837
838				p2p_ps_offload->offload_en = 1;
839
840				if (P2P_ROLE_GO == rtlpriv->mac80211.p2p) {
841					p2p_ps_offload->role = 1;
842					p2p_ps_offload->allstasleep = 0;
843				} else {
844					p2p_ps_offload->role = 0;
845				}
846
847				p2p_ps_offload->discovery = 0;
848			}
849			break;
850	case P2P_PS_SCAN:
851			RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "P2P_PS_SCAN\n");
852			p2p_ps_offload->discovery = 1;
853			break;
854	case P2P_PS_SCAN_DONE:
855			RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
856				 "P2P_PS_SCAN_DONE\n");
857			p2p_ps_offload->discovery = 0;
858			p2pinfo->p2p_ps_state = P2P_PS_ENABLE;
859			break;
860	default:
861			break;
862	}
863
864	rtl92c_fill_h2c_cmd(hw, H2C_P2P_PS_OFFLOAD, 1, (u8 *)p2p_ps_offload);
865
866}
867EXPORT_SYMBOL_GPL(rtl92c_set_p2p_ps_offload_cmd);
868