1 // SPDX-License-Identifier: GPL-2.0
2 /******************************************************************************
5 * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
6 * Linux device driver for RTL8192SU
8 * Modifications for inclusion into the Linux staging tree are
9 * Copyright(c) 2010 Larry Finger. All rights reserved.
11 * Contact information:
12 * WLAN FAE <wlanfae@realtek.com>.
13 * Larry Finger <Larry.Finger@lwfinger.net>
15 ******************************************************************************/
17 #define _RTL8712_CMD_C_
19 #include <linux/compiler.h>
20 #include <linux/kernel.h>
21 #include <linux/errno.h>
22 #include <linux/slab.h>
23 #include <linux/sched/signal.h>
24 #include <linux/module.h>
25 #include <linux/kref.h>
26 #include <linux/netdevice.h>
27 #include <linux/skbuff.h>
28 #include <linux/usb.h>
29 #include <linux/usb/ch9.h>
30 #include <linux/circ_buf.h>
31 #include <linux/uaccess.h>
32 #include <asm/byteorder.h>
33 #include <linux/atomic.h>
34 #include <linux/semaphore.h>
35 #include <linux/rtnetlink.h>
37 #include "osdep_service.h"
38 #include "drv_types.h"
39 #include "recv_osdep.h"
40 #include "mlme_osdep.h"
41 #include "rtl871x_ioctl_set.h"
43 static void check_hw_pbc(struct _adapter
*padapter
)
47 r8712_write8(padapter
, MAC_PINMUX_CTRL
, (GPIOMUX_EN
| GPIOSEL_GPIO
));
48 tmp1byte
= r8712_read8(padapter
, GPIO_IO_SEL
);
49 tmp1byte
&= ~(HAL_8192S_HW_GPIO_WPS_BIT
);
50 r8712_write8(padapter
, GPIO_IO_SEL
, tmp1byte
);
51 tmp1byte
= r8712_read8(padapter
, GPIO_CTRL
);
54 if (tmp1byte
& HAL_8192S_HW_GPIO_WPS_BIT
) {
55 /* Here we only set bPbcPressed to true
56 * After trigger PBC, the variable will be set to false
58 DBG_8712("CheckPbcGPIO - PBC is pressed !!!!\n");
59 /* 0 is the default value and it means the application monitors
60 * the HW PBC doesn't provide its pid to driver.
62 if (padapter
->pid
== 0)
64 kill_pid(find_vpid(padapter
->pid
), SIGUSR1
, 1);
68 /* query rx phy status from fw.
70 * Infrastructure mode: beacon , data.
72 static void query_fw_rx_phy_status(struct _adapter
*padapter
)
77 if (check_fwstate(&padapter
->mlmepriv
, _FW_LINKED
)) {
78 r8712_write32(padapter
, IOCMD_CTRL_REG
, 0xf4000001);
80 /* Wait FW complete IO Cmd */
81 while ((r8712_read32(padapter
, IOCMD_CTRL_REG
)) &&
87 val32
= r8712_read32(padapter
, IOCMD_DATA_REG
);
91 padapter
->recvpriv
.fw_rssi
=
92 (u8
)r8712_signal_scale_mapping(val32
);
96 /* check mlme, hw, phy, or dynamic algorithm status. */
97 static void StatusWatchdogCallback(struct _adapter
*padapter
)
99 check_hw_pbc(padapter
);
100 query_fw_rx_phy_status(padapter
);
103 static void r871x_internal_cmd_hdl(struct _adapter
*padapter
, u8
*pbuf
)
105 struct drvint_cmd_parm
*pdrvcmd
;
109 pdrvcmd
= (struct drvint_cmd_parm
*)pbuf
;
110 switch (pdrvcmd
->i_cid
) {
112 StatusWatchdogCallback(padapter
);
117 kfree(pdrvcmd
->pbuf
);
120 static u8
read_macreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
122 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
123 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
125 /* invoke cmd->callback function */
126 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
128 r8712_free_cmd_obj(pcmd
);
130 pcmd_callback(padapter
, pcmd
);
134 static u8
write_macreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
136 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
137 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
139 /* invoke cmd->callback function */
140 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
142 r8712_free_cmd_obj(pcmd
);
144 pcmd_callback(padapter
, pcmd
);
148 static u8
read_bbreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
150 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
152 r8712_free_cmd_obj(pcmd
);
156 static u8
write_bbreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
158 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
159 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
161 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
163 r8712_free_cmd_obj(pcmd
);
165 pcmd_callback(padapter
, pcmd
);
169 static u8
read_rfreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
172 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
173 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
175 if (pcmd
->rsp
&& pcmd
->rspsz
> 0)
176 memcpy(pcmd
->rsp
, (u8
*)&val
, pcmd
->rspsz
);
177 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
179 r8712_free_cmd_obj(pcmd
);
181 pcmd_callback(padapter
, pcmd
);
185 static u8
write_rfreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
187 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
188 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
190 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
192 r8712_free_cmd_obj(pcmd
);
194 pcmd_callback(padapter
, pcmd
);
198 static u8
sys_suspend_hdl(struct _adapter
*padapter
, u8
*pbuf
)
200 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
202 r8712_free_cmd_obj(pcmd
);
206 static struct cmd_obj
*cmd_hdl_filter(struct _adapter
*padapter
,
207 struct cmd_obj
*pcmd
)
209 struct cmd_obj
*pcmd_r
;
215 switch (pcmd
->cmdcode
) {
216 case GEN_CMD_CODE(_Read_MACREG
):
217 read_macreg_hdl(padapter
, (u8
*)pcmd
);
220 case GEN_CMD_CODE(_Write_MACREG
):
221 write_macreg_hdl(padapter
, (u8
*)pcmd
);
224 case GEN_CMD_CODE(_Read_BBREG
):
225 read_bbreg_hdl(padapter
, (u8
*)pcmd
);
227 case GEN_CMD_CODE(_Write_BBREG
):
228 write_bbreg_hdl(padapter
, (u8
*)pcmd
);
230 case GEN_CMD_CODE(_Read_RFREG
):
231 read_rfreg_hdl(padapter
, (u8
*)pcmd
);
233 case GEN_CMD_CODE(_Write_RFREG
):
234 write_rfreg_hdl(padapter
, (u8
*)pcmd
);
236 case GEN_CMD_CODE(_SetUsbSuspend
):
237 sys_suspend_hdl(padapter
, (u8
*)pcmd
);
239 case GEN_CMD_CODE(_JoinBss
):
240 r8712_joinbss_reset(padapter
);
241 /* Before set JoinBss_CMD to FW, driver must ensure FW is in
242 * PS_MODE_ACTIVE. Directly write rpwm to radio on and assign
243 * new pwr_mode to Driver, instead of use workitem to change
246 if (padapter
->pwrctrlpriv
.pwr_mode
> PS_MODE_ACTIVE
) {
247 padapter
->pwrctrlpriv
.pwr_mode
= PS_MODE_ACTIVE
;
248 mutex_lock(&padapter
->pwrctrlpriv
.mutex_lock
);
249 r8712_set_rpwm(padapter
, PS_STATE_S4
);
250 mutex_unlock(&padapter
->pwrctrlpriv
.mutex_lock
);
255 r871x_internal_cmd_hdl(padapter
, pcmd
->parmbuf
);
256 r8712_free_cmd_obj(pcmd
);
263 return pcmd_r
; /* if returning pcmd_r == NULL, pcmd must be free. */
266 static u8
check_cmd_fifo(struct _adapter
*padapter
, uint sz
)
271 u8
r8712_fw_cmd(struct _adapter
*pAdapter
, u32 cmd
)
273 int pollingcnts
= 50;
275 r8712_write32(pAdapter
, IOCMD_CTRL_REG
, cmd
);
277 while ((r8712_read32(pAdapter
, IOCMD_CTRL_REG
!= 0)) &&
282 if (pollingcnts
== 0)
287 void r8712_fw_cmd_data(struct _adapter
*pAdapter
, u32
*value
, u8 flag
)
289 if (flag
== 0) /* set */
290 r8712_write32(pAdapter
, IOCMD_DATA_REG
, *value
);
292 *value
= r8712_read32(pAdapter
, IOCMD_DATA_REG
);
295 int r8712_cmd_thread(void *context
)
297 struct cmd_obj
*pcmd
;
298 unsigned int cmdsz
, wr_sz
;
300 struct tx_desc
*pdesc
;
301 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
302 struct _adapter
*padapter
= context
;
303 struct cmd_priv
*pcmdpriv
= &padapter
->cmdpriv
;
304 struct completion
*cmd_queue_comp
=
305 &pcmdpriv
->cmd_queue_comp
;
306 struct mutex
*pwctrl_lock
= &padapter
->pwrctrlpriv
.mutex_lock
;
308 allow_signal(SIGTERM
);
310 if (wait_for_completion_interruptible(cmd_queue_comp
))
312 if (padapter
->bDriverStopped
|| padapter
->bSurpriseRemoved
)
314 if (r8712_register_cmd_alive(padapter
) != _SUCCESS
)
317 pcmd
= r8712_dequeue_cmd(&pcmdpriv
->cmd_queue
);
319 r8712_unregister_cmd_alive(padapter
);
322 pcmdbuf
= (__le32
*)pcmdpriv
->cmd_buf
;
323 pdesc
= (struct tx_desc
*)pcmdbuf
;
324 memset(pdesc
, 0, TXDESC_SIZE
);
325 pcmd
= cmd_hdl_filter(padapter
, pcmd
);
326 if (pcmd
) { /* if pcmd != NULL, cmd will be handled by f/w */
327 struct dvobj_priv
*pdvobj
= &padapter
->dvobjpriv
;
329 u16 cmdcode
= pcmd
->cmdcode
;
331 pcmdpriv
->cmd_issued_cnt
++;
332 cmdsz
= round_up(pcmd
->cmdsz
, 8);
333 wr_sz
= TXDESC_SIZE
+ 8 + cmdsz
;
334 pdesc
->txdw0
|= cpu_to_le32((wr_sz
- TXDESC_SIZE
) &
336 if (pdvobj
->ishighspeed
) {
337 if ((wr_sz
% 512) == 0)
340 if ((wr_sz
% 64) == 0)
343 if (blnPending
) { /* 32 bytes for TX Desc - 8 offset */
344 pdesc
->txdw0
|= cpu_to_le32(((TXDESC_SIZE
+
345 OFFSET_SZ
+ 8) << OFFSET_SHT
) &
348 pdesc
->txdw0
|= cpu_to_le32(((TXDESC_SIZE
+
353 pdesc
->txdw0
|= cpu_to_le32(OWN
| FSG
| LSG
);
354 pdesc
->txdw1
|= cpu_to_le32((0x13 << QSEL_SHT
) &
356 pcmdbuf
+= (TXDESC_SIZE
>> 2);
357 *pcmdbuf
= cpu_to_le32((cmdsz
& 0x0000ffff) |
358 (pcmd
->cmdcode
<< 16) |
359 (pcmdpriv
->cmd_seq
<< 24));
360 pcmdbuf
+= 2; /* 8 bytes alignment */
361 memcpy((u8
*)pcmdbuf
, pcmd
->parmbuf
, pcmd
->cmdsz
);
362 while (check_cmd_fifo(padapter
, wr_sz
) == _FAIL
) {
363 if (padapter
->bDriverStopped
||
364 padapter
->bSurpriseRemoved
)
370 wr_sz
+= 8; /* Append 8 bytes */
371 r8712_write_mem(padapter
, RTL8712_DMA_H2CCMD
, wr_sz
,
374 if (cmdcode
== GEN_CMD_CODE(_CreateBss
)) {
375 pcmd
->res
= H2C_SUCCESS
;
376 pcmd_callback
= cmd_callback
[cmdcode
].callback
;
378 pcmd_callback(padapter
, pcmd
);
381 if (cmdcode
== GEN_CMD_CODE(_SetPwrMode
)) {
382 if (padapter
->pwrctrlpriv
.bSleep
) {
383 mutex_lock(pwctrl_lock
);
384 r8712_set_rpwm(padapter
, PS_STATE_S2
);
385 mutex_unlock(pwctrl_lock
);
388 r8712_free_cmd_obj(pcmd
);
389 if (list_empty(&pcmdpriv
->cmd_queue
.queue
)) {
390 r8712_unregister_cmd_alive(padapter
);
398 flush_signals_thread();
400 /* free all cmd_obj resources */
402 pcmd
= r8712_dequeue_cmd(&pcmdpriv
->cmd_queue
);
405 r8712_free_cmd_obj(pcmd
);
407 complete(&pcmdpriv
->terminate_cmdthread_comp
);
411 void r8712_event_handle(struct _adapter
*padapter
, __le32
*peventbuf
)
413 u8 evt_code
, evt_seq
;
415 void (*event_callback
)(struct _adapter
*dev
, u8
*pbuf
);
416 struct evt_priv
*pevt_priv
= &padapter
->evtpriv
;
420 evt_sz
= (u16
)(le32_to_cpu(*peventbuf
) & 0xffff);
421 evt_seq
= (u8
)((le32_to_cpu(*peventbuf
) >> 24) & 0x7f);
422 evt_code
= (u8
)((le32_to_cpu(*peventbuf
) >> 16) & 0xff);
423 /* checking event sequence... */
424 if ((evt_seq
& 0x7f) != pevt_priv
->event_seq
) {
425 pevt_priv
->event_seq
= ((evt_seq
+ 1) & 0x7f);
428 /* checking if event code is valid */
429 if (evt_code
>= MAX_C2HEVT
) {
430 pevt_priv
->event_seq
= ((evt_seq
+ 1) & 0x7f);
432 } else if ((evt_code
== GEN_EVT_CODE(_Survey
)) &&
433 (evt_sz
> sizeof(struct wlan_bssid_ex
))) {
434 pevt_priv
->event_seq
= ((evt_seq
+ 1) & 0x7f);
437 /* checking if event size match the event parm size */
438 if ((wlanevents
[evt_code
].parmsize
) &&
439 (wlanevents
[evt_code
].parmsize
!= evt_sz
)) {
440 pevt_priv
->event_seq
= ((evt_seq
+ 1) & 0x7f);
442 } else if ((evt_sz
== 0) && (evt_code
!= GEN_EVT_CODE(_WPS_PBC
))) {
443 pevt_priv
->event_seq
= ((evt_seq
+ 1) & 0x7f);
446 pevt_priv
->event_seq
++; /* update evt_seq */
447 if (pevt_priv
->event_seq
> 127)
448 pevt_priv
->event_seq
= 0;
449 /* move to event content, 8 bytes alignment */
450 peventbuf
= peventbuf
+ 2;
451 event_callback
= wlanevents
[evt_code
].event_callback
;
453 event_callback(padapter
, (u8
*)peventbuf
);
454 pevt_priv
->evt_done_cnt
++;