dhd_cfg80211.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. /*
  2. * Linux cfg80211 driver - Dongle Host Driver (DHD) related
  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: wl_cfg80211.c,v 1.1.4.1.2.14 2011/02/09 01:40:07 Exp $
  25. */
  26. #include <linux/vmalloc.h>
  27. #include <net/rtnetlink.h>
  28. #include <bcmutils.h>
  29. #include <wldev_common.h>
  30. #include <wl_cfg80211.h>
  31. #include <brcm_nl80211.h>
  32. #include <dhd_cfg80211.h>
  33. #ifdef PKT_FILTER_SUPPORT
  34. #include <dngl_stats.h>
  35. #include <dhd.h>
  36. #endif
  37. extern struct bcm_cfg80211 *g_bcm_cfg;
  38. #ifdef PKT_FILTER_SUPPORT
  39. extern uint dhd_pkt_filter_enable;
  40. extern uint dhd_master_mode;
  41. extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode);
  42. #endif
  43. static int dhd_dongle_up = FALSE;
  44. #include <dngl_stats.h>
  45. #include <dhd.h>
  46. #include <dhdioctl.h>
  47. #include <wlioctl.h>
  48. #include <dhd_cfg80211.h>
  49. static s32 wl_dongle_up(struct net_device *ndev, u32 up);
  50. /**
  51. * Function implementations
  52. */
  53. s32 dhd_cfg80211_init(struct bcm_cfg80211 *cfg)
  54. {
  55. dhd_dongle_up = FALSE;
  56. return 0;
  57. }
  58. s32 dhd_cfg80211_deinit(struct bcm_cfg80211 *cfg)
  59. {
  60. dhd_dongle_up = FALSE;
  61. return 0;
  62. }
  63. s32 dhd_cfg80211_down(struct bcm_cfg80211 *cfg)
  64. {
  65. dhd_dongle_up = FALSE;
  66. return 0;
  67. }
  68. s32 dhd_cfg80211_set_p2p_info(struct bcm_cfg80211 *cfg, int val)
  69. {
  70. dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
  71. dhd->op_mode |= val;
  72. WL_ERR(("Set : op_mode=0x%04x\n", dhd->op_mode));
  73. #ifdef ARP_OFFLOAD_SUPPORT
  74. if (dhd->arp_version == 1) {
  75. /* IF P2P is enabled, disable arpoe */
  76. dhd_arp_offload_set(dhd, 0);
  77. dhd_arp_offload_enable(dhd, false);
  78. }
  79. #endif /* ARP_OFFLOAD_SUPPORT */
  80. return 0;
  81. }
  82. s32 dhd_cfg80211_clean_p2p_info(struct bcm_cfg80211 *cfg)
  83. {
  84. dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
  85. dhd->op_mode &= ~(DHD_FLAG_P2P_GC_MODE | DHD_FLAG_P2P_GO_MODE);
  86. WL_ERR(("Clean : op_mode=0x%04x\n", dhd->op_mode));
  87. #ifdef ARP_OFFLOAD_SUPPORT
  88. if (dhd->arp_version == 1) {
  89. /* IF P2P is disabled, enable arpoe back for STA mode. */
  90. dhd_arp_offload_set(dhd, dhd_arp_mode);
  91. dhd_arp_offload_enable(dhd, true);
  92. }
  93. #endif /* ARP_OFFLOAD_SUPPORT */
  94. return 0;
  95. }
  96. struct net_device* wl_cfg80211_allocate_if(struct bcm_cfg80211 *cfg, int ifidx, char *name,
  97. uint8 *mac, uint8 bssidx)
  98. {
  99. return dhd_allocate_if(cfg->pub, ifidx, name, mac, bssidx, FALSE);
  100. }
  101. int wl_cfg80211_register_if(struct bcm_cfg80211 *cfg, int ifidx, struct net_device* ndev)
  102. {
  103. return dhd_register_if(cfg->pub, ifidx, FALSE);
  104. }
  105. int wl_cfg80211_remove_if(struct bcm_cfg80211 *cfg, int ifidx, struct net_device* ndev)
  106. {
  107. return dhd_remove_if(cfg->pub, ifidx, FALSE);
  108. }
  109. struct net_device * dhd_cfg80211_netdev_free(struct net_device *ndev)
  110. {
  111. if (ndev) {
  112. if (ndev->ieee80211_ptr) {
  113. kfree(ndev->ieee80211_ptr);
  114. ndev->ieee80211_ptr = NULL;
  115. }
  116. free_netdev(ndev);
  117. return NULL;
  118. }
  119. return ndev;
  120. }
  121. void dhd_netdev_free(struct net_device *ndev)
  122. {
  123. #ifdef WL_CFG80211
  124. ndev = dhd_cfg80211_netdev_free(ndev);
  125. #endif
  126. if (ndev)
  127. free_netdev(ndev);
  128. }
  129. static s32 wl_dongle_up(struct net_device *ndev, u32 up)
  130. {
  131. s32 err = 0;
  132. err = wldev_ioctl(ndev, WLC_UP, &up, sizeof(up), true);
  133. if (unlikely(err)) {
  134. WL_ERR(("WLC_UP error (%d)\n", err));
  135. }
  136. return err;
  137. }
  138. s32 dhd_config_dongle(struct bcm_cfg80211 *cfg)
  139. {
  140. #ifndef DHD_SDALIGN
  141. #define DHD_SDALIGN 32
  142. #endif
  143. struct net_device *ndev;
  144. s32 err = 0;
  145. WL_TRACE(("In\n"));
  146. if (dhd_dongle_up) {
  147. WL_ERR(("Dongle is already up\n"));
  148. return err;
  149. }
  150. ndev = bcmcfg_to_prmry_ndev(cfg);
  151. err = wl_dongle_up(ndev, 0);
  152. if (unlikely(err)) {
  153. WL_ERR(("wl_dongle_up failed\n"));
  154. goto default_conf_out;
  155. }
  156. dhd_dongle_up = true;
  157. default_conf_out:
  158. return err;
  159. }
  160. #ifdef CONFIG_NL80211_TESTMODE
  161. #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0))
  162. int dhd_cfg80211_testmode_cmd(struct wiphy *wiphy, struct wireless_dev *wdev, void *data, int len)
  163. #else
  164. int dhd_cfg80211_testmode_cmd(struct wiphy *wiphy, void *data, int len)
  165. #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) */
  166. {
  167. struct sk_buff *reply;
  168. struct bcm_cfg80211 *cfg;
  169. dhd_pub_t *dhd;
  170. struct bcm_nlmsg_hdr *nlioc = data;
  171. dhd_ioctl_t ioc = { 0 };
  172. int err = 0;
  173. void *buf = NULL, *cur;
  174. u16 buflen;
  175. u16 maxmsglen = PAGE_SIZE - 0x100;
  176. bool newbuf = false;
  177. #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0))
  178. int8 index = 0;
  179. struct net_device *ndev = NULL;
  180. #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) */
  181. WL_TRACE(("entry: cmd = %d\n", nlioc->cmd));
  182. cfg = wiphy_priv(wiphy);
  183. dhd = cfg->pub;
  184. DHD_OS_WAKE_LOCK(dhd);
  185. /* send to dongle only if we are not waiting for reload already */
  186. if (dhd->hang_was_sent) {
  187. WL_ERR(("HANG was sent up earlier\n"));
  188. DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(dhd, DHD_EVENT_TIMEOUT_MS);
  189. DHD_OS_WAKE_UNLOCK(dhd);
  190. return OSL_ERROR(BCME_DONGLE_DOWN);
  191. }
  192. len -= sizeof(struct bcm_nlmsg_hdr);
  193. if (nlioc->len > 0) {
  194. if (nlioc->len <= len) {
  195. buf = (void *)nlioc + nlioc->offset;
  196. *(char *)(buf + nlioc->len) = '\0';
  197. } else {
  198. if (nlioc->len > DHD_IOCTL_MAXLEN)
  199. nlioc->len = DHD_IOCTL_MAXLEN;
  200. buf = vzalloc(nlioc->len);
  201. if (!buf)
  202. return -ENOMEM;
  203. newbuf = true;
  204. memcpy(buf, (void *)nlioc + nlioc->offset, len);
  205. *(char *)(buf + len) = '\0';
  206. }
  207. }
  208. #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0))
  209. ndev = wdev_to_wlc_ndev(wdev, cfg);
  210. index = dhd_net2idx(dhd->info, ndev);
  211. if (index == DHD_BAD_IF) {
  212. WL_ERR(("Bad ifidx from wdev:%p\n", wdev));
  213. return BCME_ERROR;
  214. }
  215. #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) */
  216. ioc.cmd = nlioc->cmd;
  217. ioc.len = nlioc->len;
  218. ioc.set = nlioc->set;
  219. ioc.driver = nlioc->magic;
  220. err = dhd_ioctl_process(dhd, 0, &ioc, buf);
  221. if (err) {
  222. WL_TRACE(("dhd_ioctl_process return err %d\n", err));
  223. err = OSL_ERROR(err);
  224. goto done;
  225. }
  226. cur = buf;
  227. while (nlioc->len > 0) {
  228. buflen = nlioc->len > maxmsglen ? maxmsglen : nlioc->len;
  229. nlioc->len -= buflen;
  230. reply = cfg80211_testmode_alloc_reply_skb(wiphy, buflen+4);
  231. if (!reply) {
  232. WL_ERR(("Failed to allocate reply msg\n"));
  233. err = -ENOMEM;
  234. break;
  235. }
  236. if (nla_put(reply, BCM_NLATTR_DATA, buflen, cur) ||
  237. nla_put_u16(reply, BCM_NLATTR_LEN, buflen)) {
  238. kfree_skb(reply);
  239. err = -ENOBUFS;
  240. break;
  241. }
  242. do {
  243. err = cfg80211_testmode_reply(reply);
  244. } while (err == -EAGAIN);
  245. if (err) {
  246. WL_ERR(("testmode reply failed:%d\n", err));
  247. break;
  248. }
  249. cur += buflen;
  250. }
  251. done:
  252. if (newbuf)
  253. vfree(buf);
  254. DHD_OS_WAKE_UNLOCK(dhd);
  255. return err;
  256. }
  257. #endif /* CONFIG_NL80211_TESTMODE */