dhd_cdc.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821
  1. /*
  2. * DHD Protocol Module for CDC and BDC.
  3. *
  4. * Copyright (C) 1999-2015, 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 581040 2015-08-21 05:51:27Z $
  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. #ifdef PROP_TXSTATUS
  41. #include <wlfc_proto.h>
  42. #include <dhd_wlfc.h>
  43. #endif
  44. #define RETRIES 2 /* # of retries to retrieve matching ioctl response */
  45. #define BUS_HEADER_LEN (24+DHD_SDALIGN) /* Must be at least SDPCM_RESERVE
  46. * defined in dhd_sdio.c (amount of header tha might be added)
  47. * plus any space that might be needed for alignment padding.
  48. */
  49. #define ROUND_UP_MARGIN 2048 /* Biggest SDIO block size possible for
  50. * round off at the end of buffer
  51. */
  52. typedef struct dhd_prot {
  53. uint16 reqid;
  54. uint8 pending;
  55. uint32 lastcmd;
  56. uint8 bus_header[BUS_HEADER_LEN];
  57. cdc_ioctl_t msg;
  58. unsigned char buf[WLC_IOCTL_MAXLEN + ROUND_UP_MARGIN];
  59. } dhd_prot_t;
  60. static int
  61. dhdcdc_msg(dhd_pub_t *dhd)
  62. {
  63. int err = 0;
  64. dhd_prot_t *prot = dhd->prot;
  65. int len = ltoh32(prot->msg.len) + sizeof(cdc_ioctl_t);
  66. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  67. DHD_OS_WAKE_LOCK(dhd);
  68. /* NOTE : cdc->msg.len holds the desired length of the buffer to be
  69. * returned. Only up to CDC_MAX_MSG_SIZE of this buffer area
  70. * is actually sent to the dongle
  71. */
  72. if (len > CDC_MAX_MSG_SIZE)
  73. len = CDC_MAX_MSG_SIZE;
  74. /* Send request */
  75. err = dhd_bus_txctl(dhd->bus, (uchar*)&prot->msg, len);
  76. DHD_OS_WAKE_UNLOCK(dhd);
  77. return err;
  78. }
  79. static int
  80. dhdcdc_cmplt(dhd_pub_t *dhd, uint32 id, uint32 len)
  81. {
  82. int ret;
  83. int cdc_len = len + sizeof(cdc_ioctl_t);
  84. dhd_prot_t *prot = dhd->prot;
  85. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  86. #if defined(CUSTOMER_HW4)
  87. DHD_OS_WAKE_LOCK(dhd);
  88. #endif /* OEM_ANDROID && CUSTOMER_HW4 */
  89. do {
  90. ret = dhd_bus_rxctl(dhd->bus, (uchar*)&prot->msg, cdc_len);
  91. if (ret < 0)
  92. break;
  93. } while (CDC_IOC_ID(ltoh32(prot->msg.flags)) != id);
  94. #if defined(CUSTOMER_HW4)
  95. DHD_OS_WAKE_UNLOCK(dhd);
  96. #endif /* OEM_ANDROID && CUSTOMER_HW4 */
  97. return ret;
  98. }
  99. static int
  100. dhdcdc_query_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len, uint8 action)
  101. {
  102. dhd_prot_t *prot = dhd->prot;
  103. cdc_ioctl_t *msg = &prot->msg;
  104. int ret = 0, retries = 0;
  105. uint32 id, flags = 0;
  106. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  107. DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
  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. #ifdef BCMSPI
  124. /* 11bit gSPI bus allows 2048bytes of max-data. We restrict 'len'
  125. * value which is 8Kbytes for various 'get' commands to 2000. 48 bytes are
  126. * left for sw headers and misc.
  127. */
  128. if (len > 2000) {
  129. DHD_ERROR(("dhdcdc_query_ioctl: len is truncated to 2000 bytes\n"));
  130. len = 2000;
  131. }
  132. #endif /* BCMSPI */
  133. msg->cmd = htol32(cmd);
  134. msg->len = htol32(len);
  135. msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT);
  136. CDC_SET_IF_IDX(msg, ifidx);
  137. /* add additional action bits */
  138. action &= WL_IOCTL_ACTION_MASK;
  139. msg->flags |= (action << CDCF_IOC_ACTION_SHIFT);
  140. msg->flags = htol32(msg->flags);
  141. if (buf)
  142. memcpy(prot->buf, buf, len);
  143. if ((ret = dhdcdc_msg(dhd)) < 0) {
  144. if (!dhd->hang_was_sent)
  145. DHD_ERROR(("dhdcdc_query_ioctl: dhdcdc_msg failed w/status %d\n", ret));
  146. goto done;
  147. }
  148. retry:
  149. /* wait for interrupt and get first fragment */
  150. if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
  151. goto done;
  152. flags = ltoh32(msg->flags);
  153. id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
  154. if ((id < prot->reqid) && (++retries < RETRIES))
  155. goto retry;
  156. if (id != prot->reqid) {
  157. DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
  158. dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid));
  159. ret = -EINVAL;
  160. goto done;
  161. }
  162. /* Copy info buffer */
  163. if (buf)
  164. {
  165. if (ret < (int)len)
  166. len = ret;
  167. memcpy(buf, (void*) prot->buf, len);
  168. }
  169. /* Check the ERROR flag */
  170. if (flags & CDCF_IOC_ERROR)
  171. {
  172. ret = ltoh32(msg->status);
  173. /* Cache error from dongle */
  174. dhd->dongle_error = ret;
  175. }
  176. done:
  177. return ret;
  178. }
  179. #if defined(CUSTOMER_HW4) && defined(CONFIG_CONTROL_PM)
  180. extern bool g_pm_control;
  181. #endif /* CUSTOMER_HW4 & CONFIG_CONTROL_PM */
  182. static int
  183. dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len, uint8 action)
  184. {
  185. dhd_prot_t *prot = dhd->prot;
  186. cdc_ioctl_t *msg = &prot->msg;
  187. int ret = 0;
  188. uint32 flags, id;
  189. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  190. DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
  191. if (dhd->busstate == DHD_BUS_DOWN) {
  192. DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__));
  193. return -EIO;
  194. }
  195. /* don't talk to the dongle if fw is about to be reloaded */
  196. if (dhd->hang_was_sent) {
  197. DHD_ERROR(("%s: HANG was sent up earlier. Not talking to the chip\n",
  198. __FUNCTION__));
  199. return -EIO;
  200. }
  201. #ifdef CUSTOMER_HW4
  202. if (cmd == WLC_SET_PM) {
  203. #ifdef CONFIG_CONTROL_PM
  204. if (g_pm_control == TRUE) {
  205. DHD_ERROR(("%s: SET PM ignored!(Requested:%d)\n",
  206. __FUNCTION__, *(char *)buf));
  207. goto done;
  208. }
  209. #endif /* CONFIG_CONTROL_PM */
  210. DHD_ERROR(("%s: SET PM to %d\n", __FUNCTION__, *(char *)buf));
  211. }
  212. #endif /* CUSTOMER_HW4 */
  213. memset(msg, 0, sizeof(cdc_ioctl_t));
  214. msg->cmd = htol32(cmd);
  215. msg->len = htol32(len);
  216. msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT);
  217. CDC_SET_IF_IDX(msg, ifidx);
  218. /* add additional action bits */
  219. action &= WL_IOCTL_ACTION_MASK;
  220. msg->flags |= (action << CDCF_IOC_ACTION_SHIFT) | CDCF_IOC_SET;
  221. msg->flags = htol32(msg->flags);
  222. if (buf)
  223. memcpy(prot->buf, buf, len);
  224. if ((ret = dhdcdc_msg(dhd)) < 0) {
  225. DHD_ERROR(("%s: dhdcdc_msg failed w/status %d\n", __FUNCTION__, ret));
  226. goto done;
  227. }
  228. if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
  229. goto done;
  230. flags = ltoh32(msg->flags);
  231. id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
  232. if (id != prot->reqid) {
  233. DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
  234. dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid));
  235. ret = -EINVAL;
  236. goto done;
  237. }
  238. /* Check the ERROR flag */
  239. if (flags & CDCF_IOC_ERROR)
  240. {
  241. ret = ltoh32(msg->status);
  242. /* Cache error from dongle */
  243. dhd->dongle_error = ret;
  244. }
  245. done:
  246. return ret;
  247. }
  248. int
  249. dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len)
  250. {
  251. dhd_prot_t *prot = dhd->prot;
  252. int ret = -1;
  253. uint8 action;
  254. if ((dhd->busstate == DHD_BUS_DOWN) || dhd->hang_was_sent) {
  255. DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__));
  256. goto done;
  257. }
  258. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  259. ASSERT(len <= WLC_IOCTL_MAXLEN);
  260. if (len > WLC_IOCTL_MAXLEN)
  261. goto done;
  262. if (prot->pending == TRUE) {
  263. DHD_ERROR(("CDC packet is pending!!!! cmd=0x%x (%lu) lastcmd=0x%x (%lu)\n",
  264. ioc->cmd, (unsigned long)ioc->cmd, prot->lastcmd,
  265. (unsigned long)prot->lastcmd));
  266. if ((ioc->cmd == WLC_SET_VAR) || (ioc->cmd == WLC_GET_VAR)) {
  267. DHD_TRACE(("iovar cmd=%s\n", (char*)buf));
  268. }
  269. goto done;
  270. }
  271. prot->pending = TRUE;
  272. prot->lastcmd = ioc->cmd;
  273. action = ioc->set;
  274. if (action & WL_IOCTL_ACTION_SET)
  275. ret = dhdcdc_set_ioctl(dhd, ifidx, ioc->cmd, buf, len, action);
  276. else {
  277. ret = dhdcdc_query_ioctl(dhd, ifidx, ioc->cmd, buf, len, action);
  278. if (ret > 0)
  279. ioc->used = ret - sizeof(cdc_ioctl_t);
  280. }
  281. /* Too many programs assume ioctl() returns 0 on success */
  282. if (ret >= 0)
  283. ret = 0;
  284. else {
  285. cdc_ioctl_t *msg = &prot->msg;
  286. ioc->needed = ltoh32(msg->len); /* len == needed when set/query fails from dongle */
  287. }
  288. /* Intercept the wme_dp ioctl here */
  289. if ((!ret) && (ioc->cmd == WLC_SET_VAR) && (!strcmp(buf, "wme_dp"))) {
  290. int slen, val = 0;
  291. slen = strlen("wme_dp") + 1;
  292. if (len >= (int)(slen + sizeof(int)))
  293. bcopy(((char *)buf + slen), &val, sizeof(int));
  294. dhd->wme_dp = (uint8) ltoh32(val);
  295. }
  296. prot->pending = FALSE;
  297. done:
  298. return ret;
  299. }
  300. int
  301. dhd_prot_iovar_op(dhd_pub_t *dhdp, const char *name,
  302. void *params, int plen, void *arg, int len, bool set)
  303. {
  304. return BCME_UNSUPPORTED;
  305. }
  306. void
  307. dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
  308. {
  309. if (!dhdp || !dhdp->prot)
  310. return;
  311. bcm_bprintf(strbuf, "Protocol CDC: reqid %d\n", dhdp->prot->reqid);
  312. #ifdef PROP_TXSTATUS
  313. dhd_wlfc_dump(dhdp, strbuf);
  314. #endif
  315. }
  316. /* The FreeBSD PKTPUSH could change the packet buf pinter
  317. so we need to make it changable
  318. */
  319. #define PKTBUF pktbuf
  320. void
  321. dhd_prot_hdrpush(dhd_pub_t *dhd, int ifidx, void *PKTBUF)
  322. {
  323. #ifdef BDC
  324. struct bdc_header *h;
  325. #endif /* BDC */
  326. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  327. #ifdef BDC
  328. /* Push BDC header used to convey priority for buses that don't */
  329. PKTPUSH(dhd->osh, PKTBUF, BDC_HEADER_LEN);
  330. h = (struct bdc_header *)PKTDATA(dhd->osh, PKTBUF);
  331. h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
  332. if (PKTSUMNEEDED(PKTBUF))
  333. h->flags |= BDC_FLAG_SUM_NEEDED;
  334. h->priority = (PKTPRIO(PKTBUF) & BDC_PRIORITY_MASK);
  335. h->flags2 = 0;
  336. h->dataOffset = 0;
  337. #endif /* BDC */
  338. BDC_SET_IF_IDX(h, ifidx);
  339. }
  340. #undef PKTBUF /* Only defined in the above routine */
  341. int
  342. dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pktbuf, uchar *reorder_buf_info,
  343. uint *reorder_info_len)
  344. {
  345. #ifdef BDC
  346. struct bdc_header *h;
  347. #endif
  348. uint8 data_offset = 0;
  349. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  350. #ifdef BDC
  351. if (reorder_info_len)
  352. *reorder_info_len = 0;
  353. /* Pop BDC header used to convey priority for buses that don't */
  354. if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) {
  355. DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
  356. PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN));
  357. return BCME_ERROR;
  358. }
  359. h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
  360. if (!ifidx) {
  361. /* for tx packet, skip the analysis */
  362. data_offset = h->dataOffset;
  363. PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN);
  364. goto exit;
  365. }
  366. *ifidx = BDC_GET_IF_IDX(h);
  367. if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) != BDC_PROTO_VER) {
  368. DHD_ERROR(("%s: non-BDC packet received, flags = 0x%x\n",
  369. dhd_ifname(dhd, *ifidx), h->flags));
  370. if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) == BDC_PROTO_VER_1)
  371. h->dataOffset = 0;
  372. else
  373. return BCME_ERROR;
  374. }
  375. if (h->flags & BDC_FLAG_SUM_GOOD) {
  376. DHD_INFO(("%s: BDC packet received with good rx-csum, flags 0x%x\n",
  377. dhd_ifname(dhd, *ifidx), h->flags));
  378. PKTSETSUMGOOD(pktbuf, TRUE);
  379. }
  380. PKTSETPRIO(pktbuf, (h->priority & BDC_PRIORITY_MASK));
  381. data_offset = h->dataOffset;
  382. PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN);
  383. #endif /* BDC */
  384. #ifdef PROP_TXSTATUS
  385. if (!DHD_PKTTAG_PKTDIR(PKTTAG(pktbuf))) {
  386. /*
  387. - parse txstatus only for packets that came from the firmware
  388. */
  389. dhd_wlfc_parse_header_info(dhd, pktbuf, (data_offset << 2),
  390. reorder_buf_info, reorder_info_len);
  391. }
  392. #endif /* PROP_TXSTATUS */
  393. exit:
  394. PKTPULL(dhd->osh, pktbuf, (data_offset << 2));
  395. return 0;
  396. }
  397. int
  398. dhd_prot_attach(dhd_pub_t *dhd)
  399. {
  400. dhd_prot_t *cdc;
  401. if (!(cdc = (dhd_prot_t *)DHD_OS_PREALLOC(dhd, DHD_PREALLOC_PROT, sizeof(dhd_prot_t)))) {
  402. DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
  403. goto fail;
  404. }
  405. memset(cdc, 0, sizeof(dhd_prot_t));
  406. /* ensure that the msg buf directly follows the cdc msg struct */
  407. if ((uintptr)(&cdc->msg + 1) != (uintptr)cdc->buf) {
  408. DHD_ERROR(("dhd_prot_t is not correctly defined\n"));
  409. goto fail;
  410. }
  411. dhd->prot = cdc;
  412. #ifdef BDC
  413. dhd->hdrlen += BDC_HEADER_LEN;
  414. #endif
  415. dhd->maxctl = WLC_IOCTL_MAXLEN + sizeof(cdc_ioctl_t) + ROUND_UP_MARGIN;
  416. return 0;
  417. fail:
  418. if (cdc != NULL) {
  419. DHD_OS_PREFREE(dhd, DHD_PREALLOC_PROT, cdc, sizeof(dhd_prot_t));
  420. }
  421. return BCME_NOMEM;
  422. }
  423. /* ~NOTE~ What if another thread is waiting on the semaphore? Holding it? */
  424. void
  425. dhd_prot_detach(dhd_pub_t *dhd)
  426. {
  427. #ifdef PROP_TXSTATUS
  428. dhd_wlfc_deinit(dhd);
  429. #endif
  430. DHD_OS_PREFREE(dhd, DHD_PREALLOC_PROT, dhd->prot, sizeof(dhd_prot_t));
  431. dhd->prot = NULL;
  432. }
  433. void
  434. dhd_prot_dstats(dhd_pub_t *dhd)
  435. {
  436. /* No stats from dongle added yet, copy bus stats */
  437. dhd->dstats.tx_packets = dhd->tx_packets;
  438. dhd->dstats.tx_errors = dhd->tx_errors;
  439. dhd->dstats.rx_packets = dhd->rx_packets;
  440. dhd->dstats.rx_errors = dhd->rx_errors;
  441. dhd->dstats.rx_dropped = dhd->rx_dropped;
  442. dhd->dstats.multicast = dhd->rx_multicast;
  443. return;
  444. }
  445. int
  446. dhd_prot_init(dhd_pub_t *dhd)
  447. {
  448. int ret = 0;
  449. wlc_rev_info_t revinfo;
  450. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  451. /* Get the device rev info */
  452. memset(&revinfo, 0, sizeof(revinfo));
  453. ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_REVINFO, &revinfo, sizeof(revinfo), FALSE, 0);
  454. if (ret < 0)
  455. goto done;
  456. ret = dhd_preinit_ioctls(dhd);
  457. /* Always assumes wl for now */
  458. dhd->iswl = TRUE;
  459. done:
  460. return ret;
  461. }
  462. void
  463. dhd_prot_stop(dhd_pub_t *dhd)
  464. {
  465. /* Nothing to do for CDC */
  466. }
  467. static void
  468. dhd_get_hostreorder_pkts(void *osh, struct reorder_info *ptr, void **pkt,
  469. uint32 *pkt_count, void **pplast, uint8 start, uint8 end)
  470. {
  471. void *plast = NULL, *p;
  472. uint32 pkt_cnt = 0;
  473. if (ptr->pend_pkts == 0) {
  474. DHD_REORDER(("%s: no packets in reorder queue \n", __FUNCTION__));
  475. *pplast = NULL;
  476. *pkt_count = 0;
  477. *pkt = NULL;
  478. return;
  479. }
  480. do {
  481. p = (void *)(ptr->p[start]);
  482. ptr->p[start] = NULL;
  483. if (p != NULL) {
  484. if (plast == NULL)
  485. *pkt = p;
  486. else
  487. PKTSETNEXT(osh, plast, p);
  488. plast = p;
  489. pkt_cnt++;
  490. }
  491. start++;
  492. if (start > ptr->max_idx)
  493. start = 0;
  494. } while (start != end);
  495. *pplast = plast;
  496. *pkt_count = pkt_cnt;
  497. ptr->pend_pkts -= (uint8)pkt_cnt;
  498. }
  499. int
  500. dhd_process_pkt_reorder_info(dhd_pub_t *dhd, uchar *reorder_info_buf, uint reorder_info_len,
  501. void **pkt, uint32 *pkt_count)
  502. {
  503. uint8 flow_id, max_idx, cur_idx, exp_idx;
  504. struct reorder_info *ptr;
  505. uint8 flags;
  506. void *cur_pkt, *plast = NULL;
  507. uint32 cnt = 0;
  508. if (pkt == NULL) {
  509. if (pkt_count != NULL)
  510. *pkt_count = 0;
  511. return 0;
  512. }
  513. flow_id = reorder_info_buf[WLHOST_REORDERDATA_FLOWID_OFFSET];
  514. flags = reorder_info_buf[WLHOST_REORDERDATA_FLAGS_OFFSET];
  515. DHD_REORDER(("flow_id %d, flags 0x%02x, idx(%d, %d, %d)\n", flow_id, flags,
  516. reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET],
  517. reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET],
  518. reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET]));
  519. /* validate flags and flow id */
  520. if (flags == 0xFF) {
  521. DHD_ERROR(("%s: invalid flags...so ignore this packet\n", __FUNCTION__));
  522. *pkt_count = 1;
  523. return 0;
  524. }
  525. cur_pkt = *pkt;
  526. *pkt = NULL;
  527. ptr = dhd->reorder_bufs[flow_id];
  528. if (flags & WLHOST_REORDERDATA_DEL_FLOW) {
  529. uint32 buf_size = sizeof(struct reorder_info);
  530. DHD_REORDER(("%s: Flags indicating to delete a flow id %d\n",
  531. __FUNCTION__, flow_id));
  532. if (ptr == NULL) {
  533. DHD_REORDER(("%s: received flags to cleanup, but no flow (%d) yet\n",
  534. __FUNCTION__, flow_id));
  535. *pkt_count = 1;
  536. *pkt = cur_pkt;
  537. return 0;
  538. }
  539. dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast,
  540. ptr->exp_idx, ptr->exp_idx);
  541. /* set it to the last packet */
  542. if (plast) {
  543. PKTSETNEXT(dhd->osh, plast, cur_pkt);
  544. cnt++;
  545. }
  546. else {
  547. if (cnt != 0) {
  548. DHD_ERROR(("%s: del flow: something fishy, pending packets %d\n",
  549. __FUNCTION__, cnt));
  550. }
  551. *pkt = cur_pkt;
  552. cnt = 1;
  553. }
  554. buf_size += ((ptr->max_idx + 1) * sizeof(void *));
  555. MFREE(dhd->osh, ptr, buf_size);
  556. dhd->reorder_bufs[flow_id] = NULL;
  557. *pkt_count = cnt;
  558. return 0;
  559. }
  560. /* all the other cases depend on the existance of the reorder struct for that flow id */
  561. if (ptr == NULL) {
  562. uint32 buf_size_alloc = sizeof(reorder_info_t);
  563. max_idx = reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET];
  564. buf_size_alloc += ((max_idx + 1) * sizeof(void*));
  565. /* allocate space to hold the buffers, index etc */
  566. DHD_REORDER(("%s: alloc buffer of size %d size, reorder info id %d, maxidx %d\n",
  567. __FUNCTION__, buf_size_alloc, flow_id, max_idx));
  568. ptr = (struct reorder_info *)MALLOC(dhd->osh, buf_size_alloc);
  569. if (ptr == NULL) {
  570. DHD_ERROR(("%s: Malloc failed to alloc buffer\n", __FUNCTION__));
  571. *pkt_count = 1;
  572. return 0;
  573. }
  574. bzero(ptr, buf_size_alloc);
  575. dhd->reorder_bufs[flow_id] = ptr;
  576. ptr->p = (void *)(ptr+1);
  577. ptr->max_idx = max_idx;
  578. }
  579. if (flags & WLHOST_REORDERDATA_NEW_HOLE) {
  580. DHD_REORDER(("%s: new hole, so cleanup pending buffers\n", __FUNCTION__));
  581. if (ptr->pend_pkts) {
  582. dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast,
  583. ptr->exp_idx, ptr->exp_idx);
  584. ptr->pend_pkts = 0;
  585. }
  586. ptr->cur_idx = reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET];
  587. ptr->exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET];
  588. ptr->max_idx = reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET];
  589. ptr->p[ptr->cur_idx] = cur_pkt;
  590. ptr->pend_pkts++;
  591. *pkt_count = cnt;
  592. }
  593. else if (flags & WLHOST_REORDERDATA_CURIDX_VALID) {
  594. cur_idx = reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET];
  595. exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET];
  596. if ((exp_idx == ptr->exp_idx) && (cur_idx != ptr->exp_idx)) {
  597. /* still in the current hole */
  598. /* enqueue the current on the buffer chain */
  599. if (ptr->p[cur_idx] != NULL) {
  600. DHD_REORDER(("%s: HOLE: ERROR buffer pending..free it\n",
  601. __FUNCTION__));
  602. PKTFREE(dhd->osh, ptr->p[cur_idx], TRUE);
  603. ptr->p[cur_idx] = NULL;
  604. }
  605. ptr->p[cur_idx] = cur_pkt;
  606. ptr->pend_pkts++;
  607. ptr->cur_idx = cur_idx;
  608. DHD_REORDER(("%s: fill up a hole..pending packets is %d\n",
  609. __FUNCTION__, ptr->pend_pkts));
  610. *pkt_count = 0;
  611. *pkt = NULL;
  612. }
  613. else if (ptr->exp_idx == cur_idx) {
  614. /* got the right one ..flush from cur to exp and update exp */
  615. DHD_REORDER(("%s: got the right one now, cur_idx is %d\n",
  616. __FUNCTION__, cur_idx));
  617. if (ptr->p[cur_idx] != NULL) {
  618. DHD_REORDER(("%s: Error buffer pending..free it\n",
  619. __FUNCTION__));
  620. PKTFREE(dhd->osh, ptr->p[cur_idx], TRUE);
  621. ptr->p[cur_idx] = NULL;
  622. }
  623. ptr->p[cur_idx] = cur_pkt;
  624. ptr->pend_pkts++;
  625. ptr->cur_idx = cur_idx;
  626. ptr->exp_idx = exp_idx;
  627. dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast,
  628. cur_idx, exp_idx);
  629. *pkt_count = cnt;
  630. DHD_REORDER(("%s: freeing up buffers %d, still pending %d\n",
  631. __FUNCTION__, cnt, ptr->pend_pkts));
  632. }
  633. else {
  634. uint8 end_idx;
  635. bool flush_current = FALSE;
  636. /* both cur and exp are moved now .. */
  637. DHD_REORDER(("%s:, flow %d, both moved, cur %d(%d), exp %d(%d)\n",
  638. __FUNCTION__, flow_id, ptr->cur_idx, cur_idx,
  639. ptr->exp_idx, exp_idx));
  640. if (flags & WLHOST_REORDERDATA_FLUSH_ALL)
  641. end_idx = ptr->exp_idx;
  642. else
  643. end_idx = exp_idx;
  644. /* flush pkts first */
  645. dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast,
  646. ptr->exp_idx, end_idx);
  647. if (cur_idx == ptr->max_idx) {
  648. if (exp_idx == 0)
  649. flush_current = TRUE;
  650. } else {
  651. if (exp_idx == cur_idx + 1)
  652. flush_current = TRUE;
  653. }
  654. if (flush_current) {
  655. if (plast)
  656. PKTSETNEXT(dhd->osh, plast, cur_pkt);
  657. else
  658. *pkt = cur_pkt;
  659. cnt++;
  660. }
  661. else {
  662. ptr->p[cur_idx] = cur_pkt;
  663. ptr->pend_pkts++;
  664. }
  665. ptr->exp_idx = exp_idx;
  666. ptr->cur_idx = cur_idx;
  667. *pkt_count = cnt;
  668. }
  669. }
  670. else {
  671. uint8 end_idx;
  672. /* no real packet but update to exp_seq...that means explicit window move */
  673. exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET];
  674. DHD_REORDER(("%s: move the window, cur_idx is %d, exp is %d, new exp is %d\n",
  675. __FUNCTION__, ptr->cur_idx, ptr->exp_idx, exp_idx));
  676. if (flags & WLHOST_REORDERDATA_FLUSH_ALL)
  677. end_idx = ptr->exp_idx;
  678. else
  679. end_idx = exp_idx;
  680. dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast, ptr->exp_idx, end_idx);
  681. if (plast)
  682. PKTSETNEXT(dhd->osh, plast, cur_pkt);
  683. else
  684. *pkt = cur_pkt;
  685. cnt++;
  686. *pkt_count = cnt;
  687. /* set the new expected idx */
  688. ptr->exp_idx = exp_idx;
  689. }
  690. return 0;
  691. }