dhd_cdc.c 23 KB

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