/drivers/net/wireless/bcmdhd/dhd_linux.c

https://bitbucket.org/wisechild/galaxy-nexus · C · 5041 lines · 3804 code · 837 blank · 400 comment · 685 complexity · fe4e4f51f9d74a59db8f79ca55e59935 MD5 · raw file

Large files are truncated click here to view the full file

  1. /*
  2. * Broadcom Dongle Host Driver (DHD), Linux-specific network interface
  3. * Basically selected code segments from usb-cdc.c and usb-rndis.c
  4. *
  5. * Copyright (C) 1999-2011, Broadcom Corporation
  6. *
  7. * Unless you and Broadcom execute a separate written software license
  8. * agreement governing use of this software, this software is licensed to you
  9. * under the terms of the GNU General Public License version 2 (the "GPL"),
  10. * available at http://www.broadcom.com/licenses/GPLv2.php, with the
  11. * following added to such license:
  12. *
  13. * As a special exception, the copyright holders of this software give you
  14. * permission to link this software with independent modules, and to copy and
  15. * distribute the resulting executable under terms of your choice, provided that
  16. * you also meet, for each linked independent module, the terms and conditions of
  17. * the license of that module. An independent module is a module which is not
  18. * derived from this software. The special exception does not apply to any
  19. * modifications of the software.
  20. *
  21. * Notwithstanding the above, under no circumstances may you combine this
  22. * software in any way with any other Broadcom software provided under a license
  23. * other than the GPL, without Broadcom's express prior written consent.
  24. *
  25. * $Id: dhd_linux.c 287541 2011-10-03 23:48:17Z $
  26. */
  27. #include <typedefs.h>
  28. #include <linuxver.h>
  29. #include <osl.h>
  30. #include <linux/init.h>
  31. #include <linux/kernel.h>
  32. #include <linux/slab.h>
  33. #include <linux/skbuff.h>
  34. #include <linux/netdevice.h>
  35. #include <linux/inetdevice.h>
  36. #include <linux/rtnetlink.h>
  37. #include <linux/etherdevice.h>
  38. #include <linux/random.h>
  39. #include <linux/spinlock.h>
  40. #include <linux/ethtool.h>
  41. #include <linux/fcntl.h>
  42. #include <linux/fs.h>
  43. #include <asm/uaccess.h>
  44. #include <asm/unaligned.h>
  45. #include <epivers.h>
  46. #include <bcmutils.h>
  47. #include <bcmendian.h>
  48. #include <proto/ethernet.h>
  49. #include <dngl_stats.h>
  50. #include <dhd.h>
  51. #include <dhd_bus.h>
  52. #include <dhd_proto.h>
  53. #include <dhd_dbg.h>
  54. #ifdef CONFIG_HAS_WAKELOCK
  55. #include <linux/wakelock.h>
  56. #endif
  57. #ifdef WL_CFG80211
  58. #include <wl_cfg80211.h>
  59. #endif
  60. #include <proto/802.11_bta.h>
  61. #include <proto/bt_amp_hci.h>
  62. #include <dhd_bta.h>
  63. #ifdef WLMEDIA_HTSF
  64. #include <linux/time.h>
  65. #include <htsf.h>
  66. #define HTSF_MINLEN 200 /* min. packet length to timestamp */
  67. #define HTSF_BUS_DELAY 150 /* assume a fix propagation in us */
  68. #define TSMAX 1000 /* max no. of timing record kept */
  69. #define NUMBIN 34
  70. static uint32 tsidx = 0;
  71. static uint32 htsf_seqnum = 0;
  72. uint32 tsfsync;
  73. struct timeval tsync;
  74. static uint32 tsport = 5010;
  75. typedef struct histo_ {
  76. uint32 bin[NUMBIN];
  77. } histo_t;
  78. #if !ISPOWEROF2(DHD_SDALIGN)
  79. #error DHD_SDALIGN is not a power of 2!
  80. #endif
  81. static histo_t vi_d1, vi_d2, vi_d3, vi_d4;
  82. #endif /* WLMEDIA_HTSF */
  83. #if defined(SOFTAP)
  84. extern bool ap_cfg_running;
  85. extern bool ap_fw_loaded;
  86. #endif
  87. /* enable HOSTIP cache update from the host side when an eth0:N is up */
  88. #define AOE_IP_ALIAS_SUPPORT 1
  89. #ifdef PROP_TXSTATUS
  90. #include <wlfc_proto.h>
  91. #include <dhd_wlfc.h>
  92. #endif
  93. #include <wl_android.h>
  94. #ifdef ARP_OFFLOAD_SUPPORT
  95. static int dhd_device_event(struct notifier_block *this,
  96. unsigned long event,
  97. void *ptr);
  98. static struct notifier_block dhd_notifier = {
  99. .notifier_call = dhd_device_event
  100. };
  101. #endif /* ARP_OFFLOAD_SUPPORT */
  102. #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
  103. #include <linux/suspend.h>
  104. volatile bool dhd_mmc_suspend = FALSE;
  105. DECLARE_WAIT_QUEUE_HEAD(dhd_dpc_wait);
  106. #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
  107. #if defined(OOB_INTR_ONLY)
  108. extern void dhd_enable_oob_intr(struct dhd_bus *bus, bool enable);
  109. #endif /* defined(OOB_INTR_ONLY) */
  110. #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
  111. MODULE_LICENSE("GPL v2");
  112. #endif /* LinuxVer */
  113. #include <dhd_bus.h>
  114. #ifndef PROP_TXSTATUS
  115. #define DBUS_RX_BUFFER_SIZE_DHD(net) (net->mtu + net->hard_header_len + dhd->pub.hdrlen)
  116. #else
  117. #define DBUS_RX_BUFFER_SIZE_DHD(net) (net->mtu + net->hard_header_len + dhd->pub.hdrlen + 128)
  118. #endif
  119. #if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15)
  120. const char *
  121. print_tainted()
  122. {
  123. return "";
  124. }
  125. #endif /* LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15) */
  126. /* Linux wireless extension support */
  127. #if defined(CONFIG_WIRELESS_EXT)
  128. #include <wl_iw.h>
  129. extern wl_iw_extra_params_t g_wl_iw_params;
  130. #endif /* defined(CONFIG_WIRELESS_EXT) */
  131. #if defined(CONFIG_HAS_EARLYSUSPEND)
  132. #include <linux/earlysuspend.h>
  133. extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len);
  134. extern int dhd_get_dtim_skip(dhd_pub_t *dhd);
  135. #endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
  136. #ifdef PKT_FILTER_SUPPORT
  137. extern void dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg);
  138. extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode);
  139. #endif
  140. /* Interface control information */
  141. typedef struct dhd_if {
  142. struct dhd_info *info; /* back pointer to dhd_info */
  143. /* OS/stack specifics */
  144. struct net_device *net;
  145. struct net_device_stats stats;
  146. int idx; /* iface idx in dongle */
  147. int state; /* interface state */
  148. uint subunit; /* subunit */
  149. uint8 mac_addr[ETHER_ADDR_LEN]; /* assigned MAC address */
  150. bool attached; /* Delayed attachment when unset */
  151. bool txflowcontrol; /* Per interface flow control indicator */
  152. char name[IFNAMSIZ+1]; /* linux interface name */
  153. uint8 bssidx; /* bsscfg index for the interface */
  154. bool set_multicast;
  155. } dhd_if_t;
  156. #ifdef WLMEDIA_HTSF
  157. typedef struct {
  158. uint32 low;
  159. uint32 high;
  160. } tsf_t;
  161. typedef struct {
  162. uint32 last_cycle;
  163. uint32 last_sec;
  164. uint32 last_tsf;
  165. uint32 coef; /* scaling factor */
  166. uint32 coefdec1; /* first decimal */
  167. uint32 coefdec2; /* second decimal */
  168. } htsf_t;
  169. typedef struct {
  170. uint32 t1;
  171. uint32 t2;
  172. uint32 t3;
  173. uint32 t4;
  174. } tstamp_t;
  175. static tstamp_t ts[TSMAX];
  176. static tstamp_t maxdelayts;
  177. static uint32 maxdelay = 0, tspktcnt = 0, maxdelaypktno = 0;
  178. #endif /* WLMEDIA_HTSF */
  179. /* Local private structure (extension of pub) */
  180. typedef struct dhd_info {
  181. #if defined(CONFIG_WIRELESS_EXT)
  182. wl_iw_t iw; /* wireless extensions state (must be first) */
  183. #endif /* defined(CONFIG_WIRELESS_EXT) */
  184. dhd_pub_t pub;
  185. /* For supporting multiple interfaces */
  186. dhd_if_t *iflist[DHD_MAX_IFS];
  187. struct semaphore proto_sem;
  188. #ifdef PROP_TXSTATUS
  189. spinlock_t wlfc_spinlock;
  190. #endif /* PROP_TXSTATUS */
  191. #ifdef WLMEDIA_HTSF
  192. htsf_t htsf;
  193. #endif
  194. wait_queue_head_t ioctl_resp_wait;
  195. struct timer_list timer;
  196. bool wd_timer_valid;
  197. struct tasklet_struct tasklet;
  198. spinlock_t sdlock;
  199. spinlock_t txqlock;
  200. spinlock_t dhd_lock;
  201. #ifdef DHDTHREAD
  202. /* Thread based operation */
  203. bool threads_only;
  204. struct semaphore sdsem;
  205. tsk_ctl_t thr_dpc_ctl;
  206. tsk_ctl_t thr_wdt_ctl;
  207. #else
  208. bool dhd_tasklet_create;
  209. #endif /* DHDTHREAD */
  210. tsk_ctl_t thr_sysioc_ctl;
  211. /* Wakelocks */
  212. #if defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
  213. struct wake_lock wl_wifi; /* Wifi wakelock */
  214. struct wake_lock wl_rxwake; /* Wifi rx wakelock */
  215. #endif
  216. #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
  217. /* net_device interface lock, prevent race conditions among net_dev interface
  218. * calls and wifi_on or wifi_off
  219. */
  220. struct mutex dhd_net_if_mutex;
  221. #endif
  222. spinlock_t wakelock_spinlock;
  223. int wakelock_counter;
  224. int wakelock_timeout_enable;
  225. /* Thread to issue ioctl for multicast */
  226. bool set_macaddress;
  227. struct ether_addr macvalue;
  228. wait_queue_head_t ctrl_wait;
  229. atomic_t pend_8021x_cnt;
  230. dhd_attach_states_t dhd_state;
  231. #ifdef CONFIG_HAS_EARLYSUSPEND
  232. struct early_suspend early_suspend;
  233. #endif /* CONFIG_HAS_EARLYSUSPEND */
  234. } dhd_info_t;
  235. /* Definitions to provide path to the firmware and nvram
  236. * example nvram_path[MOD_PARAM_PATHLEN]="/projects/wlan/nvram.txt"
  237. */
  238. char firmware_path[MOD_PARAM_PATHLEN];
  239. char nvram_path[MOD_PARAM_PATHLEN];
  240. extern int wl_control_wl_start(struct net_device *dev);
  241. extern int net_os_send_hang_message(struct net_device *dev);
  242. #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
  243. struct semaphore dhd_registration_sem;
  244. #define DHD_REGISTRATION_TIMEOUT 12000 /* msec : allowed time to finished dhd registration */
  245. #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
  246. /* Spawn a thread for system ioctls (set mac, set mcast) */
  247. uint dhd_sysioc = TRUE;
  248. module_param(dhd_sysioc, uint, 0);
  249. /* Error bits */
  250. module_param(dhd_msg_level, int, 0);
  251. /* load firmware and/or nvram values from the filesystem */
  252. module_param_string(firmware_path, firmware_path, MOD_PARAM_PATHLEN, 0660);
  253. module_param_string(nvram_path, nvram_path, MOD_PARAM_PATHLEN, 0);
  254. /* Watchdog interval */
  255. uint dhd_watchdog_ms = 10;
  256. module_param(dhd_watchdog_ms, uint, 0);
  257. #if defined(DHD_DEBUG)
  258. /* Console poll interval */
  259. uint dhd_console_ms = 0;
  260. module_param(dhd_console_ms, uint, 0);
  261. #endif /* defined(DHD_DEBUG) */
  262. /* ARP offload agent mode : Enable ARP Host Auto-Reply and ARP Peer Auto-Reply */
  263. uint dhd_arp_mode = 0xb;
  264. module_param(dhd_arp_mode, uint, 0);
  265. /* ARP offload enable */
  266. uint dhd_arp_enable = TRUE;
  267. module_param(dhd_arp_enable, uint, 0);
  268. /* Global Pkt filter enable control */
  269. uint dhd_pkt_filter_enable = TRUE;
  270. module_param(dhd_pkt_filter_enable, uint, 0);
  271. /* Pkt filter init setup */
  272. uint dhd_pkt_filter_init = 0;
  273. module_param(dhd_pkt_filter_init, uint, 0);
  274. /* Pkt filter mode control */
  275. uint dhd_master_mode = TRUE;
  276. module_param(dhd_master_mode, uint, 1);
  277. #ifdef DHDTHREAD
  278. /* Watchdog thread priority, -1 to use kernel timer */
  279. int dhd_watchdog_prio = 97;
  280. module_param(dhd_watchdog_prio, int, 0);
  281. /* DPC thread priority, -1 to use tasklet */
  282. int dhd_dpc_prio = 98;
  283. module_param(dhd_dpc_prio, int, 0);
  284. /* DPC thread priority, -1 to use tasklet */
  285. extern int dhd_dongle_memsize;
  286. module_param(dhd_dongle_memsize, int, 0);
  287. #endif /* DHDTHREAD */
  288. /* Control fw roaming */
  289. uint dhd_roam_disable = 0;
  290. /* Control radio state */
  291. uint dhd_radio_up = 1;
  292. /* Network inteface name */
  293. char iface_name[IFNAMSIZ];
  294. module_param_string(iface_name, iface_name, IFNAMSIZ, 0);
  295. #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
  296. #define DAEMONIZE(a) daemonize(a); \
  297. allow_signal(SIGKILL); \
  298. allow_signal(SIGTERM);
  299. #else /* Linux 2.4 (w/o preemption patch) */
  300. #define RAISE_RX_SOFTIRQ() \
  301. cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ)
  302. #define DAEMONIZE(a) daemonize(); \
  303. do { if (a) \
  304. strncpy(current->comm, a, MIN(sizeof(current->comm), (strlen(a) + 1))); \
  305. } while (0);
  306. #endif /* LINUX_VERSION_CODE */
  307. #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
  308. #define BLOCKABLE() (!in_atomic())
  309. #else
  310. #define BLOCKABLE() (!in_interrupt())
  311. #endif
  312. /* The following are specific to the SDIO dongle */
  313. /* IOCTL response timeout */
  314. int dhd_ioctl_timeout_msec = IOCTL_RESP_TIMEOUT;
  315. /* Idle timeout for backplane clock */
  316. int dhd_idletime = DHD_IDLETIME_TICKS;
  317. module_param(dhd_idletime, int, 0);
  318. /* Use polling */
  319. uint dhd_poll = FALSE;
  320. module_param(dhd_poll, uint, 0);
  321. /* Use interrupts */
  322. uint dhd_intr = TRUE;
  323. module_param(dhd_intr, uint, 0);
  324. /* SDIO Drive Strength (in milliamps) */
  325. uint dhd_sdiod_drive_strength = 6;
  326. module_param(dhd_sdiod_drive_strength, uint, 0);
  327. /* Tx/Rx bounds */
  328. extern uint dhd_txbound;
  329. extern uint dhd_rxbound;
  330. module_param(dhd_txbound, uint, 0);
  331. module_param(dhd_rxbound, uint, 0);
  332. /* Deferred transmits */
  333. extern uint dhd_deferred_tx;
  334. module_param(dhd_deferred_tx, uint, 0);
  335. #ifdef BCMDBGFS
  336. extern void dhd_dbg_init(dhd_pub_t *dhdp);
  337. extern void dhd_dbg_remove(void);
  338. #endif /* BCMDBGFS */
  339. #ifdef SDTEST
  340. /* Echo packet generator (pkts/s) */
  341. uint dhd_pktgen = 0;
  342. module_param(dhd_pktgen, uint, 0);
  343. /* Echo packet len (0 => sawtooth, max 2040) */
  344. uint dhd_pktgen_len = 0;
  345. module_param(dhd_pktgen_len, uint, 0);
  346. #endif /* SDTEST */
  347. /* Version string to report */
  348. #ifdef DHD_DEBUG
  349. #ifndef SRCBASE
  350. #define SRCBASE "drivers/net/wireless/bcmdhd"
  351. #endif
  352. #define DHD_COMPILED "\nCompiled in " SRCBASE
  353. #else
  354. #define DHD_COMPILED
  355. #endif /* DHD_DEBUG */
  356. static char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR
  357. #ifdef DHD_DEBUG
  358. "\nCompiled in " SRCBASE " on " __DATE__ " at " __TIME__
  359. #endif
  360. ;
  361. static void dhd_net_if_lock_local(dhd_info_t *dhd);
  362. static void dhd_net_if_unlock_local(dhd_info_t *dhd);
  363. #ifdef WLMEDIA_HTSF
  364. void htsf_update(dhd_info_t *dhd, void *data);
  365. tsf_t prev_tsf, cur_tsf;
  366. uint32 dhd_get_htsf(dhd_info_t *dhd, int ifidx);
  367. static int dhd_ioctl_htsf_get(dhd_info_t *dhd, int ifidx);
  368. static void dhd_dump_latency(void);
  369. static void dhd_htsf_addtxts(dhd_pub_t *dhdp, void *pktbuf);
  370. static void dhd_htsf_addrxts(dhd_pub_t *dhdp, void *pktbuf);
  371. static void dhd_dump_htsfhisto(histo_t *his, char *s);
  372. #endif /* WLMEDIA_HTSF */
  373. /* Monitor interface */
  374. int dhd_monitor_init(void *dhd_pub);
  375. int dhd_monitor_uninit(void);
  376. #if defined(CONFIG_WIRELESS_EXT)
  377. struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev);
  378. #endif /* defined(CONFIG_WIRELESS_EXT) */
  379. static void dhd_dpc(ulong data);
  380. /* forward decl */
  381. extern int dhd_wait_pend8021x(struct net_device *dev);
  382. #ifdef TOE
  383. #ifndef BDC
  384. #error TOE requires BDC
  385. #endif /* !BDC */
  386. static int dhd_toe_get(dhd_info_t *dhd, int idx, uint32 *toe_ol);
  387. static int dhd_toe_set(dhd_info_t *dhd, int idx, uint32 toe_ol);
  388. #endif /* TOE */
  389. static int dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata,
  390. wl_event_msg_t *event_ptr, void **data_ptr);
  391. #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
  392. static int dhd_sleep_pm_callback(struct notifier_block *nfb, unsigned long action, void *ignored)
  393. {
  394. int ret = NOTIFY_DONE;
  395. switch (action) {
  396. case PM_HIBERNATION_PREPARE:
  397. case PM_SUSPEND_PREPARE:
  398. dhd_mmc_suspend = TRUE;
  399. ret = NOTIFY_OK;
  400. break;
  401. case PM_POST_HIBERNATION:
  402. case PM_POST_SUSPEND:
  403. dhd_mmc_suspend = FALSE;
  404. ret = NOTIFY_OK;
  405. break;
  406. }
  407. smp_mb();
  408. return ret;
  409. }
  410. static struct notifier_block dhd_sleep_pm_notifier = {
  411. .notifier_call = dhd_sleep_pm_callback,
  412. .priority = 0
  413. };
  414. extern int register_pm_notifier(struct notifier_block *nb);
  415. extern int unregister_pm_notifier(struct notifier_block *nb);
  416. #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
  417. static void dhd_set_packet_filter(int value, dhd_pub_t *dhd)
  418. {
  419. #ifdef PKT_FILTER_SUPPORT
  420. DHD_TRACE(("%s: %d\n", __FUNCTION__, value));
  421. /* 1 - Enable packet filter, only allow unicast packet to send up */
  422. /* 0 - Disable packet filter */
  423. if (dhd_pkt_filter_enable) {
  424. int i;
  425. for (i = 0; i < dhd->pktfilter_count; i++) {
  426. dhd_pktfilter_offload_set(dhd, dhd->pktfilter[i]);
  427. dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i],
  428. value, dhd_master_mode);
  429. }
  430. }
  431. #endif
  432. }
  433. #if defined(CONFIG_HAS_EARLYSUSPEND)
  434. static int dhd_set_suspend(int value, dhd_pub_t *dhd)
  435. {
  436. int power_mode = PM_FAST;
  437. /* wl_pkt_filter_enable_t enable_parm; */
  438. char iovbuf[32];
  439. int bcn_li_dtim = 3;
  440. uint roamvar = 1;
  441. DHD_TRACE(("%s: enter, value = %d in_suspend=%d\n",
  442. __FUNCTION__, value, dhd->in_suspend));
  443. if (dhd && dhd->up) {
  444. if (value && dhd->in_suspend) {
  445. /* Kernel suspended */
  446. DHD_ERROR(("%s: force extra Suspend setting \n", __FUNCTION__));
  447. dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode,
  448. sizeof(power_mode), TRUE, 0);
  449. /* Enable packet filter, only allow unicast packet to send up */
  450. dhd_set_packet_filter(1, dhd);
  451. /* If DTIM skip is set up as default, force it to wake
  452. * each third DTIM for better power savings. Note that
  453. * one side effect is a chance to miss BC/MC packet.
  454. */
  455. bcn_li_dtim = dhd_get_dtim_skip(dhd);
  456. bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim,
  457. 4, iovbuf, sizeof(iovbuf));
  458. dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
  459. /* Disable firmware roaming during suspend */
  460. bcm_mkiovar("roam_off", (char *)&roamvar, 4,
  461. iovbuf, sizeof(iovbuf));
  462. dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
  463. } else {
  464. /* Kernel resumed */
  465. DHD_TRACE(("%s: Remove extra suspend setting \n", __FUNCTION__));
  466. power_mode = PM_FAST;
  467. dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode,
  468. sizeof(power_mode), TRUE, 0);
  469. /* disable pkt filter */
  470. dhd_set_packet_filter(0, dhd);
  471. /* restore pre-suspend setting for dtim_skip */
  472. bcm_mkiovar("bcn_li_dtim", (char *)&dhd->dtim_skip,
  473. 4, iovbuf, sizeof(iovbuf));
  474. dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
  475. roamvar = dhd_roam_disable;
  476. bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf,
  477. sizeof(iovbuf));
  478. dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
  479. }
  480. }
  481. return 0;
  482. }
  483. static void dhd_suspend_resume_helper(struct dhd_info *dhd, int val)
  484. {
  485. dhd_pub_t *dhdp = &dhd->pub;
  486. DHD_OS_WAKE_LOCK(dhdp);
  487. /* Set flag when early suspend was called */
  488. dhdp->in_suspend = val;
  489. if (!dhdp->suspend_disable_flag)
  490. dhd_set_suspend(val, dhdp);
  491. DHD_OS_WAKE_UNLOCK(dhdp);
  492. }
  493. static void dhd_early_suspend(struct early_suspend *h)
  494. {
  495. struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend);
  496. DHD_TRACE(("%s: enter\n", __FUNCTION__));
  497. if (dhd)
  498. dhd_suspend_resume_helper(dhd, 1);
  499. }
  500. static void dhd_late_resume(struct early_suspend *h)
  501. {
  502. struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend);
  503. DHD_TRACE(("%s: enter\n", __FUNCTION__));
  504. if (dhd)
  505. dhd_suspend_resume_helper(dhd, 0);
  506. }
  507. #endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
  508. /*
  509. * Generalized timeout mechanism. Uses spin sleep with exponential back-off until
  510. * the sleep time reaches one jiffy, then switches over to task delay. Usage:
  511. *
  512. * dhd_timeout_start(&tmo, usec);
  513. * while (!dhd_timeout_expired(&tmo))
  514. * if (poll_something())
  515. * break;
  516. * if (dhd_timeout_expired(&tmo))
  517. * fatal();
  518. */
  519. void
  520. dhd_timeout_start(dhd_timeout_t *tmo, uint usec)
  521. {
  522. tmo->limit = usec;
  523. tmo->increment = 0;
  524. tmo->elapsed = 0;
  525. tmo->tick = 1000000 / HZ;
  526. }
  527. int
  528. dhd_timeout_expired(dhd_timeout_t *tmo)
  529. {
  530. /* Does nothing the first call */
  531. if (tmo->increment == 0) {
  532. tmo->increment = 1;
  533. return 0;
  534. }
  535. if (tmo->elapsed >= tmo->limit)
  536. return 1;
  537. /* Add the delay that's about to take place */
  538. tmo->elapsed += tmo->increment;
  539. if (tmo->increment < tmo->tick) {
  540. OSL_DELAY(tmo->increment);
  541. tmo->increment *= 2;
  542. if (tmo->increment > tmo->tick)
  543. tmo->increment = tmo->tick;
  544. } else {
  545. wait_queue_head_t delay_wait;
  546. DECLARE_WAITQUEUE(wait, current);
  547. int pending;
  548. init_waitqueue_head(&delay_wait);
  549. add_wait_queue(&delay_wait, &wait);
  550. set_current_state(TASK_INTERRUPTIBLE);
  551. schedule_timeout(1);
  552. pending = signal_pending(current);
  553. remove_wait_queue(&delay_wait, &wait);
  554. set_current_state(TASK_RUNNING);
  555. if (pending)
  556. return 1; /* Interrupted */
  557. }
  558. return 0;
  559. }
  560. int
  561. dhd_net2idx(dhd_info_t *dhd, struct net_device *net)
  562. {
  563. int i = 0;
  564. ASSERT(dhd);
  565. while (i < DHD_MAX_IFS) {
  566. if (dhd->iflist[i] && (dhd->iflist[i]->net == net))
  567. return i;
  568. i++;
  569. }
  570. return DHD_BAD_IF;
  571. }
  572. struct net_device * dhd_idx2net(struct dhd_pub *dhd_pub, int ifidx)
  573. {
  574. struct dhd_info *dhd_info;
  575. if (!dhd_pub || ifidx < 0 || ifidx >= DHD_MAX_IFS)
  576. return NULL;
  577. dhd_info = dhd_pub->info;
  578. if (dhd_info && dhd_info->iflist[ifidx])
  579. return dhd_info->iflist[ifidx]->net;
  580. return NULL;
  581. }
  582. int
  583. dhd_ifname2idx(dhd_info_t *dhd, char *name)
  584. {
  585. int i = DHD_MAX_IFS;
  586. ASSERT(dhd);
  587. if (name == NULL || *name == '\0')
  588. return 0;
  589. while (--i > 0)
  590. if (dhd->iflist[i] && !strncmp(dhd->iflist[i]->name, name, IFNAMSIZ))
  591. break;
  592. DHD_TRACE(("%s: return idx %d for \"%s\"\n", __FUNCTION__, i, name));
  593. return i; /* default - the primary interface */
  594. }
  595. char *
  596. dhd_ifname(dhd_pub_t *dhdp, int ifidx)
  597. {
  598. dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
  599. ASSERT(dhd);
  600. if (ifidx < 0 || ifidx >= DHD_MAX_IFS) {
  601. DHD_ERROR(("%s: ifidx %d out of range\n", __FUNCTION__, ifidx));
  602. return "<if_bad>";
  603. }
  604. if (dhd->iflist[ifidx] == NULL) {
  605. DHD_ERROR(("%s: null i/f %d\n", __FUNCTION__, ifidx));
  606. return "<if_null>";
  607. }
  608. if (dhd->iflist[ifidx]->net)
  609. return dhd->iflist[ifidx]->net->name;
  610. return "<if_none>";
  611. }
  612. uint8 *
  613. dhd_bssidx2bssid(dhd_pub_t *dhdp, int idx)
  614. {
  615. int i;
  616. dhd_info_t *dhd = (dhd_info_t *)dhdp;
  617. ASSERT(dhd);
  618. for (i = 0; i < DHD_MAX_IFS; i++)
  619. if (dhd->iflist[i] && dhd->iflist[i]->bssidx == idx)
  620. return dhd->iflist[i]->mac_addr;
  621. return NULL;
  622. }
  623. static void
  624. _dhd_set_multicast_list(dhd_info_t *dhd, int ifidx)
  625. {
  626. struct net_device *dev;
  627. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
  628. struct netdev_hw_addr *ha;
  629. #else
  630. struct dev_mc_list *mclist;
  631. #endif
  632. uint32 allmulti, cnt;
  633. wl_ioctl_t ioc;
  634. char *buf, *bufp;
  635. uint buflen;
  636. int ret;
  637. ASSERT(dhd && dhd->iflist[ifidx]);
  638. dev = dhd->iflist[ifidx]->net;
  639. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
  640. netif_addr_lock_bh(dev);
  641. #endif
  642. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
  643. cnt = netdev_mc_count(dev);
  644. #else
  645. cnt = dev->mc_count;
  646. #endif
  647. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
  648. netif_addr_unlock_bh(dev);
  649. #endif
  650. /* Determine initial value of allmulti flag */
  651. allmulti = (dev->flags & IFF_ALLMULTI) ? TRUE : FALSE;
  652. /* Send down the multicast list first. */
  653. buflen = sizeof("mcast_list") + sizeof(cnt) + (cnt * ETHER_ADDR_LEN);
  654. if (!(bufp = buf = MALLOC(dhd->pub.osh, buflen))) {
  655. DHD_ERROR(("%s: out of memory for mcast_list, cnt %d\n",
  656. dhd_ifname(&dhd->pub, ifidx), cnt));
  657. return;
  658. }
  659. strcpy(bufp, "mcast_list");
  660. bufp += strlen("mcast_list") + 1;
  661. cnt = htol32(cnt);
  662. memcpy(bufp, &cnt, sizeof(cnt));
  663. bufp += sizeof(cnt);
  664. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
  665. netif_addr_lock_bh(dev);
  666. #endif
  667. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
  668. netdev_for_each_mc_addr(ha, dev) {
  669. if (!cnt)
  670. break;
  671. memcpy(bufp, ha->addr, ETHER_ADDR_LEN);
  672. bufp += ETHER_ADDR_LEN;
  673. cnt--;
  674. }
  675. #else
  676. for (mclist = dev->mc_list; (mclist && (cnt > 0)); cnt--, mclist = mclist->next) {
  677. memcpy(bufp, (void *)mclist->dmi_addr, ETHER_ADDR_LEN);
  678. bufp += ETHER_ADDR_LEN;
  679. }
  680. #endif
  681. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
  682. netif_addr_unlock_bh(dev);
  683. #endif
  684. memset(&ioc, 0, sizeof(ioc));
  685. ioc.cmd = WLC_SET_VAR;
  686. ioc.buf = buf;
  687. ioc.len = buflen;
  688. ioc.set = TRUE;
  689. ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
  690. if (ret < 0) {
  691. DHD_ERROR(("%s: set mcast_list failed, cnt %d\n",
  692. dhd_ifname(&dhd->pub, ifidx), cnt));
  693. allmulti = cnt ? TRUE : allmulti;
  694. }
  695. MFREE(dhd->pub.osh, buf, buflen);
  696. /* Now send the allmulti setting. This is based on the setting in the
  697. * net_device flags, but might be modified above to be turned on if we
  698. * were trying to set some addresses and dongle rejected it...
  699. */
  700. buflen = sizeof("allmulti") + sizeof(allmulti);
  701. if (!(buf = MALLOC(dhd->pub.osh, buflen))) {
  702. DHD_ERROR(("%s: out of memory for allmulti\n", dhd_ifname(&dhd->pub, ifidx)));
  703. return;
  704. }
  705. allmulti = htol32(allmulti);
  706. if (!bcm_mkiovar("allmulti", (void*)&allmulti, sizeof(allmulti), buf, buflen)) {
  707. DHD_ERROR(("%s: mkiovar failed for allmulti, datalen %d buflen %u\n",
  708. dhd_ifname(&dhd->pub, ifidx), (int)sizeof(allmulti), buflen));
  709. MFREE(dhd->pub.osh, buf, buflen);
  710. return;
  711. }
  712. memset(&ioc, 0, sizeof(ioc));
  713. ioc.cmd = WLC_SET_VAR;
  714. ioc.buf = buf;
  715. ioc.len = buflen;
  716. ioc.set = TRUE;
  717. ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
  718. if (ret < 0) {
  719. DHD_ERROR(("%s: set allmulti %d failed\n",
  720. dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti)));
  721. }
  722. MFREE(dhd->pub.osh, buf, buflen);
  723. /* Finally, pick up the PROMISC flag as well, like the NIC driver does */
  724. allmulti = (dev->flags & IFF_PROMISC) ? TRUE : FALSE;
  725. allmulti = htol32(allmulti);
  726. memset(&ioc, 0, sizeof(ioc));
  727. ioc.cmd = WLC_SET_PROMISC;
  728. ioc.buf = &allmulti;
  729. ioc.len = sizeof(allmulti);
  730. ioc.set = TRUE;
  731. ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
  732. if (ret < 0) {
  733. DHD_ERROR(("%s: set promisc %d failed\n",
  734. dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti)));
  735. }
  736. }
  737. static int
  738. _dhd_set_mac_address(dhd_info_t *dhd, int ifidx, struct ether_addr *addr)
  739. {
  740. char buf[32];
  741. wl_ioctl_t ioc;
  742. int ret;
  743. if (!bcm_mkiovar("cur_etheraddr", (char*)addr, ETHER_ADDR_LEN, buf, 32)) {
  744. DHD_ERROR(("%s: mkiovar failed for cur_etheraddr\n", dhd_ifname(&dhd->pub, ifidx)));
  745. return -1;
  746. }
  747. memset(&ioc, 0, sizeof(ioc));
  748. ioc.cmd = WLC_SET_VAR;
  749. ioc.buf = buf;
  750. ioc.len = 32;
  751. ioc.set = TRUE;
  752. ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
  753. if (ret < 0) {
  754. DHD_ERROR(("%s: set cur_etheraddr failed\n", dhd_ifname(&dhd->pub, ifidx)));
  755. } else {
  756. memcpy(dhd->iflist[ifidx]->net->dev_addr, addr, ETHER_ADDR_LEN);
  757. }
  758. return ret;
  759. }
  760. #ifdef SOFTAP
  761. extern struct net_device *ap_net_dev;
  762. extern tsk_ctl_t ap_eth_ctl; /* ap netdev heper thread ctl */
  763. #endif
  764. static void
  765. dhd_op_if(dhd_if_t *ifp)
  766. {
  767. dhd_info_t *dhd;
  768. int ret = 0, err = 0;
  769. #ifdef SOFTAP
  770. unsigned long flags;
  771. #endif
  772. ASSERT(ifp && ifp->info && ifp->idx); /* Virtual interfaces only */
  773. dhd = ifp->info;
  774. DHD_TRACE(("%s: idx %d, state %d\n", __FUNCTION__, ifp->idx, ifp->state));
  775. #ifdef WL_CFG80211
  776. if (wl_cfg80211_is_progress_ifchange())
  777. return;
  778. #endif
  779. switch (ifp->state) {
  780. case WLC_E_IF_ADD:
  781. /*
  782. * Delete the existing interface before overwriting it
  783. * in case we missed the WLC_E_IF_DEL event.
  784. */
  785. if (ifp->net != NULL) {
  786. DHD_ERROR(("%s: ERROR: netdev:%s already exists, try free & unregister \n",
  787. __FUNCTION__, ifp->net->name));
  788. netif_stop_queue(ifp->net);
  789. unregister_netdev(ifp->net);
  790. free_netdev(ifp->net);
  791. }
  792. /* Allocate etherdev, including space for private structure */
  793. if (!(ifp->net = alloc_etherdev(sizeof(dhd)))) {
  794. DHD_ERROR(("%s: OOM - alloc_etherdev\n", __FUNCTION__));
  795. ret = -ENOMEM;
  796. }
  797. if (ret == 0) {
  798. strncpy(ifp->net->name, ifp->name, IFNAMSIZ);
  799. ifp->net->name[IFNAMSIZ - 1] = '\0';
  800. memcpy(netdev_priv(ifp->net), &dhd, sizeof(dhd));
  801. #ifdef WL_CFG80211
  802. if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)
  803. if (!wl_cfg80211_notify_ifadd(ifp->net, ifp->idx, ifp->bssidx,
  804. dhd_net_attach)) {
  805. ifp->state = 0;
  806. return;
  807. }
  808. #endif
  809. if ((err = dhd_net_attach(&dhd->pub, ifp->idx)) != 0) {
  810. DHD_ERROR(("%s: dhd_net_attach failed, err %d\n",
  811. __FUNCTION__, err));
  812. ret = -EOPNOTSUPP;
  813. } else {
  814. #if defined(SOFTAP)
  815. if (ap_fw_loaded && !(dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)) {
  816. /* semaphore that the soft AP CODE waits on */
  817. flags = dhd_os_spin_lock(&dhd->pub);
  818. /* save ptr to wl0.1 netdev for use in wl_iw.c */
  819. ap_net_dev = ifp->net;
  820. /* signal to the SOFTAP 'sleeper' thread, wl0.1 is ready */
  821. up(&ap_eth_ctl.sema);
  822. dhd_os_spin_unlock(&dhd->pub, flags);
  823. }
  824. #endif
  825. DHD_TRACE(("\n ==== pid:%x, net_device for if:%s created ===\n\n",
  826. current->pid, ifp->net->name));
  827. ifp->state = 0;
  828. }
  829. }
  830. break;
  831. case WLC_E_IF_DEL:
  832. if (ifp->net != NULL) {
  833. DHD_TRACE(("\n%s: got 'WLC_E_IF_DEL' state\n", __FUNCTION__));
  834. #ifdef WL_CFG80211
  835. wl_cfg80211_ifdel_ops(ifp->net);
  836. #endif
  837. netif_stop_queue(ifp->net);
  838. unregister_netdev(ifp->net);
  839. ret = DHD_DEL_IF; /* Make sure the free_netdev() is called */
  840. }
  841. break;
  842. default:
  843. DHD_ERROR(("%s: bad op %d\n", __FUNCTION__, ifp->state));
  844. ASSERT(!ifp->state);
  845. break;
  846. }
  847. if (ret < 0) {
  848. ifp->set_multicast = FALSE;
  849. if (ifp->net) {
  850. free_netdev(ifp->net);
  851. }
  852. dhd->iflist[ifp->idx] = NULL;
  853. #ifdef WL_CFG80211
  854. if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) {
  855. wl_cfg80211_notify_ifdel(ifp->net);
  856. }
  857. #endif
  858. #ifdef SOFTAP
  859. flags = dhd_os_spin_lock(&dhd->pub);
  860. if (ifp->net == ap_net_dev)
  861. ap_net_dev = NULL; /* NULL SOFTAP global wl0.1 as well */
  862. dhd_os_spin_unlock(&dhd->pub, flags);
  863. #endif /* SOFTAP */
  864. MFREE(dhd->pub.osh, ifp, sizeof(*ifp));
  865. }
  866. }
  867. static int
  868. _dhd_sysioc_thread(void *data)
  869. {
  870. tsk_ctl_t *tsk = (tsk_ctl_t *)data;
  871. dhd_info_t *dhd = (dhd_info_t *)tsk->parent;
  872. int i;
  873. #ifdef SOFTAP
  874. bool in_ap = FALSE;
  875. unsigned long flags;
  876. #endif
  877. DAEMONIZE("dhd_sysioc");
  878. complete(&tsk->completed);
  879. while (down_interruptible(&tsk->sema) == 0) {
  880. SMP_RD_BARRIER_DEPENDS();
  881. if (tsk->terminated) {
  882. break;
  883. }
  884. dhd_net_if_lock_local(dhd);
  885. DHD_OS_WAKE_LOCK(&dhd->pub);
  886. for (i = 0; i < DHD_MAX_IFS; i++) {
  887. if (dhd->iflist[i]) {
  888. DHD_TRACE(("%s: interface %d\n", __FUNCTION__, i));
  889. #ifdef SOFTAP
  890. flags = dhd_os_spin_lock(&dhd->pub);
  891. in_ap = (ap_net_dev != NULL);
  892. dhd_os_spin_unlock(&dhd->pub, flags);
  893. #endif /* SOFTAP */
  894. if (dhd->iflist[i] && dhd->iflist[i]->state)
  895. dhd_op_if(dhd->iflist[i]);
  896. if (dhd->iflist[i] == NULL) {
  897. DHD_TRACE(("\n\n %s: interface %d just been removed,"
  898. "!\n\n", __FUNCTION__, i));
  899. continue;
  900. }
  901. #ifdef SOFTAP
  902. if (in_ap && dhd->set_macaddress) {
  903. DHD_TRACE(("attempt to set MAC for %s in AP Mode,"
  904. "blocked. \n", dhd->iflist[i]->net->name));
  905. dhd->set_macaddress = FALSE;
  906. continue;
  907. }
  908. if (in_ap && dhd->iflist[i]->set_multicast) {
  909. DHD_TRACE(("attempt to set MULTICAST list for %s"
  910. "in AP Mode, blocked. \n", dhd->iflist[i]->net->name));
  911. dhd->iflist[i]->set_multicast = FALSE;
  912. continue;
  913. }
  914. #endif /* SOFTAP */
  915. if (dhd->iflist[i]->set_multicast) {
  916. dhd->iflist[i]->set_multicast = FALSE;
  917. _dhd_set_multicast_list(dhd, i);
  918. }
  919. if (dhd->set_macaddress) {
  920. dhd->set_macaddress = FALSE;
  921. _dhd_set_mac_address(dhd, i, &dhd->macvalue);
  922. }
  923. }
  924. }
  925. DHD_OS_WAKE_UNLOCK(&dhd->pub);
  926. dhd_net_if_unlock_local(dhd);
  927. }
  928. DHD_TRACE(("%s: stopped\n", __FUNCTION__));
  929. complete_and_exit(&tsk->completed, 0);
  930. }
  931. static int
  932. dhd_set_mac_address(struct net_device *dev, void *addr)
  933. {
  934. int ret = 0;
  935. dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
  936. struct sockaddr *sa = (struct sockaddr *)addr;
  937. int ifidx;
  938. ifidx = dhd_net2idx(dhd, dev);
  939. if (ifidx == DHD_BAD_IF)
  940. return -1;
  941. ASSERT(&dhd->thr_sysioc_ctl.thr_pid >= 0);
  942. memcpy(&dhd->macvalue, sa->sa_data, ETHER_ADDR_LEN);
  943. dhd->set_macaddress = TRUE;
  944. up(&dhd->thr_sysioc_ctl.sema);
  945. return ret;
  946. }
  947. static void
  948. dhd_set_multicast_list(struct net_device *dev)
  949. {
  950. dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
  951. int ifidx;
  952. ifidx = dhd_net2idx(dhd, dev);
  953. if (ifidx == DHD_BAD_IF)
  954. return;
  955. ASSERT(&dhd->thr_sysioc_ctl.thr_pid >= 0);
  956. dhd->iflist[ifidx]->set_multicast = TRUE;
  957. up(&dhd->thr_sysioc_ctl.sema);
  958. }
  959. #ifdef PROP_TXSTATUS
  960. int
  961. dhd_os_wlfc_block(dhd_pub_t *pub)
  962. {
  963. dhd_info_t *di = (dhd_info_t *)(pub->info);
  964. ASSERT(di != NULL);
  965. spin_lock_bh(&di->wlfc_spinlock);
  966. return 1;
  967. }
  968. int
  969. dhd_os_wlfc_unblock(dhd_pub_t *pub)
  970. {
  971. dhd_info_t *di = (dhd_info_t *)(pub->info);
  972. ASSERT(di != NULL);
  973. spin_unlock_bh(&di->wlfc_spinlock);
  974. return 1;
  975. }
  976. const uint8 wme_fifo2ac[] = { 0, 1, 2, 3, 1, 1 };
  977. uint8 prio2fifo[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
  978. #define WME_PRIO2AC(prio) wme_fifo2ac[prio2fifo[(prio)]]
  979. #endif /* PROP_TXSTATUS */
  980. int
  981. dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf)
  982. {
  983. int ret;
  984. dhd_info_t *dhd = (dhd_info_t *)(dhdp->info);
  985. struct ether_header *eh = NULL;
  986. /* Reject if down */
  987. if (!dhdp->up || (dhdp->busstate == DHD_BUS_DOWN)) {
  988. /* free the packet here since the caller won't */
  989. PKTFREE(dhdp->osh, pktbuf, TRUE);
  990. return -ENODEV;
  991. }
  992. /* Update multicast statistic */
  993. if (PKTLEN(dhdp->osh, pktbuf) >= ETHER_ADDR_LEN) {
  994. uint8 *pktdata = (uint8 *)PKTDATA(dhdp->osh, pktbuf);
  995. eh = (struct ether_header *)pktdata;
  996. if (ETHER_ISMULTI(eh->ether_dhost))
  997. dhdp->tx_multicast++;
  998. if (ntoh16(eh->ether_type) == ETHER_TYPE_802_1X)
  999. atomic_inc(&dhd->pend_8021x_cnt);
  1000. }
  1001. /* Look into the packet and update the packet priority */
  1002. if (PKTPRIO(pktbuf) == 0)
  1003. pktsetprio(pktbuf, FALSE);
  1004. #ifdef PROP_TXSTATUS
  1005. if (dhdp->wlfc_state) {
  1006. /* store the interface ID */
  1007. DHD_PKTTAG_SETIF(PKTTAG(pktbuf), ifidx);
  1008. /* store destination MAC in the tag as well */
  1009. DHD_PKTTAG_SETDSTN(PKTTAG(pktbuf), eh->ether_dhost);
  1010. /* decide which FIFO this packet belongs to */
  1011. if (ETHER_ISMULTI(eh->ether_dhost))
  1012. /* one additional queue index (highest AC + 1) is used for bc/mc queue */
  1013. DHD_PKTTAG_SETFIFO(PKTTAG(pktbuf), AC_COUNT);
  1014. else
  1015. DHD_PKTTAG_SETFIFO(PKTTAG(pktbuf), WME_PRIO2AC(PKTPRIO(pktbuf)));
  1016. } else
  1017. #endif /* PROP_TXSTATUS */
  1018. /* If the protocol uses a data header, apply it */
  1019. dhd_prot_hdrpush(dhdp, ifidx, pktbuf);
  1020. /* Use bus module to send data frame */
  1021. #ifdef WLMEDIA_HTSF
  1022. dhd_htsf_addtxts(dhdp, pktbuf);
  1023. #endif
  1024. #ifdef PROP_TXSTATUS
  1025. if (dhdp->wlfc_state && ((athost_wl_status_info_t*)dhdp->wlfc_state)->proptxstatus_mode
  1026. != WLFC_FCMODE_NONE) {
  1027. dhd_os_wlfc_block(dhdp);
  1028. ret = dhd_wlfc_enque_sendq(dhdp->wlfc_state, DHD_PKTTAG_FIFO(PKTTAG(pktbuf)),
  1029. pktbuf);
  1030. dhd_wlfc_commit_packets(dhdp->wlfc_state, (f_commitpkt_t)dhd_bus_txdata,
  1031. dhdp->bus);
  1032. if (((athost_wl_status_info_t*)dhdp->wlfc_state)->toggle_host_if) {
  1033. ((athost_wl_status_info_t*)dhdp->wlfc_state)->toggle_host_if = 0;
  1034. }
  1035. dhd_os_wlfc_unblock(dhdp);
  1036. }
  1037. else
  1038. /* non-proptxstatus way */
  1039. ret = dhd_bus_txdata(dhdp->bus, pktbuf);
  1040. #else
  1041. ret = dhd_bus_txdata(dhdp->bus, pktbuf);
  1042. #endif /* PROP_TXSTATUS */
  1043. return ret;
  1044. }
  1045. int
  1046. dhd_start_xmit(struct sk_buff *skb, struct net_device *net)
  1047. {
  1048. int ret;
  1049. void *pktbuf;
  1050. dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
  1051. int ifidx;
  1052. #ifdef WLMEDIA_HTSF
  1053. uint8 htsfdlystat_sz = dhd->pub.htsfdlystat_sz;
  1054. #else
  1055. uint8 htsfdlystat_sz = 0;
  1056. #endif
  1057. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  1058. DHD_OS_WAKE_LOCK(&dhd->pub);
  1059. /* Reject if down */
  1060. if (!dhd->pub.up || (dhd->pub.busstate == DHD_BUS_DOWN)) {
  1061. DHD_ERROR(("%s: xmit rejected pub.up=%d busstate=%d \n",
  1062. __FUNCTION__, dhd->pub.up, dhd->pub.busstate));
  1063. netif_stop_queue(net);
  1064. /* Send Event when bus down detected during data session */
  1065. if (dhd->pub.busstate == DHD_BUS_DOWN) {
  1066. DHD_ERROR(("%s: Event HANG sent up\n", __FUNCTION__));
  1067. net_os_send_hang_message(net);
  1068. }
  1069. DHD_OS_WAKE_UNLOCK(&dhd->pub);
  1070. return -ENODEV;
  1071. }
  1072. ifidx = dhd_net2idx(dhd, net);
  1073. if (ifidx == DHD_BAD_IF) {
  1074. DHD_ERROR(("%s: bad ifidx %d\n", __FUNCTION__, ifidx));
  1075. netif_stop_queue(net);
  1076. DHD_OS_WAKE_UNLOCK(&dhd->pub);
  1077. return -ENODEV;
  1078. }
  1079. /* Make sure there's enough room for any header */
  1080. if (skb_headroom(skb) < dhd->pub.hdrlen + htsfdlystat_sz) {
  1081. struct sk_buff *skb2;
  1082. DHD_INFO(("%s: insufficient headroom\n",
  1083. dhd_ifname(&dhd->pub, ifidx)));
  1084. dhd->pub.tx_realloc++;
  1085. skb2 = skb_realloc_headroom(skb, dhd->pub.hdrlen + htsfdlystat_sz);
  1086. dev_kfree_skb(skb);
  1087. if ((skb = skb2) == NULL) {
  1088. DHD_ERROR(("%s: skb_realloc_headroom failed\n",
  1089. dhd_ifname(&dhd->pub, ifidx)));
  1090. ret = -ENOMEM;
  1091. goto done;
  1092. }
  1093. }
  1094. /* Convert to packet */
  1095. if (!(pktbuf = PKTFRMNATIVE(dhd->pub.osh, skb))) {
  1096. DHD_ERROR(("%s: PKTFRMNATIVE failed\n",
  1097. dhd_ifname(&dhd->pub, ifidx)));
  1098. dev_kfree_skb_any(skb);
  1099. ret = -ENOMEM;
  1100. goto done;
  1101. }
  1102. #ifdef WLMEDIA_HTSF
  1103. if (htsfdlystat_sz && PKTLEN(dhd->pub.osh, pktbuf) >= ETHER_ADDR_LEN) {
  1104. uint8 *pktdata = (uint8 *)PKTDATA(dhd->pub.osh, pktbuf);
  1105. struct ether_header *eh = (struct ether_header *)pktdata;
  1106. if (!ETHER_ISMULTI(eh->ether_dhost) &&
  1107. (ntoh16(eh->ether_type) == ETHER_TYPE_IP)) {
  1108. eh->ether_type = hton16(ETHER_TYPE_BRCM_PKTDLYSTATS);
  1109. }
  1110. }
  1111. #endif
  1112. ret = dhd_sendpkt(&dhd->pub, ifidx, pktbuf);
  1113. done:
  1114. if (ret)
  1115. dhd->pub.dstats.tx_dropped++;
  1116. else
  1117. dhd->pub.tx_packets++;
  1118. DHD_OS_WAKE_UNLOCK(&dhd->pub);
  1119. /* Return ok: we always eat the packet */
  1120. return 0;
  1121. }
  1122. void
  1123. dhd_txflowcontrol(dhd_pub_t *dhdp, int ifidx, bool state)
  1124. {
  1125. struct net_device *net;
  1126. dhd_info_t *dhd = dhdp->info;
  1127. int i;
  1128. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  1129. dhdp->txoff = state;
  1130. ASSERT(dhd);
  1131. if (ifidx == ALL_INTERFACES) {
  1132. /* Flow control on all active interfaces */
  1133. for (i = 0; i < DHD_MAX_IFS; i++) {
  1134. if (dhd->iflist[i]) {
  1135. net = dhd->iflist[i]->net;
  1136. if (state == ON)
  1137. netif_stop_queue(net);
  1138. else
  1139. netif_wake_queue(net);
  1140. }
  1141. }
  1142. }
  1143. else {
  1144. if (dhd->iflist[ifidx]) {
  1145. net = dhd->iflist[ifidx]->net;
  1146. if (state == ON)
  1147. netif_stop_queue(net);
  1148. else
  1149. netif_wake_queue(net);
  1150. }
  1151. }
  1152. }
  1153. void
  1154. dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan)
  1155. {
  1156. dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
  1157. struct sk_buff *skb;
  1158. uchar *eth;
  1159. uint len;
  1160. void *data, *pnext = NULL, *save_pktbuf;
  1161. int i;
  1162. dhd_if_t *ifp;
  1163. wl_event_msg_t event;
  1164. int tout = DHD_PACKET_TIMEOUT;
  1165. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  1166. save_pktbuf = pktbuf;
  1167. for (i = 0; pktbuf && i < numpkt; i++, pktbuf = pnext) {
  1168. struct ether_header *eh;
  1169. struct dot11_llc_snap_header *lsh;
  1170. ifp = dhd->iflist[ifidx];
  1171. if (ifp == NULL) {
  1172. DHD_ERROR(("%s: ifp is NULL. drop packet\n",
  1173. __FUNCTION__));
  1174. PKTFREE(dhdp->osh, pktbuf, TRUE);
  1175. continue;
  1176. }
  1177. /* Dropping packets before registering net device to avoid kernel panic */
  1178. if (!ifp->net || ifp->net->reg_state != NETREG_REGISTERED ||
  1179. !dhd->pub.up) {
  1180. DHD_ERROR(("%s: net device is NOT registered yet. drop packet\n",
  1181. __FUNCTION__));
  1182. PKTFREE(dhdp->osh, pktbuf, TRUE);
  1183. continue;
  1184. }
  1185. pnext = PKTNEXT(dhdp->osh, pktbuf);
  1186. PKTSETNEXT(wl->sh.osh, pktbuf, NULL);
  1187. eh = (struct ether_header *)PKTDATA(wl->sh.osh, pktbuf);
  1188. lsh = (struct dot11_llc_snap_header *)&eh[1];
  1189. if ((ntoh16(eh->ether_type) < ETHER_TYPE_MIN) &&
  1190. (PKTLEN(wl->sh.osh, pktbuf) >= RFC1042_HDR_LEN) &&
  1191. bcmp(lsh, BT_SIG_SNAP_MPROT, DOT11_LLC_SNAP_HDR_LEN - 2) == 0 &&
  1192. lsh->type == HTON16(BTA_PROT_L2CAP)) {
  1193. amp_hci_ACL_data_t *ACL_data = (amp_hci_ACL_data_t *)
  1194. ((uint8 *)eh + RFC1042_HDR_LEN);
  1195. ACL_data = NULL;
  1196. }
  1197. #ifdef PROP_TXSTATUS
  1198. if (dhdp->wlfc_state && PKTLEN(wl->sh.osh, pktbuf) == 0) {
  1199. /* WLFC may send header only packet when
  1200. there is an urgent message but no packet to
  1201. piggy-back on
  1202. */
  1203. ((athost_wl_status_info_t*)dhdp->wlfc_state)->stats.wlfc_header_only_pkt++;
  1204. PKTFREE(dhdp->osh, pktbuf, TRUE);
  1205. continue;
  1206. }
  1207. #endif
  1208. skb = PKTTONATIVE(dhdp->osh, pktbuf);
  1209. /* Get the protocol, maintain skb around eth_type_trans()
  1210. * The main reason for this hack is for the limitation of
  1211. * Linux 2.4 where 'eth_type_trans' uses the 'net->hard_header_len'
  1212. * to perform skb_pull inside vs ETH_HLEN. Since to avoid
  1213. * coping of the packet coming from the network stack to add
  1214. * BDC, Hardware header etc, during network interface registration
  1215. * we set the 'net->hard_header_len' to ETH_HLEN + extra space required
  1216. * for BDC, Hardware header etc. and not just the ETH_HLEN
  1217. */
  1218. eth = skb->data;
  1219. len = skb->len;
  1220. ifp = dhd->iflist[ifidx];
  1221. if (ifp == NULL)
  1222. ifp = dhd->iflist[0];
  1223. ASSERT(ifp);
  1224. skb->dev = ifp->net;
  1225. skb->protocol = eth_type_trans(skb, skb->dev);
  1226. if (skb->pkt_type == PACKET_MULTICAST) {
  1227. dhd->pub.rx_multicast++;
  1228. }
  1229. skb->data = eth;
  1230. skb->len = len;
  1231. #ifdef WLMEDIA_HTSF
  1232. dhd_htsf_addrxts(dhdp, pktbuf);
  1233. #endif
  1234. /* Strip header, count, deliver upward */
  1235. skb_pull(skb, ETH_HLEN);
  1236. /* Process special event packets and then discard them */
  1237. if (ntoh16(skb->protocol) == ETHER_TYPE_BRCM) {
  1238. dhd_wl_host_event(dhd, &ifidx,
  1239. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
  1240. skb->mac_header,
  1241. #else
  1242. skb->mac.raw,
  1243. #endif
  1244. &event,
  1245. &data);
  1246. wl_event_to_host_order(&event);
  1247. if (event.event_type == WLC_E_BTA_HCI_EVENT) {
  1248. dhd_bta_doevt(dhdp, data, event.datalen);
  1249. }
  1250. tout = DHD_EVENT_TIMEOUT;
  1251. }
  1252. ASSERT(ifidx < DHD_MAX_IFS && dhd->iflist[ifidx]);
  1253. if (dhd->iflist[ifidx] && !dhd->iflist[ifidx]->state)
  1254. ifp = dhd->iflist[ifidx];
  1255. if (ifp->net)
  1256. ifp->net->last_rx = jiffies;
  1257. dhdp->dstats.rx_bytes += skb->len;
  1258. dhdp->rx_packets++; /* Local count */
  1259. if (in_interrupt()) {
  1260. netif_rx(skb);
  1261. } else {
  1262. /* If the receive is not processed inside an ISR,
  1263. * the softirqd must be woken explicitly to service
  1264. * the NET_RX_SOFTIRQ. In 2.6 kernels, this is handled
  1265. * by netif_rx_ni(), but in earlier kernels, we need
  1266. * to do it manually.
  1267. */
  1268. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
  1269. netif_rx_ni(skb);
  1270. #else
  1271. ulong flags;
  1272. netif_rx(skb);
  1273. local_irq_save(flags);
  1274. RAISE_RX_SOFTIRQ();
  1275. local_irq_restore(flags);
  1276. #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */
  1277. }
  1278. }
  1279. DHD_OS_WAKE_LOCK_TIMEOUT_ENABLE(dhdp, tout);
  1280. }
  1281. void
  1282. dhd_event(struct dhd_info *dhd, char *evpkt, int evlen, int ifidx)
  1283. {
  1284. /* Linux version has nothing to do */
  1285. return;
  1286. }
  1287. void
  1288. dhd_txcomplete(dhd_pub_t *dhdp, void *txp, bool success)
  1289. {
  1290. uint ifidx;
  1291. dhd_info_t *dhd = (dhd_info_t *)(dhdp->info);
  1292. struct ether_header *eh;
  1293. uint16 type;
  1294. uint len;
  1295. dhd_prot_hdrpull(dhdp, &ifidx, txp);
  1296. eh = (struct ether_header *)PKTDATA(dhdp->osh, txp);
  1297. type = ntoh16(eh->ether_type);
  1298. if (type == ETHER_TYPE_802_1X)
  1299. atomic_dec(&dhd->pend_8021x_cnt);
  1300. /* Crack open the packet and check to see if it is BT HCI ACL data packet.
  1301. * If yes generate packet completion event.
  1302. */
  1303. len = PKTLEN(dhdp->osh, txp);
  1304. /* Generate ACL data tx completion event locally to avoid SDIO bus transaction */
  1305. if ((type < ETHER_TYPE_MIN) && (len >= RFC1042_HDR_LEN)) {
  1306. struct dot11_llc_snap_header *lsh = (struct dot11_llc_snap_header *)&eh[1];
  1307. if (bcmp(lsh, BT_SIG_SNAP_MPROT, DOT11_LLC_SNAP_HDR_LEN - 2) == 0 &&
  1308. ntoh16(lsh->type) == BTA_PROT_L2CAP) {
  1309. dhd_bta_tx_hcidata_complete(dhdp, txp, success);
  1310. }
  1311. }
  1312. }
  1313. static struct net_device_stats *
  1314. dhd_get_stats(struct net_device *net)
  1315. {
  1316. dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
  1317. dhd_if_t *ifp;
  1318. int ifidx;
  1319. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  1320. ifidx = dhd_net2idx(dhd, net);
  1321. if (ifidx == DHD_BAD_IF)
  1322. return NULL;
  1323. ifp = dhd->iflist[ifidx];
  1324. ASSERT(dhd && ifp);
  1325. if (dhd->pub.up) {
  1326. /* Use the protocol to get dongle stats */
  1327. dhd_prot_dstats(&dhd->pub);
  1328. }
  1329. /* Copy dongle stats to net device stats */
  1330. ifp->stats.rx_packets = dhd->pub.dstats.rx_packets;
  1331. ifp->stats.tx_packets = dhd->pub.dstats.tx_packets;
  1332. ifp->stats.rx_bytes = dhd->pub.dstats.rx_bytes;
  1333. ifp->stats.tx_bytes = dhd->pub.dstats.tx_bytes;
  1334. ifp->stats.rx_errors = dhd->pub.dstats.rx_errors;
  1335. ifp->stats.tx_errors = dhd->pub.dstats.tx_errors;
  1336. ifp->stats.rx_dropped = dhd->pub.dstats.rx_dropped;
  1337. ifp->stats.tx_dropped = dhd->pub.dstats.tx_dropped;
  1338. ifp->stats.multicast = dhd->pub.dstats.multicast;
  1339. return &ifp->stats;
  1340. }
  1341. #ifdef DHDTHREAD
  1342. static int
  1343. dhd_watchdog_thread(void *data)
  1344. {
  1345. tsk_ctl_t *tsk = (tsk_ctl_t *)data;
  1346. dhd_info_t *dhd = (dhd_info_t *)tsk->parent;
  1347. /* This thread doesn't need any user-level access,
  1348. * so get rid of all our resources
  1349. */
  1350. if (dhd_watchdog_prio > 0) {
  1351. struct sched_param param;
  1352. param.sched_priority = (dhd_watchdog_prio < MAX_RT_PRIO)?
  1353. dhd_watchdog_prio:(MAX_RT_PRIO-1);
  1354. setScheduler(current, SCHED_FIFO, &param);
  1355. }
  1356. DAEMONIZE("dhd_watchdog");
  1357. /* Run until signal received */
  1358. complete(&tsk->completed);
  1359. while (1)
  1360. if (down_interruptible (&tsk->sema) == 0) {
  1361. unsigned long flags;
  1362. SMP_RD_BARRIER_DEPENDS();
  1363. if (tsk->terminated) {
  1364. break;
  1365. }
  1366. dhd_os_sdlock(&dhd->pub);
  1367. if (dhd->pub.dongle_reset == FALSE) {
  1368. DHD_TIMER(("%s:\n", __FUNCTION__));
  1369. /* Call the bus module watchdog */
  1370. dhd_bus_watchdog(&dhd->pub);
  1371. flags = dhd_os_spin_lock(&dhd->pub);
  1372. /* Count the tick for reference */
  1373. dhd->pub.tickcnt++;
  1374. /* Reschedule the watchdog */
  1375. if (dhd->wd_timer_valid)
  1376. mod_timer(&dhd->timer,
  1377. jiffies + dhd_watchdog_ms * HZ / 1000);
  1378. dhd_os_spin_unlock(&dhd->pub, flags);
  1379. }
  1380. dhd_os_sdunlock(&dhd->pub);
  1381. DHD_OS_WAKE_UNLOCK(&dhd->pub);
  1382. } else {
  1383. break;
  1384. }
  1385. complete_and_exit(&tsk->completed, 0);
  1386. }
  1387. #endif /* DHDTHREAD */
  1388. static void dhd_watchdog(ulong data)
  1389. {
  1390. dhd_info_t *dhd = (dhd_info_t *)data;
  1391. unsigned long flags;
  1392. DHD_OS_WAKE_LOCK(&dhd->pub);
  1393. if (dhd->pub.dongle_reset) {
  1394. DHD_OS_WAKE_UNLOCK(&dhd->pub);
  1395. return;
  1396. }
  1397. #ifdef DHDTHREAD
  1398. if (dhd->thr_wdt_ctl.thr_pid >= 0) {
  1399. up(&dhd->thr_wdt_ctl.sema);
  1400. return;
  1401. }
  1402. #endif /* DHDTHREAD */
  1403. dhd_os_sdlock(&dhd->pub);
  1404. /* Call the bus module watchdog */
  1405. dhd_bus_watchdog(&dhd->pub);
  1406. flags = dhd_os_spin_lock(&dhd->pub);
  1407. /* Count the tick for reference */
  1408. dhd->pub.tickcnt++;
  1409. /* Reschedule the watchdog */
  1410. if (dhd->wd_timer_valid)
  1411. mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000);
  1412. dhd_os_spin_unlock(&dhd->pub, flags);
  1413. dhd_os_sdunlock(&dhd->pub);
  1414. DHD_OS_WAKE_UNLOCK(&dhd->pub);
  1415. }
  1416. #ifdef DHDTHREAD
  1417. static int
  1418. dhd_dpc_thread(void *data)
  1419. {
  1420. tsk_ctl_t *tsk = (tsk_ctl_t *)data;
  1421. dhd_info_t *dhd = (dhd_info_t *)tsk->parent;
  1422. /* This thread doesn't need any user-level access,
  1423. * so get rid of all our resources
  1424. */
  1425. if (dhd_dpc_prio > 0)
  1426. {
  1427. struct sched_param param;
  1428. param.sched_priority = (dhd_dpc_prio < MAX_RT_PRIO)?dhd_dpc_prio:(MAX_RT_PRIO-1);
  1429. setScheduler(current, SCHED_FIFO, &param);
  1430. }
  1431. DAEMONIZE("dhd_dpc");
  1432. /* DHD_OS_WAKE_LOCK is called in dhd_sched_dpc[dhd_linux.c] down below */
  1433. /* signal: thread has started */
  1434. complete(&tsk->completed);
  1435. /* Run until signal received */
  1436. while (1) {
  1437. if (down_interruptible(&tsk->sema) == 0) {
  1438. SMP_RD_BARRIER_DEPENDS();
  1439. if (tsk->terminated) {
  1440. break;
  1441. }
  1442. /* Call bus dpc unless it indicated down (then clean stop) */
  1443. if (dhd->pub.busstate != DHD_BUS_DOWN) {
  1444. if (dhd_bus_dpc(dhd->pub.bus)) {
  1445. up(&tsk->sema);
  1446. }
  1447. else {
  1448. DHD_OS_WAKE_UNLOCK(&dhd->pub);
  1449. }
  1450. } else {
  1451. dhd_bus_stop(dhd->pub.bus, TRUE);
  1452. DHD_OS_WAKE_UNLOCK(&dhd->pub);
  1453. }
  1454. }
  1455. else
  1456. break;
  1457. }
  1458. complete_and_exit(&tsk->completed, 0);
  1459. }
  1460. #endif /* DHDTHREAD */
  1461. static void
  1462. dhd_dpc(ulong data)
  1463. {
  1464. dhd_info_t *dhd;
  1465. dhd = (dhd_info_t *)data;
  1466. /* this (tasklet) can be scheduled in dhd_sched_dpc[dhd_linux.c]
  1467. * down below , wake lock is set,
  1468. * the tasklet is initialized in dhd_attach()
  1469. */
  1470. /* Call bus dpc unless it indicated down (then clean stop) */
  1471. if (dhd->pub.busstate != DHD_BUS_DOWN) {
  1472. if (dhd_bus_dpc(dhd->pub.bus))
  1473. tasklet_schedule(&dhd->tasklet);
  1474. else
  1475. DHD_OS_WAKE_UNLOCK(&dhd->pub);
  1476. } else {
  1477. dhd_bus_stop(dhd->pub.bus, TRUE);
  1478. DHD_OS_WAKE_UNLOCK(&dhd->pub);
  1479. }
  1480. }
  1481. void
  1482. dhd_sched_dpc(dhd_pub_t *dhdp)
  1483. {
  1484. dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
  1485. DHD_OS_WAKE_LOCK(dhdp);
  1486. #ifdef DHDTHREAD
  1487. if (dhd->thr_dpc_ctl.thr_pid >= 0) {
  1488. up(&dhd->thr_dpc_ctl.sema);
  1489. return;
  1490. }
  1491. #endif /* DHDTHREAD */
  1492. tasklet_schedule(&dhd->tasklet);
  1493. }
  1494. #ifdef TOE
  1495. /* Retrieve current toe component enables, which are kept as a bitmap in toe_ol iovar */
  1496. static int
  1497. dhd_toe_get(dhd_info_t *dhd, int ifidx, uint32 *toe_ol)
  1498. {
  1499. wl_ioctl_t ioc;
  1500. char buf[32];
  1501. int ret;
  1502. memset(&ioc, 0, sizeof(ioc));
  1503. ioc.cmd = WLC_GET_VAR;
  1504. ioc.buf = buf;
  1505. ioc.len = (uint)sizeof(buf);
  1506. ioc.set = FALSE;
  1507. strcpy(buf, "toe_ol");
  1508. if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
  1509. /* Check for older dongle image that doesn't support toe_ol */
  1510. if (ret == -EIO) {
  1511. DHD_ERROR(("%s: toe not supported by device\n",
  1512. dhd_ifname(&dhd->pub, ifidx)));
  1513. return -EOPNOTSUPP;
  1514. }
  1515. DHD_INFO(("%s: could not get toe_ol: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret));
  1516. return ret;
  1517. }
  1518. memcpy(toe_ol, buf, sizeof(uint32));
  1519. return 0;
  1520. }
  1521. /* Set current toe component enables in toe_ol iovar, and set toe global enable iovar */
  1522. static int
  1523. dhd_toe_set(dhd_info_t *dhd, int ifidx, uint32 toe_ol)
  1524. {
  1525. wl_ioctl_t ioc;
  1526. char buf[32];
  1527. int toe, ret;
  1528. memset(&ioc, 0, sizeof(ioc));
  1529. ioc.cmd = WLC_SET_VAR;
  1530. ioc.buf = buf;
  1531. ioc.len = (uint)sizeof(buf);
  1532. ioc.set = TRUE;
  1533. /* Set toe_ol as requested */
  1534. strcpy(buf, "toe_ol");
  1535. memcpy(&buf[sizeof("toe_ol")], &toe_ol, sizeof(uint32));
  1536. if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
  1537. DHD_ERROR(("%s: could not set toe_ol: ret=%d\n",
  1538. dhd_ifname(&dhd->pub, ifidx), ret));
  1539. return ret;
  1540. }
  1541. /* Enable toe globally only if any components are enabled. */
  1542. toe = (toe_ol != 0);
  1543. strcpy(buf, "toe");
  1544. memcpy(&buf[sizeof("toe")], &toe, sizeof(uint32));
  1545. if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
  1546. DHD_ERROR(("%s: could not set toe: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret));
  1547. return ret;
  1548. }
  1549. return 0;
  1550. }
  1551. #endif /* TOE */
  1552. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
  1553. static void
  1554. dhd_ethtool_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info)
  1555. {
  1556. dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
  1557. sprintf(info->driver, "wl");
  1558. sprintf(info->version, "%lu", dhd->pub.drv_version);
  1559. }
  1560. struct ethtool_ops dhd_ethtool_ops = {
  1561. .get_drvinfo = dhd_ethtool_get_drvinfo
  1562. };
  1563. #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */
  1564. #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2)
  1565. static int
  1566. dhd_ethtool(dhd_info_t *dhd, void *uaddr)
  1567. {
  1568. struct ethtool_drvinfo info;
  1569. char drvname[sizeof(info.driver)];
  1570. uint32 cmd;
  1571. #ifdef TOE
  1572. struct ethtool_value edata;
  1573. uint32 toe_cmpnt, csum_dir;
  1574. int ret;
  1575. #endif
  1576. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  1577. /* all ethtool calls start with a cmd word */
  1578. if (copy_from_user(&cmd, uaddr, sizeof (uint32)))
  1579. return -EFAULT;
  1580. switch (cmd) {
  1581. case ETHTOOL_GDRVINFO:
  1582. /* Copy out any request driver name */
  1583. if (copy_from_user(&info, uaddr, sizeof(info)))
  1584. return -EFAULT;
  1585. strncpy(drvname, info.driver, sizeof(info.driver));
  1586. drvname[sizeof(info.driver)-1] = '\0';
  1587. /* clear struct for return */
  1588. memset(&info, 0, sizeof(info));
  1589. info.cmd = cmd;
  1590. /* if dhd requested, identify ourselves */
  1591. if (strcmp(drvname, "?dhd") == 0) {
  1592. sprintf(info.driver, "dhd");
  1593. strcpy(info.version, EPI_VERSION_STR);
  1594. }
  1595. /* otherwise, require dongle to be up */
  1596. else if (!dhd->pub.up) {
  1597. DHD_ERROR(("%s: dongle is not up\n", __FUNCTION__));
  1598. return -ENODEV;
  1599. }
  1600. /* finally, report dongle driver type */
  1601. else if (dhd->pub.iswl)
  1602. sprintf(info.driver, "wl");
  1603. else
  1604. sprintf(info.driver, "xx");
  1605. sprintf(info.version, "%lu", dhd->pub.drv_version);
  1606. if (copy_to_user(uaddr, &info, sizeof(info)))
  1607. return -EFAULT;
  1608. DHD_CTL(("%s: given %*s, returning %s\n", __FUNCTION__,
  1609. (int)sizeof(drvname), drvname, info.driver));
  1610. break;
  1611. #ifdef TOE
  1612. /* Get toe…