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 >>= 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 cmd_obj *pcmd = (struct cmd_obj *)pbuf; 161 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); 167 else 168 pcmd_callback(padapter, pcmd); 169 return H2C_SUCCESS; 170} 171 172static u8 write_bbreg_hdl(struct _adapter *padapter, u8 *pbuf) 173{ 174 void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd); 175 struct cmd_obj *pcmd = (struct cmd_obj *)pbuf; 176 177 pcmd_callback = cmd_callback[pcmd->cmdcode].callback; 178 if (pcmd_callback == NULL) 179 r8712_free_cmd_obj(pcmd); 180 else 181 pcmd_callback(padapter, pcmd); 182 return H2C_SUCCESS; 183} 184 185static u8 read_rfreg_hdl(struct _adapter *padapter, u8 *pbuf) 186{ 187 u32 val; 188 void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd); 189 struct cmd_obj *pcmd = (struct cmd_obj *)pbuf; 190 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); 196 else 197 pcmd_callback(padapter, pcmd); 198 return H2C_SUCCESS; 199} 200 201static u8 write_rfreg_hdl(struct _adapter *padapter, u8 *pbuf) 202{ 203 void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd); 204 struct cmd_obj *pcmd = (struct cmd_obj *)pbuf; 205 206 pcmd_callback = cmd_callback[pcmd->cmdcode].callback; 207 if (pcmd_callback == NULL) 208 r8712_free_cmd_obj(pcmd); 209 else 210 pcmd_callback(padapter, pcmd); 211 return H2C_SUCCESS; 212} 213 214static u8 sys_suspend_hdl(struct _adapter *padapter, u8 *pbuf) 215{ 216 struct cmd_obj *pcmd = (struct cmd_obj *)pbuf; 217 218 r8712_free_cmd_obj(pcmd); 219 return H2C_SUCCESS; 220} 221 222static struct cmd_obj *cmd_hdl_filter(struct _adapter *padapter, 223 struct cmd_obj *pcmd) 224{ 225 struct cmd_obj *pcmd_r; 226 227 if (pcmd == NULL) 228 return pcmd; 229 pcmd_r = NULL; 230 231 switch (pcmd->cmdcode) { 232 case GEN_CMD_CODE(_Read_MACREG): 233 read_macreg_hdl(padapter, (u8 *)pcmd); 234 pcmd_r = pcmd; 235 break; 236 case GEN_CMD_CODE(_Write_MACREG): 237 write_macreg_hdl(padapter, (u8 *)pcmd); 238 pcmd_r = pcmd; 239 break; 240 case GEN_CMD_CODE(_Read_BBREG): 241 read_bbreg_hdl(padapter, (u8 *)pcmd); 242 break; 243 case GEN_CMD_CODE(_Write_BBREG): 244 write_bbreg_hdl(padapter, (u8 *)pcmd); 245 break; 246 case GEN_CMD_CODE(_Read_RFREG): 247 read_rfreg_hdl(padapter, (u8 *)pcmd); 248 break; 249 case GEN_CMD_CODE(_Write_RFREG): 250 write_rfreg_hdl(padapter, (u8 *)pcmd); 251 break; 252 case GEN_CMD_CODE(_SetUsbSuspend): 253 sys_suspend_hdl(padapter, (u8 *)pcmd); 254 break; 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 260 * state. */ 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)); 266 } 267 pcmd_r = pcmd; 268 break; 269 case _DRV_INT_CMD_: 270 r871x_internal_cmd_hdl(padapter, pcmd->parmbuf); 271 r8712_free_cmd_obj(pcmd); 272 pcmd_r = NULL; 273 break; 274 default: 275 pcmd_r = pcmd; 276 break; 277 } 278 return pcmd_r; /* if returning pcmd_r == NULL, pcmd must be free. */ 279} 280 281static u8 check_cmd_fifo(struct _adapter *padapter, uint sz) 282{ 283 return _SUCCESS; 284} 285 286u8 r8712_fw_cmd(struct _adapter *pAdapter, u32 cmd) 287{ 288 int pollingcnts = 50; 289 290 r8712_write32(pAdapter, IOCMD_CTRL_REG, cmd); 291 msleep(100); 292 while ((0 != r8712_read32(pAdapter, IOCMD_CTRL_REG)) && 293 (pollingcnts > 0)) { 294 pollingcnts--; 295 msleep(20); 296 } 297 if (pollingcnts == 0) 298 return false; 299 return true; 300} 301 302void r8712_fw_cmd_data(struct _adapter *pAdapter, u32 *value, u8 flag) 303{ 304 if (flag == 0) /* set */ 305 r8712_write32(pAdapter, IOCMD_DATA_REG, *value); 306 else /* query */ 307 *value = r8712_read32(pAdapter, IOCMD_DATA_REG); 308} 309 310int r8712_cmd_thread(void *context) 311{ 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); 318 319 allow_signal(SIGTERM); 320 while (1) { 321 if ((_down_sema(&(pcmdpriv->cmd_queue_sema))) == _FAIL) 322 break; 323 if ((padapter->bDriverStopped == true) || 324 (padapter->bSurpriseRemoved == true)) 325 break; 326 if (r8712_register_cmd_alive(padapter) != _SUCCESS) 327 continue; 328_next: 329 pcmd = r8712_dequeue_cmd(&(pcmdpriv->cmd_queue)); 330 if (!(pcmd)) { 331 r8712_unregister_cmd_alive(padapter); 332 continue; 333 } 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; 341 u8 blnPending = 0; 342 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) & 347 0x0000ffff); 348 if (pdvobj->ishighspeed) { 349 if ((wr_sz % 512) == 0) 350 blnPending = 1; 351 } else { 352 if ((wr_sz % 64) == 0) 353 blnPending = 1; 354 } 355 if (blnPending) /* 32 bytes for TX Desc - 8 offset */ 356 pdesc->txdw0 |= cpu_to_le32(((TXDESC_SIZE + 357 OFFSET_SZ + 8) << OFFSET_SHT) & 358 0x00ff0000); 359 else { 360 pdesc->txdw0 |= cpu_to_le32(((TXDESC_SIZE + 361 OFFSET_SZ) << 362 OFFSET_SHT) & 363 0x00ff0000); 364 } 365 pdesc->txdw0 |= cpu_to_le32(OWN | FSG | LSG); 366 pdesc->txdw1 |= cpu_to_le32((0x13 << QSEL_SHT) & 367 0x00001f00); 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)) 377 break; 378 msleep(100); 379 continue; 380 } 381 if (blnPending) 382 wr_sz += 8; /* Append 8 bytes */ 383 r8712_write_mem(padapter, RTL8712_DMA_H2CCMD, wr_sz, 384 (u8 *)pdesc); 385 pcmdpriv->cmd_seq++; 386 if (pcmd->cmdcode == GEN_CMD_CODE(_CreateBss)) { 387 pcmd->res = H2C_SUCCESS; 388 pcmd_callback = cmd_callback[pcmd-> 389 cmdcode].callback; 390 if (pcmd_callback) 391 pcmd_callback(padapter, pcmd); 392 continue; 393 } 394 if (pcmd->cmdcode == GEN_CMD_CODE(_SetPwrMode)) { 395 if (padapter->pwrctrlpriv.bSleep) { 396 _enter_pwrlock(&(padapter-> 397 pwrctrlpriv.lock)); 398 r8712_set_rpwm(padapter, PS_STATE_S2); 399 up(&padapter->pwrctrlpriv.lock); 400 } 401 } 402 r8712_free_cmd_obj(pcmd); 403 if (list_empty(&pcmdpriv->cmd_queue.queue)) { 404 r8712_unregister_cmd_alive(padapter); 405 continue; 406 } else 407 goto _next; 408 } else 409 goto _next; 410 flush_signals_thread(); 411 } 412 /* free all cmd_obj resources */ 413 do { 414 pcmd = r8712_dequeue_cmd(&(pcmdpriv->cmd_queue)); 415 if (pcmd == NULL) 416 break; 417 r8712_free_cmd_obj(pcmd); 418 } while (1); 419 up(&pcmdpriv->terminate_cmdthread_sema); 420 thread_exit(); 421} 422 423void r8712_event_handle(struct _adapter *padapter, uint *peventbuf) 424{ 425 u8 evt_code, evt_seq; 426 u16 evt_sz; 427 void (*event_callback)(struct _adapter *dev, u8 *pbuf); 428 struct evt_priv *pevt_priv = &(padapter->evtpriv); 429 430 if (peventbuf == NULL) 431 goto _abort_event_; 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); 438 goto _abort_event_; 439 } 440 /* checking if event code is valid */ 441 if (evt_code >= MAX_C2HEVT) { 442 pevt_priv->event_seq = ((evt_seq+1) & 0x7f); 443 goto _abort_event_; 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); 447 goto _abort_event_; 448 } 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); 453 goto _abort_event_; 454 } else if ((evt_sz == 0) && (evt_code != GEN_EVT_CODE(_WPS_PBC))) { 455 pevt_priv->event_seq = ((evt_seq+1)&0x7f); 456 goto _abort_event_; 457 } 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; 464 if (event_callback) 465 event_callback(padapter, (u8 *)peventbuf); 466 pevt_priv->evt_done_cnt++; 467_abort_event_: 468 return; 469} 470