PageRenderTime 35ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/drivers/net/wireless/bcm4329/src/dhd/sys/dhd_cdc.c

https://github.com/Dabary/linux_gt-i9000
C | 589 lines | 418 code | 109 blank | 62 comment | 79 complexity | 05c172b7e479295f161a86e64eb33c6d MD5 | raw file
  1. /*
  2. * DHD Protocol Module for CDC and BDC.
  3. *
  4. * Copyright (C) 1999-2010, Broadcom Corporation
  5. *
  6. * Unless you and Broadcom execute a separate written software license
  7. * agreement governing use of this software, this software is licensed to you
  8. * under the terms of the GNU General Public License version 2 (the "GPL"),
  9. * available at http://www.broadcom.com/licenses/GPLv2.php, with the
  10. * following added to such license:
  11. *
  12. * As a special exception, the copyright holders of this software give you
  13. * permission to link this software with independent modules, and to copy and
  14. * distribute the resulting executable under terms of your choice, provided that
  15. * you also meet, for each linked independent module, the terms and conditions of
  16. * the license of that module. An independent module is a module which is not
  17. * derived from this software. The special exception does not apply to any
  18. * modifications of the software.
  19. *
  20. * Notwithstanding the above, under no circumstances may you combine this
  21. * software in any way with any other Broadcom software provided under a license
  22. * other than the GPL, without Broadcom's express prior written consent.
  23. *
  24. * $Id: dhd_cdc.c,v 1.22.4.2.4.7.2.41 2010/06/23 19:58:18 Exp $
  25. *
  26. * BDC is like CDC, except it includes a header for data packets to convey
  27. * packet priority over the bus, and flags (e.g. to indicate checksum status
  28. * for dongle offload).
  29. */
  30. #include <typedefs.h>
  31. #include <osl.h>
  32. #include <bcmutils.h>
  33. #include <bcmcdc.h>
  34. #include <bcmendian.h>
  35. #include <dngl_stats.h>
  36. #include <dhd.h>
  37. #include <dhd_proto.h>
  38. #include <dhd_bus.h>
  39. #include <dhd_dbg.h>
  40. extern int dhd_preinit_ioctls(dhd_pub_t *dhd);
  41. /* Packet alignment for most efficient SDIO (can change based on platform) */
  42. #ifndef DHD_SDALIGN
  43. #define DHD_SDALIGN 32
  44. #endif
  45. #if !ISPOWEROF2(DHD_SDALIGN)
  46. #error DHD_SDALIGN is not a power of 2!
  47. #endif
  48. #define RETRIES 2 /* # of retries to retrieve matching ioctl response */
  49. #define BUS_HEADER_LEN (16+DHD_SDALIGN) /* Must be atleast SDPCM_RESERVE
  50. * defined in dhd_sdio.c (amount of header tha might be added)
  51. * plus any space that might be needed for alignment padding.
  52. */
  53. #define ROUND_UP_MARGIN 2048 /* Biggest SDIO block size possible for
  54. * round off at the end of buffer
  55. */
  56. #define MAX_TRY_CNT 5 //# of retries to disable deepsleep */
  57. typedef struct dhd_prot {
  58. uint16 reqid;
  59. uint8 pending;
  60. uint32 lastcmd;
  61. uint8 bus_header[BUS_HEADER_LEN];
  62. cdc_ioctl_t msg;
  63. unsigned char buf[WLC_IOCTL_MAXLEN + ROUND_UP_MARGIN];
  64. } dhd_prot_t;
  65. static int
  66. dhdcdc_msg(dhd_pub_t *dhd)
  67. {
  68. dhd_prot_t *prot = dhd->prot;
  69. int len = ltoh32(prot->msg.len) + sizeof(cdc_ioctl_t);
  70. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  71. /* NOTE : cdc->msg.len holds the desired length of the buffer to be
  72. * returned. Only up to CDC_MAX_MSG_SIZE of this buffer area
  73. * is actually sent to the dongle
  74. */
  75. if (len > CDC_MAX_MSG_SIZE)
  76. len = CDC_MAX_MSG_SIZE;
  77. /* Send request */
  78. return dhd_bus_txctl(dhd->bus, (uchar*)&prot->msg, len);
  79. }
  80. static int
  81. dhdcdc_cmplt(dhd_pub_t *dhd, uint32 id, uint32 len)
  82. {
  83. int ret;
  84. dhd_prot_t *prot = dhd->prot;
  85. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  86. do {
  87. ret = dhd_bus_rxctl(dhd->bus, (uchar*)&prot->msg, len+sizeof(cdc_ioctl_t));
  88. if (ret < 0)
  89. break;
  90. } while (CDC_IOC_ID(ltoh32(prot->msg.flags)) != id);
  91. return ret;
  92. }
  93. int
  94. dhdcdc_query_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len)
  95. {
  96. dhd_prot_t *prot = dhd->prot;
  97. cdc_ioctl_t *msg = &prot->msg;
  98. void *info;
  99. int ret = 0, retries = 0;
  100. uint32 id, flags = 0;
  101. if (DHD_TRACE_ON()) {
  102. if ((cmd == WLC_GET_VAR) || (cmd == WLC_SET_VAR)) {
  103. DHD_TRACE(("%s: cmd %d '%s' len %d\n", __FUNCTION__, cmd, (char *)buf, len));
  104. } else {
  105. DHD_TRACE(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
  106. }
  107. }
  108. /* Respond "bcmerror" and "bcmerrorstr" with local cache */
  109. if (cmd == WLC_GET_VAR && buf)
  110. {
  111. if (!strcmp((char *)buf, "bcmerrorstr"))
  112. {
  113. strncpy((char *)buf, bcmerrorstr(dhd->dongle_error), BCME_STRLEN);
  114. goto done;
  115. }
  116. else if (!strcmp((char *)buf, "bcmerror"))
  117. {
  118. *(int *)buf = dhd->dongle_error;
  119. goto done;
  120. }
  121. }
  122. memset(msg, 0, sizeof(cdc_ioctl_t));
  123. msg->cmd = htol32(cmd);
  124. msg->len = htol32(len);
  125. msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT);
  126. CDC_SET_IF_IDX(msg, ifidx);
  127. msg->flags = htol32(msg->flags);
  128. if (buf)
  129. memcpy(prot->buf, buf, len);
  130. if ((ret = dhdcdc_msg(dhd)) < 0) {
  131. DHD_ERROR(("dhdcdc_query_ioctl: dhdcdc_msg failed w/status %d\n", ret));
  132. goto done;
  133. }
  134. retry:
  135. /* wait for interrupt and get first fragment */
  136. if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
  137. goto done;
  138. flags = ltoh32(msg->flags);
  139. id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
  140. if ((id < prot->reqid) && (++retries < RETRIES))
  141. goto retry;
  142. if (id != prot->reqid) {
  143. DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
  144. dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid));
  145. ret = -EINVAL;
  146. goto done;
  147. }
  148. /* Check info buffer */
  149. info = (void*)&msg[1];
  150. /* Copy info buffer */
  151. if (buf)
  152. {
  153. if (ret < (int)len)
  154. len = ret;
  155. memcpy(buf, info, len);
  156. }
  157. /* Check the ERROR flag */
  158. if (flags & CDCF_IOC_ERROR)
  159. {
  160. ret = ltoh32(msg->status);
  161. /* Cache error from dongle */
  162. dhd->dongle_error = ret;
  163. }
  164. done:
  165. return ret;
  166. }
  167. int
  168. dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len)
  169. {
  170. dhd_prot_t *prot = dhd->prot;
  171. cdc_ioctl_t *msg = &prot->msg;
  172. int ret = 0;
  173. uint32 flags, id;
  174. if (prot == 0) {
  175. DHD_ERROR(("dhdcdc_set_ioctl: dhd->prot NULL. idx=%d, cmd=0x%x", ifidx, cmd));
  176. goto done;
  177. }
  178. if (DHD_TRACE_ON()) {
  179. if ((cmd == WLC_GET_VAR) || (cmd == WLC_SET_VAR)) {
  180. DHD_TRACE(("%s: cmd %d '%s' len %d\n", __FUNCTION__, cmd, (char *)buf, len));
  181. } else {
  182. DHD_TRACE(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
  183. }
  184. }
  185. memset(msg, 0, sizeof(cdc_ioctl_t));
  186. msg->cmd = htol32(cmd);
  187. msg->len = htol32(len);
  188. msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT) | CDCF_IOC_SET;
  189. CDC_SET_IF_IDX(msg, ifidx);
  190. msg->flags |= htol32(msg->flags);
  191. if (buf)
  192. memcpy(prot->buf, buf, len);
  193. if ((ret = dhdcdc_msg(dhd)) < 0)
  194. goto done;
  195. if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
  196. goto done;
  197. flags = ltoh32(msg->flags);
  198. id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
  199. if (id != prot->reqid) {
  200. DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
  201. dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid));
  202. ret = -EINVAL;
  203. goto done;
  204. }
  205. /* Check the ERROR flag */
  206. if (flags & CDCF_IOC_ERROR)
  207. {
  208. ret = ltoh32(msg->status);
  209. /* Cache error from dongle */
  210. dhd->dongle_error = ret;
  211. }
  212. done:
  213. return ret;
  214. }
  215. extern int dhd_bus_interface(struct dhd_bus *bus, uint arg, void* arg2);
  216. int
  217. dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len)
  218. {
  219. dhd_prot_t *prot = dhd->prot;
  220. int ret = -1;
  221. if (dhd->busstate == DHD_BUS_DOWN) {
  222. DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__));
  223. return ret;
  224. }
  225. dhd_os_proto_block(dhd);
  226. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  227. ASSERT(len <= WLC_IOCTL_MAXLEN);
  228. if (len > WLC_IOCTL_MAXLEN)
  229. goto done;
  230. if (prot->pending == TRUE) {
  231. DHD_TRACE(("CDC packet is pending!!!! cmd=0x%x (%lu) lastcmd=0x%x (%lu)\n",
  232. ioc->cmd, (unsigned long)ioc->cmd, prot->lastcmd,
  233. (unsigned long)prot->lastcmd));
  234. if ((ioc->cmd == WLC_SET_VAR) || (ioc->cmd == WLC_GET_VAR)) {
  235. DHD_TRACE(("iovar cmd=%s\n", (char*)buf));
  236. }
  237. goto done;
  238. }
  239. prot->pending = TRUE;
  240. prot->lastcmd = ioc->cmd;
  241. if (ioc->set)
  242. ret = dhdcdc_set_ioctl(dhd, ifidx, ioc->cmd, buf, len);
  243. else {
  244. ret = dhdcdc_query_ioctl(dhd, ifidx, ioc->cmd, buf, len);
  245. if (ret > 0)
  246. ioc->used = ret - sizeof(cdc_ioctl_t);
  247. }
  248. /* Too many programs assume ioctl() returns 0 on success */
  249. if (ret >= 0)
  250. ret = 0;
  251. else {
  252. cdc_ioctl_t *msg = &prot->msg;
  253. ioc->needed = ltoh32(msg->len); /* len == needed when set/query fails from dongle */
  254. }
  255. /* Intercept the wme_dp ioctl here */
  256. if ((!ret) && (ioc->cmd == WLC_SET_VAR) && (!strcmp(buf, "wme_dp"))) {
  257. int slen, val = 0;
  258. slen = strlen("wme_dp") + 1;
  259. if (len >= (int)(slen + sizeof(int)))
  260. bcopy(((char *)buf + slen), &val, sizeof(int));
  261. dhd->wme_dp = (uint8) ltoh32(val);
  262. }
  263. prot->pending = FALSE;
  264. done:
  265. dhd_os_proto_unblock(dhd);
  266. return ret;
  267. }
  268. int
  269. dhd_prot_iovar_op(dhd_pub_t *dhdp, const char *name,
  270. void *params, int plen, void *arg, int len, bool set)
  271. {
  272. return BCME_UNSUPPORTED;
  273. }
  274. void
  275. dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
  276. {
  277. bcm_bprintf(strbuf, "Protocol CDC: reqid %d\n", dhdp->prot->reqid);
  278. }
  279. void
  280. dhd_prot_hdrpush(dhd_pub_t *dhd, int ifidx, void *pktbuf)
  281. {
  282. #ifdef BDC
  283. struct bdc_header *h;
  284. #endif /* BDC */
  285. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  286. #ifdef BDC
  287. /* Push BDC header used to convey priority for buses that don't */
  288. PKTPUSH(dhd->osh, pktbuf, BDC_HEADER_LEN);
  289. h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
  290. h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
  291. if (PKTSUMNEEDED(pktbuf))
  292. h->flags |= BDC_FLAG_SUM_NEEDED;
  293. h->priority = (PKTPRIO(pktbuf) & BDC_PRIORITY_MASK);
  294. h->flags2 = 0;
  295. h->rssi = 0;
  296. #endif /* BDC */
  297. BDC_SET_IF_IDX(h, ifidx);
  298. }
  299. bool
  300. dhd_proto_fcinfo(dhd_pub_t *dhd, void *pktbuf, uint8 *fcbits)
  301. {
  302. #ifdef BDC
  303. struct bdc_header *h;
  304. if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) {
  305. DHD_ERROR(("%s: rx data too short (%d < %d)\n",
  306. __FUNCTION__, PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN));
  307. return BCME_ERROR;
  308. }
  309. h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
  310. *fcbits = h->priority >> BDC_PRIORITY_FC_SHIFT;
  311. if ((h->flags2 & BDC_FLAG2_FC_FLAG) == BDC_FLAG2_FC_FLAG)
  312. return TRUE;
  313. #endif
  314. return FALSE;
  315. }
  316. int
  317. dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pktbuf)
  318. {
  319. #ifdef BDC
  320. struct bdc_header *h;
  321. #endif
  322. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  323. #ifdef BDC
  324. /* Pop BDC header used to convey priority for buses that don't */
  325. if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) {
  326. DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
  327. PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN));
  328. return BCME_ERROR;
  329. }
  330. h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
  331. if ((*ifidx = BDC_GET_IF_IDX(h)) >= DHD_MAX_IFS) {
  332. DHD_ERROR(("%s: rx data ifnum out of range (%d)\n",
  333. __FUNCTION__, *ifidx));
  334. return BCME_ERROR;
  335. }
  336. if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) != BDC_PROTO_VER) {
  337. DHD_ERROR(("%s: non-BDC packet received, flags 0x%x\n",
  338. dhd_ifname(dhd, *ifidx), h->flags));
  339. return BCME_ERROR;
  340. }
  341. if (h->flags & BDC_FLAG_SUM_GOOD) {
  342. DHD_INFO(("%s: BDC packet received with good rx-csum, flags 0x%x\n",
  343. dhd_ifname(dhd, *ifidx), h->flags));
  344. PKTSETSUMGOOD(pktbuf, TRUE);
  345. }
  346. PKTSETPRIO(pktbuf, (h->priority & BDC_PRIORITY_MASK));
  347. PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN);
  348. #endif /* BDC */
  349. return 0;
  350. }
  351. int
  352. dhd_prot_attach(dhd_pub_t *dhd)
  353. {
  354. dhd_prot_t *cdc;
  355. #ifndef DHD_USE_STATIC_BUF
  356. if (!(cdc = (dhd_prot_t *)MALLOC(dhd->osh, sizeof(dhd_prot_t)))) {
  357. DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
  358. goto fail;
  359. }
  360. #else
  361. if (!(cdc = (dhd_prot_t *)dhd_os_prealloc(DHD_PREALLOC_PROT, sizeof(dhd_prot_t)))) {
  362. DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
  363. goto fail;
  364. }
  365. #endif /* DHD_USE_STATIC_BUF */
  366. memset(cdc, 0, sizeof(dhd_prot_t));
  367. /* ensure that the msg buf directly follows the cdc msg struct */
  368. if ((uintptr)(&cdc->msg + 1) != (uintptr)cdc->buf) {
  369. DHD_ERROR(("dhd_prot_t is not correctly defined\n"));
  370. goto fail;
  371. }
  372. dhd->prot = cdc;
  373. #ifdef BDC
  374. dhd->hdrlen += BDC_HEADER_LEN;
  375. #endif
  376. dhd->maxctl = WLC_IOCTL_MAXLEN + sizeof(cdc_ioctl_t) + ROUND_UP_MARGIN;
  377. return 0;
  378. fail:
  379. #ifndef DHD_USE_STATIC_BUF
  380. if (cdc != NULL)
  381. MFREE(dhd->osh, cdc, sizeof(dhd_prot_t));
  382. #endif
  383. return BCME_NOMEM;
  384. }
  385. /* ~NOTE~ What if another thread is waiting on the semaphore? Holding it? */
  386. void
  387. dhd_prot_detach(dhd_pub_t *dhd)
  388. {
  389. #ifndef DHD_USE_STATIC_BUF
  390. MFREE(dhd->osh, dhd->prot, sizeof(dhd_prot_t));
  391. #endif
  392. dhd->prot = NULL;
  393. }
  394. void
  395. dhd_prot_dstats(dhd_pub_t *dhd)
  396. {
  397. /* No stats from dongle added yet, copy bus stats */
  398. dhd->dstats.tx_packets = dhd->tx_packets;
  399. dhd->dstats.tx_errors = dhd->tx_errors;
  400. dhd->dstats.rx_packets = dhd->rx_packets;
  401. dhd->dstats.rx_errors = dhd->rx_errors;
  402. dhd->dstats.rx_dropped = dhd->rx_dropped;
  403. dhd->dstats.multicast = dhd->rx_multicast;
  404. return;
  405. }
  406. int
  407. dhd_prot_init(dhd_pub_t *dhd)
  408. {
  409. int ret = 0;
  410. char buf[128];
  411. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  412. dhd_os_proto_block(dhd);
  413. /* Get the device MAC address */
  414. strcpy(buf, "cur_etheraddr");
  415. ret = dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, buf, sizeof(buf));
  416. if (ret < 0) {
  417. dhd_os_proto_unblock(dhd);
  418. return ret;
  419. }
  420. memcpy(dhd->mac.octet, buf, ETHER_ADDR_LEN);
  421. dhd_os_proto_unblock(dhd);
  422. #ifdef EMBEDDED_PLATFORM
  423. ret = dhd_preinit_ioctls(dhd);
  424. #endif /* EMBEDDED_PLATFORM */
  425. /* Always assumes wl for now */
  426. dhd->iswl = TRUE;
  427. return ret;
  428. }
  429. void
  430. dhd_prot_stop(dhd_pub_t *dhd)
  431. {
  432. /* Nothing to do for CDC */
  433. }
  434. dhd_pub_t *dhd_get_pub(struct net_device *dev); /* dhd_linux.c */
  435. int dhd_deepsleep(struct net_device *dev, int flag)
  436. {
  437. char iovbuf[20];
  438. uint powervar = 0;
  439. dhd_pub_t *dhdp = dhd_get_pub(dev);
  440. int cnt = 0;
  441. int ret = 0;
  442. switch (flag) {
  443. case 1 : /* Deepsleep on */
  444. DHD_ERROR(("[WiFi] Deepsleep On\n"));
  445. /* Disable MPC */
  446. powervar = 0;
  447. bcm_mkiovar("mpc", (char *)&powervar, 4, iovbuf, sizeof(iovbuf));
  448. dhdcdc_set_ioctl(dhdp, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
  449. /* Enable Deepsleep*/
  450. powervar = 1;
  451. bcm_mkiovar("deepsleep", (char *)&powervar, 4, iovbuf, sizeof(iovbuf));
  452. dhdcdc_set_ioctl(dhdp, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
  453. break;
  454. case 0: /* Deepsleep Off */
  455. DHD_ERROR(("[WiFi] Deepsleep Off\n"));
  456. /* Disable Deepsleep */
  457. for( cnt = 0 ; cnt < MAX_TRY_CNT ; cnt++ ) {
  458. powervar = 0;
  459. bcm_mkiovar("deepsleep", (char *)&powervar, 4, iovbuf, sizeof(iovbuf));
  460. dhdcdc_set_ioctl(dhdp, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
  461. memset(iovbuf,0,sizeof(iovbuf));
  462. strcpy(iovbuf, "deepsleep");
  463. if ((ret = dhdcdc_query_ioctl(dhdp, 0, WLC_GET_VAR, iovbuf, sizeof(iovbuf))) < 0 ) {
  464. DHD_ERROR(("the error of dhd deepsleep status ret value : %d\n",ret));
  465. }else {
  466. if(!(*(int *)iovbuf )) {
  467. DHD_ERROR(("deepsleep mode is 0, ok , count : %d\n",cnt));
  468. break;
  469. }
  470. }
  471. }
  472. /* Enable MPC */
  473. powervar = 1;
  474. memset(iovbuf,0,sizeof(iovbuf));
  475. bcm_mkiovar("mpc", (char *)&powervar, 4, iovbuf, sizeof(iovbuf));
  476. dhdcdc_set_ioctl(dhdp, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
  477. break;
  478. }
  479. return 0;
  480. }