dhd_cdc.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817
  1. /*
  2. * DHD Protocol Module for CDC and BDC.
  3. *
  4. * Copyright (C) 1999-2014, 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 506459 2014-10-06 08:12:35Z $
  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. bcm_bprintf(strbuf, "Protocol CDC: reqid %d\n", dhdp->prot->reqid);
  310. #ifdef PROP_TXSTATUS
  311. dhd_wlfc_dump(dhdp, strbuf);
  312. #endif
  313. }
  314. /* The FreeBSD PKTPUSH could change the packet buf pinter
  315. so we need to make it changable
  316. */
  317. #define PKTBUF pktbuf
  318. void
  319. dhd_prot_hdrpush(dhd_pub_t *dhd, int ifidx, void *PKTBUF)
  320. {
  321. #ifdef BDC
  322. struct bdc_header *h;
  323. #endif /* BDC */
  324. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  325. #ifdef BDC
  326. /* Push BDC header used to convey priority for buses that don't */
  327. PKTPUSH(dhd->osh, PKTBUF, BDC_HEADER_LEN);
  328. h = (struct bdc_header *)PKTDATA(dhd->osh, PKTBUF);
  329. h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
  330. if (PKTSUMNEEDED(PKTBUF))
  331. h->flags |= BDC_FLAG_SUM_NEEDED;
  332. h->priority = (PKTPRIO(PKTBUF) & BDC_PRIORITY_MASK);
  333. h->flags2 = 0;
  334. h->dataOffset = 0;
  335. #endif /* BDC */
  336. BDC_SET_IF_IDX(h, ifidx);
  337. }
  338. #undef PKTBUF /* Only defined in the above routine */
  339. int
  340. dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pktbuf, uchar *reorder_buf_info,
  341. uint *reorder_info_len)
  342. {
  343. #ifdef BDC
  344. struct bdc_header *h;
  345. #endif
  346. uint8 data_offset = 0;
  347. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  348. #ifdef BDC
  349. if (reorder_info_len)
  350. *reorder_info_len = 0;
  351. /* Pop BDC header used to convey priority for buses that don't */
  352. if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) {
  353. DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
  354. PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN));
  355. return BCME_ERROR;
  356. }
  357. h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
  358. if (!ifidx) {
  359. /* for tx packet, skip the analysis */
  360. data_offset = h->dataOffset;
  361. PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN);
  362. goto exit;
  363. }
  364. *ifidx = BDC_GET_IF_IDX(h);
  365. if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) != BDC_PROTO_VER) {
  366. DHD_ERROR(("%s: non-BDC packet received, flags = 0x%x\n",
  367. dhd_ifname(dhd, *ifidx), h->flags));
  368. if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) == BDC_PROTO_VER_1)
  369. h->dataOffset = 0;
  370. else
  371. return BCME_ERROR;
  372. }
  373. if (h->flags & BDC_FLAG_SUM_GOOD) {
  374. DHD_INFO(("%s: BDC packet received with good rx-csum, flags 0x%x\n",
  375. dhd_ifname(dhd, *ifidx), h->flags));
  376. PKTSETSUMGOOD(pktbuf, TRUE);
  377. }
  378. PKTSETPRIO(pktbuf, (h->priority & BDC_PRIORITY_MASK));
  379. data_offset = h->dataOffset;
  380. PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN);
  381. #endif /* BDC */
  382. #ifdef PROP_TXSTATUS
  383. if (!DHD_PKTTAG_PKTDIR(PKTTAG(pktbuf))) {
  384. /*
  385. - parse txstatus only for packets that came from the firmware
  386. */
  387. dhd_wlfc_parse_header_info(dhd, pktbuf, (data_offset << 2),
  388. reorder_buf_info, reorder_info_len);
  389. }
  390. #endif /* PROP_TXSTATUS */
  391. exit:
  392. PKTPULL(dhd->osh, pktbuf, (data_offset << 2));
  393. return 0;
  394. }
  395. int
  396. dhd_prot_attach(dhd_pub_t *dhd)
  397. {
  398. dhd_prot_t *cdc;
  399. if (!(cdc = (dhd_prot_t *)DHD_OS_PREALLOC(dhd, DHD_PREALLOC_PROT, sizeof(dhd_prot_t)))) {
  400. DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
  401. goto fail;
  402. }
  403. memset(cdc, 0, sizeof(dhd_prot_t));
  404. /* ensure that the msg buf directly follows the cdc msg struct */
  405. if ((uintptr)(&cdc->msg + 1) != (uintptr)cdc->buf) {
  406. DHD_ERROR(("dhd_prot_t is not correctly defined\n"));
  407. goto fail;
  408. }
  409. dhd->prot = cdc;
  410. #ifdef BDC
  411. dhd->hdrlen += BDC_HEADER_LEN;
  412. #endif
  413. dhd->maxctl = WLC_IOCTL_MAXLEN + sizeof(cdc_ioctl_t) + ROUND_UP_MARGIN;
  414. return 0;
  415. fail:
  416. if (cdc != NULL)
  417. DHD_OS_PREFREE(dhd, cdc, sizeof(dhd_prot_t));
  418. return BCME_NOMEM;
  419. }
  420. /* ~NOTE~ What if another thread is waiting on the semaphore? Holding it? */
  421. void
  422. dhd_prot_detach(dhd_pub_t *dhd)
  423. {
  424. #ifdef PROP_TXSTATUS
  425. dhd_wlfc_deinit(dhd);
  426. #endif
  427. DHD_OS_PREFREE(dhd, dhd->prot, sizeof(dhd_prot_t));
  428. dhd->prot = NULL;
  429. }
  430. void
  431. dhd_prot_dstats(dhd_pub_t *dhd)
  432. {
  433. /* No stats from dongle added yet, copy bus stats */
  434. dhd->dstats.tx_packets = dhd->tx_packets;
  435. dhd->dstats.tx_errors = dhd->tx_errors;
  436. dhd->dstats.rx_packets = dhd->rx_packets;
  437. dhd->dstats.rx_errors = dhd->rx_errors;
  438. dhd->dstats.rx_dropped = dhd->rx_dropped;
  439. dhd->dstats.multicast = dhd->rx_multicast;
  440. return;
  441. }
  442. int
  443. dhd_prot_init(dhd_pub_t *dhd)
  444. {
  445. int ret = 0;
  446. wlc_rev_info_t revinfo;
  447. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  448. /* Get the device rev info */
  449. memset(&revinfo, 0, sizeof(revinfo));
  450. ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_REVINFO, &revinfo, sizeof(revinfo), FALSE, 0);
  451. if (ret < 0)
  452. goto done;
  453. ret = dhd_preinit_ioctls(dhd);
  454. /* Always assumes wl for now */
  455. dhd->iswl = TRUE;
  456. done:
  457. return ret;
  458. }
  459. void
  460. dhd_prot_stop(dhd_pub_t *dhd)
  461. {
  462. /* Nothing to do for CDC */
  463. }
  464. static void
  465. dhd_get_hostreorder_pkts(void *osh, struct reorder_info *ptr, void **pkt,
  466. uint32 *pkt_count, void **pplast, uint8 start, uint8 end)
  467. {
  468. void *plast = NULL, *p;
  469. uint32 pkt_cnt = 0;
  470. if (ptr->pend_pkts == 0) {
  471. DHD_REORDER(("%s: no packets in reorder queue \n", __FUNCTION__));
  472. *pplast = NULL;
  473. *pkt_count = 0;
  474. *pkt = NULL;
  475. return;
  476. }
  477. do {
  478. p = (void *)(ptr->p[start]);
  479. ptr->p[start] = NULL;
  480. if (p != NULL) {
  481. if (plast == NULL)
  482. *pkt = p;
  483. else
  484. PKTSETNEXT(osh, plast, p);
  485. plast = p;
  486. pkt_cnt++;
  487. }
  488. start++;
  489. if (start > ptr->max_idx)
  490. start = 0;
  491. } while (start != end);
  492. *pplast = plast;
  493. *pkt_count = pkt_cnt;
  494. ptr->pend_pkts -= (uint8)pkt_cnt;
  495. }
  496. int
  497. dhd_process_pkt_reorder_info(dhd_pub_t *dhd, uchar *reorder_info_buf, uint reorder_info_len,
  498. void **pkt, uint32 *pkt_count)
  499. {
  500. uint8 flow_id, max_idx, cur_idx, exp_idx;
  501. struct reorder_info *ptr;
  502. uint8 flags;
  503. void *cur_pkt, *plast = NULL;
  504. uint32 cnt = 0;
  505. if (pkt == NULL) {
  506. if (pkt_count != NULL)
  507. *pkt_count = 0;
  508. return 0;
  509. }
  510. flow_id = reorder_info_buf[WLHOST_REORDERDATA_FLOWID_OFFSET];
  511. flags = reorder_info_buf[WLHOST_REORDERDATA_FLAGS_OFFSET];
  512. DHD_REORDER(("flow_id %d, flags 0x%02x, idx(%d, %d, %d)\n", flow_id, flags,
  513. reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET],
  514. reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET],
  515. reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET]));
  516. /* validate flags and flow id */
  517. if (flags == 0xFF) {
  518. DHD_ERROR(("%s: invalid flags...so ignore this packet\n", __FUNCTION__));
  519. *pkt_count = 1;
  520. return 0;
  521. }
  522. cur_pkt = *pkt;
  523. *pkt = NULL;
  524. ptr = dhd->reorder_bufs[flow_id];
  525. if (flags & WLHOST_REORDERDATA_DEL_FLOW) {
  526. uint32 buf_size = sizeof(struct reorder_info);
  527. DHD_REORDER(("%s: Flags indicating to delete a flow id %d\n",
  528. __FUNCTION__, flow_id));
  529. if (ptr == NULL) {
  530. DHD_REORDER(("%s: received flags to cleanup, but no flow (%d) yet\n",
  531. __FUNCTION__, flow_id));
  532. *pkt_count = 1;
  533. *pkt = cur_pkt;
  534. return 0;
  535. }
  536. dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast,
  537. ptr->exp_idx, ptr->exp_idx);
  538. /* set it to the last packet */
  539. if (plast) {
  540. PKTSETNEXT(dhd->osh, plast, cur_pkt);
  541. cnt++;
  542. }
  543. else {
  544. if (cnt != 0) {
  545. DHD_ERROR(("%s: del flow: something fishy, pending packets %d\n",
  546. __FUNCTION__, cnt));
  547. }
  548. *pkt = cur_pkt;
  549. cnt = 1;
  550. }
  551. buf_size += ((ptr->max_idx + 1) * sizeof(void *));
  552. MFREE(dhd->osh, ptr, buf_size);
  553. dhd->reorder_bufs[flow_id] = NULL;
  554. *pkt_count = cnt;
  555. return 0;
  556. }
  557. /* all the other cases depend on the existance of the reorder struct for that flow id */
  558. if (ptr == NULL) {
  559. uint32 buf_size_alloc = sizeof(reorder_info_t);
  560. max_idx = reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET];
  561. buf_size_alloc += ((max_idx + 1) * sizeof(void*));
  562. /* allocate space to hold the buffers, index etc */
  563. DHD_REORDER(("%s: alloc buffer of size %d size, reorder info id %d, maxidx %d\n",
  564. __FUNCTION__, buf_size_alloc, flow_id, max_idx));
  565. ptr = (struct reorder_info *)MALLOC(dhd->osh, buf_size_alloc);
  566. if (ptr == NULL) {
  567. DHD_ERROR(("%s: Malloc failed to alloc buffer\n", __FUNCTION__));
  568. *pkt_count = 1;
  569. return 0;
  570. }
  571. bzero(ptr, buf_size_alloc);
  572. dhd->reorder_bufs[flow_id] = ptr;
  573. ptr->p = (void *)(ptr+1);
  574. ptr->max_idx = max_idx;
  575. }
  576. if (flags & WLHOST_REORDERDATA_NEW_HOLE) {
  577. DHD_REORDER(("%s: new hole, so cleanup pending buffers\n", __FUNCTION__));
  578. if (ptr->pend_pkts) {
  579. dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast,
  580. ptr->exp_idx, ptr->exp_idx);
  581. ptr->pend_pkts = 0;
  582. }
  583. ptr->cur_idx = reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET];
  584. ptr->exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET];
  585. ptr->max_idx = reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET];
  586. ptr->p[ptr->cur_idx] = cur_pkt;
  587. ptr->pend_pkts++;
  588. *pkt_count = cnt;
  589. }
  590. else if (flags & WLHOST_REORDERDATA_CURIDX_VALID) {
  591. cur_idx = reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET];
  592. exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET];
  593. if ((exp_idx == ptr->exp_idx) && (cur_idx != ptr->exp_idx)) {
  594. /* still in the current hole */
  595. /* enqueue the current on the buffer chain */
  596. if (ptr->p[cur_idx] != NULL) {
  597. DHD_REORDER(("%s: HOLE: ERROR buffer pending..free it\n",
  598. __FUNCTION__));
  599. PKTFREE(dhd->osh, ptr->p[cur_idx], TRUE);
  600. ptr->p[cur_idx] = NULL;
  601. }
  602. ptr->p[cur_idx] = cur_pkt;
  603. ptr->pend_pkts++;
  604. ptr->cur_idx = cur_idx;
  605. DHD_REORDER(("%s: fill up a hole..pending packets is %d\n",
  606. __FUNCTION__, ptr->pend_pkts));
  607. *pkt_count = 0;
  608. *pkt = NULL;
  609. }
  610. else if (ptr->exp_idx == cur_idx) {
  611. /* got the right one ..flush from cur to exp and update exp */
  612. DHD_REORDER(("%s: got the right one now, cur_idx is %d\n",
  613. __FUNCTION__, cur_idx));
  614. if (ptr->p[cur_idx] != NULL) {
  615. DHD_REORDER(("%s: Error buffer pending..free it\n",
  616. __FUNCTION__));
  617. PKTFREE(dhd->osh, ptr->p[cur_idx], TRUE);
  618. ptr->p[cur_idx] = NULL;
  619. }
  620. ptr->p[cur_idx] = cur_pkt;
  621. ptr->pend_pkts++;
  622. ptr->cur_idx = cur_idx;
  623. ptr->exp_idx = exp_idx;
  624. dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast,
  625. cur_idx, exp_idx);
  626. *pkt_count = cnt;
  627. DHD_REORDER(("%s: freeing up buffers %d, still pending %d\n",
  628. __FUNCTION__, cnt, ptr->pend_pkts));
  629. }
  630. else {
  631. uint8 end_idx;
  632. bool flush_current = FALSE;
  633. /* both cur and exp are moved now .. */
  634. DHD_REORDER(("%s:, flow %d, both moved, cur %d(%d), exp %d(%d)\n",
  635. __FUNCTION__, flow_id, ptr->cur_idx, cur_idx,
  636. ptr->exp_idx, exp_idx));
  637. if (flags & WLHOST_REORDERDATA_FLUSH_ALL)
  638. end_idx = ptr->exp_idx;
  639. else
  640. end_idx = exp_idx;
  641. /* flush pkts first */
  642. dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast,
  643. ptr->exp_idx, end_idx);
  644. if (cur_idx == ptr->max_idx) {
  645. if (exp_idx == 0)
  646. flush_current = TRUE;
  647. } else {
  648. if (exp_idx == cur_idx + 1)
  649. flush_current = TRUE;
  650. }
  651. if (flush_current) {
  652. if (plast)
  653. PKTSETNEXT(dhd->osh, plast, cur_pkt);
  654. else
  655. *pkt = cur_pkt;
  656. cnt++;
  657. }
  658. else {
  659. ptr->p[cur_idx] = cur_pkt;
  660. ptr->pend_pkts++;
  661. }
  662. ptr->exp_idx = exp_idx;
  663. ptr->cur_idx = cur_idx;
  664. *pkt_count = cnt;
  665. }
  666. }
  667. else {
  668. uint8 end_idx;
  669. /* no real packet but update to exp_seq...that means explicit window move */
  670. exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET];
  671. DHD_REORDER(("%s: move the window, cur_idx is %d, exp is %d, new exp is %d\n",
  672. __FUNCTION__, ptr->cur_idx, ptr->exp_idx, exp_idx));
  673. if (flags & WLHOST_REORDERDATA_FLUSH_ALL)
  674. end_idx = ptr->exp_idx;
  675. else
  676. end_idx = exp_idx;
  677. dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast, ptr->exp_idx, end_idx);
  678. if (plast)
  679. PKTSETNEXT(dhd->osh, plast, cur_pkt);
  680. else
  681. *pkt = cur_pkt;
  682. cnt++;
  683. *pkt_count = cnt;
  684. /* set the new expected idx */
  685. ptr->exp_idx = exp_idx;
  686. }
  687. return 0;
  688. }