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_ioctl_set.h"
55 static void check_hw_pbc(struct _adapter
*padapter
)
59 r8712_write8(padapter
, MAC_PINMUX_CTRL
, (GPIOMUX_EN
| GPIOSEL_GPIO
));
60 tmp1byte
= r8712_read8(padapter
, GPIO_IO_SEL
);
61 tmp1byte
&= ~(HAL_8192S_HW_GPIO_WPS_BIT
);
62 r8712_write8(padapter
, GPIO_IO_SEL
, tmp1byte
);
63 tmp1byte
= r8712_read8(padapter
, GPIO_CTRL
);
66 if (tmp1byte
&HAL_8192S_HW_GPIO_WPS_BIT
) {
67 /* Here we only set bPbcPressed to true
68 * After trigger PBC, the variable will be set to false */
69 DBG_8712("CheckPbcGPIO - PBC is pressed !!!!\n");
70 /* 0 is the default value and it means the application monitors
71 * the HW PBC doesn't provide its pid to driver. */
72 if (padapter
->pid
== 0)
74 kill_pid(find_vpid(padapter
->pid
), SIGUSR1
, 1);
78 /* query rx phy status from fw.
80 * Infrastructure mode: beacon , data. */
81 static void query_fw_rx_phy_status(struct _adapter
*padapter
)
86 if (check_fwstate(&padapter
->mlmepriv
, _FW_LINKED
) == true) {
87 r8712_write32(padapter
, IOCMD_CTRL_REG
, 0xf4000001);
89 /* Wait FW complete IO Cmd */
90 while ((r8712_read32(padapter
, IOCMD_CTRL_REG
)) &&
96 val32
= r8712_read32(padapter
, IOCMD_DATA_REG
);
100 padapter
->recvpriv
.fw_rssi
=
101 (u8
)r8712_signal_scale_mapping(val32
);
105 /* check mlme, hw, phy, or dynamic algorithm status. */
106 static void StatusWatchdogCallback(struct _adapter
*padapter
)
108 check_hw_pbc(padapter
);
109 query_fw_rx_phy_status(padapter
);
112 static void r871x_internal_cmd_hdl(struct _adapter
*padapter
, u8
*pbuf
)
114 struct drvint_cmd_parm
*pdrvcmd
;
118 pdrvcmd
= (struct drvint_cmd_parm
*)pbuf
;
119 switch (pdrvcmd
->i_cid
) {
121 StatusWatchdogCallback(padapter
);
126 kfree(pdrvcmd
->pbuf
);
129 static u8
read_macreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
131 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
132 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
134 /* invoke cmd->callback function */
135 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
136 if (pcmd_callback
== NULL
)
137 r8712_free_cmd_obj(pcmd
);
139 pcmd_callback(padapter
, pcmd
);
143 static u8
write_macreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
145 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
146 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
148 /* invoke cmd->callback function */
149 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
150 if (pcmd_callback
== NULL
)
151 r8712_free_cmd_obj(pcmd
);
153 pcmd_callback(padapter
, pcmd
);
157 static u8
read_bbreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
160 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
161 struct readBB_parm
*prdbbparm
;
162 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
164 prdbbparm
= (struct readBB_parm
*)pcmd
->parmbuf
;
165 if (pcmd
->rsp
&& pcmd
->rspsz
> 0)
166 memcpy(pcmd
->rsp
, (u8
*)&val
, pcmd
->rspsz
);
167 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
168 if (pcmd_callback
== NULL
)
169 r8712_free_cmd_obj(pcmd
);
171 pcmd_callback(padapter
, pcmd
);
175 static u8
write_bbreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
177 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
178 struct writeBB_parm
*pwritebbparm
;
179 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
181 pwritebbparm
= (struct writeBB_parm
*)pcmd
->parmbuf
;
182 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
183 if (pcmd_callback
== NULL
)
184 r8712_free_cmd_obj(pcmd
);
186 pcmd_callback(padapter
, pcmd
);
190 static u8
read_rfreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
193 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
194 struct readRF_parm
*prdrfparm
;
195 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
197 prdrfparm
= (struct readRF_parm
*)pcmd
->parmbuf
;
198 if (pcmd
->rsp
&& pcmd
->rspsz
> 0)
199 memcpy(pcmd
->rsp
, (u8
*)&val
, pcmd
->rspsz
);
200 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
201 if (pcmd_callback
== NULL
)
202 r8712_free_cmd_obj(pcmd
);
204 pcmd_callback(padapter
, pcmd
);
208 static u8
write_rfreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
210 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
211 struct writeRF_parm
*pwriterfparm
;
212 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
214 pwriterfparm
= (struct writeRF_parm
*)pcmd
->parmbuf
;
215 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
216 if (pcmd_callback
== NULL
)
217 r8712_free_cmd_obj(pcmd
);
219 pcmd_callback(padapter
, pcmd
);
223 static u8
sys_suspend_hdl(struct _adapter
*padapter
, u8
*pbuf
)
225 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
226 struct usb_suspend_parm
*psetusbsuspend
;
228 psetusbsuspend
= (struct usb_suspend_parm
*)pcmd
->parmbuf
;
229 r8712_free_cmd_obj(pcmd
);
233 static struct cmd_obj
*cmd_hdl_filter(struct _adapter
*padapter
,
234 struct cmd_obj
*pcmd
)
236 struct cmd_obj
*pcmd_r
;
242 switch (pcmd
->cmdcode
) {
243 case GEN_CMD_CODE(_Read_MACREG
):
244 read_macreg_hdl(padapter
, (u8
*)pcmd
);
247 case GEN_CMD_CODE(_Write_MACREG
):
248 write_macreg_hdl(padapter
, (u8
*)pcmd
);
251 case GEN_CMD_CODE(_Read_BBREG
):
252 read_bbreg_hdl(padapter
, (u8
*)pcmd
);
254 case GEN_CMD_CODE(_Write_BBREG
):
255 write_bbreg_hdl(padapter
, (u8
*)pcmd
);
257 case GEN_CMD_CODE(_Read_RFREG
):
258 read_rfreg_hdl(padapter
, (u8
*)pcmd
);
260 case GEN_CMD_CODE(_Write_RFREG
):
261 write_rfreg_hdl(padapter
, (u8
*)pcmd
);
263 case GEN_CMD_CODE(_SetUsbSuspend
):
264 sys_suspend_hdl(padapter
, (u8
*)pcmd
);
266 case GEN_CMD_CODE(_JoinBss
):
267 r8712_joinbss_reset(padapter
);
268 /* Before set JoinBss_CMD to FW, driver must ensure FW is in
269 * PS_MODE_ACTIVE. Directly write rpwm to radio on and assign
270 * new pwr_mode to Driver, instead of use workitem to change
272 if (padapter
->pwrctrlpriv
.pwr_mode
> PS_MODE_ACTIVE
) {
273 padapter
->pwrctrlpriv
.pwr_mode
= PS_MODE_ACTIVE
;
274 _enter_pwrlock(&(padapter
->pwrctrlpriv
.lock
));
275 r8712_set_rpwm(padapter
, PS_STATE_S4
);
276 up(&(padapter
->pwrctrlpriv
.lock
));
281 r871x_internal_cmd_hdl(padapter
, pcmd
->parmbuf
);
282 r8712_free_cmd_obj(pcmd
);
289 return pcmd_r
; /* if returning pcmd_r == NULL, pcmd must be free. */
292 static u8
check_cmd_fifo(struct _adapter
*padapter
, uint sz
)
298 u8
r8712_fw_cmd(struct _adapter
*pAdapter
, u32 cmd
)
300 int pollingcnts
= 50;
302 r8712_write32(pAdapter
, IOCMD_CTRL_REG
, cmd
);
304 while ((0 != r8712_read32(pAdapter
, IOCMD_CTRL_REG
)) &&
309 if (pollingcnts
== 0)
314 void r8712_fw_cmd_data(struct _adapter
*pAdapter
, u32
*value
, u8 flag
)
316 if (flag
== 0) /* set */
317 r8712_write32(pAdapter
, IOCMD_DATA_REG
, *value
);
319 *value
= r8712_read32(pAdapter
, IOCMD_DATA_REG
);
322 int r8712_cmd_thread(void *context
)
324 struct cmd_obj
*pcmd
;
325 unsigned int cmdsz
, wr_sz
, *pcmdbuf
, *prspbuf
;
326 struct tx_desc
*pdesc
;
327 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
328 struct _adapter
*padapter
= (struct _adapter
*)context
;
329 struct cmd_priv
*pcmdpriv
= &(padapter
->cmdpriv
);
331 thread_enter(padapter
);
333 if ((_down_sema(&(pcmdpriv
->cmd_queue_sema
))) == _FAIL
)
335 if ((padapter
->bDriverStopped
== true) ||
336 (padapter
->bSurpriseRemoved
== true))
338 if (r8712_register_cmd_alive(padapter
) != _SUCCESS
)
341 pcmd
= r8712_dequeue_cmd(&(pcmdpriv
->cmd_queue
));
343 r8712_unregister_cmd_alive(padapter
);
346 pcmdbuf
= (unsigned int *)pcmdpriv
->cmd_buf
;
347 prspbuf
= (unsigned int *)pcmdpriv
->rsp_buf
;
348 pdesc
= (struct tx_desc
*)pcmdbuf
;
349 memset(pdesc
, 0, TXDESC_SIZE
);
350 pcmd
= cmd_hdl_filter(padapter
, pcmd
);
351 if (pcmd
) { /* if pcmd != NULL, cmd will be handled by f/w */
352 struct dvobj_priv
*pdvobj
= (struct dvobj_priv
*)
353 &padapter
->dvobjpriv
;
355 pcmdpriv
->cmd_issued_cnt
++;
356 cmdsz
= _RND8((pcmd
->cmdsz
)); /* _RND8 */
357 wr_sz
= TXDESC_SIZE
+ 8 + cmdsz
;
358 pdesc
->txdw0
|= cpu_to_le32((wr_sz
-TXDESC_SIZE
) &
360 if (pdvobj
->ishighspeed
) {
361 if ((wr_sz
% 512) == 0)
364 if ((wr_sz
% 64) == 0)
367 if (blnPending
) /* 32 bytes for TX Desc - 8 offset */
368 pdesc
->txdw0
|= cpu_to_le32(((TXDESC_SIZE
+
369 OFFSET_SZ
+ 8) << OFFSET_SHT
) &
372 pdesc
->txdw0
|= cpu_to_le32(((TXDESC_SIZE
+
377 pdesc
->txdw0
|= cpu_to_le32(OWN
| FSG
| LSG
);
378 pdesc
->txdw1
|= cpu_to_le32((0x13 << QSEL_SHT
) &
380 pcmdbuf
+= (TXDESC_SIZE
>> 2);
381 *pcmdbuf
= cpu_to_le32((cmdsz
& 0x0000ffff) |
382 (pcmd
->cmdcode
<< 16) |
383 (pcmdpriv
->cmd_seq
<< 24));
384 pcmdbuf
+= 2 ; /* 8 bytes alignment */
385 memcpy((u8
*)pcmdbuf
, pcmd
->parmbuf
, pcmd
->cmdsz
);
386 while (check_cmd_fifo(padapter
, wr_sz
) == _FAIL
) {
387 if ((padapter
->bDriverStopped
== true) ||
388 (padapter
->bSurpriseRemoved
== true))
394 wr_sz
+= 8; /* Append 8 bytes */
395 r8712_write_mem(padapter
, RTL8712_DMA_H2CCMD
, wr_sz
,
398 if (pcmd
->cmdcode
== GEN_CMD_CODE(_CreateBss
)) {
399 pcmd
->res
= H2C_SUCCESS
;
400 pcmd_callback
= cmd_callback
[pcmd
->
403 pcmd_callback(padapter
, pcmd
);
406 if (pcmd
->cmdcode
== GEN_CMD_CODE(_SetPwrMode
)) {
407 if (padapter
->pwrctrlpriv
.bSleep
) {
408 _enter_pwrlock(&(padapter
->
410 r8712_set_rpwm(padapter
, PS_STATE_S2
);
411 up(&padapter
->pwrctrlpriv
.lock
);
414 r8712_free_cmd_obj(pcmd
);
415 if (_queue_empty(&(pcmdpriv
->cmd_queue
))) {
416 r8712_unregister_cmd_alive(padapter
);
422 flush_signals_thread();
424 /* free all cmd_obj resources */
426 pcmd
= r8712_dequeue_cmd(&(pcmdpriv
->cmd_queue
));
429 r8712_free_cmd_obj(pcmd
);
431 up(&pcmdpriv
->terminate_cmdthread_sema
);
435 void r8712_event_handle(struct _adapter
*padapter
, uint
*peventbuf
)
437 u8 evt_code
, evt_seq
;
439 void (*event_callback
)(struct _adapter
*dev
, u8
*pbuf
);
440 struct evt_priv
*pevt_priv
= &(padapter
->evtpriv
);
442 if (peventbuf
== NULL
)
444 evt_sz
= (u16
)(le32_to_cpu(*peventbuf
) & 0xffff);
445 evt_seq
= (u8
)((le32_to_cpu(*peventbuf
) >> 24) & 0x7f);
446 evt_code
= (u8
)((le32_to_cpu(*peventbuf
) >> 16) & 0xff);
447 /* checking event sequence... */
448 if ((evt_seq
& 0x7f) != pevt_priv
->event_seq
) {
449 pevt_priv
->event_seq
= ((evt_seq
+ 1) & 0x7f);
452 /* checking if event code is valid */
453 if (evt_code
>= MAX_C2HEVT
) {
454 pevt_priv
->event_seq
= ((evt_seq
+1) & 0x7f);
456 } else if ((evt_code
== GEN_EVT_CODE(_Survey
)) &&
457 (evt_sz
> sizeof(struct wlan_bssid_ex
))) {
458 pevt_priv
->event_seq
= ((evt_seq
+1)&0x7f);
461 /* checking if event size match the event parm size */
462 if ((wlanevents
[evt_code
].parmsize
) &&
463 (wlanevents
[evt_code
].parmsize
!= evt_sz
)) {
464 pevt_priv
->event_seq
= ((evt_seq
+1)&0x7f);
466 } else if ((evt_sz
== 0) && (evt_code
!= GEN_EVT_CODE(_WPS_PBC
))) {
467 pevt_priv
->event_seq
= ((evt_seq
+1)&0x7f);
470 pevt_priv
->event_seq
++; /* update evt_seq */
471 if (pevt_priv
->event_seq
> 127)
472 pevt_priv
->event_seq
= 0;
473 peventbuf
= peventbuf
+ 2; /* move to event content, 8 bytes alignment */
475 event_callback
= wlanevents
[evt_code
].event_callback
;
477 event_callback(padapter
, (u8
*)peventbuf
);
479 pevt_priv
->evt_done_cnt
++;