1/****************************************************************************** 2 * rtl8712_cmd.c 3 * 4 * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. 5 * Linux device driver for RTL8192SU 6 * 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. 10 * 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 14 * more details. 15 * 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 19 * 20 * Modifications for inclusion into the Linux staging tree are 21 * Copyright(c) 2010 Larry Finger. All rights reserved. 22 * 23 * Contact information: 24 * WLAN FAE <wlanfae@realtek.com>. 25 * Larry Finger <Larry.Finger@lwfinger.net> 26 * 27 ******************************************************************************/ 28 29#define _RTL8712_CMD_C_ 30 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> 47 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" 53 54static void check_hw_pbc(struct _adapter *padapter) 55{ 56 u8 tmp1byte; 57 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); 63 if (tmp1byte == 0xff) 64 return; 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) 72 return; 73 kill_pid(find_vpid(padapter->pid), SIGUSR1, 1); 74 } 75} 76 77/* query rx phy status from fw. 78 * Adhoc mode: beacon. 79 * Infrastructure mode: beacon , data. */ 80static void query_fw_rx_phy_status(struct _adapter *padapter) 81{ 82 u32 val32 = 0; 83 int pollingcnts = 50; 84 85 if (check_fwstate(&padapter->mlmepriv, _FW_LINKED) == true) { 86 r8712_write32(padapter, IOCMD_CTRL_REG, 0xf4000001); 87 msleep(100); 88 /* Wait FW complete IO Cmd */ 89 while ((r8712_read32(padapter, IOCMD_CTRL_REG)) && 90 (pollingcnts > 0)) { 91 pollingcnts--; 92 msleep(20); 93 } 94 if (pollingcnts != 0) 95 val32 = r8712_read32(padapter, IOCMD_DATA_REG); 96 else /* time out */ 97 val32 = 0; 98 val32 = val32 >> 4; 99 padapter->recvpriv.fw_rssi = 100 (u8)r8712_signal_scale_mapping(val32); 101 } 102} 103 104/* check mlme, hw, phy, or dynamic algorithm status. */ 105static void StatusWatchdogCallback(struct _adapter *padapter) 106{ 107 check_hw_pbc(padapter); 108 query_fw_rx_phy_status(padapter); 109} 110 111static void r871x_internal_cmd_hdl(struct _adapter *padapter, u8 *pbuf) 112{ 113 struct drvint_cmd_parm *pdrvcmd; 114 115 if (!pbuf) 116 return; 117 pdrvcmd = (struct drvint_cmd_parm *)pbuf; 118 switch (pdrvcmd->i_cid) { 119 case WDG_WK_CID: 120 StatusWatchdogCallback(padapter); 121 break; 122 default: 123 break; 124 } 125 kfree(pdrvcmd->pbuf); 126} 127 128static u8 read_macreg_hdl(struct _adapter *padapter, u8 *pbuf) 129{ 130 void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd); 131 struct cmd_obj *pcmd = (struct cmd_obj *)pbuf; 132 133 /* invoke cmd->callback function */ 134 pcmd_callback = cmd_callback[pcmd->cmdcode].callback; 135 if (pcmd_callback == NULL) 136 r8712_free_cmd_obj(pcmd); 137 else 138 pcmd_callback(padapter, pcmd); 139 return H2C_SUCCESS; 140} 141 142static u8 write_macreg_hdl(struct _adapter *padapter, u8 *pbuf) 143{ 144 void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd); 145 struct cmd_obj *pcmd = (struct cmd_obj *)pbuf; 146 147 /* invoke cmd->callback function */ 148 pcmd_callback = cmd_callback[pcmd->cmdcode].callback; 149 if (pcmd_callback == NULL) 150 r8712_free_cmd_obj(pcmd); 151 else 152 pcmd_callback(padapter, pcmd); 153 return H2C_SUCCESS; 154} 155 156static u8 read_bbreg_hdl(struct _adapter *padapter, u8 *pbuf) 157{ 158 u32 val; 159 void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd); 160 struct readBB_parm *prdbbparm; 161 struct cmd_obj *pcmd = (struct cmd_obj *)pbuf; 162 163 prdbbparm = (struct readBB_parm *)pcmd->parmbuf; 164 if (pcmd->rsp && pcmd->rspsz > 0) 165 memcpy(pcmd->rsp, (u8 *)&val, pcmd->rspsz); 166 pcmd_callback = cmd_callback[pcmd->cmdcode].callback; 167 if (pcmd_callback == NULL) 168 r8712_free_cmd_obj(pcmd); 169 else 170 pcmd_callback(padapter, pcmd); 171 return H2C_SUCCESS; 172} 173 174static u8 write_bbreg_hdl(struct _adapter *padapter, u8 *pbuf) 175{ 176 void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd); 177 struct writeBB_parm *pwritebbparm; 178 struct cmd_obj *pcmd = (struct cmd_obj *)pbuf; 179 180 pwritebbparm = (struct writeBB_parm *)pcmd->parmbuf; 181 pcmd_callback = cmd_callback[pcmd->cmdcode].callback; 182 if (pcmd_callback == NULL) 183 r8712_free_cmd_obj(pcmd); 184 else 185 pcmd_callback(padapter, pcmd); 186 return H2C_SUCCESS; 187} 188 189static u8 read_rfreg_hdl(struct _adapter *padapter, u8 *pbuf) 190{ 191 u32 val; 192 void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd); 193 struct readRF_parm *prdrfparm; 194 struct cmd_obj *pcmd = (struct cmd_obj *)pbuf; 195 196 prdrfparm = (struct readRF_parm *)pcmd->parmbuf; 197 if (pcmd->rsp && pcmd->rspsz > 0) 198 memcpy(pcmd->rsp, (u8 *)&val, pcmd->rspsz); 199 pcmd_callback = cmd_callback[pcmd->cmdcode].callback; 200 if (pcmd_callback == NULL) 201 r8712_free_cmd_obj(pcmd); 202 else 203 pcmd_callback(padapter, pcmd); 204 return H2C_SUCCESS; 205} 206 207static u8 write_rfreg_hdl(struct _adapter *padapter, u8 *pbuf) 208{ 209 void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd); 210 struct writeRF_parm *pwriterfparm; 211 struct cmd_obj *pcmd = (struct cmd_obj *)pbuf; 212 213 pwriterfparm = (struct writeRF_parm *)pcmd->parmbuf; 214 pcmd_callback = cmd_callback[pcmd->cmdcode].callback; 215 if (pcmd_callback == NULL) 216 r8712_free_cmd_obj(pcmd); 217 else 218 pcmd_callback(padapter, pcmd); 219 return H2C_SUCCESS; 220} 221 222static u8 sys_suspend_hdl(struct _adapter *padapter, u8 *pbuf) 223{ 224 struct cmd_obj *pcmd = (struct cmd_obj *)pbuf; 225 struct usb_suspend_parm *psetusbsuspend; 226 227 psetusbsuspend = (struct usb_suspend_parm *)pcmd->parmbuf; 228 r8712_free_cmd_obj(pcmd); 229 return H2C_SUCCESS; 230} 231 232static struct cmd_obj *cmd_hdl_filter(struct _adapter *padapter, 233 struct cmd_obj *pcmd) 234{ 235 struct cmd_obj *pcmd_r; 236 237 if (pcmd == NULL) 238 return pcmd; 239 pcmd_r = NULL; 240 241 switch (pcmd->cmdcode) { 242 case GEN_CMD_CODE(_Read_MACREG): 243 read_macreg_hdl(padapter, (u8 *)pcmd); 244 pcmd_r = pcmd; 245 break; 246 case GEN_CMD_CODE(_Write_MACREG): 247 write_macreg_hdl(padapter, (u8 *)pcmd); 248 pcmd_r = pcmd; 249 break; 250 case GEN_CMD_CODE(_Read_BBREG): 251 read_bbreg_hdl(padapter, (u8 *)pcmd); 252 break; 253 case GEN_CMD_CODE(_Write_BBREG): 254 write_bbreg_hdl(padapter, (u8 *)pcmd); 255 break; 256 case GEN_CMD_CODE(_Read_RFREG): 257 read_rfreg_hdl(padapter, (u8 *)pcmd); 258 break; 259 case GEN_CMD_CODE(_Write_RFREG): 260 write_rfreg_hdl(padapter, (u8 *)pcmd); 261 break; 262 case GEN_CMD_CODE(_SetUsbSuspend): 263 sys_suspend_hdl(padapter, (u8 *)pcmd); 264 break; 265 case GEN_CMD_CODE(_JoinBss): 266 r8712_joinbss_reset(padapter); 267 /* Before set JoinBss_CMD to FW, driver must ensure FW is in 268 * PS_MODE_ACTIVE. Directly write rpwm to radio on and assign 269 * new pwr_mode to Driver, instead of use workitem to change 270 * state. */ 271 if (padapter->pwrctrlpriv.pwr_mode > PS_MODE_ACTIVE) { 272 padapter->pwrctrlpriv.pwr_mode = PS_MODE_ACTIVE; 273 _enter_pwrlock(&(padapter->pwrctrlpriv.lock)); 274 r8712_set_rpwm(padapter, PS_STATE_S4); 275 up(&(padapter->pwrctrlpriv.lock)); 276 } 277 pcmd_r = pcmd; 278 break; 279 case _DRV_INT_CMD_: 280 r871x_internal_cmd_hdl(padapter, pcmd->parmbuf); 281 r8712_free_cmd_obj(pcmd); 282 pcmd_r = NULL; 283 break; 284 default: 285 pcmd_r = pcmd; 286 break; 287 } 288 return pcmd_r; /* if returning pcmd_r == NULL, pcmd must be free. */ 289} 290 291static u8 check_cmd_fifo(struct _adapter *padapter, uint sz) 292{ 293 return _SUCCESS; 294} 295 296u8 r8712_fw_cmd(struct _adapter *pAdapter, u32 cmd) 297{ 298 int pollingcnts = 50; 299 300 r8712_write32(pAdapter, IOCMD_CTRL_REG, cmd); 301 msleep(100); 302 while ((0 != r8712_read32(pAdapter, IOCMD_CTRL_REG)) && 303 (pollingcnts > 0)) { 304 pollingcnts--; 305 msleep(20); 306 } 307 if (pollingcnts == 0) 308 return false; 309 return true; 310} 311 312void r8712_fw_cmd_data(struct _adapter *pAdapter, u32 *value, u8 flag) 313{ 314 if (flag == 0) /* set */ 315 r8712_write32(pAdapter, IOCMD_DATA_REG, *value); 316 else /* query */ 317 *value = r8712_read32(pAdapter, IOCMD_DATA_REG); 318} 319 320int r8712_cmd_thread(void *context) 321{ 322 struct cmd_obj *pcmd; 323 unsigned int cmdsz, wr_sz, *pcmdbuf, *prspbuf; 324 struct tx_desc *pdesc; 325 void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd); 326 struct _adapter *padapter = (struct _adapter *)context; 327 struct cmd_priv *pcmdpriv = &(padapter->cmdpriv); 328 329 allow_signal(SIGTERM); 330 while (1) { 331 if ((_down_sema(&(pcmdpriv->cmd_queue_sema))) == _FAIL) 332 break; 333 if ((padapter->bDriverStopped == true) || 334 (padapter->bSurpriseRemoved == true)) 335 break; 336 if (r8712_register_cmd_alive(padapter) != _SUCCESS) 337 continue; 338_next: 339 pcmd = r8712_dequeue_cmd(&(pcmdpriv->cmd_queue)); 340 if (!(pcmd)) { 341 r8712_unregister_cmd_alive(padapter); 342 continue; 343 } 344 pcmdbuf = (unsigned int *)pcmdpriv->cmd_buf; 345 prspbuf = (unsigned int *)pcmdpriv->rsp_buf; 346 pdesc = (struct tx_desc *)pcmdbuf; 347 memset(pdesc, 0, TXDESC_SIZE); 348 pcmd = cmd_hdl_filter(padapter, pcmd); 349 if (pcmd) { /* if pcmd != NULL, cmd will be handled by f/w */ 350 struct dvobj_priv *pdvobj = (struct dvobj_priv *) 351 &padapter->dvobjpriv; 352 u8 blnPending = 0; 353 354 pcmdpriv->cmd_issued_cnt++; 355 cmdsz = round_up(pcmd->cmdsz, 8); 356 wr_sz = TXDESC_SIZE + 8 + cmdsz; 357 pdesc->txdw0 |= cpu_to_le32((wr_sz-TXDESC_SIZE) & 358 0x0000ffff); 359 if (pdvobj->ishighspeed) { 360 if ((wr_sz % 512) == 0) 361 blnPending = 1; 362 } else { 363 if ((wr_sz % 64) == 0) 364 blnPending = 1; 365 } 366 if (blnPending) /* 32 bytes for TX Desc - 8 offset */ 367 pdesc->txdw0 |= cpu_to_le32(((TXDESC_SIZE + 368 OFFSET_SZ + 8) << OFFSET_SHT) & 369 0x00ff0000); 370 else { 371 pdesc->txdw0 |= cpu_to_le32(((TXDESC_SIZE + 372 OFFSET_SZ) << 373 OFFSET_SHT) & 374 0x00ff0000); 375 } 376 pdesc->txdw0 |= cpu_to_le32(OWN | FSG | LSG); 377 pdesc->txdw1 |= cpu_to_le32((0x13 << QSEL_SHT) & 378 0x00001f00); 379 pcmdbuf += (TXDESC_SIZE >> 2); 380 *pcmdbuf = cpu_to_le32((cmdsz & 0x0000ffff) | 381 (pcmd->cmdcode << 16) | 382 (pcmdpriv->cmd_seq << 24)); 383 pcmdbuf += 2; /* 8 bytes alignment */ 384 memcpy((u8 *)pcmdbuf, pcmd->parmbuf, pcmd->cmdsz); 385 while (check_cmd_fifo(padapter, wr_sz) == _FAIL) { 386 if ((padapter->bDriverStopped == true) || 387 (padapter->bSurpriseRemoved == true)) 388 break; 389 msleep(100); 390 continue; 391 } 392 if (blnPending) 393 wr_sz += 8; /* Append 8 bytes */ 394 r8712_write_mem(padapter, RTL8712_DMA_H2CCMD, wr_sz, 395 (u8 *)pdesc); 396 pcmdpriv->cmd_seq++; 397 if (pcmd->cmdcode == GEN_CMD_CODE(_CreateBss)) { 398 pcmd->res = H2C_SUCCESS; 399 pcmd_callback = cmd_callback[pcmd-> 400 cmdcode].callback; 401 if (pcmd_callback) 402 pcmd_callback(padapter, pcmd); 403 continue; 404 } 405 if (pcmd->cmdcode == GEN_CMD_CODE(_SetPwrMode)) { 406 if (padapter->pwrctrlpriv.bSleep) { 407 _enter_pwrlock(&(padapter-> 408 pwrctrlpriv.lock)); 409 r8712_set_rpwm(padapter, PS_STATE_S2); 410 up(&padapter->pwrctrlpriv.lock); 411 } 412 } 413 r8712_free_cmd_obj(pcmd); 414 if (list_empty(&pcmdpriv->cmd_queue.queue)) { 415 r8712_unregister_cmd_alive(padapter); 416 continue; 417 } else 418 goto _next; 419 } else 420 goto _next; 421 flush_signals_thread(); 422 } 423 /* free all cmd_obj resources */ 424 do { 425 pcmd = r8712_dequeue_cmd(&(pcmdpriv->cmd_queue)); 426 if (pcmd == NULL) 427 break; 428 r8712_free_cmd_obj(pcmd); 429 } while (1); 430 up(&pcmdpriv->terminate_cmdthread_sema); 431 thread_exit(); 432} 433 434void r8712_event_handle(struct _adapter *padapter, uint *peventbuf) 435{ 436 u8 evt_code, evt_seq; 437 u16 evt_sz; 438 void (*event_callback)(struct _adapter *dev, u8 *pbuf); 439 struct evt_priv *pevt_priv = &(padapter->evtpriv); 440 441 if (peventbuf == NULL) 442 goto _abort_event_; 443 evt_sz = (u16)(le32_to_cpu(*peventbuf) & 0xffff); 444 evt_seq = (u8)((le32_to_cpu(*peventbuf) >> 24) & 0x7f); 445 evt_code = (u8)((le32_to_cpu(*peventbuf) >> 16) & 0xff); 446 /* checking event sequence... */ 447 if ((evt_seq & 0x7f) != pevt_priv->event_seq) { 448 pevt_priv->event_seq = ((evt_seq + 1) & 0x7f); 449 goto _abort_event_; 450 } 451 /* checking if event code is valid */ 452 if (evt_code >= MAX_C2HEVT) { 453 pevt_priv->event_seq = ((evt_seq+1) & 0x7f); 454 goto _abort_event_; 455 } else if ((evt_code == GEN_EVT_CODE(_Survey)) && 456 (evt_sz > sizeof(struct wlan_bssid_ex))) { 457 pevt_priv->event_seq = ((evt_seq+1)&0x7f); 458 goto _abort_event_; 459 } 460 /* checking if event size match the event parm size */ 461 if ((wlanevents[evt_code].parmsize) && 462 (wlanevents[evt_code].parmsize != evt_sz)) { 463 pevt_priv->event_seq = ((evt_seq+1)&0x7f); 464 goto _abort_event_; 465 } else if ((evt_sz == 0) && (evt_code != GEN_EVT_CODE(_WPS_PBC))) { 466 pevt_priv->event_seq = ((evt_seq+1)&0x7f); 467 goto _abort_event_; 468 } 469 pevt_priv->event_seq++; /* update evt_seq */ 470 if (pevt_priv->event_seq > 127) 471 pevt_priv->event_seq = 0; 472 /* move to event content, 8 bytes alignment */ 473 peventbuf = peventbuf + 2; 474 event_callback = wlanevents[evt_code].event_callback; 475 if (event_callback) 476 event_callback(padapter, (u8 *)peventbuf); 477 pevt_priv->evt_done_cnt++; 478_abort_event_: 479 return; 480} 481