PageRenderTime 52ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://github.com/getitnowmarketing/LG-2.6.32
C | 575 lines | 398 code | 112 blank | 65 comment | 64 complexity | 02d38cb6f98688ed5cdcd84368f4e14d MD5 | raw file
Possible License(s): GPL-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. /* NOTE : cdc->msg.len holds the desired length of the buffer to be
  74. * returned. Only up to CDC_MAX_MSG_SIZE of this buffer area
  75. * is actually sent to the dongle
  76. */
  77. if (len > CDC_MAX_MSG_SIZE)
  78. len = CDC_MAX_MSG_SIZE;
  79. /* Send request */
  80. return dhd_bus_txctl(dhd->bus, (uchar*)&prot->msg, len);
  81. }
  82. static int
  83. dhdcdc_cmplt(dhd_pub_t *dhd, uint32 id, uint32 len)
  84. {
  85. int ret;
  86. dhd_prot_t *prot = dhd->prot;
  87. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  88. do {
  89. ret = dhd_bus_rxctl(dhd->bus, (uchar*)&prot->msg, len+sizeof(cdc_ioctl_t));
  90. if (ret < 0)
  91. break;
  92. } while (CDC_IOC_ID(ltoh32(prot->msg.flags)) != id);
  93. return ret;
  94. }
  95. int
  96. dhdcdc_query_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len)
  97. {
  98. dhd_prot_t *prot = dhd->prot;
  99. cdc_ioctl_t *msg = &prot->msg;
  100. void *info;
  101. int ret = 0, retries = 0;
  102. uint32 id, flags = 0;
  103. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  104. DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
  105. /* Respond "bcmerror" and "bcmerrorstr" with local cache */
  106. if (cmd == WLC_GET_VAR && buf)
  107. {
  108. if (!strcmp((char *)buf, "bcmerrorstr"))
  109. {
  110. strncpy((char *)buf, bcmerrorstr(dhd->dongle_error), BCME_STRLEN);
  111. goto done;
  112. }
  113. else if (!strcmp((char *)buf, "bcmerror"))
  114. {
  115. *(int *)buf = dhd->dongle_error;
  116. goto done;
  117. }
  118. }
  119. memset(msg, 0, sizeof(cdc_ioctl_t));
  120. msg->cmd = htol32(cmd);
  121. msg->len = htol32(len);
  122. msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT);
  123. CDC_SET_IF_IDX(msg, ifidx);
  124. msg->flags = htol32(msg->flags);
  125. if (buf)
  126. memcpy(prot->buf, buf, len);
  127. if ((ret = dhdcdc_msg(dhd)) < 0) {
  128. DHD_ERROR(("dhdcdc_query_ioctl: dhdcdc_msg failed w/status %d\n", ret));
  129. goto done;
  130. }
  131. retry:
  132. /* wait for interrupt and get first fragment */
  133. if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
  134. goto done;
  135. flags = ltoh32(msg->flags);
  136. id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
  137. if ((id < prot->reqid) && (++retries < RETRIES))
  138. goto retry;
  139. if (id != prot->reqid) {
  140. DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
  141. dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid));
  142. ret = -EINVAL;
  143. goto done;
  144. }
  145. /* Check info buffer */
  146. info = (void*)&msg[1];
  147. /* Copy info buffer */
  148. if (buf)
  149. {
  150. if (ret < (int)len)
  151. len = ret;
  152. memcpy(buf, info, len);
  153. }
  154. /* Check the ERROR flag */
  155. if (flags & CDCF_IOC_ERROR)
  156. {
  157. ret = ltoh32(msg->status);
  158. /* Cache error from dongle */
  159. dhd->dongle_error = ret;
  160. }
  161. done:
  162. return ret;
  163. }
  164. int
  165. dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len)
  166. {
  167. dhd_prot_t *prot = dhd->prot;
  168. cdc_ioctl_t *msg = &prot->msg;
  169. int ret = 0;
  170. uint32 flags, id;
  171. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  172. DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
  173. memset(msg, 0, sizeof(cdc_ioctl_t));
  174. msg->cmd = htol32(cmd);
  175. msg->len = htol32(len);
  176. msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT) | CDCF_IOC_SET;
  177. CDC_SET_IF_IDX(msg, ifidx);
  178. msg->flags = htol32(msg->flags);
  179. if (buf)
  180. memcpy(prot->buf, buf, len);
  181. if ((ret = dhdcdc_msg(dhd)) < 0)
  182. goto done;
  183. if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
  184. goto done;
  185. flags = ltoh32(msg->flags);
  186. id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
  187. if (id != prot->reqid) {
  188. DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
  189. dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid));
  190. ret = -EINVAL;
  191. goto done;
  192. }
  193. /* Check the ERROR flag */
  194. if (flags & CDCF_IOC_ERROR)
  195. {
  196. ret = ltoh32(msg->status);
  197. /* Cache error from dongle */
  198. dhd->dongle_error = ret;
  199. }
  200. done:
  201. return ret;
  202. }
  203. extern int dhd_bus_interface(struct dhd_bus *bus, uint arg, void* arg2);
  204. int
  205. dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len)
  206. {
  207. dhd_prot_t *prot = dhd->prot;
  208. int ret = -1;
  209. if (dhd->busstate == DHD_BUS_DOWN) {
  210. DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__));
  211. return ret;
  212. }
  213. dhd_os_proto_block(dhd);
  214. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  215. ASSERT(len <= WLC_IOCTL_MAXLEN);
  216. if (len > WLC_IOCTL_MAXLEN)
  217. goto done;
  218. if (prot->pending == TRUE) {
  219. DHD_TRACE(("CDC packet is pending!!!! cmd=0x%x (%lu) lastcmd=0x%x (%lu)\n",
  220. ioc->cmd, (unsigned long)ioc->cmd, prot->lastcmd,
  221. (unsigned long)prot->lastcmd));
  222. if ((ioc->cmd == WLC_SET_VAR) || (ioc->cmd == WLC_GET_VAR)) {
  223. DHD_TRACE(("iovar cmd=%s\n", (char*)buf));
  224. }
  225. goto done;
  226. }
  227. prot->pending = TRUE;
  228. prot->lastcmd = ioc->cmd;
  229. if (ioc->set)
  230. ret = dhdcdc_set_ioctl(dhd, ifidx, ioc->cmd, buf, len);
  231. else {
  232. ret = dhdcdc_query_ioctl(dhd, ifidx, ioc->cmd, buf, len);
  233. if (ret > 0)
  234. ioc->used = ret - sizeof(cdc_ioctl_t);
  235. }
  236. /* Too many programs assume ioctl() returns 0 on success */
  237. if (ret >= 0)
  238. ret = 0;
  239. else {
  240. cdc_ioctl_t *msg = &prot->msg;
  241. ioc->needed = ltoh32(msg->len); /* len == needed when set/query fails from dongle */
  242. }
  243. /* Intercept the wme_dp ioctl here */
  244. if ((!ret) && (ioc->cmd == WLC_SET_VAR) && (!strcmp(buf, "wme_dp"))) {
  245. int slen, val = 0;
  246. slen = strlen("wme_dp") + 1;
  247. if (len >= (int)(slen + sizeof(int)))
  248. bcopy(((char *)buf + slen), &val, sizeof(int));
  249. dhd->wme_dp = (uint8) ltoh32(val);
  250. }
  251. prot->pending = FALSE;
  252. done:
  253. dhd_os_proto_unblock(dhd);
  254. return ret;
  255. }
  256. int
  257. dhd_prot_iovar_op(dhd_pub_t *dhdp, const char *name,
  258. void *params, int plen, void *arg, int len, bool set)
  259. {
  260. return BCME_UNSUPPORTED;
  261. }
  262. void
  263. dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
  264. {
  265. bcm_bprintf(strbuf, "Protocol CDC: reqid %d\n", dhdp->prot->reqid);
  266. }
  267. void
  268. dhd_prot_hdrpush(dhd_pub_t *dhd, int ifidx, void *pktbuf)
  269. {
  270. #ifdef BDC
  271. struct bdc_header *h;
  272. #endif /* BDC */
  273. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  274. #ifdef BDC
  275. /* Push BDC header used to convey priority for buses that don't */
  276. PKTPUSH(dhd->osh, pktbuf, BDC_HEADER_LEN);
  277. h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
  278. h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
  279. if (PKTSUMNEEDED(pktbuf))
  280. h->flags |= BDC_FLAG_SUM_NEEDED;
  281. h->priority = (PKTPRIO(pktbuf) & BDC_PRIORITY_MASK);
  282. h->flags2 = 0;
  283. h->rssi = 0;
  284. #endif /* BDC */
  285. BDC_SET_IF_IDX(h, ifidx);
  286. }
  287. bool
  288. dhd_proto_fcinfo(dhd_pub_t *dhd, void *pktbuf, uint8 *fcbits)
  289. {
  290. #ifdef BDC
  291. struct bdc_header *h;
  292. if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) {
  293. DHD_ERROR(("%s: rx data too short (%d < %d)\n",
  294. __FUNCTION__, PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN));
  295. return BCME_ERROR;
  296. }
  297. h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
  298. *fcbits = h->priority >> BDC_PRIORITY_FC_SHIFT;
  299. if ((h->flags2 & BDC_FLAG2_FC_FLAG) == BDC_FLAG2_FC_FLAG)
  300. return TRUE;
  301. #endif
  302. return FALSE;
  303. }
  304. int
  305. dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pktbuf)
  306. {
  307. #ifdef BDC
  308. struct bdc_header *h;
  309. #endif
  310. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  311. #ifdef BDC
  312. /* Pop BDC header used to convey priority for buses that don't */
  313. if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) {
  314. DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
  315. PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN));
  316. return BCME_ERROR;
  317. }
  318. h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
  319. if ((*ifidx = BDC_GET_IF_IDX(h)) >= DHD_MAX_IFS) {
  320. DHD_ERROR(("%s: rx data ifnum out of range (%d)\n",
  321. __FUNCTION__, *ifidx));
  322. return BCME_ERROR;
  323. }
  324. if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) != BDC_PROTO_VER) {
  325. DHD_ERROR(("%s: non-BDC packet received, flags 0x%x\n",
  326. dhd_ifname(dhd, *ifidx), h->flags));
  327. return BCME_ERROR;
  328. }
  329. if (h->flags & BDC_FLAG_SUM_GOOD) {
  330. DHD_INFO(("%s: BDC packet received with good rx-csum, flags 0x%x\n",
  331. dhd_ifname(dhd, *ifidx), h->flags));
  332. PKTSETSUMGOOD(pktbuf, TRUE);
  333. }
  334. PKTSETPRIO(pktbuf, (h->priority & BDC_PRIORITY_MASK));
  335. PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN);
  336. #endif /* BDC */
  337. return 0;
  338. }
  339. int
  340. dhd_prot_attach(dhd_pub_t *dhd)
  341. {
  342. dhd_prot_t *cdc;
  343. #ifndef DHD_USE_STATIC_BUF
  344. if (!(cdc = (dhd_prot_t *)MALLOC(dhd->osh, sizeof(dhd_prot_t)))) {
  345. DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
  346. goto fail;
  347. }
  348. #else
  349. if (!(cdc = (dhd_prot_t *)dhd_os_prealloc(DHD_PREALLOC_PROT, sizeof(dhd_prot_t)))) {
  350. DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
  351. goto fail;
  352. }
  353. #endif /* DHD_USE_STATIC_BUF */
  354. memset(cdc, 0, sizeof(dhd_prot_t));
  355. /* ensure that the msg buf directly follows the cdc msg struct */
  356. if ((uintptr)(&cdc->msg + 1) != (uintptr)cdc->buf) {
  357. DHD_ERROR(("dhd_prot_t is not correctly defined\n"));
  358. goto fail;
  359. }
  360. dhd->prot = cdc;
  361. #ifdef BDC
  362. dhd->hdrlen += BDC_HEADER_LEN;
  363. #endif
  364. dhd->maxctl = WLC_IOCTL_MAXLEN + sizeof(cdc_ioctl_t) + ROUND_UP_MARGIN;
  365. return 0;
  366. fail:
  367. #ifndef DHD_USE_STATIC_BUF
  368. if (cdc != NULL)
  369. MFREE(dhd->osh, cdc, sizeof(dhd_prot_t));
  370. #endif
  371. return BCME_NOMEM;
  372. }
  373. /* ~NOTE~ What if another thread is waiting on the semaphore? Holding it? */
  374. void
  375. dhd_prot_detach(dhd_pub_t *dhd)
  376. {
  377. #ifndef DHD_USE_STATIC_BUF
  378. MFREE(dhd->osh, dhd->prot, sizeof(dhd_prot_t));
  379. #endif
  380. dhd->prot = NULL;
  381. }
  382. void
  383. dhd_prot_dstats(dhd_pub_t *dhd)
  384. {
  385. /* No stats from dongle added yet, copy bus stats */
  386. dhd->dstats.tx_packets = dhd->tx_packets;
  387. dhd->dstats.tx_errors = dhd->tx_errors;
  388. dhd->dstats.rx_packets = dhd->rx_packets;
  389. dhd->dstats.rx_errors = dhd->rx_errors;
  390. dhd->dstats.rx_dropped = dhd->rx_dropped;
  391. dhd->dstats.multicast = dhd->rx_multicast;
  392. return;
  393. }
  394. int
  395. dhd_prot_init(dhd_pub_t *dhd)
  396. {
  397. int ret = 0;
  398. char buf[128];
  399. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  400. dhd_os_proto_block(dhd);
  401. /* Get the device MAC address */
  402. strcpy(buf, "cur_etheraddr");
  403. ret = dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, buf, sizeof(buf));
  404. if (ret < 0) {
  405. dhd_os_proto_unblock(dhd);
  406. return ret;
  407. }
  408. memcpy(dhd->mac.octet, buf, ETHER_ADDR_LEN);
  409. dhd_os_proto_unblock(dhd);
  410. #ifdef EMBEDDED_PLATFORM
  411. ret = dhd_preinit_ioctls(dhd);
  412. #endif /* EMBEDDED_PLATFORM */
  413. /* Always assumes wl for now */
  414. dhd->iswl = TRUE;
  415. return ret;
  416. }
  417. void
  418. dhd_prot_stop(dhd_pub_t *dhd)
  419. {
  420. /* Nothing to do for CDC */
  421. }
  422. /* LGE_CHANGE_S, [yoohoo@lge.com], 2009-11-19, Use deepsleep instead of dhd_dev_reset when driver start or stop */
  423. #if defined(CONFIG_LGE_BCM432X_PATCH) && defined(CONFIG_BRCM_USE_DEEPSLEEP)
  424. extern dhd_pub_t * get_dhd_pub_from_dev(struct net_device *dev);
  425. int dhd_deep_sleep(struct net_device *dev, int flag)
  426. {
  427. dhd_pub_t *dhd_pub = get_dhd_pub_from_dev(dev);
  428. char iovbuf[20] = {0};
  429. uint powervar = 0;
  430. DHD_TRACE(("%s: Enter Flag -> %d \n", __FUNCTION__, flag));
  431. if(dhd_pub == NULL)
  432. return 0;
  433. switch(flag) {
  434. case 1: /* DEEPSLEEP ON*/
  435. printk(KERN_INFO "===== [WiFi] DEEP SLEEP ON =====\n");
  436. /* Disable MPC */
  437. powervar = 0;
  438. bcm_mkiovar("mpc", (char *)&powervar, 4, iovbuf, sizeof(iovbuf));
  439. dhdcdc_set_ioctl(dhd_pub, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
  440. /* Enable Deep Sleep */
  441. powervar = 1;
  442. bcm_mkiovar("deepsleep", (char *)&powervar, 4, iovbuf, sizeof(iovbuf));
  443. dhdcdc_set_ioctl(dhd_pub, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
  444. break;
  445. case 0: /*DEEPSLEEP OFF*/
  446. printk(KERN_INFO "===== [WiFi] DEEP SLEEP OFF =====\n");
  447. /* Disable Deep Sleep */
  448. powervar = 0;
  449. bcm_mkiovar("deepsleep", (char *)&powervar, 4, iovbuf, sizeof(iovbuf));
  450. dhdcdc_set_ioctl(dhd_pub, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
  451. /* Enable MPC */
  452. powervar = 1;
  453. bcm_mkiovar("mpc", (char *)&powervar, 4, iovbuf, sizeof(iovbuf));
  454. dhdcdc_set_ioctl(dhd_pub, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
  455. break;
  456. default:
  457. printk(KERN_ERR "[%s] Invalid Input Flag (%d)",__FUNCTION__, flag);
  458. }
  459. return 0;
  460. }
  461. #endif /* CONFIG_LGE_BCM432X_PATCH && CONFIG_BRCM_USE_DEEPSLEEP */
  462. /* LGE_CHANGE_E, [yoohoo@lge.com], 2009-11-19, Use deepsleep instead of dhd_dev_reset when driver start or stop */
  463. //by sjpark 100827 : merge }