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 <linux/compiler.h>
32 #include <linux/kernel.h>
33 #include <linux/errno.h>
34 #include <linux/init.h>
35 #include <linux/slab.h>
36 #include <linux/module.h>
37 #include <linux/kref.h>
38 #include <linux/netdevice.h>
39 #include <linux/skbuff.h>
40 #include <linux/usb.h>
41 #include <linux/usb/ch9.h>
42 #include <linux/circ_buf.h>
43 #include <linux/uaccess.h>
44 #include <asm/byteorder.h>
45 #include <linux/atomic.h>
46 #include <linux/semaphore.h>
47 #include <linux/rtnetlink.h>
49 #include "osdep_service.h"
50 #include "drv_types.h"
51 #include "recv_osdep.h"
52 #include "mlme_osdep.h"
53 #include "rtl871x_byteorder.h"
54 #include "rtl871x_ioctl_set.h"
56 static void check_hw_pbc(struct _adapter
*padapter
)
60 r8712_write8(padapter
, MAC_PINMUX_CTRL
, (GPIOMUX_EN
| GPIOSEL_GPIO
));
61 tmp1byte
= r8712_read8(padapter
, GPIO_IO_SEL
);
62 tmp1byte
&= ~(HAL_8192S_HW_GPIO_WPS_BIT
);
63 r8712_write8(padapter
, GPIO_IO_SEL
, tmp1byte
);
64 tmp1byte
= r8712_read8(padapter
, GPIO_CTRL
);
67 if (tmp1byte
&HAL_8192S_HW_GPIO_WPS_BIT
) {
68 /* Here we only set bPbcPressed to true
69 * After trigger PBC, the variable will be set to false */
70 DBG_8712("CheckPbcGPIO - PBC is pressed !!!!\n");
71 /* 0 is the default value and it means the application monitors
72 * the HW PBC doesn't privde its pid to driver. */
73 if (padapter
->pid
== 0)
75 kill_pid(find_vpid(padapter
->pid
), SIGUSR1
, 1);
79 /* query rx phy status from fw.
81 * Infrastructure mode: beacon , data. */
82 static void query_fw_rx_phy_status(struct _adapter
*padapter
)
87 if (check_fwstate(&padapter
->mlmepriv
, _FW_LINKED
) == true) {
88 r8712_write32(padapter
, IOCMD_CTRL_REG
, 0xf4000001);
90 /* Wait FW complete IO Cmd */
91 while ((r8712_read32(padapter
, IOCMD_CTRL_REG
)) &&
97 val32
= r8712_read32(padapter
, IOCMD_DATA_REG
);
101 padapter
->recvpriv
.fw_rssi
=
102 (u8
)r8712_signal_scale_mapping(val32
);
106 /* check mlme, hw, phy, or dynamic algorithm status. */
107 static void StatusWatchdogCallback(struct _adapter
*padapter
)
109 check_hw_pbc(padapter
);
110 query_fw_rx_phy_status(padapter
);
113 static void r871x_internal_cmd_hdl(struct _adapter
*padapter
, u8
*pbuf
)
115 struct drvint_cmd_parm
*pdrvcmd
;
119 pdrvcmd
= (struct drvint_cmd_parm
*)pbuf
;
120 switch (pdrvcmd
->i_cid
) {
122 StatusWatchdogCallback(padapter
);
127 kfree(pdrvcmd
->pbuf
);
130 static u8
read_macreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
132 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
133 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
135 /* invoke cmd->callback function */
136 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
137 if (pcmd_callback
== NULL
)
138 r8712_free_cmd_obj(pcmd
);
140 pcmd_callback(padapter
, pcmd
);
144 static u8
write_macreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
146 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
147 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
149 /* invoke cmd->callback function */
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
read_bbreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
161 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
162 struct readBB_parm
*prdbbparm
;
163 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
165 prdbbparm
= (struct readBB_parm
*)pcmd
->parmbuf
;
166 if (pcmd
->rsp
&& pcmd
->rspsz
> 0)
167 memcpy(pcmd
->rsp
, (u8
*)&val
, pcmd
->rspsz
);
168 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
169 if (pcmd_callback
== NULL
)
170 r8712_free_cmd_obj(pcmd
);
172 pcmd_callback(padapter
, pcmd
);
176 static u8
write_bbreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
178 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
179 struct writeBB_parm
*pwritebbparm
;
180 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
182 pwritebbparm
= (struct writeBB_parm
*)pcmd
->parmbuf
;
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
read_rfreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
194 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
195 struct readRF_parm
*prdrfparm
;
196 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
198 prdrfparm
= (struct readRF_parm
*)pcmd
->parmbuf
;
199 if (pcmd
->rsp
&& pcmd
->rspsz
> 0)
200 memcpy(pcmd
->rsp
, (u8
*)&val
, pcmd
->rspsz
);
201 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
202 if (pcmd_callback
== NULL
)
203 r8712_free_cmd_obj(pcmd
);
205 pcmd_callback(padapter
, pcmd
);
209 static u8
write_rfreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
211 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
212 struct writeRF_parm
*pwriterfparm
;
213 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
215 pwriterfparm
= (struct writeRF_parm
*)pcmd
->parmbuf
;
216 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
217 if (pcmd_callback
== NULL
)
218 r8712_free_cmd_obj(pcmd
);
220 pcmd_callback(padapter
, pcmd
);
224 static u8
sys_suspend_hdl(struct _adapter
*padapter
, u8
*pbuf
)
226 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
227 struct usb_suspend_parm
*psetusbsuspend
;
229 psetusbsuspend
= (struct usb_suspend_parm
*)pcmd
->parmbuf
;
230 r8712_free_cmd_obj(pcmd
);
234 static struct cmd_obj
*cmd_hdl_filter(struct _adapter
*padapter
,
235 struct cmd_obj
*pcmd
)
237 struct cmd_obj
*pcmd_r
;
243 switch (pcmd
->cmdcode
) {
244 case GEN_CMD_CODE(_Read_MACREG
):
245 read_macreg_hdl(padapter
, (u8
*)pcmd
);
248 case GEN_CMD_CODE(_Write_MACREG
):
249 write_macreg_hdl(padapter
, (u8
*)pcmd
);
252 case GEN_CMD_CODE(_Read_BBREG
):
253 read_bbreg_hdl(padapter
, (u8
*)pcmd
);
255 case GEN_CMD_CODE(_Write_BBREG
):
256 write_bbreg_hdl(padapter
, (u8
*)pcmd
);
258 case GEN_CMD_CODE(_Read_RFREG
):
259 read_rfreg_hdl(padapter
, (u8
*)pcmd
);
261 case GEN_CMD_CODE(_Write_RFREG
):
262 write_rfreg_hdl(padapter
, (u8
*)pcmd
);
264 case GEN_CMD_CODE(_SetUsbSuspend
):
265 sys_suspend_hdl(padapter
, (u8
*)pcmd
);
267 case GEN_CMD_CODE(_JoinBss
):
268 r8712_joinbss_reset(padapter
);
269 /* Before set JoinBss_CMD to FW, driver must ensure FW is in
270 * PS_MODE_ACTIVE. Directly write rpwm to radio on and assign
271 * new pwr_mode to Driver, instead of use workitem to change
273 if (padapter
->pwrctrlpriv
.pwr_mode
> PS_MODE_ACTIVE
) {
274 padapter
->pwrctrlpriv
.pwr_mode
= PS_MODE_ACTIVE
;
275 _enter_pwrlock(&(padapter
->pwrctrlpriv
.lock
));
276 r8712_set_rpwm(padapter
, PS_STATE_S4
);
277 up(&(padapter
->pwrctrlpriv
.lock
));
282 r871x_internal_cmd_hdl(padapter
, pcmd
->parmbuf
);
283 r8712_free_cmd_obj(pcmd
);
290 return pcmd_r
; /* if returning pcmd_r == NULL, pcmd must be free. */
293 static u8
check_cmd_fifo(struct _adapter
*padapter
, uint sz
)
299 u8
r8712_fw_cmd(struct _adapter
*pAdapter
, u32 cmd
)
301 int pollingcnts
= 50;
303 r8712_write32(pAdapter
, IOCMD_CTRL_REG
, cmd
);
305 while ((0 != r8712_read32(pAdapter
, IOCMD_CTRL_REG
)) &&
310 if (pollingcnts
== 0)
315 void r8712_fw_cmd_data(struct _adapter
*pAdapter
, u32
*value
, u8 flag
)
317 if (flag
== 0) /* set */
318 r8712_write32(pAdapter
, IOCMD_DATA_REG
, *value
);
320 *value
= r8712_read32(pAdapter
, IOCMD_DATA_REG
);
323 int r8712_cmd_thread(void *context
)
325 struct cmd_obj
*pcmd
;
326 unsigned int cmdsz
, wr_sz
, *pcmdbuf
, *prspbuf
;
327 struct tx_desc
*pdesc
;
328 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
329 struct _adapter
*padapter
= (struct _adapter
*)context
;
330 struct cmd_priv
*pcmdpriv
= &(padapter
->cmdpriv
);
332 thread_enter(padapter
);
334 if ((_down_sema(&(pcmdpriv
->cmd_queue_sema
))) == _FAIL
)
336 if ((padapter
->bDriverStopped
== true) ||
337 (padapter
->bSurpriseRemoved
== true))
339 if (r8712_register_cmd_alive(padapter
) != _SUCCESS
)
342 pcmd
= r8712_dequeue_cmd(&(pcmdpriv
->cmd_queue
));
344 r8712_unregister_cmd_alive(padapter
);
347 pcmdbuf
= (unsigned int *)pcmdpriv
->cmd_buf
;
348 prspbuf
= (unsigned int *)pcmdpriv
->rsp_buf
;
349 pdesc
= (struct tx_desc
*)pcmdbuf
;
350 memset(pdesc
, 0, TXDESC_SIZE
);
351 pcmd
= cmd_hdl_filter(padapter
, pcmd
);
352 if (pcmd
) { /* if pcmd != NULL, cmd will be handled by f/w */
353 struct dvobj_priv
*pdvobj
= (struct dvobj_priv
*)
354 &padapter
->dvobjpriv
;
356 pcmdpriv
->cmd_issued_cnt
++;
357 cmdsz
= _RND8((pcmd
->cmdsz
)); /* _RND8 */
358 wr_sz
= TXDESC_SIZE
+ 8 + cmdsz
;
359 pdesc
->txdw0
|= cpu_to_le32((wr_sz
-TXDESC_SIZE
) &
361 if (pdvobj
->ishighspeed
) {
362 if ((wr_sz
% 512) == 0)
365 if ((wr_sz
% 64) == 0)
368 if (blnPending
) /* 32 bytes for TX Desc - 8 offset */
369 pdesc
->txdw0
|= cpu_to_le32(((TXDESC_SIZE
+
370 OFFSET_SZ
+ 8) << OFFSET_SHT
) &
373 pdesc
->txdw0
|= cpu_to_le32(((TXDESC_SIZE
+
378 pdesc
->txdw0
|= cpu_to_le32(OWN
| FSG
| LSG
);
379 pdesc
->txdw1
|= cpu_to_le32((0x13 << QSEL_SHT
) &
381 pcmdbuf
+= (TXDESC_SIZE
>> 2);
382 *pcmdbuf
= cpu_to_le32((cmdsz
& 0x0000ffff) |
383 (pcmd
->cmdcode
<< 16) |
384 (pcmdpriv
->cmd_seq
<< 24));
385 pcmdbuf
+= 2 ; /* 8 bytes aligment */
386 memcpy((u8
*)pcmdbuf
, pcmd
->parmbuf
, pcmd
->cmdsz
);
387 while (check_cmd_fifo(padapter
, wr_sz
) == _FAIL
) {
388 if ((padapter
->bDriverStopped
== true) ||
389 (padapter
->bSurpriseRemoved
== true))
395 wr_sz
+= 8; /* Append 8 bytes */
396 r8712_write_mem(padapter
, RTL8712_DMA_H2CCMD
, wr_sz
,
399 if (pcmd
->cmdcode
== GEN_CMD_CODE(_CreateBss
)) {
400 pcmd
->res
= H2C_SUCCESS
;
401 pcmd_callback
= cmd_callback
[pcmd
->
404 pcmd_callback(padapter
, pcmd
);
407 if (pcmd
->cmdcode
== GEN_CMD_CODE(_SetPwrMode
)) {
408 if (padapter
->pwrctrlpriv
.bSleep
) {
409 _enter_pwrlock(&(padapter
->
411 r8712_set_rpwm(padapter
, PS_STATE_S2
);
412 up(&padapter
->pwrctrlpriv
.lock
);
415 r8712_free_cmd_obj(pcmd
);
416 if (_queue_empty(&(pcmdpriv
->cmd_queue
))) {
417 r8712_unregister_cmd_alive(padapter
);
423 flush_signals_thread();
425 /* free all cmd_obj resources */
427 pcmd
= r8712_dequeue_cmd(&(pcmdpriv
->cmd_queue
));
430 r8712_free_cmd_obj(pcmd
);
432 up(&pcmdpriv
->terminate_cmdthread_sema
);
436 void r8712_event_handle(struct _adapter
*padapter
, uint
*peventbuf
)
438 u8 evt_code
, evt_seq
;
440 void (*event_callback
)(struct _adapter
*dev
, u8
*pbuf
);
441 struct evt_priv
*pevt_priv
= &(padapter
->evtpriv
);
443 if (peventbuf
== NULL
)
445 evt_sz
= (u16
)(le32_to_cpu(*peventbuf
) & 0xffff);
446 evt_seq
= (u8
)((le32_to_cpu(*peventbuf
) >> 24) & 0x7f);
447 evt_code
= (u8
)((le32_to_cpu(*peventbuf
) >> 16) & 0xff);
448 /* checking event sequence... */
449 if ((evt_seq
& 0x7f) != pevt_priv
->event_seq
) {
450 pevt_priv
->event_seq
= ((evt_seq
+ 1) & 0x7f);
453 /* checking if event code is valid */
454 if (evt_code
>= MAX_C2HEVT
) {
455 pevt_priv
->event_seq
= ((evt_seq
+1) & 0x7f);
457 } else if ((evt_code
== GEN_EVT_CODE(_Survey
)) &&
458 (evt_sz
> sizeof(struct wlan_bssid_ex
))) {
459 pevt_priv
->event_seq
= ((evt_seq
+1)&0x7f);
462 /* checking if event size match the event parm size */
463 if ((wlanevents
[evt_code
].parmsize
) &&
464 (wlanevents
[evt_code
].parmsize
!= evt_sz
)) {
465 pevt_priv
->event_seq
= ((evt_seq
+1)&0x7f);
467 } else if ((evt_sz
== 0) && (evt_code
!= GEN_EVT_CODE(_WPS_PBC
))) {
468 pevt_priv
->event_seq
= ((evt_seq
+1)&0x7f);
471 pevt_priv
->event_seq
++; /* update evt_seq */
472 if (pevt_priv
->event_seq
> 127)
473 pevt_priv
->event_seq
= 0;
474 peventbuf
= peventbuf
+ 2; /* move to event content, 8 bytes aligment */
476 event_callback
= wlanevents
[evt_code
].event_callback
;
478 event_callback(padapter
, (u8
*)peventbuf
);
480 pevt_priv
->evt_done_cnt
++;