1 /******************************************************************************
4 * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
5 * Linux device driver for RTL8192SU
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of version 2 of the GNU General Public License as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * You should have received a copy of the GNU General Public License along with
17 * this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
20 * Modifications for inclusion into the Linux staging tree are
21 * Copyright(c) 2010 Larry Finger. All rights reserved.
23 * Contact information:
24 * WLAN FAE <wlanfae@realtek.com>.
25 * Larry Finger <Larry.Finger@lwfinger.net>
27 ******************************************************************************/
29 #define _RTL8712_CMD_C_
31 #include "osdep_service.h"
32 #include "drv_types.h"
33 #include "recv_osdep.h"
34 #include "mlme_osdep.h"
35 #include "rtl871x_byteorder.h"
36 #include "rtl871x_ioctl_set.h"
38 static void check_hw_pbc(struct _adapter
*padapter
)
42 r8712_write8(padapter
, MAC_PINMUX_CTRL
, (GPIOMUX_EN
| GPIOSEL_GPIO
));
43 tmp1byte
= r8712_read8(padapter
, GPIO_IO_SEL
);
44 tmp1byte
&= ~(HAL_8192S_HW_GPIO_WPS_BIT
);
45 r8712_write8(padapter
, GPIO_IO_SEL
, tmp1byte
);
46 tmp1byte
= r8712_read8(padapter
, GPIO_CTRL
);
49 if (tmp1byte
&HAL_8192S_HW_GPIO_WPS_BIT
) {
50 /* Here we only set bPbcPressed to true
51 * After trigger PBC, the variable will be set to false */
52 DBG_8712("CheckPbcGPIO - PBC is pressed !!!!\n");
53 /* 0 is the default value and it means the application monitors
54 * the HW PBC doesn't privde its pid to driver. */
55 if (padapter
->pid
== 0)
57 kill_pid(find_vpid(padapter
->pid
), SIGUSR1
, 1);
61 /* query rx phy status from fw.
63 * Infrastructure mode: beacon , data. */
64 static void query_fw_rx_phy_status(struct _adapter
*padapter
)
69 if (check_fwstate(&padapter
->mlmepriv
, _FW_LINKED
) == true) {
70 r8712_write32(padapter
, IOCMD_CTRL_REG
, 0xf4000001);
72 /* Wait FW complete IO Cmd */
73 while ((r8712_read32(padapter
, IOCMD_CTRL_REG
)) &&
79 val32
= r8712_read32(padapter
, IOCMD_DATA_REG
);
83 padapter
->recvpriv
.fw_rssi
=
84 (u8
)r8712_signal_scale_mapping(val32
);
88 /* check mlme, hw, phy, or dynamic algorithm status. */
89 static void StatusWatchdogCallback(struct _adapter
*padapter
)
91 check_hw_pbc(padapter
);
92 query_fw_rx_phy_status(padapter
);
95 static void r871x_internal_cmd_hdl(struct _adapter
*padapter
, u8
*pbuf
)
97 struct drvint_cmd_parm
*pdrvcmd
;
101 pdrvcmd
= (struct drvint_cmd_parm
*)pbuf
;
102 switch (pdrvcmd
->i_cid
) {
104 StatusWatchdogCallback(padapter
);
109 kfree(pdrvcmd
->pbuf
);
112 static u8
read_macreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
114 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
115 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
117 /* invoke cmd->callback function */
118 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
119 if (pcmd_callback
== NULL
)
120 r8712_free_cmd_obj(pcmd
);
122 pcmd_callback(padapter
, pcmd
);
126 static u8
write_macreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
128 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
129 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
131 /* invoke cmd->callback function */
132 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
133 if (pcmd_callback
== NULL
)
134 r8712_free_cmd_obj(pcmd
);
136 pcmd_callback(padapter
, pcmd
);
140 static u8
read_bbreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
143 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
144 struct readBB_parm
*prdbbparm
;
145 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
147 prdbbparm
= (struct readBB_parm
*)pcmd
->parmbuf
;
148 if (pcmd
->rsp
&& pcmd
->rspsz
> 0)
149 memcpy(pcmd
->rsp
, (u8
*)&val
, pcmd
->rspsz
);
150 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
151 if (pcmd_callback
== NULL
)
152 r8712_free_cmd_obj(pcmd
);
154 pcmd_callback(padapter
, pcmd
);
158 static u8
write_bbreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
160 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
161 struct writeBB_parm
*pwritebbparm
;
162 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
164 pwritebbparm
= (struct writeBB_parm
*)pcmd
->parmbuf
;
165 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
166 if (pcmd_callback
== NULL
)
167 r8712_free_cmd_obj(pcmd
);
169 pcmd_callback(padapter
, pcmd
);
173 static u8
read_rfreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
176 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
177 struct readRF_parm
*prdrfparm
;
178 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
180 prdrfparm
= (struct readRF_parm
*)pcmd
->parmbuf
;
181 if (pcmd
->rsp
&& pcmd
->rspsz
> 0)
182 memcpy(pcmd
->rsp
, (u8
*)&val
, pcmd
->rspsz
);
183 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
184 if (pcmd_callback
== NULL
)
185 r8712_free_cmd_obj(pcmd
);
187 pcmd_callback(padapter
, pcmd
);
191 static u8
write_rfreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
193 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
194 struct writeRF_parm
*pwriterfparm
;
195 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
197 pwriterfparm
= (struct writeRF_parm
*)pcmd
->parmbuf
;
198 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
199 if (pcmd_callback
== NULL
)
200 r8712_free_cmd_obj(pcmd
);
202 pcmd_callback(padapter
, pcmd
);
206 static u8
sys_suspend_hdl(struct _adapter
*padapter
, u8
*pbuf
)
208 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
209 struct usb_suspend_parm
*psetusbsuspend
;
211 psetusbsuspend
= (struct usb_suspend_parm
*)pcmd
->parmbuf
;
212 r8712_free_cmd_obj(pcmd
);
216 static struct cmd_obj
*cmd_hdl_filter(struct _adapter
*padapter
,
217 struct cmd_obj
*pcmd
)
219 struct cmd_obj
*pcmd_r
;
225 switch (pcmd
->cmdcode
) {
226 case GEN_CMD_CODE(_Read_MACREG
):
227 read_macreg_hdl(padapter
, (u8
*)pcmd
);
230 case GEN_CMD_CODE(_Write_MACREG
):
231 write_macreg_hdl(padapter
, (u8
*)pcmd
);
234 case GEN_CMD_CODE(_Read_BBREG
):
235 read_bbreg_hdl(padapter
, (u8
*)pcmd
);
237 case GEN_CMD_CODE(_Write_BBREG
):
238 write_bbreg_hdl(padapter
, (u8
*)pcmd
);
240 case GEN_CMD_CODE(_Read_RFREG
):
241 read_rfreg_hdl(padapter
, (u8
*)pcmd
);
243 case GEN_CMD_CODE(_Write_RFREG
):
244 write_rfreg_hdl(padapter
, (u8
*)pcmd
);
246 case GEN_CMD_CODE(_SetUsbSuspend
):
247 sys_suspend_hdl(padapter
, (u8
*)pcmd
);
249 case GEN_CMD_CODE(_JoinBss
):
250 r8712_joinbss_reset(padapter
);
251 /* Before set JoinBss_CMD to FW, driver must ensure FW is in
252 * PS_MODE_ACTIVE. Directly write rpwm to radio on and assign
253 * new pwr_mode to Driver, instead of use workitem to change
255 if (padapter
->pwrctrlpriv
.pwr_mode
> PS_MODE_ACTIVE
) {
256 padapter
->pwrctrlpriv
.pwr_mode
= PS_MODE_ACTIVE
;
257 _enter_pwrlock(&(padapter
->pwrctrlpriv
.lock
));
258 r8712_set_rpwm(padapter
, PS_STATE_S4
);
259 up(&(padapter
->pwrctrlpriv
.lock
));
264 r871x_internal_cmd_hdl(padapter
, pcmd
->parmbuf
);
265 r8712_free_cmd_obj(pcmd
);
272 return pcmd_r
; /* if returning pcmd_r == NULL, pcmd must be free. */
275 static u8
check_cmd_fifo(struct _adapter
*padapter
, uint sz
)
281 u8
r8712_fw_cmd(struct _adapter
*pAdapter
, u32 cmd
)
283 int pollingcnts
= 50;
285 r8712_write32(pAdapter
, IOCMD_CTRL_REG
, cmd
);
287 while ((0 != r8712_read32(pAdapter
, IOCMD_CTRL_REG
)) &&
292 if (pollingcnts
== 0)
297 void r8712_fw_cmd_data(struct _adapter
*pAdapter
, u32
*value
, u8 flag
)
299 if (flag
== 0) /* set */
300 r8712_write32(pAdapter
, IOCMD_DATA_REG
, *value
);
302 *value
= r8712_read32(pAdapter
, IOCMD_DATA_REG
);
305 int r8712_cmd_thread(void *context
)
307 struct cmd_obj
*pcmd
;
308 unsigned int cmdsz
, wr_sz
, *pcmdbuf
, *prspbuf
;
309 struct tx_desc
*pdesc
;
310 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
311 struct _adapter
*padapter
= (struct _adapter
*)context
;
312 struct cmd_priv
*pcmdpriv
= &(padapter
->cmdpriv
);
314 thread_enter(padapter
);
316 if ((_down_sema(&(pcmdpriv
->cmd_queue_sema
))) == _FAIL
)
318 if ((padapter
->bDriverStopped
== true) ||
319 (padapter
->bSurpriseRemoved
== true))
321 if (r8712_register_cmd_alive(padapter
) != _SUCCESS
)
324 pcmd
= r8712_dequeue_cmd(&(pcmdpriv
->cmd_queue
));
326 r8712_unregister_cmd_alive(padapter
);
329 pcmdbuf
= (unsigned int *)pcmdpriv
->cmd_buf
;
330 prspbuf
= (unsigned int *)pcmdpriv
->rsp_buf
;
331 pdesc
= (struct tx_desc
*)pcmdbuf
;
332 memset(pdesc
, 0, TXDESC_SIZE
);
333 pcmd
= cmd_hdl_filter(padapter
, pcmd
);
334 if (pcmd
) { /* if pcmd != NULL, cmd will be handled by f/w */
335 struct dvobj_priv
*pdvobj
= (struct dvobj_priv
*)
336 &padapter
->dvobjpriv
;
338 pcmdpriv
->cmd_issued_cnt
++;
339 cmdsz
= _RND8((pcmd
->cmdsz
)); /* _RND8 */
340 wr_sz
= TXDESC_SIZE
+ 8 + cmdsz
;
341 pdesc
->txdw0
|= cpu_to_le32((wr_sz
-TXDESC_SIZE
) &
343 if (pdvobj
->ishighspeed
) {
344 if ((wr_sz
% 512) == 0)
347 if ((wr_sz
% 64) == 0)
350 if (blnPending
) /* 32 bytes for TX Desc - 8 offset */
351 pdesc
->txdw0
|= cpu_to_le32(((TXDESC_SIZE
+
352 OFFSET_SZ
+ 8) << OFFSET_SHT
) &
355 pdesc
->txdw0
|= cpu_to_le32(((TXDESC_SIZE
+
360 pdesc
->txdw0
|= cpu_to_le32(OWN
| FSG
| LSG
);
361 pdesc
->txdw1
|= cpu_to_le32((0x13 << QSEL_SHT
) &
363 pcmdbuf
+= (TXDESC_SIZE
>> 2);
364 *pcmdbuf
= cpu_to_le32((cmdsz
& 0x0000ffff) |
365 (pcmd
->cmdcode
<< 16) |
366 (pcmdpriv
->cmd_seq
<< 24));
367 pcmdbuf
+= 2 ; /* 8 bytes aligment */
368 memcpy((u8
*)pcmdbuf
, pcmd
->parmbuf
, pcmd
->cmdsz
);
369 while (check_cmd_fifo(padapter
, wr_sz
) == _FAIL
) {
370 if ((padapter
->bDriverStopped
== true) ||
371 (padapter
->bSurpriseRemoved
== true))
377 wr_sz
+= 8; /* Append 8 bytes */
378 r8712_write_mem(padapter
, RTL8712_DMA_H2CCMD
, wr_sz
,
381 if (pcmd
->cmdcode
== GEN_CMD_CODE(_CreateBss
)) {
382 pcmd
->res
= H2C_SUCCESS
;
383 pcmd_callback
= cmd_callback
[pcmd
->
386 pcmd_callback(padapter
, pcmd
);
389 if (pcmd
->cmdcode
== GEN_CMD_CODE(_SetPwrMode
)) {
390 if (padapter
->pwrctrlpriv
.bSleep
) {
391 _enter_pwrlock(&(padapter
->
393 r8712_set_rpwm(padapter
, PS_STATE_S2
);
394 up(&padapter
->pwrctrlpriv
.lock
);
397 r8712_free_cmd_obj(pcmd
);
398 if (_queue_empty(&(pcmdpriv
->cmd_queue
))) {
399 r8712_unregister_cmd_alive(padapter
);
405 flush_signals_thread();
407 /* free all cmd_obj resources */
409 pcmd
= r8712_dequeue_cmd(&(pcmdpriv
->cmd_queue
));
412 r8712_free_cmd_obj(pcmd
);
414 up(&pcmdpriv
->terminate_cmdthread_sema
);
418 void r8712_event_handle(struct _adapter
*padapter
, uint
*peventbuf
)
420 u8 evt_code
, evt_seq
;
422 void (*event_callback
)(struct _adapter
*dev
, u8
*pbuf
);
423 struct evt_priv
*pevt_priv
= &(padapter
->evtpriv
);
425 if (peventbuf
== NULL
)
427 evt_sz
= (u16
)(le32_to_cpu(*peventbuf
) & 0xffff);
428 evt_seq
= (u8
)((le32_to_cpu(*peventbuf
) >> 24) & 0x7f);
429 evt_code
= (u8
)((le32_to_cpu(*peventbuf
) >> 16) & 0xff);
430 /* checking event sequence... */
431 if ((evt_seq
& 0x7f) != pevt_priv
->event_seq
) {
432 pevt_priv
->event_seq
= ((evt_seq
+ 1) & 0x7f);
435 /* checking if event code is valid */
436 if (evt_code
>= MAX_C2HEVT
) {
437 pevt_priv
->event_seq
= ((evt_seq
+1) & 0x7f);
439 } else if ((evt_code
== GEN_EVT_CODE(_Survey
)) &&
440 (evt_sz
> sizeof(struct wlan_bssid_ex
))) {
441 pevt_priv
->event_seq
= ((evt_seq
+1)&0x7f);
444 /* checking if event size match the event parm size */
445 if ((wlanevents
[evt_code
].parmsize
) &&
446 (wlanevents
[evt_code
].parmsize
!= evt_sz
)) {
447 pevt_priv
->event_seq
= ((evt_seq
+1)&0x7f);
449 } else if ((evt_sz
== 0) && (evt_code
!= GEN_EVT_CODE(_WPS_PBC
))) {
450 pevt_priv
->event_seq
= ((evt_seq
+1)&0x7f);
453 pevt_priv
->event_seq
++; /* update evt_seq */
454 if (pevt_priv
->event_seq
> 127)
455 pevt_priv
->event_seq
= 0;
456 peventbuf
= peventbuf
+ 2; /* move to event content, 8 bytes aligment */
458 event_callback
= wlanevents
[evt_code
].event_callback
;
460 event_callback(padapter
, (u8
*)peventbuf
);
462 pevt_priv
->evt_done_cnt
++;