rtl8712_cmd.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  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. #define _RTL8712_CMD_C_
  29. #include <linux/compiler.h>
  30. #include <linux/kernel.h>
  31. #include <linux/errno.h>
  32. #include <linux/slab.h>
  33. #include <linux/module.h>
  34. #include <linux/kref.h>
  35. #include <linux/netdevice.h>
  36. #include <linux/skbuff.h>
  37. #include <linux/usb.h>
  38. #include <linux/usb/ch9.h>
  39. #include <linux/circ_buf.h>
  40. #include <linux/uaccess.h>
  41. #include <asm/byteorder.h>
  42. #include <linux/atomic.h>
  43. #include <linux/semaphore.h>
  44. #include <linux/rtnetlink.h>
  45. #include "osdep_service.h"
  46. #include "drv_types.h"
  47. #include "recv_osdep.h"
  48. #include "mlme_osdep.h"
  49. #include "rtl871x_ioctl_set.h"
  50. static void check_hw_pbc(struct _adapter *padapter)
  51. {
  52. u8 tmp1byte;
  53. r8712_write8(padapter, MAC_PINMUX_CTRL, (GPIOMUX_EN | GPIOSEL_GPIO));
  54. tmp1byte = r8712_read8(padapter, GPIO_IO_SEL);
  55. tmp1byte &= ~(HAL_8192S_HW_GPIO_WPS_BIT);
  56. r8712_write8(padapter, GPIO_IO_SEL, tmp1byte);
  57. tmp1byte = r8712_read8(padapter, GPIO_CTRL);
  58. if (tmp1byte == 0xff)
  59. return;
  60. if (tmp1byte & HAL_8192S_HW_GPIO_WPS_BIT) {
  61. /* Here we only set bPbcPressed to true
  62. * After trigger PBC, the variable will be set to false
  63. */
  64. DBG_8712("CheckPbcGPIO - PBC is pressed !!!!\n");
  65. /* 0 is the default value and it means the application monitors
  66. * the HW PBC doesn't provide its pid to driver.
  67. */
  68. if (padapter->pid == 0)
  69. return;
  70. kill_pid(find_vpid(padapter->pid), SIGUSR1, 1);
  71. }
  72. }
  73. /* query rx phy status from fw.
  74. * Adhoc mode: beacon.
  75. * Infrastructure mode: beacon , data.
  76. */
  77. static void query_fw_rx_phy_status(struct _adapter *padapter)
  78. {
  79. u32 val32 = 0;
  80. int pollingcnts = 50;
  81. if (check_fwstate(&padapter->mlmepriv, _FW_LINKED)) {
  82. r8712_write32(padapter, IOCMD_CTRL_REG, 0xf4000001);
  83. msleep(100);
  84. /* Wait FW complete IO Cmd */
  85. while ((r8712_read32(padapter, IOCMD_CTRL_REG)) &&
  86. (pollingcnts > 0)) {
  87. pollingcnts--;
  88. msleep(20);
  89. }
  90. if (pollingcnts != 0)
  91. val32 = r8712_read32(padapter, IOCMD_DATA_REG);
  92. else /* time out */
  93. val32 = 0;
  94. val32 >>= 4;
  95. padapter->recvpriv.fw_rssi =
  96. (u8)r8712_signal_scale_mapping(val32);
  97. }
  98. }
  99. /* check mlme, hw, phy, or dynamic algorithm status. */
  100. static void StatusWatchdogCallback(struct _adapter *padapter)
  101. {
  102. check_hw_pbc(padapter);
  103. query_fw_rx_phy_status(padapter);
  104. }
  105. static void r871x_internal_cmd_hdl(struct _adapter *padapter, u8 *pbuf)
  106. {
  107. struct drvint_cmd_parm *pdrvcmd;
  108. if (!pbuf)
  109. return;
  110. pdrvcmd = (struct drvint_cmd_parm *)pbuf;
  111. switch (pdrvcmd->i_cid) {
  112. case WDG_WK_CID:
  113. StatusWatchdogCallback(padapter);
  114. break;
  115. default:
  116. break;
  117. }
  118. kfree(pdrvcmd->pbuf);
  119. }
  120. static u8 read_macreg_hdl(struct _adapter *padapter, u8 *pbuf)
  121. {
  122. void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd);
  123. struct cmd_obj *pcmd = (struct cmd_obj *)pbuf;
  124. /* invoke cmd->callback function */
  125. pcmd_callback = cmd_callback[pcmd->cmdcode].callback;
  126. if (pcmd_callback == NULL)
  127. r8712_free_cmd_obj(pcmd);
  128. else
  129. pcmd_callback(padapter, pcmd);
  130. return H2C_SUCCESS;
  131. }
  132. static u8 write_macreg_hdl(struct _adapter *padapter, u8 *pbuf)
  133. {
  134. void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd);
  135. struct cmd_obj *pcmd = (struct cmd_obj *)pbuf;
  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. static u8 read_bbreg_hdl(struct _adapter *padapter, u8 *pbuf)
  145. {
  146. u32 val;
  147. void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd);
  148. struct cmd_obj *pcmd = (struct cmd_obj *)pbuf;
  149. if (pcmd->rsp && pcmd->rspsz > 0)
  150. memcpy(pcmd->rsp, (u8 *)&val, pcmd->rspsz);
  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. static u8 write_bbreg_hdl(struct _adapter *padapter, u8 *pbuf)
  159. {
  160. void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd);
  161. struct cmd_obj *pcmd = (struct cmd_obj *)pbuf;
  162. pcmd_callback = cmd_callback[pcmd->cmdcode].callback;
  163. if (pcmd_callback == NULL)
  164. r8712_free_cmd_obj(pcmd);
  165. else
  166. pcmd_callback(padapter, pcmd);
  167. return H2C_SUCCESS;
  168. }
  169. static u8 read_rfreg_hdl(struct _adapter *padapter, u8 *pbuf)
  170. {
  171. u32 val;
  172. void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd);
  173. struct cmd_obj *pcmd = (struct cmd_obj *)pbuf;
  174. if (pcmd->rsp && pcmd->rspsz > 0)
  175. memcpy(pcmd->rsp, (u8 *)&val, pcmd->rspsz);
  176. pcmd_callback = cmd_callback[pcmd->cmdcode].callback;
  177. if (pcmd_callback == NULL)
  178. r8712_free_cmd_obj(pcmd);
  179. else
  180. pcmd_callback(padapter, pcmd);
  181. return H2C_SUCCESS;
  182. }
  183. static u8 write_rfreg_hdl(struct _adapter *padapter, u8 *pbuf)
  184. {
  185. void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd);
  186. struct cmd_obj *pcmd = (struct cmd_obj *)pbuf;
  187. pcmd_callback = cmd_callback[pcmd->cmdcode].callback;
  188. if (pcmd_callback == NULL)
  189. r8712_free_cmd_obj(pcmd);
  190. else
  191. pcmd_callback(padapter, pcmd);
  192. return H2C_SUCCESS;
  193. }
  194. static u8 sys_suspend_hdl(struct _adapter *padapter, u8 *pbuf)
  195. {
  196. struct cmd_obj *pcmd = (struct cmd_obj *)pbuf;
  197. r8712_free_cmd_obj(pcmd);
  198. return H2C_SUCCESS;
  199. }
  200. static struct cmd_obj *cmd_hdl_filter(struct _adapter *padapter,
  201. struct cmd_obj *pcmd)
  202. {
  203. struct cmd_obj *pcmd_r;
  204. if (pcmd == NULL)
  205. return pcmd;
  206. pcmd_r = NULL;
  207. switch (pcmd->cmdcode) {
  208. case GEN_CMD_CODE(_Read_MACREG):
  209. read_macreg_hdl(padapter, (u8 *)pcmd);
  210. pcmd_r = pcmd;
  211. break;
  212. case GEN_CMD_CODE(_Write_MACREG):
  213. write_macreg_hdl(padapter, (u8 *)pcmd);
  214. pcmd_r = pcmd;
  215. break;
  216. case GEN_CMD_CODE(_Read_BBREG):
  217. read_bbreg_hdl(padapter, (u8 *)pcmd);
  218. break;
  219. case GEN_CMD_CODE(_Write_BBREG):
  220. write_bbreg_hdl(padapter, (u8 *)pcmd);
  221. break;
  222. case GEN_CMD_CODE(_Read_RFREG):
  223. read_rfreg_hdl(padapter, (u8 *)pcmd);
  224. break;
  225. case GEN_CMD_CODE(_Write_RFREG):
  226. write_rfreg_hdl(padapter, (u8 *)pcmd);
  227. break;
  228. case GEN_CMD_CODE(_SetUsbSuspend):
  229. sys_suspend_hdl(padapter, (u8 *)pcmd);
  230. break;
  231. case GEN_CMD_CODE(_JoinBss):
  232. r8712_joinbss_reset(padapter);
  233. /* Before set JoinBss_CMD to FW, driver must ensure FW is in
  234. * PS_MODE_ACTIVE. Directly write rpwm to radio on and assign
  235. * new pwr_mode to Driver, instead of use workitem to change
  236. * state.
  237. */
  238. if (padapter->pwrctrlpriv.pwr_mode > PS_MODE_ACTIVE) {
  239. padapter->pwrctrlpriv.pwr_mode = PS_MODE_ACTIVE;
  240. _enter_pwrlock(&(padapter->pwrctrlpriv.lock));
  241. r8712_set_rpwm(padapter, PS_STATE_S4);
  242. up(&(padapter->pwrctrlpriv.lock));
  243. }
  244. pcmd_r = pcmd;
  245. break;
  246. case _DRV_INT_CMD_:
  247. r871x_internal_cmd_hdl(padapter, pcmd->parmbuf);
  248. r8712_free_cmd_obj(pcmd);
  249. pcmd_r = NULL;
  250. break;
  251. default:
  252. pcmd_r = pcmd;
  253. break;
  254. }
  255. return pcmd_r; /* if returning pcmd_r == NULL, pcmd must be free. */
  256. }
  257. static u8 check_cmd_fifo(struct _adapter *padapter, uint sz)
  258. {
  259. return _SUCCESS;
  260. }
  261. u8 r8712_fw_cmd(struct _adapter *pAdapter, u32 cmd)
  262. {
  263. int pollingcnts = 50;
  264. r8712_write32(pAdapter, IOCMD_CTRL_REG, cmd);
  265. msleep(100);
  266. while ((0 != r8712_read32(pAdapter, IOCMD_CTRL_REG)) &&
  267. (pollingcnts > 0)) {
  268. pollingcnts--;
  269. msleep(20);
  270. }
  271. if (pollingcnts == 0)
  272. return false;
  273. return true;
  274. }
  275. void r8712_fw_cmd_data(struct _adapter *pAdapter, u32 *value, u8 flag)
  276. {
  277. if (flag == 0) /* set */
  278. r8712_write32(pAdapter, IOCMD_DATA_REG, *value);
  279. else /* query */
  280. *value = r8712_read32(pAdapter, IOCMD_DATA_REG);
  281. }
  282. int r8712_cmd_thread(void *context)
  283. {
  284. struct cmd_obj *pcmd;
  285. unsigned int cmdsz, wr_sz, *pcmdbuf;
  286. struct tx_desc *pdesc;
  287. void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd);
  288. struct _adapter *padapter = (struct _adapter *)context;
  289. struct cmd_priv *pcmdpriv = &(padapter->cmdpriv);
  290. allow_signal(SIGTERM);
  291. while (1) {
  292. if ((_down_sema(&(pcmdpriv->cmd_queue_sema))) == _FAIL)
  293. break;
  294. if (padapter->bDriverStopped || padapter->bSurpriseRemoved)
  295. break;
  296. if (r8712_register_cmd_alive(padapter) != _SUCCESS)
  297. continue;
  298. _next:
  299. pcmd = r8712_dequeue_cmd(&(pcmdpriv->cmd_queue));
  300. if (!(pcmd)) {
  301. r8712_unregister_cmd_alive(padapter);
  302. continue;
  303. }
  304. pcmdbuf = (unsigned int *)pcmdpriv->cmd_buf;
  305. pdesc = (struct tx_desc *)pcmdbuf;
  306. memset(pdesc, 0, TXDESC_SIZE);
  307. pcmd = cmd_hdl_filter(padapter, pcmd);
  308. if (pcmd) { /* if pcmd != NULL, cmd will be handled by f/w */
  309. struct dvobj_priv *pdvobj = &padapter->dvobjpriv;
  310. u8 blnPending = 0;
  311. pcmdpriv->cmd_issued_cnt++;
  312. cmdsz = round_up(pcmd->cmdsz, 8);
  313. wr_sz = TXDESC_SIZE + 8 + cmdsz;
  314. pdesc->txdw0 |= cpu_to_le32((wr_sz - TXDESC_SIZE) &
  315. 0x0000ffff);
  316. if (pdvobj->ishighspeed) {
  317. if ((wr_sz % 512) == 0)
  318. blnPending = 1;
  319. } else {
  320. if ((wr_sz % 64) == 0)
  321. blnPending = 1;
  322. }
  323. if (blnPending) /* 32 bytes for TX Desc - 8 offset */
  324. pdesc->txdw0 |= cpu_to_le32(((TXDESC_SIZE +
  325. OFFSET_SZ + 8) << OFFSET_SHT) &
  326. 0x00ff0000);
  327. else {
  328. pdesc->txdw0 |= cpu_to_le32(((TXDESC_SIZE +
  329. OFFSET_SZ) <<
  330. OFFSET_SHT) &
  331. 0x00ff0000);
  332. }
  333. pdesc->txdw0 |= cpu_to_le32(OWN | FSG | LSG);
  334. pdesc->txdw1 |= cpu_to_le32((0x13 << QSEL_SHT) &
  335. 0x00001f00);
  336. pcmdbuf += (TXDESC_SIZE >> 2);
  337. *pcmdbuf = cpu_to_le32((cmdsz & 0x0000ffff) |
  338. (pcmd->cmdcode << 16) |
  339. (pcmdpriv->cmd_seq << 24));
  340. pcmdbuf += 2; /* 8 bytes alignment */
  341. memcpy((u8 *)pcmdbuf, pcmd->parmbuf, pcmd->cmdsz);
  342. while (check_cmd_fifo(padapter, wr_sz) == _FAIL) {
  343. if (padapter->bDriverStopped ||
  344. padapter->bSurpriseRemoved)
  345. break;
  346. msleep(100);
  347. continue;
  348. }
  349. if (blnPending)
  350. wr_sz += 8; /* Append 8 bytes */
  351. r8712_write_mem(padapter, RTL8712_DMA_H2CCMD, wr_sz,
  352. (u8 *)pdesc);
  353. pcmdpriv->cmd_seq++;
  354. if (pcmd->cmdcode == GEN_CMD_CODE(_CreateBss)) {
  355. pcmd->res = H2C_SUCCESS;
  356. pcmd_callback = cmd_callback[pcmd->
  357. cmdcode].callback;
  358. if (pcmd_callback)
  359. pcmd_callback(padapter, pcmd);
  360. continue;
  361. }
  362. if (pcmd->cmdcode == GEN_CMD_CODE(_SetPwrMode)) {
  363. if (padapter->pwrctrlpriv.bSleep) {
  364. _enter_pwrlock(&(padapter->
  365. pwrctrlpriv.lock));
  366. r8712_set_rpwm(padapter, PS_STATE_S2);
  367. up(&padapter->pwrctrlpriv.lock);
  368. }
  369. }
  370. r8712_free_cmd_obj(pcmd);
  371. if (list_empty(&pcmdpriv->cmd_queue.queue)) {
  372. r8712_unregister_cmd_alive(padapter);
  373. continue;
  374. } else {
  375. goto _next;
  376. }
  377. } else {
  378. goto _next;
  379. }
  380. flush_signals_thread();
  381. }
  382. /* free all cmd_obj resources */
  383. do {
  384. pcmd = r8712_dequeue_cmd(&(pcmdpriv->cmd_queue));
  385. if (pcmd == NULL)
  386. break;
  387. r8712_free_cmd_obj(pcmd);
  388. } while (1);
  389. up(&pcmdpriv->terminate_cmdthread_sema);
  390. thread_exit();
  391. }
  392. void r8712_event_handle(struct _adapter *padapter, uint *peventbuf)
  393. {
  394. u8 evt_code, evt_seq;
  395. u16 evt_sz;
  396. void (*event_callback)(struct _adapter *dev, u8 *pbuf);
  397. struct evt_priv *pevt_priv = &(padapter->evtpriv);
  398. if (peventbuf == NULL)
  399. goto _abort_event_;
  400. evt_sz = (u16)(le32_to_cpu(*peventbuf) & 0xffff);
  401. evt_seq = (u8)((le32_to_cpu(*peventbuf) >> 24) & 0x7f);
  402. evt_code = (u8)((le32_to_cpu(*peventbuf) >> 16) & 0xff);
  403. /* checking event sequence... */
  404. if ((evt_seq & 0x7f) != pevt_priv->event_seq) {
  405. pevt_priv->event_seq = ((evt_seq + 1) & 0x7f);
  406. goto _abort_event_;
  407. }
  408. /* checking if event code is valid */
  409. if (evt_code >= MAX_C2HEVT) {
  410. pevt_priv->event_seq = ((evt_seq + 1) & 0x7f);
  411. goto _abort_event_;
  412. } else if ((evt_code == GEN_EVT_CODE(_Survey)) &&
  413. (evt_sz > sizeof(struct wlan_bssid_ex))) {
  414. pevt_priv->event_seq = ((evt_seq + 1) & 0x7f);
  415. goto _abort_event_;
  416. }
  417. /* checking if event size match the event parm size */
  418. if ((wlanevents[evt_code].parmsize) &&
  419. (wlanevents[evt_code].parmsize != evt_sz)) {
  420. pevt_priv->event_seq = ((evt_seq + 1) & 0x7f);
  421. goto _abort_event_;
  422. } else if ((evt_sz == 0) && (evt_code != GEN_EVT_CODE(_WPS_PBC))) {
  423. pevt_priv->event_seq = ((evt_seq + 1) & 0x7f);
  424. goto _abort_event_;
  425. }
  426. pevt_priv->event_seq++; /* update evt_seq */
  427. if (pevt_priv->event_seq > 127)
  428. pevt_priv->event_seq = 0;
  429. /* move to event content, 8 bytes alignment */
  430. peventbuf = peventbuf + 2;
  431. event_callback = wlanevents[evt_code].event_callback;
  432. if (event_callback)
  433. event_callback(padapter, (u8 *)peventbuf);
  434. pevt_priv->evt_done_cnt++;
  435. _abort_event_:
  436. return;
  437. }