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/slab.h>
35 #include <linux/module.h>
36 #include <linux/kref.h>
37 #include <linux/netdevice.h>
38 #include <linux/skbuff.h>
39 #include <linux/usb.h>
40 #include <linux/usb/ch9.h>
41 #include <linux/circ_buf.h>
42 #include <linux/uaccess.h>
43 #include <asm/byteorder.h>
44 #include <linux/atomic.h>
45 #include <linux/semaphore.h>
46 #include <linux/rtnetlink.h>
48 #include "osdep_service.h"
49 #include "drv_types.h"
50 #include "recv_osdep.h"
51 #include "mlme_osdep.h"
52 #include "rtl871x_ioctl_set.h"
54 static void check_hw_pbc(struct _adapter
*padapter
)
58 r8712_write8(padapter
, MAC_PINMUX_CTRL
, (GPIOMUX_EN
| GPIOSEL_GPIO
));
59 tmp1byte
= r8712_read8(padapter
, GPIO_IO_SEL
);
60 tmp1byte
&= ~(HAL_8192S_HW_GPIO_WPS_BIT
);
61 r8712_write8(padapter
, GPIO_IO_SEL
, tmp1byte
);
62 tmp1byte
= r8712_read8(padapter
, GPIO_CTRL
);
65 if (tmp1byte
&HAL_8192S_HW_GPIO_WPS_BIT
) {
66 /* Here we only set bPbcPressed to true
67 * After trigger PBC, the variable will be set to false */
68 DBG_8712("CheckPbcGPIO - PBC is pressed !!!!\n");
69 /* 0 is the default value and it means the application monitors
70 * the HW PBC doesn't provide its pid to driver. */
71 if (padapter
->pid
== 0)
73 kill_pid(find_vpid(padapter
->pid
), SIGUSR1
, 1);
77 /* query rx phy status from fw.
79 * Infrastructure mode: beacon , data. */
80 static void query_fw_rx_phy_status(struct _adapter
*padapter
)
85 if (check_fwstate(&padapter
->mlmepriv
, _FW_LINKED
) == true) {
86 r8712_write32(padapter
, IOCMD_CTRL_REG
, 0xf4000001);
88 /* Wait FW complete IO Cmd */
89 while ((r8712_read32(padapter
, IOCMD_CTRL_REG
)) &&
95 val32
= r8712_read32(padapter
, IOCMD_DATA_REG
);
99 padapter
->recvpriv
.fw_rssi
=
100 (u8
)r8712_signal_scale_mapping(val32
);
104 /* check mlme, hw, phy, or dynamic algorithm status. */
105 static void StatusWatchdogCallback(struct _adapter
*padapter
)
107 check_hw_pbc(padapter
);
108 query_fw_rx_phy_status(padapter
);
111 static void r871x_internal_cmd_hdl(struct _adapter
*padapter
, u8
*pbuf
)
113 struct drvint_cmd_parm
*pdrvcmd
;
117 pdrvcmd
= (struct drvint_cmd_parm
*)pbuf
;
118 switch (pdrvcmd
->i_cid
) {
120 StatusWatchdogCallback(padapter
);
125 kfree(pdrvcmd
->pbuf
);
128 static u8
read_macreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
130 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
131 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
133 /* invoke cmd->callback function */
134 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
135 if (pcmd_callback
== NULL
)
136 r8712_free_cmd_obj(pcmd
);
138 pcmd_callback(padapter
, pcmd
);
142 static u8
write_macreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
144 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
145 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
147 /* invoke cmd->callback function */
148 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
149 if (pcmd_callback
== NULL
)
150 r8712_free_cmd_obj(pcmd
);
152 pcmd_callback(padapter
, pcmd
);
156 static u8
read_bbreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
159 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
160 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
162 if (pcmd
->rsp
&& pcmd
->rspsz
> 0)
163 memcpy(pcmd
->rsp
, (u8
*)&val
, pcmd
->rspsz
);
164 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
165 if (pcmd_callback
== NULL
)
166 r8712_free_cmd_obj(pcmd
);
168 pcmd_callback(padapter
, pcmd
);
172 static u8
write_bbreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
174 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
175 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
177 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
178 if (pcmd_callback
== NULL
)
179 r8712_free_cmd_obj(pcmd
);
181 pcmd_callback(padapter
, pcmd
);
185 static u8
read_rfreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
188 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
189 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
191 if (pcmd
->rsp
&& pcmd
->rspsz
> 0)
192 memcpy(pcmd
->rsp
, (u8
*)&val
, pcmd
->rspsz
);
193 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
194 if (pcmd_callback
== NULL
)
195 r8712_free_cmd_obj(pcmd
);
197 pcmd_callback(padapter
, pcmd
);
201 static u8
write_rfreg_hdl(struct _adapter
*padapter
, u8
*pbuf
)
203 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
204 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
206 pcmd_callback
= cmd_callback
[pcmd
->cmdcode
].callback
;
207 if (pcmd_callback
== NULL
)
208 r8712_free_cmd_obj(pcmd
);
210 pcmd_callback(padapter
, pcmd
);
214 static u8
sys_suspend_hdl(struct _adapter
*padapter
, u8
*pbuf
)
216 struct cmd_obj
*pcmd
= (struct cmd_obj
*)pbuf
;
218 r8712_free_cmd_obj(pcmd
);
222 static struct cmd_obj
*cmd_hdl_filter(struct _adapter
*padapter
,
223 struct cmd_obj
*pcmd
)
225 struct cmd_obj
*pcmd_r
;
231 switch (pcmd
->cmdcode
) {
232 case GEN_CMD_CODE(_Read_MACREG
):
233 read_macreg_hdl(padapter
, (u8
*)pcmd
);
236 case GEN_CMD_CODE(_Write_MACREG
):
237 write_macreg_hdl(padapter
, (u8
*)pcmd
);
240 case GEN_CMD_CODE(_Read_BBREG
):
241 read_bbreg_hdl(padapter
, (u8
*)pcmd
);
243 case GEN_CMD_CODE(_Write_BBREG
):
244 write_bbreg_hdl(padapter
, (u8
*)pcmd
);
246 case GEN_CMD_CODE(_Read_RFREG
):
247 read_rfreg_hdl(padapter
, (u8
*)pcmd
);
249 case GEN_CMD_CODE(_Write_RFREG
):
250 write_rfreg_hdl(padapter
, (u8
*)pcmd
);
252 case GEN_CMD_CODE(_SetUsbSuspend
):
253 sys_suspend_hdl(padapter
, (u8
*)pcmd
);
255 case GEN_CMD_CODE(_JoinBss
):
256 r8712_joinbss_reset(padapter
);
257 /* Before set JoinBss_CMD to FW, driver must ensure FW is in
258 * PS_MODE_ACTIVE. Directly write rpwm to radio on and assign
259 * new pwr_mode to Driver, instead of use workitem to change
261 if (padapter
->pwrctrlpriv
.pwr_mode
> PS_MODE_ACTIVE
) {
262 padapter
->pwrctrlpriv
.pwr_mode
= PS_MODE_ACTIVE
;
263 _enter_pwrlock(&(padapter
->pwrctrlpriv
.lock
));
264 r8712_set_rpwm(padapter
, PS_STATE_S4
);
265 up(&(padapter
->pwrctrlpriv
.lock
));
270 r871x_internal_cmd_hdl(padapter
, pcmd
->parmbuf
);
271 r8712_free_cmd_obj(pcmd
);
278 return pcmd_r
; /* if returning pcmd_r == NULL, pcmd must be free. */
281 static u8
check_cmd_fifo(struct _adapter
*padapter
, uint sz
)
286 u8
r8712_fw_cmd(struct _adapter
*pAdapter
, u32 cmd
)
288 int pollingcnts
= 50;
290 r8712_write32(pAdapter
, IOCMD_CTRL_REG
, cmd
);
292 while ((0 != r8712_read32(pAdapter
, IOCMD_CTRL_REG
)) &&
297 if (pollingcnts
== 0)
302 void r8712_fw_cmd_data(struct _adapter
*pAdapter
, u32
*value
, u8 flag
)
304 if (flag
== 0) /* set */
305 r8712_write32(pAdapter
, IOCMD_DATA_REG
, *value
);
307 *value
= r8712_read32(pAdapter
, IOCMD_DATA_REG
);
310 int r8712_cmd_thread(void *context
)
312 struct cmd_obj
*pcmd
;
313 unsigned int cmdsz
, wr_sz
, *pcmdbuf
;
314 struct tx_desc
*pdesc
;
315 void (*pcmd_callback
)(struct _adapter
*dev
, struct cmd_obj
*pcmd
);
316 struct _adapter
*padapter
= (struct _adapter
*)context
;
317 struct cmd_priv
*pcmdpriv
= &(padapter
->cmdpriv
);
319 allow_signal(SIGTERM
);
321 if ((_down_sema(&(pcmdpriv
->cmd_queue_sema
))) == _FAIL
)
323 if ((padapter
->bDriverStopped
== true) ||
324 (padapter
->bSurpriseRemoved
== true))
326 if (r8712_register_cmd_alive(padapter
) != _SUCCESS
)
329 pcmd
= r8712_dequeue_cmd(&(pcmdpriv
->cmd_queue
));
331 r8712_unregister_cmd_alive(padapter
);
334 pcmdbuf
= (unsigned int *)pcmdpriv
->cmd_buf
;
335 pdesc
= (struct tx_desc
*)pcmdbuf
;
336 memset(pdesc
, 0, TXDESC_SIZE
);
337 pcmd
= cmd_hdl_filter(padapter
, pcmd
);
338 if (pcmd
) { /* if pcmd != NULL, cmd will be handled by f/w */
339 struct dvobj_priv
*pdvobj
= (struct dvobj_priv
*)
340 &padapter
->dvobjpriv
;
343 pcmdpriv
->cmd_issued_cnt
++;
344 cmdsz
= round_up(pcmd
->cmdsz
, 8);
345 wr_sz
= TXDESC_SIZE
+ 8 + cmdsz
;
346 pdesc
->txdw0
|= cpu_to_le32((wr_sz
-TXDESC_SIZE
) &
348 if (pdvobj
->ishighspeed
) {
349 if ((wr_sz
% 512) == 0)
352 if ((wr_sz
% 64) == 0)
355 if (blnPending
) /* 32 bytes for TX Desc - 8 offset */
356 pdesc
->txdw0
|= cpu_to_le32(((TXDESC_SIZE
+
357 OFFSET_SZ
+ 8) << OFFSET_SHT
) &
360 pdesc
->txdw0
|= cpu_to_le32(((TXDESC_SIZE
+
365 pdesc
->txdw0
|= cpu_to_le32(OWN
| FSG
| LSG
);
366 pdesc
->txdw1
|= cpu_to_le32((0x13 << QSEL_SHT
) &
368 pcmdbuf
+= (TXDESC_SIZE
>> 2);
369 *pcmdbuf
= cpu_to_le32((cmdsz
& 0x0000ffff) |
370 (pcmd
->cmdcode
<< 16) |
371 (pcmdpriv
->cmd_seq
<< 24));
372 pcmdbuf
+= 2; /* 8 bytes alignment */
373 memcpy((u8
*)pcmdbuf
, pcmd
->parmbuf
, pcmd
->cmdsz
);
374 while (check_cmd_fifo(padapter
, wr_sz
) == _FAIL
) {
375 if ((padapter
->bDriverStopped
== true) ||
376 (padapter
->bSurpriseRemoved
== true))
382 wr_sz
+= 8; /* Append 8 bytes */
383 r8712_write_mem(padapter
, RTL8712_DMA_H2CCMD
, wr_sz
,
386 if (pcmd
->cmdcode
== GEN_CMD_CODE(_CreateBss
)) {
387 pcmd
->res
= H2C_SUCCESS
;
388 pcmd_callback
= cmd_callback
[pcmd
->
391 pcmd_callback(padapter
, pcmd
);
394 if (pcmd
->cmdcode
== GEN_CMD_CODE(_SetPwrMode
)) {
395 if (padapter
->pwrctrlpriv
.bSleep
) {
396 _enter_pwrlock(&(padapter
->
398 r8712_set_rpwm(padapter
, PS_STATE_S2
);
399 up(&padapter
->pwrctrlpriv
.lock
);
402 r8712_free_cmd_obj(pcmd
);
403 if (list_empty(&pcmdpriv
->cmd_queue
.queue
)) {
404 r8712_unregister_cmd_alive(padapter
);
410 flush_signals_thread();
412 /* free all cmd_obj resources */
414 pcmd
= r8712_dequeue_cmd(&(pcmdpriv
->cmd_queue
));
417 r8712_free_cmd_obj(pcmd
);
419 up(&pcmdpriv
->terminate_cmdthread_sema
);
423 void r8712_event_handle(struct _adapter
*padapter
, uint
*peventbuf
)
425 u8 evt_code
, evt_seq
;
427 void (*event_callback
)(struct _adapter
*dev
, u8
*pbuf
);
428 struct evt_priv
*pevt_priv
= &(padapter
->evtpriv
);
430 if (peventbuf
== NULL
)
432 evt_sz
= (u16
)(le32_to_cpu(*peventbuf
) & 0xffff);
433 evt_seq
= (u8
)((le32_to_cpu(*peventbuf
) >> 24) & 0x7f);
434 evt_code
= (u8
)((le32_to_cpu(*peventbuf
) >> 16) & 0xff);
435 /* checking event sequence... */
436 if ((evt_seq
& 0x7f) != pevt_priv
->event_seq
) {
437 pevt_priv
->event_seq
= ((evt_seq
+ 1) & 0x7f);
440 /* checking if event code is valid */
441 if (evt_code
>= MAX_C2HEVT
) {
442 pevt_priv
->event_seq
= ((evt_seq
+1) & 0x7f);
444 } else if ((evt_code
== GEN_EVT_CODE(_Survey
)) &&
445 (evt_sz
> sizeof(struct wlan_bssid_ex
))) {
446 pevt_priv
->event_seq
= ((evt_seq
+1)&0x7f);
449 /* checking if event size match the event parm size */
450 if ((wlanevents
[evt_code
].parmsize
) &&
451 (wlanevents
[evt_code
].parmsize
!= evt_sz
)) {
452 pevt_priv
->event_seq
= ((evt_seq
+1)&0x7f);
454 } else if ((evt_sz
== 0) && (evt_code
!= GEN_EVT_CODE(_WPS_PBC
))) {
455 pevt_priv
->event_seq
= ((evt_seq
+1)&0x7f);
458 pevt_priv
->event_seq
++; /* update evt_seq */
459 if (pevt_priv
->event_seq
> 127)
460 pevt_priv
->event_seq
= 0;
461 /* move to event content, 8 bytes alignment */
462 peventbuf
= peventbuf
+ 2;
463 event_callback
= wlanevents
[evt_code
].event_callback
;
465 event_callback(padapter
, (u8
*)peventbuf
);
466 pevt_priv
->evt_done_cnt
++;