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