PageRenderTime 52ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/drivers/net/wireless/bcm4319/dhd_cdc.c

https://bitbucket.org/paulobrien/android_kernel_andypad
C | 549 lines | 389 code | 100 blank | 60 comment | 64 complexity | 45378f587ea8d39ed7477bee359546fa MD5 | raw file
Possible License(s): AGPL-1.0, GPL-2.0, LGPL-2.0
  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. #ifdef CUSTOMER_HW2
  41. int wifi_get_mac_addr(unsigned char *buf);
  42. #endif
  43. extern int dhd_preinit_ioctls(dhd_pub_t *dhd);
  44. /* Packet alignment for most efficient SDIO (can change based on platform) */
  45. #ifndef DHD_SDALIGN
  46. #define DHD_SDALIGN 32
  47. #endif
  48. #if !ISPOWEROF2(DHD_SDALIGN)
  49. #error DHD_SDALIGN is not a power of 2!
  50. #endif
  51. #define RETRIES 2 /* # of retries to retrieve matching ioctl response */
  52. #define BUS_HEADER_LEN (16+DHD_SDALIGN) /* Must be atleast SDPCM_RESERVE
  53. * defined in dhd_sdio.c (amount of header tha might be added)
  54. * plus any space that might be needed for alignment padding.
  55. */
  56. #define ROUND_UP_MARGIN 2048 /* Biggest SDIO block size possible for
  57. * round off at the end of buffer
  58. */
  59. typedef struct dhd_prot {
  60. uint16 reqid;
  61. uint8 pending;
  62. uint32 lastcmd;
  63. uint8 bus_header[BUS_HEADER_LEN];
  64. cdc_ioctl_t msg;
  65. unsigned char buf[WLC_IOCTL_MAXLEN + ROUND_UP_MARGIN];
  66. } dhd_prot_t;
  67. static int
  68. dhdcdc_msg(dhd_pub_t *dhd)
  69. {
  70. dhd_prot_t *prot = dhd->prot;
  71. int len = ltoh32(prot->msg.len) + sizeof(cdc_ioctl_t);
  72. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  73. /*
  74. * NOTE : cdc->msg.len holds the desired length of the buffer to be
  75. * returned. Only up to CDC_MAX_MSG_SIZE of this buffer area
  76. * is actually sent to the dongle
  77. */
  78. if (len > CDC_MAX_MSG_SIZE)
  79. len = CDC_MAX_MSG_SIZE;
  80. /* Send request */
  81. return dhd_bus_txctl(dhd->bus, (uchar*)&prot->msg, len);
  82. }
  83. static int
  84. dhdcdc_cmplt(dhd_pub_t *dhd, uint32 id, uint32 len)
  85. {
  86. int ret;
  87. dhd_prot_t *prot = dhd->prot;
  88. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  89. do {
  90. ret = dhd_bus_rxctl(dhd->bus, (uchar*)&prot->msg, len+sizeof(cdc_ioctl_t));
  91. if (ret < 0)
  92. break;
  93. } while (CDC_IOC_ID(ltoh32(prot->msg.flags)) != id);
  94. return ret;
  95. }
  96. int
  97. dhdcdc_query_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len)
  98. {
  99. dhd_prot_t *prot = dhd->prot;
  100. cdc_ioctl_t *msg = &prot->msg;
  101. void *info;
  102. int ret = 0, retries = 0;
  103. uint32 id, flags = 0;
  104. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  105. DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
  106. /* Respond "bcmerror" and "bcmerrorstr" with local cache */
  107. if (cmd == WLC_GET_VAR && buf)
  108. {
  109. if (!strcmp((char *)buf, "bcmerrorstr"))
  110. {
  111. strncpy((char *)buf, bcmerrorstr(dhd->dongle_error), BCME_STRLEN);
  112. goto done;
  113. }
  114. else if (!strcmp((char *)buf, "bcmerror"))
  115. {
  116. *(int *)buf = dhd->dongle_error;
  117. goto done;
  118. }
  119. }
  120. memset(msg, 0, sizeof(cdc_ioctl_t));
  121. msg->cmd = htol32(cmd);
  122. msg->len = htol32(len);
  123. msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT);
  124. CDC_SET_IF_IDX(msg, ifidx);
  125. msg->flags = htol32(msg->flags);
  126. if (buf)
  127. memcpy(prot->buf, buf, len);
  128. if ((ret = dhdcdc_msg(dhd)) < 0) {
  129. DHD_ERROR(("dhdcdc_query_ioctl: dhdcdc_msg failed w/status %d\n", ret));
  130. goto done;
  131. }
  132. retry:
  133. /* wait for interrupt and get first fragment */
  134. if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
  135. goto done;
  136. flags = ltoh32(msg->flags);
  137. id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
  138. if ((id < prot->reqid) && (++retries < RETRIES))
  139. goto retry;
  140. if (id != prot->reqid) {
  141. DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
  142. dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid));
  143. ret = -EINVAL;
  144. goto done;
  145. }
  146. /* Check info buffer */
  147. info = (void*)&msg[1];
  148. /* Copy info buffer */
  149. if (buf)
  150. {
  151. if (ret < (int)len)
  152. len = ret;
  153. memcpy(buf, info, len);
  154. }
  155. /* Check the ERROR flag */
  156. if (flags & CDCF_IOC_ERROR)
  157. {
  158. ret = ltoh32(msg->status);
  159. /* Cache error from dongle */
  160. dhd->dongle_error = ret;
  161. }
  162. done:
  163. return ret;
  164. }
  165. int
  166. dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len)
  167. {
  168. dhd_prot_t *prot = dhd->prot;
  169. cdc_ioctl_t *msg = &prot->msg;
  170. int ret = 0;
  171. uint32 flags, id;
  172. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  173. DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
  174. memset(msg, 0, sizeof(cdc_ioctl_t));
  175. msg->cmd = htol32(cmd);
  176. msg->len = htol32(len);
  177. msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT) | CDCF_IOC_SET;
  178. CDC_SET_IF_IDX(msg, ifidx);
  179. msg->flags = htol32(msg->flags);
  180. if (buf)
  181. memcpy(prot->buf, buf, len);
  182. if ((ret = dhdcdc_msg(dhd)) < 0)
  183. goto done;
  184. if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
  185. goto done;
  186. flags = ltoh32(msg->flags);
  187. id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
  188. if (id != prot->reqid) {
  189. DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
  190. dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid));
  191. ret = -EINVAL;
  192. goto done;
  193. }
  194. /* Check the ERROR flag */
  195. if (flags & CDCF_IOC_ERROR)
  196. {
  197. ret = ltoh32(msg->status);
  198. /* Cache error from dongle */
  199. dhd->dongle_error = ret;
  200. }
  201. done:
  202. return ret;
  203. }
  204. extern int dhd_bus_interface(struct dhd_bus *bus, uint arg, void* arg2);
  205. int
  206. dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len)
  207. {
  208. dhd_prot_t *prot = dhd->prot;
  209. int ret = -1;
  210. if (dhd->busstate == DHD_BUS_DOWN) {
  211. DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__));
  212. return ret;
  213. }
  214. dhd_os_proto_block(dhd);
  215. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  216. ASSERT(len <= WLC_IOCTL_MAXLEN);
  217. if (len > WLC_IOCTL_MAXLEN)
  218. goto done;
  219. if (prot->pending == TRUE) {
  220. DHD_TRACE(("CDC packet is pending!!!! cmd=0x%x (%lu) lastcmd=0x%x (%lu)\n",
  221. ioc->cmd, (unsigned long)ioc->cmd, prot->lastcmd,
  222. (unsigned long)prot->lastcmd));
  223. if ((ioc->cmd == WLC_SET_VAR) || (ioc->cmd == WLC_GET_VAR)) {
  224. DHD_TRACE(("iovar cmd=%s\n", (char*)buf));
  225. }
  226. goto done;
  227. }
  228. prot->pending = TRUE;
  229. prot->lastcmd = ioc->cmd;
  230. if (ioc->set)
  231. ret = dhdcdc_set_ioctl(dhd, ifidx, ioc->cmd, buf, len);
  232. else {
  233. ret = dhdcdc_query_ioctl(dhd, ifidx, ioc->cmd, buf, len);
  234. if (ret > 0)
  235. ioc->used = ret - sizeof(cdc_ioctl_t);
  236. }
  237. /* Too many programs assume ioctl() returns 0 on success */
  238. if (ret >= 0)
  239. ret = 0;
  240. else {
  241. cdc_ioctl_t *msg = &prot->msg;
  242. ioc->needed = ltoh32(msg->len); /* len == needed when set/query fails from dongle */
  243. }
  244. /* Intercept the wme_dp ioctl here */
  245. if ((!ret) && (ioc->cmd == WLC_SET_VAR) && (!strcmp(buf, "wme_dp"))) {
  246. int slen, val = 0;
  247. slen = strlen("wme_dp") + 1;
  248. if (len >= (int)(slen + sizeof(int)))
  249. bcopy(((char *)buf + slen), &val, sizeof(int));
  250. dhd->wme_dp = (uint8) ltoh32(val);
  251. }
  252. prot->pending = FALSE;
  253. done:
  254. dhd_os_proto_unblock(dhd);
  255. return ret;
  256. }
  257. int
  258. dhd_prot_iovar_op(dhd_pub_t *dhdp, const char *name,
  259. void *params, int plen, void *arg, int len, bool set)
  260. {
  261. return BCME_UNSUPPORTED;
  262. }
  263. void
  264. dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
  265. {
  266. bcm_bprintf(strbuf, "Protocol CDC: reqid %d\n", dhdp->prot->reqid);
  267. }
  268. #ifdef APSTA_PINGTEST
  269. extern struct ether_addr guest_eas[MAX_GUEST];
  270. #endif
  271. void
  272. dhd_prot_hdrpush(dhd_pub_t *dhd, int ifidx, void *pktbuf)
  273. {
  274. #ifdef BDC
  275. struct bdc_header *h;
  276. #ifdef APSTA_PINGTEST
  277. struct ether_header *eh;
  278. int i;
  279. #ifdef DHD_DEBUG
  280. char eabuf1[ETHER_ADDR_STR_LEN];
  281. char eabuf2[ETHER_ADDR_STR_LEN];
  282. #endif /* DHD_DEBUG */
  283. #endif /* APSTA_PINGTEST */
  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. #ifdef APSTA_PINGTEST
  289. eh = (struct ether_header *)PKTDATA(dhd->osh, pktbuf);
  290. #endif
  291. PKTPUSH(dhd->osh, pktbuf, BDC_HEADER_LEN);
  292. h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
  293. h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
  294. if (PKTSUMNEEDED(pktbuf))
  295. h->flags |= BDC_FLAG_SUM_NEEDED;
  296. h->priority = (PKTPRIO(pktbuf) & BDC_PRIORITY_MASK);
  297. h->flags2 = 0;
  298. #ifdef APSTA_PINGTEST
  299. for (i = 0; i < MAX_GUEST; ++i) {
  300. if (!ETHER_ISNULLADDR(eh->ether_dhost) &&
  301. bcmp(eh->ether_dhost, guest_eas[i].octet, ETHER_ADDR_LEN) == 0) {
  302. DHD_TRACE(("send on if 1; sa %s, da %s\n",
  303. bcm_ether_ntoa((struct ether_addr *)(eh->ether_shost), eabuf1),
  304. bcm_ether_ntoa((struct ether_addr *)(eh->ether_dhost), eabuf2)));
  305. /* assume all guest STAs are on interface 1 */
  306. h->flags2 = 1;
  307. break;
  308. }
  309. }
  310. #endif /* APSTA_PINGTEST */
  311. h->rssi = 0;
  312. #endif /* BDC */
  313. BDC_SET_IF_IDX(h, ifidx);
  314. }
  315. bool
  316. dhd_proto_fcinfo(dhd_pub_t *dhd, void *pktbuf, uint8 *fcbits)
  317. {
  318. #ifdef BDC
  319. struct bdc_header *h;
  320. if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) {
  321. DHD_ERROR(("%s: rx data too short (%d < %d)\n",
  322. __FUNCTION__, PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN));
  323. return BCME_ERROR;
  324. }
  325. h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
  326. *fcbits = h->priority >> BDC_PRIORITY_FC_SHIFT;
  327. if ((h->flags2 & BDC_FLAG2_FC_FLAG) == BDC_FLAG2_FC_FLAG)
  328. return TRUE;
  329. #endif
  330. return FALSE;
  331. }
  332. int
  333. dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pktbuf)
  334. {
  335. #ifdef BDC
  336. struct bdc_header *h;
  337. #endif
  338. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  339. #ifdef BDC
  340. /* Pop BDC header used to convey priority for buses that don't */
  341. if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) {
  342. DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
  343. PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN));
  344. return BCME_ERROR;
  345. }
  346. h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
  347. if ((*ifidx = BDC_GET_IF_IDX(h)) >= DHD_MAX_IFS) {
  348. DHD_ERROR(("%s: rx data ifnum out of range (%d)\n",
  349. __FUNCTION__, *ifidx));
  350. return BCME_ERROR;
  351. }
  352. if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) != BDC_PROTO_VER) {
  353. DHD_ERROR(("%s: non-BDC packet received, flags 0x%x\n",
  354. dhd_ifname(dhd, *ifidx), h->flags));
  355. return BCME_ERROR;
  356. }
  357. if (h->flags & BDC_FLAG_SUM_GOOD) {
  358. DHD_INFO(("%s: BDC packet received with good rx-csum, flags 0x%x\n",
  359. dhd_ifname(dhd, *ifidx), h->flags));
  360. PKTSETSUMGOOD(pktbuf, TRUE);
  361. }
  362. PKTSETPRIO(pktbuf, (h->priority & BDC_PRIORITY_MASK));
  363. PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN);
  364. #endif /* BDC */
  365. return 0;
  366. }
  367. int
  368. dhd_prot_attach(dhd_pub_t *dhd)
  369. {
  370. dhd_prot_t *cdc;
  371. #ifndef DHD_USE_STATIC_BUF
  372. if (!(cdc = (dhd_prot_t *)MALLOC(dhd->osh, sizeof(dhd_prot_t)))) {
  373. DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
  374. goto fail;
  375. }
  376. #else
  377. if (!(cdc = (dhd_prot_t *)dhd_os_prealloc(DHD_PREALLOC_PROT, sizeof(dhd_prot_t)))) {
  378. DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
  379. goto fail;
  380. }
  381. #endif /* DHD_USE_STATIC_BUF */
  382. memset(cdc, 0, sizeof(dhd_prot_t));
  383. /* ensure that the msg buf directly follows the cdc msg struct */
  384. if ((uintptr)(&cdc->msg + 1) != (uintptr)cdc->buf) {
  385. DHD_ERROR(("dhd_prot_t is not correctly defined\n"));
  386. goto fail;
  387. }
  388. dhd->prot = cdc;
  389. #ifdef BDC
  390. dhd->hdrlen += BDC_HEADER_LEN;
  391. #endif
  392. dhd->maxctl = WLC_IOCTL_MAXLEN + sizeof(cdc_ioctl_t) + ROUND_UP_MARGIN;
  393. return 0;
  394. fail:
  395. #ifndef DHD_USE_STATIC_BUF
  396. if (cdc != NULL)
  397. MFREE(dhd->osh, cdc, sizeof(dhd_prot_t));
  398. #endif
  399. return BCME_NOMEM;
  400. }
  401. /* ~NOTE~ What if another thread is waiting on the semaphore? Holding it? */
  402. void
  403. dhd_prot_detach(dhd_pub_t *dhd)
  404. {
  405. #ifndef DHD_USE_STATIC_BUF
  406. MFREE(dhd->osh, dhd->prot, sizeof(dhd_prot_t));
  407. #endif
  408. dhd->prot = NULL;
  409. }
  410. void
  411. dhd_prot_dstats(dhd_pub_t *dhd)
  412. {
  413. /* No stats from dongle added yet, copy bus stats */
  414. dhd->dstats.tx_packets = dhd->tx_packets;
  415. dhd->dstats.tx_errors = dhd->tx_errors;
  416. dhd->dstats.rx_packets = dhd->rx_packets;
  417. dhd->dstats.rx_errors = dhd->rx_errors;
  418. dhd->dstats.rx_dropped = dhd->rx_dropped;
  419. dhd->dstats.multicast = dhd->rx_multicast;
  420. return;
  421. }
  422. int
  423. dhd_prot_init(dhd_pub_t *dhd)
  424. {
  425. int ret = 0;
  426. char buf[128];
  427. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  428. dhd_os_proto_block(dhd);
  429. /* Get the device MAC address */
  430. strcpy(buf, "cur_etheraddr");
  431. ret = dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, buf, sizeof(buf));
  432. if (ret < 0) {
  433. dhd_os_proto_unblock(dhd);
  434. return ret;
  435. }
  436. memcpy(dhd->mac.octet, buf, ETHER_ADDR_LEN);
  437. dhd_os_proto_unblock(dhd);
  438. #ifdef EMBEDDED_PLATFORM
  439. ret = dhd_preinit_ioctls(dhd);
  440. #endif /* EMBEDDED_PLATFORM */
  441. /* Always assumes wl for now */
  442. dhd->iswl = TRUE;
  443. return ret;
  444. }
  445. void
  446. dhd_prot_stop(dhd_pub_t *dhd)
  447. {
  448. /* Nothing to do for CDC */
  449. }